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