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