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