msi: Initialize the size parameter.
[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 MSIFILE *msi_find_file( MSIPACKAGE *package, LPCWSTR filename )
4682 {
4683     MSIFILE *file;
4684
4685     LIST_FOR_EACH_ENTRY(file, &package->files, MSIFILE, entry)
4686     {
4687         if (!lstrcmpW(file->File, filename))
4688             return file;
4689     }
4690
4691     return NULL;
4692 }
4693
4694 static UINT ITERATE_InstallODBCDriver( MSIRECORD *rec, LPVOID param )
4695 {
4696     MSIPACKAGE *package = (MSIPACKAGE*)param;
4697     LPWSTR driver, driver_path, ptr;
4698     WCHAR outpath[MAX_PATH];
4699     MSIFILE *driver_file, *setup_file;
4700     LPCWSTR desc;
4701     DWORD len, usage;
4702     UINT r = ERROR_SUCCESS;
4703
4704     static const WCHAR driver_fmt[] = {
4705         'D','r','i','v','e','r','=','%','s',0};
4706     static const WCHAR setup_fmt[] = {
4707         'S','e','t','u','p','=','%','s',0};
4708     static const WCHAR usage_fmt[] = {
4709         'F','i','l','e','U','s','a','g','e','=','1',0};
4710
4711     desc = MSI_RecordGetString(rec, 3);
4712
4713     driver_file = msi_find_file(package, MSI_RecordGetString(rec, 4));
4714     setup_file = msi_find_file(package, MSI_RecordGetString(rec, 5));
4715
4716     if (!driver_file || !setup_file)
4717     {
4718         ERR("ODBC Driver entry not found!\n");
4719         return ERROR_FUNCTION_FAILED;
4720     }
4721
4722     len = lstrlenW(desc) + lstrlenW(driver_fmt) + lstrlenW(driver_file->FileName) +
4723           lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName) +
4724           lstrlenW(usage_fmt) + 1;
4725     driver = msi_alloc(len * sizeof(WCHAR));
4726     if (!driver)
4727         return ERROR_OUTOFMEMORY;
4728
4729     ptr = driver;
4730     lstrcpyW(ptr, desc);
4731     ptr += lstrlenW(ptr) + 1;
4732
4733     sprintfW(ptr, driver_fmt, driver_file->FileName);
4734     ptr += lstrlenW(ptr) + 1;
4735
4736     sprintfW(ptr, setup_fmt, setup_file->FileName);
4737     ptr += lstrlenW(ptr) + 1;
4738
4739     lstrcpyW(ptr, usage_fmt);
4740     ptr += lstrlenW(ptr) + 1;
4741     *ptr = '\0';
4742
4743     driver_path = strdupW(driver_file->TargetPath);
4744     ptr = strrchrW(driver_path, '\\');
4745     if (ptr) *ptr = '\0';
4746
4747     if (!SQLInstallDriverExW(driver, driver_path, outpath, MAX_PATH,
4748                              NULL, ODBC_INSTALL_COMPLETE, &usage))
4749     {
4750         ERR("Failed to install SQL driver!\n");
4751         r = ERROR_FUNCTION_FAILED;
4752     }
4753
4754     msi_free(driver);
4755     msi_free(driver_path);
4756
4757     return r;
4758 }
4759
4760 static UINT ITERATE_InstallODBCTranslator( MSIRECORD *rec, LPVOID param )
4761 {
4762     MSIPACKAGE *package = (MSIPACKAGE*)param;
4763     LPWSTR translator, translator_path, ptr;
4764     WCHAR outpath[MAX_PATH];
4765     MSIFILE *translator_file, *setup_file;
4766     LPCWSTR desc;
4767     DWORD len, usage;
4768     UINT r = ERROR_SUCCESS;
4769
4770     static const WCHAR translator_fmt[] = {
4771         'T','r','a','n','s','l','a','t','o','r','=','%','s',0};
4772     static const WCHAR setup_fmt[] = {
4773         'S','e','t','u','p','=','%','s',0};
4774
4775     desc = MSI_RecordGetString(rec, 3);
4776
4777     translator_file = msi_find_file(package, MSI_RecordGetString(rec, 4));
4778     setup_file = msi_find_file(package, MSI_RecordGetString(rec, 5));
4779
4780     if (!translator_file || !setup_file)
4781     {
4782         ERR("ODBC Translator entry not found!\n");
4783         return ERROR_FUNCTION_FAILED;
4784     }
4785
4786     len = lstrlenW(desc) + lstrlenW(translator_fmt) + lstrlenW(translator_file->FileName) +
4787           lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName) + 1;
4788     translator = msi_alloc(len * sizeof(WCHAR));
4789     if (!translator)
4790         return ERROR_OUTOFMEMORY;
4791
4792     ptr = translator;
4793     lstrcpyW(ptr, desc);
4794     ptr += lstrlenW(ptr) + 1;
4795
4796     sprintfW(ptr, translator_fmt, translator_file->FileName);
4797     ptr += lstrlenW(ptr) + 1;
4798
4799     sprintfW(ptr, setup_fmt, setup_file->FileName);
4800     ptr += lstrlenW(ptr) + 1;
4801     *ptr = '\0';
4802
4803     translator_path = strdupW(translator_file->TargetPath);
4804     ptr = strrchrW(translator_path, '\\');
4805     if (ptr) *ptr = '\0';
4806
4807     if (!SQLInstallTranslatorExW(translator, translator_path, outpath, MAX_PATH,
4808                                  NULL, ODBC_INSTALL_COMPLETE, &usage))
4809     {
4810         ERR("Failed to install SQL translator!\n");
4811         r = ERROR_FUNCTION_FAILED;
4812     }
4813
4814     msi_free(translator);
4815     msi_free(translator_path);
4816
4817     return r;
4818 }
4819
4820 static UINT ITERATE_InstallODBCDataSource( MSIRECORD *rec, LPVOID param )
4821 {
4822     LPWSTR attrs;
4823     LPCWSTR desc, driver;
4824     WORD request = ODBC_ADD_SYS_DSN;
4825     INT registration;
4826     DWORD len;
4827     UINT r = ERROR_SUCCESS;
4828
4829     static const WCHAR attrs_fmt[] = {
4830         'D','S','N','=','%','s',0 };
4831
4832     desc = MSI_RecordGetString(rec, 3);
4833     driver = MSI_RecordGetString(rec, 4);
4834     registration = MSI_RecordGetInteger(rec, 5);
4835
4836     if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_ADD_SYS_DSN;
4837     else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_ADD_DSN;
4838
4839     len = lstrlenW(attrs_fmt) + lstrlenW(desc) + 1 + 1;
4840     attrs = msi_alloc(len * sizeof(WCHAR));
4841     if (!attrs)
4842         return ERROR_OUTOFMEMORY;
4843
4844     sprintfW(attrs, attrs_fmt, desc);
4845     attrs[len - 1] = '\0';
4846
4847     if (!SQLConfigDataSourceW(NULL, request, driver, attrs))
4848     {
4849         ERR("Failed to install SQL data source!\n");
4850         r = ERROR_FUNCTION_FAILED;
4851     }
4852
4853     msi_free(attrs);
4854
4855     return r;
4856 }
4857
4858 static UINT ACTION_InstallODBC( MSIPACKAGE *package )
4859 {
4860     UINT rc;
4861     MSIQUERY *view;
4862
4863     static const WCHAR driver_query[] = {
4864         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4865         'O','D','B','C','D','r','i','v','e','r',0 };
4866
4867     static const WCHAR translator_query[] = {
4868         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4869         'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0 };
4870
4871     static const WCHAR source_query[] = {
4872         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4873         'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0 };
4874
4875     rc = MSI_DatabaseOpenViewW(package->db, driver_query, &view);
4876     if (rc != ERROR_SUCCESS)
4877         return ERROR_SUCCESS;
4878
4879     rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDriver, package);
4880     msiobj_release(&view->hdr);
4881
4882     rc = MSI_DatabaseOpenViewW(package->db, translator_query, &view);
4883     if (rc != ERROR_SUCCESS)
4884         return ERROR_SUCCESS;
4885
4886     rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCTranslator, package);
4887     msiobj_release(&view->hdr);
4888
4889     rc = MSI_DatabaseOpenViewW(package->db, source_query, &view);
4890     if (rc != ERROR_SUCCESS)
4891         return ERROR_SUCCESS;
4892
4893     rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDataSource, package);
4894     msiobj_release(&view->hdr);
4895
4896     return rc;
4897 }
4898
4899 #define ENV_ACT_SETALWAYS   0x1
4900 #define ENV_ACT_SETABSENT   0x2
4901 #define ENV_ACT_REMOVE      0x4
4902 #define ENV_ACT_REMOVEMATCH 0x8
4903
4904 #define ENV_MOD_MACHINE     0x20000000
4905 #define ENV_MOD_APPEND      0x40000000
4906 #define ENV_MOD_PREFIX      0x80000000
4907 #define ENV_MOD_MASK        0xC0000000
4908
4909 #define check_flag_combo(x, y) ((x) & ~(y)) == (y)
4910
4911 static LONG env_set_flags( LPCWSTR *name, LPCWSTR *value, DWORD *flags )
4912 {
4913     LPCWSTR cptr = *name;
4914     LPCWSTR ptr = *value;
4915
4916     static const WCHAR prefix[] = {'[','~',']',0};
4917     static const int prefix_len = 3;
4918
4919     *flags = 0;
4920     while (*cptr)
4921     {
4922         if (*cptr == '=')
4923             *flags |= ENV_ACT_SETALWAYS;
4924         else if (*cptr == '+')
4925             *flags |= ENV_ACT_SETABSENT;
4926         else if (*cptr == '-')
4927             *flags |= ENV_ACT_REMOVE;
4928         else if (*cptr == '!')
4929             *flags |= ENV_ACT_REMOVEMATCH;
4930         else if (*cptr == '*')
4931             *flags |= ENV_MOD_MACHINE;
4932         else
4933             break;
4934
4935         cptr++;
4936         (*name)++;
4937     }
4938
4939     if (!*cptr)
4940     {
4941         ERR("Missing environment variable\n");
4942         return ERROR_FUNCTION_FAILED;
4943     }
4944
4945     if (!strncmpW(ptr, prefix, prefix_len))
4946     {
4947         *flags |= ENV_MOD_APPEND;
4948         *value += lstrlenW(prefix);
4949     }
4950     else if (lstrlenW(*value) >= prefix_len)
4951     {
4952         ptr += lstrlenW(ptr) - prefix_len;
4953         if (!lstrcmpW(ptr, prefix))
4954         {
4955             *flags |= ENV_MOD_PREFIX;
4956             /* the "[~]" will be removed by deformat_string */;
4957         }
4958     }
4959
4960     if (!*flags ||
4961         check_flag_combo(*flags, ENV_ACT_SETALWAYS | ENV_ACT_SETABSENT) ||
4962         check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETABSENT) ||
4963         check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETALWAYS) ||
4964         check_flag_combo(*flags, ENV_ACT_SETABSENT | ENV_MOD_MASK))
4965     {
4966         ERR("Invalid flags: %08x\n", *flags);
4967         return ERROR_FUNCTION_FAILED;
4968     }
4969
4970     return ERROR_SUCCESS;
4971 }
4972
4973 static UINT ITERATE_WriteEnvironmentString( MSIRECORD *rec, LPVOID param )
4974 {
4975     MSIPACKAGE *package = param;
4976     LPCWSTR name, value, comp;
4977     LPWSTR data = NULL, newval = NULL;
4978     LPWSTR deformatted = NULL, ptr;
4979     DWORD flags, type, size;
4980     LONG res;
4981     HKEY env = NULL, root;
4982     LPCWSTR environment;
4983
4984     static const WCHAR user_env[] =
4985         {'E','n','v','i','r','o','n','m','e','n','t',0};
4986     static const WCHAR machine_env[] =
4987         {'S','y','s','t','e','m','\\',
4988          'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
4989          'C','o','n','t','r','o','l','\\',
4990          'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r','\\',
4991          'E','n','v','i','r','o','n','m','e','n','t',0};
4992     static const WCHAR semicolon[] = {';',0};
4993
4994     name = MSI_RecordGetString(rec, 2);
4995     value = MSI_RecordGetString(rec, 3);
4996     comp = MSI_RecordGetString(rec, 4);
4997
4998     res = env_set_flags(&name, &value, &flags);
4999     if (res != ERROR_SUCCESS)
5000        goto done;
5001
5002     deformat_string(package, value, &deformatted);
5003     if (!deformatted)
5004     {
5005         res = ERROR_OUTOFMEMORY;
5006         goto done;
5007     }
5008
5009     value = deformatted;
5010
5011     if (flags & ENV_MOD_MACHINE)
5012     {
5013         environment = machine_env;
5014         root = HKEY_LOCAL_MACHINE;
5015     }
5016     else
5017     {
5018         environment = user_env;
5019         root = HKEY_CURRENT_USER;
5020     }
5021
5022     res = RegCreateKeyExW(root, environment, 0, NULL, 0,
5023                           KEY_ALL_ACCESS, NULL, &env, NULL);
5024     if (res != ERROR_SUCCESS)
5025         goto done;
5026
5027     if (flags & ENV_ACT_REMOVE)
5028         FIXME("Not removing environment variable on uninstall!\n");
5029
5030     size = 0;
5031     res = RegQueryValueExW(env, name, NULL, &type, NULL, &size);
5032     if ((res != ERROR_SUCCESS && res != ERROR_FILE_NOT_FOUND) ||
5033         (res == ERROR_SUCCESS && type != REG_SZ && type != REG_EXPAND_SZ))
5034         goto done;
5035
5036     if (res != ERROR_FILE_NOT_FOUND)
5037     {
5038         if (flags & ENV_ACT_SETABSENT)
5039         {
5040             res = ERROR_SUCCESS;
5041             goto done;
5042         }
5043
5044         data = msi_alloc(size);
5045         if (!data)
5046         {
5047             RegCloseKey(env);
5048             return ERROR_OUTOFMEMORY;
5049         }
5050
5051         res = RegQueryValueExW(env, name, NULL, &type, (LPVOID)data, &size);
5052         if (res != ERROR_SUCCESS)
5053             goto done;
5054
5055         if (flags & ENV_ACT_REMOVEMATCH && (!value || !lstrcmpW(data, value)))
5056         {
5057             res = RegDeleteKeyW(env, name);
5058             goto done;
5059         }
5060
5061         size =  (lstrlenW(value) + 1 + size) * sizeof(WCHAR);
5062         newval =  msi_alloc(size);
5063         ptr = newval;
5064         if (!newval)
5065         {
5066             res = ERROR_OUTOFMEMORY;
5067             goto done;
5068         }
5069
5070         if (!(flags & ENV_MOD_MASK))
5071             lstrcpyW(newval, value);
5072         else
5073         {
5074             if (flags & ENV_MOD_PREFIX)
5075             {
5076                 lstrcpyW(newval, value);
5077                 lstrcatW(newval, semicolon);
5078                 ptr = newval + lstrlenW(value) + 1;
5079             }
5080
5081             lstrcpyW(ptr, data);
5082
5083             if (flags & ENV_MOD_APPEND)
5084             {
5085                 lstrcatW(newval, semicolon);
5086                 lstrcatW(newval, value);
5087             }
5088         }
5089     }
5090     else
5091     {
5092         size = (lstrlenW(value) + 1) * sizeof(WCHAR);
5093         newval = msi_alloc(size);
5094         if (!newval)
5095         {
5096             res = ERROR_OUTOFMEMORY;
5097             goto done;
5098         }
5099
5100         lstrcpyW(newval, value);
5101     }
5102
5103     TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(newval));
5104     res = RegSetValueExW(env, name, 0, type, (LPVOID)newval, size);
5105
5106 done:
5107     if (env) RegCloseKey(env);
5108     msi_free(deformatted);
5109     msi_free(data);
5110     msi_free(newval);
5111     return res;
5112 }
5113
5114 static UINT ACTION_WriteEnvironmentStrings( MSIPACKAGE *package )
5115 {
5116     UINT rc;
5117     MSIQUERY * view;
5118     static const WCHAR ExecSeqQuery[] =
5119         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5120          '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
5121     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
5122     if (rc != ERROR_SUCCESS)
5123         return ERROR_SUCCESS;
5124
5125     rc = MSI_IterateRecords(view, NULL, ITERATE_WriteEnvironmentString, package);
5126     msiobj_release(&view->hdr);
5127
5128     return rc;
5129 }
5130
5131 #define is_dot_dir(x) ((x[0] == '.') && ((x[1] == 0) || ((x[1] == '.') && (x[2] == 0))))
5132
5133 typedef struct
5134 {
5135     struct list entry;
5136     LPWSTR sourcename;
5137     LPWSTR destname;
5138     LPWSTR source;
5139     LPWSTR dest;
5140 } FILE_LIST;
5141
5142 static BOOL msi_move_file(LPCWSTR source, LPCWSTR dest, int options)
5143 {
5144     BOOL ret;
5145
5146     if (GetFileAttributesW(source) == FILE_ATTRIBUTE_DIRECTORY ||
5147         GetFileAttributesW(dest) == FILE_ATTRIBUTE_DIRECTORY)
5148     {
5149         WARN("Source or dest is directory, not moving\n");
5150         return FALSE;
5151     }
5152
5153     if (options == msidbMoveFileOptionsMove)
5154     {
5155         TRACE("moving %s -> %s\n", debugstr_w(source), debugstr_w(dest));
5156         ret = MoveFileExW(source, dest, MOVEFILE_REPLACE_EXISTING);
5157         if (!ret)
5158         {
5159             WARN("MoveFile failed: %d\n", GetLastError());
5160             return FALSE;
5161         }
5162     }
5163     else
5164     {
5165         TRACE("copying %s -> %s\n", debugstr_w(source), debugstr_w(dest));
5166         ret = CopyFileW(source, dest, FALSE);
5167         if (!ret)
5168         {
5169             WARN("CopyFile failed: %d\n", GetLastError());
5170             return FALSE;
5171         }
5172     }
5173
5174     return TRUE;
5175 }
5176
5177 static LPWSTR wildcard_to_file(LPWSTR wildcard, LPWSTR filename)
5178 {
5179     LPWSTR path, ptr;
5180     DWORD dirlen, pathlen;
5181
5182     ptr = strrchrW(wildcard, '\\');
5183     dirlen = ptr - wildcard + 1;
5184
5185     pathlen = dirlen + lstrlenW(filename) + 1;
5186     path = msi_alloc(pathlen * sizeof(WCHAR));
5187
5188     lstrcpynW(path, wildcard, dirlen + 1);
5189     lstrcatW(path, filename);
5190
5191     return path;
5192 }
5193
5194 static void free_file_entry(FILE_LIST *file)
5195 {
5196     msi_free(file->source);
5197     msi_free(file->dest);
5198     msi_free(file);
5199 }
5200
5201 static void free_list(FILE_LIST *list)
5202 {
5203     while (!list_empty(&list->entry))
5204     {
5205         FILE_LIST *file = LIST_ENTRY(list_head(&list->entry), FILE_LIST, entry);
5206
5207         list_remove(&file->entry);
5208         free_file_entry(file);
5209     }
5210 }
5211
5212 static BOOL add_wildcard(FILE_LIST *files, LPWSTR source, LPWSTR dest)
5213 {
5214     FILE_LIST *new, *file;
5215     LPWSTR ptr, filename;
5216     DWORD size;
5217
5218     new = msi_alloc_zero(sizeof(FILE_LIST));
5219     if (!new)
5220         return FALSE;
5221
5222     new->source = strdupW(source);
5223     ptr = strrchrW(dest, '\\') + 1;
5224     filename = strrchrW(new->source, '\\') + 1;
5225
5226     new->sourcename = filename;
5227
5228     if (*ptr)
5229         new->destname = ptr;
5230     else
5231         new->destname = new->sourcename;
5232
5233     size = (ptr - dest) + lstrlenW(filename) + 1;
5234     new->dest = msi_alloc(size * sizeof(WCHAR));
5235     if (!new->dest)
5236     {
5237         free_file_entry(new);
5238         return FALSE;
5239     }
5240
5241     lstrcpynW(new->dest, dest, ptr - dest + 1);
5242     lstrcatW(new->dest, filename);
5243
5244     if (list_empty(&files->entry))
5245     {
5246         list_add_head(&files->entry, &new->entry);
5247         return TRUE;
5248     }
5249
5250     LIST_FOR_EACH_ENTRY(file, &files->entry, FILE_LIST, entry)
5251     {
5252         if (lstrcmpW(source, file->source) < 0)
5253         {
5254             list_add_before(&file->entry, &new->entry);
5255             return TRUE;
5256         }
5257     }
5258
5259     list_add_after(&file->entry, &new->entry);
5260     return TRUE;
5261 }
5262
5263 static BOOL move_files_wildcard(LPWSTR source, LPWSTR dest, int options)
5264 {
5265     WIN32_FIND_DATAW wfd;
5266     HANDLE hfile;
5267     LPWSTR path;
5268     BOOL res;
5269     FILE_LIST files, *file;
5270     DWORD size;
5271
5272     hfile = FindFirstFileW(source, &wfd);
5273     if (hfile == INVALID_HANDLE_VALUE) return FALSE;
5274
5275     list_init(&files.entry);
5276
5277     for (res = TRUE; res; res = FindNextFileW(hfile, &wfd))
5278     {
5279         if (is_dot_dir(wfd.cFileName)) continue;
5280
5281         path = wildcard_to_file(source, wfd.cFileName);
5282         if (!path)
5283         {
5284             res = FALSE;
5285             goto done;
5286         }
5287
5288         add_wildcard(&files, path, dest);
5289         msi_free(path);
5290     }
5291
5292     /* only the first wildcard match gets renamed to dest */
5293     file = LIST_ENTRY(list_head(&files.entry), FILE_LIST, entry);
5294     size = (strrchrW(file->dest, '\\') - file->dest) + lstrlenW(file->destname) + 2;
5295     file->dest = msi_realloc(file->dest, size * sizeof(WCHAR));
5296     if (!file->dest)
5297     {
5298         res = FALSE;
5299         goto done;
5300     }
5301
5302     lstrcpyW(strrchrW(file->dest, '\\') + 1, file->destname);
5303
5304     while (!list_empty(&files.entry))
5305     {
5306         file = LIST_ENTRY(list_head(&files.entry), FILE_LIST, entry);
5307
5308         msi_move_file((LPCWSTR)file->source, (LPCWSTR)file->dest, options);
5309
5310         list_remove(&file->entry);
5311         free_file_entry(file);
5312     }
5313
5314     res = TRUE;
5315
5316 done:
5317     free_list(&files);
5318     FindClose(hfile);
5319     return res;
5320 }
5321
5322 static UINT ITERATE_MoveFiles( MSIRECORD *rec, LPVOID param )
5323 {
5324     MSIPACKAGE *package = param;
5325     MSICOMPONENT *comp;
5326     LPCWSTR sourcename, destname;
5327     LPWSTR sourcedir = NULL, destdir = NULL;
5328     LPWSTR source = NULL, dest = NULL;
5329     int options;
5330     DWORD size;
5331     BOOL ret, wildcards;
5332
5333     static const WCHAR backslash[] = {'\\',0};
5334
5335     comp = get_loaded_component(package, MSI_RecordGetString(rec, 2));
5336     if (!comp || !comp->Enabled ||
5337         !(comp->Action & (INSTALLSTATE_LOCAL | INSTALLSTATE_SOURCE)))
5338     {
5339         TRACE("Component not set for install, not moving file\n");
5340         return ERROR_SUCCESS;
5341     }
5342
5343     sourcename = MSI_RecordGetString(rec, 3);
5344     destname = MSI_RecordGetString(rec, 4);
5345     options = MSI_RecordGetInteger(rec, 7);
5346
5347     sourcedir = msi_dup_property(package, MSI_RecordGetString(rec, 5));
5348     if (!sourcedir)
5349         goto done;
5350
5351     destdir = msi_dup_property(package, MSI_RecordGetString(rec, 6));
5352     if (!destdir)
5353         goto done;
5354
5355     if (!sourcename)
5356     {
5357         if (GetFileAttributesW(sourcedir) == INVALID_FILE_ATTRIBUTES)
5358             goto done;
5359
5360         source = strdupW(sourcedir);
5361         if (!source)
5362             goto done;
5363     }
5364     else
5365     {
5366         size = lstrlenW(sourcedir) + lstrlenW(sourcename) + 2;
5367         source = msi_alloc(size * sizeof(WCHAR));
5368         if (!source)
5369             goto done;
5370
5371         lstrcpyW(source, sourcedir);
5372         if (source[lstrlenW(source) - 1] != '\\')
5373             lstrcatW(source, backslash);
5374         lstrcatW(source, sourcename);
5375     }
5376
5377     wildcards = strchrW(source, '*') || strchrW(source, '?');
5378
5379     if (!destname && !wildcards)
5380     {
5381         destname = strdupW(sourcename);
5382         if (!destname)
5383             goto done;
5384     }
5385
5386     size = 0;
5387     if (destname)
5388         size = lstrlenW(destname);
5389
5390     size += lstrlenW(destdir) + 2;
5391     dest = msi_alloc(size * sizeof(WCHAR));
5392     if (!dest)
5393         goto done;
5394
5395     lstrcpyW(dest, destdir);
5396     if (dest[lstrlenW(dest) - 1] != '\\')
5397         lstrcatW(dest, backslash);
5398
5399     if (destname)
5400         lstrcatW(dest, destname);
5401
5402     if (GetFileAttributesW(destdir) == INVALID_FILE_ATTRIBUTES)
5403     {
5404         ret = CreateDirectoryW(destdir, NULL);
5405         if (!ret)
5406         {
5407             WARN("CreateDirectory failed: %d\n", GetLastError());
5408             return ERROR_SUCCESS;
5409         }
5410     }
5411
5412     if (!wildcards)
5413         msi_move_file(source, dest, options);
5414     else
5415         move_files_wildcard(source, dest, options);
5416
5417 done:
5418     msi_free(sourcedir);
5419     msi_free(destdir);
5420     msi_free(source);
5421     msi_free(dest);
5422
5423     return ERROR_SUCCESS;
5424 }
5425
5426 static UINT ACTION_MoveFiles( MSIPACKAGE *package )
5427 {
5428     UINT rc;
5429     MSIQUERY *view;
5430
5431     static const WCHAR ExecSeqQuery[] =
5432         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5433          '`','M','o','v','e','F','i','l','e','`',0};
5434
5435     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
5436     if (rc != ERROR_SUCCESS)
5437         return ERROR_SUCCESS;
5438
5439     rc = MSI_IterateRecords(view, NULL, ITERATE_MoveFiles, package);
5440     msiobj_release(&view->hdr);
5441
5442     return rc;
5443 }
5444
5445 static UINT msi_unimplemented_action_stub( MSIPACKAGE *package,
5446                                            LPCSTR action, LPCWSTR table )
5447 {
5448     static const WCHAR query[] = {
5449         'S','E','L','E','C','T',' ','*',' ',
5450         'F','R','O','M',' ','`','%','s','`',0 };
5451     MSIQUERY *view = NULL;
5452     DWORD count = 0;
5453     UINT r;
5454     
5455     r = MSI_OpenQuery( package->db, &view, query, table );
5456     if (r == ERROR_SUCCESS)
5457     {
5458         r = MSI_IterateRecords(view, &count, NULL, package);
5459         msiobj_release(&view->hdr);
5460     }
5461
5462     if (count)
5463         FIXME("%s -> %u ignored %s table values\n",
5464               action, count, debugstr_w(table));
5465
5466     return ERROR_SUCCESS;
5467 }
5468
5469 static UINT ACTION_AllocateRegistrySpace( MSIPACKAGE *package )
5470 {
5471     TRACE("%p\n", package);
5472     return ERROR_SUCCESS;
5473 }
5474
5475 static UINT ACTION_RemoveIniValues( MSIPACKAGE *package )
5476 {
5477     static const WCHAR table[] =
5478          {'R','e','m','o','v','e','I','n','i','F','i','l','e',0 };
5479     return msi_unimplemented_action_stub( package, "RemoveIniValues", table );
5480 }
5481
5482 static UINT ACTION_PatchFiles( MSIPACKAGE *package )
5483 {
5484     static const WCHAR table[] = { 'P','a','t','c','h',0 };
5485     return msi_unimplemented_action_stub( package, "PatchFiles", table );
5486 }
5487
5488 static UINT ACTION_BindImage( MSIPACKAGE *package )
5489 {
5490     static const WCHAR table[] = { 'B','i','n','d','I','m','a','g','e',0 };
5491     return msi_unimplemented_action_stub( package, "BindImage", table );
5492 }
5493
5494 static UINT ACTION_IsolateComponents( MSIPACKAGE *package )
5495 {
5496     static const WCHAR table[] = {
5497         'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t',0 };
5498     return msi_unimplemented_action_stub( package, "IsolateComponents", table );
5499 }
5500
5501 static UINT ACTION_MigrateFeatureStates( MSIPACKAGE *package )
5502 {
5503     static const WCHAR table[] = { 'U','p','g','r','a','d','e',0 };
5504     return msi_unimplemented_action_stub( package, "MigrateFeatureStates", table );
5505 }
5506
5507 static UINT ACTION_SelfUnregModules( MSIPACKAGE *package )
5508 {
5509     static const WCHAR table[] = { 'S','e','l','f','R','e','g',0 };
5510     return msi_unimplemented_action_stub( package, "SelfUnregModules", table );
5511 }
5512
5513 static UINT ACTION_StopServices( MSIPACKAGE *package )
5514 {
5515     static const WCHAR table[] = {
5516         'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
5517     return msi_unimplemented_action_stub( package, "StopServices", table );
5518 }
5519
5520 static UINT ACTION_DeleteServices( MSIPACKAGE *package )
5521 {
5522     static const WCHAR table[] = {
5523         'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
5524     return msi_unimplemented_action_stub( package, "DeleteServices", table );
5525 }
5526 static UINT ACTION_ValidateProductID( MSIPACKAGE *package )
5527 {
5528         static const WCHAR table[] = {
5529                 'P','r','o','d','u','c','t','I','D',0 };
5530         return msi_unimplemented_action_stub( package, "ValidateProductID", table );
5531 }
5532
5533 static UINT ACTION_RemoveEnvironmentStrings( MSIPACKAGE *package )
5534 {
5535     static const WCHAR table[] = {
5536         'E','n','v','i','r','o','n','m','e','n','t',0 };
5537     return msi_unimplemented_action_stub( package, "RemoveEnvironmentStrings", table );
5538 }
5539
5540 static UINT ACTION_MsiPublishAssemblies( MSIPACKAGE *package )
5541 {
5542     static const WCHAR table[] = {
5543         'M','s','i','A','s','s','e','m','b','l','y',0 };
5544     return msi_unimplemented_action_stub( package, "MsiPublishAssemblies", table );
5545 }
5546
5547 static UINT ACTION_MsiUnpublishAssemblies( MSIPACKAGE *package )
5548 {
5549     static const WCHAR table[] = {
5550         'M','s','i','A','s','s','e','m','b','l','y',0 };
5551     return msi_unimplemented_action_stub( package, "MsiUnpublishAssemblies", table );
5552 }
5553
5554 static UINT ACTION_UnregisterFonts( MSIPACKAGE *package )
5555 {
5556     static const WCHAR table[] = { 'F','o','n','t',0 };
5557     return msi_unimplemented_action_stub( package, "UnregisterFonts", table );
5558 }
5559
5560 static UINT ACTION_RMCCPSearch( MSIPACKAGE *package )
5561 {
5562     static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
5563     return msi_unimplemented_action_stub( package, "RMCCPSearch", table );
5564 }
5565
5566 static UINT ACTION_RegisterComPlus( MSIPACKAGE *package )
5567 {
5568     static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
5569     return msi_unimplemented_action_stub( package, "RegisterComPlus", table );
5570 }
5571
5572 static UINT ACTION_UnregisterComPlus( MSIPACKAGE *package )
5573 {
5574     static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
5575     return msi_unimplemented_action_stub( package, "UnregisterComPlus", table );
5576 }
5577
5578 static UINT ACTION_InstallSFPCatalogFile( MSIPACKAGE *package )
5579 {
5580     static const WCHAR table[] = { 'S','F','P','C','a','t','a','l','o','g',0 };
5581     return msi_unimplemented_action_stub( package, "InstallSFPCatalogFile", table );
5582 }
5583
5584 static UINT ACTION_RemoveDuplicateFiles( MSIPACKAGE *package )
5585 {
5586     static const WCHAR table[] = { 'D','u','p','l','i','c','a','t','e','F','i','l','e',0 };
5587     return msi_unimplemented_action_stub( package, "RemoveDuplicateFiles", table );
5588 }
5589
5590 static UINT ACTION_RemoveExistingProducts( MSIPACKAGE *package )
5591 {
5592     static const WCHAR table[] = { 'U','p','g','r','a','d','e',0 };
5593     return msi_unimplemented_action_stub( package, "RemoveExistingProducts", table );
5594 }
5595
5596 static UINT ACTION_RemoveFolders( MSIPACKAGE *package )
5597 {
5598     static const WCHAR table[] = { 'C','r','e','a','t','e','F','o','l','d','e','r',0 };
5599     return msi_unimplemented_action_stub( package, "RemoveFolders", table );
5600 }
5601
5602 static UINT ACTION_RemoveODBC( MSIPACKAGE *package )
5603 {
5604     static const WCHAR table[] = { 'O','D','B','C','D','r','i','v','e','r',0 };
5605     return msi_unimplemented_action_stub( package, "RemoveODBC", table );
5606 }
5607
5608 static UINT ACTION_RemoveRegistryValues( MSIPACKAGE *package )
5609 {
5610     static const WCHAR table[] = { 'R','e','m','o','v','e','R','e','g','i','s','t','r','y',0 };
5611     return msi_unimplemented_action_stub( package, "RemoveRegistryValues", table );
5612 }
5613
5614 static UINT ACTION_RemoveShortcuts( MSIPACKAGE *package )
5615 {
5616     static const WCHAR table[] = { 'S','h','o','r','t','c','u','t',0 };
5617     return msi_unimplemented_action_stub( package, "RemoveShortcuts", table );
5618 }
5619
5620 static UINT ACTION_UnpublishComponents( MSIPACKAGE *package )
5621 {
5622     static const WCHAR table[] = { 'P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t',0 };
5623     return msi_unimplemented_action_stub( package, "UnpublishComponents", table );
5624 }
5625
5626 static UINT ACTION_UnregisterClassInfo( MSIPACKAGE *package )
5627 {
5628     static const WCHAR table[] = { 'A','p','p','I','d',0 };
5629     return msi_unimplemented_action_stub( package, "UnregisterClassInfo", table );
5630 }
5631
5632 static UINT ACTION_UnregisterExtensionInfo( MSIPACKAGE *package )
5633 {
5634     static const WCHAR table[] = { 'E','x','t','e','n','s','i','o','n',0 };
5635     return msi_unimplemented_action_stub( package, "UnregisterExtensionInfo", table );
5636 }
5637
5638 static UINT ACTION_UnregisterMIMEInfo( MSIPACKAGE *package )
5639 {
5640     static const WCHAR table[] = { 'M','I','M','E',0 };
5641     return msi_unimplemented_action_stub( package, "UnregisterMIMEInfo", table );
5642 }
5643
5644 static UINT ACTION_UnregisterProgIdInfo( MSIPACKAGE *package )
5645 {
5646     static const WCHAR table[] = { 'P','r','o','g','I','d',0 };
5647     return msi_unimplemented_action_stub( package, "UnregisterProgIdInfo", table );
5648 }
5649
5650 static UINT ACTION_UnregisterTypeLibraries( MSIPACKAGE *package )
5651 {
5652     static const WCHAR table[] = { 'T','y','p','e','L','i','b',0 };
5653     return msi_unimplemented_action_stub( package, "UnregisterTypeLibraries", table );
5654 }
5655
5656 static const struct _actions StandardActions[] = {
5657     { szAllocateRegistrySpace, ACTION_AllocateRegistrySpace },
5658     { szAppSearch, ACTION_AppSearch },
5659     { szBindImage, ACTION_BindImage },
5660     { szCCPSearch, ACTION_CCPSearch },
5661     { szCostFinalize, ACTION_CostFinalize },
5662     { szCostInitialize, ACTION_CostInitialize },
5663     { szCreateFolders, ACTION_CreateFolders },
5664     { szCreateShortcuts, ACTION_CreateShortcuts },
5665     { szDeleteServices, ACTION_DeleteServices },
5666     { szDisableRollback, NULL },
5667     { szDuplicateFiles, ACTION_DuplicateFiles },
5668     { szExecuteAction, ACTION_ExecuteAction },
5669     { szFileCost, ACTION_FileCost },
5670     { szFindRelatedProducts, ACTION_FindRelatedProducts },
5671     { szForceReboot, ACTION_ForceReboot },
5672     { szInstallAdminPackage, NULL },
5673     { szInstallExecute, ACTION_InstallExecute },
5674     { szInstallExecuteAgain, ACTION_InstallExecute },
5675     { szInstallFiles, ACTION_InstallFiles},
5676     { szInstallFinalize, ACTION_InstallFinalize },
5677     { szInstallInitialize, ACTION_InstallInitialize },
5678     { szInstallSFPCatalogFile, ACTION_InstallSFPCatalogFile },
5679     { szInstallValidate, ACTION_InstallValidate },
5680     { szIsolateComponents, ACTION_IsolateComponents },
5681     { szLaunchConditions, ACTION_LaunchConditions },
5682     { szMigrateFeatureStates, ACTION_MigrateFeatureStates },
5683     { szMoveFiles, ACTION_MoveFiles },
5684     { szMsiPublishAssemblies, ACTION_MsiPublishAssemblies },
5685     { szMsiUnpublishAssemblies, ACTION_MsiUnpublishAssemblies },
5686     { szInstallODBC, ACTION_InstallODBC },
5687     { szInstallServices, ACTION_InstallServices },
5688     { szPatchFiles, ACTION_PatchFiles },
5689     { szProcessComponents, ACTION_ProcessComponents },
5690     { szPublishComponents, ACTION_PublishComponents },
5691     { szPublishFeatures, ACTION_PublishFeatures },
5692     { szPublishProduct, ACTION_PublishProduct },
5693     { szRegisterClassInfo, ACTION_RegisterClassInfo },
5694     { szRegisterComPlus, ACTION_RegisterComPlus},
5695     { szRegisterExtensionInfo, ACTION_RegisterExtensionInfo },
5696     { szRegisterFonts, ACTION_RegisterFonts },
5697     { szRegisterMIMEInfo, ACTION_RegisterMIMEInfo },
5698     { szRegisterProduct, ACTION_RegisterProduct },
5699     { szRegisterProgIdInfo, ACTION_RegisterProgIdInfo },
5700     { szRegisterTypeLibraries, ACTION_RegisterTypeLibraries },
5701     { szRegisterUser, ACTION_RegisterUser },
5702     { szRemoveDuplicateFiles, ACTION_RemoveDuplicateFiles },
5703     { szRemoveEnvironmentStrings, ACTION_RemoveEnvironmentStrings },
5704     { szRemoveExistingProducts, ACTION_RemoveExistingProducts },
5705     { szRemoveFiles, ACTION_RemoveFiles },
5706     { szRemoveFolders, ACTION_RemoveFolders },
5707     { szRemoveIniValues, ACTION_RemoveIniValues },
5708     { szRemoveODBC, ACTION_RemoveODBC },
5709     { szRemoveRegistryValues, ACTION_RemoveRegistryValues },
5710     { szRemoveShortcuts, ACTION_RemoveShortcuts },
5711     { szResolveSource, ACTION_ResolveSource },
5712     { szRMCCPSearch, ACTION_RMCCPSearch },
5713     { szScheduleReboot, NULL },
5714     { szSelfRegModules, ACTION_SelfRegModules },
5715     { szSelfUnregModules, ACTION_SelfUnregModules },
5716     { szSetODBCFolders, NULL },
5717     { szStartServices, ACTION_StartServices },
5718     { szStopServices, ACTION_StopServices },
5719     { szUnpublishComponents, ACTION_UnpublishComponents },
5720     { szUnpublishFeatures, ACTION_UnpublishFeatures },
5721     { szUnregisterClassInfo, ACTION_UnregisterClassInfo },
5722     { szUnregisterComPlus, ACTION_UnregisterComPlus },
5723     { szUnregisterExtensionInfo, ACTION_UnregisterExtensionInfo },
5724     { szUnregisterFonts, ACTION_UnregisterFonts },
5725     { szUnregisterMIMEInfo, ACTION_UnregisterMIMEInfo },
5726     { szUnregisterProgIdInfo, ACTION_UnregisterProgIdInfo },
5727     { szUnregisterTypeLibraries, ACTION_UnregisterTypeLibraries },
5728     { szValidateProductID, ACTION_ValidateProductID },
5729     { szWriteEnvironmentStrings, ACTION_WriteEnvironmentStrings },
5730     { szWriteIniValues, ACTION_WriteIniValues },
5731     { szWriteRegistryValues, ACTION_WriteRegistryValues },
5732     { NULL, NULL },
5733 };