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