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