msi: Fix a memory leak.
[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 /*
22  * Pages I need
23  *
24 http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/installexecutesequence_table.asp
25
26 http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/standard_actions_reference.asp
27  */
28
29 #include <stdarg.h>
30
31 #define COBJMACROS
32
33 #include "windef.h"
34 #include "winbase.h"
35 #include "winerror.h"
36 #include "winreg.h"
37 #include "winsvc.h"
38 #include "wine/debug.h"
39 #include "msidefs.h"
40 #include "msipriv.h"
41 #include "winuser.h"
42 #include "shlobj.h"
43 #include "wine/unicode.h"
44 #include "winver.h"
45
46 #define REG_PROGRESS_VALUE 13200
47 #define COMPONENT_PROGRESS_VALUE 24000
48
49 WINE_DEFAULT_DEBUG_CHANNEL(msi);
50
51 /*
52  * Prototypes
53  */
54 static UINT ACTION_ProcessExecSequence(MSIPACKAGE *package, BOOL UIran);
55 static UINT ACTION_ProcessUISequence(MSIPACKAGE *package);
56 static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq, BOOL UI);
57
58 /*
59  * consts and values used
60  */
61 static const WCHAR c_colon[] = {'C',':','\\',0};
62
63 static const WCHAR szCreateFolders[] =
64     {'C','r','e','a','t','e','F','o','l','d','e','r','s',0};
65 static const WCHAR szCostFinalize[] =
66     {'C','o','s','t','F','i','n','a','l','i','z','e',0};
67 const WCHAR szInstallFiles[] =
68     {'I','n','s','t','a','l','l','F','i','l','e','s',0};
69 const WCHAR szDuplicateFiles[] =
70     {'D','u','p','l','i','c','a','t','e','F','i','l','e','s',0};
71 static const WCHAR szWriteRegistryValues[] =
72     {'W','r','i','t','e','R','e','g','i','s','t','r','y',
73             'V','a','l','u','e','s',0};
74 static const WCHAR szCostInitialize[] =
75     {'C','o','s','t','I','n','i','t','i','a','l','i','z','e',0};
76 static const WCHAR szFileCost[] = 
77     {'F','i','l','e','C','o','s','t',0};
78 static const WCHAR szInstallInitialize[] = 
79     {'I','n','s','t','a','l','l','I','n','i','t','i','a','l','i','z','e',0};
80 static const WCHAR szInstallValidate[] = 
81     {'I','n','s','t','a','l','l','V','a','l','i','d','a','t','e',0};
82 static const WCHAR szLaunchConditions[] = 
83     {'L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','s',0};
84 static const WCHAR szProcessComponents[] = 
85     {'P','r','o','c','e','s','s','C','o','m','p','o','n','e','n','t','s',0};
86 static const WCHAR szRegisterTypeLibraries[] = 
87     {'R','e','g','i','s','t','e','r','T','y','p','e',
88             'L','i','b','r','a','r','i','e','s',0};
89 const WCHAR szRegisterClassInfo[] = 
90     {'R','e','g','i','s','t','e','r','C','l','a','s','s','I','n','f','o',0};
91 const WCHAR szRegisterProgIdInfo[] = 
92     {'R','e','g','i','s','t','e','r','P','r','o','g','I','d','I','n','f','o',0};
93 static const WCHAR szCreateShortcuts[] = 
94     {'C','r','e','a','t','e','S','h','o','r','t','c','u','t','s',0};
95 static const WCHAR szPublishProduct[] = 
96     {'P','u','b','l','i','s','h','P','r','o','d','u','c','t',0};
97 static const WCHAR szWriteIniValues[] = 
98     {'W','r','i','t','e','I','n','i','V','a','l','u','e','s',0};
99 static const WCHAR szSelfRegModules[] = 
100     {'S','e','l','f','R','e','g','M','o','d','u','l','e','s',0};
101 static const WCHAR szPublishFeatures[] = 
102     {'P','u','b','l','i','s','h','F','e','a','t','u','r','e','s',0};
103 static const WCHAR szRegisterProduct[] = 
104     {'R','e','g','i','s','t','e','r','P','r','o','d','u','c','t',0};
105 static const WCHAR szInstallExecute[] = 
106     {'I','n','s','t','a','l','l','E','x','e','c','u','t','e',0};
107 static const WCHAR szInstallExecuteAgain[] = 
108     {'I','n','s','t','a','l','l','E','x','e','c','u','t','e',
109             'A','g','a','i','n',0};
110 static const WCHAR szInstallFinalize[] = 
111     {'I','n','s','t','a','l','l','F','i','n','a','l','i','z','e',0};
112 static const WCHAR szForceReboot[] = 
113     {'F','o','r','c','e','R','e','b','o','o','t',0};
114 static const WCHAR szResolveSource[] =
115     {'R','e','s','o','l','v','e','S','o','u','r','c','e',0};
116 static const WCHAR szAppSearch[] = 
117     {'A','p','p','S','e','a','r','c','h',0};
118 static const WCHAR szAllocateRegistrySpace[] = 
119     {'A','l','l','o','c','a','t','e','R','e','g','i','s','t','r','y',
120             'S','p','a','c','e',0};
121 static const WCHAR szBindImage[] = 
122     {'B','i','n','d','I','m','a','g','e',0};
123 static const WCHAR szCCPSearch[] = 
124     {'C','C','P','S','e','a','r','c','h',0};
125 static const WCHAR szDeleteServices[] = 
126     {'D','e','l','e','t','e','S','e','r','v','i','c','e','s',0};
127 static const WCHAR szDisableRollback[] = 
128     {'D','i','s','a','b','l','e','R','o','l','l','b','a','c','k',0};
129 static const WCHAR szExecuteAction[] = 
130     {'E','x','e','c','u','t','e','A','c','t','i','o','n',0};
131 const WCHAR szFindRelatedProducts[] = 
132     {'F','i','n','d','R','e','l','a','t','e','d',
133             'P','r','o','d','u','c','t','s',0};
134 static const WCHAR szInstallAdminPackage[] = 
135     {'I','n','s','t','a','l','l','A','d','m','i','n',
136             'P','a','c','k','a','g','e',0};
137 static const WCHAR szInstallSFPCatalogFile[] = 
138     {'I','n','s','t','a','l','l','S','F','P','C','a','t','a','l','o','g',
139             'F','i','l','e',0};
140 static const WCHAR szIsolateComponents[] = 
141     {'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t','s',0};
142 const WCHAR szMigrateFeatureStates[] = 
143     {'M','i','g','r','a','t','e','F','e','a','t','u','r','e',
144             'S','t','a','t','e','s',0};
145 const WCHAR szMoveFiles[] = 
146     {'M','o','v','e','F','i','l','e','s',0};
147 static const WCHAR szMsiPublishAssemblies[] = 
148     {'M','s','i','P','u','b','l','i','s','h',
149             'A','s','s','e','m','b','l','i','e','s',0};
150 static const WCHAR szMsiUnpublishAssemblies[] = 
151     {'M','s','i','U','n','p','u','b','l','i','s','h',
152             'A','s','s','e','m','b','l','i','e','s',0};
153 static const WCHAR szInstallODBC[] = 
154     {'I','n','s','t','a','l','l','O','D','B','C',0};
155 static const WCHAR szInstallServices[] = 
156     {'I','n','s','t','a','l','l','S','e','r','v','i','c','e','s',0};
157 const WCHAR szPatchFiles[] = 
158     {'P','a','t','c','h','F','i','l','e','s',0};
159 static const WCHAR szPublishComponents[] = 
160     {'P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','s',0};
161 static const WCHAR szRegisterComPlus[] =
162     {'R','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0};
163 const WCHAR szRegisterExtensionInfo[] =
164     {'R','e','g','i','s','t','e','r','E','x','t','e','n','s','i','o','n',
165             'I','n','f','o',0};
166 static const WCHAR szRegisterFonts[] =
167     {'R','e','g','i','s','t','e','r','F','o','n','t','s',0};
168 const WCHAR szRegisterMIMEInfo[] =
169     {'R','e','g','i','s','t','e','r','M','I','M','E','I','n','f','o',0};
170 static const WCHAR szRegisterUser[] =
171     {'R','e','g','i','s','t','e','r','U','s','e','r',0};
172 const WCHAR szRemoveDuplicateFiles[] =
173     {'R','e','m','o','v','e','D','u','p','l','i','c','a','t','e',
174             'F','i','l','e','s',0};
175 static const WCHAR szRemoveEnvironmentStrings[] =
176     {'R','e','m','o','v','e','E','n','v','i','r','o','n','m','e','n','t',
177             'S','t','r','i','n','g','s',0};
178 const WCHAR szRemoveExistingProducts[] =
179     {'R','e','m','o','v','e','E','x','i','s','t','i','n','g',
180             'P','r','o','d','u','c','t','s',0};
181 const WCHAR szRemoveFiles[] =
182     {'R','e','m','o','v','e','F','i','l','e','s',0};
183 static const WCHAR szRemoveFolders[] =
184     {'R','e','m','o','v','e','F','o','l','d','e','r','s',0};
185 static const WCHAR szRemoveIniValues[] =
186     {'R','e','m','o','v','e','I','n','i','V','a','l','u','e','s',0};
187 static const WCHAR szRemoveODBC[] =
188     {'R','e','m','o','v','e','O','D','B','C',0};
189 static const WCHAR szRemoveRegistryValues[] =
190     {'R','e','m','o','v','e','R','e','g','i','s','t','r','y',
191             'V','a','l','u','e','s',0};
192 static const WCHAR szRemoveShortcuts[] =
193     {'R','e','m','o','v','e','S','h','o','r','t','c','u','t','s',0};
194 static const WCHAR szRMCCPSearch[] =
195     {'R','M','C','C','P','S','e','a','r','c','h',0};
196 static const WCHAR szScheduleReboot[] =
197     {'S','c','h','e','d','u','l','e','R','e','b','o','o','t',0};
198 static const WCHAR szSelfUnregModules[] =
199     {'S','e','l','f','U','n','r','e','g','M','o','d','u','l','e','s',0};
200 static const WCHAR szSetODBCFolders[] =
201     {'S','e','t','O','D','B','C','F','o','l','d','e','r','s',0};
202 static const WCHAR szStartServices[] =
203     {'S','t','a','r','t','S','e','r','v','i','c','e','s',0};
204 static const WCHAR szStopServices[] =
205     {'S','t','o','p','S','e','r','v','i','c','e','s',0};
206 static const WCHAR szUnpublishComponents[] =
207     {'U','n','p','u','b','l','i','s','h',
208             'C','o','m','p','o','n','e','n','t','s',0};
209 static const WCHAR szUnpublishFeatures[] =
210     {'U','n','p','u','b','l','i','s','h','F','e','a','t','u','r','e','s',0};
211 const WCHAR szUnregisterClassInfo[] =
212     {'U','n','r','e','g','i','s','t','e','r','C','l','a','s','s',
213             'I','n','f','o',0};
214 static const WCHAR szUnregisterComPlus[] =
215     {'U','n','r','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0};
216 const WCHAR szUnregisterExtensionInfo[] =
217     {'U','n','r','e','g','i','s','t','e','r',
218             'E','x','t','e','n','s','i','o','n','I','n','f','o',0};
219 static const WCHAR szUnregisterFonts[] =
220     {'U','n','r','e','g','i','s','t','e','r','F','o','n','t','s',0};
221 const WCHAR szUnregisterMIMEInfo[] =
222     {'U','n','r','e','g','i','s','t','e','r','M','I','M','E','I','n','f','o',0};
223 const WCHAR szUnregisterProgIdInfo[] =
224     {'U','n','r','e','g','i','s','t','e','r','P','r','o','g','I','d',
225             'I','n','f','o',0};
226 static const WCHAR szUnregisterTypeLibraries[] =
227     {'U','n','r','e','g','i','s','t','e','r','T','y','p','e',
228             'L','i','b','r','a','r','i','e','s',0};
229 static const WCHAR szValidateProductID[] =
230     {'V','a','l','i','d','a','t','e','P','r','o','d','u','c','t','I','D',0};
231 static const WCHAR szWriteEnvironmentStrings[] =
232     {'W','r','i','t','e','E','n','v','i','r','o','n','m','e','n','t',
233             'S','t','r','i','n','g','s',0};
234
235 /* action handlers */
236 typedef UINT (*STANDARDACTIONHANDLER)(MSIPACKAGE*);
237
238 struct _actions {
239     LPCWSTR action;
240     STANDARDACTIONHANDLER handler;
241 };
242
243 static struct _actions StandardActions[];
244
245
246 /********************************************************
247  * helper functions
248  ********************************************************/
249
250 static void ui_actionstart(MSIPACKAGE *package, LPCWSTR action)
251 {
252     static const WCHAR Query_t[] = 
253         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
254          '`','A','c','t','i','o', 'n','T','e','x','t','`',' ',
255          'W','H','E','R','E', ' ','`','A','c','t','i','o','n','`',' ','=', 
256          ' ','\'','%','s','\'',0};
257     MSIRECORD * row;
258
259     row = MSI_QueryGetRecord( package->db, Query_t, action );
260     if (!row)
261         return;
262     MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONSTART, row);
263     msiobj_release(&row->hdr);
264 }
265
266 static void ui_actioninfo(MSIPACKAGE *package, LPCWSTR action, BOOL start, 
267                           UINT rc)
268 {
269     MSIRECORD * row;
270     static const WCHAR template_s[]=
271         {'A','c','t','i','o','n',' ','s','t','a','r','t',' ','%','s',':',' ',
272          '%','s', '.',0};
273     static const WCHAR template_e[]=
274         {'A','c','t','i','o','n',' ','e','n','d','e','d',' ','%','s',':',' ',
275          '%','s', '.',' ','R','e','t','u','r','n',' ','v','a','l','u','e',' ',
276          '%','i','.',0};
277     static const WCHAR format[] = 
278         {'H','H','\'',':','\'','m','m','\'',':','\'','s','s',0};
279     WCHAR message[1024];
280     WCHAR timet[0x100];
281
282     GetTimeFormatW(LOCALE_USER_DEFAULT, 0, NULL, format, timet, 0x100);
283     if (start)
284         sprintfW(message,template_s,timet,action);
285     else
286         sprintfW(message,template_e,timet,action,rc);
287     
288     row = MSI_CreateRecord(1);
289     MSI_RecordSetStringW(row,1,message);
290  
291     MSI_ProcessMessage(package, INSTALLMESSAGE_INFO, row);
292     msiobj_release(&row->hdr);
293 }
294
295 static UINT msi_parse_command_line( MSIPACKAGE *package, LPCWSTR szCommandLine )
296 {
297     LPCWSTR ptr,ptr2;
298     BOOL quote;
299     DWORD len;
300     LPWSTR prop = NULL, val = NULL;
301
302     if (!szCommandLine)
303         return ERROR_SUCCESS;
304
305     ptr = szCommandLine;
306        
307     while (*ptr)
308     {
309         if (*ptr==' ')
310         {
311             ptr++;
312             continue;
313         }
314
315         TRACE("Looking at %s\n",debugstr_w(ptr));
316
317         ptr2 = strchrW(ptr,'=');
318         if (!ptr2)
319         {
320             ERR("command line contains unknown string : %s\n", debugstr_w(ptr));
321             break;
322         }
323  
324         quote = FALSE;
325
326         len = ptr2-ptr;
327         prop = msi_alloc((len+1)*sizeof(WCHAR));
328         memcpy(prop,ptr,len*sizeof(WCHAR));
329         prop[len]=0;
330         ptr2++;
331        
332         len = 0; 
333         ptr = ptr2; 
334         while (*ptr && (quote || (!quote && *ptr!=' ')))
335         {
336             if (*ptr == '"')
337                 quote = !quote;
338             ptr++;
339             len++;
340         }
341        
342         if (*ptr2=='"')
343         {
344             ptr2++;
345             len -= 2;
346         }
347         val = msi_alloc((len+1)*sizeof(WCHAR));
348         memcpy(val,ptr2,len*sizeof(WCHAR));
349         val[len] = 0;
350
351         if (lstrlenW(prop) > 0)
352         {
353             TRACE("Found commandline property (%s) = (%s)\n", 
354                    debugstr_w(prop), debugstr_w(val));
355             MSI_SetPropertyW(package,prop,val);
356         }
357         msi_free(val);
358         msi_free(prop);
359     }
360
361     return ERROR_SUCCESS;
362 }
363
364
365 static LPWSTR* msi_split_string( LPCWSTR str, WCHAR sep )
366 {
367     LPCWSTR pc;
368     LPWSTR p, *ret = NULL;
369     UINT count = 0;
370
371     if (!str)
372         return ret;
373
374     /* count the number of substrings */
375     for ( pc = str, count = 0; pc; count++ )
376     {
377         pc = strchrW( pc, sep );
378         if (pc)
379             pc++;
380     }
381
382     /* allocate space for an array of substring pointers and the substrings */
383     ret = msi_alloc( (count+1) * sizeof (LPWSTR) +
384                      (lstrlenW(str)+1) * sizeof(WCHAR) );
385     if (!ret)
386         return ret;
387
388     /* copy the string and set the pointers */
389     p = (LPWSTR) &ret[count+1];
390     lstrcpyW( p, str );
391     for( count = 0; (ret[count] = p); count++ )
392     {
393         p = strchrW( p, sep );
394         if (p)
395             *p++ = 0;
396     }
397
398     return ret;
399 }
400
401 static UINT msi_check_transform_applicable( MSIPACKAGE *package, IStorage *patch )
402 {
403     WCHAR szProductCode[] = { 'P','r','o','d','u','c','t','C','o','d','e',0 };
404     LPWSTR prod_code, patch_product;
405     UINT ret;
406
407     prod_code = msi_dup_property( package, szProductCode );
408     patch_product = msi_get_suminfo_product( patch );
409
410     TRACE("db = %s patch = %s\n", debugstr_w(prod_code), debugstr_w(patch_product));
411
412     if ( strstrW( patch_product, prod_code ) )
413         ret = ERROR_SUCCESS;
414     else
415         ret = ERROR_FUNCTION_FAILED;
416
417     msi_free( patch_product );
418     msi_free( prod_code );
419
420     return ret;
421 }
422
423 static UINT msi_apply_substorage_transform( MSIPACKAGE *package,
424                                  MSIDATABASE *patch_db, LPCWSTR name )
425 {
426     UINT ret = ERROR_FUNCTION_FAILED;
427     IStorage *stg = NULL;
428     HRESULT r;
429
430     TRACE("%p %s\n", package, debugstr_w(name) );
431
432     if (*name++ != ':')
433     {
434         ERR("expected a colon in %s\n", debugstr_w(name));
435         return ERROR_FUNCTION_FAILED;
436     }
437
438     r = IStorage_OpenStorage( patch_db->storage, name, NULL, STGM_SHARE_EXCLUSIVE, NULL, 0, &stg );
439     if (SUCCEEDED(r))
440     {
441         ret = msi_check_transform_applicable( package, stg );
442         if (ret == ERROR_SUCCESS)
443             msi_table_apply_transform( package->db, stg );
444         else
445             TRACE("substorage transform %s wasn't applicable\n", debugstr_w(name));
446         IStorage_Release( stg );
447     }
448     else
449         ERR("failed to open substorage %s\n", debugstr_w(name));
450
451     return ERROR_SUCCESS;
452 }
453
454 static UINT msi_check_patch_applicable( MSIPACKAGE *package, MSISUMMARYINFO *si )
455 {
456     static const WCHAR szProdID[] = { 'P','r','o','d','u','c','t','I','D',0 };
457     LPWSTR guid_list, *guids, product_id;
458     UINT i, ret = ERROR_FUNCTION_FAILED;
459
460     product_id = msi_dup_property( package, szProdID );
461     if (!product_id)
462     {
463         /* FIXME: the property ProductID should be written into the DB somewhere */
464         ERR("no product ID to check\n");
465         return ERROR_SUCCESS;
466     }
467
468     guid_list = msi_suminfo_dup_string( si, PID_TEMPLATE );
469     guids = msi_split_string( guid_list, ';' );
470     for ( i = 0; guids[i] && ret != ERROR_SUCCESS; i++ )
471     {
472         if (!lstrcmpW( guids[i], product_id ))
473             ret = ERROR_SUCCESS;
474     }
475     msi_free( guids );
476     msi_free( guid_list );
477     msi_free( product_id );
478
479     return ret;
480 }
481
482 static UINT msi_parse_patch_summary( MSIPACKAGE *package, MSIDATABASE *patch_db )
483 {
484     MSISUMMARYINFO *si;
485     LPWSTR str, *substorage;
486     UINT i, r = ERROR_SUCCESS;
487
488     si = MSI_GetSummaryInformationW( patch_db->storage, 0 );
489     if (!si)
490         return ERROR_FUNCTION_FAILED;
491
492     msi_check_patch_applicable( package, si );
493
494     /* enumerate the substorage */
495     str = msi_suminfo_dup_string( si, PID_LASTAUTHOR );
496     substorage = msi_split_string( str, ';' );
497     for ( i = 0; substorage && substorage[i] && r == ERROR_SUCCESS; i++ )
498         r = msi_apply_substorage_transform( package, patch_db, substorage[i] );
499     msi_free( substorage );
500     msi_free( str );
501
502     /* FIXME: parse the sources in PID_REVNUMBER and do something with them... */
503
504     msiobj_release( &si->hdr );
505
506     return r;
507 }
508
509 static UINT msi_apply_patch_package( MSIPACKAGE *package, LPCWSTR file )
510 {
511     MSIDATABASE *patch_db = NULL;
512     UINT r;
513
514     TRACE("%p %s\n", package, debugstr_w( file ) );
515
516     /* FIXME:
517      *  We probably want to make sure we only open a patch collection here.
518      *  Patch collections (.msp) and databases (.msi) have different GUIDs
519      *  but currently MSI_OpenDatabaseW will accept both.
520      */
521     r = MSI_OpenDatabaseW( file, MSIDBOPEN_READONLY, &patch_db );
522     if ( r != ERROR_SUCCESS )
523     {
524         ERR("failed to open patch collection %s\n", debugstr_w( file ) );
525         return r;
526     }
527
528     msi_parse_patch_summary( package, patch_db );
529
530     /*
531      * There might be a CAB file in the patch package,
532      * so append it to the list of storage to search for streams.
533      */
534     append_storage_to_db( package->db, patch_db->storage );
535
536     msiobj_release( &patch_db->hdr );
537
538     return ERROR_SUCCESS;
539 }
540
541 /* get the PATCH property, and apply all the patches it specifies */
542 static UINT msi_apply_patches( MSIPACKAGE *package )
543 {
544     static const WCHAR szPatch[] = { 'P','A','T','C','H',0 };
545     LPWSTR patch_list, *patches;
546     UINT i, r = ERROR_SUCCESS;
547
548     patch_list = msi_dup_property( package, szPatch );
549
550     TRACE("patches to be applied: %s\n", debugstr_w( patch_list ) );
551
552     patches = msi_split_string( patch_list, ';' );
553     for( i=0; patches && patches[i] && r == ERROR_SUCCESS; i++ )
554         r = msi_apply_patch_package( package, patches[i] );
555
556     msi_free( patches );
557     msi_free( patch_list );
558
559     return r;
560 }
561
562 static UINT msi_apply_transforms( MSIPACKAGE *package )
563 {
564     static const WCHAR szTransforms[] = {
565         'T','R','A','N','S','F','O','R','M','S',0 };
566     LPWSTR xform_list, *xforms;
567     UINT i, r = ERROR_SUCCESS;
568
569     xform_list = msi_dup_property( package, szTransforms );
570     xforms = msi_split_string( xform_list, ';' );
571
572     for( i=0; xforms && xforms[i] && r == ERROR_SUCCESS; i++ )
573     {
574         if (xforms[i][0] == ':')
575             r = msi_apply_substorage_transform( package, package->db, &xforms[i][1] );
576         else
577             r = MSI_DatabaseApplyTransformW( package->db, xforms[i], 0 );
578     }
579
580     msi_free( xforms );
581     msi_free( xform_list );
582
583     return r;
584 }
585
586 /****************************************************
587  * TOP level entry points 
588  *****************************************************/
589
590 UINT MSI_InstallPackage( MSIPACKAGE *package, LPCWSTR szPackagePath,
591                          LPCWSTR szCommandLine )
592 {
593     UINT rc;
594     BOOL ui = FALSE;
595     static const WCHAR szUILevel[] = {'U','I','L','e','v','e','l',0};
596     static const WCHAR szAction[] = {'A','C','T','I','O','N',0};
597     static const WCHAR szInstall[] = {'I','N','S','T','A','L','L',0};
598
599     MSI_SetPropertyW(package, szAction, szInstall);
600
601     package->script = msi_alloc_zero(sizeof(MSISCRIPT));
602
603     package->script->InWhatSequence = SEQUENCE_INSTALL;
604
605     if (szPackagePath)   
606     {
607         LPWSTR p, check, path;
608  
609         path = strdupW(szPackagePath);
610         p = strrchrW(path,'\\');    
611         if (p)
612         {
613             p++;
614             *p=0;
615         }
616         else
617         {
618             msi_free(path);
619             path = msi_alloc(MAX_PATH*sizeof(WCHAR));
620             GetCurrentDirectoryW(MAX_PATH,path);
621             strcatW(path,cszbs);
622         }
623
624         check = msi_dup_property( package, cszSourceDir );
625         if (!check)
626             MSI_SetPropertyW(package, cszSourceDir, path);
627         msi_free(check);
628
629         check = msi_dup_property( package, cszSOURCEDIR );
630         if (!check)
631             MSI_SetPropertyW(package, cszSOURCEDIR, path);
632
633         msi_free( package->PackagePath );
634         package->PackagePath = path;
635
636         msi_free(check);
637     }
638
639     msi_parse_command_line( package, szCommandLine );
640
641     msi_apply_transforms( package );
642     msi_apply_patches( package );
643
644     if ( msi_get_property_int(package, szUILevel, 0) >= INSTALLUILEVEL_REDUCED )
645     {
646         package->script->InWhatSequence |= SEQUENCE_UI;
647         rc = ACTION_ProcessUISequence(package);
648         ui = TRUE;
649         if (rc == ERROR_SUCCESS)
650         {
651             package->script->InWhatSequence |= SEQUENCE_EXEC;
652             rc = ACTION_ProcessExecSequence(package,TRUE);
653         }
654     }
655     else
656         rc = ACTION_ProcessExecSequence(package,FALSE);
657     
658     if (rc == -1)
659     {
660         /* install was halted but should be considered a success */
661         rc = ERROR_SUCCESS;
662     }
663
664     package->script->CurrentlyScripting= FALSE;
665
666     /* process the ending type action */
667     if (rc == ERROR_SUCCESS)
668         ACTION_PerformActionSequence(package,-1,ui);
669     else if (rc == ERROR_INSTALL_USEREXIT) 
670         ACTION_PerformActionSequence(package,-2,ui);
671     else if (rc == ERROR_INSTALL_SUSPEND) 
672         ACTION_PerformActionSequence(package,-4,ui);
673     else  /* failed */
674         ACTION_PerformActionSequence(package,-3,ui);
675
676     /* finish up running custom actions */
677     ACTION_FinishCustomActions(package);
678     
679     return rc;
680 }
681
682 static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq, BOOL UI)
683 {
684     UINT rc = ERROR_SUCCESS;
685     MSIRECORD * row = 0;
686     static const WCHAR ExecSeqQuery[] =
687         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
688          '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
689          'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
690          '`','S','e','q','u','e','n','c','e','`',' ', '=',' ','%','i',0};
691
692     static const WCHAR UISeqQuery[] =
693         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
694      '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e',
695      '`', ' ', 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',
696          ' ', '=',' ','%','i',0};
697
698     if (UI)
699         row = MSI_QueryGetRecord(package->db, UISeqQuery, seq);
700     else
701         row = MSI_QueryGetRecord(package->db, ExecSeqQuery, seq);
702
703     if (row)
704     {
705         LPCWSTR action, cond;
706
707         TRACE("Running the actions\n"); 
708
709         /* check conditions */
710         cond = MSI_RecordGetString(row,2);
711
712         /* this is a hack to skip errors in the condition code */
713         if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
714             goto end;
715
716         action = MSI_RecordGetString(row,1);
717         if (!action)
718         {
719             ERR("failed to fetch action\n");
720             rc = ERROR_FUNCTION_FAILED;
721             goto end;
722         }
723
724         if (UI)
725             rc = ACTION_PerformUIAction(package,action);
726         else
727             rc = ACTION_PerformAction(package,action,FALSE);
728 end:
729         msiobj_release(&row->hdr);
730     }
731     else
732         rc = ERROR_SUCCESS;
733
734     return rc;
735 }
736
737 typedef struct {
738     MSIPACKAGE* package;
739     BOOL UI;
740 } iterate_action_param;
741
742 static UINT ITERATE_Actions(MSIRECORD *row, LPVOID param)
743 {
744     iterate_action_param *iap= (iterate_action_param*)param;
745     UINT rc;
746     LPCWSTR cond, action;
747
748     action = MSI_RecordGetString(row,1);
749     if (!action)
750     {
751         ERR("Error is retrieving action name\n");
752         return ERROR_FUNCTION_FAILED;
753     }
754
755     /* check conditions */
756     cond = MSI_RecordGetString(row,2);
757
758     /* this is a hack to skip errors in the condition code */
759     if (MSI_EvaluateConditionW(iap->package, cond) == MSICONDITION_FALSE)
760     {
761         TRACE("Skipping action: %s (condition is false)\n", debugstr_w(action));
762         return ERROR_SUCCESS;
763     }
764
765     if (iap->UI)
766         rc = ACTION_PerformUIAction(iap->package,action);
767     else
768         rc = ACTION_PerformAction(iap->package,action,FALSE);
769
770     msi_dialog_check_messages( NULL );
771
772     if (iap->package->CurrentInstallState != ERROR_SUCCESS )
773         rc = iap->package->CurrentInstallState;
774
775     if (rc == ERROR_FUNCTION_NOT_CALLED)
776         rc = ERROR_SUCCESS;
777
778     if (rc != ERROR_SUCCESS)
779         ERR("Execution halted, action %s returned %i\n", debugstr_w(action), rc);
780
781     return rc;
782 }
783
784 UINT MSI_Sequence( MSIPACKAGE *package, LPCWSTR szTable, INT iSequenceMode )
785 {
786     MSIQUERY * view;
787     UINT r;
788     static const WCHAR query[] =
789         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
790          '`','%','s','`',
791          ' ','W','H','E','R','E',' ', 
792          '`','S','e','q','u','e','n','c','e','`',' ',
793          '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
794          '`','S','e','q','u','e','n','c','e','`',0};
795     iterate_action_param iap;
796
797     /*
798      * FIXME: probably should be checking UILevel in the
799      *       ACTION_PerformUIAction/ACTION_PerformAction
800      *       rather than saving the UI level here. Those
801      *       two functions can be merged too.
802      */
803     iap.package = package;
804     iap.UI = TRUE;
805
806     TRACE("%p %s %i\n", package, debugstr_w(szTable), iSequenceMode );
807
808     r = MSI_OpenQuery( package->db, &view, query, szTable );
809     if (r == ERROR_SUCCESS)
810     {
811         r = MSI_IterateRecords( view, NULL, ITERATE_Actions, &iap );
812         msiobj_release(&view->hdr);
813     }
814
815     return r;
816 }
817
818 static UINT ACTION_ProcessExecSequence(MSIPACKAGE *package, BOOL UIran)
819 {
820     MSIQUERY * view;
821     UINT rc;
822     static const WCHAR ExecSeqQuery[] =
823         {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
824          '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
825          'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
826          '`','S','e','q','u','e','n','c','e','`',' ', '>',' ','%','i',' ',
827          'O','R','D','E','R',' ', 'B','Y',' ',
828          '`','S','e','q','u','e','n','c','e','`',0 };
829     MSIRECORD * row = 0;
830     static const WCHAR IVQuery[] =
831         {'S','E','L','E','C','T',' ','`','S','e','q','u','e','n','c','e','`',
832          ' ', 'F','R','O','M',' ','`','I','n','s','t','a','l','l',
833          'E','x','e','c','u','t','e','S','e','q','u','e','n','c','e','`',' ',
834          'W','H','E','R','E',' ','`','A','c','t','i','o','n','`',' ','=',
835          ' ','\'', 'I','n','s','t','a','l','l',
836          'V','a','l','i','d','a','t','e','\'', 0};
837     INT seq = 0;
838     iterate_action_param iap;
839
840     iap.package = package;
841     iap.UI = FALSE;
842
843     if (package->script->ExecuteSequenceRun)
844     {
845         TRACE("Execute Sequence already Run\n");
846         return ERROR_SUCCESS;
847     }
848
849     package->script->ExecuteSequenceRun = TRUE;
850
851     /* get the sequence number */
852     if (UIran)
853     {
854         row = MSI_QueryGetRecord(package->db, IVQuery);
855         if( !row )
856             return ERROR_FUNCTION_FAILED;
857         seq = MSI_RecordGetInteger(row,1);
858         msiobj_release(&row->hdr);
859     }
860
861     rc = MSI_OpenQuery(package->db, &view, ExecSeqQuery, seq);
862     if (rc == ERROR_SUCCESS)
863     {
864         TRACE("Running the actions\n");
865
866         rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, &iap);
867         msiobj_release(&view->hdr);
868     }
869
870     return rc;
871 }
872
873 static UINT ACTION_ProcessUISequence(MSIPACKAGE *package)
874 {
875     MSIQUERY * view;
876     UINT rc;
877     static const WCHAR ExecSeqQuery [] =
878         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
879          '`','I','n','s','t','a','l','l',
880          'U','I','S','e','q','u','e','n','c','e','`',
881          ' ','W','H','E','R','E',' ', 
882          '`','S','e','q','u','e','n','c','e','`',' ',
883          '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
884          '`','S','e','q','u','e','n','c','e','`',0};
885     iterate_action_param iap;
886
887     iap.package = package;
888     iap.UI = TRUE;
889
890     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
891     
892     if (rc == ERROR_SUCCESS)
893     {
894         TRACE("Running the actions\n"); 
895
896         rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, &iap);
897         msiobj_release(&view->hdr);
898     }
899
900     return rc;
901 }
902
903 /********************************************************
904  * ACTION helper functions and functions that perform the actions
905  *******************************************************/
906 static BOOL ACTION_HandleStandardAction(MSIPACKAGE *package, LPCWSTR action, 
907                                         UINT* rc, BOOL force )
908 {
909     BOOL ret = FALSE; 
910     BOOL run = force;
911     int i;
912
913     if (!run && !package->script->CurrentlyScripting)
914         run = TRUE;
915    
916     if (!run)
917     {
918         if (strcmpW(action,szInstallFinalize) == 0 ||
919             strcmpW(action,szInstallExecute) == 0 ||
920             strcmpW(action,szInstallExecuteAgain) == 0) 
921                 run = TRUE;
922     }
923     
924     i = 0;
925     while (StandardActions[i].action != NULL)
926     {
927         if (strcmpW(StandardActions[i].action, action)==0)
928         {
929             if (!run)
930             {
931                 ui_actioninfo(package, action, TRUE, 0);
932                 *rc = schedule_action(package,INSTALL_SCRIPT,action);
933                 ui_actioninfo(package, action, FALSE, *rc);
934             }
935             else
936             {
937                 ui_actionstart(package, action);
938                 if (StandardActions[i].handler)
939                 {
940                     *rc = StandardActions[i].handler(package);
941                 }
942                 else
943                 {
944                     FIXME("unhandled standard action %s\n",debugstr_w(action));
945                     *rc = ERROR_SUCCESS;
946                 }
947             }
948             ret = TRUE;
949             break;
950         }
951         i++;
952     }
953     return ret;
954 }
955
956 static BOOL ACTION_HandleCustomAction( MSIPACKAGE* package, LPCWSTR action,
957                                        UINT* rc, BOOL force )
958 {
959     BOOL ret=FALSE;
960     UINT arc;
961
962     arc = ACTION_CustomAction(package,action, force);
963
964     if (arc != ERROR_CALL_NOT_IMPLEMENTED)
965     {
966         *rc = arc;
967         ret = TRUE;
968     }
969     return ret;
970 }
971
972 /* 
973  * A lot of actions are really important even if they don't do anything
974  * explicit... Lots of properties are set at the beginning of the installation
975  * CostFinalize does a bunch of work to translate the directories and such
976  * 
977  * But until I get write access to the database that is hard, so I am going to
978  * hack it to see if I can get something to run.
979  */
980 UINT ACTION_PerformAction(MSIPACKAGE *package, const WCHAR *action, BOOL force)
981 {
982     UINT rc = ERROR_SUCCESS; 
983     BOOL handled;
984
985     TRACE("Performing action (%s)\n",debugstr_w(action));
986
987     handled = ACTION_HandleStandardAction(package, action, &rc, force);
988
989     if (!handled)
990         handled = ACTION_HandleCustomAction(package, action, &rc, force);
991
992     if (!handled)
993     {
994         FIXME("unhandled msi action %s\n",debugstr_w(action));
995         rc = ERROR_FUNCTION_NOT_CALLED;
996     }
997
998     return rc;
999 }
1000
1001 UINT ACTION_PerformUIAction(MSIPACKAGE *package, const WCHAR *action)
1002 {
1003     UINT rc = ERROR_SUCCESS;
1004     BOOL handled = FALSE;
1005
1006     TRACE("Performing action (%s)\n",debugstr_w(action));
1007
1008     handled = ACTION_HandleStandardAction(package, action, &rc,TRUE);
1009
1010     if (!handled)
1011         handled = ACTION_HandleCustomAction(package, action, &rc, FALSE);
1012
1013     if( !handled && ACTION_DialogBox(package,action) == ERROR_SUCCESS )
1014         handled = TRUE;
1015
1016     if (!handled)
1017     {
1018         FIXME("unhandled msi action %s\n",debugstr_w(action));
1019         rc = ERROR_FUNCTION_NOT_CALLED;
1020     }
1021
1022     return rc;
1023 }
1024
1025
1026 /*
1027  * Actual Action Handlers
1028  */
1029
1030 static UINT ITERATE_CreateFolders(MSIRECORD *row, LPVOID param)
1031 {
1032     MSIPACKAGE *package = (MSIPACKAGE*)param;
1033     LPCWSTR dir;
1034     LPWSTR full_path;
1035     MSIRECORD *uirow;
1036     MSIFOLDER *folder;
1037
1038     dir = MSI_RecordGetString(row,1);
1039     if (!dir)
1040     {
1041         ERR("Unable to get folder id\n");
1042         return ERROR_SUCCESS;
1043     }
1044
1045     full_path = resolve_folder(package,dir,FALSE,FALSE,&folder);
1046     if (!full_path)
1047     {
1048         ERR("Unable to resolve folder id %s\n",debugstr_w(dir));
1049         return ERROR_SUCCESS;
1050     }
1051
1052     TRACE("Folder is %s\n",debugstr_w(full_path));
1053
1054     /* UI stuff */
1055     uirow = MSI_CreateRecord(1);
1056     MSI_RecordSetStringW(uirow,1,full_path);
1057     ui_actiondata(package,szCreateFolders,uirow);
1058     msiobj_release( &uirow->hdr );
1059
1060     if (folder->State == 0)
1061         create_full_pathW(full_path);
1062
1063     folder->State = 3;
1064
1065     msi_free(full_path);
1066     return ERROR_SUCCESS;
1067 }
1068
1069 /* FIXME: probably should merge this with the above function */
1070 static UINT msi_create_directory( MSIPACKAGE* package, LPCWSTR dir )
1071 {
1072     UINT rc = ERROR_SUCCESS;
1073     MSIFOLDER *folder;
1074     LPWSTR install_path;
1075
1076     install_path = resolve_folder(package, dir, FALSE, FALSE, &folder);
1077     if (!install_path)
1078         return ERROR_FUNCTION_FAILED; 
1079
1080     /* create the path */
1081     if (folder->State == 0)
1082     {
1083         create_full_pathW(install_path);
1084         folder->State = 2;
1085     }
1086     msi_free(install_path);
1087
1088     return rc;
1089 }
1090
1091 UINT msi_create_component_directories( MSIPACKAGE *package )
1092 {
1093     MSICOMPONENT *comp;
1094
1095     /* create all the folders required by the components are going to install */
1096     LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
1097     {
1098         if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
1099             continue;
1100         msi_create_directory( package, comp->Directory );
1101     }
1102
1103     return ERROR_SUCCESS;
1104 }
1105
1106 /*
1107  * Also we cannot enable/disable components either, so for now I am just going 
1108  * to do all the directories for all the components.
1109  */
1110 static UINT ACTION_CreateFolders(MSIPACKAGE *package)
1111 {
1112     static const WCHAR ExecSeqQuery[] =
1113         {'S','E','L','E','C','T',' ',
1114          '`','D','i','r','e','c','t','o','r','y','_','`',
1115          ' ','F','R','O','M',' ',
1116          '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0 };
1117     UINT rc;
1118     MSIQUERY *view;
1119
1120     /* create all the empty folders specified in the CreateFolder table */
1121     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view );
1122     if (rc != ERROR_SUCCESS)
1123         return ERROR_SUCCESS;
1124
1125     rc = MSI_IterateRecords(view, NULL, ITERATE_CreateFolders, package);
1126     msiobj_release(&view->hdr);
1127
1128     msi_create_component_directories( package );
1129
1130     return rc;
1131 }
1132
1133 static UINT load_component( MSIRECORD *row, LPVOID param )
1134 {
1135     MSIPACKAGE *package = param;
1136     MSICOMPONENT *comp;
1137
1138     comp = msi_alloc_zero( sizeof(MSICOMPONENT) );
1139     if (!comp)
1140         return ERROR_FUNCTION_FAILED;
1141
1142     list_add_tail( &package->components, &comp->entry );
1143
1144     /* fill in the data */
1145     comp->Component = msi_dup_record_field( row, 1 );
1146
1147     TRACE("Loading Component %s\n", debugstr_w(comp->Component));
1148
1149     comp->ComponentId = msi_dup_record_field( row, 2 );
1150     comp->Directory = msi_dup_record_field( row, 3 );
1151     comp->Attributes = MSI_RecordGetInteger(row,4);
1152     comp->Condition = msi_dup_record_field( row, 5 );
1153     comp->KeyPath = msi_dup_record_field( row, 6 );
1154
1155     comp->Installed = INSTALLSTATE_UNKNOWN;
1156     msi_component_set_state( comp, INSTALLSTATE_UNKNOWN );
1157
1158     return ERROR_SUCCESS;
1159 }
1160
1161 static UINT load_all_components( MSIPACKAGE *package )
1162 {
1163     static const WCHAR query[] = {
1164         'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ', 
1165          '`','C','o','m','p','o','n','e','n','t','`',0 };
1166     MSIQUERY *view;
1167     UINT r;
1168
1169     if (!list_empty(&package->components))
1170         return ERROR_SUCCESS;
1171
1172     r = MSI_DatabaseOpenViewW( package->db, query, &view );
1173     if (r != ERROR_SUCCESS)
1174         return r;
1175
1176     r = MSI_IterateRecords(view, NULL, load_component, package);
1177     msiobj_release(&view->hdr);
1178     return r;
1179 }
1180
1181 typedef struct {
1182     MSIPACKAGE *package;
1183     MSIFEATURE *feature;
1184 } _ilfs;
1185
1186 static UINT add_feature_component( MSIFEATURE *feature, MSICOMPONENT *comp )
1187 {
1188     ComponentList *cl;
1189
1190     cl = msi_alloc( sizeof (*cl) );
1191     if ( !cl )
1192         return ERROR_NOT_ENOUGH_MEMORY;
1193     cl->component = comp;
1194     list_add_tail( &feature->Components, &cl->entry );
1195
1196     return ERROR_SUCCESS;
1197 }
1198
1199 static UINT add_feature_child( MSIFEATURE *parent, MSIFEATURE *child )
1200 {
1201     FeatureList *fl;
1202
1203     fl = msi_alloc( sizeof(*fl) );
1204     if ( !fl )
1205         return ERROR_NOT_ENOUGH_MEMORY;
1206     fl->feature = child;
1207     list_add_tail( &parent->Children, &fl->entry );
1208
1209     return ERROR_SUCCESS;
1210 }
1211
1212 static UINT iterate_load_featurecomponents(MSIRECORD *row, LPVOID param)
1213 {
1214     _ilfs* ilfs= (_ilfs*)param;
1215     LPCWSTR component;
1216     MSICOMPONENT *comp;
1217
1218     component = MSI_RecordGetString(row,1);
1219
1220     /* check to see if the component is already loaded */
1221     comp = get_loaded_component( ilfs->package, component );
1222     if (!comp)
1223     {
1224         ERR("unknown component %s\n", debugstr_w(component));
1225         return ERROR_FUNCTION_FAILED;
1226     }
1227
1228     add_feature_component( ilfs->feature, comp );
1229     comp->Enabled = TRUE;
1230
1231     return ERROR_SUCCESS;
1232 }
1233
1234 static MSIFEATURE *find_feature_by_name( MSIPACKAGE *package, LPCWSTR name )
1235 {
1236     MSIFEATURE *feature;
1237
1238     LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1239     {
1240         if ( !lstrcmpW( feature->Feature, name ) )
1241             return feature;
1242     }
1243
1244     return NULL;
1245 }
1246
1247 static UINT load_feature(MSIRECORD * row, LPVOID param)
1248 {
1249     MSIPACKAGE* package = (MSIPACKAGE*)param;
1250     MSIFEATURE* feature;
1251     static const WCHAR Query1[] = 
1252         {'S','E','L','E','C','T',' ',
1253          '`','C','o','m','p','o','n','e','n','t','_','`',
1254          ' ','F','R','O','M',' ','`','F','e','a','t','u','r','e',
1255          'C','o','m','p','o','n','e','n','t','s','`',' ',
1256          'W','H','E','R','E',' ',
1257          '`','F','e', 'a','t','u','r','e','_','`',' ','=','\'','%','s','\'',0};
1258     MSIQUERY * view;
1259     UINT    rc;
1260     _ilfs ilfs;
1261
1262     /* fill in the data */
1263
1264     feature = msi_alloc_zero( sizeof (MSIFEATURE) );
1265     if (!feature)
1266         return ERROR_NOT_ENOUGH_MEMORY;
1267
1268     list_init( &feature->Children );
1269     list_init( &feature->Components );
1270     
1271     feature->Feature = msi_dup_record_field( row, 1 );
1272
1273     TRACE("Loading feature %s\n",debugstr_w(feature->Feature));
1274
1275     feature->Feature_Parent = msi_dup_record_field( row, 2 );
1276     feature->Title = msi_dup_record_field( row, 3 );
1277     feature->Description = msi_dup_record_field( row, 4 );
1278
1279     if (!MSI_RecordIsNull(row,5))
1280         feature->Display = MSI_RecordGetInteger(row,5);
1281   
1282     feature->Level= MSI_RecordGetInteger(row,6);
1283     feature->Directory = msi_dup_record_field( row, 7 );
1284     feature->Attributes = MSI_RecordGetInteger(row,8);
1285
1286     feature->Installed = INSTALLSTATE_UNKNOWN;
1287     msi_feature_set_state( feature, INSTALLSTATE_UNKNOWN );
1288
1289     list_add_tail( &package->features, &feature->entry );
1290
1291     /* load feature components */
1292
1293     rc = MSI_OpenQuery( package->db, &view, Query1, feature->Feature );
1294     if (rc != ERROR_SUCCESS)
1295         return ERROR_SUCCESS;
1296
1297     ilfs.package = package;
1298     ilfs.feature = feature;
1299
1300     MSI_IterateRecords(view, NULL, iterate_load_featurecomponents , &ilfs);
1301     msiobj_release(&view->hdr);
1302
1303     return ERROR_SUCCESS;
1304 }
1305
1306 static UINT find_feature_children(MSIRECORD * row, LPVOID param)
1307 {
1308     MSIPACKAGE* package = (MSIPACKAGE*)param;
1309     MSIFEATURE *parent, *child;
1310
1311     child = find_feature_by_name( package, MSI_RecordGetString( row, 1 ) );
1312     if (!child)
1313         return ERROR_FUNCTION_FAILED;
1314
1315     if (!child->Feature_Parent)
1316         return ERROR_SUCCESS;
1317
1318     parent = find_feature_by_name( package, child->Feature_Parent );
1319     if (!parent)
1320         return ERROR_FUNCTION_FAILED;
1321
1322     add_feature_child( parent, child );
1323     return ERROR_SUCCESS;
1324 }
1325
1326 static UINT load_all_features( MSIPACKAGE *package )
1327 {
1328     static const WCHAR query[] = {
1329         'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1330         '`','F','e','a','t','u','r','e','`',' ','O','R','D','E','R',
1331         ' ','B','Y',' ','`','D','i','s','p','l','a','y','`',0};
1332     MSIQUERY *view;
1333     UINT r;
1334
1335     if (!list_empty(&package->features))
1336         return ERROR_SUCCESS;
1337  
1338     r = MSI_DatabaseOpenViewW( package->db, query, &view );
1339     if (r != ERROR_SUCCESS)
1340         return r;
1341
1342     r = MSI_IterateRecords( view, NULL, load_feature, package );
1343     if (r != ERROR_SUCCESS)
1344         return r;
1345
1346     r = MSI_IterateRecords( view, NULL, find_feature_children, package );
1347     msiobj_release( &view->hdr );
1348
1349     return r;
1350 }
1351
1352 static LPWSTR folder_split_path(LPWSTR p, WCHAR ch)
1353 {
1354     if (!p)
1355         return p;
1356     p = strchrW(p, ch);
1357     if (!p)
1358         return p;
1359     *p = 0;
1360     return p+1;
1361 }
1362
1363 static UINT load_file(MSIRECORD *row, LPVOID param)
1364 {
1365     MSIPACKAGE* package = (MSIPACKAGE*)param;
1366     LPCWSTR component;
1367     MSIFILE *file;
1368
1369     /* fill in the data */
1370
1371     file = msi_alloc_zero( sizeof (MSIFILE) );
1372     if (!file)
1373         return ERROR_NOT_ENOUGH_MEMORY;
1374  
1375     file->File = msi_dup_record_field( row, 1 );
1376
1377     component = MSI_RecordGetString( row, 2 );
1378     file->Component = get_loaded_component( package, component );
1379
1380     if (!file->Component)
1381         ERR("Unfound Component %s\n",debugstr_w(component));
1382
1383     file->FileName = msi_dup_record_field( row, 3 );
1384     reduce_to_longfilename( file->FileName );
1385
1386     file->ShortName = msi_dup_record_field( row, 3 );
1387     file->LongName = strdupW( folder_split_path(file->ShortName, '|'));
1388     
1389     file->FileSize = MSI_RecordGetInteger( row, 4 );
1390     file->Version = msi_dup_record_field( row, 5 );
1391     file->Language = msi_dup_record_field( row, 6 );
1392     file->Attributes = MSI_RecordGetInteger( row, 7 );
1393     file->Sequence = MSI_RecordGetInteger( row, 8 );
1394
1395     file->state = msifs_invalid;
1396
1397     /* if the compressed bits are not set in the file attributes,
1398      * then read the information from the package word count property
1399      */
1400     if (file->Attributes & msidbFileAttributesCompressed)
1401     {
1402         file->IsCompressed = TRUE;
1403     }
1404     else if (file->Attributes & msidbFileAttributesNoncompressed)
1405     {
1406         file->IsCompressed = FALSE;
1407     }
1408     else
1409     {
1410         file->IsCompressed = package->WordCount & MSIWORDCOUNT_COMPRESSED;
1411     }
1412
1413     TRACE("File Loaded (%s)\n",debugstr_w(file->File));  
1414
1415     list_add_tail( &package->files, &file->entry );
1416  
1417     return ERROR_SUCCESS;
1418 }
1419
1420 static UINT load_all_files(MSIPACKAGE *package)
1421 {
1422     MSIQUERY * view;
1423     UINT rc;
1424     static const WCHAR Query[] =
1425         {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1426          '`','F','i','l','e','`',' ', 'O','R','D','E','R',' ','B','Y',' ',
1427          '`','S','e','q','u','e','n','c','e','`', 0};
1428
1429     if (!list_empty(&package->files))
1430         return ERROR_SUCCESS;
1431
1432     rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
1433     if (rc != ERROR_SUCCESS)
1434         return ERROR_SUCCESS;
1435
1436     rc = MSI_IterateRecords(view, NULL, load_file, package);
1437     msiobj_release(&view->hdr);
1438
1439     return ERROR_SUCCESS;
1440 }
1441
1442
1443 /*
1444  * I am not doing any of the costing functionality yet.
1445  * Mostly looking at doing the Component and Feature loading
1446  *
1447  * The native MSI does A LOT of modification to tables here. Mostly adding
1448  * a lot of temporary columns to the Feature and Component tables.
1449  *
1450  *    note: Native msi also tracks the short filename. But I am only going to
1451  *          track the long ones.  Also looking at this directory table
1452  *          it appears that the directory table does not get the parents
1453  *          resolved base on property only based on their entries in the
1454  *          directory table.
1455  */
1456 static UINT ACTION_CostInitialize(MSIPACKAGE *package)
1457 {
1458     static const WCHAR szCosting[] =
1459         {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
1460     static const WCHAR szZero[] = { '0', 0 };
1461
1462     if ( 1 == msi_get_property_int( package, szCosting, 0 ) )
1463         return ERROR_SUCCESS;
1464
1465     MSI_SetPropertyW(package, szCosting, szZero);
1466     MSI_SetPropertyW(package, cszRootDrive, c_colon);
1467
1468     load_all_components( package );
1469     load_all_features( package );
1470     load_all_files( package );
1471
1472     return ERROR_SUCCESS;
1473 }
1474
1475 static UINT execute_script(MSIPACKAGE *package, UINT script )
1476 {
1477     int i;
1478     UINT rc = ERROR_SUCCESS;
1479
1480     TRACE("Executing Script %i\n",script);
1481
1482     if (!package->script)
1483     {
1484         ERR("no script!\n");
1485         return ERROR_FUNCTION_FAILED;
1486     }
1487
1488     for (i = 0; i < package->script->ActionCount[script]; i++)
1489     {
1490         LPWSTR action;
1491         action = package->script->Actions[script][i];
1492         ui_actionstart(package, action);
1493         TRACE("Executing Action (%s)\n",debugstr_w(action));
1494         rc = ACTION_PerformAction(package, action, TRUE);
1495         if (rc != ERROR_SUCCESS)
1496             break;
1497     }
1498     msi_free_action_script(package, script);
1499     return rc;
1500 }
1501
1502 static UINT ACTION_FileCost(MSIPACKAGE *package)
1503 {
1504     return ERROR_SUCCESS;
1505 }
1506
1507 static MSIFOLDER *load_folder( MSIPACKAGE *package, LPCWSTR dir )
1508 {
1509     static const WCHAR Query[] =
1510         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1511          '`','D','i','r','e','c', 't','o','r','y','`',' ',
1512          'W','H','E','R','E',' ', '`', 'D','i','r','e','c','t', 'o','r','y','`',
1513          ' ','=',' ','\'','%','s','\'',
1514          0};
1515     static const WCHAR szDot[] = { '.',0 };
1516     static WCHAR szEmpty[] = { 0 };
1517     LPWSTR p, tgt_short, tgt_long, src_short, src_long;
1518     LPCWSTR parent;
1519     MSIRECORD *row;
1520     MSIFOLDER *folder;
1521
1522     TRACE("Looking for dir %s\n",debugstr_w(dir));
1523
1524     folder = get_loaded_folder( package, dir );
1525     if (folder)
1526         return folder;
1527
1528     TRACE("Working to load %s\n",debugstr_w(dir));
1529
1530     folder = msi_alloc_zero( sizeof (MSIFOLDER) );
1531     if (!folder)
1532         return NULL;
1533
1534     folder->Directory = strdupW(dir);
1535
1536     row = MSI_QueryGetRecord(package->db, Query, dir);
1537     if (!row)
1538         return NULL;
1539
1540     p = msi_dup_record_field(row, 3);
1541
1542     /* split src and target dir */
1543     tgt_short = p;
1544     src_short = folder_split_path( p, ':' );
1545
1546     /* split the long and short paths */
1547     tgt_long = folder_split_path( tgt_short, '|' );
1548     src_long = folder_split_path( src_short, '|' );
1549
1550     /* check for no-op dirs */
1551     if (!lstrcmpW(szDot, tgt_short))
1552         tgt_short = szEmpty;
1553     if (!lstrcmpW(szDot, src_short))
1554         src_short = szEmpty;
1555
1556     if (!tgt_long)
1557         tgt_long = tgt_short;
1558         
1559     if (!src_short) {
1560         src_short = tgt_short;
1561         src_long = tgt_long;
1562     }
1563     
1564     if (!src_long)
1565         src_long = src_short;
1566
1567     /* FIXME: use the target short path too */
1568     folder->TargetDefault = strdupW(tgt_long);
1569     folder->SourceShortPath = strdupW(src_short);
1570     folder->SourceLongPath = strdupW(src_long);
1571     msi_free(p);
1572
1573     TRACE("TargetDefault = %s\n",debugstr_w( folder->TargetDefault ));
1574     TRACE("SourceLong = %s\n", debugstr_w( folder->SourceLongPath ));
1575     TRACE("SourceShort = %s\n", debugstr_w( folder->SourceShortPath ));
1576
1577     parent = MSI_RecordGetString(row, 2);
1578     if (parent) 
1579     {
1580         folder->Parent = load_folder( package, parent );
1581         if ( folder->Parent )
1582             TRACE("loaded parent %p %s\n", folder->Parent,
1583                   debugstr_w(folder->Parent->Directory));
1584         else
1585             ERR("failed to load parent folder %s\n", debugstr_w(parent));
1586     }
1587
1588     folder->Property = msi_dup_property( package, dir );
1589
1590     msiobj_release(&row->hdr);
1591
1592     list_add_tail( &package->folders, &folder->entry );
1593
1594     TRACE("%s returning %p\n",debugstr_w(dir),folder);
1595
1596     return folder;
1597 }
1598
1599 static void ACTION_GetComponentInstallStates(MSIPACKAGE *package)
1600 {
1601     MSICOMPONENT *comp;
1602
1603     LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
1604     {
1605         INSTALLSTATE res;
1606
1607         if (!comp->ComponentId)
1608             continue;
1609
1610         res = MsiGetComponentPathW( package->ProductCode,
1611                                     comp->ComponentId, NULL, NULL);
1612         if (res < 0)
1613             res = INSTALLSTATE_ABSENT;
1614         comp->Installed = res;
1615     }
1616 }
1617
1618 /* scan for and update current install states */
1619 static void ACTION_UpdateFeatureInstallStates(MSIPACKAGE *package)
1620 {
1621     MSICOMPONENT *comp;
1622     MSIFEATURE *feature;
1623
1624     LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1625     {
1626         ComponentList *cl;
1627         INSTALLSTATE res = INSTALLSTATE_ABSENT;
1628
1629         LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1630         {
1631             comp= cl->component;
1632
1633             if (!comp->ComponentId)
1634             {
1635                 res = INSTALLSTATE_ABSENT;
1636                 break;
1637             }
1638
1639             if (res == INSTALLSTATE_ABSENT)
1640                 res = comp->Installed;
1641             else
1642             {
1643                 if (res == comp->Installed)
1644                     continue;
1645
1646                 if (res != INSTALLSTATE_DEFAULT || res != INSTALLSTATE_LOCAL ||
1647                     res != INSTALLSTATE_SOURCE)
1648                 {
1649                     res = INSTALLSTATE_INCOMPLETE;
1650                 }
1651             }
1652         }
1653         feature->Installed = res;
1654     }
1655 }
1656
1657 static BOOL process_state_property (MSIPACKAGE* package, LPCWSTR property, 
1658                                     INSTALLSTATE state)
1659 {
1660     static const WCHAR all[]={'A','L','L',0};
1661     LPWSTR override;
1662     MSIFEATURE *feature;
1663
1664     override = msi_dup_property( package, property );
1665     if (!override)
1666         return FALSE;
1667
1668     LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1669     {
1670         if (strcmpiW(override,all)==0)
1671             msi_feature_set_state( feature, state );
1672         else
1673         {
1674             LPWSTR ptr = override;
1675             LPWSTR ptr2 = strchrW(override,',');
1676
1677             while (ptr)
1678             {
1679                 if ((ptr2 && strncmpW(ptr,feature->Feature, ptr2-ptr)==0)
1680                     || (!ptr2 && strcmpW(ptr,feature->Feature)==0))
1681                 {
1682                     msi_feature_set_state( feature, state );
1683                     break;
1684                 }
1685                 if (ptr2)
1686                 {
1687                     ptr=ptr2+1;
1688                     ptr2 = strchrW(ptr,',');
1689                 }
1690                 else
1691                     break;
1692             }
1693         }
1694     }
1695     msi_free(override);
1696
1697     return TRUE;
1698 }
1699
1700 UINT MSI_SetFeatureStates(MSIPACKAGE *package)
1701 {
1702     int install_level;
1703     static const WCHAR szlevel[] =
1704         {'I','N','S','T','A','L','L','L','E','V','E','L',0};
1705     static const WCHAR szAddLocal[] =
1706         {'A','D','D','L','O','C','A','L',0};
1707     static const WCHAR szRemove[] =
1708         {'R','E','M','O','V','E',0};
1709     static const WCHAR szReinstall[] =
1710         {'R','E','I','N','S','T','A','L','L',0};
1711     BOOL override = FALSE;
1712     MSICOMPONENT* component;
1713     MSIFEATURE *feature;
1714
1715
1716     /* I do not know if this is where it should happen.. but */
1717
1718     TRACE("Checking Install Level\n");
1719
1720     install_level = msi_get_property_int( package, szlevel, 1 );
1721
1722     /* ok here is the _real_ rub
1723      * all these activation/deactivation things happen in order and things
1724      * later on the list override things earlier on the list.
1725      * 1) INSTALLLEVEL processing
1726      * 2) ADDLOCAL
1727      * 3) REMOVE
1728      * 4) ADDSOURCE
1729      * 5) ADDDEFAULT
1730      * 6) REINSTALL
1731      * 7) COMPADDLOCAL
1732      * 8) COMPADDSOURCE
1733      * 9) FILEADDLOCAL
1734      * 10) FILEADDSOURCE
1735      * 11) FILEADDDEFAULT
1736      * I have confirmed that if ADDLOCAL is stated then the INSTALLLEVEL is
1737      * ignored for all the features. seems strange, especially since it is not
1738      * documented anywhere, but it is how it works.
1739      *
1740      * I am still ignoring a lot of these. But that is ok for now, ADDLOCAL and
1741      * REMOVE are the big ones, since we don't handle administrative installs
1742      * yet anyway.
1743      */
1744     override |= process_state_property(package,szAddLocal,INSTALLSTATE_LOCAL);
1745     override |= process_state_property(package,szRemove,INSTALLSTATE_ABSENT);
1746     override |= process_state_property(package,szReinstall,INSTALLSTATE_LOCAL);
1747
1748     if (!override)
1749     {
1750         LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1751         {
1752             BOOL feature_state = ((feature->Level > 0) &&
1753                              (feature->Level <= install_level));
1754
1755             if ((feature_state) && (feature->Action == INSTALLSTATE_UNKNOWN))
1756             {
1757                 if (feature->Attributes & msidbFeatureAttributesFavorSource)
1758                     msi_feature_set_state( feature, INSTALLSTATE_SOURCE );
1759                 else if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1760                     msi_feature_set_state( feature, INSTALLSTATE_ADVERTISED );
1761                 else
1762                     msi_feature_set_state( feature, INSTALLSTATE_LOCAL );
1763             }
1764         }
1765
1766         /* disable child features of unselected parent features */
1767         LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1768         {
1769             FeatureList *fl;
1770
1771             if (feature->Level > 0 && feature->Level <= install_level)
1772                 continue;
1773
1774             LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
1775                 msi_feature_set_state( fl->feature, INSTALLSTATE_UNKNOWN );
1776         }
1777     }
1778     else
1779     {
1780         /* set the Preselected Property */
1781         static const WCHAR szPreselected[] = {'P','r','e','s','e','l','e','c','t','e','d',0};
1782         static const WCHAR szOne[] = { '1', 0 };
1783
1784         MSI_SetPropertyW(package,szPreselected,szOne);
1785     }
1786
1787     /*
1788      * now we want to enable or disable components base on feature
1789      */
1790
1791     LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1792     {
1793         ComponentList *cl;
1794
1795         TRACE("Examining Feature %s (Installed %i, Action %i, Request %i)\n",
1796             debugstr_w(feature->Feature), feature->Installed, feature->Action,
1797             feature->ActionRequest);
1798
1799         LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1800         {
1801             component = cl->component;
1802
1803             switch (component->Attributes)
1804             {
1805             case msidbComponentAttributesLocalOnly:
1806                 msi_component_set_state( component, INSTALLSTATE_LOCAL );
1807                 break;
1808             case msidbComponentAttributesSourceOnly:
1809                 msi_component_set_state( component, INSTALLSTATE_SOURCE );
1810                 break;
1811             case msidbComponentAttributesOptional:
1812                 msi_component_set_state( component, INSTALLSTATE_DEFAULT );
1813                 break;
1814             default:
1815                 msi_component_set_state( component, INSTALLSTATE_LOCAL );
1816             }
1817
1818             if (component->ForceLocalState)
1819                 msi_component_set_state( component, INSTALLSTATE_LOCAL );
1820
1821             if (!component->Enabled)
1822                 msi_component_set_state( component, INSTALLSTATE_UNKNOWN );
1823             else if (feature->Attributes == msidbFeatureAttributesFavorLocal)
1824             {
1825                 if (!(component->Attributes & msidbComponentAttributesSourceOnly))
1826                     msi_component_set_state( component, INSTALLSTATE_LOCAL );
1827             }
1828             else if (feature->Attributes == msidbFeatureAttributesFavorSource)
1829             {
1830                 if ((component->Action == INSTALLSTATE_UNKNOWN) ||
1831                     (component->Action == INSTALLSTATE_ABSENT) ||
1832                     (component->Action == INSTALLSTATE_ADVERTISED) ||
1833                     (component->Action == INSTALLSTATE_DEFAULT))
1834                     msi_component_set_state( component, INSTALLSTATE_SOURCE );
1835             }
1836             else if (feature->ActionRequest == INSTALLSTATE_ADVERTISED)
1837             {
1838                 if ((component->Action == INSTALLSTATE_UNKNOWN) ||
1839                     (component->Action == INSTALLSTATE_ABSENT))
1840                     msi_component_set_state( component, INSTALLSTATE_ADVERTISED );
1841             }
1842             else if (feature->ActionRequest == INSTALLSTATE_ABSENT)
1843             {
1844                 if (component->Action == INSTALLSTATE_UNKNOWN)
1845                     msi_component_set_state( component, INSTALLSTATE_ABSENT );
1846             }
1847             else if (feature->ActionRequest == INSTALLSTATE_UNKNOWN)
1848                 msi_component_set_state( component, INSTALLSTATE_UNKNOWN );
1849
1850             if (component->ForceLocalState && feature->Action == INSTALLSTATE_SOURCE)
1851                 msi_feature_set_state( feature, INSTALLSTATE_LOCAL );
1852         }
1853     }
1854
1855     LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1856     {
1857         if (component->Action == INSTALLSTATE_DEFAULT)
1858         {
1859             TRACE("%s was default, setting to local\n", debugstr_w(component->Component));
1860             msi_component_set_state( component, INSTALLSTATE_LOCAL );
1861         }
1862
1863         TRACE("Result: Component %s (Installed %i, Action %i, Request %i)\n",
1864             debugstr_w(component->Component), component->Installed,
1865             component->Action, component->ActionRequest);
1866     }
1867
1868
1869     return ERROR_SUCCESS;
1870 }
1871
1872 static UINT ITERATE_CostFinalizeDirectories(MSIRECORD *row, LPVOID param)
1873 {
1874     MSIPACKAGE *package = (MSIPACKAGE*)param;
1875     LPCWSTR name;
1876     LPWSTR path;
1877
1878     name = MSI_RecordGetString(row,1);
1879
1880     /* This helper function now does ALL the work */
1881     TRACE("Dir %s ...\n",debugstr_w(name));
1882     load_folder(package,name);
1883     path = resolve_folder(package,name,FALSE,TRUE,NULL);
1884     TRACE("resolves to %s\n",debugstr_w(path));
1885     msi_free(path);
1886
1887     return ERROR_SUCCESS;
1888 }
1889
1890 static UINT ITERATE_CostFinalizeConditions(MSIRECORD *row, LPVOID param)
1891 {
1892     MSIPACKAGE *package = (MSIPACKAGE*)param;
1893     LPCWSTR name;
1894     MSIFEATURE *feature;
1895
1896     name = MSI_RecordGetString( row, 1 );
1897
1898     feature = get_loaded_feature( package, name );
1899     if (!feature)
1900         ERR("FAILED to find loaded feature %s\n",debugstr_w(name));
1901     else
1902     {
1903         LPCWSTR Condition;
1904         Condition = MSI_RecordGetString(row,3);
1905
1906         if (MSI_EvaluateConditionW(package,Condition) == MSICONDITION_TRUE)
1907         {
1908             int level = MSI_RecordGetInteger(row,2);
1909             TRACE("Reseting feature %s to level %i\n", debugstr_w(name), level);
1910             feature->Level = level;
1911         }
1912     }
1913     return ERROR_SUCCESS;
1914 }
1915
1916 LPWSTR msi_get_disk_file_version( LPCWSTR filename )
1917 {
1918     static const WCHAR name_fmt[] =
1919         {'%','u','.','%','u','.','%','u','.','%','u',0};
1920     static WCHAR name[] = {'\\',0};
1921     VS_FIXEDFILEINFO *lpVer;
1922     WCHAR filever[0x100];
1923     LPVOID version;
1924     DWORD versize;
1925     DWORD handle;
1926     UINT sz;
1927
1928     TRACE("%s\n", debugstr_w(filename));
1929
1930     versize = GetFileVersionInfoSizeW( filename, &handle );
1931     if (!versize)
1932         return NULL;
1933
1934     version = msi_alloc( versize );
1935     GetFileVersionInfoW( filename, 0, versize, version );
1936
1937     VerQueryValueW( version, name, (LPVOID*)&lpVer, &sz );
1938     msi_free( version );
1939
1940     sprintfW( filever, name_fmt,
1941         HIWORD(lpVer->dwFileVersionMS),
1942         LOWORD(lpVer->dwFileVersionMS),
1943         HIWORD(lpVer->dwFileVersionLS),
1944         LOWORD(lpVer->dwFileVersionLS));
1945
1946     return strdupW( filever );
1947 }
1948
1949 static UINT msi_check_file_install_states( MSIPACKAGE *package )
1950 {
1951     LPWSTR file_version;
1952     MSIFILE *file;
1953
1954     LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
1955     {
1956         MSICOMPONENT* comp = file->Component;
1957         LPWSTR p;
1958
1959         if (!comp)
1960             continue;
1961
1962         if (file->IsCompressed)
1963             comp->ForceLocalState = TRUE;
1964
1965         /* calculate target */
1966         p = resolve_folder(package, comp->Directory, FALSE, FALSE, NULL);
1967
1968         msi_free(file->TargetPath);
1969
1970         TRACE("file %s is named %s\n",
1971                debugstr_w(file->File), debugstr_w(file->FileName));
1972
1973         file->TargetPath = build_directory_name(2, p, file->FileName);
1974
1975         msi_free(p);
1976
1977         TRACE("file %s resolves to %s\n",
1978                debugstr_w(file->File), debugstr_w(file->TargetPath));
1979
1980         /* don't check files of components that aren't installed */
1981         if (comp->Installed == INSTALLSTATE_UNKNOWN ||
1982             comp->Installed == INSTALLSTATE_ABSENT)
1983         {
1984             file->state = msifs_missing;  /* assume files are missing */
1985             continue;
1986         }
1987
1988         if (GetFileAttributesW(file->TargetPath) == INVALID_FILE_ATTRIBUTES)
1989         {
1990             file->state = msifs_missing;
1991             comp->Cost += file->FileSize;
1992             comp->Installed = INSTALLSTATE_INCOMPLETE;
1993             continue;
1994         }
1995
1996         if (file->Version &&
1997             (file_version = msi_get_disk_file_version( file->TargetPath )))
1998         {
1999             TRACE("new %s old %s\n", debugstr_w(file->Version),
2000                   debugstr_w(file_version));
2001             /* FIXME: seems like a bad way to compare version numbers */
2002             if (lstrcmpiW(file_version, file->Version)<0)
2003             {
2004                 file->state = msifs_overwrite;
2005                 comp->Cost += file->FileSize;
2006                 comp->Installed = INSTALLSTATE_INCOMPLETE;
2007             }
2008             else
2009                 file->state = msifs_present;
2010             msi_free( file_version );
2011         }
2012         else
2013             file->state = msifs_present;
2014     }
2015
2016     return ERROR_SUCCESS;
2017 }
2018
2019 /*
2020  * A lot is done in this function aside from just the costing.
2021  * The costing needs to be implemented at some point but for now I am going
2022  * to focus on the directory building
2023  *
2024  */
2025 static UINT ACTION_CostFinalize(MSIPACKAGE *package)
2026 {
2027     static const WCHAR ExecSeqQuery[] =
2028         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2029          '`','D','i','r','e','c','t','o','r','y','`',0};
2030     static const WCHAR ConditionQuery[] =
2031         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2032          '`','C','o','n','d','i','t','i','o','n','`',0};
2033     static const WCHAR szCosting[] =
2034         {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
2035     static const WCHAR szlevel[] =
2036         {'I','N','S','T','A','L','L','L','E','V','E','L',0};
2037     static const WCHAR szOne[] = { '1', 0 };
2038     MSICOMPONENT *comp;
2039     UINT rc;
2040     MSIQUERY * view;
2041     LPWSTR level;
2042
2043     if ( 1 == msi_get_property_int( package, szCosting, 0 ) )
2044         return ERROR_SUCCESS;
2045
2046     TRACE("Building Directory properties\n");
2047
2048     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2049     if (rc == ERROR_SUCCESS)
2050     {
2051         rc = MSI_IterateRecords(view, NULL, ITERATE_CostFinalizeDirectories,
2052                         package);
2053         msiobj_release(&view->hdr);
2054     }
2055
2056     /* read components states from the registry */
2057     ACTION_GetComponentInstallStates(package);
2058
2059     TRACE("File calculations\n");
2060     msi_check_file_install_states( package );
2061
2062     TRACE("Evaluating Condition Table\n");
2063
2064     rc = MSI_DatabaseOpenViewW(package->db, ConditionQuery, &view);
2065     if (rc == ERROR_SUCCESS)
2066     {
2067         rc = MSI_IterateRecords(view, NULL, ITERATE_CostFinalizeConditions,
2068                     package);
2069         msiobj_release(&view->hdr);
2070     }
2071
2072     TRACE("Enabling or Disabling Components\n");
2073     LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2074     {
2075         if (MSI_EvaluateConditionW(package, comp->Condition) == MSICONDITION_FALSE)
2076         {
2077             TRACE("Disabling component %s\n", debugstr_w(comp->Component));
2078             comp->Enabled = FALSE;
2079         }
2080     }
2081
2082     MSI_SetPropertyW(package,szCosting,szOne);
2083     /* set default run level if not set */
2084     level = msi_dup_property( package, szlevel );
2085     if (!level)
2086         MSI_SetPropertyW(package,szlevel, szOne);
2087     msi_free(level);
2088
2089     ACTION_UpdateFeatureInstallStates(package);
2090
2091     return MSI_SetFeatureStates(package);
2092 }
2093
2094 /* OK this value is "interpreted" and then formatted based on the 
2095    first few characters */
2096 static LPSTR parse_value(MSIPACKAGE *package, LPCWSTR value, DWORD *type, 
2097                          DWORD *size)
2098 {
2099     LPSTR data = NULL;
2100     if (value[0]=='#' && value[1]!='#' && value[1]!='%')
2101     {
2102         if (value[1]=='x')
2103         {
2104             LPWSTR ptr;
2105             CHAR byte[5];
2106             LPWSTR deformated = NULL;
2107             int count;
2108
2109             deformat_string(package, &value[2], &deformated);
2110
2111             /* binary value type */
2112             ptr = deformated;
2113             *type = REG_BINARY;
2114             if (strlenW(ptr)%2)
2115                 *size = (strlenW(ptr)/2)+1;
2116             else
2117                 *size = strlenW(ptr)/2;
2118
2119             data = msi_alloc(*size);
2120
2121             byte[0] = '0'; 
2122             byte[1] = 'x'; 
2123             byte[4] = 0; 
2124             count = 0;
2125             /* if uneven pad with a zero in front */
2126             if (strlenW(ptr)%2)
2127             {
2128                 byte[2]= '0';
2129                 byte[3]= *ptr;
2130                 ptr++;
2131                 data[count] = (BYTE)strtol(byte,NULL,0);
2132                 count ++;
2133                 TRACE("Uneven byte count\n");
2134             }
2135             while (*ptr)
2136             {
2137                 byte[2]= *ptr;
2138                 ptr++;
2139                 byte[3]= *ptr;
2140                 ptr++;
2141                 data[count] = (BYTE)strtol(byte,NULL,0);
2142                 count ++;
2143             }
2144             msi_free(deformated);
2145
2146             TRACE("Data %i bytes(%i)\n",*size,count);
2147         }
2148         else
2149         {
2150             LPWSTR deformated;
2151             LPWSTR p;
2152             DWORD d = 0;
2153             deformat_string(package, &value[1], &deformated);
2154
2155             *type=REG_DWORD; 
2156             *size = sizeof(DWORD);
2157             data = msi_alloc(*size);
2158             p = deformated;
2159             if (*p == '-')
2160                 p++;
2161             while (*p)
2162             {
2163                 if ( (*p < '0') || (*p > '9') )
2164                     break;
2165                 d *= 10;
2166                 d += (*p - '0');
2167                 p++;
2168             }
2169             if (deformated[0] == '-')
2170                 d = -d;
2171             *(LPDWORD)data = d;
2172             TRACE("DWORD %i\n",*(LPDWORD)data);
2173
2174             msi_free(deformated);
2175         }
2176     }
2177     else
2178     {
2179         static const WCHAR szMulti[] = {'[','~',']',0};
2180         LPCWSTR ptr;
2181         *type=REG_SZ;
2182
2183         if (value[0]=='#')
2184         {
2185             if (value[1]=='%')
2186             {
2187                 ptr = &value[2];
2188                 *type=REG_EXPAND_SZ;
2189             }
2190             else
2191                 ptr = &value[1];
2192          }
2193          else
2194             ptr=value;
2195
2196         if (strstrW(value,szMulti))
2197             *type = REG_MULTI_SZ;
2198
2199         *size = deformat_string(package, ptr,(LPWSTR*)&data);
2200     }
2201     return data;
2202 }
2203
2204 static UINT ITERATE_WriteRegistryValues(MSIRECORD *row, LPVOID param)
2205 {
2206     MSIPACKAGE *package = (MSIPACKAGE*)param;
2207     static const WCHAR szHCR[] = 
2208         {'H','K','E','Y','_','C','L','A','S','S','E','S','_',
2209          'R','O','O','T','\\',0};
2210     static const WCHAR szHCU[] =
2211         {'H','K','E','Y','_','C','U','R','R','E','N','T','_',
2212          'U','S','E','R','\\',0};
2213     static const WCHAR szHLM[] =
2214         {'H','K','E','Y','_','L','O','C','A','L','_',
2215          'M','A','C','H','I','N','E','\\',0};
2216     static const WCHAR szHU[] =
2217         {'H','K','E','Y','_','U','S','E','R','S','\\',0};
2218
2219     LPSTR value_data = NULL;
2220     HKEY  root_key, hkey;
2221     DWORD type,size;
2222     LPWSTR  deformated;
2223     LPCWSTR szRoot, component, name, key, value;
2224     MSICOMPONENT *comp;
2225     MSIRECORD * uirow;
2226     LPWSTR uikey;
2227     INT   root;
2228     BOOL check_first = FALSE;
2229     UINT rc;
2230
2231     ui_progress(package,2,0,0,0);
2232
2233     value = NULL;
2234     key = NULL;
2235     uikey = NULL;
2236     name = NULL;
2237
2238     component = MSI_RecordGetString(row, 6);
2239     comp = get_loaded_component(package,component);
2240     if (!comp)
2241         return ERROR_SUCCESS;
2242
2243     if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
2244     {
2245         TRACE("Skipping write due to disabled component %s\n",
2246                         debugstr_w(component));
2247
2248         comp->Action = comp->Installed;
2249
2250         return ERROR_SUCCESS;
2251     }
2252
2253     comp->Action = INSTALLSTATE_LOCAL;
2254
2255     name = MSI_RecordGetString(row, 4);
2256     if( MSI_RecordIsNull(row,5) && name )
2257     {
2258         /* null values can have special meanings */
2259         if (name[0]=='-' && name[1] == 0)
2260                 return ERROR_SUCCESS;
2261         else if ((name[0]=='+' && name[1] == 0) || 
2262                  (name[0] == '*' && name[1] == 0))
2263                 name = NULL;
2264         check_first = TRUE;
2265     }
2266
2267     root = MSI_RecordGetInteger(row,2);
2268     key = MSI_RecordGetString(row, 3);
2269
2270     /* get the root key */
2271     switch (root)
2272     {
2273         case -1: 
2274             {
2275                 static const WCHAR szALLUSER[] = {'A','L','L','U','S','E','R','S',0};
2276                 LPWSTR all_users = msi_dup_property( package, szALLUSER );
2277                 if (all_users && all_users[0] == '1')
2278                 {
2279                     root_key = HKEY_LOCAL_MACHINE;
2280                     szRoot = szHLM;
2281                 }
2282                 else
2283                 {
2284                     root_key = HKEY_CURRENT_USER;
2285                     szRoot = szHCU;
2286                 }
2287                 msi_free(all_users);
2288             }
2289                  break;
2290         case 0:  root_key = HKEY_CLASSES_ROOT; 
2291                  szRoot = szHCR;
2292                  break;
2293         case 1:  root_key = HKEY_CURRENT_USER;
2294                  szRoot = szHCU;
2295                  break;
2296         case 2:  root_key = HKEY_LOCAL_MACHINE;
2297                  szRoot = szHLM;
2298                  break;
2299         case 3:  root_key = HKEY_USERS; 
2300                  szRoot = szHU;
2301                  break;
2302         default:
2303                  ERR("Unknown root %i\n",root);
2304                  root_key=NULL;
2305                  szRoot = NULL;
2306                  break;
2307     }
2308     if (!root_key)
2309         return ERROR_SUCCESS;
2310
2311     deformat_string(package, key , &deformated);
2312     size = strlenW(deformated) + strlenW(szRoot) + 1;
2313     uikey = msi_alloc(size*sizeof(WCHAR));
2314     strcpyW(uikey,szRoot);
2315     strcatW(uikey,deformated);
2316
2317     if (RegCreateKeyW( root_key, deformated, &hkey))
2318     {
2319         ERR("Could not create key %s\n",debugstr_w(deformated));
2320         msi_free(deformated);
2321         msi_free(uikey);
2322         return ERROR_SUCCESS;
2323     }
2324     msi_free(deformated);
2325
2326     value = MSI_RecordGetString(row,5);
2327     if (value)
2328         value_data = parse_value(package, value, &type, &size); 
2329     else
2330     {
2331         static const WCHAR szEmpty[] = {0};
2332         value_data = (LPSTR)strdupW(szEmpty);
2333         size = 0;
2334         type = REG_SZ;
2335     }
2336
2337     deformat_string(package, name, &deformated);
2338
2339     /* get the double nulls to terminate SZ_MULTI */
2340     if (type == REG_MULTI_SZ)
2341         size +=sizeof(WCHAR);
2342
2343     if (!check_first)
2344     {
2345         TRACE("Setting value %s of %s\n",debugstr_w(deformated),
2346                         debugstr_w(uikey));
2347         RegSetValueExW(hkey, deformated, 0, type, (LPBYTE)value_data, size);
2348     }
2349     else
2350     {
2351         DWORD sz = 0;
2352         rc = RegQueryValueExW(hkey, deformated, NULL, NULL, NULL, &sz);
2353         if (rc == ERROR_SUCCESS || rc == ERROR_MORE_DATA)
2354         {
2355             TRACE("value %s of %s checked already exists\n",
2356                             debugstr_w(deformated), debugstr_w(uikey));
2357         }
2358         else
2359         {
2360             TRACE("Checked and setting value %s of %s\n",
2361                             debugstr_w(deformated), debugstr_w(uikey));
2362             if (deformated || size)
2363                 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE) value_data, size);
2364         }
2365     }
2366     RegCloseKey(hkey);
2367
2368     uirow = MSI_CreateRecord(3);
2369     MSI_RecordSetStringW(uirow,2,deformated);
2370     MSI_RecordSetStringW(uirow,1,uikey);
2371
2372     if (type == REG_SZ)
2373         MSI_RecordSetStringW(uirow,3,(LPWSTR)value_data);
2374     else
2375         MSI_RecordSetStringW(uirow,3,value);
2376
2377     ui_actiondata(package,szWriteRegistryValues,uirow);
2378     msiobj_release( &uirow->hdr );
2379
2380     msi_free(value_data);
2381     msi_free(deformated);
2382     msi_free(uikey);
2383
2384     return ERROR_SUCCESS;
2385 }
2386
2387 static UINT ACTION_WriteRegistryValues(MSIPACKAGE *package)
2388 {
2389     UINT rc;
2390     MSIQUERY * view;
2391     static const WCHAR ExecSeqQuery[] =
2392         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2393          '`','R','e','g','i','s','t','r','y','`',0 };
2394
2395     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2396     if (rc != ERROR_SUCCESS)
2397         return ERROR_SUCCESS;
2398
2399     /* increment progress bar each time action data is sent */
2400     ui_progress(package,1,REG_PROGRESS_VALUE,1,0);
2401
2402     rc = MSI_IterateRecords(view, NULL, ITERATE_WriteRegistryValues, package);
2403
2404     msiobj_release(&view->hdr);
2405     return rc;
2406 }
2407
2408 static UINT ACTION_InstallInitialize(MSIPACKAGE *package)
2409 {
2410     package->script->CurrentlyScripting = TRUE;
2411
2412     return ERROR_SUCCESS;
2413 }
2414
2415
2416 static UINT ACTION_InstallValidate(MSIPACKAGE *package)
2417 {
2418     MSICOMPONENT *comp;
2419     DWORD progress = 0;
2420     DWORD total = 0;
2421     static const WCHAR q1[]=
2422         {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
2423          '`','R','e','g','i','s','t','r','y','`',0};
2424     UINT rc;
2425     MSIQUERY * view;
2426     MSIFEATURE *feature;
2427     MSIFILE *file;
2428
2429     TRACE("InstallValidate\n");
2430
2431     rc = MSI_DatabaseOpenViewW(package->db, q1, &view);
2432     if (rc == ERROR_SUCCESS)
2433     {
2434         MSI_IterateRecords( view, &progress, NULL, package );
2435         msiobj_release( &view->hdr );
2436         total += progress * REG_PROGRESS_VALUE;
2437     }
2438
2439     LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2440         total += COMPONENT_PROGRESS_VALUE;
2441
2442     LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2443         total += file->FileSize;
2444
2445     ui_progress(package,0,total,0,0);
2446
2447     LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2448     {
2449         TRACE("Feature: %s; Installed: %i; Action %i; Request %i\n",
2450             debugstr_w(feature->Feature), feature->Installed, feature->Action,
2451             feature->ActionRequest);
2452     }
2453     
2454     return ERROR_SUCCESS;
2455 }
2456
2457 static UINT ITERATE_LaunchConditions(MSIRECORD *row, LPVOID param)
2458 {
2459     MSIPACKAGE* package = (MSIPACKAGE*)param;
2460     LPCWSTR cond = NULL; 
2461     LPCWSTR message = NULL;
2462     static const WCHAR title[]=
2463         {'I','n','s','t','a','l','l',' ','F','a', 'i','l','e','d',0};
2464
2465     cond = MSI_RecordGetString(row,1);
2466
2467     if (MSI_EvaluateConditionW(package,cond) != MSICONDITION_TRUE)
2468     {
2469         LPWSTR deformated;
2470         message = MSI_RecordGetString(row,2);
2471         deformat_string(package,message,&deformated); 
2472         MessageBoxW(NULL,deformated,title,MB_OK);
2473         msi_free(deformated);
2474         return ERROR_FUNCTION_FAILED;
2475     }
2476
2477     return ERROR_SUCCESS;
2478 }
2479
2480 static UINT ACTION_LaunchConditions(MSIPACKAGE *package)
2481 {
2482     UINT rc;
2483     MSIQUERY * view = NULL;
2484     static const WCHAR ExecSeqQuery[] =
2485         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2486          '`','L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','`',0};
2487
2488     TRACE("Checking launch conditions\n");
2489
2490     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2491     if (rc != ERROR_SUCCESS)
2492         return ERROR_SUCCESS;
2493
2494     rc = MSI_IterateRecords(view, NULL, ITERATE_LaunchConditions, package);
2495     msiobj_release(&view->hdr);
2496
2497     return rc;
2498 }
2499
2500 static LPWSTR resolve_keypath( MSIPACKAGE* package, MSICOMPONENT *cmp )
2501 {
2502
2503     if (!cmp->KeyPath)
2504         return resolve_folder(package,cmp->Directory,FALSE,FALSE,NULL);
2505
2506     if (cmp->Attributes & msidbComponentAttributesRegistryKeyPath)
2507     {
2508         MSIRECORD * row = 0;
2509         UINT root,len;
2510         LPWSTR deformated,buffer,deformated_name;
2511         LPCWSTR key,name;
2512         static const WCHAR ExecSeqQuery[] =
2513             {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2514              '`','R','e','g','i','s','t','r','y','`',' ',
2515              'W','H','E','R','E',' ', '`','R','e','g','i','s','t','r','y','`',
2516              ' ','=',' ' ,'\'','%','s','\'',0 };
2517         static const WCHAR fmt[]={'%','0','2','i',':','\\','%','s','\\',0};
2518         static const WCHAR fmt2[]=
2519             {'%','0','2','i',':','\\','%','s','\\','%','s',0};
2520
2521         row = MSI_QueryGetRecord(package->db, ExecSeqQuery,cmp->KeyPath);
2522         if (!row)
2523             return NULL;
2524
2525         root = MSI_RecordGetInteger(row,2);
2526         key = MSI_RecordGetString(row, 3);
2527         name = MSI_RecordGetString(row, 4);
2528         deformat_string(package, key , &deformated);
2529         deformat_string(package, name, &deformated_name);
2530
2531         len = strlenW(deformated) + 6;
2532         if (deformated_name)
2533             len+=strlenW(deformated_name);
2534
2535         buffer = msi_alloc( len *sizeof(WCHAR));
2536
2537         if (deformated_name)
2538             sprintfW(buffer,fmt2,root,deformated,deformated_name);
2539         else
2540             sprintfW(buffer,fmt,root,deformated);
2541
2542         msi_free(deformated);
2543         msi_free(deformated_name);
2544         msiobj_release(&row->hdr);
2545
2546         return buffer;
2547     }
2548     else if (cmp->Attributes & msidbComponentAttributesODBCDataSource)
2549     {
2550         FIXME("UNIMPLEMENTED keypath as ODBC Source\n");
2551         return NULL;
2552     }
2553     else
2554     {
2555         MSIFILE *file = get_loaded_file( package, cmp->KeyPath );
2556
2557         if (file)
2558             return strdupW( file->TargetPath );
2559     }
2560     return NULL;
2561 }
2562
2563 static HKEY openSharedDLLsKey(void)
2564 {
2565     HKEY hkey=0;
2566     static const WCHAR path[] =
2567         {'S','o','f','t','w','a','r','e','\\',
2568          'M','i','c','r','o','s','o','f','t','\\',
2569          'W','i','n','d','o','w','s','\\',
2570          'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
2571          'S','h','a','r','e','d','D','L','L','s',0};
2572
2573     RegCreateKeyW(HKEY_LOCAL_MACHINE,path,&hkey);
2574     return hkey;
2575 }
2576
2577 static UINT ACTION_GetSharedDLLsCount(LPCWSTR dll)
2578 {
2579     HKEY hkey;
2580     DWORD count=0;
2581     DWORD type;
2582     DWORD sz = sizeof(count);
2583     DWORD rc;
2584     
2585     hkey = openSharedDLLsKey();
2586     rc = RegQueryValueExW(hkey, dll, NULL, &type, (LPBYTE)&count, &sz);
2587     if (rc != ERROR_SUCCESS)
2588         count = 0;
2589     RegCloseKey(hkey);
2590     return count;
2591 }
2592
2593 static UINT ACTION_WriteSharedDLLsCount(LPCWSTR path, UINT count)
2594 {
2595     HKEY hkey;
2596
2597     hkey = openSharedDLLsKey();
2598     if (count > 0)
2599         msi_reg_set_val_dword( hkey, path, count );
2600     else
2601         RegDeleteValueW(hkey,path);
2602     RegCloseKey(hkey);
2603     return count;
2604 }
2605
2606 /*
2607  * Return TRUE if the count should be written out and FALSE if not
2608  */
2609 static void ACTION_RefCountComponent( MSIPACKAGE* package, MSICOMPONENT *comp )
2610 {
2611     MSIFEATURE *feature;
2612     INT count = 0;
2613     BOOL write = FALSE;
2614
2615     /* only refcount DLLs */
2616     if (comp->KeyPath == NULL || 
2617         comp->Attributes & msidbComponentAttributesRegistryKeyPath || 
2618         comp->Attributes & msidbComponentAttributesODBCDataSource)
2619         write = FALSE;
2620     else
2621     {
2622         count = ACTION_GetSharedDLLsCount( comp->FullKeypath);
2623         write = (count > 0);
2624
2625         if (comp->Attributes & msidbComponentAttributesSharedDllRefCount)
2626             write = TRUE;
2627     }
2628
2629     /* increment counts */
2630     LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2631     {
2632         ComponentList *cl;
2633
2634         if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_LOCAL ))
2635             continue;
2636
2637         LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
2638         {
2639             if ( cl->component == comp )
2640                 count++;
2641         }
2642     }
2643
2644     /* decrement counts */
2645     LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2646     {
2647         ComponentList *cl;
2648
2649         if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_ABSENT ))
2650             continue;
2651
2652         LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
2653         {
2654             if ( cl->component == comp )
2655                 count--;
2656         }
2657     }
2658
2659     /* ref count all the files in the component */
2660     if (write)
2661     {
2662         MSIFILE *file;
2663
2664         LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2665         {
2666             if (file->Component == comp)
2667                 ACTION_WriteSharedDLLsCount( file->TargetPath, count );
2668         }
2669     }
2670     
2671     /* add a count for permenent */
2672     if (comp->Attributes & msidbComponentAttributesPermanent)
2673         count ++;
2674     
2675     comp->RefCount = count;
2676
2677     if (write)
2678         ACTION_WriteSharedDLLsCount( comp->FullKeypath, comp->RefCount );
2679 }
2680
2681 /*
2682  * Ok further analysis makes me think that this work is
2683  * actually done in the PublishComponents and PublishFeatures
2684  * step, and not here.  It appears like the keypath and all that is
2685  * resolved in this step, however actually written in the Publish steps.
2686  * But we will leave it here for now because it is unclear
2687  */
2688 static UINT ACTION_ProcessComponents(MSIPACKAGE *package)
2689 {
2690     WCHAR squished_pc[GUID_SIZE];
2691     WCHAR squished_cc[GUID_SIZE];
2692     UINT rc;
2693     MSICOMPONENT *comp;
2694     HKEY hkey=0,hkey2=0;
2695
2696     /* writes the Component and Features values to the registry */
2697
2698     rc = MSIREG_OpenComponents(&hkey);
2699     if (rc != ERROR_SUCCESS)
2700         return rc;
2701
2702     squash_guid(package->ProductCode,squished_pc);
2703     ui_progress(package,1,COMPONENT_PROGRESS_VALUE,1,0);
2704
2705     LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2706     {
2707         MSIRECORD * uirow;
2708
2709         ui_progress(package,2,0,0,0);
2710         if (!comp->ComponentId)
2711             continue;
2712
2713         squash_guid(comp->ComponentId,squished_cc);
2714            
2715         msi_free(comp->FullKeypath);
2716         comp->FullKeypath = resolve_keypath( package, comp );
2717
2718         /* do the refcounting */
2719         ACTION_RefCountComponent( package, comp );
2720
2721         TRACE("Component %s (%s), Keypath=%s, RefCount=%i\n",
2722                             debugstr_w(comp->Component),
2723                             debugstr_w(squished_cc),
2724                             debugstr_w(comp->FullKeypath),
2725                             comp->RefCount);
2726         /*
2727          * Write the keypath out if the component is to be registered
2728          * and delete the key if the component is to be deregistered
2729          */
2730         if (ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
2731         {
2732             rc = RegCreateKeyW(hkey,squished_cc,&hkey2);
2733             if (rc != ERROR_SUCCESS)
2734                 continue;
2735
2736             if (!comp->FullKeypath)
2737                 continue;
2738
2739             msi_reg_set_val_str( hkey2, squished_pc, comp->FullKeypath );
2740
2741             if (comp->Attributes & msidbComponentAttributesPermanent)
2742             {
2743                 static const WCHAR szPermKey[] =
2744                     { '0','0','0','0','0','0','0','0','0','0','0','0',
2745                       '0','0','0','0','0','0','0','0','0','0','0','0',
2746                       '0','0','0','0','0','0','0','0',0 };
2747
2748                 msi_reg_set_val_str( hkey2, szPermKey, comp->FullKeypath );
2749             }
2750
2751             RegCloseKey(hkey2);
2752
2753             /* UI stuff */
2754             uirow = MSI_CreateRecord(3);
2755             MSI_RecordSetStringW(uirow,1,package->ProductCode);
2756             MSI_RecordSetStringW(uirow,2,comp->ComponentId);
2757             MSI_RecordSetStringW(uirow,3,comp->FullKeypath);
2758             ui_actiondata(package,szProcessComponents,uirow);
2759             msiobj_release( &uirow->hdr );
2760         }
2761         else if (ACTION_VerifyComponentForAction( comp, INSTALLSTATE_ABSENT))
2762         {
2763             DWORD res;
2764
2765             rc = RegOpenKeyW(hkey,squished_cc,&hkey2);
2766             if (rc != ERROR_SUCCESS)
2767                 continue;
2768
2769             RegDeleteValueW(hkey2,squished_pc);
2770
2771             /* if the key is empty delete it */
2772             res = RegEnumKeyExW(hkey2,0,NULL,0,0,NULL,0,NULL);
2773             RegCloseKey(hkey2);
2774             if (res == ERROR_NO_MORE_ITEMS)
2775                 RegDeleteKeyW(hkey,squished_cc);
2776
2777             /* UI stuff */
2778             uirow = MSI_CreateRecord(2);
2779             MSI_RecordSetStringW(uirow,1,package->ProductCode);
2780             MSI_RecordSetStringW(uirow,2,comp->ComponentId);
2781             ui_actiondata(package,szProcessComponents,uirow);
2782             msiobj_release( &uirow->hdr );
2783         }
2784     } 
2785     RegCloseKey(hkey);
2786     return rc;
2787 }
2788
2789 typedef struct {
2790     CLSID       clsid;
2791     LPWSTR      source;
2792
2793     LPWSTR      path;
2794     ITypeLib    *ptLib;
2795 } typelib_struct;
2796
2797 static BOOL CALLBACK Typelib_EnumResNameProc( HMODULE hModule, LPCWSTR lpszType, 
2798                                        LPWSTR lpszName, LONG_PTR lParam)
2799 {
2800     TLIBATTR *attr;
2801     typelib_struct *tl_struct = (typelib_struct*) lParam;
2802     static const WCHAR fmt[] = {'%','s','\\','%','i',0};
2803     int sz; 
2804     HRESULT res;
2805
2806     if (!IS_INTRESOURCE(lpszName))
2807     {
2808         ERR("Not Int Resource Name %s\n",debugstr_w(lpszName));
2809         return TRUE;
2810     }
2811
2812     sz = strlenW(tl_struct->source)+4;
2813     sz *= sizeof(WCHAR);
2814
2815     if ((INT_PTR)lpszName == 1)
2816         tl_struct->path = strdupW(tl_struct->source);
2817     else
2818     {
2819         tl_struct->path = msi_alloc(sz);
2820         sprintfW(tl_struct->path,fmt,tl_struct->source, lpszName);
2821     }
2822
2823     TRACE("trying %s\n", debugstr_w(tl_struct->path));
2824     res = LoadTypeLib(tl_struct->path,&tl_struct->ptLib);
2825     if (!SUCCEEDED(res))
2826     {
2827         msi_free(tl_struct->path);
2828         tl_struct->path = NULL;
2829
2830         return TRUE;
2831     }
2832
2833     ITypeLib_GetLibAttr(tl_struct->ptLib, &attr);
2834     if (IsEqualGUID(&(tl_struct->clsid),&(attr->guid)))
2835     {
2836         ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
2837         return FALSE;
2838     }
2839
2840     msi_free(tl_struct->path);
2841     tl_struct->path = NULL;
2842
2843     ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
2844     ITypeLib_Release(tl_struct->ptLib);
2845
2846     return TRUE;
2847 }
2848
2849 static UINT ITERATE_RegisterTypeLibraries(MSIRECORD *row, LPVOID param)
2850 {
2851     MSIPACKAGE* package = (MSIPACKAGE*)param;
2852     LPCWSTR component;
2853     MSICOMPONENT *comp;
2854     MSIFILE *file;
2855     typelib_struct tl_struct;
2856     HMODULE module;
2857     static const WCHAR szTYPELIB[] = {'T','Y','P','E','L','I','B',0};
2858
2859     component = MSI_RecordGetString(row,3);
2860     comp = get_loaded_component(package,component);
2861     if (!comp)
2862         return ERROR_SUCCESS;
2863
2864     if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
2865     {
2866         TRACE("Skipping typelib reg due to disabled component\n");
2867
2868         comp->Action = comp->Installed;
2869
2870         return ERROR_SUCCESS;
2871     }
2872
2873     comp->Action = INSTALLSTATE_LOCAL;
2874
2875     file = get_loaded_file( package, comp->KeyPath ); 
2876     if (!file)
2877         return ERROR_SUCCESS;
2878
2879     module = LoadLibraryExW( file->TargetPath, NULL, LOAD_LIBRARY_AS_DATAFILE );
2880     if (module)
2881     {
2882         LPCWSTR guid;
2883         guid = MSI_RecordGetString(row,1);
2884         CLSIDFromString((LPWSTR)guid, &tl_struct.clsid);
2885         tl_struct.source = strdupW( file->TargetPath );
2886         tl_struct.path = NULL;
2887
2888         EnumResourceNamesW(module, szTYPELIB, Typelib_EnumResNameProc,
2889                         (LONG_PTR)&tl_struct);
2890
2891         if (tl_struct.path)
2892         {
2893             LPWSTR help = NULL;
2894             LPCWSTR helpid;
2895             HRESULT res;
2896
2897             helpid = MSI_RecordGetString(row,6);
2898
2899             if (helpid)
2900                 help = resolve_folder(package,helpid,FALSE,FALSE,NULL);
2901             res = RegisterTypeLib(tl_struct.ptLib,tl_struct.path,help);
2902             msi_free(help);
2903
2904             if (!SUCCEEDED(res))
2905                 ERR("Failed to register type library %s\n",
2906                         debugstr_w(tl_struct.path));
2907             else
2908             {
2909                 ui_actiondata(package,szRegisterTypeLibraries,row);
2910
2911                 TRACE("Registered %s\n", debugstr_w(tl_struct.path));
2912             }
2913
2914             ITypeLib_Release(tl_struct.ptLib);
2915             msi_free(tl_struct.path);
2916         }
2917         else
2918             ERR("Failed to load type library %s\n",
2919                     debugstr_w(tl_struct.source));
2920
2921         FreeLibrary(module);
2922         msi_free(tl_struct.source);
2923     }
2924     else
2925         ERR("Could not load file! %s\n", debugstr_w(file->TargetPath));
2926
2927     return ERROR_SUCCESS;
2928 }
2929
2930 static UINT ACTION_RegisterTypeLibraries(MSIPACKAGE *package)
2931 {
2932     /* 
2933      * OK this is a bit confusing.. I am given a _Component key and I believe
2934      * that the file that is being registered as a type library is the "key file
2935      * of that component" which I interpret to mean "The file in the KeyPath of
2936      * that component".
2937      */
2938     UINT rc;
2939     MSIQUERY * view;
2940     static const WCHAR Query[] =
2941         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2942          '`','T','y','p','e','L','i','b','`',0};
2943
2944     rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
2945     if (rc != ERROR_SUCCESS)
2946         return ERROR_SUCCESS;
2947
2948     rc = MSI_IterateRecords(view, NULL, ITERATE_RegisterTypeLibraries, package);
2949     msiobj_release(&view->hdr);
2950     return rc;
2951 }
2952
2953 static UINT ITERATE_CreateShortcuts(MSIRECORD *row, LPVOID param)
2954 {
2955     MSIPACKAGE *package = (MSIPACKAGE*)param;
2956     LPWSTR target_file, target_folder, filename;
2957     LPCWSTR buffer, extension;
2958     MSICOMPONENT *comp;
2959     static const WCHAR szlnk[]={'.','l','n','k',0};
2960     IShellLinkW *sl = NULL;
2961     IPersistFile *pf = NULL;
2962     HRESULT res;
2963
2964     buffer = MSI_RecordGetString(row,4);
2965     comp = get_loaded_component(package,buffer);
2966     if (!comp)
2967         return ERROR_SUCCESS;
2968
2969     if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL ))
2970     {
2971         TRACE("Skipping shortcut creation due to disabled component\n");
2972
2973         comp->Action = comp->Installed;
2974
2975         return ERROR_SUCCESS;
2976     }
2977
2978     comp->Action = INSTALLSTATE_LOCAL;
2979
2980     ui_actiondata(package,szCreateShortcuts,row);
2981
2982     res = CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
2983                     &IID_IShellLinkW, (LPVOID *) &sl );
2984
2985     if (FAILED( res ))
2986     {
2987         ERR("CLSID_ShellLink not available\n");
2988         goto err;
2989     }
2990
2991     res = IShellLinkW_QueryInterface( sl, &IID_IPersistFile,(LPVOID*) &pf );
2992     if (FAILED( res ))
2993     {
2994         ERR("QueryInterface(IID_IPersistFile) failed\n");
2995         goto err;
2996     }
2997
2998     buffer = MSI_RecordGetString(row,2);
2999     target_folder = resolve_folder(package, buffer,FALSE,FALSE,NULL);
3000
3001     /* may be needed because of a bug somehwere else */
3002     create_full_pathW(target_folder);
3003
3004     filename = msi_dup_record_field( row, 3 );
3005     reduce_to_longfilename(filename);
3006
3007     extension = strchrW(filename,'.');
3008     if (!extension || strcmpiW(extension,szlnk))
3009     {
3010         int len = strlenW(filename);
3011         filename = msi_realloc(filename, len * sizeof(WCHAR) + sizeof(szlnk));
3012         memcpy(filename + len, szlnk, sizeof(szlnk));
3013     }
3014     target_file = build_directory_name(2, target_folder, filename);
3015     msi_free(target_folder);
3016     msi_free(filename);
3017
3018     buffer = MSI_RecordGetString(row,5);
3019     if (strchrW(buffer,'['))
3020     {
3021         LPWSTR deformated;
3022         deformat_string(package,buffer,&deformated);
3023         IShellLinkW_SetPath(sl,deformated);
3024         msi_free(deformated);
3025     }
3026     else
3027     {
3028         FIXME("poorly handled shortcut format, advertised shortcut\n");
3029         IShellLinkW_SetPath(sl,comp->FullKeypath);
3030     }
3031
3032     if (!MSI_RecordIsNull(row,6))
3033     {
3034         LPWSTR deformated;
3035         buffer = MSI_RecordGetString(row,6);
3036         deformat_string(package,buffer,&deformated);
3037         IShellLinkW_SetArguments(sl,deformated);
3038         msi_free(deformated);
3039     }
3040
3041     if (!MSI_RecordIsNull(row,7))
3042     {
3043         buffer = MSI_RecordGetString(row,7);
3044         IShellLinkW_SetDescription(sl,buffer);
3045     }
3046
3047     if (!MSI_RecordIsNull(row,8))
3048         IShellLinkW_SetHotkey(sl,MSI_RecordGetInteger(row,8));
3049
3050     if (!MSI_RecordIsNull(row,9))
3051     {
3052         LPWSTR Path;
3053         INT index; 
3054
3055         buffer = MSI_RecordGetString(row,9);
3056
3057         Path = build_icon_path(package,buffer);
3058         index = MSI_RecordGetInteger(row,10);
3059
3060         /* no value means 0 */
3061         if (index == MSI_NULL_INTEGER)
3062             index = 0;
3063
3064         IShellLinkW_SetIconLocation(sl,Path,index);
3065         msi_free(Path);
3066     }
3067
3068     if (!MSI_RecordIsNull(row,11))
3069         IShellLinkW_SetShowCmd(sl,MSI_RecordGetInteger(row,11));
3070
3071     if (!MSI_RecordIsNull(row,12))
3072     {
3073         LPWSTR Path;
3074         buffer = MSI_RecordGetString(row,12);
3075         Path = resolve_folder(package, buffer, FALSE, FALSE, NULL);
3076         if (Path)
3077             IShellLinkW_SetWorkingDirectory(sl,Path);
3078         msi_free(Path);
3079     }
3080
3081     TRACE("Writing shortcut to %s\n",debugstr_w(target_file));
3082     IPersistFile_Save(pf,target_file,FALSE);
3083
3084     msi_free(target_file);    
3085
3086 err:
3087     if (pf)
3088         IPersistFile_Release( pf );
3089     if (sl)
3090         IShellLinkW_Release( sl );
3091
3092     return ERROR_SUCCESS;
3093 }
3094
3095 static UINT ACTION_CreateShortcuts(MSIPACKAGE *package)
3096 {
3097     UINT rc;
3098     HRESULT res;
3099     MSIQUERY * view;
3100     static const WCHAR Query[] =
3101         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3102          '`','S','h','o','r','t','c','u','t','`',0};
3103
3104     rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3105     if (rc != ERROR_SUCCESS)
3106         return ERROR_SUCCESS;
3107
3108     res = CoInitialize( NULL );
3109     if (FAILED (res))
3110     {
3111         ERR("CoInitialize failed\n");
3112         return ERROR_FUNCTION_FAILED;
3113     }
3114
3115     rc = MSI_IterateRecords(view, NULL, ITERATE_CreateShortcuts, package);
3116     msiobj_release(&view->hdr);
3117
3118     CoUninitialize();
3119
3120     return rc;
3121 }
3122
3123 static UINT ITERATE_PublishProduct(MSIRECORD *row, LPVOID param)
3124 {
3125     MSIPACKAGE* package = (MSIPACKAGE*)param;
3126     HANDLE the_file;
3127     LPWSTR FilePath;
3128     LPCWSTR FileName;
3129     CHAR buffer[1024];
3130     DWORD sz;
3131     UINT rc;
3132     MSIRECORD *uirow;
3133
3134     FileName = MSI_RecordGetString(row,1);
3135     if (!FileName)
3136     {
3137         ERR("Unable to get FileName\n");
3138         return ERROR_SUCCESS;
3139     }
3140
3141     FilePath = build_icon_path(package,FileName);
3142
3143     TRACE("Creating icon file at %s\n",debugstr_w(FilePath));
3144
3145     the_file = CreateFileW(FilePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
3146                         FILE_ATTRIBUTE_NORMAL, NULL);
3147
3148     if (the_file == INVALID_HANDLE_VALUE)
3149     {
3150         ERR("Unable to create file %s\n",debugstr_w(FilePath));
3151         msi_free(FilePath);
3152         return ERROR_SUCCESS;
3153     }
3154
3155     do 
3156     {
3157         DWORD write;
3158         sz = 1024;
3159         rc = MSI_RecordReadStream(row,2,buffer,&sz);
3160         if (rc != ERROR_SUCCESS)
3161         {
3162             ERR("Failed to get stream\n");
3163             CloseHandle(the_file);  
3164             DeleteFileW(FilePath);
3165             break;
3166         }
3167         WriteFile(the_file,buffer,sz,&write,NULL);
3168     } while (sz == 1024);
3169
3170     msi_free(FilePath);
3171
3172     CloseHandle(the_file);
3173
3174     uirow = MSI_CreateRecord(1);
3175     MSI_RecordSetStringW(uirow,1,FileName);
3176     ui_actiondata(package,szPublishProduct,uirow);
3177     msiobj_release( &uirow->hdr );
3178
3179     return ERROR_SUCCESS;
3180 }
3181
3182 /*
3183  * 99% of the work done here is only done for 
3184  * advertised installs. However this is where the
3185  * Icon table is processed and written out
3186  * so that is what I am going to do here.
3187  */
3188 static UINT ACTION_PublishProduct(MSIPACKAGE *package)
3189 {
3190     UINT rc;
3191     MSIQUERY * view;
3192     static const WCHAR Query[]=
3193         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3194          '`','I','c','o','n','`',0};
3195     /* for registry stuff */
3196     HKEY hkey=0;
3197     HKEY hukey=0;
3198     static const WCHAR szProductLanguage[] =
3199         {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
3200     static const WCHAR szARPProductIcon[] =
3201         {'A','R','P','P','R','O','D','U','C','T','I','C','O','N',0};
3202     static const WCHAR szProductVersion[] =
3203         {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
3204     DWORD langid;
3205     LPWSTR buffer;
3206     DWORD size;
3207     MSIHANDLE hDb, hSumInfo;
3208
3209     /* write out icon files */
3210
3211     rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3212     if (rc == ERROR_SUCCESS)
3213     {
3214         MSI_IterateRecords(view, NULL, ITERATE_PublishProduct, package);
3215         msiobj_release(&view->hdr);
3216     }
3217
3218     /* ok there is a lot more done here but i need to figure out what */
3219
3220     rc = MSIREG_OpenProductsKey(package->ProductCode,&hkey,TRUE);
3221     if (rc != ERROR_SUCCESS)
3222         goto end;
3223
3224     rc = MSIREG_OpenUserProductsKey(package->ProductCode,&hukey,TRUE);
3225     if (rc != ERROR_SUCCESS)
3226         goto end;
3227
3228
3229     buffer = msi_dup_property( package, INSTALLPROPERTY_PRODUCTNAMEW );
3230     msi_reg_set_val_str( hukey, INSTALLPROPERTY_PRODUCTNAMEW, buffer );
3231     msi_free(buffer);
3232
3233     langid = msi_get_property_int( package, szProductLanguage, 0 );
3234     msi_reg_set_val_dword( hkey, INSTALLPROPERTY_LANGUAGEW, langid );
3235
3236     buffer = msi_dup_property( package, szARPProductIcon );
3237     if (buffer)
3238     {
3239         LPWSTR path = build_icon_path(package,buffer);
3240         msi_reg_set_val_str( hukey, INSTALLPROPERTY_PRODUCTICONW, path );
3241         msi_free( path );
3242     }
3243     msi_free(buffer);
3244
3245     buffer = msi_dup_property( package, szProductVersion );
3246     if (buffer)
3247     {
3248         DWORD verdword = msi_version_str_to_dword(buffer);
3249         msi_reg_set_val_dword( hkey, INSTALLPROPERTY_VERSIONW, verdword );
3250     }
3251     msi_free(buffer);
3252     
3253     /* FIXME: Need to write more keys to the user registry */
3254   
3255     hDb= alloc_msihandle( &package->db->hdr );
3256     if (!hDb) {
3257         rc = ERROR_NOT_ENOUGH_MEMORY;
3258         goto end;
3259     }
3260     rc = MsiGetSummaryInformationW(hDb, NULL, 0, &hSumInfo); 
3261     MsiCloseHandle(hDb);
3262     if (rc == ERROR_SUCCESS)
3263     {
3264         WCHAR guidbuffer[0x200];
3265         size = 0x200;
3266         rc = MsiSummaryInfoGetPropertyW(hSumInfo, 9, NULL, NULL, NULL,
3267                                         guidbuffer, &size);
3268         if (rc == ERROR_SUCCESS)
3269         {
3270             WCHAR squashed[GUID_SIZE];
3271             /* for now we only care about the first guid */
3272             LPWSTR ptr = strchrW(guidbuffer,';');
3273             if (ptr) *ptr = 0;
3274             squash_guid(guidbuffer,squashed);
3275             msi_reg_set_val_str( hukey, INSTALLPROPERTY_PACKAGECODEW, squashed );
3276         }
3277         else
3278         {
3279             ERR("Unable to query Revision_Number...\n");
3280             rc = ERROR_SUCCESS;
3281         }
3282         MsiCloseHandle(hSumInfo);
3283     }
3284     else
3285     {
3286         ERR("Unable to open Summary Information\n");
3287         rc = ERROR_SUCCESS;
3288     }
3289
3290 end:
3291
3292     RegCloseKey(hkey);
3293     RegCloseKey(hukey);
3294
3295     return rc;
3296 }
3297
3298 static UINT ITERATE_WriteIniValues(MSIRECORD *row, LPVOID param)
3299 {
3300     MSIPACKAGE *package = (MSIPACKAGE*)param;
3301     LPCWSTR component,section,key,value,identifier,filename,dirproperty;
3302     LPWSTR deformated_section, deformated_key, deformated_value;
3303     LPWSTR folder, fullname = NULL;
3304     MSIRECORD * uirow;
3305     INT action;
3306     MSICOMPONENT *comp;
3307     static const WCHAR szWindowsFolder[] =
3308           {'W','i','n','d','o','w','s','F','o','l','d','e','r',0};
3309
3310     component = MSI_RecordGetString(row, 8);
3311     comp = get_loaded_component(package,component);
3312
3313     if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
3314     {
3315         TRACE("Skipping ini file due to disabled component %s\n",
3316                         debugstr_w(component));
3317
3318         comp->Action = comp->Installed;
3319
3320         return ERROR_SUCCESS;
3321     }
3322
3323     comp->Action = INSTALLSTATE_LOCAL;
3324
3325     identifier = MSI_RecordGetString(row,1); 
3326     filename = MSI_RecordGetString(row,2);
3327     dirproperty = MSI_RecordGetString(row,3);
3328     section = MSI_RecordGetString(row,4);
3329     key = MSI_RecordGetString(row,5);
3330     value = MSI_RecordGetString(row,6);
3331     action = MSI_RecordGetInteger(row,7);
3332
3333     deformat_string(package,section,&deformated_section);
3334     deformat_string(package,key,&deformated_key);
3335     deformat_string(package,value,&deformated_value);
3336
3337     if (dirproperty)
3338     {
3339         folder = resolve_folder(package, dirproperty, FALSE, FALSE, NULL);
3340         if (!folder)
3341             folder = msi_dup_property( package, dirproperty );
3342     }
3343     else
3344         folder = msi_dup_property( package, szWindowsFolder );
3345
3346     if (!folder)
3347     {
3348         ERR("Unable to resolve folder! (%s)\n",debugstr_w(dirproperty));
3349         goto cleanup;
3350     }
3351
3352     fullname = build_directory_name(2, folder, filename);
3353
3354     if (action == 0)
3355     {
3356         TRACE("Adding value %s to section %s in %s\n",
3357                 debugstr_w(deformated_key), debugstr_w(deformated_section),
3358                 debugstr_w(fullname));
3359         WritePrivateProfileStringW(deformated_section, deformated_key,
3360                                    deformated_value, fullname);
3361     }
3362     else if (action == 1)
3363     {
3364         WCHAR returned[10];
3365         GetPrivateProfileStringW(deformated_section, deformated_key, NULL,
3366                                  returned, 10, fullname);
3367         if (returned[0] == 0)
3368         {
3369             TRACE("Adding value %s to section %s in %s\n",
3370                     debugstr_w(deformated_key), debugstr_w(deformated_section),
3371                     debugstr_w(fullname));
3372
3373             WritePrivateProfileStringW(deformated_section, deformated_key,
3374                                        deformated_value, fullname);
3375         }
3376     }
3377     else if (action == 3)
3378         FIXME("Append to existing section not yet implemented\n");
3379
3380     uirow = MSI_CreateRecord(4);
3381     MSI_RecordSetStringW(uirow,1,identifier);
3382     MSI_RecordSetStringW(uirow,2,deformated_section);
3383     MSI_RecordSetStringW(uirow,3,deformated_key);
3384     MSI_RecordSetStringW(uirow,4,deformated_value);
3385     ui_actiondata(package,szWriteIniValues,uirow);
3386     msiobj_release( &uirow->hdr );
3387 cleanup:
3388     msi_free(fullname);
3389     msi_free(folder);
3390     msi_free(deformated_key);
3391     msi_free(deformated_value);
3392     msi_free(deformated_section);
3393     return ERROR_SUCCESS;
3394 }
3395
3396 static UINT ACTION_WriteIniValues(MSIPACKAGE *package)
3397 {
3398     UINT rc;
3399     MSIQUERY * view;
3400     static const WCHAR ExecSeqQuery[] = 
3401         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3402          '`','I','n','i','F','i','l','e','`',0};
3403
3404     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
3405     if (rc != ERROR_SUCCESS)
3406     {
3407         TRACE("no IniFile table\n");
3408         return ERROR_SUCCESS;
3409     }
3410
3411     rc = MSI_IterateRecords(view, NULL, ITERATE_WriteIniValues, package);
3412     msiobj_release(&view->hdr);
3413     return rc;
3414 }
3415
3416 static UINT ITERATE_SelfRegModules(MSIRECORD *row, LPVOID param)
3417 {
3418     MSIPACKAGE *package = (MSIPACKAGE*)param;
3419     LPCWSTR filename;
3420     LPWSTR FullName;
3421     MSIFILE *file;
3422     DWORD len;
3423     static const WCHAR ExeStr[] =
3424         {'r','e','g','s','v','r','3','2','.','e','x','e',' ','\"',0};
3425     static const WCHAR close[] =  {'\"',0};
3426     STARTUPINFOW si;
3427     PROCESS_INFORMATION info;
3428     BOOL brc;
3429     MSIRECORD *uirow;
3430     LPWSTR uipath, p;
3431
3432     memset(&si,0,sizeof(STARTUPINFOW));
3433
3434     filename = MSI_RecordGetString(row,1);
3435     file = get_loaded_file( package, filename );
3436
3437     if (!file)
3438     {
3439         ERR("Unable to find file id %s\n",debugstr_w(filename));
3440         return ERROR_SUCCESS;
3441     }
3442
3443     len = strlenW(ExeStr) + strlenW( file->TargetPath ) + 2;
3444
3445     FullName = msi_alloc(len*sizeof(WCHAR));
3446     strcpyW(FullName,ExeStr);
3447     strcatW( FullName, file->TargetPath );
3448     strcatW(FullName,close);
3449
3450     TRACE("Registering %s\n",debugstr_w(FullName));
3451     brc = CreateProcessW(NULL, FullName, NULL, NULL, FALSE, 0, NULL, c_colon,
3452                     &si, &info);
3453
3454     if (brc)
3455         msi_dialog_check_messages(info.hProcess);
3456
3457     msi_free(FullName);
3458
3459     /* the UI chunk */
3460     uirow = MSI_CreateRecord( 2 );
3461     uipath = strdupW( file->TargetPath );
3462     p = strrchrW(uipath,'\\');
3463     if (p)
3464         p[1]=0;
3465     MSI_RecordSetStringW( uirow, 1, &p[2] );
3466     MSI_RecordSetStringW( uirow, 2, uipath);
3467     ui_actiondata( package, szSelfRegModules, uirow);
3468     msiobj_release( &uirow->hdr );
3469     msi_free( uipath );
3470     /* FIXME: call ui_progress? */
3471
3472     return ERROR_SUCCESS;
3473 }
3474
3475 static UINT ACTION_SelfRegModules(MSIPACKAGE *package)
3476 {
3477     UINT rc;
3478     MSIQUERY * view;
3479     static const WCHAR ExecSeqQuery[] = 
3480         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3481          '`','S','e','l','f','R','e','g','`',0};
3482
3483     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
3484     if (rc != ERROR_SUCCESS)
3485     {
3486         TRACE("no SelfReg table\n");
3487         return ERROR_SUCCESS;
3488     }
3489
3490     MSI_IterateRecords(view, NULL, ITERATE_SelfRegModules, package);
3491     msiobj_release(&view->hdr);
3492
3493     return ERROR_SUCCESS;
3494 }
3495
3496 static UINT ACTION_PublishFeatures(MSIPACKAGE *package)
3497 {
3498     MSIFEATURE *feature;
3499     UINT rc;
3500     HKEY hkey=0;
3501     HKEY hukey=0;
3502     
3503     rc = MSIREG_OpenFeaturesKey(package->ProductCode,&hkey,TRUE);
3504     if (rc != ERROR_SUCCESS)
3505         goto end;
3506
3507     rc = MSIREG_OpenUserFeaturesKey(package->ProductCode,&hukey,TRUE);
3508     if (rc != ERROR_SUCCESS)
3509         goto end;
3510
3511     /* here the guids are base 85 encoded */
3512     LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3513     {
3514         ComponentList *cl;
3515         LPWSTR data = NULL;
3516         GUID clsid;
3517         INT size;
3518         BOOL absent = FALSE;
3519         MSIRECORD *uirow;
3520
3521         if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_LOCAL ) &&
3522             !ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_SOURCE ) &&
3523             !ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_ADVERTISED ))
3524             absent = TRUE;
3525
3526         size = 1;
3527         LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3528         {
3529             size += 21;
3530         }
3531         if (feature->Feature_Parent)
3532             size += strlenW( feature->Feature_Parent )+2;
3533
3534         data = msi_alloc(size * sizeof(WCHAR));
3535
3536         data[0] = 0;
3537         LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3538         {
3539             MSICOMPONENT* component = cl->component;
3540             WCHAR buf[21];
3541
3542             buf[0] = 0;
3543             if (component->ComponentId)
3544             {
3545                 TRACE("From %s\n",debugstr_w(component->ComponentId));
3546                 CLSIDFromString(component->ComponentId, &clsid);
3547                 encode_base85_guid(&clsid,buf);
3548                 TRACE("to %s\n",debugstr_w(buf));
3549                 strcatW(data,buf);
3550             }
3551         }
3552         if (feature->Feature_Parent)
3553         {
3554             static const WCHAR sep[] = {'\2',0};
3555             strcatW(data,sep);
3556             strcatW(data,feature->Feature_Parent);
3557         }
3558
3559         msi_reg_set_val_str( hkey, feature->Feature, data );
3560         msi_free(data);
3561
3562         size = 0;
3563         if (feature->Feature_Parent)
3564             size = strlenW(feature->Feature_Parent)*sizeof(WCHAR);
3565         if (!absent)
3566         {
3567             RegSetValueExW(hukey,feature->Feature,0,REG_SZ,
3568                        (LPBYTE)feature->Feature_Parent,size);
3569         }
3570         else
3571         {
3572             size += 2*sizeof(WCHAR);
3573             data = msi_alloc(size);
3574             data[0] = 0x6;
3575             data[1] = 0;
3576             if (feature->Feature_Parent)
3577                 strcpyW( &data[1], feature->Feature_Parent );
3578             RegSetValueExW(hukey,feature->Feature,0,REG_SZ,
3579                        (LPBYTE)data,size);
3580             msi_free(data);
3581         }
3582
3583         /* the UI chunk */
3584         uirow = MSI_CreateRecord( 1 );
3585         MSI_RecordSetStringW( uirow, 1, feature->Feature );
3586         ui_actiondata( package, szPublishFeatures, uirow);
3587         msiobj_release( &uirow->hdr );
3588         /* FIXME: call ui_progress? */
3589     }
3590
3591 end:
3592     RegCloseKey(hkey);
3593     RegCloseKey(hukey);
3594     return rc;
3595 }
3596
3597 static UINT msi_get_local_package_name( LPWSTR path )
3598 {
3599     static const WCHAR szInstaller[] = {
3600         '\\','I','n','s','t','a','l','l','e','r','\\',0};
3601     static const WCHAR fmt[] = { '%','x','.','m','s','i',0};
3602     DWORD time, len, i;
3603     HANDLE handle;
3604
3605     time = GetTickCount();
3606     GetWindowsDirectoryW( path, MAX_PATH );
3607     lstrcatW( path, szInstaller );
3608     CreateDirectoryW( path, NULL );
3609
3610     len = lstrlenW(path);
3611     for (i=0; i<0x10000; i++)
3612     {
3613         snprintfW( &path[len], MAX_PATH - len, fmt, (time+i)&0xffff );
3614         handle = CreateFileW( path, GENERIC_WRITE, 0, NULL,
3615                               CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0 );
3616         if (handle != INVALID_HANDLE_VALUE)
3617         {
3618             CloseHandle(handle);
3619             break;
3620         }
3621         if (GetLastError() != ERROR_FILE_EXISTS &&
3622             GetLastError() != ERROR_SHARING_VIOLATION)
3623             return ERROR_FUNCTION_FAILED;
3624     }
3625
3626     return ERROR_SUCCESS;
3627 }
3628
3629 static UINT msi_make_package_local( MSIPACKAGE *package, HKEY hkey )
3630 {
3631     static const WCHAR szOriginalDatabase[] =
3632         {'O','r','i','g','i','n','a','l','D','a','t','a','b','a','s','e',0};
3633     WCHAR packagefile[MAX_PATH];
3634     LPWSTR msiFilePath;
3635     UINT r;
3636
3637     r = msi_get_local_package_name( packagefile );
3638     if (r != ERROR_SUCCESS)
3639         return r;
3640
3641     TRACE("Copying to local package %s\n",debugstr_w(packagefile));
3642
3643     msiFilePath = msi_dup_property( package, szOriginalDatabase );
3644     r = CopyFileW( msiFilePath, packagefile, FALSE);
3645     msi_free( msiFilePath );
3646
3647     if (!r)
3648     {
3649         ERR("Unable to copy package (%s -> %s) (error %d)\n",
3650             debugstr_w(msiFilePath), debugstr_w(packagefile), GetLastError());
3651         return ERROR_FUNCTION_FAILED;
3652     }
3653
3654     /* FIXME: maybe set this key in ACTION_RegisterProduct instead */
3655     msi_reg_set_val_str( hkey, INSTALLPROPERTY_LOCALPACKAGEW, packagefile );
3656     return ERROR_SUCCESS;
3657 }
3658
3659 static UINT msi_write_uninstall_property_vals( MSIPACKAGE *package, HKEY hkey )
3660 {
3661     LPWSTR prop, val, key;
3662     static const LPCSTR propval[] = {
3663         "ARPAUTHORIZEDCDFPREFIX", "AuthorizedCDFPrefix",
3664         "ARPCONTACT",             "Contact",
3665         "ARPCOMMENTS",            "Comments",
3666         "ProductName",            "DisplayName",
3667         "ProductVersion",         "DisplayVersion",
3668         "ARPHELPLINK",            "HelpLink",
3669         "ARPHELPTELEPHONE",       "HelpTelephone",
3670         "ARPINSTALLLOCATION",     "InstallLocation",
3671         "SourceDir",              "InstallSource",
3672         "Manufacturer",           "Publisher",
3673         "ARPREADME",              "Readme",
3674         "ARPSIZE",                "Size",
3675         "ARPURLINFOABOUT",        "URLInfoAbout",
3676         "ARPURLUPDATEINFO",       "URLUpdateInfo",
3677         NULL,
3678     };
3679     const LPCSTR *p = propval;
3680
3681     while( *p )
3682     {
3683         prop = strdupAtoW( *p++ );
3684         key = strdupAtoW( *p++ );
3685         val = msi_dup_property( package, prop );
3686         msi_reg_set_val_str( hkey, key, val );
3687         msi_free(val);
3688         msi_free(key);
3689         msi_free(prop);
3690     }
3691     return ERROR_SUCCESS;
3692 }
3693
3694 static UINT ACTION_RegisterProduct(MSIPACKAGE *package)
3695 {
3696     HKEY hkey=0;
3697     LPWSTR buffer = NULL;
3698     UINT rc;
3699     DWORD size, langid;
3700     static const WCHAR szWindowsInstaller[] = 
3701         {'W','i','n','d','o','w','s','I','n','s','t','a','l','l','e','r',0};
3702     static const WCHAR szUpgradeCode[] = 
3703         {'U','p','g','r','a','d','e','C','o','d','e',0};
3704     static const WCHAR modpath_fmt[] = 
3705         {'M','s','i','E','x','e','c','.','e','x','e',' ',
3706          '/','I','[','P','r','o','d','u','c','t','C','o','d','e',']',0};
3707     static const WCHAR szModifyPath[] = 
3708         {'M','o','d','i','f','y','P','a','t','h',0};
3709     static const WCHAR szUninstallString[] = 
3710         {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0};
3711     static const WCHAR szEstimatedSize[] = 
3712         {'E','s','t','i','m','a','t','e','d','S','i','z','e',0};
3713     static const WCHAR szProductLanguage[] =
3714         {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
3715     static const WCHAR szProductVersion[] =
3716         {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
3717
3718     SYSTEMTIME systime;
3719     static const WCHAR date_fmt[] = {'%','i','%','i','%','i',0};
3720     LPWSTR upgrade_code;
3721     WCHAR szDate[9]; 
3722
3723     rc = MSIREG_OpenUninstallKey(package->ProductCode,&hkey,TRUE);
3724     if (rc != ERROR_SUCCESS)
3725         return rc;
3726
3727     /* dump all the info i can grab */
3728     /* FIXME: Flesh out more information */
3729
3730     msi_write_uninstall_property_vals( package, hkey );
3731
3732     msi_reg_set_val_dword( hkey, szWindowsInstaller, 1 );
3733     
3734     msi_make_package_local( package, hkey );
3735
3736     /* do ModifyPath and UninstallString */
3737     size = deformat_string(package,modpath_fmt,&buffer);
3738     RegSetValueExW(hkey,szModifyPath,0,REG_EXPAND_SZ,(LPBYTE)buffer,size);
3739     RegSetValueExW(hkey,szUninstallString,0,REG_EXPAND_SZ,(LPBYTE)buffer,size);
3740     msi_free(buffer);
3741
3742     /* FIXME: Write real Estimated Size when we have it */
3743     msi_reg_set_val_dword( hkey, szEstimatedSize, 0 );
3744    
3745     GetLocalTime(&systime);
3746     sprintfW(szDate,date_fmt,systime.wYear,systime.wMonth,systime.wDay);
3747     msi_reg_set_val_str( hkey, INSTALLPROPERTY_INSTALLDATEW, szDate );
3748    
3749     langid = msi_get_property_int( package, szProductLanguage, 0 );
3750     msi_reg_set_val_dword( hkey, INSTALLPROPERTY_LANGUAGEW, langid );
3751
3752     buffer = msi_dup_property( package, szProductVersion );
3753     if (buffer)
3754     {
3755         DWORD verdword = msi_version_str_to_dword(buffer);
3756
3757         msi_reg_set_val_dword( hkey, INSTALLPROPERTY_VERSIONW, verdword );
3758         msi_reg_set_val_dword( hkey, INSTALLPROPERTY_VERSIONMAJORW, verdword>>24 );
3759         msi_reg_set_val_dword( hkey, INSTALLPROPERTY_VERSIONMINORW, (verdword>>16)&0x00FF );
3760     }
3761     msi_free(buffer);
3762     
3763     /* Handle Upgrade Codes */
3764     upgrade_code = msi_dup_property( package, szUpgradeCode );
3765     if (upgrade_code)
3766     {
3767         HKEY hkey2;
3768         WCHAR squashed[33];
3769         MSIREG_OpenUpgradeCodesKey(upgrade_code, &hkey2, TRUE);
3770         squash_guid(package->ProductCode,squashed);
3771         msi_reg_set_val_str( hkey2, squashed, NULL );
3772         RegCloseKey(hkey2);
3773         MSIREG_OpenUserUpgradeCodesKey(upgrade_code, &hkey2, TRUE);
3774         squash_guid(package->ProductCode,squashed);
3775         msi_reg_set_val_str( hkey2, squashed, NULL );
3776         RegCloseKey(hkey2);
3777
3778         msi_free(upgrade_code);
3779     }
3780     
3781     RegCloseKey(hkey);
3782
3783     /* FIXME: call ui_actiondata */
3784
3785     return ERROR_SUCCESS;
3786 }
3787
3788 static UINT ACTION_InstallExecute(MSIPACKAGE *package)
3789 {
3790     return execute_script(package,INSTALL_SCRIPT);
3791 }
3792
3793 static UINT ACTION_InstallFinalize(MSIPACKAGE *package)
3794 {
3795     UINT rc;
3796
3797     /* turn off scheduleing */
3798     package->script->CurrentlyScripting= FALSE;
3799
3800     /* first do the same as an InstallExecute */
3801     rc = ACTION_InstallExecute(package);
3802     if (rc != ERROR_SUCCESS)
3803         return rc;
3804
3805     /* then handle Commit Actions */
3806     rc = execute_script(package,COMMIT_SCRIPT);
3807
3808     return rc;
3809 }
3810
3811 static UINT ACTION_ForceReboot(MSIPACKAGE *package)
3812 {
3813     static const WCHAR RunOnce[] = {
3814     'S','o','f','t','w','a','r','e','\\',
3815     'M','i','c','r','o','s','o','f','t','\\',
3816     'W','i','n','d','o','w','s','\\',
3817     'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
3818     'R','u','n','O','n','c','e',0};
3819     static const WCHAR InstallRunOnce[] = {
3820     'S','o','f','t','w','a','r','e','\\',
3821     'M','i','c','r','o','s','o','f','t','\\',
3822     'W','i','n','d','o','w','s','\\',
3823     'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
3824     'I','n','s','t','a','l','l','e','r','\\',
3825     'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
3826
3827     static const WCHAR msiexec_fmt[] = {
3828     '%','s',
3829     '\\','M','s','i','E','x','e','c','.','e','x','e',' ','/','@',' ',
3830     '\"','%','s','\"',0};
3831     static const WCHAR install_fmt[] = {
3832     '/','I',' ','\"','%','s','\"',' ',
3833     'A','F','T','E','R','R','E','B','O','O','T','=','1',' ',
3834     'R','U','N','O','N','C','E','E','N','T','R','Y','=','\"','%','s','\"',0};
3835     WCHAR buffer[256], sysdir[MAX_PATH];
3836     HKEY hkey;
3837     WCHAR squished_pc[100];
3838
3839     squash_guid(package->ProductCode,squished_pc);
3840
3841     GetSystemDirectoryW(sysdir, sizeof(sysdir)/sizeof(sysdir[0]));
3842     RegCreateKeyW(HKEY_LOCAL_MACHINE,RunOnce,&hkey);
3843     snprintfW(buffer,sizeof(buffer)/sizeof(buffer[0]),msiexec_fmt,sysdir,
3844      squished_pc);
3845
3846     msi_reg_set_val_str( hkey, squished_pc, buffer );
3847     RegCloseKey(hkey);
3848
3849     TRACE("Reboot command %s\n",debugstr_w(buffer));
3850
3851     RegCreateKeyW(HKEY_LOCAL_MACHINE,InstallRunOnce,&hkey);
3852     sprintfW(buffer,install_fmt,package->ProductCode,squished_pc);
3853
3854     msi_reg_set_val_str( hkey, squished_pc, buffer );
3855     RegCloseKey(hkey);
3856
3857     return ERROR_INSTALL_SUSPEND;
3858 }
3859
3860 static UINT ACTION_ResolveSource(MSIPACKAGE* package)
3861 {
3862     DWORD attrib, len;
3863     LPWSTR ptr, source;
3864     UINT rc;
3865     
3866     /*
3867      * we are currently doing what should be done here in the top level Install
3868      * however for Adminastrative and uninstalls this step will be needed
3869      */
3870     if (!package->PackagePath)
3871         return ERROR_SUCCESS;
3872
3873     ptr = strrchrW(package->PackagePath, '\\');
3874     if (!ptr)
3875         return ERROR_SUCCESS;
3876
3877     len = ptr - package->PackagePath + 2;
3878     source = msi_alloc(len * sizeof(WCHAR));
3879     lstrcpynW(source,  package->PackagePath, len);
3880
3881     MSI_SetPropertyW(package, cszSourceDir, source);
3882     MSI_SetPropertyW(package, cszSOURCEDIR, source);
3883
3884     msi_free(source);
3885
3886     attrib = GetFileAttributesW(package->PackagePath);
3887     if (attrib == INVALID_FILE_ATTRIBUTES)
3888     {
3889         LPWSTR prompt;
3890         LPWSTR msg;
3891         DWORD size = 0;
3892
3893         rc = MsiSourceListGetInfoW(package->ProductCode, NULL, 
3894                 MSIINSTALLCONTEXT_USERMANAGED, MSICODE_PRODUCT,
3895                 INSTALLPROPERTY_DISKPROMPTW,NULL,&size);
3896         if (rc == ERROR_MORE_DATA)
3897         {
3898             prompt = msi_alloc(size * sizeof(WCHAR));
3899             MsiSourceListGetInfoW(package->ProductCode, NULL, 
3900                     MSIINSTALLCONTEXT_USERMANAGED, MSICODE_PRODUCT,
3901                     INSTALLPROPERTY_DISKPROMPTW,prompt,&size);
3902         }
3903         else
3904             prompt = strdupW(package->PackagePath);
3905
3906         msg = generate_error_string(package,1302,1,prompt);
3907         while(attrib == INVALID_FILE_ATTRIBUTES)
3908         {
3909             rc = MessageBoxW(NULL,msg,NULL,MB_OKCANCEL);
3910             if (rc == IDCANCEL)
3911             {
3912                 rc = ERROR_INSTALL_USEREXIT;
3913                 break;
3914             }
3915             attrib = GetFileAttributesW(package->PackagePath);
3916         }
3917         msi_free(prompt);
3918         rc = ERROR_SUCCESS;
3919     }
3920     else
3921         return ERROR_SUCCESS;
3922
3923     return rc;
3924 }
3925
3926 static UINT ACTION_RegisterUser(MSIPACKAGE *package)
3927 {
3928     HKEY hkey=0;
3929     LPWSTR buffer;
3930     LPWSTR productid;
3931     UINT rc,i;
3932
3933     static const WCHAR szPropKeys[][80] = 
3934     {
3935         {'P','r','o','d','u','c','t','I','D',0},
3936         {'U','S','E','R','N','A','M','E',0},
3937         {'C','O','M','P','A','N','Y','N','A','M','E',0},
3938         {0},
3939     };
3940
3941     static const WCHAR szRegKeys[][80] = 
3942     {
3943         {'P','r','o','d','u','c','t','I','D',0},
3944         {'R','e','g','O','w','n','e','r',0},
3945         {'R','e','g','C','o','m','p','a','n','y',0},
3946         {0},
3947     };
3948
3949     productid = msi_dup_property( package, INSTALLPROPERTY_PRODUCTIDW );
3950     if (!productid)
3951         return ERROR_SUCCESS;
3952
3953     rc = MSIREG_OpenUninstallKey(package->ProductCode,&hkey,TRUE);
3954     if (rc != ERROR_SUCCESS)
3955         goto end;
3956
3957     for( i = 0; szPropKeys[i][0]; i++ )
3958     {
3959         buffer = msi_dup_property( package, szPropKeys[i] );
3960         msi_reg_set_val_str( hkey, szRegKeys[i], buffer );
3961         msi_free( buffer );
3962     }
3963
3964 end:
3965     msi_free(productid);
3966     RegCloseKey(hkey);
3967
3968     /* FIXME: call ui_actiondata */
3969
3970     return ERROR_SUCCESS;
3971 }
3972
3973
3974 static UINT ACTION_ExecuteAction(MSIPACKAGE *package)
3975 {
3976     UINT rc;
3977
3978     package->script->InWhatSequence |= SEQUENCE_EXEC;
3979     rc = ACTION_ProcessExecSequence(package,FALSE);
3980     return rc;
3981 }
3982
3983
3984 static UINT ITERATE_PublishComponent(MSIRECORD *rec, LPVOID param)
3985 {
3986     MSIPACKAGE *package = (MSIPACKAGE*)param;
3987     LPCWSTR compgroupid=NULL;
3988     LPCWSTR feature=NULL;
3989     LPCWSTR text = NULL;
3990     LPCWSTR qualifier = NULL;
3991     LPCWSTR component = NULL;
3992     LPWSTR advertise = NULL;
3993     LPWSTR output = NULL;
3994     HKEY hkey;
3995     UINT rc = ERROR_SUCCESS;
3996     MSICOMPONENT *comp;
3997     DWORD sz = 0;
3998     MSIRECORD *uirow;
3999
4000     component = MSI_RecordGetString(rec,3);
4001     comp = get_loaded_component(package,component);
4002
4003     if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL ) && 
4004        !ACTION_VerifyComponentForAction( comp, INSTALLSTATE_SOURCE ) &&
4005        !ACTION_VerifyComponentForAction( comp, INSTALLSTATE_ADVERTISED ))
4006     {
4007         TRACE("Skipping: Component %s not scheduled for install\n",
4008                         debugstr_w(component));
4009
4010         return ERROR_SUCCESS;
4011     }
4012
4013     compgroupid = MSI_RecordGetString(rec,1);
4014     qualifier = MSI_RecordGetString(rec,2);
4015
4016     rc = MSIREG_OpenUserComponentsKey(compgroupid, &hkey, TRUE);
4017     if (rc != ERROR_SUCCESS)
4018         goto end;
4019     
4020     text = MSI_RecordGetString(rec,4);
4021     feature = MSI_RecordGetString(rec,5);
4022   
4023     advertise = create_component_advertise_string(package, comp, feature);
4024
4025     sz = strlenW(advertise);
4026
4027     if (text)
4028         sz += lstrlenW(text);
4029
4030     sz+=3;
4031     sz *= sizeof(WCHAR);
4032            
4033     output = msi_alloc_zero(sz);
4034     strcpyW(output,advertise);
4035     msi_free(advertise);
4036
4037     if (text)
4038         strcatW(output,text);
4039
4040     msi_reg_set_val_multi_str( hkey, qualifier, output );
4041     
4042 end:
4043     RegCloseKey(hkey);
4044     msi_free(output);
4045
4046     /* the UI chunk */
4047     uirow = MSI_CreateRecord( 2 );
4048     MSI_RecordSetStringW( uirow, 1, compgroupid );
4049     MSI_RecordSetStringW( uirow, 2, qualifier);
4050     ui_actiondata( package, szPublishComponents, uirow);
4051     msiobj_release( &uirow->hdr );
4052     /* FIXME: call ui_progress? */
4053
4054     return rc;
4055 }
4056
4057 /*
4058  * At present I am ignorning the advertised components part of this and only
4059  * focusing on the qualified component sets
4060  */
4061 static UINT ACTION_PublishComponents(MSIPACKAGE *package)
4062 {
4063     UINT rc;
4064     MSIQUERY * view;
4065     static const WCHAR ExecSeqQuery[] =
4066         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4067          '`','P','u','b','l','i','s','h',
4068          'C','o','m','p','o','n','e','n','t','`',0};
4069     
4070     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4071     if (rc != ERROR_SUCCESS)
4072         return ERROR_SUCCESS;
4073
4074     rc = MSI_IterateRecords(view, NULL, ITERATE_PublishComponent, package);
4075     msiobj_release(&view->hdr);
4076
4077     return rc;
4078 }
4079
4080 static UINT ITERATE_InstallService(MSIRECORD *rec, LPVOID param)
4081 {
4082     MSIPACKAGE *package = (MSIPACKAGE*)param;
4083     MSIRECORD *row;
4084     MSIFILE *file;
4085     SC_HANDLE hscm, service = NULL;
4086     LPCWSTR name, disp, comp, depends, pass;
4087     LPCWSTR load_order, serv_name, key;
4088     DWORD serv_type, start_type;
4089     DWORD err_control;
4090
4091     static const WCHAR query[] =
4092         {'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
4093          '`','C','o','m','p','o','n','e','n','t','`',' ',
4094          'W','H','E','R','E',' ',
4095          '`','C','o','m','p','o','n','e','n','t','`',' ',
4096          '=','\'','%','s','\'',0};
4097
4098     hscm = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASEW, GENERIC_WRITE);
4099     if (!hscm)
4100     {
4101         ERR("Failed to open the SC Manager!\n");
4102         goto done;
4103     }
4104
4105     start_type = MSI_RecordGetInteger(rec, 5);
4106     if (start_type == SERVICE_BOOT_START || start_type == SERVICE_SYSTEM_START)
4107         goto done;
4108
4109     depends = MSI_RecordGetString(rec, 8);
4110     if (depends && *depends)
4111         FIXME("Dependency list unhandled!\n");
4112
4113     name = MSI_RecordGetString(rec, 2);
4114     disp = MSI_RecordGetString(rec, 3);
4115     serv_type = MSI_RecordGetInteger(rec, 4);
4116     err_control = MSI_RecordGetInteger(rec, 6);
4117     load_order = MSI_RecordGetString(rec, 7);
4118     serv_name = MSI_RecordGetString(rec, 9);
4119     pass = MSI_RecordGetString(rec, 10);
4120     comp = MSI_RecordGetString(rec, 12);
4121
4122     /* fetch the service path */
4123     row = MSI_QueryGetRecord(package->db, query, comp);
4124     if (!row)
4125     {
4126         ERR("Control query failed!\n");
4127         goto done;
4128     }
4129
4130     key = MSI_RecordGetString(row, 6);
4131     msiobj_release(&row->hdr);
4132
4133     file = get_loaded_file(package, key);
4134     if (!file)
4135     {
4136         ERR("Failed to load the service file\n");
4137         goto done;
4138     }
4139
4140     service = CreateServiceW(hscm, name, disp, GENERIC_ALL, serv_type,
4141                              start_type, err_control, file->TargetPath,
4142                              load_order, NULL, NULL, serv_name, pass);
4143     if (!service)
4144     {
4145         if (GetLastError() != ERROR_SERVICE_EXISTS)
4146             ERR("Failed to create service %s: %d\n", debugstr_w(name), GetLastError());
4147     }
4148
4149 done:
4150     CloseServiceHandle(service);
4151     CloseServiceHandle(hscm);
4152
4153     return ERROR_SUCCESS;
4154 }
4155
4156 static UINT ACTION_InstallServices( MSIPACKAGE *package )
4157 {
4158     UINT rc;
4159     MSIQUERY * view;
4160     static const WCHAR ExecSeqQuery[] =
4161         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4162          'S','e','r','v','i','c','e','I','n','s','t','a','l','l',0};
4163     
4164     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4165     if (rc != ERROR_SUCCESS)
4166         return ERROR_SUCCESS;
4167
4168     rc = MSI_IterateRecords(view, NULL, ITERATE_InstallService, package);
4169     msiobj_release(&view->hdr);
4170
4171     return rc;
4172 }
4173
4174 static UINT msi_unimplemented_action_stub( MSIPACKAGE *package,
4175                                            LPCSTR action, LPCWSTR table )
4176 {
4177     static const WCHAR query[] = {
4178         'S','E','L','E','C','T',' ','*',' ',
4179         'F','R','O','M',' ','`','%','s','`',0 };
4180     MSIQUERY *view = NULL;
4181     DWORD count = 0;
4182     UINT r;
4183     
4184     r = MSI_OpenQuery( package->db, &view, query, table );
4185     if (r == ERROR_SUCCESS)
4186     {
4187         r = MSI_IterateRecords(view, &count, NULL, package);
4188         msiobj_release(&view->hdr);
4189     }
4190
4191     if (count)
4192         FIXME("%s -> %u ignored %s table values\n",
4193               action, count, debugstr_w(table));
4194
4195     return ERROR_SUCCESS;
4196 }
4197
4198 static UINT ACTION_AllocateRegistrySpace( MSIPACKAGE *package )
4199 {
4200     TRACE("%p\n", package);
4201     return ERROR_SUCCESS;
4202 }
4203
4204 static UINT ACTION_RemoveIniValues( MSIPACKAGE *package )
4205 {
4206     static const WCHAR table[] =
4207          {'R','e','m','o','v','e','I','n','i','F','i','l','e',0 };
4208     return msi_unimplemented_action_stub( package, "RemoveIniValues", table );
4209 }
4210
4211 static UINT ACTION_MoveFiles( MSIPACKAGE *package )
4212 {
4213     static const WCHAR table[] = { 'M','o','v','e','F','i','l','e',0 };
4214     return msi_unimplemented_action_stub( package, "MoveFiles", table );
4215 }
4216
4217 static UINT ACTION_PatchFiles( MSIPACKAGE *package )
4218 {
4219     static const WCHAR table[] = { 'P','a','t','c','h',0 };
4220     return msi_unimplemented_action_stub( package, "PatchFiles", table );
4221 }
4222
4223 static UINT ACTION_BindImage( MSIPACKAGE *package )
4224 {
4225     static const WCHAR table[] = { 'B','i','n','d','I','m','a','g','e',0 };
4226     return msi_unimplemented_action_stub( package, "BindImage", table );
4227 }
4228
4229 static UINT ACTION_IsolateComponents( MSIPACKAGE *package )
4230 {
4231     static const WCHAR table[] = {
4232         'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t',0 };
4233     return msi_unimplemented_action_stub( package, "IsolateComponents", table );
4234 }
4235
4236 static UINT ACTION_MigrateFeatureStates( MSIPACKAGE *package )
4237 {
4238     static const WCHAR table[] = { 'U','p','g','r','a','d','e',0 };
4239     return msi_unimplemented_action_stub( package, "MigrateFeatureStates", table );
4240 }
4241
4242 static UINT ACTION_SelfUnregModules( MSIPACKAGE *package )
4243 {
4244     static const WCHAR table[] = { 'S','e','l','f','R','e','g',0 };
4245     return msi_unimplemented_action_stub( package, "SelfUnregModules", table );
4246 }
4247
4248 static UINT ACTION_StartServices( MSIPACKAGE *package )
4249 {
4250     static const WCHAR table[] = {
4251         'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
4252     return msi_unimplemented_action_stub( package, "StartServices", table );
4253 }
4254
4255 static UINT ACTION_StopServices( MSIPACKAGE *package )
4256 {
4257     static const WCHAR table[] = {
4258         'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
4259     return msi_unimplemented_action_stub( package, "StopServices", table );
4260 }
4261
4262 static UINT ACTION_DeleteServices( MSIPACKAGE *package )
4263 {
4264     static const WCHAR table[] = {
4265         'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
4266     return msi_unimplemented_action_stub( package, "DeleteServices", table );
4267 }
4268
4269 static UINT ACTION_WriteEnvironmentStrings( MSIPACKAGE *package )
4270 {
4271     static const WCHAR table[] = {
4272         'E','n','v','i','r','o','n','m','e','n','t',0 };
4273     return msi_unimplemented_action_stub( package, "WriteEnvironmentStrings", table );
4274 }
4275
4276 static UINT ACTION_RemoveEnvironmentStrings( MSIPACKAGE *package )
4277 {
4278     static const WCHAR table[] = {
4279         'E','n','v','i','r','o','n','m','e','n','t',0 };
4280     return msi_unimplemented_action_stub( package, "RemoveEnvironmentStrings", table );
4281 }
4282
4283 static UINT ACTION_MsiPublishAssemblies( MSIPACKAGE *package )
4284 {
4285     static const WCHAR table[] = {
4286         'M','s','i','A','s','s','e','m','b','l','y',0 };
4287     return msi_unimplemented_action_stub( package, "MsiPublishAssemblies", table );
4288 }
4289
4290 static UINT ACTION_MsiUnpublishAssemblies( MSIPACKAGE *package )
4291 {
4292     static const WCHAR table[] = {
4293         'M','s','i','A','s','s','e','m','b','l','y',0 };
4294     return msi_unimplemented_action_stub( package, "MsiUnpublishAssemblies", table );
4295 }
4296
4297 static UINT ACTION_UnregisterFonts( MSIPACKAGE *package )
4298 {
4299     static const WCHAR table[] = { 'F','o','n','t',0 };
4300     return msi_unimplemented_action_stub( package, "UnregisterFonts", table );
4301 }
4302
4303 static UINT ACTION_CCPSearch( MSIPACKAGE *package )
4304 {
4305     static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
4306     return msi_unimplemented_action_stub( package, "CCPSearch", table );
4307 }
4308
4309 static UINT ACTION_RMCCPSearch( MSIPACKAGE *package )
4310 {
4311     static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
4312     return msi_unimplemented_action_stub( package, "RMCCPSearch", table );
4313 }
4314
4315 static UINT ACTION_RegisterComPlus( MSIPACKAGE *package )
4316 {
4317     static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
4318     return msi_unimplemented_action_stub( package, "RegisterComPlus", table );
4319 }
4320
4321 static UINT ACTION_UnregisterComPlus( MSIPACKAGE *package )
4322 {
4323     static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
4324     return msi_unimplemented_action_stub( package, "UnregisterComPlus", table );
4325 }
4326
4327 static struct _actions StandardActions[] = {
4328     { szAllocateRegistrySpace, ACTION_AllocateRegistrySpace },
4329     { szAppSearch, ACTION_AppSearch },
4330     { szBindImage, ACTION_BindImage },
4331     { szCCPSearch, ACTION_CCPSearch},
4332     { szCostFinalize, ACTION_CostFinalize },
4333     { szCostInitialize, ACTION_CostInitialize },
4334     { szCreateFolders, ACTION_CreateFolders },
4335     { szCreateShortcuts, ACTION_CreateShortcuts },
4336     { szDeleteServices, ACTION_DeleteServices },
4337     { szDisableRollback, NULL},
4338     { szDuplicateFiles, ACTION_DuplicateFiles },
4339     { szExecuteAction, ACTION_ExecuteAction },
4340     { szFileCost, ACTION_FileCost },
4341     { szFindRelatedProducts, ACTION_FindRelatedProducts },
4342     { szForceReboot, ACTION_ForceReboot },
4343     { szInstallAdminPackage, NULL},
4344     { szInstallExecute, ACTION_InstallExecute },
4345     { szInstallExecuteAgain, ACTION_InstallExecute },
4346     { szInstallFiles, ACTION_InstallFiles},
4347     { szInstallFinalize, ACTION_InstallFinalize },
4348     { szInstallInitialize, ACTION_InstallInitialize },
4349     { szInstallSFPCatalogFile, NULL},
4350     { szInstallValidate, ACTION_InstallValidate },
4351     { szIsolateComponents, ACTION_IsolateComponents },
4352     { szLaunchConditions, ACTION_LaunchConditions },
4353     { szMigrateFeatureStates, ACTION_MigrateFeatureStates },
4354     { szMoveFiles, ACTION_MoveFiles },
4355     { szMsiPublishAssemblies, ACTION_MsiPublishAssemblies },
4356     { szMsiUnpublishAssemblies, ACTION_MsiUnpublishAssemblies },
4357     { szInstallODBC, NULL},
4358     { szInstallServices, ACTION_InstallServices },
4359     { szPatchFiles, ACTION_PatchFiles },
4360     { szProcessComponents, ACTION_ProcessComponents },
4361     { szPublishComponents, ACTION_PublishComponents },
4362     { szPublishFeatures, ACTION_PublishFeatures },
4363     { szPublishProduct, ACTION_PublishProduct },
4364     { szRegisterClassInfo, ACTION_RegisterClassInfo },
4365     { szRegisterComPlus, ACTION_RegisterComPlus},
4366     { szRegisterExtensionInfo, ACTION_RegisterExtensionInfo },
4367     { szRegisterFonts, ACTION_RegisterFonts },
4368     { szRegisterMIMEInfo, ACTION_RegisterMIMEInfo },
4369     { szRegisterProduct, ACTION_RegisterProduct },
4370     { szRegisterProgIdInfo, ACTION_RegisterProgIdInfo },
4371     { szRegisterTypeLibraries, ACTION_RegisterTypeLibraries },
4372     { szRegisterUser, ACTION_RegisterUser},
4373     { szRemoveDuplicateFiles, NULL},
4374     { szRemoveEnvironmentStrings, ACTION_RemoveEnvironmentStrings },
4375     { szRemoveExistingProducts, NULL},
4376     { szRemoveFiles, ACTION_RemoveFiles},
4377     { szRemoveFolders, NULL},
4378     { szRemoveIniValues, ACTION_RemoveIniValues },
4379     { szRemoveODBC, NULL},
4380     { szRemoveRegistryValues, NULL},
4381     { szRemoveShortcuts, NULL},
4382     { szResolveSource, ACTION_ResolveSource},
4383     { szRMCCPSearch, ACTION_RMCCPSearch},
4384     { szScheduleReboot, NULL},
4385     { szSelfRegModules, ACTION_SelfRegModules },
4386     { szSelfUnregModules, ACTION_SelfUnregModules },
4387     { szSetODBCFolders, NULL},
4388     { szStartServices, ACTION_StartServices },
4389     { szStopServices, ACTION_StopServices },
4390     { szUnpublishComponents, NULL},
4391     { szUnpublishFeatures, NULL},
4392     { szUnregisterClassInfo, NULL},
4393     { szUnregisterComPlus, ACTION_UnregisterComPlus},
4394     { szUnregisterExtensionInfo, NULL},
4395     { szUnregisterFonts, ACTION_UnregisterFonts },
4396     { szUnregisterMIMEInfo, NULL},
4397     { szUnregisterProgIdInfo, NULL},
4398     { szUnregisterTypeLibraries, NULL},
4399     { szValidateProductID, NULL},
4400     { szWriteEnvironmentStrings, ACTION_WriteEnvironmentStrings },
4401     { szWriteIniValues, ACTION_WriteIniValues },
4402     { szWriteRegistryValues, ACTION_WriteRegistryValues},
4403     { NULL, NULL},
4404 };