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