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