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