mshtml: Added put_backgroundImage implementation.
[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 = sizeof(szEmpty);
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             static const WCHAR emptyW[] = {0};
3776             size += sizeof(WCHAR);
3777             RegSetValueExW(hukey,feature->Feature,0,REG_SZ,
3778                            (LPBYTE)(feature->Feature_Parent ? feature->Feature_Parent : emptyW),size);
3779         }
3780         else
3781         {
3782             size += 2*sizeof(WCHAR);
3783             data = msi_alloc(size);
3784             data[0] = 0x6;
3785             data[1] = 0;
3786             if (feature->Feature_Parent)
3787                 strcpyW( &data[1], feature->Feature_Parent );
3788             RegSetValueExW(hukey,feature->Feature,0,REG_SZ,
3789                        (LPBYTE)data,size);
3790             msi_free(data);
3791         }
3792
3793         /* the UI chunk */
3794         uirow = MSI_CreateRecord( 1 );
3795         MSI_RecordSetStringW( uirow, 1, feature->Feature );
3796         ui_actiondata( package, szPublishFeatures, uirow);
3797         msiobj_release( &uirow->hdr );
3798         /* FIXME: call ui_progress? */
3799     }
3800
3801 end:
3802     RegCloseKey(hkey);
3803     RegCloseKey(hukey);
3804     return rc;
3805 }
3806
3807 static UINT msi_unpublish_feature(MSIPACKAGE *package, MSIFEATURE *feature)
3808 {
3809     UINT r;
3810     HKEY hkey;
3811
3812     TRACE("unpublishing feature %s\n", debugstr_w(feature->Feature));
3813
3814     r = MSIREG_OpenUserFeaturesKey(package->ProductCode, &hkey, FALSE);
3815     if (r == ERROR_SUCCESS)
3816     {
3817         RegDeleteValueW(hkey, feature->Feature);
3818         RegCloseKey(hkey);
3819     }
3820
3821     r = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, &hkey, FALSE);
3822     if (r == ERROR_SUCCESS)
3823     {
3824         RegDeleteValueW(hkey, feature->Feature);
3825         RegCloseKey(hkey);
3826     }
3827
3828     return ERROR_SUCCESS;
3829 }
3830
3831 static BOOL msi_check_unpublish(MSIPACKAGE *package)
3832 {
3833     MSIFEATURE *feature;
3834
3835     LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3836     {
3837         if (feature->ActionRequest != INSTALLSTATE_ABSENT)
3838             return FALSE;
3839     }
3840
3841     return TRUE;
3842 }
3843
3844 static UINT ACTION_UnpublishFeatures(MSIPACKAGE *package)
3845 {
3846     MSIFEATURE *feature;
3847
3848     if (!msi_check_unpublish(package))
3849         return ERROR_SUCCESS;
3850
3851     LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3852     {
3853         msi_unpublish_feature(package, feature);
3854     }
3855
3856     return ERROR_SUCCESS;
3857 }
3858
3859 static UINT msi_get_local_package_name( LPWSTR path )
3860 {
3861     static const WCHAR szInstaller[] = {
3862         '\\','I','n','s','t','a','l','l','e','r','\\',0};
3863     static const WCHAR fmt[] = { '%','x','.','m','s','i',0};
3864     DWORD time, len, i;
3865     HANDLE handle;
3866
3867     time = GetTickCount();
3868     GetWindowsDirectoryW( path, MAX_PATH );
3869     lstrcatW( path, szInstaller );
3870     CreateDirectoryW( path, NULL );
3871
3872     len = lstrlenW(path);
3873     for (i=0; i<0x10000; i++)
3874     {
3875         snprintfW( &path[len], MAX_PATH - len, fmt, (time+i)&0xffff );
3876         handle = CreateFileW( path, GENERIC_WRITE, 0, NULL,
3877                               CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0 );
3878         if (handle != INVALID_HANDLE_VALUE)
3879         {
3880             CloseHandle(handle);
3881             break;
3882         }
3883         if (GetLastError() != ERROR_FILE_EXISTS &&
3884             GetLastError() != ERROR_SHARING_VIOLATION)
3885             return ERROR_FUNCTION_FAILED;
3886     }
3887
3888     return ERROR_SUCCESS;
3889 }
3890
3891 static UINT msi_make_package_local( MSIPACKAGE *package, HKEY hkey )
3892 {
3893     WCHAR packagefile[MAX_PATH];
3894     HKEY props;
3895     UINT r;
3896
3897     r = msi_get_local_package_name( packagefile );
3898     if (r != ERROR_SUCCESS)
3899         return r;
3900
3901     TRACE("Copying to local package %s\n",debugstr_w(packagefile));
3902
3903     r = CopyFileW( package->db->path, packagefile, FALSE);
3904
3905     if (!r)
3906     {
3907         ERR("Unable to copy package (%s -> %s) (error %d)\n",
3908             debugstr_w(package->db->path), debugstr_w(packagefile), GetLastError());
3909         return ERROR_FUNCTION_FAILED;
3910     }
3911
3912     msi_reg_set_val_str( hkey, INSTALLPROPERTY_LOCALPACKAGEW, packagefile );
3913
3914     r = MSIREG_OpenCurrentUserInstallProps(package->ProductCode, &props, TRUE);
3915     if (r != ERROR_SUCCESS)
3916         return r;
3917
3918     msi_reg_set_val_str(props, INSTALLPROPERTY_LOCALPACKAGEW, packagefile);
3919     RegCloseKey(props);
3920     return ERROR_SUCCESS;
3921 }
3922
3923 static UINT msi_write_uninstall_property_vals( MSIPACKAGE *package, HKEY hkey )
3924 {
3925     LPWSTR prop, val, key;
3926     static const LPCSTR propval[] = {
3927         "ARPAUTHORIZEDCDFPREFIX", "AuthorizedCDFPrefix",
3928         "ARPCONTACT",             "Contact",
3929         "ARPCOMMENTS",            "Comments",
3930         "ProductName",            "DisplayName",
3931         "ProductVersion",         "DisplayVersion",
3932         "ARPHELPLINK",            "HelpLink",
3933         "ARPHELPTELEPHONE",       "HelpTelephone",
3934         "ARPINSTALLLOCATION",     "InstallLocation",
3935         "SourceDir",              "InstallSource",
3936         "Manufacturer",           "Publisher",
3937         "ARPREADME",              "Readme",
3938         "ARPSIZE",                "Size",
3939         "ARPURLINFOABOUT",        "URLInfoAbout",
3940         "ARPURLUPDATEINFO",       "URLUpdateInfo",
3941         NULL,
3942     };
3943     const LPCSTR *p = propval;
3944
3945     while( *p )
3946     {
3947         prop = strdupAtoW( *p++ );
3948         key = strdupAtoW( *p++ );
3949         val = msi_dup_property( package, prop );
3950         msi_reg_set_val_str( hkey, key, val );
3951         msi_free(val);
3952         msi_free(key);
3953         msi_free(prop);
3954     }
3955     return ERROR_SUCCESS;
3956 }
3957
3958 static UINT ACTION_RegisterProduct(MSIPACKAGE *package)
3959 {
3960     HKEY hkey=0;
3961     HKEY hudkey=0, props=0;
3962     LPWSTR buffer = NULL;
3963     UINT rc;
3964     DWORD size, langid;
3965     static const WCHAR szWindowsInstaller[] = 
3966         {'W','i','n','d','o','w','s','I','n','s','t','a','l','l','e','r',0};
3967     static const WCHAR szUpgradeCode[] = 
3968         {'U','p','g','r','a','d','e','C','o','d','e',0};
3969     static const WCHAR modpath_fmt[] = 
3970         {'M','s','i','E','x','e','c','.','e','x','e',' ',
3971          '/','I','[','P','r','o','d','u','c','t','C','o','d','e',']',0};
3972     static const WCHAR szModifyPath[] = 
3973         {'M','o','d','i','f','y','P','a','t','h',0};
3974     static const WCHAR szUninstallString[] = 
3975         {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0};
3976     static const WCHAR szEstimatedSize[] = 
3977         {'E','s','t','i','m','a','t','e','d','S','i','z','e',0};
3978     static const WCHAR szProductLanguage[] =
3979         {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
3980     static const WCHAR szProductVersion[] =
3981         {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
3982     static const WCHAR szProductName[] =
3983         {'P','r','o','d','u','c','t','N','a','m','e',0};
3984     static const WCHAR szDisplayName[] =
3985         {'D','i','s','p','l','a','y','N','a','m','e',0};
3986     static const WCHAR szDisplayVersion[] =
3987         {'D','i','s','p','l','a','y','V','e','r','s','i','o','n',0};
3988     static const WCHAR szManufacturer[] =
3989         {'M','a','n','u','f','a','c','t','u','r','e','r',0};
3990
3991     SYSTEMTIME systime;
3992     static const WCHAR date_fmt[] = {'%','i','%','0','2','i','%','0','2','i',0};
3993     LPWSTR upgrade_code;
3994     WCHAR szDate[9];
3995
3996     /* FIXME: also need to publish if the product is in advertise mode */
3997     if (!msi_check_publish(package))
3998         return ERROR_SUCCESS;
3999
4000     rc = MSIREG_OpenUninstallKey(package->ProductCode,&hkey,TRUE);
4001     if (rc != ERROR_SUCCESS)
4002         return rc;
4003
4004     if (package->Context == MSIINSTALLCONTEXT_MACHINE)
4005     {
4006         rc = MSIREG_OpenLocalSystemInstallProps(package->ProductCode, &props, TRUE);
4007         if (rc != ERROR_SUCCESS)
4008             return rc;
4009     }
4010     else
4011     {
4012         rc = MSIREG_OpenCurrentUserInstallProps(package->ProductCode, &props, TRUE);
4013         if (rc != ERROR_SUCCESS)
4014             return rc;
4015     }
4016
4017     /* dump all the info i can grab */
4018     /* FIXME: Flesh out more information */
4019
4020     msi_write_uninstall_property_vals( package, hkey );
4021
4022     msi_reg_set_val_dword( hkey, szWindowsInstaller, 1 );
4023     
4024     msi_make_package_local( package, hkey );
4025
4026     /* do ModifyPath and UninstallString */
4027     size = deformat_string(package,modpath_fmt,&buffer);
4028     RegSetValueExW(hkey,szModifyPath,0,REG_EXPAND_SZ,(LPBYTE)buffer,size);
4029     RegSetValueExW(hkey,szUninstallString,0,REG_EXPAND_SZ,(LPBYTE)buffer,size);
4030     msi_free(buffer);
4031
4032     /* FIXME: Write real Estimated Size when we have it */
4033     msi_reg_set_val_dword( hkey, szEstimatedSize, 0 );
4034
4035     buffer = msi_dup_property( package, szProductName );
4036     msi_reg_set_val_str( props, szDisplayName, buffer );
4037     msi_free(buffer);
4038
4039     buffer = msi_dup_property( package, cszSourceDir );
4040     msi_reg_set_val_str( props, INSTALLPROPERTY_INSTALLSOURCEW, buffer);
4041     msi_free(buffer);
4042
4043     buffer = msi_dup_property( package, szManufacturer );
4044     msi_reg_set_val_str( props, INSTALLPROPERTY_PUBLISHERW, buffer);
4045     msi_free(buffer);
4046    
4047     GetLocalTime(&systime);
4048     sprintfW(szDate,date_fmt,systime.wYear,systime.wMonth,systime.wDay);
4049     msi_reg_set_val_str( hkey, INSTALLPROPERTY_INSTALLDATEW, szDate );
4050     msi_reg_set_val_str( props, INSTALLPROPERTY_INSTALLDATEW, szDate );
4051    
4052     langid = msi_get_property_int( package, szProductLanguage, 0 );
4053     msi_reg_set_val_dword( hkey, INSTALLPROPERTY_LANGUAGEW, langid );
4054
4055     buffer = msi_dup_property( package, szProductVersion );
4056     msi_reg_set_val_str( props, szDisplayVersion, buffer );
4057     if (buffer)
4058     {
4059         DWORD verdword = msi_version_str_to_dword(buffer);
4060
4061         msi_reg_set_val_dword( hkey, INSTALLPROPERTY_VERSIONW, verdword );
4062         msi_reg_set_val_dword( props, INSTALLPROPERTY_VERSIONW, verdword );
4063         msi_reg_set_val_dword( hkey, INSTALLPROPERTY_VERSIONMAJORW, verdword>>24 );
4064         msi_reg_set_val_dword( props, INSTALLPROPERTY_VERSIONMAJORW, verdword>>24 );
4065         msi_reg_set_val_dword( hkey, INSTALLPROPERTY_VERSIONMINORW, (verdword>>16)&0x00FF );
4066         msi_reg_set_val_dword( props, INSTALLPROPERTY_VERSIONMINORW, (verdword>>16)&0x00FF );
4067     }
4068     msi_free(buffer);
4069     
4070     /* Handle Upgrade Codes */
4071     upgrade_code = msi_dup_property( package, szUpgradeCode );
4072     if (upgrade_code)
4073     {
4074         HKEY hkey2;
4075         WCHAR squashed[33];
4076         MSIREG_OpenUpgradeCodesKey(upgrade_code, &hkey2, TRUE);
4077         squash_guid(package->ProductCode,squashed);
4078         msi_reg_set_val_str( hkey2, squashed, NULL );
4079         RegCloseKey(hkey2);
4080         MSIREG_OpenUserUpgradeCodesKey(upgrade_code, &hkey2, TRUE);
4081         squash_guid(package->ProductCode,squashed);
4082         msi_reg_set_val_str( hkey2, squashed, NULL );
4083         RegCloseKey(hkey2);
4084
4085         msi_free(upgrade_code);
4086     }
4087
4088     RegCloseKey(hkey);
4089
4090     rc = MSIREG_OpenUserDataProductKey(package->ProductCode, &hudkey, TRUE);
4091     if (rc != ERROR_SUCCESS)
4092         return rc;
4093
4094     RegCloseKey(hudkey);
4095
4096     msi_reg_set_val_dword( props, szWindowsInstaller, 1 );
4097     RegCloseKey(props);
4098
4099     return ERROR_SUCCESS;
4100 }
4101
4102 static UINT ACTION_InstallExecute(MSIPACKAGE *package)
4103 {
4104     return execute_script(package,INSTALL_SCRIPT);
4105 }
4106
4107 static UINT msi_unpublish_product(MSIPACKAGE *package)
4108 {
4109     LPWSTR remove = NULL;
4110     LPWSTR *features = NULL;
4111     BOOL full_uninstall = TRUE;
4112     MSIFEATURE *feature;
4113
4114     static const WCHAR szRemove[] = {'R','E','M','O','V','E',0};
4115     static const WCHAR szAll[] = {'A','L','L',0};
4116
4117     remove = msi_dup_property(package, szRemove);
4118     if (!remove)
4119         return ERROR_SUCCESS;
4120
4121     features = msi_split_string(remove, ',');
4122     if (!features)
4123     {
4124         msi_free(remove);
4125         ERR("REMOVE feature list is empty!\n");
4126         return ERROR_FUNCTION_FAILED;
4127     }
4128
4129     if (!lstrcmpW(features[0], szAll))
4130         full_uninstall = TRUE;
4131     else
4132     {
4133         LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4134         {
4135             if (feature->Action != INSTALLSTATE_ABSENT)
4136                 full_uninstall = FALSE;
4137         }
4138     }
4139
4140     if (!full_uninstall)
4141         goto done;
4142
4143     MSIREG_DeleteProductKey(package->ProductCode);
4144     MSIREG_DeleteUserProductKey(package->ProductCode);
4145     MSIREG_DeleteUserDataProductKey(package->ProductCode);
4146     MSIREG_DeleteUserFeaturesKey(package->ProductCode);
4147     MSIREG_DeleteUninstallKey(package->ProductCode);
4148
4149 done:
4150     msi_free(remove);
4151     msi_free(features);
4152     return ERROR_SUCCESS;
4153 }
4154
4155 static UINT ACTION_InstallFinalize(MSIPACKAGE *package)
4156 {
4157     UINT rc;
4158
4159     rc = msi_unpublish_product(package);
4160     if (rc != ERROR_SUCCESS)
4161         return rc;
4162
4163     /* turn off scheduling */
4164     package->script->CurrentlyScripting= FALSE;
4165
4166     /* first do the same as an InstallExecute */
4167     rc = ACTION_InstallExecute(package);
4168     if (rc != ERROR_SUCCESS)
4169         return rc;
4170
4171     /* then handle Commit Actions */
4172     rc = execute_script(package,COMMIT_SCRIPT);
4173
4174     return rc;
4175 }
4176
4177 UINT ACTION_ForceReboot(MSIPACKAGE *package)
4178 {
4179     static const WCHAR RunOnce[] = {
4180     'S','o','f','t','w','a','r','e','\\',
4181     'M','i','c','r','o','s','o','f','t','\\',
4182     'W','i','n','d','o','w','s','\\',
4183     'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
4184     'R','u','n','O','n','c','e',0};
4185     static const WCHAR InstallRunOnce[] = {
4186     'S','o','f','t','w','a','r','e','\\',
4187     'M','i','c','r','o','s','o','f','t','\\',
4188     'W','i','n','d','o','w','s','\\',
4189     'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
4190     'I','n','s','t','a','l','l','e','r','\\',
4191     'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
4192
4193     static const WCHAR msiexec_fmt[] = {
4194     '%','s',
4195     '\\','M','s','i','E','x','e','c','.','e','x','e',' ','/','@',' ',
4196     '\"','%','s','\"',0};
4197     static const WCHAR install_fmt[] = {
4198     '/','I',' ','\"','%','s','\"',' ',
4199     'A','F','T','E','R','R','E','B','O','O','T','=','1',' ',
4200     'R','U','N','O','N','C','E','E','N','T','R','Y','=','\"','%','s','\"',0};
4201     WCHAR buffer[256], sysdir[MAX_PATH];
4202     HKEY hkey;
4203     WCHAR squished_pc[100];
4204
4205     squash_guid(package->ProductCode,squished_pc);
4206
4207     GetSystemDirectoryW(sysdir, sizeof(sysdir)/sizeof(sysdir[0]));
4208     RegCreateKeyW(HKEY_LOCAL_MACHINE,RunOnce,&hkey);
4209     snprintfW(buffer,sizeof(buffer)/sizeof(buffer[0]),msiexec_fmt,sysdir,
4210      squished_pc);
4211
4212     msi_reg_set_val_str( hkey, squished_pc, buffer );
4213     RegCloseKey(hkey);
4214
4215     TRACE("Reboot command %s\n",debugstr_w(buffer));
4216
4217     RegCreateKeyW(HKEY_LOCAL_MACHINE,InstallRunOnce,&hkey);
4218     sprintfW(buffer,install_fmt,package->ProductCode,squished_pc);
4219
4220     msi_reg_set_val_str( hkey, squished_pc, buffer );
4221     RegCloseKey(hkey);
4222
4223     return ERROR_INSTALL_SUSPEND;
4224 }
4225
4226 static UINT ACTION_ResolveSource(MSIPACKAGE* package)
4227 {
4228     DWORD attrib;
4229     UINT rc;
4230
4231     /*
4232      * We are currently doing what should be done here in the top level Install
4233      * however for Administrative and uninstalls this step will be needed
4234      */
4235     if (!package->PackagePath)
4236         return ERROR_SUCCESS;
4237
4238     msi_set_sourcedir_props(package, TRUE);
4239
4240     attrib = GetFileAttributesW(package->db->path);
4241     if (attrib == INVALID_FILE_ATTRIBUTES)
4242     {
4243         LPWSTR prompt;
4244         LPWSTR msg;
4245         DWORD size = 0;
4246
4247         rc = MsiSourceListGetInfoW(package->ProductCode, NULL, 
4248                 package->Context, MSICODE_PRODUCT,
4249                 INSTALLPROPERTY_DISKPROMPTW,NULL,&size);
4250         if (rc == ERROR_MORE_DATA)
4251         {
4252             prompt = msi_alloc(size * sizeof(WCHAR));
4253             MsiSourceListGetInfoW(package->ProductCode, NULL, 
4254                     package->Context, MSICODE_PRODUCT,
4255                     INSTALLPROPERTY_DISKPROMPTW,prompt,&size);
4256         }
4257         else
4258             prompt = strdupW(package->db->path);
4259
4260         msg = generate_error_string(package,1302,1,prompt);
4261         while(attrib == INVALID_FILE_ATTRIBUTES)
4262         {
4263             rc = MessageBoxW(NULL,msg,NULL,MB_OKCANCEL);
4264             if (rc == IDCANCEL)
4265             {
4266                 rc = ERROR_INSTALL_USEREXIT;
4267                 break;
4268             }
4269             attrib = GetFileAttributesW(package->db->path);
4270         }
4271         msi_free(prompt);
4272         rc = ERROR_SUCCESS;
4273     }
4274     else
4275         return ERROR_SUCCESS;
4276
4277     return rc;
4278 }
4279
4280 static UINT ACTION_RegisterUser(MSIPACKAGE *package)
4281 {
4282     HKEY hkey=0;
4283     LPWSTR buffer;
4284     LPWSTR productid;
4285     UINT rc,i;
4286
4287     static const WCHAR szPropKeys[][80] = 
4288     {
4289         {'P','r','o','d','u','c','t','I','D',0},
4290         {'U','S','E','R','N','A','M','E',0},
4291         {'C','O','M','P','A','N','Y','N','A','M','E',0},
4292         {0},
4293     };
4294
4295     static const WCHAR szRegKeys[][80] = 
4296     {
4297         {'P','r','o','d','u','c','t','I','D',0},
4298         {'R','e','g','O','w','n','e','r',0},
4299         {'R','e','g','C','o','m','p','a','n','y',0},
4300         {0},
4301     };
4302
4303     if (msi_check_unpublish(package))
4304     {
4305         MSIREG_DeleteUserDataProductKey(package->ProductCode);
4306         return ERROR_SUCCESS;
4307     }
4308
4309     productid = msi_dup_property( package, INSTALLPROPERTY_PRODUCTIDW );
4310     if (!productid)
4311         return ERROR_SUCCESS;
4312
4313     rc = MSIREG_OpenCurrentUserInstallProps(package->ProductCode, &hkey, TRUE);
4314     if (rc != ERROR_SUCCESS)
4315         goto end;
4316
4317     for( i = 0; szPropKeys[i][0]; i++ )
4318     {
4319         buffer = msi_dup_property( package, szPropKeys[i] );
4320         msi_reg_set_val_str( hkey, szRegKeys[i], buffer );
4321         msi_free( buffer );
4322     }
4323
4324 end:
4325     msi_free(productid);
4326     RegCloseKey(hkey);
4327
4328     /* FIXME: call ui_actiondata */
4329
4330     return rc;
4331 }
4332
4333
4334 static UINT ACTION_ExecuteAction(MSIPACKAGE *package)
4335 {
4336     UINT rc;
4337
4338     package->script->InWhatSequence |= SEQUENCE_EXEC;
4339     rc = ACTION_ProcessExecSequence(package,FALSE);
4340     return rc;
4341 }
4342
4343
4344 static UINT ITERATE_PublishComponent(MSIRECORD *rec, LPVOID param)
4345 {
4346     MSIPACKAGE *package = (MSIPACKAGE*)param;
4347     LPCWSTR compgroupid=NULL;
4348     LPCWSTR feature=NULL;
4349     LPCWSTR text = NULL;
4350     LPCWSTR qualifier = NULL;
4351     LPCWSTR component = NULL;
4352     LPWSTR advertise = NULL;
4353     LPWSTR output = NULL;
4354     HKEY hkey;
4355     UINT rc = ERROR_SUCCESS;
4356     MSICOMPONENT *comp;
4357     DWORD sz = 0;
4358     MSIRECORD *uirow;
4359
4360     component = MSI_RecordGetString(rec,3);
4361     comp = get_loaded_component(package,component);
4362
4363     if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL ) && 
4364        !ACTION_VerifyComponentForAction( comp, INSTALLSTATE_SOURCE ) &&
4365        !ACTION_VerifyComponentForAction( comp, INSTALLSTATE_ADVERTISED ))
4366     {
4367         TRACE("Skipping: Component %s not scheduled for install\n",
4368                         debugstr_w(component));
4369
4370         return ERROR_SUCCESS;
4371     }
4372
4373     compgroupid = MSI_RecordGetString(rec,1);
4374     qualifier = MSI_RecordGetString(rec,2);
4375
4376     rc = MSIREG_OpenUserComponentsKey(compgroupid, &hkey, TRUE);
4377     if (rc != ERROR_SUCCESS)
4378         goto end;
4379     
4380     text = MSI_RecordGetString(rec,4);
4381     feature = MSI_RecordGetString(rec,5);
4382   
4383     advertise = create_component_advertise_string(package, comp, feature);
4384
4385     sz = strlenW(advertise);
4386
4387     if (text)
4388         sz += lstrlenW(text);
4389
4390     sz+=3;
4391     sz *= sizeof(WCHAR);
4392            
4393     output = msi_alloc_zero(sz);
4394     strcpyW(output,advertise);
4395     msi_free(advertise);
4396
4397     if (text)
4398         strcatW(output,text);
4399
4400     msi_reg_set_val_multi_str( hkey, qualifier, output );
4401     
4402 end:
4403     RegCloseKey(hkey);
4404     msi_free(output);
4405
4406     /* the UI chunk */
4407     uirow = MSI_CreateRecord( 2 );
4408     MSI_RecordSetStringW( uirow, 1, compgroupid );
4409     MSI_RecordSetStringW( uirow, 2, qualifier);
4410     ui_actiondata( package, szPublishComponents, uirow);
4411     msiobj_release( &uirow->hdr );
4412     /* FIXME: call ui_progress? */
4413
4414     return rc;
4415 }
4416
4417 /*
4418  * At present I am ignorning the advertised components part of this and only
4419  * focusing on the qualified component sets
4420  */
4421 static UINT ACTION_PublishComponents(MSIPACKAGE *package)
4422 {
4423     UINT rc;
4424     MSIQUERY * view;
4425     static const WCHAR ExecSeqQuery[] =
4426         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4427          '`','P','u','b','l','i','s','h',
4428          'C','o','m','p','o','n','e','n','t','`',0};
4429     
4430     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4431     if (rc != ERROR_SUCCESS)
4432         return ERROR_SUCCESS;
4433
4434     rc = MSI_IterateRecords(view, NULL, ITERATE_PublishComponent, package);
4435     msiobj_release(&view->hdr);
4436
4437     return rc;
4438 }
4439
4440 static UINT ITERATE_InstallService(MSIRECORD *rec, LPVOID param)
4441 {
4442     MSIPACKAGE *package = (MSIPACKAGE*)param;
4443     MSIRECORD *row;
4444     MSIFILE *file;
4445     SC_HANDLE hscm, service = NULL;
4446     LPCWSTR comp, depends, pass;
4447     LPWSTR name = NULL, disp = NULL;
4448     LPCWSTR load_order, serv_name, key;
4449     DWORD serv_type, start_type;
4450     DWORD err_control;
4451
4452     static const WCHAR query[] =
4453         {'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
4454          '`','C','o','m','p','o','n','e','n','t','`',' ',
4455          'W','H','E','R','E',' ',
4456          '`','C','o','m','p','o','n','e','n','t','`',' ',
4457          '=','\'','%','s','\'',0};
4458
4459     hscm = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASEW, GENERIC_WRITE);
4460     if (!hscm)
4461     {
4462         ERR("Failed to open the SC Manager!\n");
4463         goto done;
4464     }
4465
4466     start_type = MSI_RecordGetInteger(rec, 5);
4467     if (start_type == SERVICE_BOOT_START || start_type == SERVICE_SYSTEM_START)
4468         goto done;
4469
4470     depends = MSI_RecordGetString(rec, 8);
4471     if (depends && *depends)
4472         FIXME("Dependency list unhandled!\n");
4473
4474     deformat_string(package, MSI_RecordGetString(rec, 2), &name);
4475     deformat_string(package, MSI_RecordGetString(rec, 3), &disp);
4476     serv_type = MSI_RecordGetInteger(rec, 4);
4477     err_control = MSI_RecordGetInteger(rec, 6);
4478     load_order = MSI_RecordGetString(rec, 7);
4479     serv_name = MSI_RecordGetString(rec, 9);
4480     pass = MSI_RecordGetString(rec, 10);
4481     comp = MSI_RecordGetString(rec, 12);
4482
4483     /* fetch the service path */
4484     row = MSI_QueryGetRecord(package->db, query, comp);
4485     if (!row)
4486     {
4487         ERR("Control query failed!\n");
4488         goto done;
4489     }
4490
4491     key = MSI_RecordGetString(row, 6);
4492
4493     file = get_loaded_file(package, key);
4494     msiobj_release(&row->hdr);
4495     if (!file)
4496     {
4497         ERR("Failed to load the service file\n");
4498         goto done;
4499     }
4500
4501     service = CreateServiceW(hscm, name, disp, GENERIC_ALL, serv_type,
4502                              start_type, err_control, file->TargetPath,
4503                              load_order, NULL, NULL, serv_name, pass);
4504     if (!service)
4505     {
4506         if (GetLastError() != ERROR_SERVICE_EXISTS)
4507             ERR("Failed to create service %s: %d\n", debugstr_w(name), GetLastError());
4508     }
4509
4510 done:
4511     CloseServiceHandle(service);
4512     CloseServiceHandle(hscm);
4513     msi_free(name);
4514     msi_free(disp);
4515
4516     return ERROR_SUCCESS;
4517 }
4518
4519 static UINT ACTION_InstallServices( MSIPACKAGE *package )
4520 {
4521     UINT rc;
4522     MSIQUERY * view;
4523     static const WCHAR ExecSeqQuery[] =
4524         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4525          'S','e','r','v','i','c','e','I','n','s','t','a','l','l',0};
4526     
4527     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4528     if (rc != ERROR_SUCCESS)
4529         return ERROR_SUCCESS;
4530
4531     rc = MSI_IterateRecords(view, NULL, ITERATE_InstallService, package);
4532     msiobj_release(&view->hdr);
4533
4534     return rc;
4535 }
4536
4537 /* converts arg1[~]arg2[~]arg3 to a list of ptrs to the strings */
4538 static LPCWSTR *msi_service_args_to_vector(LPWSTR args, DWORD *numargs)
4539 {
4540     LPCWSTR *vector, *temp_vector;
4541     LPWSTR p, q;
4542     DWORD sep_len;
4543
4544     static const WCHAR separator[] = {'[','~',']',0};
4545
4546     *numargs = 0;
4547     sep_len = sizeof(separator) / sizeof(WCHAR) - 1;
4548
4549     if (!args)
4550         return NULL;
4551
4552     vector = msi_alloc(sizeof(LPWSTR));
4553     if (!vector)
4554         return NULL;
4555
4556     p = args;
4557     do
4558     {
4559         (*numargs)++;
4560         vector[*numargs - 1] = p;
4561
4562         if ((q = strstrW(p, separator)))
4563         {
4564             *q = '\0';
4565
4566             temp_vector = msi_realloc(vector, (*numargs + 1) * sizeof(LPWSTR));
4567             if (!temp_vector)
4568             {
4569                 msi_free(vector);
4570                 return NULL;
4571             }
4572             vector = temp_vector;
4573
4574             p = q + sep_len;
4575         }
4576     } while (q);
4577
4578     return vector;
4579 }
4580
4581 static UINT ITERATE_StartService(MSIRECORD *rec, LPVOID param)
4582 {
4583     MSIPACKAGE *package = (MSIPACKAGE *)param;
4584     MSICOMPONENT *comp;
4585     SC_HANDLE scm, service = NULL;
4586     LPCWSTR name, *vector = NULL;
4587     LPWSTR args;
4588     DWORD event, numargs;
4589     UINT r = ERROR_FUNCTION_FAILED;
4590
4591     comp = get_loaded_component(package, MSI_RecordGetString(rec, 6));
4592     if (!comp || comp->Action == INSTALLSTATE_UNKNOWN || comp->Action == INSTALLSTATE_ABSENT)
4593         return ERROR_SUCCESS;
4594
4595     name = MSI_RecordGetString(rec, 2);
4596     event = MSI_RecordGetInteger(rec, 3);
4597     args = strdupW(MSI_RecordGetString(rec, 4));
4598
4599     if (!(event & msidbServiceControlEventStart))
4600         return ERROR_SUCCESS;
4601
4602     scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
4603     if (!scm)
4604     {
4605         ERR("Failed to open the service control manager\n");
4606         goto done;
4607     }
4608
4609     service = OpenServiceW(scm, name, SERVICE_START);
4610     if (!service)
4611     {
4612         ERR("Failed to open service %s\n", debugstr_w(name));
4613         goto done;
4614     }
4615
4616     vector = msi_service_args_to_vector(args, &numargs);
4617
4618     if (!StartServiceW(service, numargs, vector))
4619     {
4620         ERR("Failed to start service %s\n", debugstr_w(name));
4621         goto done;
4622     }
4623
4624     r = ERROR_SUCCESS;
4625
4626 done:
4627     CloseServiceHandle(service);
4628     CloseServiceHandle(scm);
4629
4630     msi_free(args);
4631     msi_free(vector);
4632     return r;
4633 }
4634
4635 static UINT ACTION_StartServices( MSIPACKAGE *package )
4636 {
4637     UINT rc;
4638     MSIQUERY *view;
4639
4640     static const WCHAR query[] = {
4641         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4642         'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
4643
4644     rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4645     if (rc != ERROR_SUCCESS)
4646         return ERROR_SUCCESS;
4647
4648     rc = MSI_IterateRecords(view, NULL, ITERATE_StartService, package);
4649     msiobj_release(&view->hdr);
4650
4651     return rc;
4652 }
4653
4654 static BOOL stop_service_dependents(SC_HANDLE scm, SC_HANDLE service)
4655 {
4656     DWORD i, needed, count;
4657     ENUM_SERVICE_STATUSW *dependencies;
4658     SERVICE_STATUS ss;
4659     SC_HANDLE depserv;
4660
4661     if (EnumDependentServicesW(service, SERVICE_ACTIVE, NULL,
4662                                0, &needed, &count))
4663         return TRUE;
4664
4665     if (GetLastError() != ERROR_MORE_DATA)
4666         return FALSE;
4667
4668     dependencies = msi_alloc(needed);
4669     if (!dependencies)
4670         return FALSE;
4671
4672     if (!EnumDependentServicesW(service, SERVICE_ACTIVE, dependencies,
4673                                 needed, &needed, &count))
4674         goto error;
4675
4676     for (i = 0; i < count; i++)
4677     {
4678         depserv = OpenServiceW(scm, dependencies[i].lpServiceName,
4679                                SERVICE_STOP | SERVICE_QUERY_STATUS);
4680         if (!depserv)
4681             goto error;
4682
4683         if (!ControlService(depserv, SERVICE_CONTROL_STOP, &ss))
4684             goto error;
4685     }
4686
4687     return TRUE;
4688
4689 error:
4690     msi_free(dependencies);
4691     return FALSE;
4692 }
4693
4694 static UINT ITERATE_StopService(MSIRECORD *rec, LPVOID param)
4695 {
4696     MSIPACKAGE *package = (MSIPACKAGE *)param;
4697     MSICOMPONENT *comp;
4698     SERVICE_STATUS status;
4699     SERVICE_STATUS_PROCESS ssp;
4700     SC_HANDLE scm = NULL, service = NULL;
4701     LPWSTR name, args;
4702     DWORD event, needed;
4703
4704     event = MSI_RecordGetInteger(rec, 3);
4705     if (!(event & msidbServiceControlEventStop))
4706         return ERROR_SUCCESS;
4707
4708     comp = get_loaded_component(package, MSI_RecordGetString(rec, 6));
4709     if (!comp || comp->Action == INSTALLSTATE_UNKNOWN || comp->Action == INSTALLSTATE_ABSENT)
4710         return ERROR_SUCCESS;
4711
4712     deformat_string(package, MSI_RecordGetString(rec, 2), &name);
4713     deformat_string(package, MSI_RecordGetString(rec, 4), &args);
4714     args = strdupW(MSI_RecordGetString(rec, 4));
4715
4716     scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
4717     if (!scm)
4718     {
4719         WARN("Failed to open the SCM: %d\n", GetLastError());
4720         goto done;
4721     }
4722
4723     service = OpenServiceW(scm, name,
4724                            SERVICE_STOP |
4725                            SERVICE_QUERY_STATUS |
4726                            SERVICE_ENUMERATE_DEPENDENTS);
4727     if (!service)
4728     {
4729         WARN("Failed to open service (%s): %d\n",
4730               debugstr_w(name), GetLastError());
4731         goto done;
4732     }
4733
4734     if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp,
4735                               sizeof(SERVICE_STATUS_PROCESS), &needed))
4736     {
4737         WARN("Failed to query service status (%s): %d\n",
4738              debugstr_w(name), GetLastError());
4739         goto done;
4740     }
4741
4742     if (ssp.dwCurrentState == SERVICE_STOPPED)
4743         goto done;
4744
4745     stop_service_dependents(scm, service);
4746
4747     if (!ControlService(service, SERVICE_CONTROL_STOP, &status))
4748         WARN("Failed to stop service (%s): %d\n", debugstr_w(name), GetLastError());
4749
4750 done:
4751     CloseServiceHandle(service);
4752     CloseServiceHandle(scm);
4753     msi_free(name);
4754     msi_free(args);
4755
4756     return ERROR_SUCCESS;
4757 }
4758
4759 static UINT ACTION_StopServices( MSIPACKAGE *package )
4760 {
4761     UINT rc;
4762     MSIQUERY *view;
4763
4764     static const WCHAR query[] = {
4765         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4766         'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
4767
4768     rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4769     if (rc != ERROR_SUCCESS)
4770         return ERROR_SUCCESS;
4771
4772     rc = MSI_IterateRecords(view, NULL, ITERATE_StopService, package);
4773     msiobj_release(&view->hdr);
4774
4775     return rc;
4776 }
4777
4778 static MSIFILE *msi_find_file( MSIPACKAGE *package, LPCWSTR filename )
4779 {
4780     MSIFILE *file;
4781
4782     LIST_FOR_EACH_ENTRY(file, &package->files, MSIFILE, entry)
4783     {
4784         if (!lstrcmpW(file->File, filename))
4785             return file;
4786     }
4787
4788     return NULL;
4789 }
4790
4791 static UINT ITERATE_InstallODBCDriver( MSIRECORD *rec, LPVOID param )
4792 {
4793     MSIPACKAGE *package = (MSIPACKAGE*)param;
4794     LPWSTR driver, driver_path, ptr;
4795     WCHAR outpath[MAX_PATH];
4796     MSIFILE *driver_file, *setup_file;
4797     LPCWSTR desc;
4798     DWORD len, usage;
4799     UINT r = ERROR_SUCCESS;
4800
4801     static const WCHAR driver_fmt[] = {
4802         'D','r','i','v','e','r','=','%','s',0};
4803     static const WCHAR setup_fmt[] = {
4804         'S','e','t','u','p','=','%','s',0};
4805     static const WCHAR usage_fmt[] = {
4806         'F','i','l','e','U','s','a','g','e','=','1',0};
4807
4808     desc = MSI_RecordGetString(rec, 3);
4809
4810     driver_file = msi_find_file(package, MSI_RecordGetString(rec, 4));
4811     setup_file = msi_find_file(package, MSI_RecordGetString(rec, 5));
4812
4813     if (!driver_file || !setup_file)
4814     {
4815         ERR("ODBC Driver entry not found!\n");
4816         return ERROR_FUNCTION_FAILED;
4817     }
4818
4819     len = lstrlenW(desc) + lstrlenW(driver_fmt) + lstrlenW(driver_file->FileName) +
4820           lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName) +
4821           lstrlenW(usage_fmt) + 1;
4822     driver = msi_alloc(len * sizeof(WCHAR));
4823     if (!driver)
4824         return ERROR_OUTOFMEMORY;
4825
4826     ptr = driver;
4827     lstrcpyW(ptr, desc);
4828     ptr += lstrlenW(ptr) + 1;
4829
4830     sprintfW(ptr, driver_fmt, driver_file->FileName);
4831     ptr += lstrlenW(ptr) + 1;
4832
4833     sprintfW(ptr, setup_fmt, setup_file->FileName);
4834     ptr += lstrlenW(ptr) + 1;
4835
4836     lstrcpyW(ptr, usage_fmt);
4837     ptr += lstrlenW(ptr) + 1;
4838     *ptr = '\0';
4839
4840     driver_path = strdupW(driver_file->TargetPath);
4841     ptr = strrchrW(driver_path, '\\');
4842     if (ptr) *ptr = '\0';
4843
4844     if (!SQLInstallDriverExW(driver, driver_path, outpath, MAX_PATH,
4845                              NULL, ODBC_INSTALL_COMPLETE, &usage))
4846     {
4847         ERR("Failed to install SQL driver!\n");
4848         r = ERROR_FUNCTION_FAILED;
4849     }
4850
4851     msi_free(driver);
4852     msi_free(driver_path);
4853
4854     return r;
4855 }
4856
4857 static UINT ITERATE_InstallODBCTranslator( MSIRECORD *rec, LPVOID param )
4858 {
4859     MSIPACKAGE *package = (MSIPACKAGE*)param;
4860     LPWSTR translator, translator_path, ptr;
4861     WCHAR outpath[MAX_PATH];
4862     MSIFILE *translator_file, *setup_file;
4863     LPCWSTR desc;
4864     DWORD len, usage;
4865     UINT r = ERROR_SUCCESS;
4866
4867     static const WCHAR translator_fmt[] = {
4868         'T','r','a','n','s','l','a','t','o','r','=','%','s',0};
4869     static const WCHAR setup_fmt[] = {
4870         'S','e','t','u','p','=','%','s',0};
4871
4872     desc = MSI_RecordGetString(rec, 3);
4873
4874     translator_file = msi_find_file(package, MSI_RecordGetString(rec, 4));
4875     setup_file = msi_find_file(package, MSI_RecordGetString(rec, 5));
4876
4877     if (!translator_file || !setup_file)
4878     {
4879         ERR("ODBC Translator entry not found!\n");
4880         return ERROR_FUNCTION_FAILED;
4881     }
4882
4883     len = lstrlenW(desc) + lstrlenW(translator_fmt) + lstrlenW(translator_file->FileName) +
4884           lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName) + 1;
4885     translator = msi_alloc(len * sizeof(WCHAR));
4886     if (!translator)
4887         return ERROR_OUTOFMEMORY;
4888
4889     ptr = translator;
4890     lstrcpyW(ptr, desc);
4891     ptr += lstrlenW(ptr) + 1;
4892
4893     sprintfW(ptr, translator_fmt, translator_file->FileName);
4894     ptr += lstrlenW(ptr) + 1;
4895
4896     sprintfW(ptr, setup_fmt, setup_file->FileName);
4897     ptr += lstrlenW(ptr) + 1;
4898     *ptr = '\0';
4899
4900     translator_path = strdupW(translator_file->TargetPath);
4901     ptr = strrchrW(translator_path, '\\');
4902     if (ptr) *ptr = '\0';
4903
4904     if (!SQLInstallTranslatorExW(translator, translator_path, outpath, MAX_PATH,
4905                                  NULL, ODBC_INSTALL_COMPLETE, &usage))
4906     {
4907         ERR("Failed to install SQL translator!\n");
4908         r = ERROR_FUNCTION_FAILED;
4909     }
4910
4911     msi_free(translator);
4912     msi_free(translator_path);
4913
4914     return r;
4915 }
4916
4917 static UINT ITERATE_InstallODBCDataSource( MSIRECORD *rec, LPVOID param )
4918 {
4919     LPWSTR attrs;
4920     LPCWSTR desc, driver;
4921     WORD request = ODBC_ADD_SYS_DSN;
4922     INT registration;
4923     DWORD len;
4924     UINT r = ERROR_SUCCESS;
4925
4926     static const WCHAR attrs_fmt[] = {
4927         'D','S','N','=','%','s',0 };
4928
4929     desc = MSI_RecordGetString(rec, 3);
4930     driver = MSI_RecordGetString(rec, 4);
4931     registration = MSI_RecordGetInteger(rec, 5);
4932
4933     if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_ADD_SYS_DSN;
4934     else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_ADD_DSN;
4935
4936     len = lstrlenW(attrs_fmt) + lstrlenW(desc) + 1 + 1;
4937     attrs = msi_alloc(len * sizeof(WCHAR));
4938     if (!attrs)
4939         return ERROR_OUTOFMEMORY;
4940
4941     sprintfW(attrs, attrs_fmt, desc);
4942     attrs[len - 1] = '\0';
4943
4944     if (!SQLConfigDataSourceW(NULL, request, driver, attrs))
4945     {
4946         ERR("Failed to install SQL data source!\n");
4947         r = ERROR_FUNCTION_FAILED;
4948     }
4949
4950     msi_free(attrs);
4951
4952     return r;
4953 }
4954
4955 static UINT ACTION_InstallODBC( MSIPACKAGE *package )
4956 {
4957     UINT rc;
4958     MSIQUERY *view;
4959
4960     static const WCHAR driver_query[] = {
4961         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4962         'O','D','B','C','D','r','i','v','e','r',0 };
4963
4964     static const WCHAR translator_query[] = {
4965         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4966         'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0 };
4967
4968     static const WCHAR source_query[] = {
4969         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4970         'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0 };
4971
4972     rc = MSI_DatabaseOpenViewW(package->db, driver_query, &view);
4973     if (rc != ERROR_SUCCESS)
4974         return ERROR_SUCCESS;
4975
4976     rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDriver, package);
4977     msiobj_release(&view->hdr);
4978
4979     rc = MSI_DatabaseOpenViewW(package->db, translator_query, &view);
4980     if (rc != ERROR_SUCCESS)
4981         return ERROR_SUCCESS;
4982
4983     rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCTranslator, package);
4984     msiobj_release(&view->hdr);
4985
4986     rc = MSI_DatabaseOpenViewW(package->db, source_query, &view);
4987     if (rc != ERROR_SUCCESS)
4988         return ERROR_SUCCESS;
4989
4990     rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDataSource, package);
4991     msiobj_release(&view->hdr);
4992
4993     return rc;
4994 }
4995
4996 #define ENV_ACT_SETALWAYS   0x1
4997 #define ENV_ACT_SETABSENT   0x2
4998 #define ENV_ACT_REMOVE      0x4
4999 #define ENV_ACT_REMOVEMATCH 0x8
5000
5001 #define ENV_MOD_MACHINE     0x20000000
5002 #define ENV_MOD_APPEND      0x40000000
5003 #define ENV_MOD_PREFIX      0x80000000
5004 #define ENV_MOD_MASK        0xC0000000
5005
5006 #define check_flag_combo(x, y) ((x) & ~(y)) == (y)
5007
5008 static LONG env_set_flags( LPCWSTR *name, LPCWSTR *value, DWORD *flags )
5009 {
5010     LPCWSTR cptr = *name;
5011     LPCWSTR ptr = *value;
5012
5013     static const WCHAR prefix[] = {'[','~',']',0};
5014     static const int prefix_len = 3;
5015
5016     *flags = 0;
5017     while (*cptr)
5018     {
5019         if (*cptr == '=')
5020             *flags |= ENV_ACT_SETALWAYS;
5021         else if (*cptr == '+')
5022             *flags |= ENV_ACT_SETABSENT;
5023         else if (*cptr == '-')
5024             *flags |= ENV_ACT_REMOVE;
5025         else if (*cptr == '!')
5026             *flags |= ENV_ACT_REMOVEMATCH;
5027         else if (*cptr == '*')
5028             *flags |= ENV_MOD_MACHINE;
5029         else
5030             break;
5031
5032         cptr++;
5033         (*name)++;
5034     }
5035
5036     if (!*cptr)
5037     {
5038         ERR("Missing environment variable\n");
5039         return ERROR_FUNCTION_FAILED;
5040     }
5041
5042     if (!strncmpW(ptr, prefix, prefix_len))
5043     {
5044         *flags |= ENV_MOD_APPEND;
5045         *value += lstrlenW(prefix);
5046     }
5047     else if (lstrlenW(*value) >= prefix_len)
5048     {
5049         ptr += lstrlenW(ptr) - prefix_len;
5050         if (!lstrcmpW(ptr, prefix))
5051         {
5052             *flags |= ENV_MOD_PREFIX;
5053             /* the "[~]" will be removed by deformat_string */;
5054         }
5055     }
5056
5057     if (!*flags ||
5058         check_flag_combo(*flags, ENV_ACT_SETALWAYS | ENV_ACT_SETABSENT) ||
5059         check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETABSENT) ||
5060         check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETALWAYS) ||
5061         check_flag_combo(*flags, ENV_ACT_SETABSENT | ENV_MOD_MASK))
5062     {
5063         ERR("Invalid flags: %08x\n", *flags);
5064         return ERROR_FUNCTION_FAILED;
5065     }
5066
5067     return ERROR_SUCCESS;
5068 }
5069
5070 static UINT ITERATE_WriteEnvironmentString( MSIRECORD *rec, LPVOID param )
5071 {
5072     MSIPACKAGE *package = param;
5073     LPCWSTR name, value;
5074     LPWSTR data = NULL, newval = NULL;
5075     LPWSTR deformatted = NULL, ptr;
5076     DWORD flags, type, size;
5077     LONG res;
5078     HKEY env = NULL, root;
5079     LPCWSTR environment;
5080
5081     static const WCHAR user_env[] =
5082         {'E','n','v','i','r','o','n','m','e','n','t',0};
5083     static const WCHAR machine_env[] =
5084         {'S','y','s','t','e','m','\\',
5085          'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
5086          'C','o','n','t','r','o','l','\\',
5087          'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r','\\',
5088          'E','n','v','i','r','o','n','m','e','n','t',0};
5089     static const WCHAR semicolon[] = {';',0};
5090
5091     name = MSI_RecordGetString(rec, 2);
5092     value = MSI_RecordGetString(rec, 3);
5093
5094     res = env_set_flags(&name, &value, &flags);
5095     if (res != ERROR_SUCCESS)
5096        goto done;
5097
5098     deformat_string(package, value, &deformatted);
5099     if (!deformatted)
5100     {
5101         res = ERROR_OUTOFMEMORY;
5102         goto done;
5103     }
5104
5105     value = deformatted;
5106
5107     if (flags & ENV_MOD_MACHINE)
5108     {
5109         environment = machine_env;
5110         root = HKEY_LOCAL_MACHINE;
5111     }
5112     else
5113     {
5114         environment = user_env;
5115         root = HKEY_CURRENT_USER;
5116     }
5117
5118     res = RegCreateKeyExW(root, environment, 0, NULL, 0,
5119                           KEY_ALL_ACCESS, NULL, &env, NULL);
5120     if (res != ERROR_SUCCESS)
5121         goto done;
5122
5123     if (flags & ENV_ACT_REMOVE)
5124         FIXME("Not removing environment variable on uninstall!\n");
5125
5126     size = 0;
5127     res = RegQueryValueExW(env, name, NULL, &type, NULL, &size);
5128     if ((res != ERROR_SUCCESS && res != ERROR_FILE_NOT_FOUND) ||
5129         (res == ERROR_SUCCESS && type != REG_SZ && type != REG_EXPAND_SZ))
5130         goto done;
5131
5132     if (res != ERROR_FILE_NOT_FOUND)
5133     {
5134         if (flags & ENV_ACT_SETABSENT)
5135         {
5136             res = ERROR_SUCCESS;
5137             goto done;
5138         }
5139
5140         data = msi_alloc(size);
5141         if (!data)
5142         {
5143             RegCloseKey(env);
5144             return ERROR_OUTOFMEMORY;
5145         }
5146
5147         res = RegQueryValueExW(env, name, NULL, &type, (LPVOID)data, &size);
5148         if (res != ERROR_SUCCESS)
5149             goto done;
5150
5151         if (flags & ENV_ACT_REMOVEMATCH && (!value || !lstrcmpW(data, value)))
5152         {
5153             res = RegDeleteKeyW(env, name);
5154             goto done;
5155         }
5156
5157         size =  (lstrlenW(value) + 1 + size) * sizeof(WCHAR);
5158         newval =  msi_alloc(size);
5159         ptr = newval;
5160         if (!newval)
5161         {
5162             res = ERROR_OUTOFMEMORY;
5163             goto done;
5164         }
5165
5166         if (!(flags & ENV_MOD_MASK))
5167             lstrcpyW(newval, value);
5168         else
5169         {
5170             if (flags & ENV_MOD_PREFIX)
5171             {
5172                 lstrcpyW(newval, value);
5173                 lstrcatW(newval, semicolon);
5174                 ptr = newval + lstrlenW(value) + 1;
5175             }
5176
5177             lstrcpyW(ptr, data);
5178
5179             if (flags & ENV_MOD_APPEND)
5180             {
5181                 lstrcatW(newval, semicolon);
5182                 lstrcatW(newval, value);
5183             }
5184         }
5185     }
5186     else
5187     {
5188         size = (lstrlenW(value) + 1) * sizeof(WCHAR);
5189         newval = msi_alloc(size);
5190         if (!newval)
5191         {
5192             res = ERROR_OUTOFMEMORY;
5193             goto done;
5194         }
5195
5196         lstrcpyW(newval, value);
5197     }
5198
5199     TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(newval));
5200     res = RegSetValueExW(env, name, 0, type, (LPVOID)newval, size);
5201
5202 done:
5203     if (env) RegCloseKey(env);
5204     msi_free(deformatted);
5205     msi_free(data);
5206     msi_free(newval);
5207     return res;
5208 }
5209
5210 static UINT ACTION_WriteEnvironmentStrings( MSIPACKAGE *package )
5211 {
5212     UINT rc;
5213     MSIQUERY * view;
5214     static const WCHAR ExecSeqQuery[] =
5215         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5216          '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
5217     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
5218     if (rc != ERROR_SUCCESS)
5219         return ERROR_SUCCESS;
5220
5221     rc = MSI_IterateRecords(view, NULL, ITERATE_WriteEnvironmentString, package);
5222     msiobj_release(&view->hdr);
5223
5224     return rc;
5225 }
5226
5227 #define is_dot_dir(x) ((x[0] == '.') && ((x[1] == 0) || ((x[1] == '.') && (x[2] == 0))))
5228
5229 typedef struct
5230 {
5231     struct list entry;
5232     LPWSTR sourcename;
5233     LPWSTR destname;
5234     LPWSTR source;
5235     LPWSTR dest;
5236 } FILE_LIST;
5237
5238 static BOOL msi_move_file(LPCWSTR source, LPCWSTR dest, int options)
5239 {
5240     BOOL ret;
5241
5242     if (GetFileAttributesW(source) == FILE_ATTRIBUTE_DIRECTORY ||
5243         GetFileAttributesW(dest) == FILE_ATTRIBUTE_DIRECTORY)
5244     {
5245         WARN("Source or dest is directory, not moving\n");
5246         return FALSE;
5247     }
5248
5249     if (options == msidbMoveFileOptionsMove)
5250     {
5251         TRACE("moving %s -> %s\n", debugstr_w(source), debugstr_w(dest));
5252         ret = MoveFileExW(source, dest, MOVEFILE_REPLACE_EXISTING);
5253         if (!ret)
5254         {
5255             WARN("MoveFile failed: %d\n", GetLastError());
5256             return FALSE;
5257         }
5258     }
5259     else
5260     {
5261         TRACE("copying %s -> %s\n", debugstr_w(source), debugstr_w(dest));
5262         ret = CopyFileW(source, dest, FALSE);
5263         if (!ret)
5264         {
5265             WARN("CopyFile failed: %d\n", GetLastError());
5266             return FALSE;
5267         }
5268     }
5269
5270     return TRUE;
5271 }
5272
5273 static LPWSTR wildcard_to_file(LPWSTR wildcard, LPWSTR filename)
5274 {
5275     LPWSTR path, ptr;
5276     DWORD dirlen, pathlen;
5277
5278     ptr = strrchrW(wildcard, '\\');
5279     dirlen = ptr - wildcard + 1;
5280
5281     pathlen = dirlen + lstrlenW(filename) + 1;
5282     path = msi_alloc(pathlen * sizeof(WCHAR));
5283
5284     lstrcpynW(path, wildcard, dirlen + 1);
5285     lstrcatW(path, filename);
5286
5287     return path;
5288 }
5289
5290 static void free_file_entry(FILE_LIST *file)
5291 {
5292     msi_free(file->source);
5293     msi_free(file->dest);
5294     msi_free(file);
5295 }
5296
5297 static void free_list(FILE_LIST *list)
5298 {
5299     while (!list_empty(&list->entry))
5300     {
5301         FILE_LIST *file = LIST_ENTRY(list_head(&list->entry), FILE_LIST, entry);
5302
5303         list_remove(&file->entry);
5304         free_file_entry(file);
5305     }
5306 }
5307
5308 static BOOL add_wildcard(FILE_LIST *files, LPWSTR source, LPWSTR dest)
5309 {
5310     FILE_LIST *new, *file;
5311     LPWSTR ptr, filename;
5312     DWORD size;
5313
5314     new = msi_alloc_zero(sizeof(FILE_LIST));
5315     if (!new)
5316         return FALSE;
5317
5318     new->source = strdupW(source);
5319     ptr = strrchrW(dest, '\\') + 1;
5320     filename = strrchrW(new->source, '\\') + 1;
5321
5322     new->sourcename = filename;
5323
5324     if (*ptr)
5325         new->destname = ptr;
5326     else
5327         new->destname = new->sourcename;
5328
5329     size = (ptr - dest) + lstrlenW(filename) + 1;
5330     new->dest = msi_alloc(size * sizeof(WCHAR));
5331     if (!new->dest)
5332     {
5333         free_file_entry(new);
5334         return FALSE;
5335     }
5336
5337     lstrcpynW(new->dest, dest, ptr - dest + 1);
5338     lstrcatW(new->dest, filename);
5339
5340     if (list_empty(&files->entry))
5341     {
5342         list_add_head(&files->entry, &new->entry);
5343         return TRUE;
5344     }
5345
5346     LIST_FOR_EACH_ENTRY(file, &files->entry, FILE_LIST, entry)
5347     {
5348         if (lstrcmpW(source, file->source) < 0)
5349         {
5350             list_add_before(&file->entry, &new->entry);
5351             return TRUE;
5352         }
5353     }
5354
5355     list_add_after(&file->entry, &new->entry);
5356     return TRUE;
5357 }
5358
5359 static BOOL move_files_wildcard(LPWSTR source, LPWSTR dest, int options)
5360 {
5361     WIN32_FIND_DATAW wfd;
5362     HANDLE hfile;
5363     LPWSTR path;
5364     BOOL res;
5365     FILE_LIST files, *file;
5366     DWORD size;
5367
5368     hfile = FindFirstFileW(source, &wfd);
5369     if (hfile == INVALID_HANDLE_VALUE) return FALSE;
5370
5371     list_init(&files.entry);
5372
5373     for (res = TRUE; res; res = FindNextFileW(hfile, &wfd))
5374     {
5375         if (is_dot_dir(wfd.cFileName)) continue;
5376
5377         path = wildcard_to_file(source, wfd.cFileName);
5378         if (!path)
5379         {
5380             res = FALSE;
5381             goto done;
5382         }
5383
5384         add_wildcard(&files, path, dest);
5385         msi_free(path);
5386     }
5387
5388     /* no files match the wildcard */
5389     if (list_empty(&files.entry))
5390         goto done;
5391
5392     /* only the first wildcard match gets renamed to dest */
5393     file = LIST_ENTRY(list_head(&files.entry), FILE_LIST, entry);
5394     size = (strrchrW(file->dest, '\\') - file->dest) + lstrlenW(file->destname) + 2;
5395     file->dest = msi_realloc(file->dest, size * sizeof(WCHAR));
5396     if (!file->dest)
5397     {
5398         res = FALSE;
5399         goto done;
5400     }
5401
5402     lstrcpyW(strrchrW(file->dest, '\\') + 1, file->destname);
5403
5404     while (!list_empty(&files.entry))
5405     {
5406         file = LIST_ENTRY(list_head(&files.entry), FILE_LIST, entry);
5407
5408         msi_move_file((LPCWSTR)file->source, (LPCWSTR)file->dest, options);
5409
5410         list_remove(&file->entry);
5411         free_file_entry(file);
5412     }
5413
5414     res = TRUE;
5415
5416 done:
5417     free_list(&files);
5418     FindClose(hfile);
5419     return res;
5420 }
5421
5422 static UINT ITERATE_MoveFiles( MSIRECORD *rec, LPVOID param )
5423 {
5424     MSIPACKAGE *package = param;
5425     MSICOMPONENT *comp;
5426     LPCWSTR sourcename, destname;
5427     LPWSTR sourcedir = NULL, destdir = NULL;
5428     LPWSTR source = NULL, dest = NULL;
5429     int options;
5430     DWORD size;
5431     BOOL ret, wildcards;
5432
5433     static const WCHAR backslash[] = {'\\',0};
5434
5435     comp = get_loaded_component(package, MSI_RecordGetString(rec, 2));
5436     if (!comp || !comp->Enabled ||
5437         !(comp->Action & (INSTALLSTATE_LOCAL | INSTALLSTATE_SOURCE)))
5438     {
5439         TRACE("Component not set for install, not moving file\n");
5440         return ERROR_SUCCESS;
5441     }
5442
5443     sourcename = MSI_RecordGetString(rec, 3);
5444     destname = MSI_RecordGetString(rec, 4);
5445     options = MSI_RecordGetInteger(rec, 7);
5446
5447     sourcedir = msi_dup_property(package, MSI_RecordGetString(rec, 5));
5448     if (!sourcedir)
5449         goto done;
5450
5451     destdir = msi_dup_property(package, MSI_RecordGetString(rec, 6));
5452     if (!destdir)
5453         goto done;
5454
5455     if (!sourcename)
5456     {
5457         if (GetFileAttributesW(sourcedir) == INVALID_FILE_ATTRIBUTES)
5458             goto done;
5459
5460         source = strdupW(sourcedir);
5461         if (!source)
5462             goto done;
5463     }
5464     else
5465     {
5466         size = lstrlenW(sourcedir) + lstrlenW(sourcename) + 2;
5467         source = msi_alloc(size * sizeof(WCHAR));
5468         if (!source)
5469             goto done;
5470
5471         lstrcpyW(source, sourcedir);
5472         if (source[lstrlenW(source) - 1] != '\\')
5473             lstrcatW(source, backslash);
5474         lstrcatW(source, sourcename);
5475     }
5476
5477     wildcards = strchrW(source, '*') || strchrW(source, '?');
5478
5479     if (!destname && !wildcards)
5480     {
5481         destname = strdupW(sourcename);
5482         if (!destname)
5483             goto done;
5484     }
5485
5486     size = 0;
5487     if (destname)
5488         size = lstrlenW(destname);
5489
5490     size += lstrlenW(destdir) + 2;
5491     dest = msi_alloc(size * sizeof(WCHAR));
5492     if (!dest)
5493         goto done;
5494
5495     lstrcpyW(dest, destdir);
5496     if (dest[lstrlenW(dest) - 1] != '\\')
5497         lstrcatW(dest, backslash);
5498
5499     if (destname)
5500         lstrcatW(dest, destname);
5501
5502     if (GetFileAttributesW(destdir) == INVALID_FILE_ATTRIBUTES)
5503     {
5504         ret = CreateDirectoryW(destdir, NULL);
5505         if (!ret)
5506         {
5507             WARN("CreateDirectory failed: %d\n", GetLastError());
5508             return ERROR_SUCCESS;
5509         }
5510     }
5511
5512     if (!wildcards)
5513         msi_move_file(source, dest, options);
5514     else
5515         move_files_wildcard(source, dest, options);
5516
5517 done:
5518     msi_free(sourcedir);
5519     msi_free(destdir);
5520     msi_free(source);
5521     msi_free(dest);
5522
5523     return ERROR_SUCCESS;
5524 }
5525
5526 static UINT ACTION_MoveFiles( MSIPACKAGE *package )
5527 {
5528     UINT rc;
5529     MSIQUERY *view;
5530
5531     static const WCHAR ExecSeqQuery[] =
5532         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5533          '`','M','o','v','e','F','i','l','e','`',0};
5534
5535     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
5536     if (rc != ERROR_SUCCESS)
5537         return ERROR_SUCCESS;
5538
5539     rc = MSI_IterateRecords(view, NULL, ITERATE_MoveFiles, package);
5540     msiobj_release(&view->hdr);
5541
5542     return rc;
5543 }
5544
5545 static UINT msi_unimplemented_action_stub( MSIPACKAGE *package,
5546                                            LPCSTR action, LPCWSTR table )
5547 {
5548     static const WCHAR query[] = {
5549         'S','E','L','E','C','T',' ','*',' ',
5550         'F','R','O','M',' ','`','%','s','`',0 };
5551     MSIQUERY *view = NULL;
5552     DWORD count = 0;
5553     UINT r;
5554     
5555     r = MSI_OpenQuery( package->db, &view, query, table );
5556     if (r == ERROR_SUCCESS)
5557     {
5558         r = MSI_IterateRecords(view, &count, NULL, package);
5559         msiobj_release(&view->hdr);
5560     }
5561
5562     if (count)
5563         FIXME("%s -> %u ignored %s table values\n",
5564               action, count, debugstr_w(table));
5565
5566     return ERROR_SUCCESS;
5567 }
5568
5569 static UINT ACTION_AllocateRegistrySpace( MSIPACKAGE *package )
5570 {
5571     TRACE("%p\n", package);
5572     return ERROR_SUCCESS;
5573 }
5574
5575 static UINT ACTION_RemoveIniValues( MSIPACKAGE *package )
5576 {
5577     static const WCHAR table[] =
5578          {'R','e','m','o','v','e','I','n','i','F','i','l','e',0 };
5579     return msi_unimplemented_action_stub( package, "RemoveIniValues", table );
5580 }
5581
5582 static UINT ACTION_PatchFiles( MSIPACKAGE *package )
5583 {
5584     static const WCHAR table[] = { 'P','a','t','c','h',0 };
5585     return msi_unimplemented_action_stub( package, "PatchFiles", table );
5586 }
5587
5588 static UINT ACTION_BindImage( MSIPACKAGE *package )
5589 {
5590     static const WCHAR table[] = { 'B','i','n','d','I','m','a','g','e',0 };
5591     return msi_unimplemented_action_stub( package, "BindImage", table );
5592 }
5593
5594 static UINT ACTION_IsolateComponents( MSIPACKAGE *package )
5595 {
5596     static const WCHAR table[] = {
5597         'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t',0 };
5598     return msi_unimplemented_action_stub( package, "IsolateComponents", table );
5599 }
5600
5601 static UINT ACTION_MigrateFeatureStates( MSIPACKAGE *package )
5602 {
5603     static const WCHAR table[] = { 'U','p','g','r','a','d','e',0 };
5604     return msi_unimplemented_action_stub( package, "MigrateFeatureStates", table );
5605 }
5606
5607 static UINT ACTION_SelfUnregModules( MSIPACKAGE *package )
5608 {
5609     static const WCHAR table[] = { 'S','e','l','f','R','e','g',0 };
5610     return msi_unimplemented_action_stub( package, "SelfUnregModules", table );
5611 }
5612
5613 static UINT ACTION_DeleteServices( MSIPACKAGE *package )
5614 {
5615     static const WCHAR table[] = {
5616         'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
5617     return msi_unimplemented_action_stub( package, "DeleteServices", table );
5618 }
5619 static UINT ACTION_ValidateProductID( MSIPACKAGE *package )
5620 {
5621         static const WCHAR table[] = {
5622                 'P','r','o','d','u','c','t','I','D',0 };
5623         return msi_unimplemented_action_stub( package, "ValidateProductID", table );
5624 }
5625
5626 static UINT ACTION_RemoveEnvironmentStrings( MSIPACKAGE *package )
5627 {
5628     static const WCHAR table[] = {
5629         'E','n','v','i','r','o','n','m','e','n','t',0 };
5630     return msi_unimplemented_action_stub( package, "RemoveEnvironmentStrings", table );
5631 }
5632
5633 static UINT ACTION_MsiPublishAssemblies( MSIPACKAGE *package )
5634 {
5635     static const WCHAR table[] = {
5636         'M','s','i','A','s','s','e','m','b','l','y',0 };
5637     return msi_unimplemented_action_stub( package, "MsiPublishAssemblies", table );
5638 }
5639
5640 static UINT ACTION_MsiUnpublishAssemblies( MSIPACKAGE *package )
5641 {
5642     static const WCHAR table[] = {
5643         'M','s','i','A','s','s','e','m','b','l','y',0 };
5644     return msi_unimplemented_action_stub( package, "MsiUnpublishAssemblies", table );
5645 }
5646
5647 static UINT ACTION_UnregisterFonts( MSIPACKAGE *package )
5648 {
5649     static const WCHAR table[] = { 'F','o','n','t',0 };
5650     return msi_unimplemented_action_stub( package, "UnregisterFonts", table );
5651 }
5652
5653 static UINT ACTION_RMCCPSearch( MSIPACKAGE *package )
5654 {
5655     static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
5656     return msi_unimplemented_action_stub( package, "RMCCPSearch", table );
5657 }
5658
5659 static UINT ACTION_RegisterComPlus( MSIPACKAGE *package )
5660 {
5661     static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
5662     return msi_unimplemented_action_stub( package, "RegisterComPlus", table );
5663 }
5664
5665 static UINT ACTION_UnregisterComPlus( MSIPACKAGE *package )
5666 {
5667     static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
5668     return msi_unimplemented_action_stub( package, "UnregisterComPlus", table );
5669 }
5670
5671 static UINT ACTION_InstallSFPCatalogFile( MSIPACKAGE *package )
5672 {
5673     static const WCHAR table[] = { 'S','F','P','C','a','t','a','l','o','g',0 };
5674     return msi_unimplemented_action_stub( package, "InstallSFPCatalogFile", table );
5675 }
5676
5677 static UINT ACTION_RemoveDuplicateFiles( MSIPACKAGE *package )
5678 {
5679     static const WCHAR table[] = { 'D','u','p','l','i','c','a','t','e','F','i','l','e',0 };
5680     return msi_unimplemented_action_stub( package, "RemoveDuplicateFiles", table );
5681 }
5682
5683 static UINT ACTION_RemoveExistingProducts( MSIPACKAGE *package )
5684 {
5685     static const WCHAR table[] = { 'U','p','g','r','a','d','e',0 };
5686     return msi_unimplemented_action_stub( package, "RemoveExistingProducts", table );
5687 }
5688
5689 static UINT ACTION_RemoveFolders( MSIPACKAGE *package )
5690 {
5691     static const WCHAR table[] = { 'C','r','e','a','t','e','F','o','l','d','e','r',0 };
5692     return msi_unimplemented_action_stub( package, "RemoveFolders", table );
5693 }
5694
5695 static UINT ACTION_RemoveODBC( MSIPACKAGE *package )
5696 {
5697     static const WCHAR table[] = { 'O','D','B','C','D','r','i','v','e','r',0 };
5698     return msi_unimplemented_action_stub( package, "RemoveODBC", table );
5699 }
5700
5701 static UINT ACTION_RemoveRegistryValues( MSIPACKAGE *package )
5702 {
5703     static const WCHAR table[] = { 'R','e','m','o','v','e','R','e','g','i','s','t','r','y',0 };
5704     return msi_unimplemented_action_stub( package, "RemoveRegistryValues", table );
5705 }
5706
5707 static UINT ACTION_RemoveShortcuts( MSIPACKAGE *package )
5708 {
5709     static const WCHAR table[] = { 'S','h','o','r','t','c','u','t',0 };
5710     return msi_unimplemented_action_stub( package, "RemoveShortcuts", table );
5711 }
5712
5713 static UINT ACTION_UnpublishComponents( MSIPACKAGE *package )
5714 {
5715     static const WCHAR table[] = { 'P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t',0 };
5716     return msi_unimplemented_action_stub( package, "UnpublishComponents", table );
5717 }
5718
5719 static UINT ACTION_UnregisterClassInfo( MSIPACKAGE *package )
5720 {
5721     static const WCHAR table[] = { 'A','p','p','I','d',0 };
5722     return msi_unimplemented_action_stub( package, "UnregisterClassInfo", table );
5723 }
5724
5725 static UINT ACTION_UnregisterExtensionInfo( MSIPACKAGE *package )
5726 {
5727     static const WCHAR table[] = { 'E','x','t','e','n','s','i','o','n',0 };
5728     return msi_unimplemented_action_stub( package, "UnregisterExtensionInfo", table );
5729 }
5730
5731 static UINT ACTION_UnregisterMIMEInfo( MSIPACKAGE *package )
5732 {
5733     static const WCHAR table[] = { 'M','I','M','E',0 };
5734     return msi_unimplemented_action_stub( package, "UnregisterMIMEInfo", table );
5735 }
5736
5737 static UINT ACTION_UnregisterProgIdInfo( MSIPACKAGE *package )
5738 {
5739     static const WCHAR table[] = { 'P','r','o','g','I','d',0 };
5740     return msi_unimplemented_action_stub( package, "UnregisterProgIdInfo", table );
5741 }
5742
5743 static UINT ACTION_UnregisterTypeLibraries( MSIPACKAGE *package )
5744 {
5745     static const WCHAR table[] = { 'T','y','p','e','L','i','b',0 };
5746     return msi_unimplemented_action_stub( package, "UnregisterTypeLibraries", table );
5747 }
5748
5749 static const struct _actions StandardActions[] = {
5750     { szAllocateRegistrySpace, ACTION_AllocateRegistrySpace },
5751     { szAppSearch, ACTION_AppSearch },
5752     { szBindImage, ACTION_BindImage },
5753     { szCCPSearch, ACTION_CCPSearch },
5754     { szCostFinalize, ACTION_CostFinalize },
5755     { szCostInitialize, ACTION_CostInitialize },
5756     { szCreateFolders, ACTION_CreateFolders },
5757     { szCreateShortcuts, ACTION_CreateShortcuts },
5758     { szDeleteServices, ACTION_DeleteServices },
5759     { szDisableRollback, NULL },
5760     { szDuplicateFiles, ACTION_DuplicateFiles },
5761     { szExecuteAction, ACTION_ExecuteAction },
5762     { szFileCost, ACTION_FileCost },
5763     { szFindRelatedProducts, ACTION_FindRelatedProducts },
5764     { szForceReboot, ACTION_ForceReboot },
5765     { szInstallAdminPackage, NULL },
5766     { szInstallExecute, ACTION_InstallExecute },
5767     { szInstallExecuteAgain, ACTION_InstallExecute },
5768     { szInstallFiles, ACTION_InstallFiles},
5769     { szInstallFinalize, ACTION_InstallFinalize },
5770     { szInstallInitialize, ACTION_InstallInitialize },
5771     { szInstallSFPCatalogFile, ACTION_InstallSFPCatalogFile },
5772     { szInstallValidate, ACTION_InstallValidate },
5773     { szIsolateComponents, ACTION_IsolateComponents },
5774     { szLaunchConditions, ACTION_LaunchConditions },
5775     { szMigrateFeatureStates, ACTION_MigrateFeatureStates },
5776     { szMoveFiles, ACTION_MoveFiles },
5777     { szMsiPublishAssemblies, ACTION_MsiPublishAssemblies },
5778     { szMsiUnpublishAssemblies, ACTION_MsiUnpublishAssemblies },
5779     { szInstallODBC, ACTION_InstallODBC },
5780     { szInstallServices, ACTION_InstallServices },
5781     { szPatchFiles, ACTION_PatchFiles },
5782     { szProcessComponents, ACTION_ProcessComponents },
5783     { szPublishComponents, ACTION_PublishComponents },
5784     { szPublishFeatures, ACTION_PublishFeatures },
5785     { szPublishProduct, ACTION_PublishProduct },
5786     { szRegisterClassInfo, ACTION_RegisterClassInfo },
5787     { szRegisterComPlus, ACTION_RegisterComPlus},
5788     { szRegisterExtensionInfo, ACTION_RegisterExtensionInfo },
5789     { szRegisterFonts, ACTION_RegisterFonts },
5790     { szRegisterMIMEInfo, ACTION_RegisterMIMEInfo },
5791     { szRegisterProduct, ACTION_RegisterProduct },
5792     { szRegisterProgIdInfo, ACTION_RegisterProgIdInfo },
5793     { szRegisterTypeLibraries, ACTION_RegisterTypeLibraries },
5794     { szRegisterUser, ACTION_RegisterUser },
5795     { szRemoveDuplicateFiles, ACTION_RemoveDuplicateFiles },
5796     { szRemoveEnvironmentStrings, ACTION_RemoveEnvironmentStrings },
5797     { szRemoveExistingProducts, ACTION_RemoveExistingProducts },
5798     { szRemoveFiles, ACTION_RemoveFiles },
5799     { szRemoveFolders, ACTION_RemoveFolders },
5800     { szRemoveIniValues, ACTION_RemoveIniValues },
5801     { szRemoveODBC, ACTION_RemoveODBC },
5802     { szRemoveRegistryValues, ACTION_RemoveRegistryValues },
5803     { szRemoveShortcuts, ACTION_RemoveShortcuts },
5804     { szResolveSource, ACTION_ResolveSource },
5805     { szRMCCPSearch, ACTION_RMCCPSearch },
5806     { szScheduleReboot, NULL },
5807     { szSelfRegModules, ACTION_SelfRegModules },
5808     { szSelfUnregModules, ACTION_SelfUnregModules },
5809     { szSetODBCFolders, NULL },
5810     { szStartServices, ACTION_StartServices },
5811     { szStopServices, ACTION_StopServices },
5812     { szUnpublishComponents, ACTION_UnpublishComponents },
5813     { szUnpublishFeatures, ACTION_UnpublishFeatures },
5814     { szUnregisterClassInfo, ACTION_UnregisterClassInfo },
5815     { szUnregisterComPlus, ACTION_UnregisterComPlus },
5816     { szUnregisterExtensionInfo, ACTION_UnregisterExtensionInfo },
5817     { szUnregisterFonts, ACTION_UnregisterFonts },
5818     { szUnregisterMIMEInfo, ACTION_UnregisterMIMEInfo },
5819     { szUnregisterProgIdInfo, ACTION_UnregisterProgIdInfo },
5820     { szUnregisterTypeLibraries, ACTION_UnregisterTypeLibraries },
5821     { szValidateProductID, ACTION_ValidateProductID },
5822     { szWriteEnvironmentStrings, ACTION_WriteEnvironmentStrings },
5823     { szWriteIniValues, ACTION_WriteIniValues },
5824     { szWriteRegistryValues, ACTION_WriteRegistryValues },
5825     { NULL, NULL },
5826 };
5827
5828 static BOOL ACTION_HandleStandardAction(MSIPACKAGE *package, LPCWSTR action,
5829                                         UINT* rc, BOOL force )
5830 {
5831     BOOL ret = FALSE;
5832     BOOL run = force;
5833     int i;
5834
5835     if (!run && !package->script->CurrentlyScripting)
5836         run = TRUE;
5837
5838     if (!run)
5839     {
5840         if (strcmpW(action,szInstallFinalize) == 0 ||
5841             strcmpW(action,szInstallExecute) == 0 ||
5842             strcmpW(action,szInstallExecuteAgain) == 0)
5843                 run = TRUE;
5844     }
5845
5846     i = 0;
5847     while (StandardActions[i].action != NULL)
5848     {
5849         if (strcmpW(StandardActions[i].action, action)==0)
5850         {
5851             if (!run)
5852             {
5853                 ui_actioninfo(package, action, TRUE, 0);
5854                 *rc = schedule_action(package,INSTALL_SCRIPT,action);
5855                 ui_actioninfo(package, action, FALSE, *rc);
5856             }
5857             else
5858             {
5859                 ui_actionstart(package, action);
5860                 if (StandardActions[i].handler)
5861                 {
5862                     *rc = StandardActions[i].handler(package);
5863                 }
5864                 else
5865                 {
5866                     FIXME("unhandled standard action %s\n",debugstr_w(action));
5867                     *rc = ERROR_SUCCESS;
5868                 }
5869             }
5870             ret = TRUE;
5871             break;
5872         }
5873         i++;
5874     }
5875     return ret;
5876 }