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