msi/tests: Run the 'in_use' tests again.
[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 ITERATE_SelfUnregModules( MSIRECORD *row, LPVOID param )
3709 {
3710     static const WCHAR regsvr32[] =
3711         {'r','e','g','s','v','r','3','2','.','e','x','e',' ','/','u',' ','\"',0};
3712     static const WCHAR close[] =  {'\"',0};
3713     MSIPACKAGE *package = param;
3714     LPCWSTR filename;
3715     LPWSTR cmdline;
3716     MSIFILE *file;
3717     DWORD len;
3718     STARTUPINFOW si;
3719     PROCESS_INFORMATION pi;
3720     BOOL ret;
3721     MSIRECORD *uirow;
3722     LPWSTR uipath, p;
3723
3724     memset( &si, 0, sizeof(STARTUPINFOW) );
3725
3726     filename = MSI_RecordGetString( row, 1 );
3727     file = get_loaded_file( package, filename );
3728
3729     if (!file)
3730     {
3731         ERR("Unable to find file id %s\n", debugstr_w(filename));
3732         return ERROR_SUCCESS;
3733     }
3734
3735     len = strlenW( regsvr32 ) + strlenW( file->TargetPath ) + 2;
3736
3737     cmdline = msi_alloc( len * sizeof(WCHAR) );
3738     strcpyW( cmdline, regsvr32 );
3739     strcatW( cmdline, file->TargetPath );
3740     strcatW( cmdline, close );
3741
3742     TRACE("Unregistering %s\n", debugstr_w(cmdline));
3743
3744     ret = CreateProcessW( NULL, cmdline, NULL, NULL, FALSE, 0, NULL, c_colon, &si, &pi );
3745     if (ret)
3746     {
3747         CloseHandle( pi.hThread );
3748         msi_dialog_check_messages( pi.hProcess );
3749         CloseHandle( pi.hProcess );
3750     }
3751
3752     msi_free( cmdline );
3753
3754     uirow = MSI_CreateRecord( 2 );
3755     uipath = strdupW( file->TargetPath );
3756     if ((p = strrchrW( uipath, '\\' )))
3757     {
3758         *p = 0;
3759         MSI_RecordSetStringW( uirow, 1, ++p );
3760     }
3761     MSI_RecordSetStringW( uirow, 2, uipath );
3762     ui_actiondata( package, szSelfUnregModules, uirow );
3763     msiobj_release( &uirow->hdr );
3764     msi_free( uipath );
3765     /* FIXME call ui_progress? */
3766
3767     return ERROR_SUCCESS;
3768 }
3769
3770 static UINT ACTION_SelfUnregModules( MSIPACKAGE *package )
3771 {
3772     UINT rc;
3773     MSIQUERY *view;
3774     static const WCHAR query[] =
3775         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3776          '`','S','e','l','f','R','e','g','`',0};
3777
3778     rc = MSI_DatabaseOpenViewW( package->db, query, &view );
3779     if (rc != ERROR_SUCCESS)
3780     {
3781         TRACE("no SelfReg table\n");
3782         return ERROR_SUCCESS;
3783     }
3784
3785     MSI_IterateRecords( view, NULL, ITERATE_SelfUnregModules, package );
3786     msiobj_release( &view->hdr );
3787
3788     return ERROR_SUCCESS;
3789 }
3790
3791 static UINT ACTION_PublishFeatures(MSIPACKAGE *package)
3792 {
3793     MSIFEATURE *feature;
3794     UINT rc;
3795     HKEY hkey;
3796     HKEY userdata = NULL;
3797
3798     if (!msi_check_publish(package))
3799         return ERROR_SUCCESS;
3800
3801     rc = MSIREG_OpenFeaturesKey(package->ProductCode, package->Context,
3802                                 &hkey, TRUE);
3803     if (rc != ERROR_SUCCESS)
3804         goto end;
3805
3806     rc = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, package->Context,
3807                                         &userdata, TRUE);
3808     if (rc != ERROR_SUCCESS)
3809         goto end;
3810
3811     /* here the guids are base 85 encoded */
3812     LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3813     {
3814         ComponentList *cl;
3815         LPWSTR data = NULL;
3816         GUID clsid;
3817         INT size;
3818         BOOL absent = FALSE;
3819         MSIRECORD *uirow;
3820
3821         if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_LOCAL ) &&
3822             !ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_SOURCE ) &&
3823             !ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_ADVERTISED ))
3824             absent = TRUE;
3825
3826         size = 1;
3827         LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3828         {
3829             size += 21;
3830         }
3831         if (feature->Feature_Parent)
3832             size += strlenW( feature->Feature_Parent )+2;
3833
3834         data = msi_alloc(size * sizeof(WCHAR));
3835
3836         data[0] = 0;
3837         LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3838         {
3839             MSICOMPONENT* component = cl->component;
3840             WCHAR buf[21];
3841
3842             buf[0] = 0;
3843             if (component->ComponentId)
3844             {
3845                 TRACE("From %s\n",debugstr_w(component->ComponentId));
3846                 CLSIDFromString(component->ComponentId, &clsid);
3847                 encode_base85_guid(&clsid,buf);
3848                 TRACE("to %s\n",debugstr_w(buf));
3849                 strcatW(data,buf);
3850             }
3851         }
3852
3853         if (feature->Feature_Parent)
3854         {
3855             static const WCHAR sep[] = {'\2',0};
3856             strcatW(data,sep);
3857             strcatW(data,feature->Feature_Parent);
3858         }
3859
3860         msi_reg_set_val_str( userdata, feature->Feature, data );
3861         msi_free(data);
3862
3863         size = 0;
3864         if (feature->Feature_Parent)
3865             size = strlenW(feature->Feature_Parent)*sizeof(WCHAR);
3866         if (!absent)
3867         {
3868             size += sizeof(WCHAR);
3869             RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
3870                            (LPBYTE)(feature->Feature_Parent ? feature->Feature_Parent : szEmpty),size);
3871         }
3872         else
3873         {
3874             size += 2*sizeof(WCHAR);
3875             data = msi_alloc(size);
3876             data[0] = 0x6;
3877             data[1] = 0;
3878             if (feature->Feature_Parent)
3879                 strcpyW( &data[1], feature->Feature_Parent );
3880             RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
3881                        (LPBYTE)data,size);
3882             msi_free(data);
3883         }
3884
3885         /* the UI chunk */
3886         uirow = MSI_CreateRecord( 1 );
3887         MSI_RecordSetStringW( uirow, 1, feature->Feature );
3888         ui_actiondata( package, szPublishFeatures, uirow);
3889         msiobj_release( &uirow->hdr );
3890         /* FIXME: call ui_progress? */
3891     }
3892
3893 end:
3894     RegCloseKey(hkey);
3895     RegCloseKey(userdata);
3896     return rc;
3897 }
3898
3899 static UINT msi_unpublish_feature(MSIPACKAGE *package, MSIFEATURE *feature)
3900 {
3901     UINT r;
3902     HKEY hkey;
3903
3904     TRACE("unpublishing feature %s\n", debugstr_w(feature->Feature));
3905
3906     r = MSIREG_OpenFeaturesKey(package->ProductCode, package->Context,
3907                                &hkey, FALSE);
3908     if (r == ERROR_SUCCESS)
3909     {
3910         RegDeleteValueW(hkey, feature->Feature);
3911         RegCloseKey(hkey);
3912     }
3913
3914     r = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, package->Context,
3915                                        &hkey, FALSE);
3916     if (r == ERROR_SUCCESS)
3917     {
3918         RegDeleteValueW(hkey, feature->Feature);
3919         RegCloseKey(hkey);
3920     }
3921
3922     return ERROR_SUCCESS;
3923 }
3924
3925 static UINT ACTION_UnpublishFeatures(MSIPACKAGE *package)
3926 {
3927     MSIFEATURE *feature;
3928
3929     if (!msi_check_unpublish(package))
3930         return ERROR_SUCCESS;
3931
3932     LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3933     {
3934         msi_unpublish_feature(package, feature);
3935     }
3936
3937     return ERROR_SUCCESS;
3938 }
3939
3940 static UINT msi_publish_install_properties(MSIPACKAGE *package, HKEY hkey)
3941 {
3942     LPWSTR prop, val, key;
3943     SYSTEMTIME systime;
3944     DWORD size, langid;
3945     WCHAR date[9];
3946     LPWSTR buffer;
3947
3948     static const WCHAR date_fmt[] = {'%','i','%','0','2','i','%','0','2','i',0};
3949     static const WCHAR szWindowsInstaller[] =
3950         {'W','i','n','d','o','w','s','I','n','s','t','a','l','l','e','r',0};
3951     static const WCHAR modpath_fmt[] =
3952         {'M','s','i','E','x','e','c','.','e','x','e',' ',
3953          '/','I','[','P','r','o','d','u','c','t','C','o','d','e',']',0};
3954     static const WCHAR szModifyPath[] =
3955         {'M','o','d','i','f','y','P','a','t','h',0};
3956     static const WCHAR szUninstallString[] =
3957         {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0};
3958     static const WCHAR szEstimatedSize[] =
3959         {'E','s','t','i','m','a','t','e','d','S','i','z','e',0};
3960     static const WCHAR szProductLanguage[] =
3961         {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
3962     static const WCHAR szProductVersion[] =
3963         {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
3964     static const WCHAR szProductName[] =
3965         {'P','r','o','d','u','c','t','N','a','m','e',0};
3966     static const WCHAR szDisplayName[] =
3967         {'D','i','s','p','l','a','y','N','a','m','e',0};
3968     static const WCHAR szDisplayVersion[] =
3969         {'D','i','s','p','l','a','y','V','e','r','s','i','o','n',0};
3970     static const WCHAR szManufacturer[] =
3971         {'M','a','n','u','f','a','c','t','u','r','e','r',0};
3972
3973     static const LPCSTR propval[] = {
3974         "ARPAUTHORIZEDCDFPREFIX", "AuthorizedCDFPrefix",
3975         "ARPCONTACT",             "Contact",
3976         "ARPCOMMENTS",            "Comments",
3977         "ProductName",            "DisplayName",
3978         "ProductVersion",         "DisplayVersion",
3979         "ARPHELPLINK",            "HelpLink",
3980         "ARPHELPTELEPHONE",       "HelpTelephone",
3981         "ARPINSTALLLOCATION",     "InstallLocation",
3982         "SourceDir",              "InstallSource",
3983         "Manufacturer",           "Publisher",
3984         "ARPREADME",              "Readme",
3985         "ARPSIZE",                "Size",
3986         "ARPURLINFOABOUT",        "URLInfoAbout",
3987         "ARPURLUPDATEINFO",       "URLUpdateInfo",
3988         NULL,
3989     };
3990     const LPCSTR *p = propval;
3991
3992     while (*p)
3993     {
3994         prop = strdupAtoW(*p++);
3995         key = strdupAtoW(*p++);
3996         val = msi_dup_property(package, prop);
3997         msi_reg_set_val_str(hkey, key, val);
3998         msi_free(val);
3999         msi_free(key);
4000         msi_free(prop);
4001     }
4002
4003     msi_reg_set_val_dword(hkey, szWindowsInstaller, 1);
4004
4005     size = deformat_string(package, modpath_fmt, &buffer);
4006     RegSetValueExW(hkey, szModifyPath, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
4007     RegSetValueExW(hkey, szUninstallString, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
4008     msi_free(buffer);
4009
4010     /* FIXME: Write real Estimated Size when we have it */
4011     msi_reg_set_val_dword(hkey, szEstimatedSize, 0);
4012
4013     buffer = msi_dup_property(package, szProductName);
4014     msi_reg_set_val_str(hkey, szDisplayName, buffer);
4015     msi_free(buffer);
4016
4017     buffer = msi_dup_property(package, cszSourceDir);
4018     msi_reg_set_val_str(hkey, INSTALLPROPERTY_INSTALLSOURCEW, buffer);
4019     msi_free(buffer);
4020
4021     buffer = msi_dup_property(package, szManufacturer);
4022     msi_reg_set_val_str(hkey, INSTALLPROPERTY_PUBLISHERW, buffer);
4023     msi_free(buffer);
4024
4025     GetLocalTime(&systime);
4026     sprintfW(date, date_fmt, systime.wYear, systime.wMonth, systime.wDay);
4027     msi_reg_set_val_str(hkey, INSTALLPROPERTY_INSTALLDATEW, date);
4028
4029     langid = msi_get_property_int(package, szProductLanguage, 0);
4030     msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
4031
4032     buffer = msi_dup_property(package, szProductVersion);
4033     msi_reg_set_val_str(hkey, szDisplayVersion, buffer);
4034     if (buffer)
4035     {
4036         DWORD verdword = msi_version_str_to_dword(buffer);
4037
4038         msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
4039         msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMAJORW, verdword >> 24);
4040         msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMINORW, (verdword >> 16) & 0xFF);
4041         msi_free(buffer);
4042     }
4043
4044     return ERROR_SUCCESS;
4045 }
4046
4047 static UINT ACTION_RegisterProduct(MSIPACKAGE *package)
4048 {
4049     WCHAR squashed_pc[SQUISH_GUID_SIZE];
4050     LPWSTR upgrade_code;
4051     HKEY hkey, props;
4052     HKEY upgrade;
4053     UINT rc;
4054
4055     static const WCHAR szUpgradeCode[] = {
4056         'U','p','g','r','a','d','e','C','o','d','e',0};
4057
4058     /* FIXME: also need to publish if the product is in advertise mode */
4059     if (!msi_check_publish(package))
4060         return ERROR_SUCCESS;
4061
4062     rc = MSIREG_OpenUninstallKey(package->ProductCode, &hkey, TRUE);
4063     if (rc != ERROR_SUCCESS)
4064         return rc;
4065
4066     rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context,
4067                                  NULL, &props, TRUE);
4068     if (rc != ERROR_SUCCESS)
4069         goto done;
4070
4071     msi_reg_set_val_str( props, INSTALLPROPERTY_LOCALPACKAGEW, package->db->localfile );
4072     msi_free( package->db->localfile );
4073     package->db->localfile = NULL;
4074
4075     rc = msi_publish_install_properties(package, hkey);
4076     if (rc != ERROR_SUCCESS)
4077         goto done;
4078
4079     rc = msi_publish_install_properties(package, props);
4080     if (rc != ERROR_SUCCESS)
4081         goto done;
4082
4083     upgrade_code = msi_dup_property(package, szUpgradeCode);
4084     if (upgrade_code)
4085     {
4086         MSIREG_OpenUpgradeCodesKey(upgrade_code, &upgrade, TRUE);
4087         squash_guid(package->ProductCode, squashed_pc);
4088         msi_reg_set_val_str(upgrade, squashed_pc, NULL);
4089         RegCloseKey(upgrade);
4090         msi_free(upgrade_code);
4091     }
4092
4093 done:
4094     RegCloseKey(hkey);
4095
4096     return ERROR_SUCCESS;
4097 }
4098
4099 static UINT ACTION_InstallExecute(MSIPACKAGE *package)
4100 {
4101     return execute_script(package,INSTALL_SCRIPT);
4102 }
4103
4104 static UINT msi_unpublish_product(MSIPACKAGE *package)
4105 {
4106     LPWSTR upgrade;
4107     LPWSTR remove = NULL;
4108     LPWSTR *features = NULL;
4109     BOOL full_uninstall = TRUE;
4110     MSIFEATURE *feature;
4111
4112     static const WCHAR szUpgradeCode[] =
4113         {'U','p','g','r','a','d','e','C','o','d','e',0};
4114
4115     remove = msi_dup_property(package, szRemove);
4116     if (!remove)
4117         return ERROR_SUCCESS;
4118
4119     features = msi_split_string(remove, ',');
4120     if (!features)
4121     {
4122         msi_free(remove);
4123         ERR("REMOVE feature list is empty!\n");
4124         return ERROR_FUNCTION_FAILED;
4125     }
4126
4127     if (!lstrcmpW(features[0], szAll))
4128         full_uninstall = TRUE;
4129     else
4130     {
4131         LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4132         {
4133             if (feature->Action != INSTALLSTATE_ABSENT)
4134                 full_uninstall = FALSE;
4135         }
4136     }
4137
4138     if (!full_uninstall)
4139         goto done;
4140
4141     MSIREG_DeleteProductKey(package->ProductCode);
4142     MSIREG_DeleteUserDataProductKey(package->ProductCode);
4143     MSIREG_DeleteUninstallKey(package->ProductCode);
4144
4145     if (package->Context == MSIINSTALLCONTEXT_MACHINE)
4146     {
4147         MSIREG_DeleteLocalClassesProductKey(package->ProductCode);
4148         MSIREG_DeleteLocalClassesFeaturesKey(package->ProductCode);
4149     }
4150     else
4151     {
4152         MSIREG_DeleteUserProductKey(package->ProductCode);
4153         MSIREG_DeleteUserFeaturesKey(package->ProductCode);
4154     }
4155
4156     upgrade = msi_dup_property(package, szUpgradeCode);
4157     if (upgrade)
4158     {
4159         MSIREG_DeleteUserUpgradeCodesKey(upgrade);
4160         msi_free(upgrade);
4161     }
4162
4163 done:
4164     msi_free(remove);
4165     msi_free(features);
4166     return ERROR_SUCCESS;
4167 }
4168
4169 static UINT ACTION_InstallFinalize(MSIPACKAGE *package)
4170 {
4171     UINT rc;
4172
4173     rc = msi_unpublish_product(package);
4174     if (rc != ERROR_SUCCESS)
4175         return rc;
4176
4177     /* turn off scheduling */
4178     package->script->CurrentlyScripting= FALSE;
4179
4180     /* first do the same as an InstallExecute */
4181     rc = ACTION_InstallExecute(package);
4182     if (rc != ERROR_SUCCESS)
4183         return rc;
4184
4185     /* then handle Commit Actions */
4186     rc = execute_script(package,COMMIT_SCRIPT);
4187
4188     return rc;
4189 }
4190
4191 UINT ACTION_ForceReboot(MSIPACKAGE *package)
4192 {
4193     static const WCHAR RunOnce[] = {
4194     'S','o','f','t','w','a','r','e','\\',
4195     'M','i','c','r','o','s','o','f','t','\\',
4196     'W','i','n','d','o','w','s','\\',
4197     'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
4198     'R','u','n','O','n','c','e',0};
4199     static const WCHAR InstallRunOnce[] = {
4200     'S','o','f','t','w','a','r','e','\\',
4201     'M','i','c','r','o','s','o','f','t','\\',
4202     'W','i','n','d','o','w','s','\\',
4203     'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
4204     'I','n','s','t','a','l','l','e','r','\\',
4205     'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
4206
4207     static const WCHAR msiexec_fmt[] = {
4208     '%','s',
4209     '\\','M','s','i','E','x','e','c','.','e','x','e',' ','/','@',' ',
4210     '\"','%','s','\"',0};
4211     static const WCHAR install_fmt[] = {
4212     '/','I',' ','\"','%','s','\"',' ',
4213     'A','F','T','E','R','R','E','B','O','O','T','=','1',' ',
4214     'R','U','N','O','N','C','E','E','N','T','R','Y','=','\"','%','s','\"',0};
4215     WCHAR buffer[256], sysdir[MAX_PATH];
4216     HKEY hkey;
4217     WCHAR squished_pc[100];
4218
4219     squash_guid(package->ProductCode,squished_pc);
4220
4221     GetSystemDirectoryW(sysdir, sizeof(sysdir)/sizeof(sysdir[0]));
4222     RegCreateKeyW(HKEY_LOCAL_MACHINE,RunOnce,&hkey);
4223     snprintfW(buffer,sizeof(buffer)/sizeof(buffer[0]),msiexec_fmt,sysdir,
4224      squished_pc);
4225
4226     msi_reg_set_val_str( hkey, squished_pc, buffer );
4227     RegCloseKey(hkey);
4228
4229     TRACE("Reboot command %s\n",debugstr_w(buffer));
4230
4231     RegCreateKeyW(HKEY_LOCAL_MACHINE,InstallRunOnce,&hkey);
4232     sprintfW(buffer,install_fmt,package->ProductCode,squished_pc);
4233
4234     msi_reg_set_val_str( hkey, squished_pc, buffer );
4235     RegCloseKey(hkey);
4236
4237     return ERROR_INSTALL_SUSPEND;
4238 }
4239
4240 static UINT ACTION_ResolveSource(MSIPACKAGE* package)
4241 {
4242     DWORD attrib;
4243     UINT rc;
4244
4245     /*
4246      * We are currently doing what should be done here in the top level Install
4247      * however for Administrative and uninstalls this step will be needed
4248      */
4249     if (!package->PackagePath)
4250         return ERROR_SUCCESS;
4251
4252     msi_set_sourcedir_props(package, TRUE);
4253
4254     attrib = GetFileAttributesW(package->db->path);
4255     if (attrib == INVALID_FILE_ATTRIBUTES)
4256     {
4257         LPWSTR prompt;
4258         LPWSTR msg;
4259         DWORD size = 0;
4260
4261         rc = MsiSourceListGetInfoW(package->ProductCode, NULL, 
4262                 package->Context, MSICODE_PRODUCT,
4263                 INSTALLPROPERTY_DISKPROMPTW,NULL,&size);
4264         if (rc == ERROR_MORE_DATA)
4265         {
4266             prompt = msi_alloc(size * sizeof(WCHAR));
4267             MsiSourceListGetInfoW(package->ProductCode, NULL, 
4268                     package->Context, MSICODE_PRODUCT,
4269                     INSTALLPROPERTY_DISKPROMPTW,prompt,&size);
4270         }
4271         else
4272             prompt = strdupW(package->db->path);
4273
4274         msg = generate_error_string(package,1302,1,prompt);
4275         while(attrib == INVALID_FILE_ATTRIBUTES)
4276         {
4277             rc = MessageBoxW(NULL,msg,NULL,MB_OKCANCEL);
4278             if (rc == IDCANCEL)
4279             {
4280                 rc = ERROR_INSTALL_USEREXIT;
4281                 break;
4282             }
4283             attrib = GetFileAttributesW(package->db->path);
4284         }
4285         msi_free(prompt);
4286         rc = ERROR_SUCCESS;
4287     }
4288     else
4289         return ERROR_SUCCESS;
4290
4291     return rc;
4292 }
4293
4294 static UINT ACTION_RegisterUser(MSIPACKAGE *package)
4295 {
4296     HKEY hkey=0;
4297     LPWSTR buffer;
4298     LPWSTR productid;
4299     UINT rc,i;
4300
4301     static const WCHAR szPropKeys[][80] = 
4302     {
4303         {'P','r','o','d','u','c','t','I','D',0},
4304         {'U','S','E','R','N','A','M','E',0},
4305         {'C','O','M','P','A','N','Y','N','A','M','E',0},
4306         {0},
4307     };
4308
4309     static const WCHAR szRegKeys[][80] = 
4310     {
4311         {'P','r','o','d','u','c','t','I','D',0},
4312         {'R','e','g','O','w','n','e','r',0},
4313         {'R','e','g','C','o','m','p','a','n','y',0},
4314         {0},
4315     };
4316
4317     if (msi_check_unpublish(package))
4318     {
4319         MSIREG_DeleteUserDataProductKey(package->ProductCode);
4320         return ERROR_SUCCESS;
4321     }
4322
4323     productid = msi_dup_property( package, INSTALLPROPERTY_PRODUCTIDW );
4324     if (!productid)
4325         return ERROR_SUCCESS;
4326
4327     rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context,
4328                                  NULL, &hkey, TRUE);
4329     if (rc != ERROR_SUCCESS)
4330         goto end;
4331
4332     for( i = 0; szPropKeys[i][0]; i++ )
4333     {
4334         buffer = msi_dup_property( package, szPropKeys[i] );
4335         msi_reg_set_val_str( hkey, szRegKeys[i], buffer );
4336         msi_free( buffer );
4337     }
4338
4339 end:
4340     msi_free(productid);
4341     RegCloseKey(hkey);
4342
4343     /* FIXME: call ui_actiondata */
4344
4345     return rc;
4346 }
4347
4348
4349 static UINT ACTION_ExecuteAction(MSIPACKAGE *package)
4350 {
4351     UINT rc;
4352
4353     package->script->InWhatSequence |= SEQUENCE_EXEC;
4354     rc = ACTION_ProcessExecSequence(package,FALSE);
4355     return rc;
4356 }
4357
4358
4359 static UINT ITERATE_PublishComponent(MSIRECORD *rec, LPVOID param)
4360 {
4361     MSIPACKAGE *package = param;
4362     LPCWSTR compgroupid=NULL;
4363     LPCWSTR feature=NULL;
4364     LPCWSTR text = NULL;
4365     LPCWSTR qualifier = NULL;
4366     LPCWSTR component = NULL;
4367     LPWSTR advertise = NULL;
4368     LPWSTR output = NULL;
4369     HKEY hkey;
4370     UINT rc = ERROR_SUCCESS;
4371     MSICOMPONENT *comp;
4372     DWORD sz = 0;
4373     MSIRECORD *uirow;
4374
4375     component = MSI_RecordGetString(rec,3);
4376     comp = get_loaded_component(package,component);
4377
4378     if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL ) && 
4379        !ACTION_VerifyComponentForAction( comp, INSTALLSTATE_SOURCE ) &&
4380        !ACTION_VerifyComponentForAction( comp, INSTALLSTATE_ADVERTISED ))
4381     {
4382         TRACE("Skipping: Component %s not scheduled for install\n",
4383                         debugstr_w(component));
4384
4385         return ERROR_SUCCESS;
4386     }
4387
4388     compgroupid = MSI_RecordGetString(rec,1);
4389     qualifier = MSI_RecordGetString(rec,2);
4390
4391     rc = MSIREG_OpenUserComponentsKey(compgroupid, &hkey, TRUE);
4392     if (rc != ERROR_SUCCESS)
4393         goto end;
4394     
4395     text = MSI_RecordGetString(rec,4);
4396     feature = MSI_RecordGetString(rec,5);
4397   
4398     advertise = create_component_advertise_string(package, comp, feature);
4399
4400     sz = strlenW(advertise);
4401
4402     if (text)
4403         sz += lstrlenW(text);
4404
4405     sz+=3;
4406     sz *= sizeof(WCHAR);
4407            
4408     output = msi_alloc_zero(sz);
4409     strcpyW(output,advertise);
4410     msi_free(advertise);
4411
4412     if (text)
4413         strcatW(output,text);
4414
4415     msi_reg_set_val_multi_str( hkey, qualifier, output );
4416     
4417 end:
4418     RegCloseKey(hkey);
4419     msi_free(output);
4420
4421     /* the UI chunk */
4422     uirow = MSI_CreateRecord( 2 );
4423     MSI_RecordSetStringW( uirow, 1, compgroupid );
4424     MSI_RecordSetStringW( uirow, 2, qualifier);
4425     ui_actiondata( package, szPublishComponents, uirow);
4426     msiobj_release( &uirow->hdr );
4427     /* FIXME: call ui_progress? */
4428
4429     return rc;
4430 }
4431
4432 /*
4433  * At present I am ignorning the advertised components part of this and only
4434  * focusing on the qualified component sets
4435  */
4436 static UINT ACTION_PublishComponents(MSIPACKAGE *package)
4437 {
4438     UINT rc;
4439     MSIQUERY * view;
4440     static const WCHAR ExecSeqQuery[] =
4441         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4442          '`','P','u','b','l','i','s','h',
4443          'C','o','m','p','o','n','e','n','t','`',0};
4444     
4445     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4446     if (rc != ERROR_SUCCESS)
4447         return ERROR_SUCCESS;
4448
4449     rc = MSI_IterateRecords(view, NULL, ITERATE_PublishComponent, package);
4450     msiobj_release(&view->hdr);
4451
4452     return rc;
4453 }
4454
4455 static UINT ITERATE_InstallService(MSIRECORD *rec, LPVOID param)
4456 {
4457     MSIPACKAGE *package = param;
4458     MSIRECORD *row;
4459     MSIFILE *file;
4460     SC_HANDLE hscm, service = NULL;
4461     LPCWSTR comp, depends, pass;
4462     LPWSTR name = NULL, disp = NULL;
4463     LPCWSTR load_order, serv_name, key;
4464     DWORD serv_type, start_type;
4465     DWORD err_control;
4466
4467     static const WCHAR query[] =
4468         {'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
4469          '`','C','o','m','p','o','n','e','n','t','`',' ',
4470          'W','H','E','R','E',' ',
4471          '`','C','o','m','p','o','n','e','n','t','`',' ',
4472          '=','\'','%','s','\'',0};
4473
4474     hscm = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASEW, GENERIC_WRITE);
4475     if (!hscm)
4476     {
4477         ERR("Failed to open the SC Manager!\n");
4478         goto done;
4479     }
4480
4481     start_type = MSI_RecordGetInteger(rec, 5);
4482     if (start_type == SERVICE_BOOT_START || start_type == SERVICE_SYSTEM_START)
4483         goto done;
4484
4485     depends = MSI_RecordGetString(rec, 8);
4486     if (depends && *depends)
4487         FIXME("Dependency list unhandled!\n");
4488
4489     deformat_string(package, MSI_RecordGetString(rec, 2), &name);
4490     deformat_string(package, MSI_RecordGetString(rec, 3), &disp);
4491     serv_type = MSI_RecordGetInteger(rec, 4);
4492     err_control = MSI_RecordGetInteger(rec, 6);
4493     load_order = MSI_RecordGetString(rec, 7);
4494     serv_name = MSI_RecordGetString(rec, 9);
4495     pass = MSI_RecordGetString(rec, 10);
4496     comp = MSI_RecordGetString(rec, 12);
4497
4498     /* fetch the service path */
4499     row = MSI_QueryGetRecord(package->db, query, comp);
4500     if (!row)
4501     {
4502         ERR("Control query failed!\n");
4503         goto done;
4504     }
4505
4506     key = MSI_RecordGetString(row, 6);
4507
4508     file = get_loaded_file(package, key);
4509     msiobj_release(&row->hdr);
4510     if (!file)
4511     {
4512         ERR("Failed to load the service file\n");
4513         goto done;
4514     }
4515
4516     service = CreateServiceW(hscm, name, disp, GENERIC_ALL, serv_type,
4517                              start_type, err_control, file->TargetPath,
4518                              load_order, NULL, NULL, serv_name, pass);
4519     if (!service)
4520     {
4521         if (GetLastError() != ERROR_SERVICE_EXISTS)
4522             ERR("Failed to create service %s: %d\n", debugstr_w(name), GetLastError());
4523     }
4524
4525 done:
4526     CloseServiceHandle(service);
4527     CloseServiceHandle(hscm);
4528     msi_free(name);
4529     msi_free(disp);
4530
4531     return ERROR_SUCCESS;
4532 }
4533
4534 static UINT ACTION_InstallServices( MSIPACKAGE *package )
4535 {
4536     UINT rc;
4537     MSIQUERY * view;
4538     static const WCHAR ExecSeqQuery[] =
4539         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4540          'S','e','r','v','i','c','e','I','n','s','t','a','l','l',0};
4541     
4542     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4543     if (rc != ERROR_SUCCESS)
4544         return ERROR_SUCCESS;
4545
4546     rc = MSI_IterateRecords(view, NULL, ITERATE_InstallService, package);
4547     msiobj_release(&view->hdr);
4548
4549     return rc;
4550 }
4551
4552 /* converts arg1[~]arg2[~]arg3 to a list of ptrs to the strings */
4553 static LPCWSTR *msi_service_args_to_vector(LPWSTR args, DWORD *numargs)
4554 {
4555     LPCWSTR *vector, *temp_vector;
4556     LPWSTR p, q;
4557     DWORD sep_len;
4558
4559     static const WCHAR separator[] = {'[','~',']',0};
4560
4561     *numargs = 0;
4562     sep_len = sizeof(separator) / sizeof(WCHAR) - 1;
4563
4564     if (!args)
4565         return NULL;
4566
4567     vector = msi_alloc(sizeof(LPWSTR));
4568     if (!vector)
4569         return NULL;
4570
4571     p = args;
4572     do
4573     {
4574         (*numargs)++;
4575         vector[*numargs - 1] = p;
4576
4577         if ((q = strstrW(p, separator)))
4578         {
4579             *q = '\0';
4580
4581             temp_vector = msi_realloc(vector, (*numargs + 1) * sizeof(LPWSTR));
4582             if (!temp_vector)
4583             {
4584                 msi_free(vector);
4585                 return NULL;
4586             }
4587             vector = temp_vector;
4588
4589             p = q + sep_len;
4590         }
4591     } while (q);
4592
4593     return vector;
4594 }
4595
4596 static UINT ITERATE_StartService(MSIRECORD *rec, LPVOID param)
4597 {
4598     MSIPACKAGE *package = param;
4599     MSICOMPONENT *comp;
4600     SC_HANDLE scm, service = NULL;
4601     LPCWSTR *vector = NULL;
4602     LPWSTR name, args;
4603     DWORD event, numargs;
4604     UINT r = ERROR_FUNCTION_FAILED;
4605
4606     comp = get_loaded_component(package, MSI_RecordGetString(rec, 6));
4607     if (!comp || comp->Action == INSTALLSTATE_UNKNOWN || comp->Action == INSTALLSTATE_ABSENT)
4608         return ERROR_SUCCESS;
4609
4610     deformat_string(package, MSI_RecordGetString(rec, 2), &name);
4611     deformat_string(package, MSI_RecordGetString(rec, 4), &args);
4612     event = MSI_RecordGetInteger(rec, 3);
4613
4614     if (!(event & msidbServiceControlEventStart))
4615         return ERROR_SUCCESS;
4616
4617     scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
4618     if (!scm)
4619     {
4620         ERR("Failed to open the service control manager\n");
4621         goto done;
4622     }
4623
4624     service = OpenServiceW(scm, name, SERVICE_START);
4625     if (!service)
4626     {
4627         ERR("Failed to open service %s (%u)\n", debugstr_w(name), GetLastError());
4628         goto done;
4629     }
4630
4631     vector = msi_service_args_to_vector(args, &numargs);
4632
4633     if (!StartServiceW(service, numargs, vector) &&
4634         GetLastError() != ERROR_SERVICE_ALREADY_RUNNING)
4635     {
4636         ERR("Failed to start service %s (%u)\n", debugstr_w(name), GetLastError());
4637         goto done;
4638     }
4639
4640     r = ERROR_SUCCESS;
4641
4642 done:
4643     CloseServiceHandle(service);
4644     CloseServiceHandle(scm);
4645
4646     msi_free(name);
4647     msi_free(args);
4648     msi_free(vector);
4649     return r;
4650 }
4651
4652 static UINT ACTION_StartServices( MSIPACKAGE *package )
4653 {
4654     UINT rc;
4655     MSIQUERY *view;
4656
4657     static const WCHAR query[] = {
4658         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4659         'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
4660
4661     rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4662     if (rc != ERROR_SUCCESS)
4663         return ERROR_SUCCESS;
4664
4665     rc = MSI_IterateRecords(view, NULL, ITERATE_StartService, package);
4666     msiobj_release(&view->hdr);
4667
4668     return rc;
4669 }
4670
4671 static BOOL stop_service_dependents(SC_HANDLE scm, SC_HANDLE service)
4672 {
4673     DWORD i, needed, count;
4674     ENUM_SERVICE_STATUSW *dependencies;
4675     SERVICE_STATUS ss;
4676     SC_HANDLE depserv;
4677
4678     if (EnumDependentServicesW(service, SERVICE_ACTIVE, NULL,
4679                                0, &needed, &count))
4680         return TRUE;
4681
4682     if (GetLastError() != ERROR_MORE_DATA)
4683         return FALSE;
4684
4685     dependencies = msi_alloc(needed);
4686     if (!dependencies)
4687         return FALSE;
4688
4689     if (!EnumDependentServicesW(service, SERVICE_ACTIVE, dependencies,
4690                                 needed, &needed, &count))
4691         goto error;
4692
4693     for (i = 0; i < count; i++)
4694     {
4695         depserv = OpenServiceW(scm, dependencies[i].lpServiceName,
4696                                SERVICE_STOP | SERVICE_QUERY_STATUS);
4697         if (!depserv)
4698             goto error;
4699
4700         if (!ControlService(depserv, SERVICE_CONTROL_STOP, &ss))
4701             goto error;
4702     }
4703
4704     return TRUE;
4705
4706 error:
4707     msi_free(dependencies);
4708     return FALSE;
4709 }
4710
4711 static UINT stop_service( LPCWSTR name )
4712 {
4713     SC_HANDLE scm = NULL, service = NULL;
4714     SERVICE_STATUS status;
4715     SERVICE_STATUS_PROCESS ssp;
4716     DWORD needed;
4717
4718     scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
4719     if (!scm)
4720     {
4721         WARN("Failed to open the SCM: %d\n", GetLastError());
4722         goto done;
4723     }
4724
4725     service = OpenServiceW(scm, name,
4726                            SERVICE_STOP |
4727                            SERVICE_QUERY_STATUS |
4728                            SERVICE_ENUMERATE_DEPENDENTS);
4729     if (!service)
4730     {
4731         WARN("Failed to open service (%s): %d\n", debugstr_w(name), GetLastError());
4732         goto done;
4733     }
4734
4735     if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp,
4736                               sizeof(SERVICE_STATUS_PROCESS), &needed))
4737     {
4738         WARN("Failed to query service status (%s): %d\n", debugstr_w(name), GetLastError());
4739         goto done;
4740     }
4741
4742     if (ssp.dwCurrentState == SERVICE_STOPPED)
4743         goto done;
4744
4745     stop_service_dependents(scm, service);
4746
4747     if (!ControlService(service, SERVICE_CONTROL_STOP, &status))
4748         WARN("Failed to stop service (%s): %d\n", debugstr_w(name), GetLastError());
4749
4750 done:
4751     CloseServiceHandle(service);
4752     CloseServiceHandle(scm);
4753
4754     return ERROR_SUCCESS;
4755 }
4756
4757 static UINT ITERATE_StopService( MSIRECORD *rec, LPVOID param )
4758 {
4759     MSIPACKAGE *package = param;
4760     MSICOMPONENT *comp;
4761     LPWSTR name;
4762     DWORD event;
4763
4764     event = MSI_RecordGetInteger( rec, 3 );
4765     if (!(event & msidbServiceControlEventStop))
4766         return ERROR_SUCCESS;
4767
4768     comp = get_loaded_component( package, MSI_RecordGetString( rec, 6 ) );
4769     if (!comp || comp->Action == INSTALLSTATE_UNKNOWN || comp->Action == INSTALLSTATE_ABSENT)
4770         return ERROR_SUCCESS;
4771
4772     deformat_string( package, MSI_RecordGetString( rec, 2 ), &name );
4773     stop_service( name );
4774     msi_free( name );
4775
4776     return ERROR_SUCCESS;
4777 }
4778
4779 static UINT ACTION_StopServices( MSIPACKAGE *package )
4780 {
4781     UINT rc;
4782     MSIQUERY *view;
4783
4784     static const WCHAR query[] = {
4785         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4786         'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
4787
4788     rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4789     if (rc != ERROR_SUCCESS)
4790         return ERROR_SUCCESS;
4791
4792     rc = MSI_IterateRecords(view, NULL, ITERATE_StopService, package);
4793     msiobj_release(&view->hdr);
4794
4795     return rc;
4796 }
4797
4798 static UINT ITERATE_DeleteService( MSIRECORD *rec, LPVOID param )
4799 {
4800     MSIPACKAGE *package = param;
4801     MSICOMPONENT *comp;
4802     LPWSTR name = NULL;
4803     DWORD event;
4804     SC_HANDLE scm = NULL, service = NULL;
4805
4806     event = MSI_RecordGetInteger( rec, 3 );
4807     if (!(event & msidbServiceControlEventDelete))
4808         return ERROR_SUCCESS;
4809
4810     comp = get_loaded_component( package, MSI_RecordGetString(rec, 6) );
4811     if (!comp || comp->Action == INSTALLSTATE_UNKNOWN || comp->Action == INSTALLSTATE_ABSENT)
4812         return ERROR_SUCCESS;
4813
4814     deformat_string( package, MSI_RecordGetString(rec, 2), &name );
4815     stop_service( name );
4816
4817     scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_ALL_ACCESS );
4818     if (!scm)
4819     {
4820         WARN("Failed to open the SCM: %d\n", GetLastError());
4821         goto done;
4822     }
4823
4824     service = OpenServiceW( scm, name, DELETE );
4825     if (!service)
4826     {
4827         WARN("Failed to open service (%s): %u\n", debugstr_w(name), GetLastError());
4828         goto done;
4829     }
4830
4831     if (!DeleteService( service ))
4832         WARN("Failed to delete service (%s): %u\n", debugstr_w(name), GetLastError());
4833
4834 done:
4835     CloseServiceHandle( service );
4836     CloseServiceHandle( scm );
4837     msi_free( name );
4838
4839     return ERROR_SUCCESS;
4840 }
4841
4842 static UINT ACTION_DeleteServices( MSIPACKAGE *package )
4843 {
4844     UINT rc;
4845     MSIQUERY *view;
4846
4847     static const WCHAR query[] = {
4848         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4849         'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
4850
4851     rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4852     if (rc != ERROR_SUCCESS)
4853         return ERROR_SUCCESS;
4854
4855     rc = MSI_IterateRecords( view, NULL, ITERATE_DeleteService, package );
4856     msiobj_release( &view->hdr );
4857
4858     return rc;
4859 }
4860
4861 static MSIFILE *msi_find_file( MSIPACKAGE *package, LPCWSTR filename )
4862 {
4863     MSIFILE *file;
4864
4865     LIST_FOR_EACH_ENTRY(file, &package->files, MSIFILE, entry)
4866     {
4867         if (!lstrcmpW(file->File, filename))
4868             return file;
4869     }
4870
4871     return NULL;
4872 }
4873
4874 static UINT ITERATE_InstallODBCDriver( MSIRECORD *rec, LPVOID param )
4875 {
4876     MSIPACKAGE *package = param;
4877     LPWSTR driver, driver_path, ptr;
4878     WCHAR outpath[MAX_PATH];
4879     MSIFILE *driver_file, *setup_file;
4880     LPCWSTR desc;
4881     DWORD len, usage;
4882     UINT r = ERROR_SUCCESS;
4883
4884     static const WCHAR driver_fmt[] = {
4885         'D','r','i','v','e','r','=','%','s',0};
4886     static const WCHAR setup_fmt[] = {
4887         'S','e','t','u','p','=','%','s',0};
4888     static const WCHAR usage_fmt[] = {
4889         'F','i','l','e','U','s','a','g','e','=','1',0};
4890
4891     desc = MSI_RecordGetString(rec, 3);
4892
4893     driver_file = msi_find_file(package, MSI_RecordGetString(rec, 4));
4894     setup_file = msi_find_file(package, MSI_RecordGetString(rec, 5));
4895
4896     if (!driver_file || !setup_file)
4897     {
4898         ERR("ODBC Driver entry not found!\n");
4899         return ERROR_FUNCTION_FAILED;
4900     }
4901
4902     len = lstrlenW(desc) + lstrlenW(driver_fmt) + lstrlenW(driver_file->FileName) +
4903           lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName) +
4904           lstrlenW(usage_fmt) + 1;
4905     driver = msi_alloc(len * sizeof(WCHAR));
4906     if (!driver)
4907         return ERROR_OUTOFMEMORY;
4908
4909     ptr = driver;
4910     lstrcpyW(ptr, desc);
4911     ptr += lstrlenW(ptr) + 1;
4912
4913     sprintfW(ptr, driver_fmt, driver_file->FileName);
4914     ptr += lstrlenW(ptr) + 1;
4915
4916     sprintfW(ptr, setup_fmt, setup_file->FileName);
4917     ptr += lstrlenW(ptr) + 1;
4918
4919     lstrcpyW(ptr, usage_fmt);
4920     ptr += lstrlenW(ptr) + 1;
4921     *ptr = '\0';
4922
4923     driver_path = strdupW(driver_file->TargetPath);
4924     ptr = strrchrW(driver_path, '\\');
4925     if (ptr) *ptr = '\0';
4926
4927     if (!SQLInstallDriverExW(driver, driver_path, outpath, MAX_PATH,
4928                              NULL, ODBC_INSTALL_COMPLETE, &usage))
4929     {
4930         ERR("Failed to install SQL driver!\n");
4931         r = ERROR_FUNCTION_FAILED;
4932     }
4933
4934     msi_free(driver);
4935     msi_free(driver_path);
4936
4937     return r;
4938 }
4939
4940 static UINT ITERATE_InstallODBCTranslator( MSIRECORD *rec, LPVOID param )
4941 {
4942     MSIPACKAGE *package = param;
4943     LPWSTR translator, translator_path, ptr;
4944     WCHAR outpath[MAX_PATH];
4945     MSIFILE *translator_file, *setup_file;
4946     LPCWSTR desc;
4947     DWORD len, usage;
4948     UINT r = ERROR_SUCCESS;
4949
4950     static const WCHAR translator_fmt[] = {
4951         'T','r','a','n','s','l','a','t','o','r','=','%','s',0};
4952     static const WCHAR setup_fmt[] = {
4953         'S','e','t','u','p','=','%','s',0};
4954
4955     desc = MSI_RecordGetString(rec, 3);
4956
4957     translator_file = msi_find_file(package, MSI_RecordGetString(rec, 4));
4958     setup_file = msi_find_file(package, MSI_RecordGetString(rec, 5));
4959
4960     if (!translator_file || !setup_file)
4961     {
4962         ERR("ODBC Translator entry not found!\n");
4963         return ERROR_FUNCTION_FAILED;
4964     }
4965
4966     len = lstrlenW(desc) + lstrlenW(translator_fmt) + lstrlenW(translator_file->FileName) +
4967           lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName) + 1;
4968     translator = msi_alloc(len * sizeof(WCHAR));
4969     if (!translator)
4970         return ERROR_OUTOFMEMORY;
4971
4972     ptr = translator;
4973     lstrcpyW(ptr, desc);
4974     ptr += lstrlenW(ptr) + 1;
4975
4976     sprintfW(ptr, translator_fmt, translator_file->FileName);
4977     ptr += lstrlenW(ptr) + 1;
4978
4979     sprintfW(ptr, setup_fmt, setup_file->FileName);
4980     ptr += lstrlenW(ptr) + 1;
4981     *ptr = '\0';
4982
4983     translator_path = strdupW(translator_file->TargetPath);
4984     ptr = strrchrW(translator_path, '\\');
4985     if (ptr) *ptr = '\0';
4986
4987     if (!SQLInstallTranslatorExW(translator, translator_path, outpath, MAX_PATH,
4988                                  NULL, ODBC_INSTALL_COMPLETE, &usage))
4989     {
4990         ERR("Failed to install SQL translator!\n");
4991         r = ERROR_FUNCTION_FAILED;
4992     }
4993
4994     msi_free(translator);
4995     msi_free(translator_path);
4996
4997     return r;
4998 }
4999
5000 static UINT ITERATE_InstallODBCDataSource( MSIRECORD *rec, LPVOID param )
5001 {
5002     LPWSTR attrs;
5003     LPCWSTR desc, driver;
5004     WORD request = ODBC_ADD_SYS_DSN;
5005     INT registration;
5006     DWORD len;
5007     UINT r = ERROR_SUCCESS;
5008
5009     static const WCHAR attrs_fmt[] = {
5010         'D','S','N','=','%','s',0 };
5011
5012     desc = MSI_RecordGetString(rec, 3);
5013     driver = MSI_RecordGetString(rec, 4);
5014     registration = MSI_RecordGetInteger(rec, 5);
5015
5016     if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_ADD_SYS_DSN;
5017     else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_ADD_DSN;
5018
5019     len = lstrlenW(attrs_fmt) + lstrlenW(desc) + 1 + 1;
5020     attrs = msi_alloc(len * sizeof(WCHAR));
5021     if (!attrs)
5022         return ERROR_OUTOFMEMORY;
5023
5024     sprintfW(attrs, attrs_fmt, desc);
5025     attrs[len - 1] = '\0';
5026
5027     if (!SQLConfigDataSourceW(NULL, request, driver, attrs))
5028     {
5029         ERR("Failed to install SQL data source!\n");
5030         r = ERROR_FUNCTION_FAILED;
5031     }
5032
5033     msi_free(attrs);
5034
5035     return r;
5036 }
5037
5038 static UINT ACTION_InstallODBC( MSIPACKAGE *package )
5039 {
5040     UINT rc;
5041     MSIQUERY *view;
5042
5043     static const WCHAR driver_query[] = {
5044         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5045         'O','D','B','C','D','r','i','v','e','r',0 };
5046
5047     static const WCHAR translator_query[] = {
5048         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5049         'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0 };
5050
5051     static const WCHAR source_query[] = {
5052         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5053         'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0 };
5054
5055     rc = MSI_DatabaseOpenViewW(package->db, driver_query, &view);
5056     if (rc != ERROR_SUCCESS)
5057         return ERROR_SUCCESS;
5058
5059     rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDriver, package);
5060     msiobj_release(&view->hdr);
5061
5062     rc = MSI_DatabaseOpenViewW(package->db, translator_query, &view);
5063     if (rc != ERROR_SUCCESS)
5064         return ERROR_SUCCESS;
5065
5066     rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCTranslator, package);
5067     msiobj_release(&view->hdr);
5068
5069     rc = MSI_DatabaseOpenViewW(package->db, source_query, &view);
5070     if (rc != ERROR_SUCCESS)
5071         return ERROR_SUCCESS;
5072
5073     rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDataSource, package);
5074     msiobj_release(&view->hdr);
5075
5076     return rc;
5077 }
5078
5079 #define ENV_ACT_SETALWAYS   0x1
5080 #define ENV_ACT_SETABSENT   0x2
5081 #define ENV_ACT_REMOVE      0x4
5082 #define ENV_ACT_REMOVEMATCH 0x8
5083
5084 #define ENV_MOD_MACHINE     0x20000000
5085 #define ENV_MOD_APPEND      0x40000000
5086 #define ENV_MOD_PREFIX      0x80000000
5087 #define ENV_MOD_MASK        0xC0000000
5088
5089 #define check_flag_combo(x, y) ((x) & ~(y)) == (y)
5090
5091 static LONG env_set_flags( LPCWSTR *name, LPCWSTR *value, DWORD *flags )
5092 {
5093     LPCWSTR cptr = *name;
5094
5095     static const WCHAR prefix[] = {'[','~',']',0};
5096     static const int prefix_len = 3;
5097
5098     *flags = 0;
5099     while (*cptr)
5100     {
5101         if (*cptr == '=')
5102             *flags |= ENV_ACT_SETALWAYS;
5103         else if (*cptr == '+')
5104             *flags |= ENV_ACT_SETABSENT;
5105         else if (*cptr == '-')
5106             *flags |= ENV_ACT_REMOVE;
5107         else if (*cptr == '!')
5108             *flags |= ENV_ACT_REMOVEMATCH;
5109         else if (*cptr == '*')
5110             *flags |= ENV_MOD_MACHINE;
5111         else
5112             break;
5113
5114         cptr++;
5115         (*name)++;
5116     }
5117
5118     if (!*cptr)
5119     {
5120         ERR("Missing environment variable\n");
5121         return ERROR_FUNCTION_FAILED;
5122     }
5123
5124     if (*value)
5125     {
5126         LPCWSTR ptr = *value;
5127         if (!strncmpW(ptr, prefix, prefix_len))
5128         {
5129             if (ptr[prefix_len] == szSemiColon[0])
5130             {
5131                 *flags |= ENV_MOD_APPEND;
5132                 *value += lstrlenW(prefix);
5133             }
5134             else
5135             {
5136                 *value = NULL;
5137             }
5138         }
5139         else if (lstrlenW(*value) >= prefix_len)
5140         {
5141             ptr += lstrlenW(ptr) - prefix_len;
5142             if (!lstrcmpW(ptr, prefix))
5143             {
5144                 if ((ptr-1) > *value && *(ptr-1) == szSemiColon[0])
5145                 {
5146                     *flags |= ENV_MOD_PREFIX;
5147                     /* the "[~]" will be removed by deformat_string */;
5148                 }
5149                 else
5150                 {
5151                     *value = NULL;
5152                 }
5153             }
5154         }
5155     }
5156
5157     if (check_flag_combo(*flags, ENV_ACT_SETALWAYS | ENV_ACT_SETABSENT) ||
5158         check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETABSENT) ||
5159         check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETALWAYS) ||
5160         check_flag_combo(*flags, ENV_ACT_SETABSENT | ENV_MOD_MASK))
5161     {
5162         ERR("Invalid flags: %08x\n", *flags);
5163         return ERROR_FUNCTION_FAILED;
5164     }
5165
5166     if (!*flags)
5167         *flags = ENV_ACT_SETALWAYS | ENV_ACT_REMOVE;
5168
5169     return ERROR_SUCCESS;
5170 }
5171
5172 static UINT ITERATE_WriteEnvironmentString( MSIRECORD *rec, LPVOID param )
5173 {
5174     MSIPACKAGE *package = param;
5175     LPCWSTR name, value;
5176     LPWSTR data = NULL, newval = NULL;
5177     LPWSTR deformatted = NULL, ptr;
5178     DWORD flags, type, size;
5179     LONG res;
5180     HKEY env = NULL, root;
5181     LPCWSTR environment;
5182
5183     static const WCHAR user_env[] =
5184         {'E','n','v','i','r','o','n','m','e','n','t',0};
5185     static const WCHAR machine_env[] =
5186         {'S','y','s','t','e','m','\\',
5187          'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
5188          'C','o','n','t','r','o','l','\\',
5189          'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r','\\',
5190          'E','n','v','i','r','o','n','m','e','n','t',0};
5191
5192     name = MSI_RecordGetString(rec, 2);
5193     value = MSI_RecordGetString(rec, 3);
5194
5195     TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
5196
5197     res = env_set_flags(&name, &value, &flags);
5198     if (res != ERROR_SUCCESS || !value)
5199        goto done;
5200
5201     if (value && !deformat_string(package, value, &deformatted))
5202     {
5203         res = ERROR_OUTOFMEMORY;
5204         goto done;
5205     }
5206
5207     value = deformatted;
5208
5209     if (flags & ENV_MOD_MACHINE)
5210     {
5211         environment = machine_env;
5212         root = HKEY_LOCAL_MACHINE;
5213     }
5214     else
5215     {
5216         environment = user_env;
5217         root = HKEY_CURRENT_USER;
5218     }
5219
5220     res = RegCreateKeyExW(root, environment, 0, NULL, 0,
5221                           KEY_ALL_ACCESS, NULL, &env, NULL);
5222     if (res != ERROR_SUCCESS)
5223         goto done;
5224
5225     if (flags & ENV_ACT_REMOVE)
5226         FIXME("Not removing environment variable on uninstall!\n");
5227
5228     size = 0;
5229     type = REG_SZ;
5230     res = RegQueryValueExW(env, name, NULL, &type, NULL, &size);
5231     if ((res != ERROR_SUCCESS && res != ERROR_FILE_NOT_FOUND) ||
5232         (res == ERROR_SUCCESS && type != REG_SZ && type != REG_EXPAND_SZ))
5233         goto done;
5234
5235     if ((res == ERROR_FILE_NOT_FOUND || !(flags & ENV_MOD_MASK)))
5236     {
5237         /* Nothing to do. */
5238         if (!value)
5239         {
5240             res = ERROR_SUCCESS;
5241             goto done;
5242         }
5243
5244         /* If we are appending but the string was empty, strip ; */
5245         if ((flags & ENV_MOD_APPEND) && (value[0] == szSemiColon[0])) value++;
5246
5247         size = (lstrlenW(value) + 1) * sizeof(WCHAR);
5248         newval = strdupW(value);
5249         if (!newval)
5250         {
5251             res = ERROR_OUTOFMEMORY;
5252             goto done;
5253         }
5254     }
5255     else
5256     {
5257         /* Contrary to MSDN, +-variable to [~];path works */
5258         if (flags & ENV_ACT_SETABSENT && !(flags & ENV_MOD_MASK))
5259         {
5260             res = ERROR_SUCCESS;
5261             goto done;
5262         }
5263
5264         data = msi_alloc(size);
5265         if (!data)
5266         {
5267             RegCloseKey(env);
5268             return ERROR_OUTOFMEMORY;
5269         }
5270
5271         res = RegQueryValueExW(env, name, NULL, &type, (LPVOID)data, &size);
5272         if (res != ERROR_SUCCESS)
5273             goto done;
5274
5275         if (flags & ENV_ACT_REMOVEMATCH && (!value || !lstrcmpW(data, value)))
5276         {
5277             res = RegDeleteKeyW(env, name);
5278             goto done;
5279         }
5280
5281         size = (lstrlenW(data) + 1) * sizeof(WCHAR);
5282         if (flags & ENV_MOD_MASK)
5283         {
5284             DWORD mod_size;
5285             int multiplier = 0;
5286             if (flags & ENV_MOD_APPEND) multiplier++;
5287             if (flags & ENV_MOD_PREFIX) multiplier++;
5288             mod_size = lstrlenW(value) * multiplier;
5289             size += mod_size * sizeof(WCHAR);
5290         }
5291
5292         newval = msi_alloc(size);
5293         ptr = newval;
5294         if (!newval)
5295         {
5296             res = ERROR_OUTOFMEMORY;
5297             goto done;
5298         }
5299
5300         if (flags & ENV_MOD_PREFIX)
5301         {
5302             lstrcpyW(newval, value);
5303             ptr = newval + lstrlenW(value);
5304         }
5305
5306         lstrcpyW(ptr, data);
5307
5308         if (flags & ENV_MOD_APPEND)
5309         {
5310             lstrcatW(newval, value);
5311         }
5312     }
5313     TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(newval));
5314     res = RegSetValueExW(env, name, 0, type, (LPVOID)newval, size);
5315
5316 done:
5317     if (env) RegCloseKey(env);
5318     msi_free(deformatted);
5319     msi_free(data);
5320     msi_free(newval);
5321     return res;
5322 }
5323
5324 static UINT ACTION_WriteEnvironmentStrings( MSIPACKAGE *package )
5325 {
5326     UINT rc;
5327     MSIQUERY * view;
5328     static const WCHAR ExecSeqQuery[] =
5329         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5330          '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
5331     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
5332     if (rc != ERROR_SUCCESS)
5333         return ERROR_SUCCESS;
5334
5335     rc = MSI_IterateRecords(view, NULL, ITERATE_WriteEnvironmentString, package);
5336     msiobj_release(&view->hdr);
5337
5338     return rc;
5339 }
5340
5341 #define is_dot_dir(x) ((x[0] == '.') && ((x[1] == 0) || ((x[1] == '.') && (x[2] == 0))))
5342
5343 typedef struct
5344 {
5345     struct list entry;
5346     LPWSTR sourcename;
5347     LPWSTR destname;
5348     LPWSTR source;
5349     LPWSTR dest;
5350 } FILE_LIST;
5351
5352 static BOOL msi_move_file(LPCWSTR source, LPCWSTR dest, int options)
5353 {
5354     BOOL ret;
5355
5356     if (GetFileAttributesW(source) == FILE_ATTRIBUTE_DIRECTORY ||
5357         GetFileAttributesW(dest) == FILE_ATTRIBUTE_DIRECTORY)
5358     {
5359         WARN("Source or dest is directory, not moving\n");
5360         return FALSE;
5361     }
5362
5363     if (options == msidbMoveFileOptionsMove)
5364     {
5365         TRACE("moving %s -> %s\n", debugstr_w(source), debugstr_w(dest));
5366         ret = MoveFileExW(source, dest, MOVEFILE_REPLACE_EXISTING);
5367         if (!ret)
5368         {
5369             WARN("MoveFile failed: %d\n", GetLastError());
5370             return FALSE;
5371         }
5372     }
5373     else
5374     {
5375         TRACE("copying %s -> %s\n", debugstr_w(source), debugstr_w(dest));
5376         ret = CopyFileW(source, dest, FALSE);
5377         if (!ret)
5378         {
5379             WARN("CopyFile failed: %d\n", GetLastError());
5380             return FALSE;
5381         }
5382     }
5383
5384     return TRUE;
5385 }
5386
5387 static LPWSTR wildcard_to_file(LPWSTR wildcard, LPWSTR filename)
5388 {
5389     LPWSTR path, ptr;
5390     DWORD dirlen, pathlen;
5391
5392     ptr = strrchrW(wildcard, '\\');
5393     dirlen = ptr - wildcard + 1;
5394
5395     pathlen = dirlen + lstrlenW(filename) + 1;
5396     path = msi_alloc(pathlen * sizeof(WCHAR));
5397
5398     lstrcpynW(path, wildcard, dirlen + 1);
5399     lstrcatW(path, filename);
5400
5401     return path;
5402 }
5403
5404 static void free_file_entry(FILE_LIST *file)
5405 {
5406     msi_free(file->source);
5407     msi_free(file->dest);
5408     msi_free(file);
5409 }
5410
5411 static void free_list(FILE_LIST *list)
5412 {
5413     while (!list_empty(&list->entry))
5414     {
5415         FILE_LIST *file = LIST_ENTRY(list_head(&list->entry), FILE_LIST, entry);
5416
5417         list_remove(&file->entry);
5418         free_file_entry(file);
5419     }
5420 }
5421
5422 static BOOL add_wildcard(FILE_LIST *files, LPWSTR source, LPWSTR dest)
5423 {
5424     FILE_LIST *new, *file;
5425     LPWSTR ptr, filename;
5426     DWORD size;
5427
5428     new = msi_alloc_zero(sizeof(FILE_LIST));
5429     if (!new)
5430         return FALSE;
5431
5432     new->source = strdupW(source);
5433     ptr = strrchrW(dest, '\\') + 1;
5434     filename = strrchrW(new->source, '\\') + 1;
5435
5436     new->sourcename = filename;
5437
5438     if (*ptr)
5439         new->destname = ptr;
5440     else
5441         new->destname = new->sourcename;
5442
5443     size = (ptr - dest) + lstrlenW(filename) + 1;
5444     new->dest = msi_alloc(size * sizeof(WCHAR));
5445     if (!new->dest)
5446     {
5447         free_file_entry(new);
5448         return FALSE;
5449     }
5450
5451     lstrcpynW(new->dest, dest, ptr - dest + 1);
5452     lstrcatW(new->dest, filename);
5453
5454     if (list_empty(&files->entry))
5455     {
5456         list_add_head(&files->entry, &new->entry);
5457         return TRUE;
5458     }
5459
5460     LIST_FOR_EACH_ENTRY(file, &files->entry, FILE_LIST, entry)
5461     {
5462         if (lstrcmpW(source, file->source) < 0)
5463         {
5464             list_add_before(&file->entry, &new->entry);
5465             return TRUE;
5466         }
5467     }
5468
5469     list_add_after(&file->entry, &new->entry);
5470     return TRUE;
5471 }
5472
5473 static BOOL move_files_wildcard(LPWSTR source, LPWSTR dest, int options)
5474 {
5475     WIN32_FIND_DATAW wfd;
5476     HANDLE hfile;
5477     LPWSTR path;
5478     BOOL res;
5479     FILE_LIST files, *file;
5480     DWORD size;
5481
5482     hfile = FindFirstFileW(source, &wfd);
5483     if (hfile == INVALID_HANDLE_VALUE) return FALSE;
5484
5485     list_init(&files.entry);
5486
5487     for (res = TRUE; res; res = FindNextFileW(hfile, &wfd))
5488     {
5489         if (is_dot_dir(wfd.cFileName)) continue;
5490
5491         path = wildcard_to_file(source, wfd.cFileName);
5492         if (!path)
5493         {
5494             res = FALSE;
5495             goto done;
5496         }
5497
5498         add_wildcard(&files, path, dest);
5499         msi_free(path);
5500     }
5501
5502     /* no files match the wildcard */
5503     if (list_empty(&files.entry))
5504         goto done;
5505
5506     /* only the first wildcard match gets renamed to dest */
5507     file = LIST_ENTRY(list_head(&files.entry), FILE_LIST, entry);
5508     size = (strrchrW(file->dest, '\\') - file->dest) + lstrlenW(file->destname) + 2;
5509     file->dest = msi_realloc(file->dest, size * sizeof(WCHAR));
5510     if (!file->dest)
5511     {
5512         res = FALSE;
5513         goto done;
5514     }
5515
5516     /* file->dest may be shorter after the reallocation, so add a NULL
5517      * terminator.  This is needed for the call to strrchrW, as there will no
5518      * longer be a NULL terminator within the bounds of the allocation in this case.
5519      */
5520     file->dest[size - 1] = '\0';
5521     lstrcpyW(strrchrW(file->dest, '\\') + 1, file->destname);
5522
5523     while (!list_empty(&files.entry))
5524     {
5525         file = LIST_ENTRY(list_head(&files.entry), FILE_LIST, entry);
5526
5527         msi_move_file(file->source, file->dest, options);
5528
5529         list_remove(&file->entry);
5530         free_file_entry(file);
5531     }
5532
5533     res = TRUE;
5534
5535 done:
5536     free_list(&files);
5537     FindClose(hfile);
5538     return res;
5539 }
5540
5541 static UINT ITERATE_MoveFiles( MSIRECORD *rec, LPVOID param )
5542 {
5543     MSIPACKAGE *package = param;
5544     MSICOMPONENT *comp;
5545     LPCWSTR sourcename;
5546     LPWSTR destname = NULL;
5547     LPWSTR sourcedir = NULL, destdir = NULL;
5548     LPWSTR source = NULL, dest = NULL;
5549     int options;
5550     DWORD size;
5551     BOOL ret, wildcards;
5552
5553     comp = get_loaded_component(package, MSI_RecordGetString(rec, 2));
5554     if (!comp || !comp->Enabled ||
5555         !(comp->Action & (INSTALLSTATE_LOCAL | INSTALLSTATE_SOURCE)))
5556     {
5557         TRACE("Component not set for install, not moving file\n");
5558         return ERROR_SUCCESS;
5559     }
5560
5561     sourcename = MSI_RecordGetString(rec, 3);
5562     options = MSI_RecordGetInteger(rec, 7);
5563
5564     sourcedir = msi_dup_property(package, MSI_RecordGetString(rec, 5));
5565     if (!sourcedir)
5566         goto done;
5567
5568     destdir = msi_dup_property(package, MSI_RecordGetString(rec, 6));
5569     if (!destdir)
5570         goto done;
5571
5572     if (!sourcename)
5573     {
5574         if (GetFileAttributesW(sourcedir) == INVALID_FILE_ATTRIBUTES)
5575             goto done;
5576
5577         source = strdupW(sourcedir);
5578         if (!source)
5579             goto done;
5580     }
5581     else
5582     {
5583         size = lstrlenW(sourcedir) + lstrlenW(sourcename) + 2;
5584         source = msi_alloc(size * sizeof(WCHAR));
5585         if (!source)
5586             goto done;
5587
5588         lstrcpyW(source, sourcedir);
5589         if (source[lstrlenW(source) - 1] != '\\')
5590             lstrcatW(source, szBackSlash);
5591         lstrcatW(source, sourcename);
5592     }
5593
5594     wildcards = strchrW(source, '*') || strchrW(source, '?');
5595
5596     if (MSI_RecordIsNull(rec, 4))
5597     {
5598         if (!wildcards)
5599         {
5600             destname = strdupW(sourcename);
5601             if (!destname)
5602                 goto done;
5603         }
5604     }
5605     else
5606     {
5607         destname = strdupW(MSI_RecordGetString(rec, 4));
5608         if (destname)
5609             reduce_to_longfilename(destname);
5610     }
5611
5612     size = 0;
5613     if (destname)
5614         size = lstrlenW(destname);
5615
5616     size += lstrlenW(destdir) + 2;
5617     dest = msi_alloc(size * sizeof(WCHAR));
5618     if (!dest)
5619         goto done;
5620
5621     lstrcpyW(dest, destdir);
5622     if (dest[lstrlenW(dest) - 1] != '\\')
5623         lstrcatW(dest, szBackSlash);
5624
5625     if (destname)
5626         lstrcatW(dest, destname);
5627
5628     if (GetFileAttributesW(destdir) == INVALID_FILE_ATTRIBUTES)
5629     {
5630         ret = CreateDirectoryW(destdir, NULL);
5631         if (!ret)
5632         {
5633             WARN("CreateDirectory failed: %d\n", GetLastError());
5634             return ERROR_SUCCESS;
5635         }
5636     }
5637
5638     if (!wildcards)
5639         msi_move_file(source, dest, options);
5640     else
5641         move_files_wildcard(source, dest, options);
5642
5643 done:
5644     msi_free(sourcedir);
5645     msi_free(destdir);
5646     msi_free(destname);
5647     msi_free(source);
5648     msi_free(dest);
5649
5650     return ERROR_SUCCESS;
5651 }
5652
5653 static UINT ACTION_MoveFiles( MSIPACKAGE *package )
5654 {
5655     UINT rc;
5656     MSIQUERY *view;
5657
5658     static const WCHAR ExecSeqQuery[] =
5659         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5660          '`','M','o','v','e','F','i','l','e','`',0};
5661
5662     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
5663     if (rc != ERROR_SUCCESS)
5664         return ERROR_SUCCESS;
5665
5666     rc = MSI_IterateRecords(view, NULL, ITERATE_MoveFiles, package);
5667     msiobj_release(&view->hdr);
5668
5669     return rc;
5670 }
5671
5672 typedef struct tagMSIASSEMBLY
5673 {
5674     struct list entry;
5675     MSICOMPONENT *component;
5676     MSIFEATURE *feature;
5677     MSIFILE *file;
5678     LPWSTR manifest;
5679     LPWSTR application;
5680     DWORD attributes;
5681     BOOL installed;
5682 } MSIASSEMBLY;
5683
5684 static HRESULT (WINAPI *pCreateAssemblyCache)(IAssemblyCache **ppAsmCache,
5685                                               DWORD dwReserved);
5686 static HRESULT (WINAPI *pLoadLibraryShim)(LPCWSTR szDllName, LPCWSTR szVersion,
5687                                           LPVOID pvReserved, HMODULE *phModDll);
5688
5689 static BOOL init_functionpointers(void)
5690 {
5691     HRESULT hr;
5692     HMODULE hfusion;
5693     HMODULE hmscoree;
5694
5695     static const WCHAR szFusion[] = {'f','u','s','i','o','n','.','d','l','l',0};
5696
5697     hmscoree = LoadLibraryA("mscoree.dll");
5698     if (!hmscoree)
5699     {
5700         WARN("mscoree.dll not available\n");
5701         return FALSE;
5702     }
5703
5704     pLoadLibraryShim = (void *)GetProcAddress(hmscoree, "LoadLibraryShim");
5705     if (!pLoadLibraryShim)
5706     {
5707         WARN("LoadLibraryShim not available\n");
5708         FreeLibrary(hmscoree);
5709         return FALSE;
5710     }
5711
5712     hr = pLoadLibraryShim(szFusion, NULL, NULL, &hfusion);
5713     if (FAILED(hr))
5714     {
5715         WARN("fusion.dll not available\n");
5716         FreeLibrary(hmscoree);
5717         return FALSE;
5718     }
5719
5720     pCreateAssemblyCache = (void *)GetProcAddress(hfusion, "CreateAssemblyCache");
5721
5722     FreeLibrary(hmscoree);
5723     return TRUE;
5724 }
5725
5726 static UINT install_assembly(MSIPACKAGE *package, MSIASSEMBLY *assembly,
5727                              LPWSTR path)
5728 {
5729     IAssemblyCache *cache;
5730     HRESULT hr;
5731     UINT r = ERROR_FUNCTION_FAILED;
5732
5733     TRACE("installing assembly: %s\n", debugstr_w(path));
5734
5735     if (assembly->feature)
5736         msi_feature_set_state(package, assembly->feature, INSTALLSTATE_LOCAL);
5737
5738     if (assembly->manifest)
5739         FIXME("Manifest unhandled\n");
5740
5741     if (assembly->application)
5742     {
5743         FIXME("Assembly should be privately installed\n");
5744         return ERROR_SUCCESS;
5745     }
5746
5747     if (assembly->attributes == msidbAssemblyAttributesWin32)
5748     {
5749         FIXME("Win32 assemblies not handled\n");
5750         return ERROR_SUCCESS;
5751     }
5752
5753     hr = pCreateAssemblyCache(&cache, 0);
5754     if (FAILED(hr))
5755         goto done;
5756
5757     hr = IAssemblyCache_InstallAssembly(cache, 0, path, NULL);
5758     if (FAILED(hr))
5759         ERR("Failed to install assembly: %s %08x\n", debugstr_w(path), hr);
5760
5761     r = ERROR_SUCCESS;
5762
5763 done:
5764     IAssemblyCache_Release(cache);
5765     return r;
5766 }
5767
5768 typedef struct tagASSEMBLY_LIST
5769 {
5770     MSIPACKAGE *package;
5771     IAssemblyCache *cache;
5772     struct list *assemblies;
5773 } ASSEMBLY_LIST;
5774
5775 typedef struct tagASSEMBLY_NAME
5776 {
5777     LPWSTR name;
5778     LPWSTR version;
5779     LPWSTR culture;
5780     LPWSTR pubkeytoken;
5781 } ASSEMBLY_NAME;
5782
5783 static UINT parse_assembly_name(MSIRECORD *rec, LPVOID param)
5784 {
5785     ASSEMBLY_NAME *asmname = param;
5786     LPCWSTR name = MSI_RecordGetString(rec, 2);
5787     LPWSTR val = msi_dup_record_field(rec, 3);
5788
5789     static const WCHAR Name[] = {'N','a','m','e',0};
5790     static const WCHAR Version[] = {'V','e','r','s','i','o','n',0};
5791     static const WCHAR Culture[] = {'C','u','l','t','u','r','e',0};
5792     static const WCHAR PublicKeyToken[] = {
5793         'P','u','b','l','i','c','K','e','y','T','o','k','e','n',0};
5794
5795     if (!strcmpiW(name, Name))
5796         asmname->name = val;
5797     else if (!strcmpiW(name, Version))
5798         asmname->version = val;
5799     else if (!strcmpiW(name, Culture))
5800         asmname->culture = val;
5801     else if (!strcmpiW(name, PublicKeyToken))
5802         asmname->pubkeytoken = val;
5803     else
5804         msi_free(val);
5805
5806     return ERROR_SUCCESS;
5807 }
5808
5809 static void append_str(LPWSTR *str, DWORD *size, LPCWSTR append)
5810 {
5811     if (!*str)
5812     {
5813         *size = lstrlenW(append) + 1;
5814         *str = msi_alloc((*size) * sizeof(WCHAR));
5815         lstrcpyW(*str, append);
5816         return;
5817     }
5818
5819     (*size) += lstrlenW(append);
5820     *str = msi_realloc(*str, (*size) * sizeof(WCHAR));
5821     lstrcatW(*str, append);
5822 }
5823
5824 static BOOL check_assembly_installed(MSIDATABASE *db, IAssemblyCache *cache,
5825                                      MSICOMPONENT *comp)
5826 {
5827     ASSEMBLY_INFO asminfo;
5828     ASSEMBLY_NAME name;
5829     MSIQUERY *view;
5830     LPWSTR disp;
5831     DWORD size;
5832     BOOL found;
5833     UINT r;
5834
5835     static const WCHAR separator[] = {',',' ',0};
5836     static const WCHAR Version[] = {'V','e','r','s','i','o','n','=',0};
5837     static const WCHAR Culture[] = {'C','u','l','t','u','r','e','=',0};
5838     static const WCHAR PublicKeyToken[] = {
5839         'P','u','b','l','i','c','K','e','y','T','o','k','e','n','=',0};
5840     static const WCHAR query[] = {
5841         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5842         '`','M','s','i','A','s','s','e','m','b','l','y','N','a','m','e','`',' ',
5843         'W','H','E','R','E',' ','`','C','o','m','p','o','n','e','n','t','_','`',
5844         '=','\'','%','s','\'',0};
5845
5846     disp = NULL;
5847     found = FALSE;
5848     ZeroMemory(&name, sizeof(ASSEMBLY_NAME));
5849     ZeroMemory(&asminfo, sizeof(ASSEMBLY_INFO));
5850
5851     r = MSI_OpenQuery(db, &view, query, comp->Component);
5852     if (r != ERROR_SUCCESS)
5853         return ERROR_SUCCESS;
5854
5855     MSI_IterateRecords(view, NULL, parse_assembly_name, &name);
5856     msiobj_release(&view->hdr);
5857
5858     if (!name.name)
5859     {
5860         ERR("No assembly name specified!\n");
5861         goto done;
5862     }
5863
5864     append_str(&disp, &size, name.name);
5865
5866     if (name.version)
5867     {
5868         append_str(&disp, &size, separator);
5869         append_str(&disp, &size, Version);
5870         append_str(&disp, &size, name.version);
5871     }
5872
5873     if (name.culture)
5874     {
5875         append_str(&disp, &size, separator);
5876         append_str(&disp, &size, Culture);
5877         append_str(&disp, &size, name.culture);
5878     }
5879
5880     if (name.pubkeytoken)
5881     {
5882         append_str(&disp, &size, separator);
5883         append_str(&disp, &size, PublicKeyToken);
5884         append_str(&disp, &size, name.pubkeytoken);
5885     }
5886
5887     asminfo.cbAssemblyInfo = sizeof(ASSEMBLY_INFO);
5888     IAssemblyCache_QueryAssemblyInfo(cache, QUERYASMINFO_FLAG_VALIDATE,
5889                                      disp, &asminfo);
5890     found = (asminfo.dwAssemblyFlags == ASSEMBLYINFO_FLAG_INSTALLED);
5891
5892 done:
5893     msi_free(disp);
5894     msi_free(name.name);
5895     msi_free(name.version);
5896     msi_free(name.culture);
5897     msi_free(name.pubkeytoken);
5898
5899     return found;
5900 }
5901
5902 static UINT load_assembly(MSIRECORD *rec, LPVOID param)
5903 {
5904     ASSEMBLY_LIST *list = param;
5905     MSIASSEMBLY *assembly;
5906
5907     assembly = msi_alloc_zero(sizeof(MSIASSEMBLY));
5908     if (!assembly)
5909         return ERROR_OUTOFMEMORY;
5910
5911     assembly->component = get_loaded_component(list->package, MSI_RecordGetString(rec, 1));
5912
5913     if (!assembly->component || !assembly->component->Enabled ||
5914         !(assembly->component->Action & (INSTALLSTATE_LOCAL | INSTALLSTATE_SOURCE)))
5915     {
5916         TRACE("Component not set for install, not publishing assembly\n");
5917         msi_free(assembly);
5918         return ERROR_SUCCESS;
5919     }
5920
5921     assembly->feature = find_feature_by_name(list->package, MSI_RecordGetString(rec, 2));
5922     assembly->file = msi_find_file(list->package, assembly->component->KeyPath);
5923
5924     if (!assembly->file)
5925     {
5926         ERR("File %s not found\n", debugstr_w(assembly->component->KeyPath));
5927         return ERROR_FUNCTION_FAILED;
5928     }
5929
5930     assembly->manifest = strdupW(MSI_RecordGetString(rec, 3));
5931     assembly->application = strdupW(MSI_RecordGetString(rec, 4));
5932     assembly->attributes = MSI_RecordGetInteger(rec, 5);
5933
5934     if (assembly->application)
5935     {
5936         WCHAR version[24];
5937         DWORD size = sizeof(version)/sizeof(WCHAR);
5938
5939         /* FIXME: we should probably check the manifest file here */
5940
5941         if (!MsiGetFileVersionW(assembly->file->TargetPath, version, &size, NULL, NULL) &&
5942             (!assembly->file->Version || strcmpW(version, assembly->file->Version) >= 0))
5943         {
5944             assembly->installed = TRUE;
5945         }
5946     }
5947     else
5948         assembly->installed = check_assembly_installed(list->package->db,
5949                                                        list->cache,
5950                                                        assembly->component);
5951
5952     list_add_head(list->assemblies, &assembly->entry);
5953     return ERROR_SUCCESS;
5954 }
5955
5956 static UINT load_assemblies(MSIPACKAGE *package, struct list *assemblies)
5957 {
5958     IAssemblyCache *cache = NULL;
5959     ASSEMBLY_LIST list;
5960     MSIQUERY *view;
5961     HRESULT hr;
5962     UINT r;
5963
5964     static const WCHAR query[] =
5965         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5966          '`','M','s','i','A','s','s','e','m','b','l','y','`',0};
5967
5968     r = MSI_DatabaseOpenViewW(package->db, query, &view);
5969     if (r != ERROR_SUCCESS)
5970         return ERROR_SUCCESS;
5971
5972     hr = pCreateAssemblyCache(&cache, 0);
5973     if (FAILED(hr))
5974         return ERROR_FUNCTION_FAILED;
5975
5976     list.package = package;
5977     list.cache = cache;
5978     list.assemblies = assemblies;
5979
5980     r = MSI_IterateRecords(view, NULL, load_assembly, &list);
5981     msiobj_release(&view->hdr);
5982
5983     IAssemblyCache_Release(cache);
5984
5985     return r;
5986 }
5987
5988 static void free_assemblies(struct list *assemblies)
5989 {
5990     struct list *item, *cursor;
5991
5992     LIST_FOR_EACH_SAFE(item, cursor, assemblies)
5993     {
5994         MSIASSEMBLY *assembly = LIST_ENTRY(item, MSIASSEMBLY, entry);
5995
5996         list_remove(&assembly->entry);
5997         msi_free(assembly->application);
5998         msi_free(assembly->manifest);
5999         msi_free(assembly);
6000     }
6001 }
6002
6003 static BOOL find_assembly(struct list *assemblies, LPCWSTR file, MSIASSEMBLY **out)
6004 {
6005     MSIASSEMBLY *assembly;
6006
6007     LIST_FOR_EACH_ENTRY(assembly, assemblies, MSIASSEMBLY, entry)
6008     {
6009         if (!lstrcmpW(assembly->file->File, file))
6010         {
6011             *out = assembly;
6012             return TRUE;
6013         }
6014     }
6015
6016     return FALSE;
6017 }
6018
6019 static BOOL installassembly_cb(MSIPACKAGE *package, LPCWSTR file, DWORD action,
6020                                LPWSTR *path, DWORD *attrs, PVOID user)
6021 {
6022     MSIASSEMBLY *assembly;
6023     WCHAR temppath[MAX_PATH];
6024     struct list *assemblies = user;
6025     UINT r;
6026
6027     if (!find_assembly(assemblies, file, &assembly))
6028         return FALSE;
6029
6030     GetTempPathW(MAX_PATH, temppath);
6031     PathAddBackslashW(temppath);
6032     lstrcatW(temppath, assembly->file->FileName);
6033
6034     if (action == MSICABEXTRACT_BEGINEXTRACT)
6035     {
6036         if (assembly->installed)
6037             return FALSE;
6038
6039         *path = strdupW(temppath);
6040         *attrs = assembly->file->Attributes;
6041     }
6042     else if (action == MSICABEXTRACT_FILEEXTRACTED)
6043     {
6044         assembly->installed = TRUE;
6045
6046         r = install_assembly(package, assembly, temppath);
6047         if (r != ERROR_SUCCESS)
6048             ERR("Failed to install assembly\n");
6049     }
6050
6051     return TRUE;
6052 }
6053
6054 static UINT ACTION_MsiPublishAssemblies( MSIPACKAGE *package )
6055 {
6056     UINT r;
6057     struct list assemblies = LIST_INIT(assemblies);
6058     MSIASSEMBLY *assembly;
6059     MSIMEDIAINFO *mi;
6060
6061     if (!init_functionpointers() || !pCreateAssemblyCache)
6062         return ERROR_FUNCTION_FAILED;
6063
6064     r = load_assemblies(package, &assemblies);
6065     if (r != ERROR_SUCCESS)
6066         goto done;
6067
6068     if (list_empty(&assemblies))
6069         goto done;
6070
6071     mi = msi_alloc_zero(sizeof(MSIMEDIAINFO));
6072     if (!mi)
6073     {
6074         r = ERROR_OUTOFMEMORY;
6075         goto done;
6076     }
6077
6078     LIST_FOR_EACH_ENTRY(assembly, &assemblies, MSIASSEMBLY, entry)
6079     {
6080         if (assembly->installed && !mi->is_continuous)
6081             continue;
6082
6083         if (assembly->file->Sequence > mi->last_sequence || mi->is_continuous ||
6084             (assembly->file->IsCompressed && !mi->is_extracted))
6085         {
6086             MSICABDATA data;
6087
6088             r = ready_media(package, assembly->file, mi);
6089             if (r != ERROR_SUCCESS)
6090             {
6091                 ERR("Failed to ready media\n");
6092                 break;
6093             }
6094
6095             data.mi = mi;
6096             data.package = package;
6097             data.cb = installassembly_cb;
6098             data.user = &assemblies;
6099
6100             if (assembly->file->IsCompressed &&
6101                 !msi_cabextract(package, mi, &data))
6102             {
6103                 ERR("Failed to extract cabinet: %s\n", debugstr_w(mi->cabinet));
6104                 r = ERROR_FUNCTION_FAILED;
6105                 break;
6106             }
6107         }
6108
6109         if (!assembly->file->IsCompressed)
6110         {
6111             LPWSTR source = resolve_file_source(package, assembly->file);
6112
6113             r = install_assembly(package, assembly, source);
6114             if (r != ERROR_SUCCESS)
6115                 ERR("Failed to install assembly\n");
6116
6117             msi_free(source);
6118         }
6119
6120         /* FIXME: write Installer assembly reg values */
6121     }
6122
6123 done:
6124     free_assemblies(&assemblies);
6125     return r;
6126 }
6127
6128 static UINT ACTION_ValidateProductID( MSIPACKAGE *package )
6129 {
6130     LPWSTR key, template, id;
6131     UINT r = ERROR_SUCCESS;
6132
6133     id = msi_dup_property( package, szProductID );
6134     if (id)
6135     {
6136         msi_free( id );
6137         return ERROR_SUCCESS;
6138     }
6139     template = msi_dup_property( package, szPIDTemplate );
6140     key = msi_dup_property( package, szPIDKEY );
6141
6142     if (key && template)
6143     {
6144         FIXME( "partial stub: template %s key %s\n", debugstr_w(template), debugstr_w(key) );
6145         r = MSI_SetPropertyW( package, szProductID, key );
6146     }
6147     msi_free( template );
6148     msi_free( key );
6149     return r;
6150 }
6151
6152 static UINT ACTION_ScheduleReboot( MSIPACKAGE *package )
6153 {
6154     TRACE("\n");
6155     package->need_reboot = 1;
6156     return ERROR_SUCCESS;
6157 }
6158
6159 static UINT ACTION_AllocateRegistrySpace( MSIPACKAGE *package )
6160 {
6161     TRACE("%p\n", package);
6162     return ERROR_SUCCESS;
6163 }
6164
6165 static UINT ACTION_DisableRollback( MSIPACKAGE *package )
6166 {
6167     FIXME("%p\n", package);
6168     return ERROR_SUCCESS;
6169 }
6170
6171 static UINT ACTION_InstallAdminPackage( MSIPACKAGE *package )
6172 {
6173     FIXME("%p\n", package);
6174     return ERROR_SUCCESS;
6175 }
6176
6177 static UINT msi_unimplemented_action_stub( MSIPACKAGE *package,
6178                                            LPCSTR action, LPCWSTR table )
6179 {
6180     static const WCHAR query[] = {
6181         'S','E','L','E','C','T',' ','*',' ',
6182         'F','R','O','M',' ','`','%','s','`',0 };
6183     MSIQUERY *view = NULL;
6184     DWORD count = 0;
6185     UINT r;
6186     
6187     r = MSI_OpenQuery( package->db, &view, query, table );
6188     if (r == ERROR_SUCCESS)
6189     {
6190         r = MSI_IterateRecords(view, &count, NULL, package);
6191         msiobj_release(&view->hdr);
6192     }
6193
6194     if (count)
6195         FIXME("%s -> %u ignored %s table values\n",
6196               action, count, debugstr_w(table));
6197
6198     return ERROR_SUCCESS;
6199 }
6200
6201 static UINT ACTION_RemoveIniValues( MSIPACKAGE *package )
6202 {
6203     static const WCHAR table[] =
6204          {'R','e','m','o','v','e','I','n','i','F','i','l','e',0 };
6205     return msi_unimplemented_action_stub( package, "RemoveIniValues", table );
6206 }
6207
6208 static UINT ACTION_PatchFiles( MSIPACKAGE *package )
6209 {
6210     static const WCHAR table[] = { 'P','a','t','c','h',0 };
6211     return msi_unimplemented_action_stub( package, "PatchFiles", table );
6212 }
6213
6214 static UINT ACTION_BindImage( MSIPACKAGE *package )
6215 {
6216     static const WCHAR table[] = { 'B','i','n','d','I','m','a','g','e',0 };
6217     return msi_unimplemented_action_stub( package, "BindImage", table );
6218 }
6219
6220 static UINT ACTION_IsolateComponents( MSIPACKAGE *package )
6221 {
6222     static const WCHAR table[] = {
6223         'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t',0 };
6224     return msi_unimplemented_action_stub( package, "IsolateComponents", table );
6225 }
6226
6227 static UINT ACTION_MigrateFeatureStates( MSIPACKAGE *package )
6228 {
6229     static const WCHAR table[] = { 'U','p','g','r','a','d','e',0 };
6230     return msi_unimplemented_action_stub( package, "MigrateFeatureStates", table );
6231 }
6232
6233 static UINT ACTION_RemoveEnvironmentStrings( MSIPACKAGE *package )
6234 {
6235     static const WCHAR table[] = {
6236         'E','n','v','i','r','o','n','m','e','n','t',0 };
6237     return msi_unimplemented_action_stub( package, "RemoveEnvironmentStrings", table );
6238 }
6239
6240 static UINT ACTION_MsiUnpublishAssemblies( MSIPACKAGE *package )
6241 {
6242     static const WCHAR table[] = {
6243         'M','s','i','A','s','s','e','m','b','l','y',0 };
6244     return msi_unimplemented_action_stub( package, "MsiUnpublishAssemblies", table );
6245 }
6246
6247 static UINT ACTION_RMCCPSearch( MSIPACKAGE *package )
6248 {
6249     static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
6250     return msi_unimplemented_action_stub( package, "RMCCPSearch", table );
6251 }
6252
6253 static UINT ACTION_RegisterComPlus( MSIPACKAGE *package )
6254 {
6255     static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
6256     return msi_unimplemented_action_stub( package, "RegisterComPlus", table );
6257 }
6258
6259 static UINT ACTION_UnregisterComPlus( MSIPACKAGE *package )
6260 {
6261     static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
6262     return msi_unimplemented_action_stub( package, "UnregisterComPlus", table );
6263 }
6264
6265 static UINT ACTION_InstallSFPCatalogFile( MSIPACKAGE *package )
6266 {
6267     static const WCHAR table[] = { 'S','F','P','C','a','t','a','l','o','g',0 };
6268     return msi_unimplemented_action_stub( package, "InstallSFPCatalogFile", table );
6269 }
6270
6271 static UINT ACTION_RemoveDuplicateFiles( MSIPACKAGE *package )
6272 {
6273     static const WCHAR table[] = { 'D','u','p','l','i','c','a','t','e','F','i','l','e',0 };
6274     return msi_unimplemented_action_stub( package, "RemoveDuplicateFiles", table );
6275 }
6276
6277 static UINT ACTION_RemoveExistingProducts( MSIPACKAGE *package )
6278 {
6279     static const WCHAR table[] = { 'U','p','g','r','a','d','e',0 };
6280     return msi_unimplemented_action_stub( package, "RemoveExistingProducts", table );
6281 }
6282
6283 static UINT ACTION_RemoveODBC( MSIPACKAGE *package )
6284 {
6285     static const WCHAR table[] = { 'O','D','B','C','D','r','i','v','e','r',0 };
6286     return msi_unimplemented_action_stub( package, "RemoveODBC", table );
6287 }
6288
6289 static UINT ACTION_RemoveRegistryValues( MSIPACKAGE *package )
6290 {
6291     static const WCHAR table[] = { 'R','e','m','o','v','e','R','e','g','i','s','t','r','y',0 };
6292     return msi_unimplemented_action_stub( package, "RemoveRegistryValues", table );
6293 }
6294
6295 static UINT ACTION_RemoveShortcuts( MSIPACKAGE *package )
6296 {
6297     static const WCHAR table[] = { 'S','h','o','r','t','c','u','t',0 };
6298     return msi_unimplemented_action_stub( package, "RemoveShortcuts", table );
6299 }
6300
6301 static UINT ACTION_SetODBCFolders( MSIPACKAGE *package )
6302 {
6303     static const WCHAR table[] = { 'D','i','r','e','c','t','o','r','y',0 };
6304     return msi_unimplemented_action_stub( package, "SetODBCFolders", table );
6305 }
6306
6307 static UINT ACTION_UnpublishComponents( MSIPACKAGE *package )
6308 {
6309     static const WCHAR table[] = { 'P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t',0 };
6310     return msi_unimplemented_action_stub( package, "UnpublishComponents", table );
6311 }
6312
6313 static UINT ACTION_UnregisterClassInfo( MSIPACKAGE *package )
6314 {
6315     static const WCHAR table[] = { 'A','p','p','I','d',0 };
6316     return msi_unimplemented_action_stub( package, "UnregisterClassInfo", table );
6317 }
6318
6319 static UINT ACTION_UnregisterExtensionInfo( MSIPACKAGE *package )
6320 {
6321     static const WCHAR table[] = { 'E','x','t','e','n','s','i','o','n',0 };
6322     return msi_unimplemented_action_stub( package, "UnregisterExtensionInfo", table );
6323 }
6324
6325 static UINT ACTION_UnregisterMIMEInfo( MSIPACKAGE *package )
6326 {
6327     static const WCHAR table[] = { 'M','I','M','E',0 };
6328     return msi_unimplemented_action_stub( package, "UnregisterMIMEInfo", table );
6329 }
6330
6331 static UINT ACTION_UnregisterProgIdInfo( MSIPACKAGE *package )
6332 {
6333     static const WCHAR table[] = { 'P','r','o','g','I','d',0 };
6334     return msi_unimplemented_action_stub( package, "UnregisterProgIdInfo", table );
6335 }
6336
6337 static UINT ACTION_UnregisterTypeLibraries( MSIPACKAGE *package )
6338 {
6339     static const WCHAR table[] = { 'T','y','p','e','L','i','b',0 };
6340     return msi_unimplemented_action_stub( package, "UnregisterTypeLibraries", table );
6341 }
6342
6343 typedef UINT (*STANDARDACTIONHANDLER)(MSIPACKAGE*);
6344
6345 static const struct
6346 {
6347     const WCHAR *action;
6348     UINT (*handler)(MSIPACKAGE *);
6349 }
6350 StandardActions[] =
6351 {
6352     { szAllocateRegistrySpace, ACTION_AllocateRegistrySpace },
6353     { szAppSearch, ACTION_AppSearch },
6354     { szBindImage, ACTION_BindImage },
6355     { szCCPSearch, ACTION_CCPSearch },
6356     { szCostFinalize, ACTION_CostFinalize },
6357     { szCostInitialize, ACTION_CostInitialize },
6358     { szCreateFolders, ACTION_CreateFolders },
6359     { szCreateShortcuts, ACTION_CreateShortcuts },
6360     { szDeleteServices, ACTION_DeleteServices },
6361     { szDisableRollback, ACTION_DisableRollback },
6362     { szDuplicateFiles, ACTION_DuplicateFiles },
6363     { szExecuteAction, ACTION_ExecuteAction },
6364     { szFileCost, ACTION_FileCost },
6365     { szFindRelatedProducts, ACTION_FindRelatedProducts },
6366     { szForceReboot, ACTION_ForceReboot },
6367     { szInstallAdminPackage, ACTION_InstallAdminPackage },
6368     { szInstallExecute, ACTION_InstallExecute },
6369     { szInstallExecuteAgain, ACTION_InstallExecute },
6370     { szInstallFiles, ACTION_InstallFiles},
6371     { szInstallFinalize, ACTION_InstallFinalize },
6372     { szInstallInitialize, ACTION_InstallInitialize },
6373     { szInstallSFPCatalogFile, ACTION_InstallSFPCatalogFile },
6374     { szInstallValidate, ACTION_InstallValidate },
6375     { szIsolateComponents, ACTION_IsolateComponents },
6376     { szLaunchConditions, ACTION_LaunchConditions },
6377     { szMigrateFeatureStates, ACTION_MigrateFeatureStates },
6378     { szMoveFiles, ACTION_MoveFiles },
6379     { szMsiPublishAssemblies, ACTION_MsiPublishAssemblies },
6380     { szMsiUnpublishAssemblies, ACTION_MsiUnpublishAssemblies },
6381     { szInstallODBC, ACTION_InstallODBC },
6382     { szInstallServices, ACTION_InstallServices },
6383     { szPatchFiles, ACTION_PatchFiles },
6384     { szProcessComponents, ACTION_ProcessComponents },
6385     { szPublishComponents, ACTION_PublishComponents },
6386     { szPublishFeatures, ACTION_PublishFeatures },
6387     { szPublishProduct, ACTION_PublishProduct },
6388     { szRegisterClassInfo, ACTION_RegisterClassInfo },
6389     { szRegisterComPlus, ACTION_RegisterComPlus},
6390     { szRegisterExtensionInfo, ACTION_RegisterExtensionInfo },
6391     { szRegisterFonts, ACTION_RegisterFonts },
6392     { szRegisterMIMEInfo, ACTION_RegisterMIMEInfo },
6393     { szRegisterProduct, ACTION_RegisterProduct },
6394     { szRegisterProgIdInfo, ACTION_RegisterProgIdInfo },
6395     { szRegisterTypeLibraries, ACTION_RegisterTypeLibraries },
6396     { szRegisterUser, ACTION_RegisterUser },
6397     { szRemoveDuplicateFiles, ACTION_RemoveDuplicateFiles },
6398     { szRemoveEnvironmentStrings, ACTION_RemoveEnvironmentStrings },
6399     { szRemoveExistingProducts, ACTION_RemoveExistingProducts },
6400     { szRemoveFiles, ACTION_RemoveFiles },
6401     { szRemoveFolders, ACTION_RemoveFolders },
6402     { szRemoveIniValues, ACTION_RemoveIniValues },
6403     { szRemoveODBC, ACTION_RemoveODBC },
6404     { szRemoveRegistryValues, ACTION_RemoveRegistryValues },
6405     { szRemoveShortcuts, ACTION_RemoveShortcuts },
6406     { szResolveSource, ACTION_ResolveSource },
6407     { szRMCCPSearch, ACTION_RMCCPSearch },
6408     { szScheduleReboot, ACTION_ScheduleReboot },
6409     { szSelfRegModules, ACTION_SelfRegModules },
6410     { szSelfUnregModules, ACTION_SelfUnregModules },
6411     { szSetODBCFolders, ACTION_SetODBCFolders },
6412     { szStartServices, ACTION_StartServices },
6413     { szStopServices, ACTION_StopServices },
6414     { szUnpublishComponents, ACTION_UnpublishComponents },
6415     { szUnpublishFeatures, ACTION_UnpublishFeatures },
6416     { szUnregisterClassInfo, ACTION_UnregisterClassInfo },
6417     { szUnregisterComPlus, ACTION_UnregisterComPlus },
6418     { szUnregisterExtensionInfo, ACTION_UnregisterExtensionInfo },
6419     { szUnregisterFonts, ACTION_UnregisterFonts },
6420     { szUnregisterMIMEInfo, ACTION_UnregisterMIMEInfo },
6421     { szUnregisterProgIdInfo, ACTION_UnregisterProgIdInfo },
6422     { szUnregisterTypeLibraries, ACTION_UnregisterTypeLibraries },
6423     { szValidateProductID, ACTION_ValidateProductID },
6424     { szWriteEnvironmentStrings, ACTION_WriteEnvironmentStrings },
6425     { szWriteIniValues, ACTION_WriteIniValues },
6426     { szWriteRegistryValues, ACTION_WriteRegistryValues },
6427     { NULL, NULL },
6428 };
6429
6430 static BOOL ACTION_HandleStandardAction(MSIPACKAGE *package, LPCWSTR action,
6431                                         UINT* rc, BOOL force )
6432 {
6433     BOOL ret = FALSE;
6434     BOOL run = force;
6435     int i;
6436
6437     if (!run && !package->script->CurrentlyScripting)
6438         run = TRUE;
6439
6440     if (!run)
6441     {
6442         if (strcmpW(action,szInstallFinalize) == 0 ||
6443             strcmpW(action,szInstallExecute) == 0 ||
6444             strcmpW(action,szInstallExecuteAgain) == 0)
6445                 run = TRUE;
6446     }
6447
6448     i = 0;
6449     while (StandardActions[i].action != NULL)
6450     {
6451         if (strcmpW(StandardActions[i].action, action)==0)
6452         {
6453             if (!run)
6454             {
6455                 ui_actioninfo(package, action, TRUE, 0);
6456                 *rc = schedule_action(package,INSTALL_SCRIPT,action);
6457                 ui_actioninfo(package, action, FALSE, *rc);
6458             }
6459             else
6460             {
6461                 ui_actionstart(package, action);
6462                 if (StandardActions[i].handler)
6463                 {
6464                     *rc = StandardActions[i].handler(package);
6465                 }
6466                 else
6467                 {
6468                     FIXME("unhandled standard action %s\n",debugstr_w(action));
6469                     *rc = ERROR_SUCCESS;
6470                 }
6471             }
6472             ret = TRUE;
6473             break;
6474         }
6475         i++;
6476     }
6477     return ret;
6478 }
6479
6480 UINT ACTION_PerformAction(MSIPACKAGE *package, const WCHAR *action, UINT script, BOOL force)
6481 {
6482     UINT rc = ERROR_SUCCESS;
6483     BOOL handled;
6484
6485     TRACE("Performing action (%s)\n", debugstr_w(action));
6486
6487     handled = ACTION_HandleStandardAction(package, action, &rc, force);
6488
6489     if (!handled)
6490         handled = ACTION_HandleCustomAction(package, action, &rc, script, force);
6491
6492     if (!handled)
6493     {
6494         WARN("unhandled msi action %s\n", debugstr_w(action));
6495         rc = ERROR_FUNCTION_NOT_CALLED;
6496     }
6497
6498     return rc;
6499 }
6500
6501 UINT ACTION_PerformUIAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
6502 {
6503     UINT rc = ERROR_SUCCESS;
6504     BOOL handled = FALSE;
6505
6506     TRACE("Performing action (%s)\n", debugstr_w(action));
6507
6508     handled = ACTION_HandleStandardAction(package, action, &rc,TRUE);
6509
6510     if (!handled)
6511         handled = ACTION_HandleCustomAction(package, action, &rc, script, FALSE);
6512
6513     if( !handled && ACTION_DialogBox(package, action) == ERROR_SUCCESS )
6514         handled = TRUE;
6515
6516     if (!handled)
6517     {
6518         WARN("unhandled msi action %s\n", debugstr_w(action));
6519         rc = ERROR_FUNCTION_NOT_CALLED;
6520     }
6521
6522     return rc;
6523 }
6524
6525 static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq)
6526 {
6527     UINT rc = ERROR_SUCCESS;
6528     MSIRECORD *row;
6529
6530     static const WCHAR ExecSeqQuery[] =
6531         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6532          '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
6533          'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
6534          '`','S','e','q','u','e','n','c','e','`',' ', '=',' ','%','i',0};
6535     static const WCHAR UISeqQuery[] =
6536         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6537      '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e',
6538      '`', ' ', 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',
6539          ' ', '=',' ','%','i',0};
6540
6541     if (needs_ui_sequence(package))
6542         row = MSI_QueryGetRecord(package->db, UISeqQuery, seq);
6543     else
6544         row = MSI_QueryGetRecord(package->db, ExecSeqQuery, seq);
6545
6546     if (row)
6547     {
6548         LPCWSTR action, cond;
6549
6550         TRACE("Running the actions\n");
6551
6552         /* check conditions */
6553         cond = MSI_RecordGetString(row, 2);
6554
6555         /* this is a hack to skip errors in the condition code */
6556         if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
6557         {
6558             msiobj_release(&row->hdr);
6559             return ERROR_SUCCESS;
6560         }
6561
6562         action = MSI_RecordGetString(row, 1);
6563         if (!action)
6564         {
6565             ERR("failed to fetch action\n");
6566             msiobj_release(&row->hdr);
6567             return ERROR_FUNCTION_FAILED;
6568         }
6569
6570         if (needs_ui_sequence(package))
6571             rc = ACTION_PerformUIAction(package, action, -1);
6572         else
6573             rc = ACTION_PerformAction(package, action, -1, FALSE);
6574
6575         msiobj_release(&row->hdr);
6576     }
6577
6578     return rc;
6579 }
6580
6581 /****************************************************
6582  * TOP level entry points
6583  *****************************************************/
6584
6585 UINT MSI_InstallPackage( MSIPACKAGE *package, LPCWSTR szPackagePath,
6586                          LPCWSTR szCommandLine )
6587 {
6588     UINT rc;
6589     BOOL ui_exists;
6590
6591     static const WCHAR szAction[] = {'A','C','T','I','O','N',0};
6592     static const WCHAR szInstall[] = {'I','N','S','T','A','L','L',0};
6593
6594     MSI_SetPropertyW(package, szAction, szInstall);
6595
6596     package->script->InWhatSequence = SEQUENCE_INSTALL;
6597
6598     if (szPackagePath)
6599     {
6600         LPWSTR p, dir;
6601         LPCWSTR file;
6602
6603         dir = strdupW(szPackagePath);
6604         p = strrchrW(dir, '\\');
6605         if (p)
6606         {
6607             *(++p) = 0;
6608             file = szPackagePath + (p - dir);
6609         }
6610         else
6611         {
6612             msi_free(dir);
6613             dir = msi_alloc(MAX_PATH * sizeof(WCHAR));
6614             GetCurrentDirectoryW(MAX_PATH, dir);
6615             lstrcatW(dir, szBackSlash);
6616             file = szPackagePath;
6617         }
6618
6619         msi_free( package->PackagePath );
6620         package->PackagePath = msi_alloc((lstrlenW(dir) + lstrlenW(file) + 1) * sizeof(WCHAR));
6621         if (!package->PackagePath)
6622         {
6623             msi_free(dir);
6624             return ERROR_OUTOFMEMORY;
6625         }
6626
6627         lstrcpyW(package->PackagePath, dir);
6628         lstrcatW(package->PackagePath, file);
6629         msi_free(dir);
6630
6631         msi_set_sourcedir_props(package, FALSE);
6632     }
6633
6634     msi_parse_command_line( package, szCommandLine, FALSE );
6635
6636     msi_apply_transforms( package );
6637     msi_apply_patches( package );
6638
6639     if (!szCommandLine && msi_get_property_int( package, szInstalled, 0 ))
6640     {
6641         TRACE("setting reinstall property\n");
6642         MSI_SetPropertyW( package, szReinstall, szAll );
6643     }
6644
6645     /* properties may have been added by a transform */
6646     msi_clone_properties( package );
6647     msi_set_context( package );
6648
6649     if (needs_ui_sequence( package))
6650     {
6651         package->script->InWhatSequence |= SEQUENCE_UI;
6652         rc = ACTION_ProcessUISequence(package);
6653         ui_exists = ui_sequence_exists(package);
6654         if (rc == ERROR_SUCCESS || !ui_exists)
6655         {
6656             package->script->InWhatSequence |= SEQUENCE_EXEC;
6657             rc = ACTION_ProcessExecSequence(package, ui_exists);
6658         }
6659     }
6660     else
6661         rc = ACTION_ProcessExecSequence(package, FALSE);
6662
6663     package->script->CurrentlyScripting = FALSE;
6664
6665     /* process the ending type action */
6666     if (rc == ERROR_SUCCESS)
6667         ACTION_PerformActionSequence(package, -1);
6668     else if (rc == ERROR_INSTALL_USEREXIT)
6669         ACTION_PerformActionSequence(package, -2);
6670     else if (rc == ERROR_INSTALL_SUSPEND)
6671         ACTION_PerformActionSequence(package, -4);
6672     else  /* failed */
6673         ACTION_PerformActionSequence(package, -3);
6674
6675     /* finish up running custom actions */
6676     ACTION_FinishCustomActions(package);
6677
6678     if (rc == ERROR_SUCCESS && package->need_reboot)
6679         return ERROR_SUCCESS_REBOOT_REQUIRED;
6680
6681     return rc;
6682 }