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