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