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