wined3d: Merge the various resource desc structures.
[wine] / dlls / msi / action.c
1 /*
2  * Implementation of the Microsoft Installer (msi.dll)
3  *
4  * Copyright 2004,2005 Aric Stewart for CodeWeavers
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20
21 #include <stdarg.h>
22
23 #define COBJMACROS
24
25 #include "windef.h"
26 #include "winbase.h"
27 #include "winerror.h"
28 #include "winreg.h"
29 #include "winsvc.h"
30 #include "odbcinst.h"
31 #include "wine/debug.h"
32 #include "msidefs.h"
33 #include "msipriv.h"
34 #include "winuser.h"
35 #include "shlobj.h"
36 #include "objbase.h"
37 #include "mscoree.h"
38 #include "shlwapi.h"
39 #include "wine/unicode.h"
40 #include "winver.h"
41
42 #define REG_PROGRESS_VALUE 13200
43 #define COMPONENT_PROGRESS_VALUE 24000
44
45 WINE_DEFAULT_DEBUG_CHANNEL(msi);
46
47 /*
48  * consts and values used
49  */
50 static const WCHAR c_colon[] = {'C',':','\\',0};
51
52 static const WCHAR szCreateFolders[] =
53     {'C','r','e','a','t','e','F','o','l','d','e','r','s',0};
54 static const WCHAR szCostFinalize[] =
55     {'C','o','s','t','F','i','n','a','l','i','z','e',0};
56 static const WCHAR szWriteRegistryValues[] =
57     {'W','r','i','t','e','R','e','g','i','s','t','r','y','V','a','l','u','e','s',0};
58 static const WCHAR szCostInitialize[] =
59     {'C','o','s','t','I','n','i','t','i','a','l','i','z','e',0};
60 static const WCHAR szFileCost[] = 
61     {'F','i','l','e','C','o','s','t',0};
62 static const WCHAR szInstallInitialize[] = 
63     {'I','n','s','t','a','l','l','I','n','i','t','i','a','l','i','z','e',0};
64 static const WCHAR szInstallValidate[] = 
65     {'I','n','s','t','a','l','l','V','a','l','i','d','a','t','e',0};
66 static const WCHAR szLaunchConditions[] = 
67     {'L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','s',0};
68 static const WCHAR szProcessComponents[] = 
69     {'P','r','o','c','e','s','s','C','o','m','p','o','n','e','n','t','s',0};
70 static const WCHAR szRegisterTypeLibraries[] = 
71     {'R','e','g','i','s','t','e','r','T','y','p','e','L','i','b','r','a','r','i','e','s',0};
72 static const WCHAR szCreateShortcuts[] = 
73     {'C','r','e','a','t','e','S','h','o','r','t','c','u','t','s',0};
74 static const WCHAR szPublishProduct[] = 
75     {'P','u','b','l','i','s','h','P','r','o','d','u','c','t',0};
76 static const WCHAR szWriteIniValues[] = 
77     {'W','r','i','t','e','I','n','i','V','a','l','u','e','s',0};
78 static const WCHAR szSelfRegModules[] = 
79     {'S','e','l','f','R','e','g','M','o','d','u','l','e','s',0};
80 static const WCHAR szPublishFeatures[] = 
81     {'P','u','b','l','i','s','h','F','e','a','t','u','r','e','s',0};
82 static const WCHAR szRegisterProduct[] = 
83     {'R','e','g','i','s','t','e','r','P','r','o','d','u','c','t',0};
84 static const WCHAR szInstallExecute[] = 
85     {'I','n','s','t','a','l','l','E','x','e','c','u','t','e',0};
86 static const WCHAR szInstallExecuteAgain[] = 
87     {'I','n','s','t','a','l','l','E','x','e','c','u','t','e','A','g','a','i','n',0};
88 static const WCHAR szInstallFinalize[] = 
89     {'I','n','s','t','a','l','l','F','i','n','a','l','i','z','e',0};
90 static const WCHAR szForceReboot[] = 
91     {'F','o','r','c','e','R','e','b','o','o','t',0};
92 static const WCHAR szResolveSource[] =
93     {'R','e','s','o','l','v','e','S','o','u','r','c','e',0};
94 static const WCHAR szAllocateRegistrySpace[] = 
95     {'A','l','l','o','c','a','t','e','R','e','g','i','s','t','r','y','S','p','a','c','e',0};
96 static const WCHAR szBindImage[] = 
97     {'B','i','n','d','I','m','a','g','e',0};
98 static const WCHAR szDeleteServices[] = 
99     {'D','e','l','e','t','e','S','e','r','v','i','c','e','s',0};
100 static const WCHAR szDisableRollback[] = 
101     {'D','i','s','a','b','l','e','R','o','l','l','b','a','c','k',0};
102 static const WCHAR szExecuteAction[] = 
103     {'E','x','e','c','u','t','e','A','c','t','i','o','n',0};
104 static const WCHAR szInstallAdminPackage[] = 
105     {'I','n','s','t','a','l','l','A','d','m','i','n','P','a','c','k','a','g','e',0};
106 static const WCHAR szInstallSFPCatalogFile[] = 
107     {'I','n','s','t','a','l','l','S','F','P','C','a','t','a','l','o','g','F','i','l','e',0};
108 static const WCHAR szIsolateComponents[] = 
109     {'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t','s',0};
110 static const WCHAR szMigrateFeatureStates[] =
111     {'M','i','g','r','a','t','e','F','e','a','t','u','r','e','S','t','a','t','e','s',0};
112 static const WCHAR szMsiUnpublishAssemblies[] = 
113     {'M','s','i','U','n','p','u','b','l','i','s','h','A','s','s','e','m','b','l','i','e','s',0};
114 static const WCHAR szInstallODBC[] = 
115     {'I','n','s','t','a','l','l','O','D','B','C',0};
116 static const WCHAR szInstallServices[] = 
117     {'I','n','s','t','a','l','l','S','e','r','v','i','c','e','s',0};
118 static const WCHAR szPatchFiles[] =
119     {'P','a','t','c','h','F','i','l','e','s',0};
120 static const WCHAR szPublishComponents[] = 
121     {'P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','s',0};
122 static const WCHAR szRegisterComPlus[] =
123     {'R','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0};
124 static const WCHAR szRegisterUser[] =
125     {'R','e','g','i','s','t','e','r','U','s','e','r',0};
126 static const WCHAR szRemoveEnvironmentStrings[] =
127     {'R','e','m','o','v','e','E','n','v','i','r','o','n','m','e','n','t','S','t','r','i','n','g','s',0};
128 static const WCHAR szRemoveExistingProducts[] =
129     {'R','e','m','o','v','e','E','x','i','s','t','i','n','g','P','r','o','d','u','c','t','s',0};
130 static const WCHAR szRemoveFolders[] =
131     {'R','e','m','o','v','e','F','o','l','d','e','r','s',0};
132 static const WCHAR szRemoveIniValues[] =
133     {'R','e','m','o','v','e','I','n','i','V','a','l','u','e','s',0};
134 static const WCHAR szRemoveODBC[] =
135     {'R','e','m','o','v','e','O','D','B','C',0};
136 static const WCHAR szRemoveRegistryValues[] =
137     {'R','e','m','o','v','e','R','e','g','i','s','t','r','y','V','a','l','u','e','s',0};
138 static const WCHAR szRemoveShortcuts[] =
139     {'R','e','m','o','v','e','S','h','o','r','t','c','u','t','s',0};
140 static const WCHAR szRMCCPSearch[] =
141     {'R','M','C','C','P','S','e','a','r','c','h',0};
142 static const WCHAR szScheduleReboot[] =
143     {'S','c','h','e','d','u','l','e','R','e','b','o','o','t',0};
144 static const WCHAR szSelfUnregModules[] =
145     {'S','e','l','f','U','n','r','e','g','M','o','d','u','l','e','s',0};
146 static const WCHAR szSetODBCFolders[] =
147     {'S','e','t','O','D','B','C','F','o','l','d','e','r','s',0};
148 static const WCHAR szStartServices[] =
149     {'S','t','a','r','t','S','e','r','v','i','c','e','s',0};
150 static const WCHAR szStopServices[] =
151     {'S','t','o','p','S','e','r','v','i','c','e','s',0};
152 static const WCHAR szUnpublishComponents[] =
153     {'U','n','p','u','b','l','i','s','h', 'C','o','m','p','o','n','e','n','t','s',0};
154 static const WCHAR szUnpublishFeatures[] =
155     {'U','n','p','u','b','l','i','s','h','F','e','a','t','u','r','e','s',0};
156 static const WCHAR szUnregisterComPlus[] =
157     {'U','n','r','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0};
158 static const WCHAR szUnregisterTypeLibraries[] =
159     {'U','n','r','e','g','i','s','t','e','r','T','y','p','e','L','i','b','r','a','r','i','e','s',0};
160 static const WCHAR szValidateProductID[] =
161     {'V','a','l','i','d','a','t','e','P','r','o','d','u','c','t','I','D',0};
162 static const WCHAR szWriteEnvironmentStrings[] =
163     {'W','r','i','t','e','E','n','v','i','r','o','n','m','e','n','t','S','t','r','i','n','g','s',0};
164
165 static void ui_actionstart(MSIPACKAGE *package, LPCWSTR action)
166 {
167     static const WCHAR Query_t[] = 
168         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
169          '`','A','c','t','i','o', 'n','T','e','x','t','`',' ',
170          'W','H','E','R','E', ' ','`','A','c','t','i','o','n','`',' ','=', 
171          ' ','\'','%','s','\'',0};
172     MSIRECORD * row;
173
174     row = MSI_QueryGetRecord( package->db, Query_t, action );
175     if (!row)
176         return;
177     MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONSTART, row);
178     msiobj_release(&row->hdr);
179 }
180
181 static void ui_actioninfo(MSIPACKAGE *package, LPCWSTR action, BOOL start, 
182                           UINT rc)
183 {
184     MSIRECORD * row;
185     static const WCHAR template_s[]=
186         {'A','c','t','i','o','n',' ','s','t','a','r','t',' ','%','s',':',' ',
187          '%','s', '.',0};
188     static const WCHAR template_e[]=
189         {'A','c','t','i','o','n',' ','e','n','d','e','d',' ','%','s',':',' ',
190          '%','s', '.',' ','R','e','t','u','r','n',' ','v','a','l','u','e',' ',
191          '%','i','.',0};
192     static const WCHAR format[] = 
193         {'H','H','\'',':','\'','m','m','\'',':','\'','s','s',0};
194     WCHAR message[1024];
195     WCHAR timet[0x100];
196
197     GetTimeFormatW(LOCALE_USER_DEFAULT, 0, NULL, format, timet, 0x100);
198     if (start)
199         sprintfW(message,template_s,timet,action);
200     else
201         sprintfW(message,template_e,timet,action,rc);
202     
203     row = MSI_CreateRecord(1);
204     MSI_RecordSetStringW(row,1,message);
205  
206     MSI_ProcessMessage(package, INSTALLMESSAGE_INFO, row);
207     msiobj_release(&row->hdr);
208 }
209
210 enum parse_state
211 {
212     state_whitespace,
213     state_token,
214     state_quote
215 };
216
217 static int parse_prop( const WCHAR *str, WCHAR *value, int *quotes )
218 {
219     enum parse_state state = state_quote;
220     const WCHAR *p;
221     WCHAR *out = value;
222     int ignore, in_quotes = 0, count = 0, len = 0;
223
224     for (p = str; *p; p++)
225     {
226         ignore = 0;
227         switch (state)
228         {
229         case state_whitespace:
230             switch (*p)
231             {
232             case ' ':
233                 if (!count) goto done;
234                 in_quotes = 1;
235                 ignore = 1;
236                 break;
237             case '"':
238                 state = state_quote;
239                 if (in_quotes) count--;
240                 else count++;
241                 break;
242             default:
243                 state = state_token;
244                 if (!count) in_quotes = 0;
245                 else in_quotes = 1;
246                 len++;
247                 break;
248             }
249             break;
250
251         case state_token:
252             switch (*p)
253             {
254             case '"':
255                 state = state_quote;
256                 if (in_quotes) count--;
257                 else count++;
258                 break;
259             case ' ':
260                 state = state_whitespace;
261                 if (!count) goto done;
262                 in_quotes = 1;
263                 break;
264             default:
265                 if (!count) in_quotes = 0;
266                 else in_quotes = 1;
267                 len++;
268                 break;
269             }
270             break;
271
272         case state_quote:
273             switch (*p)
274             {
275             case '"':
276                 if (in_quotes) count--;
277                 else count++;
278                 break;
279             case ' ':
280                 state = state_whitespace;
281                 if (!count || !len) goto done;
282                 in_quotes = 1;
283                 break;
284             default:
285                 state = state_token;
286                 if (!count) in_quotes = 0;
287                 else in_quotes = 1;
288                 len++;
289                 break;
290             }
291             break;
292
293         default: break;
294         }
295         if (!ignore) *out++ = *p;
296     }
297
298 done:
299     if (!len) *value = 0;
300     else *out = 0;
301
302     *quotes = count;
303     return p - str;
304 }
305
306 static void remove_quotes( WCHAR *str )
307 {
308     WCHAR *p = str;
309     int len = strlenW( str );
310
311     while ((p = strchrW( p, '"' )))
312     {
313         memmove( p, p + 1, (len - (p - str)) * sizeof(WCHAR) );
314         p++;
315     }
316 }
317
318 UINT msi_parse_command_line( MSIPACKAGE *package, LPCWSTR szCommandLine,
319                              BOOL preserve_case )
320 {
321     LPCWSTR ptr, ptr2;
322     int quotes;
323     DWORD len;
324     WCHAR *prop, *val;
325     UINT r;
326
327     if (!szCommandLine)
328         return ERROR_SUCCESS;
329
330     ptr = szCommandLine;
331     while (*ptr)
332     {
333         while (*ptr == ' ') ptr++;
334         if (!*ptr) break;
335
336         ptr2 = strchrW( ptr, '=' );
337         if (!ptr2) return ERROR_INVALID_COMMAND_LINE;
338  
339         len = ptr2 - ptr;
340         if (!len) return ERROR_INVALID_COMMAND_LINE;
341
342         prop = msi_alloc( (len + 1) * sizeof(WCHAR) );
343         memcpy( prop, ptr, len * sizeof(WCHAR) );
344         prop[len] = 0;
345         if (!preserve_case) struprW( prop );
346
347         ptr2++;
348         while (*ptr2 == ' ') ptr2++;
349
350         quotes = 0;
351         val = msi_alloc( (strlenW( ptr2 ) + 1) * sizeof(WCHAR) );
352         len = parse_prop( ptr2, val, &quotes );
353         if (quotes % 2)
354         {
355             WARN("unbalanced quotes\n");
356             msi_free( val );
357             msi_free( prop );
358             return ERROR_INVALID_COMMAND_LINE;
359         }
360         remove_quotes( val );
361         TRACE("Found commandline property %s = %s\n", debugstr_w(prop), debugstr_w(val));
362
363         r = msi_set_property( package->db, prop, val );
364         if (r == ERROR_SUCCESS && !strcmpW( prop, cszSourceDir ))
365             msi_reset_folders( package, TRUE );
366
367         msi_free( val );
368         msi_free( prop );
369
370         ptr = ptr2 + len;
371     }
372
373     return ERROR_SUCCESS;
374 }
375
376 WCHAR **msi_split_string( const WCHAR *str, WCHAR sep )
377 {
378     LPCWSTR pc;
379     LPWSTR p, *ret = NULL;
380     UINT count = 0;
381
382     if (!str)
383         return ret;
384
385     /* count the number of substrings */
386     for ( pc = str, count = 0; pc; count++ )
387     {
388         pc = strchrW( pc, sep );
389         if (pc)
390             pc++;
391     }
392
393     /* allocate space for an array of substring pointers and the substrings */
394     ret = msi_alloc( (count+1) * sizeof (LPWSTR) +
395                      (lstrlenW(str)+1) * sizeof(WCHAR) );
396     if (!ret)
397         return ret;
398
399     /* copy the string and set the pointers */
400     p = (LPWSTR) &ret[count+1];
401     lstrcpyW( p, str );
402     for( count = 0; (ret[count] = p); count++ )
403     {
404         p = strchrW( p, sep );
405         if (p)
406             *p++ = 0;
407     }
408
409     return ret;
410 }
411
412 static UINT msi_check_transform_applicable( MSIPACKAGE *package, IStorage *patch )
413 {
414     static const WCHAR szSystemLanguageID[] =
415         { 'S','y','s','t','e','m','L','a','n','g','u','a','g','e','I','D',0 };
416
417     LPWSTR prod_code, patch_product, langid = NULL, template = NULL;
418     UINT ret = ERROR_FUNCTION_FAILED;
419
420     prod_code = msi_dup_property( package->db, szProductCode );
421     patch_product = msi_get_suminfo_product( patch );
422
423     TRACE("db = %s patch = %s\n", debugstr_w(prod_code), debugstr_w(patch_product));
424
425     if ( strstrW( patch_product, prod_code ) )
426     {
427         MSISUMMARYINFO *si;
428         const WCHAR *p;
429
430         si = MSI_GetSummaryInformationW( patch, 0 );
431         if (!si)
432         {
433             ERR("no summary information!\n");
434             goto end;
435         }
436
437         template = msi_suminfo_dup_string( si, PID_TEMPLATE );
438         if (!template)
439         {
440             ERR("no template property!\n");
441             msiobj_release( &si->hdr );
442             goto end;
443         }
444
445         if (!template[0])
446         {
447             ret = ERROR_SUCCESS;
448             msiobj_release( &si->hdr );
449             goto end;
450         }
451
452         langid = msi_dup_property( package->db, szSystemLanguageID );
453         if (!langid)
454         {
455             msiobj_release( &si->hdr );
456             goto end;
457         }
458
459         p = strchrW( template, ';' );
460         if (p && (!strcmpW( p + 1, langid ) || !strcmpW( p + 1, szZero )))
461         {
462             TRACE("applicable transform\n");
463             ret = ERROR_SUCCESS;
464         }
465
466         /* FIXME: check platform */
467
468         msiobj_release( &si->hdr );
469     }
470
471 end:
472     msi_free( patch_product );
473     msi_free( prod_code );
474     msi_free( template );
475     msi_free( langid );
476
477     return ret;
478 }
479
480 static UINT msi_apply_substorage_transform( MSIPACKAGE *package,
481                                  MSIDATABASE *patch_db, LPCWSTR name )
482 {
483     UINT ret = ERROR_FUNCTION_FAILED;
484     IStorage *stg = NULL;
485     HRESULT r;
486
487     TRACE("%p %s\n", package, debugstr_w(name) );
488
489     if (*name++ != ':')
490     {
491         ERR("expected a colon in %s\n", debugstr_w(name));
492         return ERROR_FUNCTION_FAILED;
493     }
494
495     r = IStorage_OpenStorage( patch_db->storage, name, NULL, STGM_SHARE_EXCLUSIVE, NULL, 0, &stg );
496     if (SUCCEEDED(r))
497     {
498         ret = msi_check_transform_applicable( package, stg );
499         if (ret == ERROR_SUCCESS)
500             msi_table_apply_transform( package->db, stg );
501         else
502             TRACE("substorage transform %s wasn't applicable\n", debugstr_w(name));
503         IStorage_Release( stg );
504     }
505     else
506         ERR("failed to open substorage %s\n", debugstr_w(name));
507
508     return ERROR_SUCCESS;
509 }
510
511 UINT msi_check_patch_applicable( MSIPACKAGE *package, MSISUMMARYINFO *si )
512 {
513     LPWSTR guid_list, *guids, product_code;
514     UINT i, ret = ERROR_FUNCTION_FAILED;
515
516     product_code = msi_dup_property( package->db, szProductCode );
517     if (!product_code)
518     {
519         /* FIXME: the property ProductCode should be written into the DB somewhere */
520         ERR("no product code to check\n");
521         return ERROR_SUCCESS;
522     }
523
524     guid_list = msi_suminfo_dup_string( si, PID_TEMPLATE );
525     guids = msi_split_string( guid_list, ';' );
526     for ( i = 0; guids[i] && ret != ERROR_SUCCESS; i++ )
527     {
528         if (!strcmpW( guids[i], product_code ))
529             ret = ERROR_SUCCESS;
530     }
531     msi_free( guids );
532     msi_free( guid_list );
533     msi_free( product_code );
534
535     return ret;
536 }
537
538 static UINT msi_set_media_source_prop(MSIPACKAGE *package)
539 {
540     MSIQUERY *view;
541     MSIRECORD *rec = NULL;
542     LPWSTR patch;
543     LPCWSTR prop;
544     UINT r;
545
546     static const WCHAR query[] = {'S','E','L','E','C','T',' ',
547         '`','S','o','u','r','c','e','`',' ','F','R','O','M',' ',
548         '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
549         '`','S','o','u','r','c','e','`',' ','I','S',' ',
550         'N','O','T',' ','N','U','L','L',0};
551
552     r = MSI_DatabaseOpenViewW(package->db, query, &view);
553     if (r != ERROR_SUCCESS)
554         return r;
555
556     r = MSI_ViewExecute(view, 0);
557     if (r != ERROR_SUCCESS)
558         goto done;
559
560     if (MSI_ViewFetch(view, &rec) == ERROR_SUCCESS)
561     {
562         prop = MSI_RecordGetString(rec, 1);
563         patch = msi_dup_property(package->db, szPatch);
564         msi_set_property(package->db, prop, patch);
565         msi_free(patch);
566     }
567
568 done:
569     if (rec) msiobj_release(&rec->hdr);
570     msiobj_release(&view->hdr);
571
572     return r;
573 }
574
575 UINT msi_parse_patch_summary( MSISUMMARYINFO *si, MSIPATCHINFO **patch )
576 {
577     MSIPATCHINFO *pi;
578     UINT r = ERROR_SUCCESS;
579     WCHAR *p;
580
581     pi = msi_alloc_zero( sizeof(MSIPATCHINFO) );
582     if (!pi)
583         return ERROR_OUTOFMEMORY;
584
585     pi->patchcode = msi_suminfo_dup_string( si, PID_REVNUMBER );
586     if (!pi->patchcode)
587     {
588         msi_free( pi );
589         return ERROR_OUTOFMEMORY;
590     }
591
592     p = pi->patchcode;
593     if (*p != '{')
594     {
595         msi_free( pi->patchcode );
596         msi_free( pi );
597         return ERROR_PATCH_PACKAGE_INVALID;
598     }
599
600     p = strchrW( p + 1, '}' );
601     if (!p)
602     {
603         msi_free( pi->patchcode );
604         msi_free( pi );
605         return ERROR_PATCH_PACKAGE_INVALID;
606     }
607
608     if (p[1])
609     {
610         FIXME("patch obsoletes %s\n", debugstr_w(p + 1));
611         p[1] = 0;
612     }
613
614     TRACE("patch code %s\n", debugstr_w(pi->patchcode));
615
616     pi->transforms = msi_suminfo_dup_string( si, PID_LASTAUTHOR );
617     if (!pi->transforms)
618     {
619         msi_free( pi->patchcode );
620         msi_free( pi );
621         return ERROR_OUTOFMEMORY;
622     }
623
624     *patch = pi;
625     return r;
626 }
627
628 UINT msi_apply_patch_db( MSIPACKAGE *package, MSIDATABASE *patch_db, MSIPATCHINFO *patch )
629 {
630     UINT i, r = ERROR_SUCCESS;
631     WCHAR **substorage;
632
633     /* apply substorage transforms */
634     substorage = msi_split_string( patch->transforms, ';' );
635     for (i = 0; substorage && substorage[i] && r == ERROR_SUCCESS; i++)
636         r = msi_apply_substorage_transform( package, patch_db, substorage[i] );
637
638     msi_free( substorage );
639     if (r != ERROR_SUCCESS)
640         return r;
641
642     msi_set_media_source_prop( package );
643
644     /*
645      * There might be a CAB file in the patch package,
646      * so append it to the list of storages to search for streams.
647      */
648     append_storage_to_db( package->db, patch_db->storage );
649
650     patch->state = MSIPATCHSTATE_APPLIED;
651     list_add_tail( &package->patches, &patch->entry );
652     return ERROR_SUCCESS;
653 }
654
655 static UINT msi_apply_patch_package( MSIPACKAGE *package, LPCWSTR file )
656 {
657     static const WCHAR dotmsp[] = {'.','m','s','p',0};
658     MSIDATABASE *patch_db = NULL;
659     WCHAR localfile[MAX_PATH];
660     MSISUMMARYINFO *si;
661     MSIPATCHINFO *patch = NULL;
662     UINT r = ERROR_SUCCESS;
663
664     TRACE("%p %s\n", package, debugstr_w( file ) );
665
666     r = MSI_OpenDatabaseW( file, MSIDBOPEN_READONLY + MSIDBOPEN_PATCHFILE, &patch_db );
667     if ( r != ERROR_SUCCESS )
668     {
669         ERR("failed to open patch collection %s\n", debugstr_w( file ) );
670         return r;
671     }
672
673     si = MSI_GetSummaryInformationW( patch_db->storage, 0 );
674     if (!si)
675     {
676         msiobj_release( &patch_db->hdr );
677         return ERROR_FUNCTION_FAILED;
678     }
679
680     r = msi_check_patch_applicable( package, si );
681     if (r != ERROR_SUCCESS)
682     {
683         TRACE("patch not applicable\n");
684         r = ERROR_SUCCESS;
685         goto done;
686     }
687
688     r = msi_parse_patch_summary( si, &patch );
689     if ( r != ERROR_SUCCESS )
690         goto done;
691
692     r = msi_get_local_package_name( localfile, dotmsp );
693     if ( r != ERROR_SUCCESS )
694         goto done;
695
696     TRACE("copying to local package %s\n", debugstr_w(localfile));
697
698     if (!CopyFileW( file, localfile, FALSE ))
699     {
700         ERR("Unable to copy package (%s -> %s) (error %u)\n",
701             debugstr_w(file), debugstr_w(localfile), GetLastError());
702         r = GetLastError();
703         goto done;
704     }
705     patch->localfile = strdupW( localfile );
706
707     r = msi_apply_patch_db( package, patch_db, patch );
708     if ( r != ERROR_SUCCESS )
709         WARN("patch failed to apply %u\n", r);
710
711 done:
712     msiobj_release( &si->hdr );
713     msiobj_release( &patch_db->hdr );
714     if (patch && r != ERROR_SUCCESS)
715     {
716         if (patch->localfile)
717             DeleteFileW( patch->localfile );
718
719         msi_free( patch->patchcode );
720         msi_free( patch->transforms );
721         msi_free( patch->localfile );
722         msi_free( patch );
723     }
724     return r;
725 }
726
727 /* get the PATCH property, and apply all the patches it specifies */
728 static UINT msi_apply_patches( MSIPACKAGE *package )
729 {
730     LPWSTR patch_list, *patches;
731     UINT i, r = ERROR_SUCCESS;
732
733     patch_list = msi_dup_property( package->db, szPatch );
734
735     TRACE("patches to be applied: %s\n", debugstr_w( patch_list ) );
736
737     patches = msi_split_string( patch_list, ';' );
738     for( i=0; patches && patches[i] && r == ERROR_SUCCESS; i++ )
739         r = msi_apply_patch_package( package, patches[i] );
740
741     msi_free( patches );
742     msi_free( patch_list );
743
744     return r;
745 }
746
747 static UINT msi_apply_transforms( MSIPACKAGE *package )
748 {
749     static const WCHAR szTransforms[] = {
750         'T','R','A','N','S','F','O','R','M','S',0 };
751     LPWSTR xform_list, *xforms;
752     UINT i, r = ERROR_SUCCESS;
753
754     xform_list = msi_dup_property( package->db, szTransforms );
755     xforms = msi_split_string( xform_list, ';' );
756
757     for( i=0; xforms && xforms[i] && r == ERROR_SUCCESS; i++ )
758     {
759         if (xforms[i][0] == ':')
760             r = msi_apply_substorage_transform( package, package->db, xforms[i] );
761         else
762         {
763             WCHAR *transform;
764
765             if (!PathIsRelativeW( xforms[i] )) transform = xforms[i];
766             else
767             {
768                 WCHAR *p = strrchrW( package->PackagePath, '\\' );
769                 DWORD len = p - package->PackagePath + 1;
770
771                 if (!(transform = msi_alloc( (len + strlenW( xforms[i] ) + 1) * sizeof(WCHAR)) ))
772                 {
773                     msi_free( xforms );
774                     msi_free( xform_list );
775                     return ERROR_OUTOFMEMORY;
776                 }
777                 memcpy( transform, package->PackagePath, len * sizeof(WCHAR) );
778                 memcpy( transform + len, xforms[i], (strlenW( xforms[i] ) + 1) * sizeof(WCHAR) );
779             }
780             r = MSI_DatabaseApplyTransformW( package->db, transform, 0 );
781             if (transform != xforms[i]) msi_free( transform );
782         }
783     }
784
785     msi_free( xforms );
786     msi_free( xform_list );
787
788     return r;
789 }
790
791 static BOOL ui_sequence_exists( MSIPACKAGE *package )
792 {
793     MSIQUERY *view;
794     UINT rc;
795
796     static const WCHAR ExecSeqQuery [] =
797         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
798          '`','I','n','s','t','a','l','l',
799          'U','I','S','e','q','u','e','n','c','e','`',
800          ' ','W','H','E','R','E',' ',
801          '`','S','e','q','u','e','n','c','e','`',' ',
802          '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
803          '`','S','e','q','u','e','n','c','e','`',0};
804
805     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
806     if (rc == ERROR_SUCCESS)
807     {
808         msiobj_release(&view->hdr);
809         return TRUE;
810     }
811
812     return FALSE;
813 }
814
815 UINT msi_set_sourcedir_props(MSIPACKAGE *package, BOOL replace)
816 {
817     LPWSTR source, check;
818
819     if (msi_get_property_int( package->db, szInstalled, 0 ))
820     {
821         HKEY hkey;
822
823         MSIREG_OpenInstallProps( package->ProductCode, package->Context, NULL, &hkey, FALSE );
824         source = msi_reg_get_val_str( hkey, INSTALLPROPERTY_INSTALLSOURCEW );
825         RegCloseKey( hkey );
826     }
827     else
828     {
829         LPWSTR p, db;
830         DWORD len;
831
832         db = msi_dup_property( package->db, szOriginalDatabase );
833         if (!db)
834             return ERROR_OUTOFMEMORY;
835
836         p = strrchrW( db, '\\' );
837         if (!p)
838         {
839             p = strrchrW( db, '/' );
840             if (!p)
841             {
842                 msi_free(db);
843                 return ERROR_SUCCESS;
844             }
845         }
846
847         len = p - db + 2;
848         source = msi_alloc( len * sizeof(WCHAR) );
849         lstrcpynW( source, db, len );
850         msi_free( db );
851     }
852
853     check = msi_dup_property( package->db, cszSourceDir );
854     if (!check || replace)
855     {
856         UINT r = msi_set_property( package->db, cszSourceDir, source );
857         if (r == ERROR_SUCCESS)
858             msi_reset_folders( package, TRUE );
859     }
860     msi_free( check );
861
862     check = msi_dup_property( package->db, cszSOURCEDIR );
863     if (!check || replace)
864         msi_set_property( package->db, cszSOURCEDIR, source );
865
866     msi_free( check );
867     msi_free( source );
868
869     return ERROR_SUCCESS;
870 }
871
872 static BOOL needs_ui_sequence(MSIPACKAGE *package)
873 {
874     INT level = msi_get_property_int(package->db, szUILevel, 0);
875     return (level & INSTALLUILEVEL_MASK) >= INSTALLUILEVEL_REDUCED;
876 }
877
878 UINT msi_set_context(MSIPACKAGE *package)
879 {
880     int num;
881
882     package->Context = MSIINSTALLCONTEXT_USERUNMANAGED;
883
884     num = msi_get_property_int(package->db, szAllUsers, 0);
885     if (num == 1 || num == 2)
886         package->Context = MSIINSTALLCONTEXT_MACHINE;
887
888     return ERROR_SUCCESS;
889 }
890
891 static UINT ITERATE_Actions(MSIRECORD *row, LPVOID param)
892 {
893     UINT rc;
894     LPCWSTR cond, action;
895     MSIPACKAGE *package = param;
896
897     action = MSI_RecordGetString(row,1);
898     if (!action)
899     {
900         ERR("Error is retrieving action name\n");
901         return ERROR_FUNCTION_FAILED;
902     }
903
904     /* check conditions */
905     cond = MSI_RecordGetString(row,2);
906
907     /* this is a hack to skip errors in the condition code */
908     if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
909     {
910         TRACE("Skipping action: %s (condition is false)\n", debugstr_w(action));
911         return ERROR_SUCCESS;
912     }
913
914     if (needs_ui_sequence(package))
915         rc = ACTION_PerformUIAction(package, action, -1);
916     else
917         rc = ACTION_PerformAction(package, action, -1);
918
919     msi_dialog_check_messages( NULL );
920
921     if (package->CurrentInstallState != ERROR_SUCCESS)
922         rc = package->CurrentInstallState;
923
924     if (rc == ERROR_FUNCTION_NOT_CALLED)
925         rc = ERROR_SUCCESS;
926
927     if (rc != ERROR_SUCCESS)
928         ERR("Execution halted, action %s returned %i\n", debugstr_w(action), rc);
929
930     return rc;
931 }
932
933 UINT MSI_Sequence( MSIPACKAGE *package, LPCWSTR szTable, INT iSequenceMode )
934 {
935     MSIQUERY * view;
936     UINT r;
937     static const WCHAR query[] =
938         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
939          '`','%','s','`',
940          ' ','W','H','E','R','E',' ', 
941          '`','S','e','q','u','e','n','c','e','`',' ',
942          '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
943          '`','S','e','q','u','e','n','c','e','`',0};
944
945     TRACE("%p %s %i\n", package, debugstr_w(szTable), iSequenceMode );
946
947     r = MSI_OpenQuery( package->db, &view, query, szTable );
948     if (r == ERROR_SUCCESS)
949     {
950         r = MSI_IterateRecords( view, NULL, ITERATE_Actions, package );
951         msiobj_release(&view->hdr);
952     }
953
954     return r;
955 }
956
957 static UINT ACTION_ProcessExecSequence(MSIPACKAGE *package, BOOL UIran)
958 {
959     MSIQUERY * view;
960     UINT rc;
961     static const WCHAR ExecSeqQuery[] =
962         {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
963          '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
964          'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
965          '`','S','e','q','u','e','n','c','e','`',' ', '>',' ','%','i',' ',
966          'O','R','D','E','R',' ', 'B','Y',' ',
967          '`','S','e','q','u','e','n','c','e','`',0 };
968     static const WCHAR IVQuery[] =
969         {'S','E','L','E','C','T',' ','`','S','e','q','u','e','n','c','e','`',
970          ' ', 'F','R','O','M',' ','`','I','n','s','t','a','l','l',
971          'E','x','e','c','u','t','e','S','e','q','u','e','n','c','e','`',' ',
972          'W','H','E','R','E',' ','`','A','c','t','i','o','n','`',' ','=',
973          ' ','\'', 'I','n','s','t','a','l','l',
974          'V','a','l','i','d','a','t','e','\'', 0};
975     INT seq = 0;
976
977     if (package->script->ExecuteSequenceRun)
978     {
979         TRACE("Execute Sequence already Run\n");
980         return ERROR_SUCCESS;
981     }
982
983     package->script->ExecuteSequenceRun = TRUE;
984
985     /* get the sequence number */
986     if (UIran)
987     {
988         MSIRECORD *row = MSI_QueryGetRecord(package->db, IVQuery);
989         if( !row )
990             return ERROR_FUNCTION_FAILED;
991         seq = MSI_RecordGetInteger(row,1);
992         msiobj_release(&row->hdr);
993     }
994
995     rc = MSI_OpenQuery(package->db, &view, ExecSeqQuery, seq);
996     if (rc == ERROR_SUCCESS)
997     {
998         TRACE("Running the actions\n");
999
1000         msi_set_property(package->db, cszSourceDir, NULL);
1001
1002         rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, package);
1003         msiobj_release(&view->hdr);
1004     }
1005
1006     return rc;
1007 }
1008
1009 static UINT ACTION_ProcessUISequence(MSIPACKAGE *package)
1010 {
1011     MSIQUERY * view;
1012     UINT rc;
1013     static const WCHAR ExecSeqQuery [] =
1014         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1015          '`','I','n','s','t','a','l','l',
1016          'U','I','S','e','q','u','e','n','c','e','`',
1017          ' ','W','H','E','R','E',' ', 
1018          '`','S','e','q','u','e','n','c','e','`',' ',
1019          '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
1020          '`','S','e','q','u','e','n','c','e','`',0};
1021
1022     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
1023     if (rc == ERROR_SUCCESS)
1024     {
1025         TRACE("Running the actions\n"); 
1026
1027         rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, package);
1028         msiobj_release(&view->hdr);
1029     }
1030
1031     return rc;
1032 }
1033
1034 /********************************************************
1035  * ACTION helper functions and functions that perform the actions
1036  *******************************************************/
1037 static BOOL ACTION_HandleCustomAction( MSIPACKAGE* package, LPCWSTR action,
1038                                        UINT* rc, UINT script, BOOL force )
1039 {
1040     BOOL ret=FALSE;
1041     UINT arc;
1042
1043     arc = ACTION_CustomAction(package, action, script, force);
1044
1045     if (arc != ERROR_CALL_NOT_IMPLEMENTED)
1046     {
1047         *rc = arc;
1048         ret = TRUE;
1049     }
1050     return ret;
1051 }
1052
1053 /*
1054  * Actual Action Handlers
1055  */
1056
1057 static UINT ITERATE_CreateFolders(MSIRECORD *row, LPVOID param)
1058 {
1059     MSIPACKAGE *package = param;
1060     LPCWSTR dir, component;
1061     LPWSTR full_path;
1062     MSIRECORD *uirow;
1063     MSIFOLDER *folder;
1064     MSICOMPONENT *comp;
1065
1066     component = MSI_RecordGetString(row, 2);
1067     if (!component)
1068         return ERROR_SUCCESS;
1069
1070     comp = get_loaded_component(package, component);
1071     if (!comp)
1072         return ERROR_SUCCESS;
1073
1074     if (!comp->Enabled)
1075     {
1076         TRACE("component is disabled\n");
1077         return ERROR_SUCCESS;
1078     }
1079
1080     if (comp->ActionRequest != INSTALLSTATE_LOCAL)
1081     {
1082         TRACE("Component not scheduled for installation: %s\n", debugstr_w(component));
1083         comp->Action = comp->Installed;
1084         return ERROR_SUCCESS;
1085     }
1086     comp->Action = INSTALLSTATE_LOCAL;
1087
1088     dir = MSI_RecordGetString(row,1);
1089     if (!dir)
1090     {
1091         ERR("Unable to get folder id\n");
1092         return ERROR_SUCCESS;
1093     }
1094
1095     uirow = MSI_CreateRecord(1);
1096     MSI_RecordSetStringW(uirow, 1, dir);
1097     ui_actiondata(package, szCreateFolders, uirow);
1098     msiobj_release(&uirow->hdr);
1099
1100     full_path = resolve_target_folder( package, dir, FALSE, TRUE, &folder );
1101     if (!full_path)
1102     {
1103         ERR("Unable to resolve folder id %s\n",debugstr_w(dir));
1104         return ERROR_SUCCESS;
1105     }
1106
1107     TRACE("Folder is %s\n",debugstr_w(full_path));
1108
1109     if (folder->State == 0)
1110         create_full_pathW(full_path);
1111
1112     folder->State = 3;
1113
1114     msi_free(full_path);
1115     return ERROR_SUCCESS;
1116 }
1117
1118 static UINT ACTION_CreateFolders(MSIPACKAGE *package)
1119 {
1120     static const WCHAR query[] =
1121         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1122          '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0};
1123     UINT rc;
1124     MSIQUERY *view;
1125
1126     /* create all the empty folders specified in the CreateFolder table */
1127     rc = MSI_DatabaseOpenViewW(package->db, query, &view );
1128     if (rc != ERROR_SUCCESS)
1129         return ERROR_SUCCESS;
1130
1131     rc = MSI_IterateRecords(view, NULL, ITERATE_CreateFolders, package);
1132     msiobj_release(&view->hdr);
1133
1134     return rc;
1135 }
1136
1137 static UINT ITERATE_RemoveFolders( MSIRECORD *row, LPVOID param )
1138 {
1139     MSIPACKAGE *package = param;
1140     LPCWSTR dir, component;
1141     LPWSTR full_path;
1142     MSIRECORD *uirow;
1143     MSIFOLDER *folder;
1144     MSICOMPONENT *comp;
1145
1146     component = MSI_RecordGetString(row, 2);
1147     if (!component)
1148         return ERROR_SUCCESS;
1149
1150     comp = get_loaded_component(package, component);
1151     if (!comp)
1152         return ERROR_SUCCESS;
1153
1154     if (!comp->Enabled)
1155     {
1156         TRACE("component is disabled\n");
1157         return ERROR_SUCCESS;
1158     }
1159
1160     if (comp->ActionRequest != INSTALLSTATE_ABSENT)
1161     {
1162         TRACE("Component not scheduled for removal: %s\n", debugstr_w(component));
1163         comp->Action = comp->Installed;
1164         return ERROR_SUCCESS;
1165     }
1166     comp->Action = INSTALLSTATE_ABSENT;
1167
1168     dir = MSI_RecordGetString( row, 1 );
1169     if (!dir)
1170     {
1171         ERR("Unable to get folder id\n");
1172         return ERROR_SUCCESS;
1173     }
1174
1175     full_path = resolve_target_folder( package, dir, FALSE, TRUE, &folder );
1176     if (!full_path)
1177     {
1178         ERR("Unable to resolve folder id %s\n", debugstr_w(dir));
1179         return ERROR_SUCCESS;
1180     }
1181
1182     TRACE("folder is %s\n", debugstr_w(full_path));
1183
1184     uirow = MSI_CreateRecord( 1 );
1185     MSI_RecordSetStringW( uirow, 1, dir );
1186     ui_actiondata( package, szRemoveFolders, uirow );
1187     msiobj_release( &uirow->hdr );
1188
1189     RemoveDirectoryW( full_path );
1190     folder->State = 0;
1191
1192     msi_free( full_path );
1193     return ERROR_SUCCESS;
1194 }
1195
1196 static UINT ACTION_RemoveFolders( MSIPACKAGE *package )
1197 {
1198     static const WCHAR query[] =
1199         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1200          '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0};
1201
1202     MSIQUERY *view;
1203     UINT rc;
1204
1205     rc = MSI_DatabaseOpenViewW( package->db, query, &view );
1206     if (rc != ERROR_SUCCESS)
1207         return ERROR_SUCCESS;
1208
1209     rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveFolders, package );
1210     msiobj_release( &view->hdr );
1211
1212     return rc;
1213 }
1214
1215 static UINT load_component( MSIRECORD *row, LPVOID param )
1216 {
1217     MSIPACKAGE *package = param;
1218     MSICOMPONENT *comp;
1219
1220     comp = msi_alloc_zero( sizeof(MSICOMPONENT) );
1221     if (!comp)
1222         return ERROR_FUNCTION_FAILED;
1223
1224     list_add_tail( &package->components, &comp->entry );
1225
1226     /* fill in the data */
1227     comp->Component = msi_dup_record_field( row, 1 );
1228
1229     TRACE("Loading Component %s\n", debugstr_w(comp->Component));
1230
1231     comp->ComponentId = msi_dup_record_field( row, 2 );
1232     comp->Directory = msi_dup_record_field( row, 3 );
1233     comp->Attributes = MSI_RecordGetInteger(row,4);
1234     comp->Condition = msi_dup_record_field( row, 5 );
1235     comp->KeyPath = msi_dup_record_field( row, 6 );
1236
1237     comp->Installed = INSTALLSTATE_UNKNOWN;
1238     comp->Action = INSTALLSTATE_UNKNOWN;
1239     comp->ActionRequest = INSTALLSTATE_UNKNOWN;
1240
1241     comp->assembly = load_assembly( package, comp );
1242     return ERROR_SUCCESS;
1243 }
1244
1245 static UINT load_all_components( MSIPACKAGE *package )
1246 {
1247     static const WCHAR query[] = {
1248         'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ', 
1249          '`','C','o','m','p','o','n','e','n','t','`',0 };
1250     MSIQUERY *view;
1251     UINT r;
1252
1253     if (!list_empty(&package->components))
1254         return ERROR_SUCCESS;
1255
1256     r = MSI_DatabaseOpenViewW( package->db, query, &view );
1257     if (r != ERROR_SUCCESS)
1258         return r;
1259
1260     r = MSI_IterateRecords(view, NULL, load_component, package);
1261     msiobj_release(&view->hdr);
1262     return r;
1263 }
1264
1265 typedef struct {
1266     MSIPACKAGE *package;
1267     MSIFEATURE *feature;
1268 } _ilfs;
1269
1270 static UINT add_feature_component( MSIFEATURE *feature, MSICOMPONENT *comp )
1271 {
1272     ComponentList *cl;
1273
1274     cl = msi_alloc( sizeof (*cl) );
1275     if ( !cl )
1276         return ERROR_NOT_ENOUGH_MEMORY;
1277     cl->component = comp;
1278     list_add_tail( &feature->Components, &cl->entry );
1279
1280     return ERROR_SUCCESS;
1281 }
1282
1283 static UINT add_feature_child( MSIFEATURE *parent, MSIFEATURE *child )
1284 {
1285     FeatureList *fl;
1286
1287     fl = msi_alloc( sizeof(*fl) );
1288     if ( !fl )
1289         return ERROR_NOT_ENOUGH_MEMORY;
1290     fl->feature = child;
1291     list_add_tail( &parent->Children, &fl->entry );
1292
1293     return ERROR_SUCCESS;
1294 }
1295
1296 static UINT iterate_load_featurecomponents(MSIRECORD *row, LPVOID param)
1297 {
1298     _ilfs* ilfs = param;
1299     LPCWSTR component;
1300     MSICOMPONENT *comp;
1301
1302     component = MSI_RecordGetString(row,1);
1303
1304     /* check to see if the component is already loaded */
1305     comp = get_loaded_component( ilfs->package, component );
1306     if (!comp)
1307     {
1308         ERR("unknown component %s\n", debugstr_w(component));
1309         return ERROR_FUNCTION_FAILED;
1310     }
1311
1312     add_feature_component( ilfs->feature, comp );
1313     comp->Enabled = TRUE;
1314
1315     return ERROR_SUCCESS;
1316 }
1317
1318 static MSIFEATURE *find_feature_by_name( MSIPACKAGE *package, LPCWSTR name )
1319 {
1320     MSIFEATURE *feature;
1321
1322     if ( !name )
1323         return NULL;
1324
1325     LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1326     {
1327         if ( !strcmpW( feature->Feature, name ) )
1328             return feature;
1329     }
1330
1331     return NULL;
1332 }
1333
1334 static UINT load_feature(MSIRECORD * row, LPVOID param)
1335 {
1336     MSIPACKAGE* package = param;
1337     MSIFEATURE* feature;
1338     static const WCHAR Query1[] = 
1339         {'S','E','L','E','C','T',' ',
1340          '`','C','o','m','p','o','n','e','n','t','_','`',
1341          ' ','F','R','O','M',' ','`','F','e','a','t','u','r','e',
1342          'C','o','m','p','o','n','e','n','t','s','`',' ',
1343          'W','H','E','R','E',' ',
1344          '`','F','e', 'a','t','u','r','e','_','`',' ','=','\'','%','s','\'',0};
1345     MSIQUERY * view;
1346     UINT    rc;
1347     _ilfs ilfs;
1348
1349     /* fill in the data */
1350
1351     feature = msi_alloc_zero( sizeof (MSIFEATURE) );
1352     if (!feature)
1353         return ERROR_NOT_ENOUGH_MEMORY;
1354
1355     list_init( &feature->Children );
1356     list_init( &feature->Components );
1357     
1358     feature->Feature = msi_dup_record_field( row, 1 );
1359
1360     TRACE("Loading feature %s\n",debugstr_w(feature->Feature));
1361
1362     feature->Feature_Parent = msi_dup_record_field( row, 2 );
1363     feature->Title = msi_dup_record_field( row, 3 );
1364     feature->Description = msi_dup_record_field( row, 4 );
1365
1366     if (!MSI_RecordIsNull(row,5))
1367         feature->Display = MSI_RecordGetInteger(row,5);
1368   
1369     feature->Level= MSI_RecordGetInteger(row,6);
1370     feature->Directory = msi_dup_record_field( row, 7 );
1371     feature->Attributes = MSI_RecordGetInteger(row,8);
1372
1373     feature->Installed = INSTALLSTATE_UNKNOWN;
1374     feature->Action = INSTALLSTATE_UNKNOWN;
1375     feature->ActionRequest = INSTALLSTATE_UNKNOWN;
1376
1377     list_add_tail( &package->features, &feature->entry );
1378
1379     /* load feature components */
1380
1381     rc = MSI_OpenQuery( package->db, &view, Query1, feature->Feature );
1382     if (rc != ERROR_SUCCESS)
1383         return ERROR_SUCCESS;
1384
1385     ilfs.package = package;
1386     ilfs.feature = feature;
1387
1388     MSI_IterateRecords(view, NULL, iterate_load_featurecomponents , &ilfs);
1389     msiobj_release(&view->hdr);
1390
1391     return ERROR_SUCCESS;
1392 }
1393
1394 static UINT find_feature_children(MSIRECORD * row, LPVOID param)
1395 {
1396     MSIPACKAGE* package = param;
1397     MSIFEATURE *parent, *child;
1398
1399     child = find_feature_by_name( package, MSI_RecordGetString( row, 1 ) );
1400     if (!child)
1401         return ERROR_FUNCTION_FAILED;
1402
1403     if (!child->Feature_Parent)
1404         return ERROR_SUCCESS;
1405
1406     parent = find_feature_by_name( package, child->Feature_Parent );
1407     if (!parent)
1408         return ERROR_FUNCTION_FAILED;
1409
1410     add_feature_child( parent, child );
1411     return ERROR_SUCCESS;
1412 }
1413
1414 static UINT load_all_features( MSIPACKAGE *package )
1415 {
1416     static const WCHAR query[] = {
1417         'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1418         '`','F','e','a','t','u','r','e','`',' ','O','R','D','E','R',
1419         ' ','B','Y',' ','`','D','i','s','p','l','a','y','`',0};
1420     MSIQUERY *view;
1421     UINT r;
1422
1423     if (!list_empty(&package->features))
1424         return ERROR_SUCCESS;
1425  
1426     r = MSI_DatabaseOpenViewW( package->db, query, &view );
1427     if (r != ERROR_SUCCESS)
1428         return r;
1429
1430     r = MSI_IterateRecords( view, NULL, load_feature, package );
1431     if (r != ERROR_SUCCESS)
1432         return r;
1433
1434     r = MSI_IterateRecords( view, NULL, find_feature_children, package );
1435     msiobj_release( &view->hdr );
1436
1437     return r;
1438 }
1439
1440 static LPWSTR folder_split_path(LPWSTR p, WCHAR ch)
1441 {
1442     if (!p)
1443         return p;
1444     p = strchrW(p, ch);
1445     if (!p)
1446         return p;
1447     *p = 0;
1448     return p+1;
1449 }
1450
1451 static UINT load_file_hash(MSIPACKAGE *package, MSIFILE *file)
1452 {
1453     static const WCHAR query[] = {
1454         'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1455         '`','M','s','i','F','i','l','e','H','a','s','h','`',' ',
1456         'W','H','E','R','E',' ','`','F','i','l','e','_','`',' ','=',' ','\'','%','s','\'',0};
1457     MSIQUERY *view = NULL;
1458     MSIRECORD *row = NULL;
1459     UINT r;
1460
1461     TRACE("%s\n", debugstr_w(file->File));
1462
1463     r = MSI_OpenQuery(package->db, &view, query, file->File);
1464     if (r != ERROR_SUCCESS)
1465         goto done;
1466
1467     r = MSI_ViewExecute(view, NULL);
1468     if (r != ERROR_SUCCESS)
1469         goto done;
1470
1471     r = MSI_ViewFetch(view, &row);
1472     if (r != ERROR_SUCCESS)
1473         goto done;
1474
1475     file->hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
1476     file->hash.dwData[0] = MSI_RecordGetInteger(row, 3);
1477     file->hash.dwData[1] = MSI_RecordGetInteger(row, 4);
1478     file->hash.dwData[2] = MSI_RecordGetInteger(row, 5);
1479     file->hash.dwData[3] = MSI_RecordGetInteger(row, 6);
1480
1481 done:
1482     if (view) msiobj_release(&view->hdr);
1483     if (row) msiobj_release(&row->hdr);
1484     return r;
1485 }
1486
1487 static UINT load_file_disk_id( MSIPACKAGE *package, MSIFILE *file )
1488 {
1489     MSIRECORD *row;
1490     static const WCHAR query[] = {
1491         'S','E','L','E','C','T',' ','`','D','i','s','k','I','d','`',' ', 'F','R','O','M',' ',
1492         '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
1493         '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ','>','=',' ','%','i',0};
1494
1495     row = MSI_QueryGetRecord( package->db, query, file->Sequence );
1496     if (!row)
1497     {
1498         WARN("query failed\n");
1499         return ERROR_FUNCTION_FAILED;
1500     }
1501
1502     file->disk_id = MSI_RecordGetInteger( row, 1 );
1503     msiobj_release( &row->hdr );
1504     return ERROR_SUCCESS;
1505 }
1506
1507 static UINT load_file(MSIRECORD *row, LPVOID param)
1508 {
1509     MSIPACKAGE* package = param;
1510     LPCWSTR component;
1511     MSIFILE *file;
1512
1513     /* fill in the data */
1514
1515     file = msi_alloc_zero( sizeof (MSIFILE) );
1516     if (!file)
1517         return ERROR_NOT_ENOUGH_MEMORY;
1518  
1519     file->File = msi_dup_record_field( row, 1 );
1520
1521     component = MSI_RecordGetString( row, 2 );
1522     file->Component = get_loaded_component( package, component );
1523
1524     if (!file->Component)
1525     {
1526         WARN("Component not found: %s\n", debugstr_w(component));
1527         msi_free(file->File);
1528         msi_free(file);
1529         return ERROR_SUCCESS;
1530     }
1531
1532     file->FileName = msi_dup_record_field( row, 3 );
1533     reduce_to_longfilename( file->FileName );
1534
1535     file->ShortName = msi_dup_record_field( row, 3 );
1536     file->LongName = strdupW( folder_split_path(file->ShortName, '|'));
1537     
1538     file->FileSize = MSI_RecordGetInteger( row, 4 );
1539     file->Version = msi_dup_record_field( row, 5 );
1540     file->Language = msi_dup_record_field( row, 6 );
1541     file->Attributes = MSI_RecordGetInteger( row, 7 );
1542     file->Sequence = MSI_RecordGetInteger( row, 8 );
1543
1544     file->state = msifs_invalid;
1545
1546     /* if the compressed bits are not set in the file attributes,
1547      * then read the information from the package word count property
1548      */
1549     if (package->WordCount & msidbSumInfoSourceTypeAdminImage)
1550     {
1551         file->IsCompressed = FALSE;
1552     }
1553     else if (file->Attributes &
1554              (msidbFileAttributesCompressed | msidbFileAttributesPatchAdded))
1555     {
1556         file->IsCompressed = TRUE;
1557     }
1558     else if (file->Attributes & msidbFileAttributesNoncompressed)
1559     {
1560         file->IsCompressed = FALSE;
1561     }
1562     else
1563     {
1564         file->IsCompressed = package->WordCount & msidbSumInfoSourceTypeCompressed;
1565     }
1566
1567     load_file_hash(package, file);
1568     load_file_disk_id(package, file);
1569
1570     TRACE("File Loaded (%s)\n",debugstr_w(file->File));  
1571
1572     list_add_tail( &package->files, &file->entry );
1573  
1574     return ERROR_SUCCESS;
1575 }
1576
1577 static UINT load_all_files(MSIPACKAGE *package)
1578 {
1579     MSIQUERY * view;
1580     UINT rc;
1581     static const WCHAR Query[] =
1582         {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1583          '`','F','i','l','e','`',' ', 'O','R','D','E','R',' ','B','Y',' ',
1584          '`','S','e','q','u','e','n','c','e','`', 0};
1585
1586     if (!list_empty(&package->files))
1587         return ERROR_SUCCESS;
1588
1589     rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
1590     if (rc != ERROR_SUCCESS)
1591         return ERROR_SUCCESS;
1592
1593     rc = MSI_IterateRecords(view, NULL, load_file, package);
1594     msiobj_release(&view->hdr);
1595
1596     return ERROR_SUCCESS;
1597 }
1598
1599 static UINT load_folder( MSIRECORD *row, LPVOID param )
1600 {
1601     MSIPACKAGE *package = param;
1602     static WCHAR szEmpty[] = { 0 };
1603     LPWSTR p, tgt_short, tgt_long, src_short, src_long;
1604     MSIFOLDER *folder;
1605
1606     folder = msi_alloc_zero( sizeof (MSIFOLDER) );
1607     if (!folder)
1608         return ERROR_NOT_ENOUGH_MEMORY;
1609
1610     folder->Directory = msi_dup_record_field( row, 1 );
1611
1612     TRACE("%s\n", debugstr_w(folder->Directory));
1613
1614     p = msi_dup_record_field(row, 3);
1615
1616     /* split src and target dir */
1617     tgt_short = p;
1618     src_short = folder_split_path( p, ':' );
1619
1620     /* split the long and short paths */
1621     tgt_long = folder_split_path( tgt_short, '|' );
1622     src_long = folder_split_path( src_short, '|' );
1623
1624     /* check for no-op dirs */
1625     if (tgt_short && !strcmpW( szDot, tgt_short ))
1626         tgt_short = szEmpty;
1627     if (src_short && !strcmpW( szDot, src_short ))
1628         src_short = szEmpty;
1629
1630     if (!tgt_long)
1631         tgt_long = tgt_short;
1632
1633     if (!src_short) {
1634         src_short = tgt_short;
1635         src_long = tgt_long;
1636     }
1637
1638     if (!src_long)
1639         src_long = src_short;
1640
1641     /* FIXME: use the target short path too */
1642     folder->TargetDefault = strdupW(tgt_long);
1643     folder->SourceShortPath = strdupW(src_short);
1644     folder->SourceLongPath = strdupW(src_long);
1645     msi_free(p);
1646
1647     TRACE("TargetDefault = %s\n",debugstr_w( folder->TargetDefault ));
1648     TRACE("SourceLong = %s\n", debugstr_w( folder->SourceLongPath ));
1649     TRACE("SourceShort = %s\n", debugstr_w( folder->SourceShortPath ));
1650
1651     folder->Parent = msi_dup_record_field( row, 2 );
1652
1653     folder->Property = msi_dup_property( package->db, folder->Directory );
1654
1655     list_add_tail( &package->folders, &folder->entry );
1656
1657     TRACE("returning %p\n", folder);
1658
1659     return ERROR_SUCCESS;
1660 }
1661
1662 static UINT load_all_folders( MSIPACKAGE *package )
1663 {
1664     static const WCHAR query[] = {
1665         'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
1666          '`','D','i','r','e','c','t','o','r','y','`',0 };
1667     MSIQUERY *view;
1668     UINT r;
1669
1670     if (!list_empty(&package->folders))
1671         return ERROR_SUCCESS;
1672
1673     r = MSI_DatabaseOpenViewW( package->db, query, &view );
1674     if (r != ERROR_SUCCESS)
1675         return r;
1676
1677     r = MSI_IterateRecords(view, NULL, load_folder, package);
1678     msiobj_release(&view->hdr);
1679     return r;
1680 }
1681
1682 /*
1683  * I am not doing any of the costing functionality yet.
1684  * Mostly looking at doing the Component and Feature loading
1685  *
1686  * The native MSI does A LOT of modification to tables here. Mostly adding
1687  * a lot of temporary columns to the Feature and Component tables.
1688  *
1689  *    note: Native msi also tracks the short filename. But I am only going to
1690  *          track the long ones.  Also looking at this directory table
1691  *          it appears that the directory table does not get the parents
1692  *          resolved base on property only based on their entries in the
1693  *          directory table.
1694  */
1695 static UINT ACTION_CostInitialize(MSIPACKAGE *package)
1696 {
1697     static const WCHAR szCosting[] =
1698         {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
1699
1700     msi_set_property( package->db, szCosting, szZero );
1701     msi_set_property( package->db, cszRootDrive, c_colon );
1702
1703     load_all_folders( package );
1704     load_all_components( package );
1705     load_all_features( package );
1706     load_all_files( package );
1707
1708     return ERROR_SUCCESS;
1709 }
1710
1711 static UINT execute_script(MSIPACKAGE *package, UINT script )
1712 {
1713     UINT i;
1714     UINT rc = ERROR_SUCCESS;
1715
1716     TRACE("Executing Script %i\n",script);
1717
1718     if (!package->script)
1719     {
1720         ERR("no script!\n");
1721         return ERROR_FUNCTION_FAILED;
1722     }
1723
1724     for (i = 0; i < package->script->ActionCount[script]; i++)
1725     {
1726         LPWSTR action;
1727         action = package->script->Actions[script][i];
1728         ui_actionstart(package, action);
1729         TRACE("Executing Action (%s)\n",debugstr_w(action));
1730         rc = ACTION_PerformAction(package, action, script);
1731         if (rc != ERROR_SUCCESS)
1732             break;
1733     }
1734     msi_free_action_script(package, script);
1735     return rc;
1736 }
1737
1738 static UINT ACTION_FileCost(MSIPACKAGE *package)
1739 {
1740     return ERROR_SUCCESS;
1741 }
1742
1743 static void ACTION_GetComponentInstallStates(MSIPACKAGE *package)
1744 {
1745     MSICOMPONENT *comp;
1746     UINT r;
1747
1748     LIST_FOR_EACH_ENTRY(comp, &package->components, MSICOMPONENT, entry)
1749     {
1750         if (!comp->ComponentId) continue;
1751
1752         r = MsiQueryComponentStateW( package->ProductCode, NULL,
1753                                      MSIINSTALLCONTEXT_USERMANAGED, comp->ComponentId,
1754                                      &comp->Installed );
1755         if (r != ERROR_SUCCESS)
1756             r = MsiQueryComponentStateW( package->ProductCode, NULL,
1757                                          MSIINSTALLCONTEXT_USERUNMANAGED, comp->ComponentId,
1758                                          &comp->Installed );
1759         if (r != ERROR_SUCCESS)
1760             r = MsiQueryComponentStateW( package->ProductCode, NULL,
1761                                          MSIINSTALLCONTEXT_MACHINE, comp->ComponentId,
1762                                          &comp->Installed );
1763         if (r != ERROR_SUCCESS)
1764             comp->Installed = INSTALLSTATE_ABSENT;
1765     }
1766 }
1767
1768 static void ACTION_GetFeatureInstallStates(MSIPACKAGE *package)
1769 {
1770     MSIFEATURE *feature;
1771
1772     LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1773     {
1774         INSTALLSTATE state = MsiQueryFeatureStateW( package->ProductCode, feature->Feature );
1775
1776         if (state == INSTALLSTATE_UNKNOWN || state == INSTALLSTATE_INVALIDARG)
1777             feature->Installed = INSTALLSTATE_ABSENT;
1778         else
1779             feature->Installed = state;
1780     }
1781 }
1782
1783 static inline BOOL is_feature_selected( MSIFEATURE *feature, INT level )
1784 {
1785     return (feature->Level > 0 && feature->Level <= level);
1786 }
1787
1788 static BOOL process_state_property(MSIPACKAGE* package, int level,
1789                                    LPCWSTR property, INSTALLSTATE state)
1790 {
1791     LPWSTR override;
1792     MSIFEATURE *feature;
1793
1794     override = msi_dup_property( package->db, property );
1795     if (!override)
1796         return FALSE;
1797
1798     LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1799     {
1800         if (strcmpW( property, szRemove ) && !is_feature_selected( feature, level ))
1801             continue;
1802
1803         if (!strcmpW(property, szReinstall)) state = feature->Installed;
1804
1805         if (!strcmpiW( override, szAll ))
1806         {
1807             if (feature->Installed != state)
1808             {
1809                 feature->Action = state;
1810                 feature->ActionRequest = state;
1811             }
1812         }
1813         else
1814         {
1815             LPWSTR ptr = override;
1816             LPWSTR ptr2 = strchrW(override,',');
1817
1818             while (ptr)
1819             {
1820                 int len = ptr2 - ptr;
1821
1822                 if ((ptr2 && strlenW(feature->Feature) == len && !strncmpW(ptr, feature->Feature, len))
1823                     || (!ptr2 && !strcmpW(ptr, feature->Feature)))
1824                 {
1825                     if (feature->Installed != state)
1826                     {
1827                         feature->Action = state;
1828                         feature->ActionRequest = state;
1829                     }
1830                     break;
1831                 }
1832                 if (ptr2)
1833                 {
1834                     ptr=ptr2+1;
1835                     ptr2 = strchrW(ptr,',');
1836                 }
1837                 else
1838                     break;
1839             }
1840         }
1841     }
1842     msi_free(override);
1843     return TRUE;
1844 }
1845
1846 static BOOL process_overrides( MSIPACKAGE *package, int level )
1847 {
1848     static const WCHAR szAddLocal[] =
1849         {'A','D','D','L','O','C','A','L',0};
1850     static const WCHAR szAddSource[] =
1851         {'A','D','D','S','O','U','R','C','E',0};
1852     static const WCHAR szAdvertise[] =
1853         {'A','D','V','E','R','T','I','S','E',0};
1854     BOOL ret = FALSE;
1855
1856     /* all these activation/deactivation things happen in order and things
1857      * later on the list override things earlier on the list.
1858      *
1859      *  0  INSTALLLEVEL processing
1860      *  1  ADDLOCAL
1861      *  2  REMOVE
1862      *  3  ADDSOURCE
1863      *  4  ADDDEFAULT
1864      *  5  REINSTALL
1865      *  6  ADVERTISE
1866      *  7  COMPADDLOCAL
1867      *  8  COMPADDSOURCE
1868      *  9  FILEADDLOCAL
1869      * 10  FILEADDSOURCE
1870      * 11  FILEADDDEFAULT
1871      */
1872     ret |= process_state_property( package, level, szAddLocal, INSTALLSTATE_LOCAL );
1873     ret |= process_state_property( package, level, szRemove, INSTALLSTATE_ABSENT );
1874     ret |= process_state_property( package, level, szAddSource, INSTALLSTATE_SOURCE );
1875     ret |= process_state_property( package, level, szReinstall, INSTALLSTATE_UNKNOWN );
1876     ret |= process_state_property( package, level, szAdvertise, INSTALLSTATE_ADVERTISED );
1877
1878     if (ret)
1879         msi_set_property( package->db, szPreselected, szOne );
1880
1881     return ret;
1882 }
1883
1884 UINT MSI_SetFeatureStates(MSIPACKAGE *package)
1885 {
1886     int level;
1887     static const WCHAR szlevel[] =
1888         {'I','N','S','T','A','L','L','L','E','V','E','L',0};
1889     MSICOMPONENT* component;
1890     MSIFEATURE *feature;
1891
1892     TRACE("Checking Install Level\n");
1893
1894     level = msi_get_property_int(package->db, szlevel, 1);
1895
1896     if (!msi_get_property_int( package->db, szPreselected, 0 ))
1897     {
1898         LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1899         {
1900             if (!is_feature_selected( feature, level )) continue;
1901
1902             if (feature->ActionRequest == INSTALLSTATE_UNKNOWN)
1903             {
1904                 if (feature->Attributes & msidbFeatureAttributesFavorSource)
1905                 {
1906                     feature->Action = INSTALLSTATE_SOURCE;
1907                     feature->ActionRequest = INSTALLSTATE_SOURCE;
1908                 }
1909                 else if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1910                 {
1911                     feature->Action = INSTALLSTATE_ADVERTISED;
1912                     feature->ActionRequest = INSTALLSTATE_ADVERTISED;
1913                 }
1914                 else
1915                 {
1916                     feature->Action = INSTALLSTATE_LOCAL;
1917                     feature->ActionRequest = INSTALLSTATE_LOCAL;
1918                 }
1919             }
1920         }
1921
1922         /* disable child features of unselected parent features */
1923         LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1924         {
1925             FeatureList *fl;
1926
1927             if (is_feature_selected( feature, level )) continue;
1928
1929             LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
1930             {
1931                 fl->feature->Action = INSTALLSTATE_UNKNOWN;
1932                 fl->feature->ActionRequest = INSTALLSTATE_UNKNOWN;
1933             }
1934         }
1935     }
1936     else /* preselected */
1937     {
1938         LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1939         {
1940             if (!is_feature_selected( feature, level )) continue;
1941
1942             if (feature->ActionRequest == INSTALLSTATE_UNKNOWN)
1943             {
1944                 if (feature->Installed == INSTALLSTATE_ABSENT)
1945                 {
1946                     feature->Action = INSTALLSTATE_UNKNOWN;
1947                     feature->ActionRequest = INSTALLSTATE_UNKNOWN;
1948                 }
1949                 else
1950                 {
1951                     feature->Action = feature->Installed;
1952                     feature->ActionRequest = feature->Installed;
1953                 }
1954             }
1955         }
1956     }
1957
1958     /* now we want to set component state based based on feature state */
1959     LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1960     {
1961         ComponentList *cl;
1962
1963         TRACE("Examining Feature %s (Level %d Installed %d Request %d Action %d)\n",
1964               debugstr_w(feature->Feature), feature->Level, feature->Installed,
1965               feature->ActionRequest, feature->Action);
1966
1967         if (!is_feature_selected( feature, level )) continue;
1968
1969         /* features with components that have compressed files are made local */
1970         LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1971         {
1972             if (cl->component->ForceLocalState &&
1973                 feature->ActionRequest == INSTALLSTATE_SOURCE)
1974             {
1975                 feature->Action = INSTALLSTATE_LOCAL;
1976                 feature->ActionRequest = INSTALLSTATE_LOCAL;
1977                 break;
1978             }
1979         }
1980
1981         LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1982         {
1983             component = cl->component;
1984
1985             switch (feature->ActionRequest)
1986             {
1987             case INSTALLSTATE_ABSENT:
1988                 component->anyAbsent = 1;
1989                 break;
1990             case INSTALLSTATE_ADVERTISED:
1991                 component->hasAdvertiseFeature = 1;
1992                 break;
1993             case INSTALLSTATE_SOURCE:
1994                 component->hasSourceFeature = 1;
1995                 break;
1996             case INSTALLSTATE_LOCAL:
1997                 component->hasLocalFeature = 1;
1998                 break;
1999             case INSTALLSTATE_DEFAULT:
2000                 if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
2001                     component->hasAdvertiseFeature = 1;
2002                 else if (feature->Attributes & msidbFeatureAttributesFavorSource)
2003                     component->hasSourceFeature = 1;
2004                 else
2005                     component->hasLocalFeature = 1;
2006                 break;
2007             default:
2008                 break;
2009             }
2010         }
2011     }
2012
2013     LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
2014     {
2015         /* check if it's local or source */
2016         if (!(component->Attributes & msidbComponentAttributesOptional) &&
2017              (component->hasLocalFeature || component->hasSourceFeature))
2018         {
2019             if ((component->Attributes & msidbComponentAttributesSourceOnly) &&
2020                  !component->ForceLocalState)
2021             {
2022                 component->Action = INSTALLSTATE_SOURCE;
2023                 component->ActionRequest = INSTALLSTATE_SOURCE;
2024             }
2025             else
2026             {
2027                 component->Action = INSTALLSTATE_LOCAL;
2028                 component->ActionRequest = INSTALLSTATE_LOCAL;
2029             }
2030             continue;
2031         }
2032
2033         /* if any feature is local, the component must be local too */
2034         if (component->hasLocalFeature)
2035         {
2036             component->Action = INSTALLSTATE_LOCAL;
2037             component->ActionRequest = INSTALLSTATE_LOCAL;
2038             continue;
2039         }
2040         if (component->hasSourceFeature)
2041         {
2042             component->Action = INSTALLSTATE_SOURCE;
2043             component->ActionRequest = INSTALLSTATE_SOURCE;
2044             continue;
2045         }
2046         if (component->hasAdvertiseFeature)
2047         {
2048             component->Action = INSTALLSTATE_ADVERTISED;
2049             component->ActionRequest = INSTALLSTATE_ADVERTISED;
2050             continue;
2051         }
2052         TRACE("nobody wants component %s\n", debugstr_w(component->Component));
2053         if (component->anyAbsent &&
2054             (component->Installed == INSTALLSTATE_LOCAL || component->Installed == INSTALLSTATE_SOURCE))
2055         {
2056             component->Action = INSTALLSTATE_ABSENT;
2057             component->ActionRequest = INSTALLSTATE_ABSENT;
2058         }
2059     }
2060
2061     LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
2062     {
2063         if (component->ActionRequest == INSTALLSTATE_DEFAULT)
2064         {
2065             TRACE("%s was default, setting to local\n", debugstr_w(component->Component));
2066             component->Action = INSTALLSTATE_LOCAL;
2067             component->ActionRequest = INSTALLSTATE_LOCAL;
2068         }
2069
2070         if (component->ActionRequest == INSTALLSTATE_SOURCE &&
2071             component->Installed == INSTALLSTATE_SOURCE &&
2072             component->hasSourceFeature)
2073         {
2074             component->Action = INSTALLSTATE_UNKNOWN;
2075             component->ActionRequest = INSTALLSTATE_UNKNOWN;
2076         }
2077
2078         TRACE("Result: Component %s (Installed %d Request %d Action %d)\n",
2079               debugstr_w(component->Component), component->Installed, component->ActionRequest, component->Action);
2080     }
2081
2082     return ERROR_SUCCESS;
2083 }
2084
2085 static UINT ITERATE_CostFinalizeDirectories(MSIRECORD *row, LPVOID param)
2086 {
2087     MSIPACKAGE *package = param;
2088     LPCWSTR name;
2089     LPWSTR path;
2090     MSIFOLDER *f;
2091
2092     name = MSI_RecordGetString(row,1);
2093
2094     f = get_loaded_folder(package, name);
2095     if (!f) return ERROR_SUCCESS;
2096
2097     /* reset the ResolvedTarget */
2098     msi_free(f->ResolvedTarget);
2099     f->ResolvedTarget = NULL;
2100
2101     TRACE("directory %s ...\n", debugstr_w(name));
2102     path = resolve_target_folder( package, name, TRUE, TRUE, NULL );
2103     TRACE("resolves to %s\n", debugstr_w(path));
2104     msi_free(path);
2105
2106     return ERROR_SUCCESS;
2107 }
2108
2109 static UINT ITERATE_CostFinalizeConditions(MSIRECORD *row, LPVOID param)
2110 {
2111     MSIPACKAGE *package = param;
2112     LPCWSTR name;
2113     MSIFEATURE *feature;
2114
2115     name = MSI_RecordGetString( row, 1 );
2116
2117     feature = get_loaded_feature( package, name );
2118     if (!feature)
2119         ERR("FAILED to find loaded feature %s\n",debugstr_w(name));
2120     else
2121     {
2122         LPCWSTR Condition;
2123         Condition = MSI_RecordGetString(row,3);
2124
2125         if (MSI_EvaluateConditionW(package,Condition) == MSICONDITION_TRUE)
2126         {
2127             int level = MSI_RecordGetInteger(row,2);
2128             TRACE("Resetting feature %s to level %i\n", debugstr_w(name), level);
2129             feature->Level = level;
2130         }
2131     }
2132     return ERROR_SUCCESS;
2133 }
2134
2135 VS_FIXEDFILEINFO *msi_get_disk_file_version( LPCWSTR filename )
2136 {
2137     static const WCHAR name[] = {'\\',0};
2138     VS_FIXEDFILEINFO *ptr, *ret;
2139     LPVOID version;
2140     DWORD versize, handle;
2141     UINT sz;
2142
2143     TRACE("%s\n", debugstr_w(filename));
2144
2145     versize = GetFileVersionInfoSizeW( filename, &handle );
2146     if (!versize)
2147         return NULL;
2148
2149     version = msi_alloc( versize );
2150     if (!version)
2151         return NULL;
2152
2153     GetFileVersionInfoW( filename, 0, versize, version );
2154
2155     if (!VerQueryValueW( version, name, (LPVOID *)&ptr, &sz ))
2156     {
2157         msi_free( version );
2158         return NULL;
2159     }
2160
2161     ret = msi_alloc( sz );
2162     memcpy( ret, ptr, sz );
2163
2164     msi_free( version );
2165     return ret;
2166 }
2167
2168 int msi_compare_file_versions( VS_FIXEDFILEINFO *fi, const WCHAR *version )
2169 {
2170     DWORD ms, ls;
2171
2172     msi_parse_version_string( version, &ms, &ls );
2173
2174     if (fi->dwFileVersionMS > ms) return 1;
2175     else if (fi->dwFileVersionMS < ms) return -1;
2176     else if (fi->dwFileVersionLS > ls) return 1;
2177     else if (fi->dwFileVersionLS < ls) return -1;
2178     return 0;
2179 }
2180
2181 int msi_compare_font_versions( const WCHAR *ver1, const WCHAR *ver2 )
2182 {
2183     DWORD ms1, ms2;
2184
2185     msi_parse_version_string( ver1, &ms1, NULL );
2186     msi_parse_version_string( ver2, &ms2, NULL );
2187
2188     if (ms1 > ms2) return 1;
2189     else if (ms1 < ms2) return -1;
2190     return 0;
2191 }
2192
2193 DWORD msi_get_disk_file_size( LPCWSTR filename )
2194 {
2195     HANDLE file;
2196     DWORD size;
2197
2198     TRACE("%s\n", debugstr_w(filename));
2199
2200     file = CreateFileW( filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL );
2201     if (file == INVALID_HANDLE_VALUE)
2202         return INVALID_FILE_SIZE;
2203
2204     size = GetFileSize( file, NULL );
2205     CloseHandle( file );
2206     return size;
2207 }
2208
2209 BOOL msi_file_hash_matches( MSIFILE *file )
2210 {
2211     UINT r;
2212     MSIFILEHASHINFO hash;
2213
2214     hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
2215     r = MsiGetFileHashW( file->TargetPath, 0, &hash );
2216     if (r != ERROR_SUCCESS)
2217         return FALSE;
2218
2219     return !memcmp( &hash, &file->hash, sizeof(MSIFILEHASHINFO) );
2220 }
2221
2222 static WCHAR *get_temp_dir( void )
2223 {
2224     static UINT id;
2225     WCHAR tmp[MAX_PATH], dir[MAX_PATH];
2226
2227     GetTempPathW( MAX_PATH, tmp );
2228     for (;;)
2229     {
2230         if (!GetTempFileNameW( tmp, szMsi, ++id, dir )) return NULL;
2231         if (CreateDirectoryW( dir, NULL )) break;
2232     }
2233     return strdupW( dir );
2234 }
2235
2236 static void set_target_path( MSIPACKAGE *package, MSIFILE *file )
2237 {
2238     MSIASSEMBLY *assembly = file->Component->assembly;
2239
2240     TRACE("file %s is named %s\n", debugstr_w(file->File), debugstr_w(file->FileName));
2241
2242     msi_free( file->TargetPath );
2243     if (assembly && !assembly->application)
2244     {
2245         if (!assembly->tempdir) assembly->tempdir = get_temp_dir();
2246         file->TargetPath = build_directory_name( 2, assembly->tempdir, file->FileName );
2247         track_tempfile( package, file->TargetPath );
2248     }
2249     else
2250     {
2251         WCHAR *dir = resolve_target_folder( package, file->Component->Directory, FALSE, TRUE, NULL );
2252         file->TargetPath = build_directory_name( 2, dir, file->FileName );
2253         msi_free( dir );
2254     }
2255
2256     TRACE("resolves to %s\n", debugstr_w(file->TargetPath));
2257 }
2258
2259 static UINT calculate_file_cost( MSIPACKAGE *package )
2260 {
2261     VS_FIXEDFILEINFO *file_version;
2262     WCHAR *font_version;
2263     MSIFILE *file;
2264
2265     LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2266     {
2267         MSICOMPONENT *comp = file->Component;
2268         DWORD file_size;
2269
2270         if (!comp->Enabled) continue;
2271
2272         if (file->IsCompressed)
2273             comp->ForceLocalState = TRUE;
2274
2275         set_target_path( package, file );
2276
2277         if ((comp->assembly && !comp->assembly->installed) ||
2278             GetFileAttributesW(file->TargetPath) == INVALID_FILE_ATTRIBUTES)
2279         {
2280             comp->Cost += file->FileSize;
2281             continue;
2282         }
2283         file_size = msi_get_disk_file_size( file->TargetPath );
2284
2285         if (file->Version)
2286         {
2287             if ((file_version = msi_get_disk_file_version( file->TargetPath )))
2288             {
2289                 if (msi_compare_file_versions( file_version, file->Version ) < 0)
2290                 {
2291                     comp->Cost += file->FileSize - file_size;
2292                 }
2293                 msi_free( file_version );
2294                 continue;
2295             }
2296             else if ((font_version = font_version_from_file( file->TargetPath )))
2297             {
2298                 if (msi_compare_font_versions( font_version, file->Version ) < 0)
2299                 {
2300                     comp->Cost += file->FileSize - file_size;
2301                 }
2302                 msi_free( font_version );
2303                 continue;
2304             }
2305         }
2306         if (file_size != file->FileSize)
2307         {
2308             comp->Cost += file->FileSize - file_size;
2309         }
2310     }
2311     return ERROR_SUCCESS;
2312 }
2313
2314 /*
2315  * A lot is done in this function aside from just the costing.
2316  * The costing needs to be implemented at some point but for now I am going
2317  * to focus on the directory building
2318  *
2319  */
2320 static UINT ACTION_CostFinalize(MSIPACKAGE *package)
2321 {
2322     static const WCHAR ExecSeqQuery[] =
2323         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2324          '`','D','i','r','e','c','t','o','r','y','`',0};
2325     static const WCHAR ConditionQuery[] =
2326         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2327          '`','C','o','n','d','i','t','i','o','n','`',0};
2328     static const WCHAR szCosting[] =
2329         {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
2330     static const WCHAR szlevel[] =
2331         {'I','N','S','T','A','L','L','L','E','V','E','L',0};
2332     static const WCHAR szOutOfDiskSpace[] =
2333         {'O','u','t','O','f','D','i','s','k','S','p','a','c','e',0};
2334     MSICOMPONENT *comp;
2335     UINT rc = ERROR_SUCCESS;
2336     MSIQUERY * view;
2337     LPWSTR level;
2338
2339     TRACE("Building Directory properties\n");
2340
2341     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2342     if (rc == ERROR_SUCCESS)
2343     {
2344         rc = MSI_IterateRecords(view, NULL, ITERATE_CostFinalizeDirectories,
2345                         package);
2346         msiobj_release(&view->hdr);
2347     }
2348
2349     TRACE("Evaluating component conditions\n");
2350     LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2351     {
2352         if (MSI_EvaluateConditionW( package, comp->Condition ) == MSICONDITION_FALSE)
2353         {
2354             TRACE("Disabling component %s\n", debugstr_w(comp->Component));
2355             comp->Enabled = FALSE;
2356         }
2357         else
2358             comp->Enabled = TRUE;
2359     }
2360
2361     /* read components states from the registry */
2362     ACTION_GetComponentInstallStates(package);
2363     ACTION_GetFeatureInstallStates(package);
2364
2365     if (!process_overrides( package, msi_get_property_int( package->db, szlevel, 1 ) ))
2366     {
2367         TRACE("Evaluating feature conditions\n");
2368
2369         rc = MSI_DatabaseOpenViewW( package->db, ConditionQuery, &view );
2370         if (rc == ERROR_SUCCESS)
2371         {
2372             rc = MSI_IterateRecords( view, NULL, ITERATE_CostFinalizeConditions, package );
2373             msiobj_release( &view->hdr );
2374         }
2375     }
2376
2377     TRACE("Calculating file cost\n");
2378     calculate_file_cost( package );
2379
2380     msi_set_property( package->db, szCosting, szOne );
2381     /* set default run level if not set */
2382     level = msi_dup_property( package->db, szlevel );
2383     if (!level)
2384         msi_set_property( package->db, szlevel, szOne );
2385     msi_free(level);
2386
2387     /* FIXME: check volume disk space */
2388     msi_set_property( package->db, szOutOfDiskSpace, szZero );
2389
2390     return MSI_SetFeatureStates(package);
2391 }
2392
2393 /* OK this value is "interpreted" and then formatted based on the 
2394    first few characters */
2395 static LPSTR parse_value(MSIPACKAGE *package, LPCWSTR value, DWORD *type, 
2396                          DWORD *size)
2397 {
2398     LPSTR data = NULL;
2399
2400     if (value[0]=='#' && value[1]!='#' && value[1]!='%')
2401     {
2402         if (value[1]=='x')
2403         {
2404             LPWSTR ptr;
2405             CHAR byte[5];
2406             LPWSTR deformated = NULL;
2407             int count;
2408
2409             deformat_string(package, &value[2], &deformated);
2410
2411             /* binary value type */
2412             ptr = deformated;
2413             *type = REG_BINARY;
2414             if (strlenW(ptr)%2)
2415                 *size = (strlenW(ptr)/2)+1;
2416             else
2417                 *size = strlenW(ptr)/2;
2418
2419             data = msi_alloc(*size);
2420
2421             byte[0] = '0'; 
2422             byte[1] = 'x'; 
2423             byte[4] = 0; 
2424             count = 0;
2425             /* if uneven pad with a zero in front */
2426             if (strlenW(ptr)%2)
2427             {
2428                 byte[2]= '0';
2429                 byte[3]= *ptr;
2430                 ptr++;
2431                 data[count] = (BYTE)strtol(byte,NULL,0);
2432                 count ++;
2433                 TRACE("Uneven byte count\n");
2434             }
2435             while (*ptr)
2436             {
2437                 byte[2]= *ptr;
2438                 ptr++;
2439                 byte[3]= *ptr;
2440                 ptr++;
2441                 data[count] = (BYTE)strtol(byte,NULL,0);
2442                 count ++;
2443             }
2444             msi_free(deformated);
2445
2446             TRACE("Data %i bytes(%i)\n",*size,count);
2447         }
2448         else
2449         {
2450             LPWSTR deformated;
2451             LPWSTR p;
2452             DWORD d = 0;
2453             deformat_string(package, &value[1], &deformated);
2454
2455             *type=REG_DWORD; 
2456             *size = sizeof(DWORD);
2457             data = msi_alloc(*size);
2458             p = deformated;
2459             if (*p == '-')
2460                 p++;
2461             while (*p)
2462             {
2463                 if ( (*p < '0') || (*p > '9') )
2464                     break;
2465                 d *= 10;
2466                 d += (*p - '0');
2467                 p++;
2468             }
2469             if (deformated[0] == '-')
2470                 d = -d;
2471             *(LPDWORD)data = d;
2472             TRACE("DWORD %i\n",*(LPDWORD)data);
2473
2474             msi_free(deformated);
2475         }
2476     }
2477     else
2478     {
2479         static const WCHAR szMulti[] = {'[','~',']',0};
2480         LPCWSTR ptr;
2481         *type=REG_SZ;
2482
2483         if (value[0]=='#')
2484         {
2485             if (value[1]=='%')
2486             {
2487                 ptr = &value[2];
2488                 *type=REG_EXPAND_SZ;
2489             }
2490             else
2491                 ptr = &value[1];
2492          }
2493          else
2494             ptr=value;
2495
2496         if (strstrW(value, szMulti))
2497             *type = REG_MULTI_SZ;
2498
2499         /* remove initial delimiter */
2500         if (!strncmpW(value, szMulti, 3))
2501             ptr = value + 3;
2502
2503         *size = deformat_string(package, ptr,(LPWSTR*)&data);
2504
2505         /* add double NULL terminator */
2506         if (*type == REG_MULTI_SZ)
2507         {
2508             *size += 2 * sizeof(WCHAR); /* two NULL terminators */
2509             data = msi_realloc_zero(data, *size);
2510         }
2511     }
2512     return data;
2513 }
2514
2515 static const WCHAR *get_root_key( MSIPACKAGE *package, INT root, HKEY *root_key )
2516 {
2517     const WCHAR *ret;
2518
2519     switch (root)
2520     {
2521     case -1:
2522         if (msi_get_property_int( package->db, szAllUsers, 0 ))
2523         {
2524             *root_key = HKEY_LOCAL_MACHINE;
2525             ret = szHLM;
2526         }
2527         else
2528         {
2529             *root_key = HKEY_CURRENT_USER;
2530             ret = szHCU;
2531         }
2532         break;
2533     case 0:
2534         *root_key = HKEY_CLASSES_ROOT;
2535         ret = szHCR;
2536         break;
2537     case 1:
2538         *root_key = HKEY_CURRENT_USER;
2539         ret = szHCU;
2540         break;
2541     case 2:
2542         *root_key = HKEY_LOCAL_MACHINE;
2543         ret = szHLM;
2544         break;
2545     case 3:
2546         *root_key = HKEY_USERS;
2547         ret = szHU;
2548         break;
2549     default:
2550         ERR("Unknown root %i\n", root);
2551         return NULL;
2552     }
2553
2554     return ret;
2555 }
2556
2557 static WCHAR *get_keypath( MSIPACKAGE *package, HKEY root, const WCHAR *path )
2558 {
2559     static const WCHAR prefixW[] = {'S','O','F','T','W','A','R','E','\\'};
2560     static const UINT len = sizeof(prefixW) / sizeof(prefixW[0]);
2561
2562     if (is_64bit && package->platform == PLATFORM_INTEL &&
2563         root == HKEY_LOCAL_MACHINE && !strncmpiW( path, prefixW, len ))
2564     {
2565         UINT size;
2566         WCHAR *path_32node;
2567
2568         size = (strlenW( path ) + strlenW( szWow6432Node ) + 1) * sizeof(WCHAR);
2569         path_32node = msi_alloc( size );
2570         if (!path_32node)
2571             return NULL;
2572
2573         memcpy( path_32node, path, len * sizeof(WCHAR) );
2574         path_32node[len] = 0;
2575         strcatW( path_32node, szWow6432Node );
2576         strcatW( path_32node, szBackSlash );
2577         strcatW( path_32node, path + len );
2578         return path_32node;
2579     }
2580
2581     return strdupW( path );
2582 }
2583
2584 static UINT ITERATE_WriteRegistryValues(MSIRECORD *row, LPVOID param)
2585 {
2586     MSIPACKAGE *package = param;
2587     LPSTR value_data = NULL;
2588     HKEY  root_key, hkey;
2589     DWORD type,size;
2590     LPWSTR deformated, uikey, keypath;
2591     LPCWSTR szRoot, component, name, key, value;
2592     MSICOMPONENT *comp;
2593     MSIRECORD * uirow;
2594     INT   root;
2595     BOOL check_first = FALSE;
2596     UINT rc;
2597
2598     ui_progress(package,2,0,0,0);
2599
2600     component = MSI_RecordGetString(row, 6);
2601     comp = get_loaded_component(package,component);
2602     if (!comp)
2603         return ERROR_SUCCESS;
2604
2605     if (!comp->Enabled)
2606     {
2607         TRACE("component is disabled\n");
2608         return ERROR_SUCCESS;
2609     }
2610
2611     if (comp->ActionRequest != INSTALLSTATE_LOCAL)
2612     {
2613         TRACE("Component not scheduled for installation: %s\n", debugstr_w(component));
2614         comp->Action = comp->Installed;
2615         return ERROR_SUCCESS;
2616     }
2617     comp->Action = INSTALLSTATE_LOCAL;
2618
2619     name = MSI_RecordGetString(row, 4);
2620     if( MSI_RecordIsNull(row,5) && name )
2621     {
2622         /* null values can have special meanings */
2623         if (name[0]=='-' && name[1] == 0)
2624                 return ERROR_SUCCESS;
2625         else if ((name[0]=='+' && name[1] == 0) || 
2626                  (name[0] == '*' && name[1] == 0))
2627                 name = NULL;
2628         check_first = TRUE;
2629     }
2630
2631     root = MSI_RecordGetInteger(row,2);
2632     key = MSI_RecordGetString(row, 3);
2633
2634     szRoot = get_root_key( package, root, &root_key );
2635     if (!szRoot)
2636         return ERROR_SUCCESS;
2637
2638     deformat_string(package, key , &deformated);
2639     size = strlenW(deformated) + strlenW(szRoot) + 1;
2640     uikey = msi_alloc(size*sizeof(WCHAR));
2641     strcpyW(uikey,szRoot);
2642     strcatW(uikey,deformated);
2643
2644     keypath = get_keypath( package, root_key, deformated );
2645     msi_free( deformated );
2646     if (RegCreateKeyW( root_key, keypath, &hkey ))
2647     {
2648         ERR("Could not create key %s\n", debugstr_w(keypath));
2649         msi_free(uikey);
2650         msi_free(keypath);
2651         return ERROR_SUCCESS;
2652     }
2653
2654     value = MSI_RecordGetString(row,5);
2655     if (value)
2656         value_data = parse_value(package, value, &type, &size); 
2657     else
2658     {
2659         value_data = (LPSTR)strdupW(szEmpty);
2660         size = sizeof(szEmpty);
2661         type = REG_SZ;
2662     }
2663
2664     deformat_string(package, name, &deformated);
2665
2666     if (!check_first)
2667     {
2668         TRACE("Setting value %s of %s\n",debugstr_w(deformated),
2669                         debugstr_w(uikey));
2670         RegSetValueExW(hkey, deformated, 0, type, (LPBYTE)value_data, size);
2671     }
2672     else
2673     {
2674         DWORD sz = 0;
2675         rc = RegQueryValueExW(hkey, deformated, NULL, NULL, NULL, &sz);
2676         if (rc == ERROR_SUCCESS || rc == ERROR_MORE_DATA)
2677         {
2678             TRACE("value %s of %s checked already exists\n",
2679                             debugstr_w(deformated), debugstr_w(uikey));
2680         }
2681         else
2682         {
2683             TRACE("Checked and setting value %s of %s\n",
2684                             debugstr_w(deformated), debugstr_w(uikey));
2685             if (deformated || size)
2686                 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE) value_data, size);
2687         }
2688     }
2689     RegCloseKey(hkey);
2690
2691     uirow = MSI_CreateRecord(3);
2692     MSI_RecordSetStringW(uirow,2,deformated);
2693     MSI_RecordSetStringW(uirow,1,uikey);
2694     if (type == REG_SZ || type == REG_EXPAND_SZ)
2695         MSI_RecordSetStringW(uirow,3,(LPWSTR)value_data);
2696     ui_actiondata(package,szWriteRegistryValues,uirow);
2697     msiobj_release( &uirow->hdr );
2698
2699     msi_free(value_data);
2700     msi_free(deformated);
2701     msi_free(uikey);
2702     msi_free(keypath);
2703
2704     return ERROR_SUCCESS;
2705 }
2706
2707 static UINT ACTION_WriteRegistryValues(MSIPACKAGE *package)
2708 {
2709     UINT rc;
2710     MSIQUERY * view;
2711     static const WCHAR ExecSeqQuery[] =
2712         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2713          '`','R','e','g','i','s','t','r','y','`',0 };
2714
2715     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2716     if (rc != ERROR_SUCCESS)
2717         return ERROR_SUCCESS;
2718
2719     /* increment progress bar each time action data is sent */
2720     ui_progress(package,1,REG_PROGRESS_VALUE,1,0);
2721
2722     rc = MSI_IterateRecords(view, NULL, ITERATE_WriteRegistryValues, package);
2723
2724     msiobj_release(&view->hdr);
2725     return rc;
2726 }
2727
2728 static void delete_reg_key_or_value( HKEY hkey_root, LPCWSTR key, LPCWSTR value, BOOL delete_key )
2729 {
2730     LONG res;
2731     HKEY hkey;
2732     DWORD num_subkeys, num_values;
2733
2734     if (delete_key)
2735     {
2736         if ((res = RegDeleteTreeW( hkey_root, key )))
2737         {
2738             TRACE("Failed to delete key %s (%d)\n", debugstr_w(key), res);
2739         }
2740         return;
2741     }
2742
2743     if (!(res = RegOpenKeyW( hkey_root, key, &hkey )))
2744     {
2745         if ((res = RegDeleteValueW( hkey, value )))
2746         {
2747             TRACE("Failed to delete value %s (%d)\n", debugstr_w(value), res);
2748         }
2749         res = RegQueryInfoKeyW( hkey, NULL, NULL, NULL, &num_subkeys, NULL, NULL, &num_values,
2750                                 NULL, NULL, NULL, NULL );
2751         RegCloseKey( hkey );
2752         if (!res && !num_subkeys && !num_values)
2753         {
2754             TRACE("Removing empty key %s\n", debugstr_w(key));
2755             RegDeleteKeyW( hkey_root, key );
2756         }
2757         return;
2758     }
2759     TRACE("Failed to open key %s (%d)\n", debugstr_w(key), res);
2760 }
2761
2762
2763 static UINT ITERATE_RemoveRegistryValuesOnUninstall( MSIRECORD *row, LPVOID param )
2764 {
2765     MSIPACKAGE *package = param;
2766     LPCWSTR component, name, key_str, root_key_str;
2767     LPWSTR deformated_key, deformated_name, ui_key_str, keypath;
2768     MSICOMPONENT *comp;
2769     MSIRECORD *uirow;
2770     BOOL delete_key = FALSE;
2771     HKEY hkey_root;
2772     UINT size;
2773     INT root;
2774
2775     ui_progress( package, 2, 0, 0, 0 );
2776
2777     component = MSI_RecordGetString( row, 6 );
2778     comp = get_loaded_component( package, component );
2779     if (!comp)
2780         return ERROR_SUCCESS;
2781
2782     if (!comp->Enabled)
2783     {
2784         TRACE("component is disabled\n");
2785         return ERROR_SUCCESS;
2786     }
2787
2788     if (comp->ActionRequest != INSTALLSTATE_ABSENT)
2789     {
2790         TRACE("Component not scheduled for removal: %s\n", debugstr_w(component));
2791         comp->Action = comp->Installed;
2792         return ERROR_SUCCESS;
2793     }
2794     comp->Action = INSTALLSTATE_ABSENT;
2795
2796     name = MSI_RecordGetString( row, 4 );
2797     if (MSI_RecordIsNull( row, 5 ) && name )
2798     {
2799         if (name[0] == '+' && !name[1])
2800             return ERROR_SUCCESS;
2801         else if ((name[0] == '-' && !name[1]) || (name[0] == '*' && !name[1]))
2802         {
2803             delete_key = TRUE;
2804             name = NULL;
2805         }
2806     }
2807
2808     root = MSI_RecordGetInteger( row, 2 );
2809     key_str = MSI_RecordGetString( row, 3 );
2810
2811     root_key_str = get_root_key( package, root, &hkey_root );
2812     if (!root_key_str)
2813         return ERROR_SUCCESS;
2814
2815     deformat_string( package, key_str, &deformated_key );
2816     size = strlenW( deformated_key ) + strlenW( root_key_str ) + 1;
2817     ui_key_str = msi_alloc( size * sizeof(WCHAR) );
2818     strcpyW( ui_key_str, root_key_str );
2819     strcatW( ui_key_str, deformated_key );
2820
2821     deformat_string( package, name, &deformated_name );
2822
2823     keypath = get_keypath( package, hkey_root, deformated_key );
2824     msi_free( deformated_key );
2825     delete_reg_key_or_value( hkey_root, keypath, deformated_name, delete_key );
2826     msi_free( keypath );
2827
2828     uirow = MSI_CreateRecord( 2 );
2829     MSI_RecordSetStringW( uirow, 1, ui_key_str );
2830     MSI_RecordSetStringW( uirow, 2, deformated_name );
2831
2832     ui_actiondata( package, szRemoveRegistryValues, uirow );
2833     msiobj_release( &uirow->hdr );
2834
2835     msi_free( ui_key_str );
2836     msi_free( deformated_name );
2837     return ERROR_SUCCESS;
2838 }
2839
2840 static UINT ITERATE_RemoveRegistryValuesOnInstall( MSIRECORD *row, LPVOID param )
2841 {
2842     MSIPACKAGE *package = param;
2843     LPCWSTR component, name, key_str, root_key_str;
2844     LPWSTR deformated_key, deformated_name, ui_key_str, keypath;
2845     MSICOMPONENT *comp;
2846     MSIRECORD *uirow;
2847     BOOL delete_key = FALSE;
2848     HKEY hkey_root;
2849     UINT size;
2850     INT root;
2851
2852     ui_progress( package, 2, 0, 0, 0 );
2853
2854     component = MSI_RecordGetString( row, 5 );
2855     comp = get_loaded_component( package, component );
2856     if (!comp)
2857         return ERROR_SUCCESS;
2858
2859     if (!comp->Enabled)
2860     {
2861         TRACE("component is disabled\n");
2862         return ERROR_SUCCESS;
2863     }
2864
2865     if (comp->ActionRequest != INSTALLSTATE_LOCAL)
2866     {
2867         TRACE("Component not scheduled for installation: %s\n", debugstr_w(component));
2868         comp->Action = comp->Installed;
2869         return ERROR_SUCCESS;
2870     }
2871     comp->Action = INSTALLSTATE_LOCAL;
2872
2873     if ((name = MSI_RecordGetString( row, 4 )))
2874     {
2875         if (name[0] == '-' && !name[1])
2876         {
2877             delete_key = TRUE;
2878             name = NULL;
2879         }
2880     }
2881
2882     root = MSI_RecordGetInteger( row, 2 );
2883     key_str = MSI_RecordGetString( row, 3 );
2884
2885     root_key_str = get_root_key( package, root, &hkey_root );
2886     if (!root_key_str)
2887         return ERROR_SUCCESS;
2888
2889     deformat_string( package, key_str, &deformated_key );
2890     size = strlenW( deformated_key ) + strlenW( root_key_str ) + 1;
2891     ui_key_str = msi_alloc( size * sizeof(WCHAR) );
2892     strcpyW( ui_key_str, root_key_str );
2893     strcatW( ui_key_str, deformated_key );
2894
2895     deformat_string( package, name, &deformated_name );
2896
2897     keypath = get_keypath( package, hkey_root, deformated_key );
2898     msi_free( deformated_key );
2899     delete_reg_key_or_value( hkey_root, keypath, deformated_name, delete_key );
2900     msi_free( keypath );
2901
2902     uirow = MSI_CreateRecord( 2 );
2903     MSI_RecordSetStringW( uirow, 1, ui_key_str );
2904     MSI_RecordSetStringW( uirow, 2, deformated_name );
2905
2906     ui_actiondata( package, szRemoveRegistryValues, uirow );
2907     msiobj_release( &uirow->hdr );
2908
2909     msi_free( ui_key_str );
2910     msi_free( deformated_name );
2911     return ERROR_SUCCESS;
2912 }
2913
2914 static UINT ACTION_RemoveRegistryValues( MSIPACKAGE *package )
2915 {
2916     UINT rc;
2917     MSIQUERY *view;
2918     static const WCHAR registry_query[] =
2919         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2920          '`','R','e','g','i','s','t','r','y','`',0 };
2921     static const WCHAR remove_registry_query[] =
2922         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2923          '`','R','e','m','o','v','e','R','e','g','i','s','t','r','y','`',0 };
2924
2925     /* increment progress bar each time action data is sent */
2926     ui_progress( package, 1, REG_PROGRESS_VALUE, 1, 0 );
2927
2928     rc = MSI_DatabaseOpenViewW( package->db, registry_query, &view );
2929     if (rc == ERROR_SUCCESS)
2930     {
2931         rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveRegistryValuesOnUninstall, package );
2932         msiobj_release( &view->hdr );
2933         if (rc != ERROR_SUCCESS)
2934             return rc;
2935     }
2936
2937     rc = MSI_DatabaseOpenViewW( package->db, remove_registry_query, &view );
2938     if (rc == ERROR_SUCCESS)
2939     {
2940         rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveRegistryValuesOnInstall, package );
2941         msiobj_release( &view->hdr );
2942         if (rc != ERROR_SUCCESS)
2943             return rc;
2944     }
2945
2946     return ERROR_SUCCESS;
2947 }
2948
2949 static UINT ACTION_InstallInitialize(MSIPACKAGE *package)
2950 {
2951     package->script->CurrentlyScripting = TRUE;
2952
2953     return ERROR_SUCCESS;
2954 }
2955
2956
2957 static UINT ACTION_InstallValidate(MSIPACKAGE *package)
2958 {
2959     MSICOMPONENT *comp;
2960     DWORD progress = 0;
2961     DWORD total = 0;
2962     static const WCHAR q1[]=
2963         {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
2964          '`','R','e','g','i','s','t','r','y','`',0};
2965     UINT rc;
2966     MSIQUERY * view;
2967     MSIFEATURE *feature;
2968     MSIFILE *file;
2969
2970     TRACE("InstallValidate\n");
2971
2972     rc = MSI_DatabaseOpenViewW(package->db, q1, &view);
2973     if (rc == ERROR_SUCCESS)
2974     {
2975         MSI_IterateRecords( view, &progress, NULL, package );
2976         msiobj_release( &view->hdr );
2977         total += progress * REG_PROGRESS_VALUE;
2978     }
2979
2980     LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2981         total += COMPONENT_PROGRESS_VALUE;
2982
2983     LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2984         total += file->FileSize;
2985
2986     ui_progress(package,0,total,0,0);
2987
2988     LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2989     {
2990         TRACE("Feature: %s Installed %d Request %d Action %d\n",
2991               debugstr_w(feature->Feature), feature->Installed,
2992               feature->ActionRequest, feature->Action);
2993     }
2994     
2995     return ERROR_SUCCESS;
2996 }
2997
2998 static UINT ITERATE_LaunchConditions(MSIRECORD *row, LPVOID param)
2999 {
3000     MSIPACKAGE* package = param;
3001     LPCWSTR cond = NULL; 
3002     LPCWSTR message = NULL;
3003     UINT r;
3004
3005     static const WCHAR title[]=
3006         {'I','n','s','t','a','l','l',' ','F','a', 'i','l','e','d',0};
3007
3008     cond = MSI_RecordGetString(row,1);
3009
3010     r = MSI_EvaluateConditionW(package,cond);
3011     if (r == MSICONDITION_FALSE)
3012     {
3013         if ((gUILevel & INSTALLUILEVEL_MASK) != INSTALLUILEVEL_NONE)
3014         {
3015             LPWSTR deformated;
3016             message = MSI_RecordGetString(row,2);
3017             deformat_string(package,message,&deformated);
3018             MessageBoxW(NULL,deformated,title,MB_OK);
3019             msi_free(deformated);
3020         }
3021
3022         return ERROR_INSTALL_FAILURE;
3023     }
3024
3025     return ERROR_SUCCESS;
3026 }
3027
3028 static UINT ACTION_LaunchConditions(MSIPACKAGE *package)
3029 {
3030     UINT rc;
3031     MSIQUERY * view = NULL;
3032     static const WCHAR ExecSeqQuery[] =
3033         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3034          '`','L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','`',0};
3035
3036     TRACE("Checking launch conditions\n");
3037
3038     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
3039     if (rc != ERROR_SUCCESS)
3040         return ERROR_SUCCESS;
3041
3042     rc = MSI_IterateRecords(view, NULL, ITERATE_LaunchConditions, package);
3043     msiobj_release(&view->hdr);
3044
3045     return rc;
3046 }
3047
3048 static LPWSTR resolve_keypath( MSIPACKAGE* package, MSICOMPONENT *cmp )
3049 {
3050
3051     if (!cmp->KeyPath)
3052         return resolve_target_folder( package, cmp->Directory, FALSE, TRUE, NULL );
3053
3054     if (cmp->Attributes & msidbComponentAttributesRegistryKeyPath)
3055     {
3056         MSIRECORD * row = 0;
3057         UINT root,len;
3058         LPWSTR deformated,buffer,deformated_name;
3059         LPCWSTR key,name;
3060         static const WCHAR ExecSeqQuery[] =
3061             {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3062              '`','R','e','g','i','s','t','r','y','`',' ',
3063              'W','H','E','R','E',' ', '`','R','e','g','i','s','t','r','y','`',
3064              ' ','=',' ' ,'\'','%','s','\'',0 };
3065         static const WCHAR fmt[]={'%','0','2','i',':','\\','%','s','\\',0};
3066         static const WCHAR fmt2[]=
3067             {'%','0','2','i',':','\\','%','s','\\','%','s',0};
3068
3069         row = MSI_QueryGetRecord(package->db, ExecSeqQuery,cmp->KeyPath);
3070         if (!row)
3071             return NULL;
3072
3073         root = MSI_RecordGetInteger(row,2);
3074         key = MSI_RecordGetString(row, 3);
3075         name = MSI_RecordGetString(row, 4);
3076         deformat_string(package, key , &deformated);
3077         deformat_string(package, name, &deformated_name);
3078
3079         len = strlenW(deformated) + 6;
3080         if (deformated_name)
3081             len+=strlenW(deformated_name);
3082
3083         buffer = msi_alloc( len *sizeof(WCHAR));
3084
3085         if (deformated_name)
3086             sprintfW(buffer,fmt2,root,deformated,deformated_name);
3087         else
3088             sprintfW(buffer,fmt,root,deformated);
3089
3090         msi_free(deformated);
3091         msi_free(deformated_name);
3092         msiobj_release(&row->hdr);
3093
3094         return buffer;
3095     }
3096     else if (cmp->Attributes & msidbComponentAttributesODBCDataSource)
3097     {
3098         FIXME("UNIMPLEMENTED keypath as ODBC Source\n");
3099         return NULL;
3100     }
3101     else
3102     {
3103         MSIFILE *file = get_loaded_file( package, cmp->KeyPath );
3104
3105         if (file)
3106             return strdupW( file->TargetPath );
3107     }
3108     return NULL;
3109 }
3110
3111 static HKEY openSharedDLLsKey(void)
3112 {
3113     HKEY hkey=0;
3114     static const WCHAR path[] =
3115         {'S','o','f','t','w','a','r','e','\\',
3116          'M','i','c','r','o','s','o','f','t','\\',
3117          'W','i','n','d','o','w','s','\\',
3118          'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
3119          'S','h','a','r','e','d','D','L','L','s',0};
3120
3121     RegCreateKeyW(HKEY_LOCAL_MACHINE,path,&hkey);
3122     return hkey;
3123 }
3124
3125 static UINT ACTION_GetSharedDLLsCount(LPCWSTR dll)
3126 {
3127     HKEY hkey;
3128     DWORD count=0;
3129     DWORD type;
3130     DWORD sz = sizeof(count);
3131     DWORD rc;
3132     
3133     hkey = openSharedDLLsKey();
3134     rc = RegQueryValueExW(hkey, dll, NULL, &type, (LPBYTE)&count, &sz);
3135     if (rc != ERROR_SUCCESS)
3136         count = 0;
3137     RegCloseKey(hkey);
3138     return count;
3139 }
3140
3141 static UINT ACTION_WriteSharedDLLsCount(LPCWSTR path, UINT count)
3142 {
3143     HKEY hkey;
3144
3145     hkey = openSharedDLLsKey();
3146     if (count > 0)
3147         msi_reg_set_val_dword( hkey, path, count );
3148     else
3149         RegDeleteValueW(hkey,path);
3150     RegCloseKey(hkey);
3151     return count;
3152 }
3153
3154 /*
3155  * Return TRUE if the count should be written out and FALSE if not
3156  */
3157 static void ACTION_RefCountComponent( MSIPACKAGE* package, MSICOMPONENT *comp )
3158 {
3159     MSIFEATURE *feature;
3160     INT count = 0;
3161     BOOL write = FALSE;
3162
3163     /* only refcount DLLs */
3164     if (comp->KeyPath == NULL || 
3165         comp->Attributes & msidbComponentAttributesRegistryKeyPath || 
3166         comp->Attributes & msidbComponentAttributesODBCDataSource)
3167         write = FALSE;
3168     else
3169     {
3170         count = ACTION_GetSharedDLLsCount( comp->FullKeypath);
3171         write = (count > 0);
3172
3173         if (comp->Attributes & msidbComponentAttributesSharedDllRefCount)
3174             write = TRUE;
3175     }
3176
3177     /* increment counts */
3178     LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3179     {
3180         ComponentList *cl;
3181
3182         if (feature->ActionRequest != INSTALLSTATE_LOCAL)
3183             continue;
3184
3185         LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3186         {
3187             if ( cl->component == comp )
3188                 count++;
3189         }
3190     }
3191
3192     /* decrement counts */
3193     LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3194     {
3195         ComponentList *cl;
3196
3197         if (feature->ActionRequest != INSTALLSTATE_ABSENT)
3198             continue;
3199
3200         LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3201         {
3202             if ( cl->component == comp )
3203                 count--;
3204         }
3205     }
3206
3207     /* ref count all the files in the component */
3208     if (write)
3209     {
3210         MSIFILE *file;
3211
3212         LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
3213         {
3214             if (file->Component == comp)
3215                 ACTION_WriteSharedDLLsCount( file->TargetPath, count );
3216         }
3217     }
3218     
3219     /* add a count for permanent */
3220     if (comp->Attributes & msidbComponentAttributesPermanent)
3221         count ++;
3222     
3223     comp->RefCount = count;
3224
3225     if (write)
3226         ACTION_WriteSharedDLLsCount( comp->FullKeypath, comp->RefCount );
3227 }
3228
3229 static UINT ACTION_ProcessComponents(MSIPACKAGE *package)
3230 {
3231     WCHAR squished_pc[GUID_SIZE];
3232     WCHAR squished_cc[GUID_SIZE];
3233     UINT rc;
3234     MSICOMPONENT *comp;
3235     HKEY hkey;
3236
3237     TRACE("\n");
3238
3239     squash_guid(package->ProductCode,squished_pc);
3240     ui_progress(package,1,COMPONENT_PROGRESS_VALUE,1,0);
3241
3242     msi_set_sourcedir_props(package, FALSE);
3243
3244     LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
3245     {
3246         MSIRECORD * uirow;
3247
3248         ui_progress(package,2,0,0,0);
3249         if (!comp->ComponentId)
3250             continue;
3251
3252         squash_guid(comp->ComponentId,squished_cc);
3253
3254         msi_free(comp->FullKeypath);
3255         comp->FullKeypath = resolve_keypath( package, comp );
3256
3257         ACTION_RefCountComponent( package, comp );
3258
3259         TRACE("Component %s (%s), Keypath=%s, RefCount=%i Request=%u\n",
3260                             debugstr_w(comp->Component),
3261                             debugstr_w(squished_cc),
3262                             debugstr_w(comp->FullKeypath),
3263                             comp->RefCount,
3264                             comp->ActionRequest);
3265
3266         if (comp->ActionRequest == INSTALLSTATE_LOCAL ||
3267             comp->ActionRequest == INSTALLSTATE_SOURCE)
3268         {
3269             if (!comp->FullKeypath)
3270                 continue;
3271
3272             if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3273                 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, szLocalSid,
3274                                                      &hkey, TRUE);
3275             else
3276                 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, NULL,
3277                                                      &hkey, TRUE);
3278
3279             if (rc != ERROR_SUCCESS)
3280                 continue;
3281
3282             if (comp->Attributes & msidbComponentAttributesPermanent)
3283             {
3284                 static const WCHAR szPermKey[] =
3285                     { '0','0','0','0','0','0','0','0','0','0','0','0',
3286                       '0','0','0','0','0','0','0','0','0','0','0','0',
3287                       '0','0','0','0','0','0','0','0',0 };
3288
3289                 msi_reg_set_val_str(hkey, szPermKey, comp->FullKeypath);
3290             }
3291
3292             if (comp->ActionRequest == INSTALLSTATE_LOCAL)
3293                 msi_reg_set_val_str(hkey, squished_pc, comp->FullKeypath);
3294             else
3295             {
3296                 MSIFILE *file;
3297                 MSIRECORD *row;
3298                 LPWSTR ptr, ptr2;
3299                 WCHAR source[MAX_PATH];
3300                 WCHAR base[MAX_PATH];
3301                 LPWSTR sourcepath;
3302
3303                 static const WCHAR fmt[] = {'%','0','2','d','\\',0};
3304                 static const WCHAR query[] = {
3305                     'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
3306                     '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
3307                     '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ',
3308                     '>','=',' ','%','i',' ','O','R','D','E','R',' ','B','Y',' ',
3309                     '`','D','i','s','k','I','d','`',0};
3310
3311                 if (!comp->KeyPath || !(file = get_loaded_file(package, comp->KeyPath)))
3312                     continue;
3313
3314                 row = MSI_QueryGetRecord(package->db, query, file->Sequence);
3315                 sprintfW(source, fmt, MSI_RecordGetInteger(row, 1));
3316                 ptr2 = strrchrW(source, '\\') + 1;
3317                 msiobj_release(&row->hdr);
3318
3319                 lstrcpyW(base, package->PackagePath);
3320                 ptr = strrchrW(base, '\\');
3321                 *(ptr + 1) = '\0';
3322
3323                 sourcepath = resolve_file_source(package, file);
3324                 ptr = sourcepath + lstrlenW(base);
3325                 lstrcpyW(ptr2, ptr);
3326                 msi_free(sourcepath);
3327
3328                 msi_reg_set_val_str(hkey, squished_pc, source);
3329             }
3330             RegCloseKey(hkey);
3331         }
3332         else if (comp->ActionRequest == INSTALLSTATE_ABSENT)
3333         {
3334             if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3335                 MSIREG_DeleteUserDataComponentKey(comp->ComponentId, szLocalSid);
3336             else
3337                 MSIREG_DeleteUserDataComponentKey(comp->ComponentId, NULL);
3338         }
3339         comp->Action = comp->ActionRequest;
3340
3341         /* UI stuff */
3342         uirow = MSI_CreateRecord(3);
3343         MSI_RecordSetStringW(uirow,1,package->ProductCode);
3344         MSI_RecordSetStringW(uirow,2,comp->ComponentId);
3345         MSI_RecordSetStringW(uirow,3,comp->FullKeypath);
3346         ui_actiondata(package,szProcessComponents,uirow);
3347         msiobj_release( &uirow->hdr );
3348     }
3349
3350     return ERROR_SUCCESS;
3351 }
3352
3353 typedef struct {
3354     CLSID       clsid;
3355     LPWSTR      source;
3356
3357     LPWSTR      path;
3358     ITypeLib    *ptLib;
3359 } typelib_struct;
3360
3361 static BOOL CALLBACK Typelib_EnumResNameProc( HMODULE hModule, LPCWSTR lpszType, 
3362                                        LPWSTR lpszName, LONG_PTR lParam)
3363 {
3364     TLIBATTR *attr;
3365     typelib_struct *tl_struct = (typelib_struct*) lParam;
3366     static const WCHAR fmt[] = {'%','s','\\','%','i',0};
3367     int sz; 
3368     HRESULT res;
3369
3370     if (!IS_INTRESOURCE(lpszName))
3371     {
3372         ERR("Not Int Resource Name %s\n",debugstr_w(lpszName));
3373         return TRUE;
3374     }
3375
3376     sz = strlenW(tl_struct->source)+4;
3377     sz *= sizeof(WCHAR);
3378
3379     if ((INT_PTR)lpszName == 1)
3380         tl_struct->path = strdupW(tl_struct->source);
3381     else
3382     {
3383         tl_struct->path = msi_alloc(sz);
3384         sprintfW(tl_struct->path,fmt,tl_struct->source, lpszName);
3385     }
3386
3387     TRACE("trying %s\n", debugstr_w(tl_struct->path));
3388     res = LoadTypeLib(tl_struct->path,&tl_struct->ptLib);
3389     if (FAILED(res))
3390     {
3391         msi_free(tl_struct->path);
3392         tl_struct->path = NULL;
3393
3394         return TRUE;
3395     }
3396
3397     ITypeLib_GetLibAttr(tl_struct->ptLib, &attr);
3398     if (IsEqualGUID(&(tl_struct->clsid),&(attr->guid)))
3399     {
3400         ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
3401         return FALSE;
3402     }
3403
3404     msi_free(tl_struct->path);
3405     tl_struct->path = NULL;
3406
3407     ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
3408     ITypeLib_Release(tl_struct->ptLib);
3409
3410     return TRUE;
3411 }
3412
3413 static UINT ITERATE_RegisterTypeLibraries(MSIRECORD *row, LPVOID param)
3414 {
3415     MSIPACKAGE* package = param;
3416     LPCWSTR component;
3417     MSICOMPONENT *comp;
3418     MSIFILE *file;
3419     typelib_struct tl_struct;
3420     ITypeLib *tlib;
3421     HMODULE module;
3422     HRESULT hr;
3423
3424     component = MSI_RecordGetString(row,3);
3425     comp = get_loaded_component(package,component);
3426     if (!comp)
3427         return ERROR_SUCCESS;
3428
3429     if (!comp->Enabled)
3430     {
3431         TRACE("component is disabled\n");
3432         return ERROR_SUCCESS;
3433     }
3434
3435     if (comp->ActionRequest != INSTALLSTATE_LOCAL)
3436     {
3437         TRACE("Component not scheduled for installation: %s\n", debugstr_w(component));
3438         comp->Action = comp->Installed;
3439         return ERROR_SUCCESS;
3440     }
3441     comp->Action = INSTALLSTATE_LOCAL;
3442
3443     if (!comp->KeyPath || !(file = get_loaded_file( package, comp->KeyPath )))
3444     {
3445         TRACE("component has no key path\n");
3446         return ERROR_SUCCESS;
3447     }
3448     ui_actiondata( package, szRegisterTypeLibraries, row );
3449
3450     module = LoadLibraryExW( file->TargetPath, NULL, LOAD_LIBRARY_AS_DATAFILE );
3451     if (module)
3452     {
3453         LPCWSTR guid;
3454         guid = MSI_RecordGetString(row,1);
3455         CLSIDFromString((LPCWSTR)guid, &tl_struct.clsid);
3456         tl_struct.source = strdupW( file->TargetPath );
3457         tl_struct.path = NULL;
3458
3459         EnumResourceNamesW(module, szTYPELIB, Typelib_EnumResNameProc,
3460                         (LONG_PTR)&tl_struct);
3461
3462         if (tl_struct.path)
3463         {
3464             LPWSTR help = NULL;
3465             LPCWSTR helpid;
3466             HRESULT res;
3467
3468             helpid = MSI_RecordGetString(row,6);
3469
3470             if (helpid) help = resolve_target_folder( package, helpid, FALSE, TRUE, NULL );
3471             res = RegisterTypeLib(tl_struct.ptLib,tl_struct.path,help);
3472             msi_free(help);
3473
3474             if (FAILED(res))
3475                 ERR("Failed to register type library %s\n",
3476                         debugstr_w(tl_struct.path));
3477             else
3478                 TRACE("Registered %s\n", debugstr_w(tl_struct.path));
3479
3480             ITypeLib_Release(tl_struct.ptLib);
3481             msi_free(tl_struct.path);
3482         }
3483         else
3484             ERR("Failed to load type library %s\n",
3485                     debugstr_w(tl_struct.source));
3486
3487         FreeLibrary(module);
3488         msi_free(tl_struct.source);
3489     }
3490     else
3491     {
3492         hr = LoadTypeLibEx(file->TargetPath, REGKIND_REGISTER, &tlib);
3493         if (FAILED(hr))
3494         {
3495             ERR("Failed to load type library: %08x\n", hr);
3496             return ERROR_INSTALL_FAILURE;
3497         }
3498
3499         ITypeLib_Release(tlib);
3500     }
3501
3502     return ERROR_SUCCESS;
3503 }
3504
3505 static UINT ACTION_RegisterTypeLibraries(MSIPACKAGE *package)
3506 {
3507     /* 
3508      * OK this is a bit confusing.. I am given a _Component key and I believe
3509      * that the file that is being registered as a type library is the "key file
3510      * of that component" which I interpret to mean "The file in the KeyPath of
3511      * that component".
3512      */
3513     UINT rc;
3514     MSIQUERY * view;
3515     static const WCHAR Query[] =
3516         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3517          '`','T','y','p','e','L','i','b','`',0};
3518
3519     rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3520     if (rc != ERROR_SUCCESS)
3521         return ERROR_SUCCESS;
3522
3523     rc = MSI_IterateRecords(view, NULL, ITERATE_RegisterTypeLibraries, package);
3524     msiobj_release(&view->hdr);
3525     return rc;
3526 }
3527
3528 static UINT ITERATE_UnregisterTypeLibraries( MSIRECORD *row, LPVOID param )
3529 {
3530     MSIPACKAGE *package = param;
3531     LPCWSTR component, guid;
3532     MSICOMPONENT *comp;
3533     GUID libid;
3534     UINT version;
3535     LCID language;
3536     SYSKIND syskind;
3537     HRESULT hr;
3538
3539     component = MSI_RecordGetString( row, 3 );
3540     comp = get_loaded_component( package, component );
3541     if (!comp)
3542         return ERROR_SUCCESS;
3543
3544     if (!comp->Enabled)
3545     {
3546         TRACE("component is disabled\n");
3547         return ERROR_SUCCESS;
3548     }
3549
3550     if (comp->ActionRequest != INSTALLSTATE_ABSENT)
3551     {
3552         TRACE("Component not scheduled for removal %s\n", debugstr_w(component));
3553         comp->Action = comp->Installed;
3554         return ERROR_SUCCESS;
3555     }
3556     comp->Action = INSTALLSTATE_ABSENT;
3557
3558     ui_actiondata( package, szUnregisterTypeLibraries, row );
3559
3560     guid = MSI_RecordGetString( row, 1 );
3561     CLSIDFromString( (LPCWSTR)guid, &libid );
3562     version = MSI_RecordGetInteger( row, 4 );
3563     language = MSI_RecordGetInteger( row, 2 );
3564
3565 #ifdef _WIN64
3566     syskind = SYS_WIN64;
3567 #else
3568     syskind = SYS_WIN32;
3569 #endif
3570
3571     hr = UnRegisterTypeLib( &libid, (version >> 8) & 0xffff, version & 0xff, language, syskind );
3572     if (FAILED(hr))
3573     {
3574         WARN("Failed to unregister typelib: %08x\n", hr);
3575     }
3576
3577     return ERROR_SUCCESS;
3578 }
3579
3580 static UINT ACTION_UnregisterTypeLibraries( MSIPACKAGE *package )
3581 {
3582     UINT rc;
3583     MSIQUERY *view;
3584     static const WCHAR query[] =
3585         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3586          '`','T','y','p','e','L','i','b','`',0};
3587
3588     rc = MSI_DatabaseOpenViewW( package->db, query, &view );
3589     if (rc != ERROR_SUCCESS)
3590         return ERROR_SUCCESS;
3591
3592     rc = MSI_IterateRecords( view, NULL, ITERATE_UnregisterTypeLibraries, package );
3593     msiobj_release( &view->hdr );
3594     return rc;
3595 }
3596
3597 static WCHAR *get_link_file( MSIPACKAGE *package, MSIRECORD *row )
3598 {
3599     static const WCHAR szlnk[] = {'.','l','n','k',0};
3600     LPCWSTR directory, extension;
3601     LPWSTR link_folder, link_file, filename;
3602
3603     directory = MSI_RecordGetString( row, 2 );
3604     link_folder = resolve_target_folder( package, directory, FALSE, TRUE, NULL );
3605
3606     /* may be needed because of a bug somewhere else */
3607     create_full_pathW( link_folder );
3608
3609     filename = msi_dup_record_field( row, 3 );
3610     reduce_to_longfilename( filename );
3611
3612     extension = strchrW( filename, '.' );
3613     if (!extension || strcmpiW( extension, szlnk ))
3614     {
3615         int len = strlenW( filename );
3616         filename = msi_realloc( filename, len * sizeof(WCHAR) + sizeof(szlnk) );
3617         memcpy( filename + len, szlnk, sizeof(szlnk) );
3618     }
3619     link_file = build_directory_name( 2, link_folder, filename );
3620     msi_free( link_folder );
3621     msi_free( filename );
3622
3623     return link_file;
3624 }
3625
3626 static UINT ITERATE_CreateShortcuts(MSIRECORD *row, LPVOID param)
3627 {
3628     MSIPACKAGE *package = param;
3629     LPWSTR link_file, deformated, path;
3630     LPCWSTR component, target;
3631     MSICOMPONENT *comp;
3632     IShellLinkW *sl = NULL;
3633     IPersistFile *pf = NULL;
3634     HRESULT res;
3635
3636     component = MSI_RecordGetString(row, 4);
3637     comp = get_loaded_component(package, component);
3638     if (!comp)
3639         return ERROR_SUCCESS;
3640
3641     if (!comp->Enabled)
3642     {
3643         TRACE("component is disabled\n");
3644         return ERROR_SUCCESS;
3645     }
3646
3647     if (comp->ActionRequest != INSTALLSTATE_LOCAL)
3648     {
3649         TRACE("Component not scheduled for installation %s\n", debugstr_w(component));
3650         comp->Action = comp->Installed;
3651         return ERROR_SUCCESS;
3652     }
3653     comp->Action = INSTALLSTATE_LOCAL;
3654
3655     ui_actiondata(package,szCreateShortcuts,row);
3656
3657     res = CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
3658                     &IID_IShellLinkW, (LPVOID *) &sl );
3659
3660     if (FAILED( res ))
3661     {
3662         ERR("CLSID_ShellLink not available\n");
3663         goto err;
3664     }
3665
3666     res = IShellLinkW_QueryInterface( sl, &IID_IPersistFile,(LPVOID*) &pf );
3667     if (FAILED( res ))
3668     {
3669         ERR("QueryInterface(IID_IPersistFile) failed\n");
3670         goto err;
3671     }
3672
3673     target = MSI_RecordGetString(row, 5);
3674     if (strchrW(target, '['))
3675     {
3676         deformat_string(package, target, &deformated);
3677         IShellLinkW_SetPath(sl,deformated);
3678         msi_free(deformated);
3679     }
3680     else
3681     {
3682         FIXME("poorly handled shortcut format, advertised shortcut\n");
3683         IShellLinkW_SetPath(sl,comp->FullKeypath);
3684     }
3685
3686     if (!MSI_RecordIsNull(row,6))
3687     {
3688         LPCWSTR arguments = MSI_RecordGetString(row, 6);
3689         deformat_string(package, arguments, &deformated);
3690         IShellLinkW_SetArguments(sl,deformated);
3691         msi_free(deformated);
3692     }
3693
3694     if (!MSI_RecordIsNull(row,7))
3695     {
3696         LPCWSTR description = MSI_RecordGetString(row, 7);
3697         IShellLinkW_SetDescription(sl, description);
3698     }
3699
3700     if (!MSI_RecordIsNull(row,8))
3701         IShellLinkW_SetHotkey(sl,MSI_RecordGetInteger(row,8));
3702
3703     if (!MSI_RecordIsNull(row,9))
3704     {
3705         INT index; 
3706         LPCWSTR icon = MSI_RecordGetString(row, 9);
3707
3708         path = build_icon_path(package, icon);
3709         index = MSI_RecordGetInteger(row,10);
3710
3711         /* no value means 0 */
3712         if (index == MSI_NULL_INTEGER)
3713             index = 0;
3714
3715         IShellLinkW_SetIconLocation(sl, path, index);
3716         msi_free(path);
3717     }
3718
3719     if (!MSI_RecordIsNull(row,11))
3720         IShellLinkW_SetShowCmd(sl,MSI_RecordGetInteger(row,11));
3721
3722     if (!MSI_RecordIsNull(row,12))
3723     {
3724         LPCWSTR wkdir = MSI_RecordGetString(row, 12);
3725         path = resolve_target_folder( package, wkdir, FALSE, TRUE, NULL );
3726         if (path)
3727             IShellLinkW_SetWorkingDirectory(sl, path);
3728         msi_free(path);
3729     }
3730
3731     link_file = get_link_file(package, row);
3732
3733     TRACE("Writing shortcut to %s\n", debugstr_w(link_file));
3734     IPersistFile_Save(pf, link_file, FALSE);
3735
3736     msi_free(link_file);
3737
3738 err:
3739     if (pf)
3740         IPersistFile_Release( pf );
3741     if (sl)
3742         IShellLinkW_Release( sl );
3743
3744     return ERROR_SUCCESS;
3745 }
3746
3747 static UINT ACTION_CreateShortcuts(MSIPACKAGE *package)
3748 {
3749     UINT rc;
3750     HRESULT res;
3751     MSIQUERY * view;
3752     static const WCHAR Query[] =
3753         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3754          '`','S','h','o','r','t','c','u','t','`',0};
3755
3756     rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3757     if (rc != ERROR_SUCCESS)
3758         return ERROR_SUCCESS;
3759
3760     res = CoInitialize( NULL );
3761
3762     rc = MSI_IterateRecords(view, NULL, ITERATE_CreateShortcuts, package);
3763     msiobj_release(&view->hdr);
3764
3765     if (SUCCEEDED(res))
3766         CoUninitialize();
3767
3768     return rc;
3769 }
3770
3771 static UINT ITERATE_RemoveShortcuts( MSIRECORD *row, LPVOID param )
3772 {
3773     MSIPACKAGE *package = param;
3774     LPWSTR link_file;
3775     LPCWSTR component;
3776     MSICOMPONENT *comp;
3777
3778     component = MSI_RecordGetString( row, 4 );
3779     comp = get_loaded_component( package, component );
3780     if (!comp)
3781         return ERROR_SUCCESS;
3782
3783     if (!comp->Enabled)
3784     {
3785         TRACE("component is disabled\n");
3786         return ERROR_SUCCESS;
3787     }
3788
3789     if (comp->ActionRequest != INSTALLSTATE_ABSENT)
3790     {
3791         TRACE("Component not scheduled for removal %s\n", debugstr_w(component));
3792         comp->Action = comp->Installed;
3793         return ERROR_SUCCESS;
3794     }
3795     comp->Action = INSTALLSTATE_ABSENT;
3796
3797     ui_actiondata( package, szRemoveShortcuts, row );
3798
3799     link_file = get_link_file( package, row );
3800
3801     TRACE("Removing shortcut file %s\n", debugstr_w( link_file ));
3802     if (!DeleteFileW( link_file ))
3803     {
3804         WARN("Failed to remove shortcut file %u\n", GetLastError());
3805     }
3806     msi_free( link_file );
3807
3808     return ERROR_SUCCESS;
3809 }
3810
3811 static UINT ACTION_RemoveShortcuts( MSIPACKAGE *package )
3812 {
3813     UINT rc;
3814     MSIQUERY *view;
3815     static const WCHAR query[] =
3816         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3817          '`','S','h','o','r','t','c','u','t','`',0};
3818
3819     rc = MSI_DatabaseOpenViewW( package->db, query, &view );
3820     if (rc != ERROR_SUCCESS)
3821         return ERROR_SUCCESS;
3822
3823     rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveShortcuts, package );
3824     msiobj_release( &view->hdr );
3825
3826     return rc;
3827 }
3828
3829 static UINT ITERATE_PublishIcon(MSIRECORD *row, LPVOID param)
3830 {
3831     MSIPACKAGE* package = param;
3832     HANDLE the_file;
3833     LPWSTR FilePath;
3834     LPCWSTR FileName;
3835     CHAR buffer[1024];
3836     DWORD sz;
3837     UINT rc;
3838
3839     FileName = MSI_RecordGetString(row,1);
3840     if (!FileName)
3841     {
3842         ERR("Unable to get FileName\n");
3843         return ERROR_SUCCESS;
3844     }
3845
3846     FilePath = build_icon_path(package,FileName);
3847
3848     TRACE("Creating icon file at %s\n",debugstr_w(FilePath));
3849
3850     the_file = CreateFileW(FilePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
3851                         FILE_ATTRIBUTE_NORMAL, NULL);
3852
3853     if (the_file == INVALID_HANDLE_VALUE)
3854     {
3855         ERR("Unable to create file %s\n",debugstr_w(FilePath));
3856         msi_free(FilePath);
3857         return ERROR_SUCCESS;
3858     }
3859
3860     do 
3861     {
3862         DWORD write;
3863         sz = 1024;
3864         rc = MSI_RecordReadStream(row,2,buffer,&sz);
3865         if (rc != ERROR_SUCCESS)
3866         {
3867             ERR("Failed to get stream\n");
3868             CloseHandle(the_file);  
3869             DeleteFileW(FilePath);
3870             break;
3871         }
3872         WriteFile(the_file,buffer,sz,&write,NULL);
3873     } while (sz == 1024);
3874
3875     msi_free(FilePath);
3876     CloseHandle(the_file);
3877
3878     return ERROR_SUCCESS;
3879 }
3880
3881 static UINT msi_publish_icons(MSIPACKAGE *package)
3882 {
3883     UINT r;
3884     MSIQUERY *view;
3885
3886     static const WCHAR query[]= {
3887         'S','E','L','E','C','T',' ','*',' ',
3888         'F','R','O','M',' ','`','I','c','o','n','`',0};
3889
3890     r = MSI_DatabaseOpenViewW(package->db, query, &view);
3891     if (r == ERROR_SUCCESS)
3892     {
3893         MSI_IterateRecords(view, NULL, ITERATE_PublishIcon, package);
3894         msiobj_release(&view->hdr);
3895     }
3896
3897     return ERROR_SUCCESS;
3898 }
3899
3900 static UINT msi_publish_sourcelist(MSIPACKAGE *package, HKEY hkey)
3901 {
3902     UINT r;
3903     HKEY source;
3904     LPWSTR buffer;
3905     MSIMEDIADISK *disk;
3906     MSISOURCELISTINFO *info;
3907
3908     r = RegCreateKeyW(hkey, szSourceList, &source);
3909     if (r != ERROR_SUCCESS)
3910         return r;
3911
3912     RegCloseKey(source);
3913
3914     buffer = strrchrW(package->PackagePath, '\\') + 1;
3915     r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3916                               package->Context, MSICODE_PRODUCT,
3917                               INSTALLPROPERTY_PACKAGENAMEW, buffer);
3918     if (r != ERROR_SUCCESS)
3919         return r;
3920
3921     r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3922                               package->Context, MSICODE_PRODUCT,
3923                               INSTALLPROPERTY_MEDIAPACKAGEPATHW, szEmpty);
3924     if (r != ERROR_SUCCESS)
3925         return r;
3926
3927     r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3928                               package->Context, MSICODE_PRODUCT,
3929                               INSTALLPROPERTY_DISKPROMPTW, szEmpty);
3930     if (r != ERROR_SUCCESS)
3931         return r;
3932
3933     LIST_FOR_EACH_ENTRY(info, &package->sourcelist_info, MSISOURCELISTINFO, entry)
3934     {
3935         if (!strcmpW( info->property, INSTALLPROPERTY_LASTUSEDSOURCEW ))
3936             msi_set_last_used_source(package->ProductCode, NULL, info->context,
3937                                      info->options, info->value);
3938         else
3939             MsiSourceListSetInfoW(package->ProductCode, NULL,
3940                                   info->context, info->options,
3941                                   info->property, info->value);
3942     }
3943
3944     LIST_FOR_EACH_ENTRY(disk, &package->sourcelist_media, MSIMEDIADISK, entry)
3945     {
3946         MsiSourceListAddMediaDiskW(package->ProductCode, NULL,
3947                                    disk->context, disk->options,
3948                                    disk->disk_id, disk->volume_label, disk->disk_prompt);
3949     }
3950
3951     return ERROR_SUCCESS;
3952 }
3953
3954 static UINT msi_publish_product_properties(MSIPACKAGE *package, HKEY hkey)
3955 {
3956     MSIHANDLE hdb, suminfo;
3957     WCHAR guids[MAX_PATH];
3958     WCHAR packcode[SQUISH_GUID_SIZE];
3959     LPWSTR buffer;
3960     LPWSTR ptr;
3961     DWORD langid;
3962     DWORD size;
3963     UINT r;
3964
3965     static const WCHAR szProductLanguage[] =
3966         {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
3967     static const WCHAR szARPProductIcon[] =
3968         {'A','R','P','P','R','O','D','U','C','T','I','C','O','N',0};
3969     static const WCHAR szProductVersion[] =
3970         {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
3971     static const WCHAR szAssignment[] =
3972         {'A','s','s','i','g','n','m','e','n','t',0};
3973     static const WCHAR szAdvertiseFlags[] =
3974         {'A','d','v','e','r','t','i','s','e','F','l','a','g','s',0};
3975     static const WCHAR szClients[] =
3976         {'C','l','i','e','n','t','s',0};
3977     static const WCHAR szColon[] = {':',0};
3978
3979     buffer = msi_dup_property(package->db, INSTALLPROPERTY_PRODUCTNAMEW);
3980     msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTNAMEW, buffer);
3981     msi_free(buffer);
3982
3983     langid = msi_get_property_int(package->db, szProductLanguage, 0);
3984     msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
3985
3986     /* FIXME */
3987     msi_reg_set_val_dword(hkey, INSTALLPROPERTY_AUTHORIZED_LUA_APPW, 0);
3988
3989     buffer = msi_dup_property(package->db, szARPProductIcon);
3990     if (buffer)
3991     {
3992         LPWSTR path = build_icon_path(package,buffer);
3993         msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTICONW, path);
3994         msi_free(path);
3995         msi_free(buffer);
3996     }
3997
3998     buffer = msi_dup_property(package->db, szProductVersion);
3999     if (buffer)
4000     {
4001         DWORD verdword = msi_version_str_to_dword(buffer);
4002         msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
4003         msi_free(buffer);
4004     }
4005
4006     msi_reg_set_val_dword(hkey, szAssignment, 0);
4007     msi_reg_set_val_dword(hkey, szAdvertiseFlags, 0x184);
4008     msi_reg_set_val_dword(hkey, INSTALLPROPERTY_INSTANCETYPEW, 0);
4009     msi_reg_set_val_str(hkey, szClients, szColon);
4010
4011     hdb = alloc_msihandle(&package->db->hdr);
4012     if (!hdb)
4013         return ERROR_NOT_ENOUGH_MEMORY;
4014
4015     r = MsiGetSummaryInformationW(hdb, NULL, 0, &suminfo);
4016     MsiCloseHandle(hdb);
4017     if (r != ERROR_SUCCESS)
4018         goto done;
4019
4020     size = MAX_PATH;
4021     r = MsiSummaryInfoGetPropertyW(suminfo, PID_REVNUMBER, NULL, NULL,
4022                                    NULL, guids, &size);
4023     if (r != ERROR_SUCCESS)
4024         goto done;
4025
4026     ptr = strchrW(guids, ';');
4027     if (ptr) *ptr = 0;
4028     squash_guid(guids, packcode);
4029     msi_reg_set_val_str(hkey, INSTALLPROPERTY_PACKAGECODEW, packcode);
4030
4031 done:
4032     MsiCloseHandle(suminfo);
4033     return ERROR_SUCCESS;
4034 }
4035
4036 static UINT msi_publish_upgrade_code(MSIPACKAGE *package)
4037 {
4038     UINT r;
4039     HKEY hkey;
4040     LPWSTR upgrade;
4041     WCHAR squashed_pc[SQUISH_GUID_SIZE];
4042
4043     upgrade = msi_dup_property(package->db, szUpgradeCode);
4044     if (!upgrade)
4045         return ERROR_SUCCESS;
4046
4047     if (package->Context == MSIINSTALLCONTEXT_MACHINE)
4048     {
4049         r = MSIREG_OpenClassesUpgradeCodesKey(upgrade, &hkey, TRUE);
4050         if (r != ERROR_SUCCESS)
4051             goto done;
4052     }
4053     else
4054     {
4055         r = MSIREG_OpenUserUpgradeCodesKey(upgrade, &hkey, TRUE);
4056         if (r != ERROR_SUCCESS)
4057             goto done;
4058     }
4059
4060     squash_guid(package->ProductCode, squashed_pc);
4061     msi_reg_set_val_str(hkey, squashed_pc, NULL);
4062
4063     RegCloseKey(hkey);
4064
4065 done:
4066     msi_free(upgrade);
4067     return r;
4068 }
4069
4070 static BOOL msi_check_publish(MSIPACKAGE *package)
4071 {
4072     MSIFEATURE *feature;
4073
4074     LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4075     {
4076         if (feature->ActionRequest == INSTALLSTATE_LOCAL)
4077             return TRUE;
4078     }
4079
4080     return FALSE;
4081 }
4082
4083 static BOOL msi_check_unpublish(MSIPACKAGE *package)
4084 {
4085     MSIFEATURE *feature;
4086
4087     LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4088     {
4089         if (feature->ActionRequest != INSTALLSTATE_ABSENT)
4090             return FALSE;
4091     }
4092
4093     return TRUE;
4094 }
4095
4096 static UINT msi_publish_patches( MSIPACKAGE *package )
4097 {
4098     static const WCHAR szAllPatches[] = {'A','l','l','P','a','t','c','h','e','s',0};
4099     WCHAR patch_squashed[GUID_SIZE];
4100     HKEY patches_key = NULL, product_patches_key = NULL, product_key;
4101     LONG res;
4102     MSIPATCHINFO *patch;
4103     UINT r;
4104     WCHAR *p, *all_patches = NULL;
4105     DWORD len = 0;
4106
4107     r = MSIREG_OpenProductKey( package->ProductCode, NULL, package->Context, &product_key, TRUE );
4108     if (r != ERROR_SUCCESS)
4109         return ERROR_FUNCTION_FAILED;
4110
4111     res = RegCreateKeyExW( product_key, szPatches, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &patches_key, NULL );
4112     if (res != ERROR_SUCCESS)
4113     {
4114         r = ERROR_FUNCTION_FAILED;
4115         goto done;
4116     }
4117
4118     r = MSIREG_OpenUserDataProductPatchesKey( package->ProductCode, package->Context, &product_patches_key, TRUE );
4119     if (r != ERROR_SUCCESS)
4120         goto done;
4121
4122     LIST_FOR_EACH_ENTRY( patch, &package->patches, MSIPATCHINFO, entry )
4123     {
4124         squash_guid( patch->patchcode, patch_squashed );
4125         len += strlenW( patch_squashed ) + 1;
4126     }
4127
4128     p = all_patches = msi_alloc( (len + 1) * sizeof(WCHAR) );
4129     if (!all_patches)
4130         goto done;
4131
4132     LIST_FOR_EACH_ENTRY( patch, &package->patches, MSIPATCHINFO, entry )
4133     {
4134         HKEY patch_key;
4135
4136         squash_guid( patch->patchcode, p );
4137         p += strlenW( p ) + 1;
4138
4139         res = RegSetValueExW( patches_key, patch_squashed, 0, REG_SZ,
4140                               (const BYTE *)patch->transforms,
4141                               (strlenW(patch->transforms) + 1) * sizeof(WCHAR) );
4142         if (res != ERROR_SUCCESS)
4143             goto done;
4144
4145         r = MSIREG_OpenUserDataPatchKey( patch->patchcode, package->Context, &patch_key, TRUE );
4146         if (r != ERROR_SUCCESS)
4147             goto done;
4148
4149         res = RegSetValueExW( patch_key, szLocalPackage, 0, REG_SZ,
4150                               (const BYTE *)patch->localfile,
4151                               (strlenW(patch->localfile) + 1) * sizeof(WCHAR) );
4152         RegCloseKey( patch_key );
4153         if (res != ERROR_SUCCESS)
4154             goto done;
4155
4156         res = RegCreateKeyExW( product_patches_key, patch_squashed, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &patch_key, NULL );
4157         if (res != ERROR_SUCCESS)
4158             goto done;
4159
4160         res = RegSetValueExW( patch_key, szState, 0, REG_DWORD, (const BYTE *)&patch->state, sizeof(patch->state) );
4161         RegCloseKey( patch_key );
4162         if (res != ERROR_SUCCESS)
4163             goto done;
4164     }
4165
4166     all_patches[len] = 0;
4167     res = RegSetValueExW( patches_key, szPatches, 0, REG_MULTI_SZ,
4168                           (const BYTE *)all_patches, (len + 1) * sizeof(WCHAR) );
4169     if (res != ERROR_SUCCESS)
4170         goto done;
4171
4172     res = RegSetValueExW( product_patches_key, szAllPatches, 0, REG_MULTI_SZ,
4173                           (const BYTE *)all_patches, (len + 1) * sizeof(WCHAR) );
4174     if (res != ERROR_SUCCESS)
4175         r = ERROR_FUNCTION_FAILED;
4176
4177 done:
4178     RegCloseKey( product_patches_key );
4179     RegCloseKey( patches_key );
4180     RegCloseKey( product_key );
4181     msi_free( all_patches );
4182     return r;
4183 }
4184
4185 /*
4186  * 99% of the work done here is only done for 
4187  * advertised installs. However this is where the
4188  * Icon table is processed and written out
4189  * so that is what I am going to do here.
4190  */
4191 static UINT ACTION_PublishProduct(MSIPACKAGE *package)
4192 {
4193     UINT rc;
4194     HKEY hukey = NULL, hudkey = NULL;
4195     MSIRECORD *uirow;
4196
4197     if (!list_empty(&package->patches))
4198     {
4199         rc = msi_publish_patches(package);
4200         if (rc != ERROR_SUCCESS)
4201             goto end;
4202     }
4203
4204     /* FIXME: also need to publish if the product is in advertise mode */
4205     if (!msi_check_publish(package))
4206         return ERROR_SUCCESS;
4207
4208     rc = MSIREG_OpenProductKey(package->ProductCode, NULL, package->Context,
4209                                &hukey, TRUE);
4210     if (rc != ERROR_SUCCESS)
4211         goto end;
4212
4213     rc = MSIREG_OpenUserDataProductKey(package->ProductCode, package->Context,
4214                                        NULL, &hudkey, TRUE);
4215     if (rc != ERROR_SUCCESS)
4216         goto end;
4217
4218     rc = msi_publish_upgrade_code(package);
4219     if (rc != ERROR_SUCCESS)
4220         goto end;
4221
4222     rc = msi_publish_product_properties(package, hukey);
4223     if (rc != ERROR_SUCCESS)
4224         goto end;
4225
4226     rc = msi_publish_sourcelist(package, hukey);
4227     if (rc != ERROR_SUCCESS)
4228         goto end;
4229
4230     rc = msi_publish_icons(package);
4231
4232 end:
4233     uirow = MSI_CreateRecord( 1 );
4234     MSI_RecordSetStringW( uirow, 1, package->ProductCode );
4235     ui_actiondata( package, szPublishProduct, uirow );
4236     msiobj_release( &uirow->hdr );
4237
4238     RegCloseKey(hukey);
4239     RegCloseKey(hudkey);
4240
4241     return rc;
4242 }
4243
4244 static WCHAR *get_ini_file_name( MSIPACKAGE *package, MSIRECORD *row )
4245 {
4246     WCHAR *filename, *ptr, *folder, *ret;
4247     const WCHAR *dirprop;
4248
4249     filename = msi_dup_record_field( row, 2 );
4250     if (filename && (ptr = strchrW( filename, '|' )))
4251         ptr++;
4252     else
4253         ptr = filename;
4254
4255     dirprop = MSI_RecordGetString( row, 3 );
4256     if (dirprop)
4257     {
4258         folder = resolve_target_folder( package, dirprop, FALSE, TRUE, NULL );
4259         if (!folder)
4260             folder = msi_dup_property( package->db, dirprop );
4261     }
4262     else
4263         folder = msi_dup_property( package->db, szWindowsFolder );
4264
4265     if (!folder)
4266     {
4267         ERR("Unable to resolve folder %s\n", debugstr_w(dirprop));
4268         msi_free( filename );
4269         return NULL;
4270     }
4271
4272     ret = build_directory_name( 2, folder, ptr );
4273
4274     msi_free( filename );
4275     msi_free( folder );
4276     return ret;
4277 }
4278
4279 static UINT ITERATE_WriteIniValues(MSIRECORD *row, LPVOID param)
4280 {
4281     MSIPACKAGE *package = param;
4282     LPCWSTR component, section, key, value, identifier;
4283     LPWSTR deformated_section, deformated_key, deformated_value, fullname;
4284     MSIRECORD * uirow;
4285     INT action;
4286     MSICOMPONENT *comp;
4287
4288     component = MSI_RecordGetString(row, 8);
4289     comp = get_loaded_component(package,component);
4290     if (!comp)
4291         return ERROR_SUCCESS;
4292
4293     if (!comp->Enabled)
4294     {
4295         TRACE("component is disabled\n");
4296         return ERROR_SUCCESS;
4297     }
4298
4299     if (comp->ActionRequest != INSTALLSTATE_LOCAL)
4300     {
4301         TRACE("Component not scheduled for installation %s\n", debugstr_w(component));
4302         comp->Action = comp->Installed;
4303         return ERROR_SUCCESS;
4304     }
4305     comp->Action = INSTALLSTATE_LOCAL;
4306
4307     identifier = MSI_RecordGetString(row,1); 
4308     section = MSI_RecordGetString(row,4);
4309     key = MSI_RecordGetString(row,5);
4310     value = MSI_RecordGetString(row,6);
4311     action = MSI_RecordGetInteger(row,7);
4312
4313     deformat_string(package,section,&deformated_section);
4314     deformat_string(package,key,&deformated_key);
4315     deformat_string(package,value,&deformated_value);
4316
4317     fullname = get_ini_file_name(package, row);
4318
4319     if (action == 0)
4320     {
4321         TRACE("Adding value %s to section %s in %s\n",
4322                 debugstr_w(deformated_key), debugstr_w(deformated_section),
4323                 debugstr_w(fullname));
4324         WritePrivateProfileStringW(deformated_section, deformated_key,
4325                                    deformated_value, fullname);
4326     }
4327     else if (action == 1)
4328     {
4329         WCHAR returned[10];
4330         GetPrivateProfileStringW(deformated_section, deformated_key, NULL,
4331                                  returned, 10, fullname);
4332         if (returned[0] == 0)
4333         {
4334             TRACE("Adding value %s to section %s in %s\n",
4335                     debugstr_w(deformated_key), debugstr_w(deformated_section),
4336                     debugstr_w(fullname));
4337
4338             WritePrivateProfileStringW(deformated_section, deformated_key,
4339                                        deformated_value, fullname);
4340         }
4341     }
4342     else if (action == 3)
4343         FIXME("Append to existing section not yet implemented\n");
4344
4345     uirow = MSI_CreateRecord(4);
4346     MSI_RecordSetStringW(uirow,1,identifier);
4347     MSI_RecordSetStringW(uirow,2,deformated_section);
4348     MSI_RecordSetStringW(uirow,3,deformated_key);
4349     MSI_RecordSetStringW(uirow,4,deformated_value);
4350     ui_actiondata(package,szWriteIniValues,uirow);
4351     msiobj_release( &uirow->hdr );
4352
4353     msi_free(fullname);
4354     msi_free(deformated_key);
4355     msi_free(deformated_value);
4356     msi_free(deformated_section);
4357     return ERROR_SUCCESS;
4358 }
4359
4360 static UINT ACTION_WriteIniValues(MSIPACKAGE *package)
4361 {
4362     UINT rc;
4363     MSIQUERY * view;
4364     static const WCHAR ExecSeqQuery[] = 
4365         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4366          '`','I','n','i','F','i','l','e','`',0};
4367
4368     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4369     if (rc != ERROR_SUCCESS)
4370     {
4371         TRACE("no IniFile table\n");
4372         return ERROR_SUCCESS;
4373     }
4374
4375     rc = MSI_IterateRecords(view, NULL, ITERATE_WriteIniValues, package);
4376     msiobj_release(&view->hdr);
4377     return rc;
4378 }
4379
4380 static UINT ITERATE_RemoveIniValuesOnUninstall( MSIRECORD *row, LPVOID param )
4381 {
4382     MSIPACKAGE *package = param;
4383     LPCWSTR component, section, key, value, identifier;
4384     LPWSTR deformated_section, deformated_key, deformated_value, filename;
4385     MSICOMPONENT *comp;
4386     MSIRECORD *uirow;
4387     INT action;
4388
4389     component = MSI_RecordGetString( row, 8 );
4390     comp = get_loaded_component( package, component );
4391     if (!comp)
4392         return ERROR_SUCCESS;
4393
4394     if (!comp->Enabled)
4395     {
4396         TRACE("component is disabled\n");
4397         return ERROR_SUCCESS;
4398     }
4399
4400     if (comp->ActionRequest != INSTALLSTATE_ABSENT)
4401     {
4402         TRACE("Component not scheduled for removal %s\n", debugstr_w(component));
4403         comp->Action = comp->Installed;
4404         return ERROR_SUCCESS;
4405     }
4406     comp->Action = INSTALLSTATE_ABSENT;
4407
4408     identifier = MSI_RecordGetString( row, 1 );
4409     section = MSI_RecordGetString( row, 4 );
4410     key = MSI_RecordGetString( row, 5 );
4411     value = MSI_RecordGetString( row, 6 );
4412     action = MSI_RecordGetInteger( row, 7 );
4413
4414     deformat_string( package, section, &deformated_section );
4415     deformat_string( package, key, &deformated_key );
4416     deformat_string( package, value, &deformated_value );
4417
4418     if (action == msidbIniFileActionAddLine || action == msidbIniFileActionCreateLine)
4419     {
4420         filename = get_ini_file_name( package, row );
4421
4422         TRACE("Removing key %s from section %s in %s\n",
4423                debugstr_w(deformated_key), debugstr_w(deformated_section), debugstr_w(filename));
4424
4425         if (!WritePrivateProfileStringW( deformated_section, deformated_key, NULL, filename ))
4426         {
4427             WARN("Unable to remove key %u\n", GetLastError());
4428         }
4429         msi_free( filename );
4430     }
4431     else
4432         FIXME("Unsupported action %d\n", action);
4433
4434
4435     uirow = MSI_CreateRecord( 4 );
4436     MSI_RecordSetStringW( uirow, 1, identifier );
4437     MSI_RecordSetStringW( uirow, 2, deformated_section );
4438     MSI_RecordSetStringW( uirow, 3, deformated_key );
4439     MSI_RecordSetStringW( uirow, 4, deformated_value );
4440     ui_actiondata( package, szRemoveIniValues, uirow );
4441     msiobj_release( &uirow->hdr );
4442
4443     msi_free( deformated_key );
4444     msi_free( deformated_value );
4445     msi_free( deformated_section );
4446     return ERROR_SUCCESS;
4447 }
4448
4449 static UINT ITERATE_RemoveIniValuesOnInstall( MSIRECORD *row, LPVOID param )
4450 {
4451     MSIPACKAGE *package = param;
4452     LPCWSTR component, section, key, value, identifier;
4453     LPWSTR deformated_section, deformated_key, deformated_value, filename;
4454     MSICOMPONENT *comp;
4455     MSIRECORD *uirow;
4456     INT action;
4457
4458     component = MSI_RecordGetString( row, 8 );
4459     comp = get_loaded_component( package, component );
4460     if (!comp)
4461         return ERROR_SUCCESS;
4462
4463     if (!comp->Enabled)
4464     {
4465         TRACE("component is disabled\n");
4466         return ERROR_SUCCESS;
4467     }
4468
4469     if (comp->ActionRequest != INSTALLSTATE_LOCAL)
4470     {
4471         TRACE("Component not scheduled for installation %s\n", debugstr_w(component));
4472         comp->Action = comp->Installed;
4473         return ERROR_SUCCESS;
4474     }
4475     comp->Action = INSTALLSTATE_LOCAL;
4476
4477     identifier = MSI_RecordGetString( row, 1 );
4478     section = MSI_RecordGetString( row, 4 );
4479     key = MSI_RecordGetString( row, 5 );
4480     value = MSI_RecordGetString( row, 6 );
4481     action = MSI_RecordGetInteger( row, 7 );
4482
4483     deformat_string( package, section, &deformated_section );
4484     deformat_string( package, key, &deformated_key );
4485     deformat_string( package, value, &deformated_value );
4486
4487     if (action == msidbIniFileActionRemoveLine)
4488     {
4489         filename = get_ini_file_name( package, row );
4490
4491         TRACE("Removing key %s from section %s in %s\n",
4492                debugstr_w(deformated_key), debugstr_w(deformated_section), debugstr_w(filename));
4493
4494         if (!WritePrivateProfileStringW( deformated_section, deformated_key, NULL, filename ))
4495         {
4496             WARN("Unable to remove key %u\n", GetLastError());
4497         }
4498         msi_free( filename );
4499     }
4500     else
4501         FIXME("Unsupported action %d\n", action);
4502
4503     uirow = MSI_CreateRecord( 4 );
4504     MSI_RecordSetStringW( uirow, 1, identifier );
4505     MSI_RecordSetStringW( uirow, 2, deformated_section );
4506     MSI_RecordSetStringW( uirow, 3, deformated_key );
4507     MSI_RecordSetStringW( uirow, 4, deformated_value );
4508     ui_actiondata( package, szRemoveIniValues, uirow );
4509     msiobj_release( &uirow->hdr );
4510
4511     msi_free( deformated_key );
4512     msi_free( deformated_value );
4513     msi_free( deformated_section );
4514     return ERROR_SUCCESS;
4515 }
4516
4517 static UINT ACTION_RemoveIniValues( MSIPACKAGE *package )
4518 {
4519     UINT rc;
4520     MSIQUERY *view;
4521     static const WCHAR query[] =
4522         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4523          '`','I','n','i','F','i','l','e','`',0};
4524     static const WCHAR remove_query[] =
4525         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4526          '`','R','e','m','o','v','e','I','n','i','F','i','l','e','`',0};
4527
4528     rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4529     if (rc == ERROR_SUCCESS)
4530     {
4531         rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveIniValuesOnUninstall, package );
4532         msiobj_release( &view->hdr );
4533         if (rc != ERROR_SUCCESS)
4534             return rc;
4535     }
4536
4537     rc = MSI_DatabaseOpenViewW( package->db, remove_query, &view );
4538     if (rc == ERROR_SUCCESS)
4539     {
4540         rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveIniValuesOnInstall, package );
4541         msiobj_release( &view->hdr );
4542         if (rc != ERROR_SUCCESS)
4543             return rc;
4544     }
4545
4546     return ERROR_SUCCESS;
4547 }
4548
4549 static void register_dll( const WCHAR *dll, BOOL unregister )
4550 {
4551     HMODULE hmod;
4552
4553     hmod = LoadLibraryExW( dll, 0, LOAD_WITH_ALTERED_SEARCH_PATH );
4554     if (hmod)
4555     {
4556         HRESULT (WINAPI *func_ptr)( void );
4557         const char *func = unregister ? "DllUnregisterServer" : "DllRegisterServer";
4558
4559         func_ptr = (void *)GetProcAddress( hmod, func );
4560         if (func_ptr)
4561         {
4562             HRESULT hr = func_ptr();
4563             if (FAILED( hr ))
4564                 WARN("failed to register dll 0x%08x\n", hr);
4565         }
4566         else
4567             WARN("entry point %s not found\n", func);
4568         FreeLibrary( hmod );
4569         return;
4570     }
4571     WARN("failed to load library %u\n", GetLastError());
4572 }
4573
4574 static UINT ITERATE_SelfRegModules(MSIRECORD *row, LPVOID param)
4575 {
4576     MSIPACKAGE *package = param;
4577     LPCWSTR filename;
4578     MSIFILE *file;
4579     MSIRECORD *uirow;
4580
4581     filename = MSI_RecordGetString(row,1);
4582     file = get_loaded_file( package, filename );
4583
4584     if (!file)
4585     {
4586         ERR("Unable to find file id %s\n",debugstr_w(filename));
4587         return ERROR_SUCCESS;
4588     }
4589
4590     TRACE("Registering %s\n", debugstr_w( file->TargetPath ));
4591
4592     register_dll( file->TargetPath, FALSE );
4593
4594     uirow = MSI_CreateRecord( 2 );
4595     MSI_RecordSetStringW( uirow, 1, filename );
4596     MSI_RecordSetStringW( uirow, 2, file->Component->Directory );
4597     ui_actiondata( package, szSelfRegModules, uirow );
4598     msiobj_release( &uirow->hdr );
4599
4600     return ERROR_SUCCESS;
4601 }
4602
4603 static UINT ACTION_SelfRegModules(MSIPACKAGE *package)
4604 {
4605     UINT rc;
4606     MSIQUERY * view;
4607     static const WCHAR ExecSeqQuery[] = 
4608         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4609          '`','S','e','l','f','R','e','g','`',0};
4610
4611     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4612     if (rc != ERROR_SUCCESS)
4613     {
4614         TRACE("no SelfReg table\n");
4615         return ERROR_SUCCESS;
4616     }
4617
4618     MSI_IterateRecords(view, NULL, ITERATE_SelfRegModules, package);
4619     msiobj_release(&view->hdr);
4620
4621     return ERROR_SUCCESS;
4622 }
4623
4624 static UINT ITERATE_SelfUnregModules( MSIRECORD *row, LPVOID param )
4625 {
4626     MSIPACKAGE *package = param;
4627     LPCWSTR filename;
4628     MSIFILE *file;
4629     MSIRECORD *uirow;
4630
4631     filename = MSI_RecordGetString( row, 1 );
4632     file = get_loaded_file( package, filename );
4633
4634     if (!file)
4635     {
4636         ERR("Unable to find file id %s\n", debugstr_w(filename));
4637         return ERROR_SUCCESS;
4638     }
4639
4640     TRACE("Unregistering %s\n", debugstr_w( file->TargetPath ));
4641
4642     register_dll( file->TargetPath, TRUE );
4643
4644     uirow = MSI_CreateRecord( 2 );
4645     MSI_RecordSetStringW( uirow, 1, filename );
4646     MSI_RecordSetStringW( uirow, 2, file->Component->Directory );
4647     ui_actiondata( package, szSelfUnregModules, uirow );
4648     msiobj_release( &uirow->hdr );
4649
4650     return ERROR_SUCCESS;
4651 }
4652
4653 static UINT ACTION_SelfUnregModules( MSIPACKAGE *package )
4654 {
4655     UINT rc;
4656     MSIQUERY *view;
4657     static const WCHAR query[] =
4658         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4659          '`','S','e','l','f','R','e','g','`',0};
4660
4661     rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4662     if (rc != ERROR_SUCCESS)
4663     {
4664         TRACE("no SelfReg table\n");
4665         return ERROR_SUCCESS;
4666     }
4667
4668     MSI_IterateRecords( view, NULL, ITERATE_SelfUnregModules, package );
4669     msiobj_release( &view->hdr );
4670
4671     return ERROR_SUCCESS;
4672 }
4673
4674 static UINT ACTION_PublishFeatures(MSIPACKAGE *package)
4675 {
4676     MSIFEATURE *feature;
4677     UINT rc;
4678     HKEY hkey = NULL, userdata = NULL;
4679
4680     if (!msi_check_publish(package))
4681         return ERROR_SUCCESS;
4682
4683     rc = MSIREG_OpenFeaturesKey(package->ProductCode, package->Context,
4684                                 &hkey, TRUE);
4685     if (rc != ERROR_SUCCESS)
4686         goto end;
4687
4688     rc = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, package->Context,
4689                                         &userdata, TRUE);
4690     if (rc != ERROR_SUCCESS)
4691         goto end;
4692
4693     /* here the guids are base 85 encoded */
4694     LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
4695     {
4696         ComponentList *cl;
4697         LPWSTR data = NULL;
4698         GUID clsid;
4699         INT size;
4700         BOOL absent = FALSE;
4701         MSIRECORD *uirow;
4702
4703         if (feature->ActionRequest != INSTALLSTATE_LOCAL &&
4704             feature->ActionRequest != INSTALLSTATE_SOURCE &&
4705             feature->ActionRequest != INSTALLSTATE_ADVERTISED) absent = TRUE;
4706
4707         size = 1;
4708         LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
4709         {
4710             size += 21;
4711         }
4712         if (feature->Feature_Parent)
4713             size += strlenW( feature->Feature_Parent )+2;
4714
4715         data = msi_alloc(size * sizeof(WCHAR));
4716
4717         data[0] = 0;
4718         LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
4719         {
4720             MSICOMPONENT* component = cl->component;
4721             WCHAR buf[21];
4722
4723             buf[0] = 0;
4724             if (component->ComponentId)
4725             {
4726                 TRACE("From %s\n",debugstr_w(component->ComponentId));
4727                 CLSIDFromString(component->ComponentId, &clsid);
4728                 encode_base85_guid(&clsid,buf);
4729                 TRACE("to %s\n",debugstr_w(buf));
4730                 strcatW(data,buf);
4731             }
4732         }
4733
4734         if (feature->Feature_Parent)
4735         {
4736             static const WCHAR sep[] = {'\2',0};
4737             strcatW(data,sep);
4738             strcatW(data,feature->Feature_Parent);
4739         }
4740
4741         msi_reg_set_val_str( userdata, feature->Feature, data );
4742         msi_free(data);
4743
4744         size = 0;
4745         if (feature->Feature_Parent)
4746             size = strlenW(feature->Feature_Parent)*sizeof(WCHAR);
4747         if (!absent)
4748         {
4749             size += sizeof(WCHAR);
4750             RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
4751                            (const BYTE*)(feature->Feature_Parent ? feature->Feature_Parent : szEmpty),size);
4752         }
4753         else
4754         {
4755             size += 2*sizeof(WCHAR);
4756             data = msi_alloc(size);
4757             data[0] = 0x6;
4758             data[1] = 0;
4759             if (feature->Feature_Parent)
4760                 strcpyW( &data[1], feature->Feature_Parent );
4761             RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
4762                        (LPBYTE)data,size);
4763             msi_free(data);
4764         }
4765
4766         /* the UI chunk */
4767         uirow = MSI_CreateRecord( 1 );
4768         MSI_RecordSetStringW( uirow, 1, feature->Feature );
4769         ui_actiondata( package, szPublishFeatures, uirow);
4770         msiobj_release( &uirow->hdr );
4771         /* FIXME: call ui_progress? */
4772     }
4773
4774 end:
4775     RegCloseKey(hkey);
4776     RegCloseKey(userdata);
4777     return rc;
4778 }
4779
4780 static UINT msi_unpublish_feature(MSIPACKAGE *package, MSIFEATURE *feature)
4781 {
4782     UINT r;
4783     HKEY hkey;
4784     MSIRECORD *uirow;
4785
4786     TRACE("unpublishing feature %s\n", debugstr_w(feature->Feature));
4787
4788     r = MSIREG_OpenFeaturesKey(package->ProductCode, package->Context,
4789                                &hkey, FALSE);
4790     if (r == ERROR_SUCCESS)
4791     {
4792         RegDeleteValueW(hkey, feature->Feature);
4793         RegCloseKey(hkey);
4794     }
4795
4796     r = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, package->Context,
4797                                        &hkey, FALSE);
4798     if (r == ERROR_SUCCESS)
4799     {
4800         RegDeleteValueW(hkey, feature->Feature);
4801         RegCloseKey(hkey);
4802     }
4803
4804     uirow = MSI_CreateRecord( 1 );
4805     MSI_RecordSetStringW( uirow, 1, feature->Feature );
4806     ui_actiondata( package, szUnpublishFeatures, uirow );
4807     msiobj_release( &uirow->hdr );
4808
4809     return ERROR_SUCCESS;
4810 }
4811
4812 static UINT ACTION_UnpublishFeatures(MSIPACKAGE *package)
4813 {
4814     MSIFEATURE *feature;
4815
4816     if (!msi_check_unpublish(package))
4817         return ERROR_SUCCESS;
4818
4819     LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4820     {
4821         msi_unpublish_feature(package, feature);
4822     }
4823
4824     return ERROR_SUCCESS;
4825 }
4826
4827 static UINT msi_publish_install_properties(MSIPACKAGE *package, HKEY hkey)
4828 {
4829     SYSTEMTIME systime;
4830     DWORD size, langid;
4831     WCHAR date[9], *val, *buffer;
4832     const WCHAR *prop, *key;
4833
4834     static const WCHAR date_fmt[] = {'%','i','%','0','2','i','%','0','2','i',0};
4835     static const WCHAR szWindowsInstaller[] =
4836         {'W','i','n','d','o','w','s','I','n','s','t','a','l','l','e','r',0};
4837     static const WCHAR modpath_fmt[] =
4838         {'M','s','i','E','x','e','c','.','e','x','e',' ',
4839          '/','I','[','P','r','o','d','u','c','t','C','o','d','e',']',0};
4840     static const WCHAR szModifyPath[] =
4841         {'M','o','d','i','f','y','P','a','t','h',0};
4842     static const WCHAR szUninstallString[] =
4843         {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0};
4844     static const WCHAR szEstimatedSize[] =
4845         {'E','s','t','i','m','a','t','e','d','S','i','z','e',0};
4846     static const WCHAR szProductLanguage[] =
4847         {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
4848     static const WCHAR szProductVersion[] =
4849         {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
4850     static const WCHAR szDisplayVersion[] =
4851         {'D','i','s','p','l','a','y','V','e','r','s','i','o','n',0};
4852     static const WCHAR szInstallSource[] =
4853         {'I','n','s','t','a','l','l','S','o','u','r','c','e',0};
4854     static const WCHAR szARPAUTHORIZEDCDFPREFIX[] =
4855         {'A','R','P','A','U','T','H','O','R','I','Z','E','D','C','D','F','P','R','E','F','I','X',0};
4856     static const WCHAR szAuthorizedCDFPrefix[] =
4857         {'A','u','t','h','o','r','i','z','e','d','C','D','F','P','r','e','f','i','x',0};
4858     static const WCHAR szARPCONTACT[] =
4859         {'A','R','P','C','O','N','T','A','C','T',0};
4860     static const WCHAR szContact[] =
4861         {'C','o','n','t','a','c','t',0};
4862     static const WCHAR szARPCOMMENTS[] =
4863         {'A','R','P','C','O','M','M','E','N','T','S',0};
4864     static const WCHAR szComments[] =
4865         {'C','o','m','m','e','n','t','s',0};
4866     static const WCHAR szProductName[] =
4867         {'P','r','o','d','u','c','t','N','a','m','e',0};
4868     static const WCHAR szDisplayName[] =
4869         {'D','i','s','p','l','a','y','N','a','m','e',0};
4870     static const WCHAR szARPHELPLINK[] =
4871         {'A','R','P','H','E','L','P','L','I','N','K',0};
4872     static const WCHAR szHelpLink[] =
4873         {'H','e','l','p','L','i','n','k',0};
4874     static const WCHAR szARPHELPTELEPHONE[] =
4875         {'A','R','P','H','E','L','P','T','E','L','E','P','H','O','N','E',0};
4876     static const WCHAR szHelpTelephone[] =
4877         {'H','e','l','p','T','e','l','e','p','h','o','n','e',0};
4878     static const WCHAR szARPINSTALLLOCATION[] =
4879         {'A','R','P','I','N','S','T','A','L','L','L','O','C','A','T','I','O','N',0};
4880     static const WCHAR szInstallLocation[] =
4881         {'I','n','s','t','a','l','l','L','o','c','a','t','i','o','n',0};
4882     static const WCHAR szManufacturer[] =
4883         {'M','a','n','u','f','a','c','t','u','r','e','r',0};
4884     static const WCHAR szPublisher[] =
4885         {'P','u','b','l','i','s','h','e','r',0};
4886     static const WCHAR szARPREADME[] =
4887         {'A','R','P','R','E','A','D','M','E',0};
4888     static const WCHAR szReadme[] =
4889         {'R','e','a','d','M','e',0};
4890     static const WCHAR szARPSIZE[] =
4891         {'A','R','P','S','I','Z','E',0};
4892     static const WCHAR szSize[] =
4893         {'S','i','z','e',0};
4894     static const WCHAR szARPURLINFOABOUT[] =
4895         {'A','R','P','U','R','L','I','N','F','O','A','B','O','U','T',0};
4896     static const WCHAR szURLInfoAbout[] =
4897         {'U','R','L','I','n','f','o','A','b','o','u','t',0};
4898     static const WCHAR szARPURLUPDATEINFO[] =
4899         {'A','R','P','U','R','L','U','P','D','A','T','E','I','N','F','O',0};
4900     static const WCHAR szURLUpdateInfo[] =
4901         {'U','R','L','U','p','d','a','t','e','I','n','f','o',0};
4902
4903     static const WCHAR *propval[] = {
4904         szARPAUTHORIZEDCDFPREFIX, szAuthorizedCDFPrefix,
4905         szARPCONTACT,             szContact,
4906         szARPCOMMENTS,            szComments,
4907         szProductName,            szDisplayName,
4908         szARPHELPLINK,            szHelpLink,
4909         szARPHELPTELEPHONE,       szHelpTelephone,
4910         szARPINSTALLLOCATION,     szInstallLocation,
4911         cszSourceDir,             szInstallSource,
4912         szManufacturer,           szPublisher,
4913         szARPREADME,              szReadme,
4914         szARPSIZE,                szSize,
4915         szARPURLINFOABOUT,        szURLInfoAbout,
4916         szARPURLUPDATEINFO,       szURLUpdateInfo,
4917         NULL
4918     };
4919     const WCHAR **p = propval;
4920
4921     while (*p)
4922     {
4923         prop = *p++;
4924         key = *p++;
4925         val = msi_dup_property(package->db, prop);
4926         msi_reg_set_val_str(hkey, key, val);
4927         msi_free(val);
4928     }
4929
4930     msi_reg_set_val_dword(hkey, szWindowsInstaller, 1);
4931
4932     size = deformat_string(package, modpath_fmt, &buffer);
4933     RegSetValueExW(hkey, szModifyPath, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
4934     RegSetValueExW(hkey, szUninstallString, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
4935     msi_free(buffer);
4936
4937     /* FIXME: Write real Estimated Size when we have it */
4938     msi_reg_set_val_dword(hkey, szEstimatedSize, 0);
4939
4940     GetLocalTime(&systime);
4941     sprintfW(date, date_fmt, systime.wYear, systime.wMonth, systime.wDay);
4942     msi_reg_set_val_str(hkey, INSTALLPROPERTY_INSTALLDATEW, date);
4943
4944     langid = msi_get_property_int(package->db, szProductLanguage, 0);
4945     msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
4946
4947     buffer = msi_dup_property(package->db, szProductVersion);
4948     msi_reg_set_val_str(hkey, szDisplayVersion, buffer);
4949     if (buffer)
4950     {
4951         DWORD verdword = msi_version_str_to_dword(buffer);
4952
4953         msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
4954         msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMAJORW, verdword >> 24);
4955         msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMINORW, (verdword >> 16) & 0xFF);
4956         msi_free(buffer);
4957     }
4958
4959     return ERROR_SUCCESS;
4960 }
4961
4962 static UINT ACTION_RegisterProduct(MSIPACKAGE *package)
4963 {
4964     WCHAR squashed_pc[SQUISH_GUID_SIZE];
4965     MSIRECORD *uirow;
4966     LPWSTR upgrade_code;
4967     HKEY hkey, props;
4968     HKEY upgrade;
4969     UINT rc;
4970
4971     /* FIXME: also need to publish if the product is in advertise mode */
4972     if (!msi_check_publish(package))
4973         return ERROR_SUCCESS;
4974
4975     rc = MSIREG_OpenUninstallKey(package, &hkey, TRUE);
4976     if (rc != ERROR_SUCCESS)
4977         return rc;
4978
4979     rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context,
4980                                  NULL, &props, TRUE);
4981     if (rc != ERROR_SUCCESS)
4982         goto done;
4983
4984     msi_reg_set_val_str( props, INSTALLPROPERTY_LOCALPACKAGEW, package->db->localfile );
4985     msi_free( package->db->localfile );
4986     package->db->localfile = NULL;
4987
4988     rc = msi_publish_install_properties(package, hkey);
4989     if (rc != ERROR_SUCCESS)
4990         goto done;
4991
4992     rc = msi_publish_install_properties(package, props);
4993     if (rc != ERROR_SUCCESS)
4994         goto done;
4995
4996     upgrade_code = msi_dup_property(package->db, szUpgradeCode);
4997     if (upgrade_code)
4998     {
4999         MSIREG_OpenUpgradeCodesKey(upgrade_code, &upgrade, TRUE);
5000         squash_guid(package->ProductCode, squashed_pc);
5001         msi_reg_set_val_str(upgrade, squashed_pc, NULL);
5002         RegCloseKey(upgrade);
5003         msi_free(upgrade_code);
5004     }
5005
5006 done:
5007     uirow = MSI_CreateRecord( 1 );
5008     MSI_RecordSetStringW( uirow, 1, package->ProductCode );
5009     ui_actiondata( package, szRegisterProduct, uirow );
5010     msiobj_release( &uirow->hdr );
5011
5012     RegCloseKey(hkey);
5013     return ERROR_SUCCESS;
5014 }
5015
5016 static UINT ACTION_InstallExecute(MSIPACKAGE *package)
5017 {
5018     return execute_script(package,INSTALL_SCRIPT);
5019 }
5020
5021 static UINT msi_unpublish_product(MSIPACKAGE *package, WCHAR *remove)
5022 {
5023     WCHAR *upgrade, **features;
5024     BOOL full_uninstall = TRUE;
5025     MSIFEATURE *feature;
5026     MSIPATCHINFO *patch;
5027
5028     static const WCHAR szUpgradeCode[] =
5029         {'U','p','g','r','a','d','e','C','o','d','e',0};
5030
5031     features = msi_split_string(remove, ',');
5032     if (!features)
5033     {
5034         ERR("REMOVE feature list is empty!\n");
5035         return ERROR_FUNCTION_FAILED;
5036     }
5037
5038     if (!strcmpW( features[0], szAll ))
5039         full_uninstall = TRUE;
5040     else
5041     {
5042         LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
5043         {
5044             if (feature->Action != INSTALLSTATE_ABSENT)
5045                 full_uninstall = FALSE;
5046         }
5047     }
5048     msi_free(features);
5049
5050     if (!full_uninstall)
5051         return ERROR_SUCCESS;
5052
5053     MSIREG_DeleteProductKey(package->ProductCode);
5054     MSIREG_DeleteUserDataProductKey(package->ProductCode);
5055     MSIREG_DeleteUninstallKey(package);
5056
5057     MSIREG_DeleteLocalClassesProductKey(package->ProductCode);
5058     MSIREG_DeleteLocalClassesFeaturesKey(package->ProductCode);
5059     MSIREG_DeleteUserProductKey(package->ProductCode);
5060     MSIREG_DeleteUserFeaturesKey(package->ProductCode);
5061
5062     upgrade = msi_dup_property(package->db, szUpgradeCode);
5063     if (upgrade)
5064     {
5065         MSIREG_DeleteUserUpgradeCodesKey(upgrade);
5066         MSIREG_DeleteClassesUpgradeCodesKey(upgrade);
5067         msi_free(upgrade);
5068     }
5069
5070     LIST_FOR_EACH_ENTRY(patch, &package->patches, MSIPATCHINFO, entry)
5071     {
5072         MSIREG_DeleteUserDataPatchKey(patch->patchcode, package->Context);
5073     }
5074
5075     return ERROR_SUCCESS;
5076 }
5077
5078 static UINT ACTION_InstallFinalize(MSIPACKAGE *package)
5079 {
5080     UINT rc;
5081     WCHAR *remove;
5082
5083     /* turn off scheduling */
5084     package->script->CurrentlyScripting= FALSE;
5085
5086     /* first do the same as an InstallExecute */
5087     rc = ACTION_InstallExecute(package);
5088     if (rc != ERROR_SUCCESS)
5089         return rc;
5090
5091     /* then handle Commit Actions */
5092     rc = execute_script(package,COMMIT_SCRIPT);
5093     if (rc != ERROR_SUCCESS)
5094         return rc;
5095
5096     remove = msi_dup_property(package->db, szRemove);
5097     if (remove)
5098         rc = msi_unpublish_product(package, remove);
5099
5100     msi_free(remove);
5101     return rc;
5102 }
5103
5104 UINT ACTION_ForceReboot(MSIPACKAGE *package)
5105 {
5106     static const WCHAR RunOnce[] = {
5107     'S','o','f','t','w','a','r','e','\\',
5108     'M','i','c','r','o','s','o','f','t','\\',
5109     'W','i','n','d','o','w','s','\\',
5110     'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
5111     'R','u','n','O','n','c','e',0};
5112     static const WCHAR InstallRunOnce[] = {
5113     'S','o','f','t','w','a','r','e','\\',
5114     'M','i','c','r','o','s','o','f','t','\\',
5115     'W','i','n','d','o','w','s','\\',
5116     'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
5117     'I','n','s','t','a','l','l','e','r','\\',
5118     'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
5119
5120     static const WCHAR msiexec_fmt[] = {
5121     '%','s',
5122     '\\','M','s','i','E','x','e','c','.','e','x','e',' ','/','@',' ',
5123     '\"','%','s','\"',0};
5124     static const WCHAR install_fmt[] = {
5125     '/','I',' ','\"','%','s','\"',' ',
5126     'A','F','T','E','R','R','E','B','O','O','T','=','1',' ',
5127     'R','U','N','O','N','C','E','E','N','T','R','Y','=','\"','%','s','\"',0};
5128     WCHAR buffer[256], sysdir[MAX_PATH];
5129     HKEY hkey;
5130     WCHAR squished_pc[100];
5131
5132     squash_guid(package->ProductCode,squished_pc);
5133
5134     GetSystemDirectoryW(sysdir, sizeof(sysdir)/sizeof(sysdir[0]));
5135     RegCreateKeyW(HKEY_LOCAL_MACHINE,RunOnce,&hkey);
5136     snprintfW(buffer,sizeof(buffer)/sizeof(buffer[0]),msiexec_fmt,sysdir,
5137      squished_pc);
5138
5139     msi_reg_set_val_str( hkey, squished_pc, buffer );
5140     RegCloseKey(hkey);
5141
5142     TRACE("Reboot command %s\n",debugstr_w(buffer));
5143
5144     RegCreateKeyW(HKEY_LOCAL_MACHINE,InstallRunOnce,&hkey);
5145     sprintfW(buffer,install_fmt,package->ProductCode,squished_pc);
5146
5147     msi_reg_set_val_str( hkey, squished_pc, buffer );
5148     RegCloseKey(hkey);
5149
5150     return ERROR_INSTALL_SUSPEND;
5151 }
5152
5153 static UINT ACTION_ResolveSource(MSIPACKAGE* package)
5154 {
5155     DWORD attrib;
5156     UINT rc;
5157
5158     /*
5159      * We are currently doing what should be done here in the top level Install
5160      * however for Administrative and uninstalls this step will be needed
5161      */
5162     if (!package->PackagePath)
5163         return ERROR_SUCCESS;
5164
5165     msi_set_sourcedir_props(package, TRUE);
5166
5167     attrib = GetFileAttributesW(package->db->path);
5168     if (attrib == INVALID_FILE_ATTRIBUTES)
5169     {
5170         LPWSTR prompt;
5171         LPWSTR msg;
5172         DWORD size = 0;
5173
5174         rc = MsiSourceListGetInfoW(package->ProductCode, NULL, 
5175                 package->Context, MSICODE_PRODUCT,
5176                 INSTALLPROPERTY_DISKPROMPTW,NULL,&size);
5177         if (rc == ERROR_MORE_DATA)
5178         {
5179             prompt = msi_alloc(size * sizeof(WCHAR));
5180             MsiSourceListGetInfoW(package->ProductCode, NULL, 
5181                     package->Context, MSICODE_PRODUCT,
5182                     INSTALLPROPERTY_DISKPROMPTW,prompt,&size);
5183         }
5184         else
5185             prompt = strdupW(package->db->path);
5186
5187         msg = generate_error_string(package,1302,1,prompt);
5188         while(attrib == INVALID_FILE_ATTRIBUTES)
5189         {
5190             rc = MessageBoxW(NULL,msg,NULL,MB_OKCANCEL);
5191             if (rc == IDCANCEL)
5192             {
5193                 rc = ERROR_INSTALL_USEREXIT;
5194                 break;
5195             }
5196             attrib = GetFileAttributesW(package->db->path);
5197         }
5198         msi_free(prompt);
5199         rc = ERROR_SUCCESS;
5200     }
5201     else
5202         return ERROR_SUCCESS;
5203
5204     return rc;
5205 }
5206
5207 static UINT ACTION_RegisterUser(MSIPACKAGE *package)
5208 {
5209     HKEY hkey = 0;
5210     LPWSTR buffer, productid = NULL;
5211     UINT i, rc = ERROR_SUCCESS;
5212     MSIRECORD *uirow;
5213
5214     static const WCHAR szPropKeys[][80] = 
5215     {
5216         {'P','r','o','d','u','c','t','I','D',0},
5217         {'U','S','E','R','N','A','M','E',0},
5218         {'C','O','M','P','A','N','Y','N','A','M','E',0},
5219         {0},
5220     };
5221
5222     static const WCHAR szRegKeys[][80] = 
5223     {
5224         {'P','r','o','d','u','c','t','I','D',0},
5225         {'R','e','g','O','w','n','e','r',0},
5226         {'R','e','g','C','o','m','p','a','n','y',0},
5227         {0},
5228     };
5229
5230     if (msi_check_unpublish(package))
5231     {
5232         MSIREG_DeleteUserDataProductKey(package->ProductCode);
5233         goto end;
5234     }
5235
5236     productid = msi_dup_property( package->db, INSTALLPROPERTY_PRODUCTIDW );
5237     if (!productid)
5238         goto end;
5239
5240     rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context,
5241                                  NULL, &hkey, TRUE);
5242     if (rc != ERROR_SUCCESS)
5243         goto end;
5244
5245     for( i = 0; szPropKeys[i][0]; i++ )
5246     {
5247         buffer = msi_dup_property( package->db, szPropKeys[i] );
5248         msi_reg_set_val_str( hkey, szRegKeys[i], buffer );
5249         msi_free( buffer );
5250     }
5251
5252 end:
5253     uirow = MSI_CreateRecord( 1 );
5254     MSI_RecordSetStringW( uirow, 1, productid );
5255     ui_actiondata( package, szRegisterUser, uirow );
5256     msiobj_release( &uirow->hdr );
5257
5258     msi_free(productid);
5259     RegCloseKey(hkey);
5260     return rc;
5261 }
5262
5263
5264 static UINT ACTION_ExecuteAction(MSIPACKAGE *package)
5265 {
5266     UINT rc;
5267
5268     package->script->InWhatSequence |= SEQUENCE_EXEC;
5269     rc = ACTION_ProcessExecSequence(package,FALSE);
5270     return rc;
5271 }
5272
5273
5274 static UINT ITERATE_PublishComponent(MSIRECORD *rec, LPVOID param)
5275 {
5276     MSIPACKAGE *package = param;
5277     LPCWSTR compgroupid, component, feature, qualifier, text;
5278     LPWSTR advertise = NULL, output = NULL, existing = NULL, p, q;
5279     HKEY hkey = NULL;
5280     UINT rc;
5281     MSICOMPONENT *comp;
5282     MSIFEATURE *feat;
5283     DWORD sz;
5284     MSIRECORD *uirow;
5285     int len;
5286
5287     feature = MSI_RecordGetString(rec, 5);
5288     feat = get_loaded_feature(package, feature);
5289     if (!feat)
5290         return ERROR_SUCCESS;
5291
5292     if (feat->ActionRequest != INSTALLSTATE_LOCAL &&
5293         feat->ActionRequest != INSTALLSTATE_SOURCE &&
5294         feat->ActionRequest != INSTALLSTATE_ADVERTISED)
5295     {
5296         TRACE("Feature %s not scheduled for installation\n", debugstr_w(feature));
5297         feat->Action = feat->Installed;
5298         return ERROR_SUCCESS;
5299     }
5300
5301     component = MSI_RecordGetString(rec, 3);
5302     comp = get_loaded_component(package, component);
5303     if (!comp)
5304         return ERROR_SUCCESS;
5305
5306     compgroupid = MSI_RecordGetString(rec,1);
5307     qualifier = MSI_RecordGetString(rec,2);
5308
5309     rc = MSIREG_OpenUserComponentsKey(compgroupid, &hkey, TRUE);
5310     if (rc != ERROR_SUCCESS)
5311         goto end;
5312
5313     advertise = create_component_advertise_string( package, comp, feature );
5314     text = MSI_RecordGetString( rec, 4 );
5315     if (text)
5316     {
5317         p = msi_alloc( (strlenW( advertise ) + strlenW( text ) + 1) * sizeof(WCHAR) );
5318         strcpyW( p, advertise );
5319         strcatW( p, text );
5320         msi_free( advertise );
5321         advertise = p;
5322     }
5323     existing = msi_reg_get_val_str( hkey, qualifier );
5324
5325     sz = strlenW( advertise ) + 1;
5326     if (existing)
5327     {
5328         for (p = existing; *p; p += len)
5329         {
5330             len = strlenW( p ) + 1;
5331             if (strcmpW( advertise, p )) sz += len;
5332         }
5333     }
5334     if (!(output = msi_alloc( (sz + 1) * sizeof(WCHAR) )))
5335     {
5336         rc = ERROR_OUTOFMEMORY;
5337         goto end;
5338     }
5339     q = output;
5340     if (existing)
5341     {
5342         for (p = existing; *p; p += len)
5343         {
5344             len = strlenW( p ) + 1;
5345             if (strcmpW( advertise, p ))
5346             {
5347                 memcpy( q, p, len * sizeof(WCHAR) );
5348                 q += len;
5349             }
5350         }
5351     }
5352     strcpyW( q, advertise );
5353     q[strlenW( q ) + 1] = 0;
5354
5355     msi_reg_set_val_multi_str( hkey, qualifier, output );
5356     
5357 end:
5358     RegCloseKey(hkey);
5359     msi_free( output );
5360     msi_free( advertise );
5361     msi_free( existing );
5362
5363     /* the UI chunk */
5364     uirow = MSI_CreateRecord( 2 );
5365     MSI_RecordSetStringW( uirow, 1, compgroupid );
5366     MSI_RecordSetStringW( uirow, 2, qualifier);
5367     ui_actiondata( package, szPublishComponents, uirow);
5368     msiobj_release( &uirow->hdr );
5369     /* FIXME: call ui_progress? */
5370
5371     return rc;
5372 }
5373
5374 /*
5375  * At present I am ignorning the advertised components part of this and only
5376  * focusing on the qualified component sets
5377  */
5378 static UINT ACTION_PublishComponents(MSIPACKAGE *package)
5379 {
5380     UINT rc;
5381     MSIQUERY * view;
5382     static const WCHAR ExecSeqQuery[] =
5383         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5384          '`','P','u','b','l','i','s','h',
5385          'C','o','m','p','o','n','e','n','t','`',0};
5386     
5387     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
5388     if (rc != ERROR_SUCCESS)
5389         return ERROR_SUCCESS;
5390
5391     rc = MSI_IterateRecords(view, NULL, ITERATE_PublishComponent, package);
5392     msiobj_release(&view->hdr);
5393
5394     return rc;
5395 }
5396
5397 static UINT ITERATE_UnpublishComponent( MSIRECORD *rec, LPVOID param )
5398 {
5399     static const WCHAR szInstallerComponents[] = {
5400         'S','o','f','t','w','a','r','e','\\',
5401         'M','i','c','r','o','s','o','f','t','\\',
5402         'I','n','s','t','a','l','l','e','r','\\',
5403         'C','o','m','p','o','n','e','n','t','s','\\',0};
5404
5405     MSIPACKAGE *package = param;
5406     LPCWSTR compgroupid, component, feature, qualifier;
5407     MSICOMPONENT *comp;
5408     MSIFEATURE *feat;
5409     MSIRECORD *uirow;
5410     WCHAR squashed[GUID_SIZE], keypath[MAX_PATH];
5411     LONG res;
5412
5413     feature = MSI_RecordGetString( rec, 5 );
5414     feat = get_loaded_feature( package, feature );
5415     if (!feat)
5416         return ERROR_SUCCESS;
5417
5418     if (feat->ActionRequest != INSTALLSTATE_ABSENT)
5419     {
5420         TRACE("Feature %s not scheduled for removal\n", debugstr_w(feature));
5421         feat->Action = feat->Installed;
5422         return ERROR_SUCCESS;
5423     }
5424
5425     component = MSI_RecordGetString( rec, 3 );
5426     comp = get_loaded_component( package, component );
5427     if (!comp)
5428         return ERROR_SUCCESS;
5429
5430     compgroupid = MSI_RecordGetString( rec, 1 );
5431     qualifier = MSI_RecordGetString( rec, 2 );
5432
5433     squash_guid( compgroupid, squashed );
5434     strcpyW( keypath, szInstallerComponents );
5435     strcatW( keypath, squashed );
5436
5437     res = RegDeleteKeyW( HKEY_CURRENT_USER, keypath );
5438     if (res != ERROR_SUCCESS)
5439     {
5440         WARN("Unable to delete component key %d\n", res);
5441     }
5442
5443     uirow = MSI_CreateRecord( 2 );
5444     MSI_RecordSetStringW( uirow, 1, compgroupid );
5445     MSI_RecordSetStringW( uirow, 2, qualifier );
5446     ui_actiondata( package, szUnpublishComponents, uirow );
5447     msiobj_release( &uirow->hdr );
5448
5449     return ERROR_SUCCESS;
5450 }
5451
5452 static UINT ACTION_UnpublishComponents( MSIPACKAGE *package )
5453 {
5454     UINT rc;
5455     MSIQUERY *view;
5456     static const WCHAR query[] =
5457         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5458          '`','P','u','b','l','i','s','h',
5459          'C','o','m','p','o','n','e','n','t','`',0};
5460
5461     rc = MSI_DatabaseOpenViewW( package->db, query, &view );
5462     if (rc != ERROR_SUCCESS)
5463         return ERROR_SUCCESS;
5464
5465     rc = MSI_IterateRecords( view, NULL, ITERATE_UnpublishComponent, package );
5466     msiobj_release( &view->hdr );
5467
5468     return rc;
5469 }
5470
5471 static UINT ITERATE_InstallService(MSIRECORD *rec, LPVOID param)
5472 {
5473     MSIPACKAGE *package = param;
5474     MSIRECORD *row;
5475     MSIFILE *file;
5476     SC_HANDLE hscm, service = NULL;
5477     LPCWSTR comp, key;
5478     LPWSTR name = NULL, disp = NULL, load_order = NULL, serv_name = NULL;
5479     LPWSTR depends = NULL, pass = NULL, args = NULL, image_path = NULL;
5480     DWORD serv_type, start_type, err_control;
5481     SERVICE_DESCRIPTIONW sd = {NULL};
5482
5483     static const WCHAR query[] =
5484         {'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
5485          '`','C','o','m','p','o','n','e','n','t','`',' ',
5486          'W','H','E','R','E',' ',
5487          '`','C','o','m','p','o','n','e','n','t','`',' ',
5488          '=','\'','%','s','\'',0};
5489
5490     hscm = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASEW, GENERIC_WRITE);
5491     if (!hscm)
5492     {
5493         ERR("Failed to open the SC Manager!\n");
5494         goto done;
5495     }
5496
5497     comp = MSI_RecordGetString( rec, 12 );
5498     if (!get_loaded_component( package, comp ))
5499         goto done;
5500
5501     start_type = MSI_RecordGetInteger(rec, 5);
5502     if (start_type == SERVICE_BOOT_START || start_type == SERVICE_SYSTEM_START)
5503         goto done;
5504
5505     deformat_string(package, MSI_RecordGetString(rec, 2), &name);
5506     deformat_string(package, MSI_RecordGetString(rec, 3), &disp);
5507     serv_type = MSI_RecordGetInteger(rec, 4);
5508     err_control = MSI_RecordGetInteger(rec, 6);
5509     deformat_string(package, MSI_RecordGetString(rec, 7), &load_order);
5510     deformat_string(package, MSI_RecordGetString(rec, 8), &depends);
5511     deformat_string(package, MSI_RecordGetString(rec, 9), &serv_name);
5512     deformat_string(package, MSI_RecordGetString(rec, 10), &pass);
5513     deformat_string(package, MSI_RecordGetString(rec, 11), &args);
5514     deformat_string(package, MSI_RecordGetString(rec, 13), &sd.lpDescription);
5515
5516     /* fetch the service path */
5517     row = MSI_QueryGetRecord(package->db, query, comp);
5518     if (!row)
5519     {
5520         ERR("Control query failed!\n");
5521         goto done;
5522     }
5523     key = MSI_RecordGetString(row, 6);
5524
5525     file = get_loaded_file(package, key);
5526     msiobj_release(&row->hdr);
5527     if (!file)
5528     {
5529         ERR("Failed to load the service file\n");
5530         goto done;
5531     }
5532
5533     if (!args || !args[0]) image_path = file->TargetPath;
5534     else
5535     {
5536         int len = strlenW(file->TargetPath) + strlenW(args) + 2;
5537         if (!(image_path = msi_alloc(len * sizeof(WCHAR))))
5538             return ERROR_OUTOFMEMORY;
5539
5540         strcpyW(image_path, file->TargetPath);
5541         strcatW(image_path, szSpace);
5542         strcatW(image_path, args);
5543     }
5544     service = CreateServiceW(hscm, name, disp, GENERIC_ALL, serv_type,
5545                              start_type, err_control, image_path, load_order,
5546                              NULL, depends, serv_name, pass);
5547
5548     if (!service)
5549     {
5550         if (GetLastError() != ERROR_SERVICE_EXISTS)
5551             ERR("Failed to create service %s: %d\n", debugstr_w(name), GetLastError());
5552     }
5553     else if (sd.lpDescription)
5554     {
5555         if (!ChangeServiceConfig2W(service, SERVICE_CONFIG_DESCRIPTION, &sd))
5556             WARN("failed to set service description %u\n", GetLastError());
5557     }
5558
5559     if (image_path != file->TargetPath) msi_free(image_path);
5560 done:
5561     CloseServiceHandle(service);
5562     CloseServiceHandle(hscm);
5563     msi_free(name);
5564     msi_free(disp);
5565     msi_free(sd.lpDescription);
5566     msi_free(load_order);
5567     msi_free(serv_name);
5568     msi_free(pass);
5569     msi_free(depends);
5570     msi_free(args);
5571
5572     return ERROR_SUCCESS;
5573 }
5574
5575 static UINT ACTION_InstallServices( MSIPACKAGE *package )
5576 {
5577     UINT rc;
5578     MSIQUERY * view;
5579     static const WCHAR ExecSeqQuery[] =
5580         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5581          'S','e','r','v','i','c','e','I','n','s','t','a','l','l',0};
5582     
5583     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
5584     if (rc != ERROR_SUCCESS)
5585         return ERROR_SUCCESS;
5586
5587     rc = MSI_IterateRecords(view, NULL, ITERATE_InstallService, package);
5588     msiobj_release(&view->hdr);
5589
5590     return rc;
5591 }
5592
5593 /* converts arg1[~]arg2[~]arg3 to a list of ptrs to the strings */
5594 static LPCWSTR *msi_service_args_to_vector(LPWSTR args, DWORD *numargs)
5595 {
5596     LPCWSTR *vector, *temp_vector;
5597     LPWSTR p, q;
5598     DWORD sep_len;
5599
5600     static const WCHAR separator[] = {'[','~',']',0};
5601
5602     *numargs = 0;
5603     sep_len = sizeof(separator) / sizeof(WCHAR) - 1;
5604
5605     if (!args)
5606         return NULL;
5607
5608     vector = msi_alloc(sizeof(LPWSTR));
5609     if (!vector)
5610         return NULL;
5611
5612     p = args;
5613     do
5614     {
5615         (*numargs)++;
5616         vector[*numargs - 1] = p;
5617
5618         if ((q = strstrW(p, separator)))
5619         {
5620             *q = '\0';
5621
5622             temp_vector = msi_realloc(vector, (*numargs + 1) * sizeof(LPWSTR));
5623             if (!temp_vector)
5624             {
5625                 msi_free(vector);
5626                 return NULL;
5627             }
5628             vector = temp_vector;
5629
5630             p = q + sep_len;
5631         }
5632     } while (q);
5633
5634     return vector;
5635 }
5636
5637 static UINT ITERATE_StartService(MSIRECORD *rec, LPVOID param)
5638 {
5639     MSIPACKAGE *package = param;
5640     MSICOMPONENT *comp;
5641     MSIRECORD *uirow;
5642     SC_HANDLE scm = NULL, service = NULL;
5643     LPCWSTR component, *vector = NULL;
5644     LPWSTR name, args, display_name = NULL;
5645     DWORD event, numargs, len;
5646     UINT r = ERROR_FUNCTION_FAILED;
5647
5648     component = MSI_RecordGetString(rec, 6);
5649     comp = get_loaded_component(package, component);
5650     if (!comp)
5651         return ERROR_SUCCESS;
5652
5653     if (!comp->Enabled)
5654     {
5655         TRACE("component is disabled\n");
5656         return ERROR_SUCCESS;
5657     }
5658
5659     if (comp->ActionRequest != INSTALLSTATE_LOCAL)
5660     {
5661         TRACE("Component not scheduled for installation: %s\n", debugstr_w(component));
5662         comp->Action = comp->Installed;
5663         return ERROR_SUCCESS;
5664     }
5665     comp->Action = INSTALLSTATE_LOCAL;
5666
5667     deformat_string(package, MSI_RecordGetString(rec, 2), &name);
5668     deformat_string(package, MSI_RecordGetString(rec, 4), &args);
5669     event = MSI_RecordGetInteger(rec, 3);
5670
5671     if (!(event & msidbServiceControlEventStart))
5672     {
5673         r = ERROR_SUCCESS;
5674         goto done;
5675     }
5676
5677     scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
5678     if (!scm)
5679     {
5680         ERR("Failed to open the service control manager\n");
5681         goto done;
5682     }
5683
5684     len = 0;
5685     if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
5686         GetLastError() == ERROR_INSUFFICIENT_BUFFER)
5687     {
5688         if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
5689             GetServiceDisplayNameW( scm, name, display_name, &len );
5690     }
5691
5692     service = OpenServiceW(scm, name, SERVICE_START);
5693     if (!service)
5694     {
5695         ERR("Failed to open service %s (%u)\n", debugstr_w(name), GetLastError());
5696         goto done;
5697     }
5698
5699     vector = msi_service_args_to_vector(args, &numargs);
5700
5701     if (!StartServiceW(service, numargs, vector) &&
5702         GetLastError() != ERROR_SERVICE_ALREADY_RUNNING)
5703     {
5704         ERR("Failed to start service %s (%u)\n", debugstr_w(name), GetLastError());
5705         goto done;
5706     }
5707
5708     r = ERROR_SUCCESS;
5709
5710 done:
5711     uirow = MSI_CreateRecord( 2 );
5712     MSI_RecordSetStringW( uirow, 1, display_name );
5713     MSI_RecordSetStringW( uirow, 2, name );
5714     ui_actiondata( package, szStartServices, uirow );
5715     msiobj_release( &uirow->hdr );
5716
5717     CloseServiceHandle(service);
5718     CloseServiceHandle(scm);
5719
5720     msi_free(name);
5721     msi_free(args);
5722     msi_free(vector);
5723     msi_free(display_name);
5724     return r;
5725 }
5726
5727 static UINT ACTION_StartServices( MSIPACKAGE *package )
5728 {
5729     UINT rc;
5730     MSIQUERY *view;
5731
5732     static const WCHAR query[] = {
5733         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5734         'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
5735
5736     rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5737     if (rc != ERROR_SUCCESS)
5738         return ERROR_SUCCESS;
5739
5740     rc = MSI_IterateRecords(view, NULL, ITERATE_StartService, package);
5741     msiobj_release(&view->hdr);
5742
5743     return rc;
5744 }
5745
5746 static BOOL stop_service_dependents(SC_HANDLE scm, SC_HANDLE service)
5747 {
5748     DWORD i, needed, count;
5749     ENUM_SERVICE_STATUSW *dependencies;
5750     SERVICE_STATUS ss;
5751     SC_HANDLE depserv;
5752
5753     if (EnumDependentServicesW(service, SERVICE_ACTIVE, NULL,
5754                                0, &needed, &count))
5755         return TRUE;
5756
5757     if (GetLastError() != ERROR_MORE_DATA)
5758         return FALSE;
5759
5760     dependencies = msi_alloc(needed);
5761     if (!dependencies)
5762         return FALSE;
5763
5764     if (!EnumDependentServicesW(service, SERVICE_ACTIVE, dependencies,
5765                                 needed, &needed, &count))
5766         goto error;
5767
5768     for (i = 0; i < count; i++)
5769     {
5770         depserv = OpenServiceW(scm, dependencies[i].lpServiceName,
5771                                SERVICE_STOP | SERVICE_QUERY_STATUS);
5772         if (!depserv)
5773             goto error;
5774
5775         if (!ControlService(depserv, SERVICE_CONTROL_STOP, &ss))
5776             goto error;
5777     }
5778
5779     return TRUE;
5780
5781 error:
5782     msi_free(dependencies);
5783     return FALSE;
5784 }
5785
5786 static UINT stop_service( LPCWSTR name )
5787 {
5788     SC_HANDLE scm = NULL, service = NULL;
5789     SERVICE_STATUS status;
5790     SERVICE_STATUS_PROCESS ssp;
5791     DWORD needed;
5792
5793     scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
5794     if (!scm)
5795     {
5796         WARN("Failed to open the SCM: %d\n", GetLastError());
5797         goto done;
5798     }
5799
5800     service = OpenServiceW(scm, name,
5801                            SERVICE_STOP |
5802                            SERVICE_QUERY_STATUS |
5803                            SERVICE_ENUMERATE_DEPENDENTS);
5804     if (!service)
5805     {
5806         WARN("Failed to open service (%s): %d\n", debugstr_w(name), GetLastError());
5807         goto done;
5808     }
5809
5810     if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp,
5811                               sizeof(SERVICE_STATUS_PROCESS), &needed))
5812     {
5813         WARN("Failed to query service status (%s): %d\n", debugstr_w(name), GetLastError());
5814         goto done;
5815     }
5816
5817     if (ssp.dwCurrentState == SERVICE_STOPPED)
5818         goto done;
5819
5820     stop_service_dependents(scm, service);
5821
5822     if (!ControlService(service, SERVICE_CONTROL_STOP, &status))
5823         WARN("Failed to stop service (%s): %d\n", debugstr_w(name), GetLastError());
5824
5825 done:
5826     CloseServiceHandle(service);
5827     CloseServiceHandle(scm);
5828
5829     return ERROR_SUCCESS;
5830 }
5831
5832 static UINT ITERATE_StopService( MSIRECORD *rec, LPVOID param )
5833 {
5834     MSIPACKAGE *package = param;
5835     MSICOMPONENT *comp;
5836     MSIRECORD *uirow;
5837     LPCWSTR component;
5838     LPWSTR name = NULL, display_name = NULL;
5839     DWORD event, len;
5840     SC_HANDLE scm;
5841
5842     event = MSI_RecordGetInteger( rec, 3 );
5843     if (!(event & msidbServiceControlEventStop))
5844         return ERROR_SUCCESS;
5845
5846     component = MSI_RecordGetString( rec, 6 );
5847     comp = get_loaded_component( package, component );
5848     if (!comp)
5849         return ERROR_SUCCESS;
5850
5851     if (!comp->Enabled)
5852     {
5853         TRACE("component is disabled\n");
5854         return ERROR_SUCCESS;
5855     }
5856
5857     if (comp->ActionRequest != INSTALLSTATE_ABSENT)
5858     {
5859         TRACE("Component not scheduled for removal: %s\n", debugstr_w(component));
5860         comp->Action = comp->Installed;
5861         return ERROR_SUCCESS;
5862     }
5863     comp->Action = INSTALLSTATE_ABSENT;
5864
5865     scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_CONNECT );
5866     if (!scm)
5867     {
5868         ERR("Failed to open the service control manager\n");
5869         goto done;
5870     }
5871
5872     len = 0;
5873     if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
5874         GetLastError() == ERROR_INSUFFICIENT_BUFFER)
5875     {
5876         if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
5877             GetServiceDisplayNameW( scm, name, display_name, &len );
5878     }
5879     CloseServiceHandle( scm );
5880
5881     deformat_string( package, MSI_RecordGetString( rec, 2 ), &name );
5882     stop_service( name );
5883
5884 done:
5885     uirow = MSI_CreateRecord( 2 );
5886     MSI_RecordSetStringW( uirow, 1, display_name );
5887     MSI_RecordSetStringW( uirow, 2, name );
5888     ui_actiondata( package, szStopServices, uirow );
5889     msiobj_release( &uirow->hdr );
5890
5891     msi_free( name );
5892     msi_free( display_name );
5893     return ERROR_SUCCESS;
5894 }
5895
5896 static UINT ACTION_StopServices( MSIPACKAGE *package )
5897 {
5898     UINT rc;
5899     MSIQUERY *view;
5900
5901     static const WCHAR query[] = {
5902         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5903         'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
5904
5905     rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5906     if (rc != ERROR_SUCCESS)
5907         return ERROR_SUCCESS;
5908
5909     rc = MSI_IterateRecords(view, NULL, ITERATE_StopService, package);
5910     msiobj_release(&view->hdr);
5911
5912     return rc;
5913 }
5914
5915 static UINT ITERATE_DeleteService( MSIRECORD *rec, LPVOID param )
5916 {
5917     MSIPACKAGE *package = param;
5918     MSICOMPONENT *comp;
5919     MSIRECORD *uirow;
5920     LPCWSTR component;
5921     LPWSTR name = NULL, display_name = NULL;
5922     DWORD event, len;
5923     SC_HANDLE scm = NULL, service = NULL;
5924
5925     event = MSI_RecordGetInteger( rec, 3 );
5926     if (!(event & msidbServiceControlEventDelete))
5927         return ERROR_SUCCESS;
5928
5929     component = MSI_RecordGetString(rec, 6);
5930     comp = get_loaded_component(package, component);
5931     if (!comp)
5932         return ERROR_SUCCESS;
5933
5934     if (!comp->Enabled)
5935     {
5936         TRACE("component is disabled\n");
5937         return ERROR_SUCCESS;
5938     }
5939
5940     if (comp->ActionRequest != INSTALLSTATE_ABSENT)
5941     {
5942         TRACE("Component not scheduled for removal: %s\n", debugstr_w(component));
5943         comp->Action = comp->Installed;
5944         return ERROR_SUCCESS;
5945     }
5946     comp->Action = INSTALLSTATE_ABSENT;
5947
5948     deformat_string( package, MSI_RecordGetString(rec, 2), &name );
5949     stop_service( name );
5950
5951     scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_ALL_ACCESS );
5952     if (!scm)
5953     {
5954         WARN("Failed to open the SCM: %d\n", GetLastError());
5955         goto done;
5956     }
5957
5958     len = 0;
5959     if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
5960         GetLastError() == ERROR_INSUFFICIENT_BUFFER)
5961     {
5962         if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
5963             GetServiceDisplayNameW( scm, name, display_name, &len );
5964     }
5965
5966     service = OpenServiceW( scm, name, DELETE );
5967     if (!service)
5968     {
5969         WARN("Failed to open service (%s): %u\n", debugstr_w(name), GetLastError());
5970         goto done;
5971     }
5972
5973     if (!DeleteService( service ))
5974         WARN("Failed to delete service (%s): %u\n", debugstr_w(name), GetLastError());
5975
5976 done:
5977     uirow = MSI_CreateRecord( 2 );
5978     MSI_RecordSetStringW( uirow, 1, display_name );
5979     MSI_RecordSetStringW( uirow, 2, name );
5980     ui_actiondata( package, szDeleteServices, uirow );
5981     msiobj_release( &uirow->hdr );
5982
5983     CloseServiceHandle( service );
5984     CloseServiceHandle( scm );
5985     msi_free( name );
5986     msi_free( display_name );
5987
5988     return ERROR_SUCCESS;
5989 }
5990
5991 static UINT ACTION_DeleteServices( MSIPACKAGE *package )
5992 {
5993     UINT rc;
5994     MSIQUERY *view;
5995
5996     static const WCHAR query[] = {
5997         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5998         'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
5999
6000     rc = MSI_DatabaseOpenViewW( package->db, query, &view );
6001     if (rc != ERROR_SUCCESS)
6002         return ERROR_SUCCESS;
6003
6004     rc = MSI_IterateRecords( view, NULL, ITERATE_DeleteService, package );
6005     msiobj_release( &view->hdr );
6006
6007     return rc;
6008 }
6009
6010 static UINT ITERATE_InstallODBCDriver( MSIRECORD *rec, LPVOID param )
6011 {
6012     MSIPACKAGE *package = param;
6013     LPWSTR driver, driver_path, ptr;
6014     WCHAR outpath[MAX_PATH];
6015     MSIFILE *driver_file = NULL, *setup_file = NULL;
6016     MSICOMPONENT *comp;
6017     MSIRECORD *uirow;
6018     LPCWSTR desc, file_key, component;
6019     DWORD len, usage;
6020     UINT r = ERROR_SUCCESS;
6021
6022     static const WCHAR driver_fmt[] = {
6023         'D','r','i','v','e','r','=','%','s',0};
6024     static const WCHAR setup_fmt[] = {
6025         'S','e','t','u','p','=','%','s',0};
6026     static const WCHAR usage_fmt[] = {
6027         'F','i','l','e','U','s','a','g','e','=','1',0};
6028
6029     component = MSI_RecordGetString( rec, 2 );
6030     comp = get_loaded_component( package, component );
6031     if (!comp)
6032         return ERROR_SUCCESS;
6033
6034     if (!comp->Enabled)
6035     {
6036         TRACE("component is disabled\n");
6037         return ERROR_SUCCESS;
6038     }
6039
6040     desc = MSI_RecordGetString(rec, 3);
6041
6042     file_key = MSI_RecordGetString( rec, 4 );
6043     if (file_key) driver_file = get_loaded_file( package, file_key );
6044
6045     file_key = MSI_RecordGetString( rec, 5 );
6046     if (file_key) setup_file = get_loaded_file( package, file_key );
6047
6048     if (!driver_file)
6049     {
6050         ERR("ODBC Driver entry not found!\n");
6051         return ERROR_FUNCTION_FAILED;
6052     }
6053
6054     len = lstrlenW(desc) + lstrlenW(driver_fmt) + lstrlenW(driver_file->FileName);
6055     if (setup_file)
6056         len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
6057     len += lstrlenW(usage_fmt) + 2; /* \0\0 */
6058
6059     driver = msi_alloc(len * sizeof(WCHAR));
6060     if (!driver)
6061         return ERROR_OUTOFMEMORY;
6062
6063     ptr = driver;
6064     lstrcpyW(ptr, desc);
6065     ptr += lstrlenW(ptr) + 1;
6066
6067     len = sprintfW(ptr, driver_fmt, driver_file->FileName);
6068     ptr += len + 1;
6069
6070     if (setup_file)
6071     {
6072         len = sprintfW(ptr, setup_fmt, setup_file->FileName);
6073         ptr += len + 1;
6074     }
6075
6076     lstrcpyW(ptr, usage_fmt);
6077     ptr += lstrlenW(ptr) + 1;
6078     *ptr = '\0';
6079
6080     driver_path = strdupW(driver_file->TargetPath);
6081     ptr = strrchrW(driver_path, '\\');
6082     if (ptr) *ptr = '\0';
6083
6084     if (!SQLInstallDriverExW(driver, driver_path, outpath, MAX_PATH,
6085                              NULL, ODBC_INSTALL_COMPLETE, &usage))
6086     {
6087         ERR("Failed to install SQL driver!\n");
6088         r = ERROR_FUNCTION_FAILED;
6089     }
6090
6091     uirow = MSI_CreateRecord( 5 );
6092     MSI_RecordSetStringW( uirow, 1, desc );
6093     MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6094     MSI_RecordSetStringW( uirow, 3, driver_file->Component->Directory );
6095     ui_actiondata( package, szInstallODBC, uirow );
6096     msiobj_release( &uirow->hdr );
6097
6098     msi_free(driver);
6099     msi_free(driver_path);
6100
6101     return r;
6102 }
6103
6104 static UINT ITERATE_InstallODBCTranslator( MSIRECORD *rec, LPVOID param )
6105 {
6106     MSIPACKAGE *package = param;
6107     LPWSTR translator, translator_path, ptr;
6108     WCHAR outpath[MAX_PATH];
6109     MSIFILE *translator_file = NULL, *setup_file = NULL;
6110     MSICOMPONENT *comp;
6111     MSIRECORD *uirow;
6112     LPCWSTR desc, file_key, component;
6113     DWORD len, usage;
6114     UINT r = ERROR_SUCCESS;
6115
6116     static const WCHAR translator_fmt[] = {
6117         'T','r','a','n','s','l','a','t','o','r','=','%','s',0};
6118     static const WCHAR setup_fmt[] = {
6119         'S','e','t','u','p','=','%','s',0};
6120
6121     component = MSI_RecordGetString( rec, 2 );
6122     comp = get_loaded_component( package, component );
6123     if (!comp)
6124         return ERROR_SUCCESS;
6125
6126     if (!comp->Enabled)
6127     {
6128         TRACE("component is disabled\n");
6129         return ERROR_SUCCESS;
6130     }
6131
6132     desc = MSI_RecordGetString(rec, 3);
6133
6134     file_key = MSI_RecordGetString( rec, 4 );
6135     if (file_key) translator_file = get_loaded_file( package, file_key );
6136
6137     file_key = MSI_RecordGetString( rec, 5 );
6138     if (file_key) setup_file = get_loaded_file( package, file_key );
6139
6140     if (!translator_file)
6141     {
6142         ERR("ODBC Translator entry not found!\n");
6143         return ERROR_FUNCTION_FAILED;
6144     }
6145
6146     len = lstrlenW(desc) + lstrlenW(translator_fmt) + lstrlenW(translator_file->FileName) + 2; /* \0\0 */
6147     if (setup_file)
6148         len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
6149
6150     translator = msi_alloc(len * sizeof(WCHAR));
6151     if (!translator)
6152         return ERROR_OUTOFMEMORY;
6153
6154     ptr = translator;
6155     lstrcpyW(ptr, desc);
6156     ptr += lstrlenW(ptr) + 1;
6157
6158     len = sprintfW(ptr, translator_fmt, translator_file->FileName);
6159     ptr += len + 1;
6160
6161     if (setup_file)
6162     {
6163         len = sprintfW(ptr, setup_fmt, setup_file->FileName);
6164         ptr += len + 1;
6165     }
6166     *ptr = '\0';
6167
6168     translator_path = strdupW(translator_file->TargetPath);
6169     ptr = strrchrW(translator_path, '\\');
6170     if (ptr) *ptr = '\0';
6171
6172     if (!SQLInstallTranslatorExW(translator, translator_path, outpath, MAX_PATH,
6173                                  NULL, ODBC_INSTALL_COMPLETE, &usage))
6174     {
6175         ERR("Failed to install SQL translator!\n");
6176         r = ERROR_FUNCTION_FAILED;
6177     }
6178
6179     uirow = MSI_CreateRecord( 5 );
6180     MSI_RecordSetStringW( uirow, 1, desc );
6181     MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6182     MSI_RecordSetStringW( uirow, 3, translator_file->Component->Directory );
6183     ui_actiondata( package, szInstallODBC, uirow );
6184     msiobj_release( &uirow->hdr );
6185
6186     msi_free(translator);
6187     msi_free(translator_path);
6188
6189     return r;
6190 }
6191
6192 static UINT ITERATE_InstallODBCDataSource( MSIRECORD *rec, LPVOID param )
6193 {
6194     MSIPACKAGE *package = param;
6195     MSICOMPONENT *comp;
6196     LPWSTR attrs;
6197     LPCWSTR desc, driver, component;
6198     WORD request = ODBC_ADD_SYS_DSN;
6199     INT registration;
6200     DWORD len;
6201     UINT r = ERROR_SUCCESS;
6202     MSIRECORD *uirow;
6203
6204     static const WCHAR attrs_fmt[] = {
6205         'D','S','N','=','%','s',0 };
6206
6207     component = MSI_RecordGetString( rec, 2 );
6208     comp = get_loaded_component( package, component );
6209     if (!comp)
6210         return ERROR_SUCCESS;
6211
6212     if (!comp->Enabled)
6213     {
6214         TRACE("component is disabled\n");
6215         return ERROR_SUCCESS;
6216     }
6217
6218     desc = MSI_RecordGetString(rec, 3);
6219     driver = MSI_RecordGetString(rec, 4);
6220     registration = MSI_RecordGetInteger(rec, 5);
6221
6222     if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_ADD_SYS_DSN;
6223     else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_ADD_DSN;
6224
6225     len = lstrlenW(attrs_fmt) + lstrlenW(desc) + 2; /* \0\0 */
6226     attrs = msi_alloc(len * sizeof(WCHAR));
6227     if (!attrs)
6228         return ERROR_OUTOFMEMORY;
6229
6230     len = sprintfW(attrs, attrs_fmt, desc);
6231     attrs[len + 1] = 0;
6232
6233     if (!SQLConfigDataSourceW(NULL, request, driver, attrs))
6234     {
6235         ERR("Failed to install SQL data source!\n");
6236         r = ERROR_FUNCTION_FAILED;
6237     }
6238
6239     uirow = MSI_CreateRecord( 5 );
6240     MSI_RecordSetStringW( uirow, 1, desc );
6241     MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6242     MSI_RecordSetInteger( uirow, 3, request );
6243     ui_actiondata( package, szInstallODBC, uirow );
6244     msiobj_release( &uirow->hdr );
6245
6246     msi_free(attrs);
6247
6248     return r;
6249 }
6250
6251 static UINT ACTION_InstallODBC( MSIPACKAGE *package )
6252 {
6253     UINT rc;
6254     MSIQUERY *view;
6255
6256     static const WCHAR driver_query[] = {
6257         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6258         'O','D','B','C','D','r','i','v','e','r',0 };
6259
6260     static const WCHAR translator_query[] = {
6261         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6262         'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0 };
6263
6264     static const WCHAR source_query[] = {
6265         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6266         'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0 };
6267
6268     rc = MSI_DatabaseOpenViewW(package->db, driver_query, &view);
6269     if (rc != ERROR_SUCCESS)
6270         return ERROR_SUCCESS;
6271
6272     rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDriver, package);
6273     msiobj_release(&view->hdr);
6274
6275     rc = MSI_DatabaseOpenViewW(package->db, translator_query, &view);
6276     if (rc != ERROR_SUCCESS)
6277         return ERROR_SUCCESS;
6278
6279     rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCTranslator, package);
6280     msiobj_release(&view->hdr);
6281
6282     rc = MSI_DatabaseOpenViewW(package->db, source_query, &view);
6283     if (rc != ERROR_SUCCESS)
6284         return ERROR_SUCCESS;
6285
6286     rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDataSource, package);
6287     msiobj_release(&view->hdr);
6288
6289     return rc;
6290 }
6291
6292 static UINT ITERATE_RemoveODBCDriver( MSIRECORD *rec, LPVOID param )
6293 {
6294     MSIPACKAGE *package = param;
6295     MSICOMPONENT *comp;
6296     MSIRECORD *uirow;
6297     DWORD usage;
6298     LPCWSTR desc, component;
6299
6300     component = MSI_RecordGetString( rec, 2 );
6301     comp = get_loaded_component( package, component );
6302     if (!comp)
6303         return ERROR_SUCCESS;
6304
6305     if (!comp->Enabled)
6306     {
6307         TRACE("component is disabled\n");
6308         return ERROR_SUCCESS;
6309     }
6310
6311     desc = MSI_RecordGetString( rec, 3 );
6312     if (!SQLRemoveDriverW( desc, FALSE, &usage ))
6313     {
6314         WARN("Failed to remove ODBC driver\n");
6315     }
6316     else if (!usage)
6317     {
6318         FIXME("Usage count reached 0\n");
6319     }
6320
6321     uirow = MSI_CreateRecord( 2 );
6322     MSI_RecordSetStringW( uirow, 1, desc );
6323     MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6324     ui_actiondata( package, szRemoveODBC, uirow );
6325     msiobj_release( &uirow->hdr );
6326
6327     return ERROR_SUCCESS;
6328 }
6329
6330 static UINT ITERATE_RemoveODBCTranslator( MSIRECORD *rec, LPVOID param )
6331 {
6332     MSIPACKAGE *package = param;
6333     MSICOMPONENT *comp;
6334     MSIRECORD *uirow;
6335     DWORD usage;
6336     LPCWSTR desc, component;
6337
6338     component = MSI_RecordGetString( rec, 2 );
6339     comp = get_loaded_component( package, component );
6340     if (!comp)
6341         return ERROR_SUCCESS;
6342
6343     if (!comp->Enabled)
6344     {
6345         TRACE("component is disabled\n");
6346         return ERROR_SUCCESS;
6347     }
6348
6349     desc = MSI_RecordGetString( rec, 3 );
6350     if (!SQLRemoveTranslatorW( desc, &usage ))
6351     {
6352         WARN("Failed to remove ODBC translator\n");
6353     }
6354     else if (!usage)
6355     {
6356         FIXME("Usage count reached 0\n");
6357     }
6358
6359     uirow = MSI_CreateRecord( 2 );
6360     MSI_RecordSetStringW( uirow, 1, desc );
6361     MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6362     ui_actiondata( package, szRemoveODBC, uirow );
6363     msiobj_release( &uirow->hdr );
6364
6365     return ERROR_SUCCESS;
6366 }
6367
6368 static UINT ITERATE_RemoveODBCDataSource( MSIRECORD *rec, LPVOID param )
6369 {
6370     MSIPACKAGE *package = param;
6371     MSICOMPONENT *comp;
6372     MSIRECORD *uirow;
6373     LPWSTR attrs;
6374     LPCWSTR desc, driver, component;
6375     WORD request = ODBC_REMOVE_SYS_DSN;
6376     INT registration;
6377     DWORD len;
6378
6379     static const WCHAR attrs_fmt[] = {
6380         'D','S','N','=','%','s',0 };
6381
6382     component = MSI_RecordGetString( rec, 2 );
6383     comp = get_loaded_component( package, component );
6384     if (!comp)
6385         return ERROR_SUCCESS;
6386
6387     if (!comp->Enabled)
6388     {
6389         TRACE("component is disabled\n");
6390         return ERROR_SUCCESS;
6391     }
6392
6393     desc = MSI_RecordGetString( rec, 3 );
6394     driver = MSI_RecordGetString( rec, 4 );
6395     registration = MSI_RecordGetInteger( rec, 5 );
6396
6397     if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_REMOVE_SYS_DSN;
6398     else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_REMOVE_DSN;
6399
6400     len = strlenW( attrs_fmt ) + strlenW( desc ) + 2; /* \0\0 */
6401     attrs = msi_alloc( len * sizeof(WCHAR) );
6402     if (!attrs)
6403         return ERROR_OUTOFMEMORY;
6404
6405     FIXME("Use ODBCSourceAttribute table\n");
6406
6407     len = sprintfW( attrs, attrs_fmt, desc );
6408     attrs[len + 1] = 0;
6409
6410     if (!SQLConfigDataSourceW( NULL, request, driver, attrs ))
6411     {
6412         WARN("Failed to remove ODBC data source\n");
6413     }
6414     msi_free( attrs );
6415
6416     uirow = MSI_CreateRecord( 3 );
6417     MSI_RecordSetStringW( uirow, 1, desc );
6418     MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6419     MSI_RecordSetInteger( uirow, 3, request );
6420     ui_actiondata( package, szRemoveODBC, uirow );
6421     msiobj_release( &uirow->hdr );
6422
6423     return ERROR_SUCCESS;
6424 }
6425
6426 static UINT ACTION_RemoveODBC( MSIPACKAGE *package )
6427 {
6428     UINT rc;
6429     MSIQUERY *view;
6430
6431     static const WCHAR driver_query[] = {
6432         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6433         'O','D','B','C','D','r','i','v','e','r',0 };
6434
6435     static const WCHAR translator_query[] = {
6436         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6437         'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0 };
6438
6439     static const WCHAR source_query[] = {
6440         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6441         'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0 };
6442
6443     rc = MSI_DatabaseOpenViewW( package->db, driver_query, &view );
6444     if (rc != ERROR_SUCCESS)
6445         return ERROR_SUCCESS;
6446
6447     rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDriver, package );
6448     msiobj_release( &view->hdr );
6449
6450     rc = MSI_DatabaseOpenViewW( package->db, translator_query, &view );
6451     if (rc != ERROR_SUCCESS)
6452         return ERROR_SUCCESS;
6453
6454     rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCTranslator, package );
6455     msiobj_release( &view->hdr );
6456
6457     rc = MSI_DatabaseOpenViewW( package->db, source_query, &view );
6458     if (rc != ERROR_SUCCESS)
6459         return ERROR_SUCCESS;
6460
6461     rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDataSource, package );
6462     msiobj_release( &view->hdr );
6463
6464     return rc;
6465 }
6466
6467 #define ENV_ACT_SETALWAYS   0x1
6468 #define ENV_ACT_SETABSENT   0x2
6469 #define ENV_ACT_REMOVE      0x4
6470 #define ENV_ACT_REMOVEMATCH 0x8
6471
6472 #define ENV_MOD_MACHINE     0x20000000
6473 #define ENV_MOD_APPEND      0x40000000
6474 #define ENV_MOD_PREFIX      0x80000000
6475 #define ENV_MOD_MASK        0xC0000000
6476
6477 #define check_flag_combo(x, y) ((x) & ~(y)) == (y)
6478
6479 static UINT env_parse_flags( LPCWSTR *name, LPCWSTR *value, DWORD *flags )
6480 {
6481     LPCWSTR cptr = *name;
6482
6483     static const WCHAR prefix[] = {'[','~',']',0};
6484     static const int prefix_len = 3;
6485
6486     *flags = 0;
6487     while (*cptr)
6488     {
6489         if (*cptr == '=')
6490             *flags |= ENV_ACT_SETALWAYS;
6491         else if (*cptr == '+')
6492             *flags |= ENV_ACT_SETABSENT;
6493         else if (*cptr == '-')
6494             *flags |= ENV_ACT_REMOVE;
6495         else if (*cptr == '!')
6496             *flags |= ENV_ACT_REMOVEMATCH;
6497         else if (*cptr == '*')
6498             *flags |= ENV_MOD_MACHINE;
6499         else
6500             break;
6501
6502         cptr++;
6503         (*name)++;
6504     }
6505
6506     if (!*cptr)
6507     {
6508         ERR("Missing environment variable\n");
6509         return ERROR_FUNCTION_FAILED;
6510     }
6511
6512     if (*value)
6513     {
6514         LPCWSTR ptr = *value;
6515         if (!strncmpW(ptr, prefix, prefix_len))
6516         {
6517             if (ptr[prefix_len] == szSemiColon[0])
6518             {
6519                 *flags |= ENV_MOD_APPEND;
6520                 *value += lstrlenW(prefix);
6521             }
6522             else
6523             {
6524                 *value = NULL;
6525             }
6526         }
6527         else if (lstrlenW(*value) >= prefix_len)
6528         {
6529             ptr += lstrlenW(ptr) - prefix_len;
6530             if (!strcmpW( ptr, prefix ))
6531             {
6532                 if ((ptr-1) > *value && *(ptr-1) == szSemiColon[0])
6533                 {
6534                     *flags |= ENV_MOD_PREFIX;
6535                     /* the "[~]" will be removed by deformat_string */;
6536                 }
6537                 else
6538                 {
6539                     *value = NULL;
6540                 }
6541             }
6542         }
6543     }
6544
6545     if (check_flag_combo(*flags, ENV_ACT_SETALWAYS | ENV_ACT_SETABSENT) ||
6546         check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETABSENT) ||
6547         check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETALWAYS) ||
6548         check_flag_combo(*flags, ENV_ACT_SETABSENT | ENV_MOD_MASK))
6549     {
6550         ERR("Invalid flags: %08x\n", *flags);
6551         return ERROR_FUNCTION_FAILED;
6552     }
6553
6554     if (!*flags)
6555         *flags = ENV_ACT_SETALWAYS | ENV_ACT_REMOVE;
6556
6557     return ERROR_SUCCESS;
6558 }
6559
6560 static UINT open_env_key( DWORD flags, HKEY *key )
6561 {
6562     static const WCHAR user_env[] =
6563         {'E','n','v','i','r','o','n','m','e','n','t',0};
6564     static const WCHAR machine_env[] =
6565         {'S','y','s','t','e','m','\\',
6566          'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
6567          'C','o','n','t','r','o','l','\\',
6568          'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r','\\',
6569          'E','n','v','i','r','o','n','m','e','n','t',0};
6570     const WCHAR *env;
6571     HKEY root;
6572     LONG res;
6573
6574     if (flags & ENV_MOD_MACHINE)
6575     {
6576         env = machine_env;
6577         root = HKEY_LOCAL_MACHINE;
6578     }
6579     else
6580     {
6581         env = user_env;
6582         root = HKEY_CURRENT_USER;
6583     }
6584
6585     res = RegOpenKeyExW( root, env, 0, KEY_ALL_ACCESS, key );
6586     if (res != ERROR_SUCCESS)
6587     {
6588         WARN("Failed to open key %s (%d)\n", debugstr_w(env), res);
6589         return ERROR_FUNCTION_FAILED;
6590     }
6591
6592     return ERROR_SUCCESS;
6593 }
6594
6595 static UINT ITERATE_WriteEnvironmentString( MSIRECORD *rec, LPVOID param )
6596 {
6597     MSIPACKAGE *package = param;
6598     LPCWSTR name, value, component;
6599     LPWSTR data = NULL, newval = NULL, deformatted = NULL, ptr;
6600     DWORD flags, type, size;
6601     UINT res;
6602     HKEY env = NULL;
6603     MSICOMPONENT *comp;
6604     MSIRECORD *uirow;
6605     int action = 0;
6606
6607     component = MSI_RecordGetString(rec, 4);
6608     comp = get_loaded_component(package, component);
6609     if (!comp)
6610         return ERROR_SUCCESS;
6611
6612     if (!comp->Enabled)
6613     {
6614         TRACE("component is disabled\n");
6615         return ERROR_SUCCESS;
6616     }
6617
6618     if (comp->ActionRequest != INSTALLSTATE_LOCAL)
6619     {
6620         TRACE("Component not scheduled for installation: %s\n", debugstr_w(component));
6621         comp->Action = comp->Installed;
6622         return ERROR_SUCCESS;
6623     }
6624     comp->Action = INSTALLSTATE_LOCAL;
6625
6626     name = MSI_RecordGetString(rec, 2);
6627     value = MSI_RecordGetString(rec, 3);
6628
6629     TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
6630
6631     res = env_parse_flags(&name, &value, &flags);
6632     if (res != ERROR_SUCCESS || !value)
6633        goto done;
6634
6635     if (value && !deformat_string(package, value, &deformatted))
6636     {
6637         res = ERROR_OUTOFMEMORY;
6638         goto done;
6639     }
6640
6641     value = deformatted;
6642
6643     res = open_env_key( flags, &env );
6644     if (res != ERROR_SUCCESS)
6645         goto done;
6646
6647     if (flags & ENV_MOD_MACHINE)
6648         action |= 0x20000000;
6649
6650     size = 0;
6651     type = REG_SZ;
6652     res = RegQueryValueExW(env, name, NULL, &type, NULL, &size);
6653     if ((res != ERROR_SUCCESS && res != ERROR_FILE_NOT_FOUND) ||
6654         (res == ERROR_SUCCESS && type != REG_SZ && type != REG_EXPAND_SZ))
6655         goto done;
6656
6657     if ((res == ERROR_FILE_NOT_FOUND || !(flags & ENV_MOD_MASK)))
6658     {
6659         action = 0x2;
6660
6661         /* Nothing to do. */
6662         if (!value)
6663         {
6664             res = ERROR_SUCCESS;
6665             goto done;
6666         }
6667
6668         /* If we are appending but the string was empty, strip ; */
6669         if ((flags & ENV_MOD_APPEND) && (value[0] == szSemiColon[0])) value++;
6670
6671         size = (lstrlenW(value) + 1) * sizeof(WCHAR);
6672         newval = strdupW(value);
6673         if (!newval)
6674         {
6675             res = ERROR_OUTOFMEMORY;
6676             goto done;
6677         }
6678     }
6679     else
6680     {
6681         action = 0x1;
6682
6683         /* Contrary to MSDN, +-variable to [~];path works */
6684         if (flags & ENV_ACT_SETABSENT && !(flags & ENV_MOD_MASK))
6685         {
6686             res = ERROR_SUCCESS;
6687             goto done;
6688         }
6689
6690         data = msi_alloc(size);
6691         if (!data)
6692         {
6693             RegCloseKey(env);
6694             return ERROR_OUTOFMEMORY;
6695         }
6696
6697         res = RegQueryValueExW(env, name, NULL, &type, (LPVOID)data, &size);
6698         if (res != ERROR_SUCCESS)
6699             goto done;
6700
6701         if (flags & ENV_ACT_REMOVEMATCH && (!value || !strcmpW( data, value )))
6702         {
6703             action = 0x4;
6704             res = RegDeleteValueW(env, name);
6705             if (res != ERROR_SUCCESS)
6706                 WARN("Failed to remove value %s (%d)\n", debugstr_w(name), res);
6707             goto done;
6708         }
6709
6710         size = (lstrlenW(data) + 1) * sizeof(WCHAR);
6711         if (flags & ENV_MOD_MASK)
6712         {
6713             DWORD mod_size;
6714             int multiplier = 0;
6715             if (flags & ENV_MOD_APPEND) multiplier++;
6716             if (flags & ENV_MOD_PREFIX) multiplier++;
6717             mod_size = lstrlenW(value) * multiplier;
6718             size += mod_size * sizeof(WCHAR);
6719         }
6720
6721         newval = msi_alloc(size);
6722         ptr = newval;
6723         if (!newval)
6724         {
6725             res = ERROR_OUTOFMEMORY;
6726             goto done;
6727         }
6728
6729         if (flags & ENV_MOD_PREFIX)
6730         {
6731             lstrcpyW(newval, value);
6732             ptr = newval + lstrlenW(value);
6733             action |= 0x80000000;
6734         }
6735
6736         lstrcpyW(ptr, data);
6737
6738         if (flags & ENV_MOD_APPEND)
6739         {
6740             lstrcatW(newval, value);
6741             action |= 0x40000000;
6742         }
6743     }
6744     TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(newval));
6745     res = RegSetValueExW(env, name, 0, type, (LPVOID)newval, size);
6746     if (res)
6747     {
6748         WARN("Failed to set %s to %s (%d)\n",  debugstr_w(name), debugstr_w(newval), res);
6749     }
6750
6751 done:
6752     uirow = MSI_CreateRecord( 3 );
6753     MSI_RecordSetStringW( uirow, 1, name );
6754     MSI_RecordSetStringW( uirow, 2, newval );
6755     MSI_RecordSetInteger( uirow, 3, action );
6756     ui_actiondata( package, szWriteEnvironmentStrings, uirow );
6757     msiobj_release( &uirow->hdr );
6758
6759     if (env) RegCloseKey(env);
6760     msi_free(deformatted);
6761     msi_free(data);
6762     msi_free(newval);
6763     return res;
6764 }
6765
6766 static UINT ACTION_WriteEnvironmentStrings( MSIPACKAGE *package )
6767 {
6768     UINT rc;
6769     MSIQUERY * view;
6770     static const WCHAR ExecSeqQuery[] =
6771         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6772          '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
6773     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
6774     if (rc != ERROR_SUCCESS)
6775         return ERROR_SUCCESS;
6776
6777     rc = MSI_IterateRecords(view, NULL, ITERATE_WriteEnvironmentString, package);
6778     msiobj_release(&view->hdr);
6779
6780     return rc;
6781 }
6782
6783 static UINT ITERATE_RemoveEnvironmentString( MSIRECORD *rec, LPVOID param )
6784 {
6785     MSIPACKAGE *package = param;
6786     LPCWSTR name, value, component;
6787     LPWSTR deformatted = NULL;
6788     DWORD flags;
6789     HKEY env;
6790     MSICOMPONENT *comp;
6791     MSIRECORD *uirow;
6792     int action = 0;
6793     LONG res;
6794     UINT r;
6795
6796     component = MSI_RecordGetString( rec, 4 );
6797     comp = get_loaded_component( package, component );
6798     if (!comp)
6799         return ERROR_SUCCESS;
6800
6801     if (!comp->Enabled)
6802     {
6803         TRACE("component is disabled\n");
6804         return ERROR_SUCCESS;
6805     }
6806
6807     if (comp->ActionRequest != INSTALLSTATE_ABSENT)
6808     {
6809         TRACE("Component not scheduled for removal: %s\n", debugstr_w(component));
6810         comp->Action = comp->Installed;
6811         return ERROR_SUCCESS;
6812     }
6813     comp->Action = INSTALLSTATE_ABSENT;
6814
6815     name = MSI_RecordGetString( rec, 2 );
6816     value = MSI_RecordGetString( rec, 3 );
6817
6818     TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
6819
6820     r = env_parse_flags( &name, &value, &flags );
6821     if (r != ERROR_SUCCESS)
6822        return r;
6823
6824     if (!(flags & ENV_ACT_REMOVE))
6825     {
6826         TRACE("Environment variable %s not marked for removal\n", debugstr_w(name));
6827         return ERROR_SUCCESS;
6828     }
6829
6830     if (value && !deformat_string( package, value, &deformatted ))
6831         return ERROR_OUTOFMEMORY;
6832
6833     value = deformatted;
6834
6835     r = open_env_key( flags, &env );
6836     if (r != ERROR_SUCCESS)
6837     {
6838         r = ERROR_SUCCESS;
6839         goto done;
6840     }
6841
6842     if (flags & ENV_MOD_MACHINE)
6843         action |= 0x20000000;
6844
6845     TRACE("Removing %s\n", debugstr_w(name));
6846
6847     res = RegDeleteValueW( env, name );
6848     if (res != ERROR_SUCCESS)
6849     {
6850         WARN("Failed to delete value %s (%d)\n", debugstr_w(name), res);
6851         r = ERROR_SUCCESS;
6852     }
6853
6854 done:
6855     uirow = MSI_CreateRecord( 3 );
6856     MSI_RecordSetStringW( uirow, 1, name );
6857     MSI_RecordSetStringW( uirow, 2, value );
6858     MSI_RecordSetInteger( uirow, 3, action );
6859     ui_actiondata( package, szRemoveEnvironmentStrings, uirow );
6860     msiobj_release( &uirow->hdr );
6861
6862     if (env) RegCloseKey( env );
6863     msi_free( deformatted );
6864     return r;
6865 }
6866
6867 static UINT ACTION_RemoveEnvironmentStrings( MSIPACKAGE *package )
6868 {
6869     UINT rc;
6870     MSIQUERY *view;
6871     static const WCHAR query[] =
6872         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6873          '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
6874
6875     rc = MSI_DatabaseOpenViewW( package->db, query, &view );
6876     if (rc != ERROR_SUCCESS)
6877         return ERROR_SUCCESS;
6878
6879     rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveEnvironmentString, package );
6880     msiobj_release( &view->hdr );
6881
6882     return rc;
6883 }
6884
6885 static UINT ACTION_ValidateProductID( MSIPACKAGE *package )
6886 {
6887     LPWSTR key, template, id;
6888     UINT r = ERROR_SUCCESS;
6889
6890     id = msi_dup_property( package->db, szProductID );
6891     if (id)
6892     {
6893         msi_free( id );
6894         return ERROR_SUCCESS;
6895     }
6896     template = msi_dup_property( package->db, szPIDTemplate );
6897     key = msi_dup_property( package->db, szPIDKEY );
6898
6899     if (key && template)
6900     {
6901         FIXME( "partial stub: template %s key %s\n", debugstr_w(template), debugstr_w(key) );
6902         r = msi_set_property( package->db, szProductID, key );
6903     }
6904     msi_free( template );
6905     msi_free( key );
6906     return r;
6907 }
6908
6909 static UINT ACTION_ScheduleReboot( MSIPACKAGE *package )
6910 {
6911     TRACE("\n");
6912     package->need_reboot = 1;
6913     return ERROR_SUCCESS;
6914 }
6915
6916 static UINT ACTION_AllocateRegistrySpace( MSIPACKAGE *package )
6917 {
6918     static const WCHAR szAvailableFreeReg[] =
6919         {'A','V','A','I','L','A','B','L','E','F','R','E','E','R','E','G',0};
6920     MSIRECORD *uirow;
6921     int space = msi_get_property_int( package->db, szAvailableFreeReg, 0 );
6922
6923     TRACE("%p %d kilobytes\n", package, space);
6924
6925     uirow = MSI_CreateRecord( 1 );
6926     MSI_RecordSetInteger( uirow, 1, space );
6927     ui_actiondata( package, szAllocateRegistrySpace, uirow );
6928     msiobj_release( &uirow->hdr );
6929
6930     return ERROR_SUCCESS;
6931 }
6932
6933 static UINT ACTION_DisableRollback( MSIPACKAGE *package )
6934 {
6935     FIXME("%p\n", package);
6936     return ERROR_SUCCESS;
6937 }
6938
6939 static UINT ACTION_InstallAdminPackage( MSIPACKAGE *package )
6940 {
6941     FIXME("%p\n", package);
6942     return ERROR_SUCCESS;
6943 }
6944
6945 static UINT ACTION_SetODBCFolders( MSIPACKAGE *package )
6946 {
6947     UINT r, count;
6948     MSIQUERY *view;
6949
6950     static const WCHAR driver_query[] = {
6951         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6952         'O','D','B','C','D','r','i','v','e','r',0 };
6953
6954     static const WCHAR translator_query[] = {
6955         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6956         'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0 };
6957
6958     r = MSI_DatabaseOpenViewW( package->db, driver_query, &view );
6959     if (r == ERROR_SUCCESS)
6960     {
6961         count = 0;
6962         r = MSI_IterateRecords( view, &count, NULL, package );
6963         msiobj_release( &view->hdr );
6964         if (count) FIXME("ignored %u rows in ODBCDriver table\n", count);
6965     }
6966
6967     r = MSI_DatabaseOpenViewW( package->db, translator_query, &view );
6968     if (r == ERROR_SUCCESS)
6969     {
6970         count = 0;
6971         r = MSI_IterateRecords( view, &count, NULL, package );
6972         msiobj_release( &view->hdr );
6973         if (count) FIXME("ignored %u rows in ODBCTranslator table\n", count);
6974     }
6975
6976     return ERROR_SUCCESS;
6977 }
6978
6979 static UINT ITERATE_RemoveExistingProducts( MSIRECORD *rec, LPVOID param )
6980 {
6981     MSIPACKAGE *package = param;
6982     const WCHAR *property = MSI_RecordGetString( rec, 1 );
6983     WCHAR *value;
6984
6985     if ((value = msi_dup_property( package->db, property )))
6986     {
6987         FIXME("remove %s\n", debugstr_w(value));
6988         msi_free( value );
6989     }
6990     return ERROR_SUCCESS;
6991 }
6992
6993 static UINT ACTION_RemoveExistingProducts( MSIPACKAGE *package )
6994 {
6995     UINT r;
6996     MSIQUERY *view;
6997
6998     static const WCHAR query[] =
6999         {'S','E','L','E','C','T',' ','A','c','t','i','o','n','P','r','o','p','e','r','t','y',
7000          ' ','F','R','O','M',' ','U','p','g','r','a','d','e',0};
7001
7002     r = MSI_DatabaseOpenViewW( package->db, query, &view );
7003     if (r == ERROR_SUCCESS)
7004     {
7005         r = MSI_IterateRecords( view, NULL, ITERATE_RemoveExistingProducts, package );
7006         msiobj_release( &view->hdr );
7007     }
7008     return ERROR_SUCCESS;
7009 }
7010
7011 static UINT ITERATE_MigrateFeatureStates( MSIRECORD *rec, LPVOID param )
7012 {
7013     MSIPACKAGE *package = param;
7014     int attributes = MSI_RecordGetInteger( rec, 5 );
7015
7016     if (attributes & msidbUpgradeAttributesMigrateFeatures)
7017     {
7018         const WCHAR *upgrade_code = MSI_RecordGetString( rec, 1 );
7019         const WCHAR *version_min = MSI_RecordGetString( rec, 2 );
7020         const WCHAR *version_max = MSI_RecordGetString( rec, 3 );
7021         const WCHAR *language = MSI_RecordGetString( rec, 4 );
7022         HKEY hkey;
7023         UINT r;
7024
7025         if (package->Context == MSIINSTALLCONTEXT_MACHINE)
7026         {
7027             r = MSIREG_OpenClassesUpgradeCodesKey( upgrade_code, &hkey, FALSE );
7028             if (r != ERROR_SUCCESS)
7029                 return ERROR_SUCCESS;
7030         }
7031         else
7032         {
7033             r = MSIREG_OpenUserUpgradeCodesKey( upgrade_code, &hkey, FALSE );
7034             if (r != ERROR_SUCCESS)
7035                 return ERROR_SUCCESS;
7036         }
7037         RegCloseKey( hkey );
7038
7039         FIXME("migrate feature states from %s version min %s version max %s language %s\n",
7040               debugstr_w(upgrade_code), debugstr_w(version_min),
7041               debugstr_w(version_max), debugstr_w(language));
7042     }
7043     return ERROR_SUCCESS;
7044 }
7045
7046 static UINT ACTION_MigrateFeatureStates( MSIPACKAGE *package )
7047 {
7048     UINT r;
7049     MSIQUERY *view;
7050
7051     static const WCHAR query[] =
7052         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','U','p','g','r','a','d','e',0};
7053
7054     if (msi_get_property_int( package->db, szInstalled, 0 ))
7055     {
7056         TRACE("product is installed, skipping action\n");
7057         return ERROR_SUCCESS;
7058     }
7059     if (msi_get_property_int( package->db, szPreselected, 0 ))
7060     {
7061         TRACE("Preselected property is set, not migrating feature states\n");
7062         return ERROR_SUCCESS;
7063     }
7064
7065     r = MSI_DatabaseOpenViewW( package->db, query, &view );
7066     if (r == ERROR_SUCCESS)
7067     {
7068         r = MSI_IterateRecords( view, NULL, ITERATE_MigrateFeatureStates, package );
7069         msiobj_release( &view->hdr );
7070     }
7071     return ERROR_SUCCESS;
7072 }
7073
7074 static UINT msi_unimplemented_action_stub( MSIPACKAGE *package,
7075                                            LPCSTR action, LPCWSTR table )
7076 {
7077     static const WCHAR query[] = {
7078         'S','E','L','E','C','T',' ','*',' ',
7079         'F','R','O','M',' ','`','%','s','`',0 };
7080     MSIQUERY *view = NULL;
7081     DWORD count = 0;
7082     UINT r;
7083     
7084     r = MSI_OpenQuery( package->db, &view, query, table );
7085     if (r == ERROR_SUCCESS)
7086     {
7087         r = MSI_IterateRecords(view, &count, NULL, package);
7088         msiobj_release(&view->hdr);
7089     }
7090
7091     if (count)
7092         FIXME("%s -> %u ignored %s table values\n",
7093               action, count, debugstr_w(table));
7094
7095     return ERROR_SUCCESS;
7096 }
7097
7098 static UINT ACTION_PatchFiles( MSIPACKAGE *package )
7099 {
7100     static const WCHAR table[] = { 'P','a','t','c','h',0 };
7101     return msi_unimplemented_action_stub( package, "PatchFiles", table );
7102 }
7103
7104 static UINT ACTION_BindImage( MSIPACKAGE *package )
7105 {
7106     static const WCHAR table[] = { 'B','i','n','d','I','m','a','g','e',0 };
7107     return msi_unimplemented_action_stub( package, "BindImage", table );
7108 }
7109
7110 static UINT ACTION_IsolateComponents( MSIPACKAGE *package )
7111 {
7112     static const WCHAR table[] = {
7113         'I','s','o','l','a','t','e','d','C','o','m','p','o','n','e','n','t',0 };
7114     return msi_unimplemented_action_stub( package, "IsolateComponents", table );
7115 }
7116
7117 static UINT ACTION_RMCCPSearch( MSIPACKAGE *package )
7118 {
7119     static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
7120     return msi_unimplemented_action_stub( package, "RMCCPSearch", table );
7121 }
7122
7123 static UINT ACTION_RegisterComPlus( MSIPACKAGE *package )
7124 {
7125     static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
7126     return msi_unimplemented_action_stub( package, "RegisterComPlus", table );
7127 }
7128
7129 static UINT ACTION_UnregisterComPlus( MSIPACKAGE *package )
7130 {
7131     static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
7132     return msi_unimplemented_action_stub( package, "UnregisterComPlus", table );
7133 }
7134
7135 static UINT ACTION_InstallSFPCatalogFile( MSIPACKAGE *package )
7136 {
7137     static const WCHAR table[] = { 'S','F','P','C','a','t','a','l','o','g',0 };
7138     return msi_unimplemented_action_stub( package, "InstallSFPCatalogFile", table );
7139 }
7140
7141 typedef UINT (*STANDARDACTIONHANDLER)(MSIPACKAGE*);
7142
7143 static const struct
7144 {
7145     const WCHAR *action;
7146     UINT (*handler)(MSIPACKAGE *);
7147 }
7148 StandardActions[] =
7149 {
7150     { szAllocateRegistrySpace, ACTION_AllocateRegistrySpace },
7151     { szAppSearch, ACTION_AppSearch },
7152     { szBindImage, ACTION_BindImage },
7153     { szCCPSearch, ACTION_CCPSearch },
7154     { szCostFinalize, ACTION_CostFinalize },
7155     { szCostInitialize, ACTION_CostInitialize },
7156     { szCreateFolders, ACTION_CreateFolders },
7157     { szCreateShortcuts, ACTION_CreateShortcuts },
7158     { szDeleteServices, ACTION_DeleteServices },
7159     { szDisableRollback, ACTION_DisableRollback },
7160     { szDuplicateFiles, ACTION_DuplicateFiles },
7161     { szExecuteAction, ACTION_ExecuteAction },
7162     { szFileCost, ACTION_FileCost },
7163     { szFindRelatedProducts, ACTION_FindRelatedProducts },
7164     { szForceReboot, ACTION_ForceReboot },
7165     { szInstallAdminPackage, ACTION_InstallAdminPackage },
7166     { szInstallExecute, ACTION_InstallExecute },
7167     { szInstallExecuteAgain, ACTION_InstallExecute },
7168     { szInstallFiles, ACTION_InstallFiles},
7169     { szInstallFinalize, ACTION_InstallFinalize },
7170     { szInstallInitialize, ACTION_InstallInitialize },
7171     { szInstallSFPCatalogFile, ACTION_InstallSFPCatalogFile },
7172     { szInstallValidate, ACTION_InstallValidate },
7173     { szIsolateComponents, ACTION_IsolateComponents },
7174     { szLaunchConditions, ACTION_LaunchConditions },
7175     { szMigrateFeatureStates, ACTION_MigrateFeatureStates },
7176     { szMoveFiles, ACTION_MoveFiles },
7177     { szMsiPublishAssemblies, ACTION_MsiPublishAssemblies },
7178     { szMsiUnpublishAssemblies, ACTION_MsiUnpublishAssemblies },
7179     { szInstallODBC, ACTION_InstallODBC },
7180     { szInstallServices, ACTION_InstallServices },
7181     { szPatchFiles, ACTION_PatchFiles },
7182     { szProcessComponents, ACTION_ProcessComponents },
7183     { szPublishComponents, ACTION_PublishComponents },
7184     { szPublishFeatures, ACTION_PublishFeatures },
7185     { szPublishProduct, ACTION_PublishProduct },
7186     { szRegisterClassInfo, ACTION_RegisterClassInfo },
7187     { szRegisterComPlus, ACTION_RegisterComPlus},
7188     { szRegisterExtensionInfo, ACTION_RegisterExtensionInfo },
7189     { szRegisterFonts, ACTION_RegisterFonts },
7190     { szRegisterMIMEInfo, ACTION_RegisterMIMEInfo },
7191     { szRegisterProduct, ACTION_RegisterProduct },
7192     { szRegisterProgIdInfo, ACTION_RegisterProgIdInfo },
7193     { szRegisterTypeLibraries, ACTION_RegisterTypeLibraries },
7194     { szRegisterUser, ACTION_RegisterUser },
7195     { szRemoveDuplicateFiles, ACTION_RemoveDuplicateFiles },
7196     { szRemoveEnvironmentStrings, ACTION_RemoveEnvironmentStrings },
7197     { szRemoveExistingProducts, ACTION_RemoveExistingProducts },
7198     { szRemoveFiles, ACTION_RemoveFiles },
7199     { szRemoveFolders, ACTION_RemoveFolders },
7200     { szRemoveIniValues, ACTION_RemoveIniValues },
7201     { szRemoveODBC, ACTION_RemoveODBC },
7202     { szRemoveRegistryValues, ACTION_RemoveRegistryValues },
7203     { szRemoveShortcuts, ACTION_RemoveShortcuts },
7204     { szResolveSource, ACTION_ResolveSource },
7205     { szRMCCPSearch, ACTION_RMCCPSearch },
7206     { szScheduleReboot, ACTION_ScheduleReboot },
7207     { szSelfRegModules, ACTION_SelfRegModules },
7208     { szSelfUnregModules, ACTION_SelfUnregModules },
7209     { szSetODBCFolders, ACTION_SetODBCFolders },
7210     { szStartServices, ACTION_StartServices },
7211     { szStopServices, ACTION_StopServices },
7212     { szUnpublishComponents, ACTION_UnpublishComponents },
7213     { szUnpublishFeatures, ACTION_UnpublishFeatures },
7214     { szUnregisterClassInfo, ACTION_UnregisterClassInfo },
7215     { szUnregisterComPlus, ACTION_UnregisterComPlus },
7216     { szUnregisterExtensionInfo, ACTION_UnregisterExtensionInfo },
7217     { szUnregisterFonts, ACTION_UnregisterFonts },
7218     { szUnregisterMIMEInfo, ACTION_UnregisterMIMEInfo },
7219     { szUnregisterProgIdInfo, ACTION_UnregisterProgIdInfo },
7220     { szUnregisterTypeLibraries, ACTION_UnregisterTypeLibraries },
7221     { szValidateProductID, ACTION_ValidateProductID },
7222     { szWriteEnvironmentStrings, ACTION_WriteEnvironmentStrings },
7223     { szWriteIniValues, ACTION_WriteIniValues },
7224     { szWriteRegistryValues, ACTION_WriteRegistryValues },
7225     { NULL, NULL },
7226 };
7227
7228 static BOOL ACTION_HandleStandardAction( MSIPACKAGE *package, LPCWSTR action, UINT *rc )
7229 {
7230     BOOL ret = FALSE;
7231     UINT i;
7232
7233     i = 0;
7234     while (StandardActions[i].action != NULL)
7235     {
7236         if (!strcmpW( StandardActions[i].action, action ))
7237         {
7238             ui_actionstart( package, action );
7239             if (StandardActions[i].handler)
7240             {
7241                 ui_actioninfo( package, action, TRUE, 0 );
7242                 *rc = StandardActions[i].handler( package );
7243                 ui_actioninfo( package, action, FALSE, *rc );
7244             }
7245             else
7246             {
7247                 FIXME("unhandled standard action %s\n", debugstr_w(action));
7248                 *rc = ERROR_SUCCESS;
7249             }
7250             ret = TRUE;
7251             break;
7252         }
7253         i++;
7254     }
7255     return ret;
7256 }
7257
7258 UINT ACTION_PerformAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
7259 {
7260     UINT rc = ERROR_SUCCESS;
7261     BOOL handled;
7262
7263     TRACE("Performing action (%s)\n", debugstr_w(action));
7264
7265     handled = ACTION_HandleStandardAction(package, action, &rc);
7266
7267     if (!handled)
7268         handled = ACTION_HandleCustomAction(package, action, &rc, script, TRUE);
7269
7270     if (!handled)
7271     {
7272         WARN("unhandled msi action %s\n", debugstr_w(action));
7273         rc = ERROR_FUNCTION_NOT_CALLED;
7274     }
7275
7276     return rc;
7277 }
7278
7279 UINT ACTION_PerformUIAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
7280 {
7281     UINT rc = ERROR_SUCCESS;
7282     BOOL handled = FALSE;
7283
7284     TRACE("Performing action (%s)\n", debugstr_w(action));
7285
7286     handled = ACTION_HandleStandardAction(package, action, &rc);
7287
7288     if (!handled)
7289         handled = ACTION_HandleCustomAction(package, action, &rc, script, FALSE);
7290
7291     if( !handled && ACTION_DialogBox(package, action) == ERROR_SUCCESS )
7292         handled = TRUE;
7293
7294     if (!handled)
7295     {
7296         WARN("unhandled msi action %s\n", debugstr_w(action));
7297         rc = ERROR_FUNCTION_NOT_CALLED;
7298     }
7299
7300     return rc;
7301 }
7302
7303 static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq)
7304 {
7305     UINT rc = ERROR_SUCCESS;
7306     MSIRECORD *row;
7307
7308     static const WCHAR ExecSeqQuery[] =
7309         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7310          '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
7311          'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
7312          '`','S','e','q','u','e','n','c','e','`',' ', '=',' ','%','i',0};
7313     static const WCHAR UISeqQuery[] =
7314         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7315      '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e',
7316      '`', ' ', 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',
7317          ' ', '=',' ','%','i',0};
7318
7319     if (needs_ui_sequence(package))
7320         row = MSI_QueryGetRecord(package->db, UISeqQuery, seq);
7321     else
7322         row = MSI_QueryGetRecord(package->db, ExecSeqQuery, seq);
7323
7324     if (row)
7325     {
7326         LPCWSTR action, cond;
7327
7328         TRACE("Running the actions\n");
7329
7330         /* check conditions */
7331         cond = MSI_RecordGetString(row, 2);
7332
7333         /* this is a hack to skip errors in the condition code */
7334         if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
7335         {
7336             msiobj_release(&row->hdr);
7337             return ERROR_SUCCESS;
7338         }
7339
7340         action = MSI_RecordGetString(row, 1);
7341         if (!action)
7342         {
7343             ERR("failed to fetch action\n");
7344             msiobj_release(&row->hdr);
7345             return ERROR_FUNCTION_FAILED;
7346         }
7347
7348         if (needs_ui_sequence(package))
7349             rc = ACTION_PerformUIAction(package, action, -1);
7350         else
7351             rc = ACTION_PerformAction(package, action, -1);
7352
7353         msiobj_release(&row->hdr);
7354     }
7355
7356     return rc;
7357 }
7358
7359 /****************************************************
7360  * TOP level entry points
7361  *****************************************************/
7362
7363 UINT MSI_InstallPackage( MSIPACKAGE *package, LPCWSTR szPackagePath,
7364                          LPCWSTR szCommandLine )
7365 {
7366     UINT rc;
7367     BOOL ui_exists;
7368
7369     static const WCHAR szAction[] = {'A','C','T','I','O','N',0};
7370     static const WCHAR szInstall[] = {'I','N','S','T','A','L','L',0};
7371
7372     msi_set_property( package->db, szAction, szInstall );
7373
7374     package->script->InWhatSequence = SEQUENCE_INSTALL;
7375
7376     if (szPackagePath)
7377     {
7378         LPWSTR p, dir;
7379         LPCWSTR file;
7380
7381         dir = strdupW(szPackagePath);
7382         p = strrchrW(dir, '\\');
7383         if (p)
7384         {
7385             *(++p) = 0;
7386             file = szPackagePath + (p - dir);
7387         }
7388         else
7389         {
7390             msi_free(dir);
7391             dir = msi_alloc(MAX_PATH * sizeof(WCHAR));
7392             GetCurrentDirectoryW(MAX_PATH, dir);
7393             lstrcatW(dir, szBackSlash);
7394             file = szPackagePath;
7395         }
7396
7397         msi_free( package->PackagePath );
7398         package->PackagePath = msi_alloc((lstrlenW(dir) + lstrlenW(file) + 1) * sizeof(WCHAR));
7399         if (!package->PackagePath)
7400         {
7401             msi_free(dir);
7402             return ERROR_OUTOFMEMORY;
7403         }
7404
7405         lstrcpyW(package->PackagePath, dir);
7406         lstrcatW(package->PackagePath, file);
7407         msi_free(dir);
7408
7409         msi_set_sourcedir_props(package, FALSE);
7410     }
7411
7412     rc = msi_parse_command_line( package, szCommandLine, FALSE );
7413     if (rc != ERROR_SUCCESS)
7414         return rc;
7415
7416     msi_apply_transforms( package );
7417     msi_apply_patches( package );
7418
7419     if (!szCommandLine && msi_get_property_int( package->db, szInstalled, 0 ))
7420     {
7421         TRACE("setting reinstall property\n");
7422         msi_set_property( package->db, szReinstall, szAll );
7423     }
7424
7425     /* properties may have been added by a transform */
7426     msi_clone_properties( package );
7427
7428     msi_parse_command_line( package, szCommandLine, FALSE );
7429     msi_adjust_privilege_properties( package );
7430     msi_set_context( package );
7431
7432     if (needs_ui_sequence( package))
7433     {
7434         package->script->InWhatSequence |= SEQUENCE_UI;
7435         rc = ACTION_ProcessUISequence(package);
7436         ui_exists = ui_sequence_exists(package);
7437         if (rc == ERROR_SUCCESS || !ui_exists)
7438         {
7439             package->script->InWhatSequence |= SEQUENCE_EXEC;
7440             rc = ACTION_ProcessExecSequence(package, ui_exists);
7441         }
7442     }
7443     else
7444         rc = ACTION_ProcessExecSequence(package, FALSE);
7445
7446     package->script->CurrentlyScripting = FALSE;
7447
7448     /* process the ending type action */
7449     if (rc == ERROR_SUCCESS)
7450         ACTION_PerformActionSequence(package, -1);
7451     else if (rc == ERROR_INSTALL_USEREXIT)
7452         ACTION_PerformActionSequence(package, -2);
7453     else if (rc == ERROR_INSTALL_SUSPEND)
7454         ACTION_PerformActionSequence(package, -4);
7455     else  /* failed */
7456         ACTION_PerformActionSequence(package, -3);
7457
7458     /* finish up running custom actions */
7459     ACTION_FinishCustomActions(package);
7460
7461     if (rc == ERROR_SUCCESS && package->need_reboot)
7462         return ERROR_SUCCESS_REBOOT_REQUIRED;
7463
7464     return rc;
7465 }