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