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