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