msi: Split ACTION_CostFinalize into two functions.
[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 static UINT msi_check_file_install_states( MSIPACKAGE *package )
1943 {
1944     LPWSTR file_version;
1945     MSIFILE *file;
1946
1947     LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
1948     {
1949         MSICOMPONENT* comp = file->Component;
1950         LPWSTR p;
1951
1952         if (!comp)
1953             continue;
1954
1955         if (file->IsCompressed)
1956             comp->ForceLocalState = TRUE;
1957
1958         /* calculate target */
1959         p = resolve_folder(package, comp->Directory, FALSE, FALSE, NULL);
1960
1961         msi_free(file->TargetPath);
1962
1963         TRACE("file %s is named %s\n",
1964                debugstr_w(file->File), debugstr_w(file->FileName));
1965
1966         file->TargetPath = build_directory_name(2, p, file->FileName);
1967
1968         msi_free(p);
1969
1970         TRACE("file %s resolves to %s\n",
1971                debugstr_w(file->File), debugstr_w(file->TargetPath));
1972
1973         /* don't check files of components that aren't installed */
1974         if (comp->Installed == INSTALLSTATE_UNKNOWN ||
1975             comp->Installed == INSTALLSTATE_ABSENT)
1976         {
1977             file->state = msifs_missing;  /* assume files are missing */
1978             continue;
1979         }
1980
1981         if (GetFileAttributesW(file->TargetPath) == INVALID_FILE_ATTRIBUTES)
1982         {
1983             file->state = msifs_missing;
1984             comp->Cost += file->FileSize;
1985             comp->Installed = INSTALLSTATE_INCOMPLETE;
1986             continue;
1987         }
1988
1989         if (file->Version &&
1990             (file_version = msi_get_disk_file_version( file->TargetPath )))
1991         {
1992             TRACE("new %s old %s\n", debugstr_w(file->Version),
1993                   debugstr_w(file_version));
1994             /* FIXME: seems like a bad way to compare version numbers */
1995             if (lstrcmpiW(file_version, file->Version)<0)
1996             {
1997                 file->state = msifs_overwrite;
1998                 comp->Cost += file->FileSize;
1999                 comp->Installed = INSTALLSTATE_INCOMPLETE;
2000             }
2001             else
2002                 file->state = msifs_present;
2003             msi_free( file_version );
2004         }
2005         else
2006             file->state = msifs_present;
2007     }
2008
2009     return ERROR_SUCCESS;
2010 }
2011
2012 /*
2013  * A lot is done in this function aside from just the costing.
2014  * The costing needs to be implemented at some point but for now I am going
2015  * to focus on the directory building
2016  *
2017  */
2018 static UINT ACTION_CostFinalize(MSIPACKAGE *package)
2019 {
2020     static const WCHAR ExecSeqQuery[] =
2021         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2022          '`','D','i','r','e','c','t','o','r','y','`',0};
2023     static const WCHAR ConditionQuery[] =
2024         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2025          '`','C','o','n','d','i','t','i','o','n','`',0};
2026     static const WCHAR szCosting[] =
2027         {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
2028     static const WCHAR szlevel[] =
2029         {'I','N','S','T','A','L','L','L','E','V','E','L',0};
2030     static const WCHAR szOne[] = { '1', 0 };
2031     MSICOMPONENT *comp;
2032     UINT rc;
2033     MSIQUERY * view;
2034     LPWSTR level;
2035
2036     if ( 1 == msi_get_property_int( package, szCosting, 0 ) )
2037         return ERROR_SUCCESS;
2038
2039     TRACE("Building Directory properties\n");
2040
2041     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2042     if (rc == ERROR_SUCCESS)
2043     {
2044         rc = MSI_IterateRecords(view, NULL, ITERATE_CostFinalizeDirectories,
2045                         package);
2046         msiobj_release(&view->hdr);
2047     }
2048
2049     /* read components states from the registry */
2050     ACTION_GetComponentInstallStates(package);
2051
2052     TRACE("File calculations\n");
2053     msi_check_file_install_states( package );
2054
2055     TRACE("Evaluating Condition Table\n");
2056
2057     rc = MSI_DatabaseOpenViewW(package->db, ConditionQuery, &view);
2058     if (rc == ERROR_SUCCESS)
2059     {
2060         rc = MSI_IterateRecords(view, NULL, ITERATE_CostFinalizeConditions,
2061                     package);
2062         msiobj_release(&view->hdr);
2063     }
2064
2065     TRACE("Enabling or Disabling Components\n");
2066     LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2067     {
2068         if (MSI_EvaluateConditionW(package, comp->Condition) == MSICONDITION_FALSE)
2069         {
2070             TRACE("Disabling component %s\n", debugstr_w(comp->Component));
2071             comp->Enabled = FALSE;
2072         }
2073     }
2074
2075     MSI_SetPropertyW(package,szCosting,szOne);
2076     /* set default run level if not set */
2077     level = msi_dup_property( package, szlevel );
2078     if (!level)
2079         MSI_SetPropertyW(package,szlevel, szOne);
2080     msi_free(level);
2081
2082     ACTION_UpdateFeatureInstallStates(package);
2083
2084     return MSI_SetFeatureStates(package);
2085 }
2086
2087 /* OK this value is "interpreted" and then formatted based on the 
2088    first few characters */
2089 static LPSTR parse_value(MSIPACKAGE *package, LPCWSTR value, DWORD *type, 
2090                          DWORD *size)
2091 {
2092     LPSTR data = NULL;
2093     if (value[0]=='#' && value[1]!='#' && value[1]!='%')
2094     {
2095         if (value[1]=='x')
2096         {
2097             LPWSTR ptr;
2098             CHAR byte[5];
2099             LPWSTR deformated = NULL;
2100             int count;
2101
2102             deformat_string(package, &value[2], &deformated);
2103
2104             /* binary value type */
2105             ptr = deformated;
2106             *type = REG_BINARY;
2107             if (strlenW(ptr)%2)
2108                 *size = (strlenW(ptr)/2)+1;
2109             else
2110                 *size = strlenW(ptr)/2;
2111
2112             data = msi_alloc(*size);
2113
2114             byte[0] = '0'; 
2115             byte[1] = 'x'; 
2116             byte[4] = 0; 
2117             count = 0;
2118             /* if uneven pad with a zero in front */
2119             if (strlenW(ptr)%2)
2120             {
2121                 byte[2]= '0';
2122                 byte[3]= *ptr;
2123                 ptr++;
2124                 data[count] = (BYTE)strtol(byte,NULL,0);
2125                 count ++;
2126                 TRACE("Uneven byte count\n");
2127             }
2128             while (*ptr)
2129             {
2130                 byte[2]= *ptr;
2131                 ptr++;
2132                 byte[3]= *ptr;
2133                 ptr++;
2134                 data[count] = (BYTE)strtol(byte,NULL,0);
2135                 count ++;
2136             }
2137             msi_free(deformated);
2138
2139             TRACE("Data %i bytes(%i)\n",*size,count);
2140         }
2141         else
2142         {
2143             LPWSTR deformated;
2144             LPWSTR p;
2145             DWORD d = 0;
2146             deformat_string(package, &value[1], &deformated);
2147
2148             *type=REG_DWORD; 
2149             *size = sizeof(DWORD);
2150             data = msi_alloc(*size);
2151             p = deformated;
2152             if (*p == '-')
2153                 p++;
2154             while (*p)
2155             {
2156                 if ( (*p < '0') || (*p > '9') )
2157                     break;
2158                 d *= 10;
2159                 d += (*p - '0');
2160                 p++;
2161             }
2162             if (deformated[0] == '-')
2163                 d = -d;
2164             *(LPDWORD)data = d;
2165             TRACE("DWORD %i\n",*(LPDWORD)data);
2166
2167             msi_free(deformated);
2168         }
2169     }
2170     else
2171     {
2172         static const WCHAR szMulti[] = {'[','~',']',0};
2173         LPCWSTR ptr;
2174         *type=REG_SZ;
2175
2176         if (value[0]=='#')
2177         {
2178             if (value[1]=='%')
2179             {
2180                 ptr = &value[2];
2181                 *type=REG_EXPAND_SZ;
2182             }
2183             else
2184                 ptr = &value[1];
2185          }
2186          else
2187             ptr=value;
2188
2189         if (strstrW(value,szMulti))
2190             *type = REG_MULTI_SZ;
2191
2192         *size = deformat_string(package, ptr,(LPWSTR*)&data);
2193     }
2194     return data;
2195 }
2196
2197 static UINT ITERATE_WriteRegistryValues(MSIRECORD *row, LPVOID param)
2198 {
2199     MSIPACKAGE *package = (MSIPACKAGE*)param;
2200     static const WCHAR szHCR[] = 
2201         {'H','K','E','Y','_','C','L','A','S','S','E','S','_',
2202          'R','O','O','T','\\',0};
2203     static const WCHAR szHCU[] =
2204         {'H','K','E','Y','_','C','U','R','R','E','N','T','_',
2205          'U','S','E','R','\\',0};
2206     static const WCHAR szHLM[] =
2207         {'H','K','E','Y','_','L','O','C','A','L','_',
2208          'M','A','C','H','I','N','E','\\',0};
2209     static const WCHAR szHU[] =
2210         {'H','K','E','Y','_','U','S','E','R','S','\\',0};
2211
2212     LPSTR value_data = NULL;
2213     HKEY  root_key, hkey;
2214     DWORD type,size;
2215     LPWSTR  deformated;
2216     LPCWSTR szRoot, component, name, key, value;
2217     MSICOMPONENT *comp;
2218     MSIRECORD * uirow;
2219     LPWSTR uikey;
2220     INT   root;
2221     BOOL check_first = FALSE;
2222     UINT rc;
2223
2224     ui_progress(package,2,0,0,0);
2225
2226     value = NULL;
2227     key = NULL;
2228     uikey = NULL;
2229     name = NULL;
2230
2231     component = MSI_RecordGetString(row, 6);
2232     comp = get_loaded_component(package,component);
2233     if (!comp)
2234         return ERROR_SUCCESS;
2235
2236     if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
2237     {
2238         TRACE("Skipping write due to disabled component %s\n",
2239                         debugstr_w(component));
2240
2241         comp->Action = comp->Installed;
2242
2243         return ERROR_SUCCESS;
2244     }
2245
2246     comp->Action = INSTALLSTATE_LOCAL;
2247
2248     name = MSI_RecordGetString(row, 4);
2249     if( MSI_RecordIsNull(row,5) && name )
2250     {
2251         /* null values can have special meanings */
2252         if (name[0]=='-' && name[1] == 0)
2253                 return ERROR_SUCCESS;
2254         else if ((name[0]=='+' && name[1] == 0) || 
2255                  (name[0] == '*' && name[1] == 0))
2256                 name = NULL;
2257         check_first = TRUE;
2258     }
2259
2260     root = MSI_RecordGetInteger(row,2);
2261     key = MSI_RecordGetString(row, 3);
2262
2263     /* get the root key */
2264     switch (root)
2265     {
2266         case -1: 
2267             {
2268                 static const WCHAR szALLUSER[] = {'A','L','L','U','S','E','R','S',0};
2269                 LPWSTR all_users = msi_dup_property( package, szALLUSER );
2270                 if (all_users && all_users[0] == '1')
2271                 {
2272                     root_key = HKEY_LOCAL_MACHINE;
2273                     szRoot = szHLM;
2274                 }
2275                 else
2276                 {
2277                     root_key = HKEY_CURRENT_USER;
2278                     szRoot = szHCU;
2279                 }
2280                 msi_free(all_users);
2281             }
2282                  break;
2283         case 0:  root_key = HKEY_CLASSES_ROOT; 
2284                  szRoot = szHCR;
2285                  break;
2286         case 1:  root_key = HKEY_CURRENT_USER;
2287                  szRoot = szHCU;
2288                  break;
2289         case 2:  root_key = HKEY_LOCAL_MACHINE;
2290                  szRoot = szHLM;
2291                  break;
2292         case 3:  root_key = HKEY_USERS; 
2293                  szRoot = szHU;
2294                  break;
2295         default:
2296                  ERR("Unknown root %i\n",root);
2297                  root_key=NULL;
2298                  szRoot = NULL;
2299                  break;
2300     }
2301     if (!root_key)
2302         return ERROR_SUCCESS;
2303
2304     deformat_string(package, key , &deformated);
2305     size = strlenW(deformated) + strlenW(szRoot) + 1;
2306     uikey = msi_alloc(size*sizeof(WCHAR));
2307     strcpyW(uikey,szRoot);
2308     strcatW(uikey,deformated);
2309
2310     if (RegCreateKeyW( root_key, deformated, &hkey))
2311     {
2312         ERR("Could not create key %s\n",debugstr_w(deformated));
2313         msi_free(deformated);
2314         msi_free(uikey);
2315         return ERROR_SUCCESS;
2316     }
2317     msi_free(deformated);
2318
2319     value = MSI_RecordGetString(row,5);
2320     if (value)
2321         value_data = parse_value(package, value, &type, &size); 
2322     else
2323     {
2324         static const WCHAR szEmpty[] = {0};
2325         value_data = (LPSTR)strdupW(szEmpty);
2326         size = 0;
2327         type = REG_SZ;
2328     }
2329
2330     deformat_string(package, name, &deformated);
2331
2332     /* get the double nulls to terminate SZ_MULTI */
2333     if (type == REG_MULTI_SZ)
2334         size +=sizeof(WCHAR);
2335
2336     if (!check_first)
2337     {
2338         TRACE("Setting value %s of %s\n",debugstr_w(deformated),
2339                         debugstr_w(uikey));
2340         RegSetValueExW(hkey, deformated, 0, type, (LPBYTE)value_data, size);
2341     }
2342     else
2343     {
2344         DWORD sz = 0;
2345         rc = RegQueryValueExW(hkey, deformated, NULL, NULL, NULL, &sz);
2346         if (rc == ERROR_SUCCESS || rc == ERROR_MORE_DATA)
2347         {
2348             TRACE("value %s of %s checked already exists\n",
2349                             debugstr_w(deformated), debugstr_w(uikey));
2350         }
2351         else
2352         {
2353             TRACE("Checked and setting value %s of %s\n",
2354                             debugstr_w(deformated), debugstr_w(uikey));
2355             if (deformated || size)
2356                 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE) value_data, size);
2357         }
2358     }
2359     RegCloseKey(hkey);
2360
2361     uirow = MSI_CreateRecord(3);
2362     MSI_RecordSetStringW(uirow,2,deformated);
2363     MSI_RecordSetStringW(uirow,1,uikey);
2364
2365     if (type == REG_SZ)
2366         MSI_RecordSetStringW(uirow,3,(LPWSTR)value_data);
2367     else
2368         MSI_RecordSetStringW(uirow,3,value);
2369
2370     ui_actiondata(package,szWriteRegistryValues,uirow);
2371     msiobj_release( &uirow->hdr );
2372
2373     msi_free(value_data);
2374     msi_free(deformated);
2375     msi_free(uikey);
2376
2377     return ERROR_SUCCESS;
2378 }
2379
2380 static UINT ACTION_WriteRegistryValues(MSIPACKAGE *package)
2381 {
2382     UINT rc;
2383     MSIQUERY * view;
2384     static const WCHAR ExecSeqQuery[] =
2385         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2386          '`','R','e','g','i','s','t','r','y','`',0 };
2387
2388     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2389     if (rc != ERROR_SUCCESS)
2390         return ERROR_SUCCESS;
2391
2392     /* increment progress bar each time action data is sent */
2393     ui_progress(package,1,REG_PROGRESS_VALUE,1,0);
2394
2395     rc = MSI_IterateRecords(view, NULL, ITERATE_WriteRegistryValues, package);
2396
2397     msiobj_release(&view->hdr);
2398     return rc;
2399 }
2400
2401 static UINT ACTION_InstallInitialize(MSIPACKAGE *package)
2402 {
2403     package->script->CurrentlyScripting = TRUE;
2404
2405     return ERROR_SUCCESS;
2406 }
2407
2408
2409 static UINT ACTION_InstallValidate(MSIPACKAGE *package)
2410 {
2411     MSICOMPONENT *comp;
2412     DWORD progress = 0;
2413     DWORD total = 0;
2414     static const WCHAR q1[]=
2415         {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
2416          '`','R','e','g','i','s','t','r','y','`',0};
2417     UINT rc;
2418     MSIQUERY * view;
2419     MSIFEATURE *feature;
2420     MSIFILE *file;
2421
2422     TRACE("InstallValidate\n");
2423
2424     rc = MSI_DatabaseOpenViewW(package->db, q1, &view);
2425     if (rc == ERROR_SUCCESS)
2426     {
2427         MSI_IterateRecords( view, &progress, NULL, package );
2428         msiobj_release( &view->hdr );
2429         total += progress * REG_PROGRESS_VALUE;
2430     }
2431
2432     LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2433         total += COMPONENT_PROGRESS_VALUE;
2434
2435     LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2436         total += file->FileSize;
2437
2438     ui_progress(package,0,total,0,0);
2439
2440     LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2441     {
2442         TRACE("Feature: %s; Installed: %i; Action %i; Request %i\n",
2443             debugstr_w(feature->Feature), feature->Installed, feature->Action,
2444             feature->ActionRequest);
2445     }
2446     
2447     return ERROR_SUCCESS;
2448 }
2449
2450 static UINT ITERATE_LaunchConditions(MSIRECORD *row, LPVOID param)
2451 {
2452     MSIPACKAGE* package = (MSIPACKAGE*)param;
2453     LPCWSTR cond = NULL; 
2454     LPCWSTR message = NULL;
2455     static const WCHAR title[]=
2456         {'I','n','s','t','a','l','l',' ','F','a', 'i','l','e','d',0};
2457
2458     cond = MSI_RecordGetString(row,1);
2459
2460     if (MSI_EvaluateConditionW(package,cond) != MSICONDITION_TRUE)
2461     {
2462         LPWSTR deformated;
2463         message = MSI_RecordGetString(row,2);
2464         deformat_string(package,message,&deformated); 
2465         MessageBoxW(NULL,deformated,title,MB_OK);
2466         msi_free(deformated);
2467         return ERROR_FUNCTION_FAILED;
2468     }
2469
2470     return ERROR_SUCCESS;
2471 }
2472
2473 static UINT ACTION_LaunchConditions(MSIPACKAGE *package)
2474 {
2475     UINT rc;
2476     MSIQUERY * view = NULL;
2477     static const WCHAR ExecSeqQuery[] =
2478         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2479          '`','L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','`',0};
2480
2481     TRACE("Checking launch conditions\n");
2482
2483     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2484     if (rc != ERROR_SUCCESS)
2485         return ERROR_SUCCESS;
2486
2487     rc = MSI_IterateRecords(view, NULL, ITERATE_LaunchConditions, package);
2488     msiobj_release(&view->hdr);
2489
2490     return rc;
2491 }
2492
2493 static LPWSTR resolve_keypath( MSIPACKAGE* package, MSICOMPONENT *cmp )
2494 {
2495
2496     if (!cmp->KeyPath)
2497         return resolve_folder(package,cmp->Directory,FALSE,FALSE,NULL);
2498
2499     if (cmp->Attributes & msidbComponentAttributesRegistryKeyPath)
2500     {
2501         MSIRECORD * row = 0;
2502         UINT root,len;
2503         LPWSTR deformated,buffer,deformated_name;
2504         LPCWSTR key,name;
2505         static const WCHAR ExecSeqQuery[] =
2506             {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2507              '`','R','e','g','i','s','t','r','y','`',' ',
2508              'W','H','E','R','E',' ', '`','R','e','g','i','s','t','r','y','`',
2509              ' ','=',' ' ,'\'','%','s','\'',0 };
2510         static const WCHAR fmt[]={'%','0','2','i',':','\\','%','s','\\',0};
2511         static const WCHAR fmt2[]=
2512             {'%','0','2','i',':','\\','%','s','\\','%','s',0};
2513
2514         row = MSI_QueryGetRecord(package->db, ExecSeqQuery,cmp->KeyPath);
2515         if (!row)
2516             return NULL;
2517
2518         root = MSI_RecordGetInteger(row,2);
2519         key = MSI_RecordGetString(row, 3);
2520         name = MSI_RecordGetString(row, 4);
2521         deformat_string(package, key , &deformated);
2522         deformat_string(package, name, &deformated_name);
2523
2524         len = strlenW(deformated) + 6;
2525         if (deformated_name)
2526             len+=strlenW(deformated_name);
2527
2528         buffer = msi_alloc( len *sizeof(WCHAR));
2529
2530         if (deformated_name)
2531             sprintfW(buffer,fmt2,root,deformated,deformated_name);
2532         else
2533             sprintfW(buffer,fmt,root,deformated);
2534
2535         msi_free(deformated);
2536         msi_free(deformated_name);
2537         msiobj_release(&row->hdr);
2538
2539         return buffer;
2540     }
2541     else if (cmp->Attributes & msidbComponentAttributesODBCDataSource)
2542     {
2543         FIXME("UNIMPLEMENTED keypath as ODBC Source\n");
2544         return NULL;
2545     }
2546     else
2547     {
2548         MSIFILE *file = get_loaded_file( package, cmp->KeyPath );
2549
2550         if (file)
2551             return strdupW( file->TargetPath );
2552     }
2553     return NULL;
2554 }
2555
2556 static HKEY openSharedDLLsKey(void)
2557 {
2558     HKEY hkey=0;
2559     static const WCHAR path[] =
2560         {'S','o','f','t','w','a','r','e','\\',
2561          'M','i','c','r','o','s','o','f','t','\\',
2562          'W','i','n','d','o','w','s','\\',
2563          'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
2564          'S','h','a','r','e','d','D','L','L','s',0};
2565
2566     RegCreateKeyW(HKEY_LOCAL_MACHINE,path,&hkey);
2567     return hkey;
2568 }
2569
2570 static UINT ACTION_GetSharedDLLsCount(LPCWSTR dll)
2571 {
2572     HKEY hkey;
2573     DWORD count=0;
2574     DWORD type;
2575     DWORD sz = sizeof(count);
2576     DWORD rc;
2577     
2578     hkey = openSharedDLLsKey();
2579     rc = RegQueryValueExW(hkey, dll, NULL, &type, (LPBYTE)&count, &sz);
2580     if (rc != ERROR_SUCCESS)
2581         count = 0;
2582     RegCloseKey(hkey);
2583     return count;
2584 }
2585
2586 static UINT ACTION_WriteSharedDLLsCount(LPCWSTR path, UINT count)
2587 {
2588     HKEY hkey;
2589
2590     hkey = openSharedDLLsKey();
2591     if (count > 0)
2592         msi_reg_set_val_dword( hkey, path, count );
2593     else
2594         RegDeleteValueW(hkey,path);
2595     RegCloseKey(hkey);
2596     return count;
2597 }
2598
2599 /*
2600  * Return TRUE if the count should be written out and FALSE if not
2601  */
2602 static void ACTION_RefCountComponent( MSIPACKAGE* package, MSICOMPONENT *comp )
2603 {
2604     MSIFEATURE *feature;
2605     INT count = 0;
2606     BOOL write = FALSE;
2607
2608     /* only refcount DLLs */
2609     if (comp->KeyPath == NULL || 
2610         comp->Attributes & msidbComponentAttributesRegistryKeyPath || 
2611         comp->Attributes & msidbComponentAttributesODBCDataSource)
2612         write = FALSE;
2613     else
2614     {
2615         count = ACTION_GetSharedDLLsCount( comp->FullKeypath);
2616         write = (count > 0);
2617
2618         if (comp->Attributes & msidbComponentAttributesSharedDllRefCount)
2619             write = TRUE;
2620     }
2621
2622     /* increment counts */
2623     LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2624     {
2625         ComponentList *cl;
2626
2627         if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_LOCAL ))
2628             continue;
2629
2630         LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
2631         {
2632             if ( cl->component == comp )
2633                 count++;
2634         }
2635     }
2636
2637     /* decrement counts */
2638     LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2639     {
2640         ComponentList *cl;
2641
2642         if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_ABSENT ))
2643             continue;
2644
2645         LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
2646         {
2647             if ( cl->component == comp )
2648                 count--;
2649         }
2650     }
2651
2652     /* ref count all the files in the component */
2653     if (write)
2654     {
2655         MSIFILE *file;
2656
2657         LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2658         {
2659             if (file->Component == comp)
2660                 ACTION_WriteSharedDLLsCount( file->TargetPath, count );
2661         }
2662     }
2663     
2664     /* add a count for permenent */
2665     if (comp->Attributes & msidbComponentAttributesPermanent)
2666         count ++;
2667     
2668     comp->RefCount = count;
2669
2670     if (write)
2671         ACTION_WriteSharedDLLsCount( comp->FullKeypath, comp->RefCount );
2672 }
2673
2674 /*
2675  * Ok further analysis makes me think that this work is
2676  * actually done in the PublishComponents and PublishFeatures
2677  * step, and not here.  It appears like the keypath and all that is
2678  * resolved in this step, however actually written in the Publish steps.
2679  * But we will leave it here for now because it is unclear
2680  */
2681 static UINT ACTION_ProcessComponents(MSIPACKAGE *package)
2682 {
2683     WCHAR squished_pc[GUID_SIZE];
2684     WCHAR squished_cc[GUID_SIZE];
2685     UINT rc;
2686     MSICOMPONENT *comp;
2687     HKEY hkey=0,hkey2=0;
2688
2689     /* writes the Component and Features values to the registry */
2690
2691     rc = MSIREG_OpenComponents(&hkey);
2692     if (rc != ERROR_SUCCESS)
2693         return rc;
2694
2695     squash_guid(package->ProductCode,squished_pc);
2696     ui_progress(package,1,COMPONENT_PROGRESS_VALUE,1,0);
2697
2698     LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2699     {
2700         MSIRECORD * uirow;
2701
2702         ui_progress(package,2,0,0,0);
2703         if (!comp->ComponentId)
2704             continue;
2705
2706         squash_guid(comp->ComponentId,squished_cc);
2707            
2708         msi_free(comp->FullKeypath);
2709         comp->FullKeypath = resolve_keypath( package, comp );
2710
2711         /* do the refcounting */
2712         ACTION_RefCountComponent( package, comp );
2713
2714         TRACE("Component %s (%s), Keypath=%s, RefCount=%i\n",
2715                             debugstr_w(comp->Component),
2716                             debugstr_w(squished_cc),
2717                             debugstr_w(comp->FullKeypath),
2718                             comp->RefCount);
2719         /*
2720          * Write the keypath out if the component is to be registered
2721          * and delete the key if the component is to be deregistered
2722          */
2723         if (ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
2724         {
2725             rc = RegCreateKeyW(hkey,squished_cc,&hkey2);
2726             if (rc != ERROR_SUCCESS)
2727                 continue;
2728
2729             if (!comp->FullKeypath)
2730                 continue;
2731
2732             msi_reg_set_val_str( hkey2, squished_pc, comp->FullKeypath );
2733
2734             if (comp->Attributes & msidbComponentAttributesPermanent)
2735             {
2736                 static const WCHAR szPermKey[] =
2737                     { '0','0','0','0','0','0','0','0','0','0','0','0',
2738                       '0','0','0','0','0','0','0','0','0','0','0','0',
2739                       '0','0','0','0','0','0','0','0',0 };
2740
2741                 msi_reg_set_val_str( hkey2, szPermKey, comp->FullKeypath );
2742             }
2743
2744             RegCloseKey(hkey2);
2745
2746             /* UI stuff */
2747             uirow = MSI_CreateRecord(3);
2748             MSI_RecordSetStringW(uirow,1,package->ProductCode);
2749             MSI_RecordSetStringW(uirow,2,comp->ComponentId);
2750             MSI_RecordSetStringW(uirow,3,comp->FullKeypath);
2751             ui_actiondata(package,szProcessComponents,uirow);
2752             msiobj_release( &uirow->hdr );
2753         }
2754         else if (ACTION_VerifyComponentForAction( comp, INSTALLSTATE_ABSENT))
2755         {
2756             DWORD res;
2757
2758             rc = RegOpenKeyW(hkey,squished_cc,&hkey2);
2759             if (rc != ERROR_SUCCESS)
2760                 continue;
2761
2762             RegDeleteValueW(hkey2,squished_pc);
2763
2764             /* if the key is empty delete it */
2765             res = RegEnumKeyExW(hkey2,0,NULL,0,0,NULL,0,NULL);
2766             RegCloseKey(hkey2);
2767             if (res == ERROR_NO_MORE_ITEMS)
2768                 RegDeleteKeyW(hkey,squished_cc);
2769
2770             /* UI stuff */
2771             uirow = MSI_CreateRecord(2);
2772             MSI_RecordSetStringW(uirow,1,package->ProductCode);
2773             MSI_RecordSetStringW(uirow,2,comp->ComponentId);
2774             ui_actiondata(package,szProcessComponents,uirow);
2775             msiobj_release( &uirow->hdr );
2776         }
2777     } 
2778     RegCloseKey(hkey);
2779     return rc;
2780 }
2781
2782 typedef struct {
2783     CLSID       clsid;
2784     LPWSTR      source;
2785
2786     LPWSTR      path;
2787     ITypeLib    *ptLib;
2788 } typelib_struct;
2789
2790 static BOOL CALLBACK Typelib_EnumResNameProc( HMODULE hModule, LPCWSTR lpszType, 
2791                                        LPWSTR lpszName, LONG_PTR lParam)
2792 {
2793     TLIBATTR *attr;
2794     typelib_struct *tl_struct = (typelib_struct*) lParam;
2795     static const WCHAR fmt[] = {'%','s','\\','%','i',0};
2796     int sz; 
2797     HRESULT res;
2798
2799     if (!IS_INTRESOURCE(lpszName))
2800     {
2801         ERR("Not Int Resource Name %s\n",debugstr_w(lpszName));
2802         return TRUE;
2803     }
2804
2805     sz = strlenW(tl_struct->source)+4;
2806     sz *= sizeof(WCHAR);
2807
2808     if ((INT_PTR)lpszName == 1)
2809         tl_struct->path = strdupW(tl_struct->source);
2810     else
2811     {
2812         tl_struct->path = msi_alloc(sz);
2813         sprintfW(tl_struct->path,fmt,tl_struct->source, lpszName);
2814     }
2815
2816     TRACE("trying %s\n", debugstr_w(tl_struct->path));
2817     res = LoadTypeLib(tl_struct->path,&tl_struct->ptLib);
2818     if (!SUCCEEDED(res))
2819     {
2820         msi_free(tl_struct->path);
2821         tl_struct->path = NULL;
2822
2823         return TRUE;
2824     }
2825
2826     ITypeLib_GetLibAttr(tl_struct->ptLib, &attr);
2827     if (IsEqualGUID(&(tl_struct->clsid),&(attr->guid)))
2828     {
2829         ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
2830         return FALSE;
2831     }
2832
2833     msi_free(tl_struct->path);
2834     tl_struct->path = NULL;
2835
2836     ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
2837     ITypeLib_Release(tl_struct->ptLib);
2838
2839     return TRUE;
2840 }
2841
2842 static UINT ITERATE_RegisterTypeLibraries(MSIRECORD *row, LPVOID param)
2843 {
2844     MSIPACKAGE* package = (MSIPACKAGE*)param;
2845     LPCWSTR component;
2846     MSICOMPONENT *comp;
2847     MSIFILE *file;
2848     typelib_struct tl_struct;
2849     HMODULE module;
2850     static const WCHAR szTYPELIB[] = {'T','Y','P','E','L','I','B',0};
2851
2852     component = MSI_RecordGetString(row,3);
2853     comp = get_loaded_component(package,component);
2854     if (!comp)
2855         return ERROR_SUCCESS;
2856
2857     if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
2858     {
2859         TRACE("Skipping typelib reg due to disabled component\n");
2860
2861         comp->Action = comp->Installed;
2862
2863         return ERROR_SUCCESS;
2864     }
2865
2866     comp->Action = INSTALLSTATE_LOCAL;
2867
2868     file = get_loaded_file( package, comp->KeyPath ); 
2869     if (!file)
2870         return ERROR_SUCCESS;
2871
2872     module = LoadLibraryExW( file->TargetPath, NULL, LOAD_LIBRARY_AS_DATAFILE );
2873     if (module)
2874     {
2875         LPCWSTR guid;
2876         guid = MSI_RecordGetString(row,1);
2877         CLSIDFromString((LPWSTR)guid, &tl_struct.clsid);
2878         tl_struct.source = strdupW( file->TargetPath );
2879         tl_struct.path = NULL;
2880
2881         EnumResourceNamesW(module, szTYPELIB, Typelib_EnumResNameProc,
2882                         (LONG_PTR)&tl_struct);
2883
2884         if (tl_struct.path)
2885         {
2886             LPWSTR help = NULL;
2887             LPCWSTR helpid;
2888             HRESULT res;
2889
2890             helpid = MSI_RecordGetString(row,6);
2891
2892             if (helpid)
2893                 help = resolve_folder(package,helpid,FALSE,FALSE,NULL);
2894             res = RegisterTypeLib(tl_struct.ptLib,tl_struct.path,help);
2895             msi_free(help);
2896
2897             if (!SUCCEEDED(res))
2898                 ERR("Failed to register type library %s\n",
2899                         debugstr_w(tl_struct.path));
2900             else
2901             {
2902                 ui_actiondata(package,szRegisterTypeLibraries,row);
2903
2904                 TRACE("Registered %s\n", debugstr_w(tl_struct.path));
2905             }
2906
2907             ITypeLib_Release(tl_struct.ptLib);
2908             msi_free(tl_struct.path);
2909         }
2910         else
2911             ERR("Failed to load type library %s\n",
2912                     debugstr_w(tl_struct.source));
2913
2914         FreeLibrary(module);
2915         msi_free(tl_struct.source);
2916     }
2917     else
2918         ERR("Could not load file! %s\n", debugstr_w(file->TargetPath));
2919
2920     return ERROR_SUCCESS;
2921 }
2922
2923 static UINT ACTION_RegisterTypeLibraries(MSIPACKAGE *package)
2924 {
2925     /* 
2926      * OK this is a bit confusing.. I am given a _Component key and I believe
2927      * that the file that is being registered as a type library is the "key file
2928      * of that component" which I interpret to mean "The file in the KeyPath of
2929      * that component".
2930      */
2931     UINT rc;
2932     MSIQUERY * view;
2933     static const WCHAR Query[] =
2934         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2935          '`','T','y','p','e','L','i','b','`',0};
2936
2937     rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
2938     if (rc != ERROR_SUCCESS)
2939         return ERROR_SUCCESS;
2940
2941     rc = MSI_IterateRecords(view, NULL, ITERATE_RegisterTypeLibraries, package);
2942     msiobj_release(&view->hdr);
2943     return rc;
2944 }
2945
2946 static UINT ITERATE_CreateShortcuts(MSIRECORD *row, LPVOID param)
2947 {
2948     MSIPACKAGE *package = (MSIPACKAGE*)param;
2949     LPWSTR target_file, target_folder, filename;
2950     LPCWSTR buffer, extension;
2951     MSICOMPONENT *comp;
2952     static const WCHAR szlnk[]={'.','l','n','k',0};
2953     IShellLinkW *sl = NULL;
2954     IPersistFile *pf = NULL;
2955     HRESULT res;
2956
2957     buffer = MSI_RecordGetString(row,4);
2958     comp = get_loaded_component(package,buffer);
2959     if (!comp)
2960         return ERROR_SUCCESS;
2961
2962     if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL ))
2963     {
2964         TRACE("Skipping shortcut creation due to disabled component\n");
2965
2966         comp->Action = comp->Installed;
2967
2968         return ERROR_SUCCESS;
2969     }
2970
2971     comp->Action = INSTALLSTATE_LOCAL;
2972
2973     ui_actiondata(package,szCreateShortcuts,row);
2974
2975     res = CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
2976                     &IID_IShellLinkW, (LPVOID *) &sl );
2977
2978     if (FAILED( res ))
2979     {
2980         ERR("CLSID_ShellLink not available\n");
2981         goto err;
2982     }
2983
2984     res = IShellLinkW_QueryInterface( sl, &IID_IPersistFile,(LPVOID*) &pf );
2985     if (FAILED( res ))
2986     {
2987         ERR("QueryInterface(IID_IPersistFile) failed\n");
2988         goto err;
2989     }
2990
2991     buffer = MSI_RecordGetString(row,2);
2992     target_folder = resolve_folder(package, buffer,FALSE,FALSE,NULL);
2993
2994     /* may be needed because of a bug somehwere else */
2995     create_full_pathW(target_folder);
2996
2997     filename = msi_dup_record_field( row, 3 );
2998     reduce_to_longfilename(filename);
2999
3000     extension = strchrW(filename,'.');
3001     if (!extension || strcmpiW(extension,szlnk))
3002     {
3003         int len = strlenW(filename);
3004         filename = msi_realloc(filename, len * sizeof(WCHAR) + sizeof(szlnk));
3005         memcpy(filename + len, szlnk, sizeof(szlnk));
3006     }
3007     target_file = build_directory_name(2, target_folder, filename);
3008     msi_free(target_folder);
3009     msi_free(filename);
3010
3011     buffer = MSI_RecordGetString(row,5);
3012     if (strchrW(buffer,'['))
3013     {
3014         LPWSTR deformated;
3015         deformat_string(package,buffer,&deformated);
3016         IShellLinkW_SetPath(sl,deformated);
3017         msi_free(deformated);
3018     }
3019     else
3020     {
3021         FIXME("poorly handled shortcut format, advertised shortcut\n");
3022         IShellLinkW_SetPath(sl,comp->FullKeypath);
3023     }
3024
3025     if (!MSI_RecordIsNull(row,6))
3026     {
3027         LPWSTR deformated;
3028         buffer = MSI_RecordGetString(row,6);
3029         deformat_string(package,buffer,&deformated);
3030         IShellLinkW_SetArguments(sl,deformated);
3031         msi_free(deformated);
3032     }
3033
3034     if (!MSI_RecordIsNull(row,7))
3035     {
3036         buffer = MSI_RecordGetString(row,7);
3037         IShellLinkW_SetDescription(sl,buffer);
3038     }
3039
3040     if (!MSI_RecordIsNull(row,8))
3041         IShellLinkW_SetHotkey(sl,MSI_RecordGetInteger(row,8));
3042
3043     if (!MSI_RecordIsNull(row,9))
3044     {
3045         LPWSTR Path;
3046         INT index; 
3047
3048         buffer = MSI_RecordGetString(row,9);
3049
3050         Path = build_icon_path(package,buffer);
3051         index = MSI_RecordGetInteger(row,10);
3052
3053         /* no value means 0 */
3054         if (index == MSI_NULL_INTEGER)
3055             index = 0;
3056
3057         IShellLinkW_SetIconLocation(sl,Path,index);
3058         msi_free(Path);
3059     }
3060
3061     if (!MSI_RecordIsNull(row,11))
3062         IShellLinkW_SetShowCmd(sl,MSI_RecordGetInteger(row,11));
3063
3064     if (!MSI_RecordIsNull(row,12))
3065     {
3066         LPWSTR Path;
3067         buffer = MSI_RecordGetString(row,12);
3068         Path = resolve_folder(package, buffer, FALSE, FALSE, NULL);
3069         if (Path)
3070             IShellLinkW_SetWorkingDirectory(sl,Path);
3071         msi_free(Path);
3072     }
3073
3074     TRACE("Writing shortcut to %s\n",debugstr_w(target_file));
3075     IPersistFile_Save(pf,target_file,FALSE);
3076
3077     msi_free(target_file);    
3078
3079 err:
3080     if (pf)
3081         IPersistFile_Release( pf );
3082     if (sl)
3083         IShellLinkW_Release( sl );
3084
3085     return ERROR_SUCCESS;
3086 }
3087
3088 static UINT ACTION_CreateShortcuts(MSIPACKAGE *package)
3089 {
3090     UINT rc;
3091     HRESULT res;
3092     MSIQUERY * view;
3093     static const WCHAR Query[] =
3094         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3095          '`','S','h','o','r','t','c','u','t','`',0};
3096
3097     rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3098     if (rc != ERROR_SUCCESS)
3099         return ERROR_SUCCESS;
3100
3101     res = CoInitialize( NULL );
3102     if (FAILED (res))
3103     {
3104         ERR("CoInitialize failed\n");
3105         return ERROR_FUNCTION_FAILED;
3106     }
3107
3108     rc = MSI_IterateRecords(view, NULL, ITERATE_CreateShortcuts, package);
3109     msiobj_release(&view->hdr);
3110
3111     CoUninitialize();
3112
3113     return rc;
3114 }
3115
3116 static UINT ITERATE_PublishProduct(MSIRECORD *row, LPVOID param)
3117 {
3118     MSIPACKAGE* package = (MSIPACKAGE*)param;
3119     HANDLE the_file;
3120     LPWSTR FilePath;
3121     LPCWSTR FileName;
3122     CHAR buffer[1024];
3123     DWORD sz;
3124     UINT rc;
3125     MSIRECORD *uirow;
3126
3127     FileName = MSI_RecordGetString(row,1);
3128     if (!FileName)
3129     {
3130         ERR("Unable to get FileName\n");
3131         return ERROR_SUCCESS;
3132     }
3133
3134     FilePath = build_icon_path(package,FileName);
3135
3136     TRACE("Creating icon file at %s\n",debugstr_w(FilePath));
3137
3138     the_file = CreateFileW(FilePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
3139                         FILE_ATTRIBUTE_NORMAL, NULL);
3140
3141     if (the_file == INVALID_HANDLE_VALUE)
3142     {
3143         ERR("Unable to create file %s\n",debugstr_w(FilePath));
3144         msi_free(FilePath);
3145         return ERROR_SUCCESS;
3146     }
3147
3148     do 
3149     {
3150         DWORD write;
3151         sz = 1024;
3152         rc = MSI_RecordReadStream(row,2,buffer,&sz);
3153         if (rc != ERROR_SUCCESS)
3154         {
3155             ERR("Failed to get stream\n");
3156             CloseHandle(the_file);  
3157             DeleteFileW(FilePath);
3158             break;
3159         }
3160         WriteFile(the_file,buffer,sz,&write,NULL);
3161     } while (sz == 1024);
3162
3163     msi_free(FilePath);
3164
3165     CloseHandle(the_file);
3166
3167     uirow = MSI_CreateRecord(1);
3168     MSI_RecordSetStringW(uirow,1,FileName);
3169     ui_actiondata(package,szPublishProduct,uirow);
3170     msiobj_release( &uirow->hdr );
3171
3172     return ERROR_SUCCESS;
3173 }
3174
3175 /*
3176  * 99% of the work done here is only done for 
3177  * advertised installs. However this is where the
3178  * Icon table is processed and written out
3179  * so that is what I am going to do here.
3180  */
3181 static UINT ACTION_PublishProduct(MSIPACKAGE *package)
3182 {
3183     UINT rc;
3184     MSIQUERY * view;
3185     static const WCHAR Query[]=
3186         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3187          '`','I','c','o','n','`',0};
3188     /* for registry stuff */
3189     HKEY hkey=0;
3190     HKEY hukey=0;
3191     static const WCHAR szProductLanguage[] =
3192         {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
3193     static const WCHAR szARPProductIcon[] =
3194         {'A','R','P','P','R','O','D','U','C','T','I','C','O','N',0};
3195     static const WCHAR szProductVersion[] =
3196         {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
3197     DWORD langid;
3198     LPWSTR buffer;
3199     DWORD size;
3200     MSIHANDLE hDb, hSumInfo;
3201
3202     /* write out icon files */
3203
3204     rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3205     if (rc == ERROR_SUCCESS)
3206     {
3207         MSI_IterateRecords(view, NULL, ITERATE_PublishProduct, package);
3208         msiobj_release(&view->hdr);
3209     }
3210
3211     /* ok there is a lot more done here but i need to figure out what */
3212
3213     rc = MSIREG_OpenProductsKey(package->ProductCode,&hkey,TRUE);
3214     if (rc != ERROR_SUCCESS)
3215         goto end;
3216
3217     rc = MSIREG_OpenUserProductsKey(package->ProductCode,&hukey,TRUE);
3218     if (rc != ERROR_SUCCESS)
3219         goto end;
3220
3221
3222     buffer = msi_dup_property( package, INSTALLPROPERTY_PRODUCTNAMEW );
3223     msi_reg_set_val_str( hukey, INSTALLPROPERTY_PRODUCTNAMEW, buffer );
3224     msi_free(buffer);
3225
3226     langid = msi_get_property_int( package, szProductLanguage, 0 );
3227     msi_reg_set_val_dword( hkey, INSTALLPROPERTY_LANGUAGEW, langid );
3228
3229     buffer = msi_dup_property( package, szARPProductIcon );
3230     if (buffer)
3231     {
3232         LPWSTR path = build_icon_path(package,buffer);
3233         msi_reg_set_val_str( hukey, INSTALLPROPERTY_PRODUCTICONW, path );
3234         msi_free( path );
3235     }
3236     msi_free(buffer);
3237
3238     buffer = msi_dup_property( package, szProductVersion );
3239     if (buffer)
3240     {
3241         DWORD verdword = msi_version_str_to_dword(buffer);
3242         msi_reg_set_val_dword( hkey, INSTALLPROPERTY_VERSIONW, verdword );
3243     }
3244     msi_free(buffer);
3245     
3246     /* FIXME: Need to write more keys to the user registry */
3247   
3248     hDb= alloc_msihandle( &package->db->hdr );
3249     if (!hDb) {
3250         rc = ERROR_NOT_ENOUGH_MEMORY;
3251         goto end;
3252     }
3253     rc = MsiGetSummaryInformationW(hDb, NULL, 0, &hSumInfo); 
3254     MsiCloseHandle(hDb);
3255     if (rc == ERROR_SUCCESS)
3256     {
3257         WCHAR guidbuffer[0x200];
3258         size = 0x200;
3259         rc = MsiSummaryInfoGetPropertyW(hSumInfo, 9, NULL, NULL, NULL,
3260                                         guidbuffer, &size);
3261         if (rc == ERROR_SUCCESS)
3262         {
3263             WCHAR squashed[GUID_SIZE];
3264             /* for now we only care about the first guid */
3265             LPWSTR ptr = strchrW(guidbuffer,';');
3266             if (ptr) *ptr = 0;
3267             squash_guid(guidbuffer,squashed);
3268             msi_reg_set_val_str( hukey, INSTALLPROPERTY_PACKAGECODEW, squashed );
3269         }
3270         else
3271         {
3272             ERR("Unable to query Revision_Number...\n");
3273             rc = ERROR_SUCCESS;
3274         }
3275         MsiCloseHandle(hSumInfo);
3276     }
3277     else
3278     {
3279         ERR("Unable to open Summary Information\n");
3280         rc = ERROR_SUCCESS;
3281     }
3282
3283 end:
3284
3285     RegCloseKey(hkey);
3286     RegCloseKey(hukey);
3287
3288     return rc;
3289 }
3290
3291 static UINT ITERATE_WriteIniValues(MSIRECORD *row, LPVOID param)
3292 {
3293     MSIPACKAGE *package = (MSIPACKAGE*)param;
3294     LPCWSTR component,section,key,value,identifier,filename,dirproperty;
3295     LPWSTR deformated_section, deformated_key, deformated_value;
3296     LPWSTR folder, fullname = NULL;
3297     MSIRECORD * uirow;
3298     INT action;
3299     MSICOMPONENT *comp;
3300     static const WCHAR szWindowsFolder[] =
3301           {'W','i','n','d','o','w','s','F','o','l','d','e','r',0};
3302
3303     component = MSI_RecordGetString(row, 8);
3304     comp = get_loaded_component(package,component);
3305
3306     if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
3307     {
3308         TRACE("Skipping ini file due to disabled component %s\n",
3309                         debugstr_w(component));
3310
3311         comp->Action = comp->Installed;
3312
3313         return ERROR_SUCCESS;
3314     }
3315
3316     comp->Action = INSTALLSTATE_LOCAL;
3317
3318     identifier = MSI_RecordGetString(row,1); 
3319     filename = MSI_RecordGetString(row,2);
3320     dirproperty = MSI_RecordGetString(row,3);
3321     section = MSI_RecordGetString(row,4);
3322     key = MSI_RecordGetString(row,5);
3323     value = MSI_RecordGetString(row,6);
3324     action = MSI_RecordGetInteger(row,7);
3325
3326     deformat_string(package,section,&deformated_section);
3327     deformat_string(package,key,&deformated_key);
3328     deformat_string(package,value,&deformated_value);
3329
3330     if (dirproperty)
3331     {
3332         folder = resolve_folder(package, dirproperty, FALSE, FALSE, NULL);
3333         if (!folder)
3334             folder = msi_dup_property( package, dirproperty );
3335     }
3336     else
3337         folder = msi_dup_property( package, szWindowsFolder );
3338
3339     if (!folder)
3340     {
3341         ERR("Unable to resolve folder! (%s)\n",debugstr_w(dirproperty));
3342         goto cleanup;
3343     }
3344
3345     fullname = build_directory_name(2, folder, filename);
3346
3347     if (action == 0)
3348     {
3349         TRACE("Adding value %s to section %s in %s\n",
3350                 debugstr_w(deformated_key), debugstr_w(deformated_section),
3351                 debugstr_w(fullname));
3352         WritePrivateProfileStringW(deformated_section, deformated_key,
3353                                    deformated_value, fullname);
3354     }
3355     else if (action == 1)
3356     {
3357         WCHAR returned[10];
3358         GetPrivateProfileStringW(deformated_section, deformated_key, NULL,
3359                                  returned, 10, fullname);
3360         if (returned[0] == 0)
3361         {
3362             TRACE("Adding value %s to section %s in %s\n",
3363                     debugstr_w(deformated_key), debugstr_w(deformated_section),
3364                     debugstr_w(fullname));
3365
3366             WritePrivateProfileStringW(deformated_section, deformated_key,
3367                                        deformated_value, fullname);
3368         }
3369     }
3370     else if (action == 3)
3371         FIXME("Append to existing section not yet implemented\n");
3372
3373     uirow = MSI_CreateRecord(4);
3374     MSI_RecordSetStringW(uirow,1,identifier);
3375     MSI_RecordSetStringW(uirow,2,deformated_section);
3376     MSI_RecordSetStringW(uirow,3,deformated_key);
3377     MSI_RecordSetStringW(uirow,4,deformated_value);
3378     ui_actiondata(package,szWriteIniValues,uirow);
3379     msiobj_release( &uirow->hdr );
3380 cleanup:
3381     msi_free(fullname);
3382     msi_free(folder);
3383     msi_free(deformated_key);
3384     msi_free(deformated_value);
3385     msi_free(deformated_section);
3386     return ERROR_SUCCESS;
3387 }
3388
3389 static UINT ACTION_WriteIniValues(MSIPACKAGE *package)
3390 {
3391     UINT rc;
3392     MSIQUERY * view;
3393     static const WCHAR ExecSeqQuery[] = 
3394         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3395          '`','I','n','i','F','i','l','e','`',0};
3396
3397     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
3398     if (rc != ERROR_SUCCESS)
3399     {
3400         TRACE("no IniFile table\n");
3401         return ERROR_SUCCESS;
3402     }
3403
3404     rc = MSI_IterateRecords(view, NULL, ITERATE_WriteIniValues, package);
3405     msiobj_release(&view->hdr);
3406     return rc;
3407 }
3408
3409 static UINT ITERATE_SelfRegModules(MSIRECORD *row, LPVOID param)
3410 {
3411     MSIPACKAGE *package = (MSIPACKAGE*)param;
3412     LPCWSTR filename;
3413     LPWSTR FullName;
3414     MSIFILE *file;
3415     DWORD len;
3416     static const WCHAR ExeStr[] =
3417         {'r','e','g','s','v','r','3','2','.','e','x','e',' ','\"',0};
3418     static const WCHAR close[] =  {'\"',0};
3419     STARTUPINFOW si;
3420     PROCESS_INFORMATION info;
3421     BOOL brc;
3422     MSIRECORD *uirow;
3423     LPWSTR uipath, p;
3424
3425     memset(&si,0,sizeof(STARTUPINFOW));
3426
3427     filename = MSI_RecordGetString(row,1);
3428     file = get_loaded_file( package, filename );
3429
3430     if (!file)
3431     {
3432         ERR("Unable to find file id %s\n",debugstr_w(filename));
3433         return ERROR_SUCCESS;
3434     }
3435
3436     len = strlenW(ExeStr) + strlenW( file->TargetPath ) + 2;
3437
3438     FullName = msi_alloc(len*sizeof(WCHAR));
3439     strcpyW(FullName,ExeStr);
3440     strcatW( FullName, file->TargetPath );
3441     strcatW(FullName,close);
3442
3443     TRACE("Registering %s\n",debugstr_w(FullName));
3444     brc = CreateProcessW(NULL, FullName, NULL, NULL, FALSE, 0, NULL, c_colon,
3445                     &si, &info);
3446
3447     if (brc)
3448         msi_dialog_check_messages(info.hProcess);
3449
3450     msi_free(FullName);
3451
3452     /* the UI chunk */
3453     uirow = MSI_CreateRecord( 2 );
3454     uipath = strdupW( file->TargetPath );
3455     p = strrchrW(uipath,'\\');
3456     if (p)
3457         p[1]=0;
3458     MSI_RecordSetStringW( uirow, 1, &p[2] );
3459     MSI_RecordSetStringW( uirow, 2, uipath);
3460     ui_actiondata( package, szSelfRegModules, uirow);
3461     msiobj_release( &uirow->hdr );
3462     msi_free( uipath );
3463     /* FIXME: call ui_progress? */
3464
3465     return ERROR_SUCCESS;
3466 }
3467
3468 static UINT ACTION_SelfRegModules(MSIPACKAGE *package)
3469 {
3470     UINT rc;
3471     MSIQUERY * view;
3472     static const WCHAR ExecSeqQuery[] = 
3473         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3474          '`','S','e','l','f','R','e','g','`',0};
3475
3476     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
3477     if (rc != ERROR_SUCCESS)
3478     {
3479         TRACE("no SelfReg table\n");
3480         return ERROR_SUCCESS;
3481     }
3482
3483     MSI_IterateRecords(view, NULL, ITERATE_SelfRegModules, package);
3484     msiobj_release(&view->hdr);
3485
3486     return ERROR_SUCCESS;
3487 }
3488
3489 static UINT ACTION_PublishFeatures(MSIPACKAGE *package)
3490 {
3491     MSIFEATURE *feature;
3492     UINT rc;
3493     HKEY hkey=0;
3494     HKEY hukey=0;
3495     
3496     rc = MSIREG_OpenFeaturesKey(package->ProductCode,&hkey,TRUE);
3497     if (rc != ERROR_SUCCESS)
3498         goto end;
3499
3500     rc = MSIREG_OpenUserFeaturesKey(package->ProductCode,&hukey,TRUE);
3501     if (rc != ERROR_SUCCESS)
3502         goto end;
3503
3504     /* here the guids are base 85 encoded */
3505     LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3506     {
3507         ComponentList *cl;
3508         LPWSTR data = NULL;
3509         GUID clsid;
3510         INT size;
3511         BOOL absent = FALSE;
3512         MSIRECORD *uirow;
3513
3514         if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_LOCAL ) &&
3515             !ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_SOURCE ) &&
3516             !ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_ADVERTISED ))
3517             absent = TRUE;
3518
3519         size = 1;
3520         LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3521         {
3522             size += 21;
3523         }
3524         if (feature->Feature_Parent)
3525             size += strlenW( feature->Feature_Parent )+2;
3526
3527         data = msi_alloc(size * sizeof(WCHAR));
3528
3529         data[0] = 0;
3530         LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3531         {
3532             MSICOMPONENT* component = cl->component;
3533             WCHAR buf[21];
3534
3535             buf[0] = 0;
3536             if (component->ComponentId)
3537             {
3538                 TRACE("From %s\n",debugstr_w(component->ComponentId));
3539                 CLSIDFromString(component->ComponentId, &clsid);
3540                 encode_base85_guid(&clsid,buf);
3541                 TRACE("to %s\n",debugstr_w(buf));
3542                 strcatW(data,buf);
3543             }
3544         }
3545         if (feature->Feature_Parent)
3546         {
3547             static const WCHAR sep[] = {'\2',0};
3548             strcatW(data,sep);
3549             strcatW(data,feature->Feature_Parent);
3550         }
3551
3552         msi_reg_set_val_str( hkey, feature->Feature, data );
3553         msi_free(data);
3554
3555         size = 0;
3556         if (feature->Feature_Parent)
3557             size = strlenW(feature->Feature_Parent)*sizeof(WCHAR);
3558         if (!absent)
3559         {
3560             RegSetValueExW(hukey,feature->Feature,0,REG_SZ,
3561                        (LPBYTE)feature->Feature_Parent,size);
3562         }
3563         else
3564         {
3565             size += 2*sizeof(WCHAR);
3566             data = msi_alloc(size);
3567             data[0] = 0x6;
3568             data[1] = 0;
3569             if (feature->Feature_Parent)
3570                 strcpyW( &data[1], feature->Feature_Parent );
3571             RegSetValueExW(hukey,feature->Feature,0,REG_SZ,
3572                        (LPBYTE)data,size);
3573             msi_free(data);
3574         }
3575
3576         /* the UI chunk */
3577         uirow = MSI_CreateRecord( 1 );
3578         MSI_RecordSetStringW( uirow, 1, feature->Feature );
3579         ui_actiondata( package, szPublishFeatures, uirow);
3580         msiobj_release( &uirow->hdr );
3581         /* FIXME: call ui_progress? */
3582     }
3583
3584 end:
3585     RegCloseKey(hkey);
3586     RegCloseKey(hukey);
3587     return rc;
3588 }
3589
3590 static UINT msi_get_local_package_name( LPWSTR path )
3591 {
3592     static const WCHAR szInstaller[] = {
3593         '\\','I','n','s','t','a','l','l','e','r','\\',0};
3594     static const WCHAR fmt[] = { '%','x','.','m','s','i',0};
3595     DWORD time, len, i;
3596     HANDLE handle;
3597
3598     time = GetTickCount();
3599     GetWindowsDirectoryW( path, MAX_PATH );
3600     lstrcatW( path, szInstaller );
3601     CreateDirectoryW( path, NULL );
3602
3603     len = lstrlenW(path);
3604     for (i=0; i<0x10000; i++)
3605     {
3606         snprintfW( &path[len], MAX_PATH - len, fmt, (time+i)&0xffff );
3607         handle = CreateFileW( path, GENERIC_WRITE, 0, NULL,
3608                               CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0 );
3609         if (handle != INVALID_HANDLE_VALUE)
3610         {
3611             CloseHandle(handle);
3612             break;
3613         }
3614         if (GetLastError() != ERROR_FILE_EXISTS &&
3615             GetLastError() != ERROR_SHARING_VIOLATION)
3616             return ERROR_FUNCTION_FAILED;
3617     }
3618
3619     return ERROR_SUCCESS;
3620 }
3621
3622 static UINT msi_make_package_local( MSIPACKAGE *package, HKEY hkey )
3623 {
3624     static const WCHAR szOriginalDatabase[] =
3625         {'O','r','i','g','i','n','a','l','D','a','t','a','b','a','s','e',0};
3626     WCHAR packagefile[MAX_PATH];
3627     LPWSTR msiFilePath;
3628     UINT r;
3629
3630     r = msi_get_local_package_name( packagefile );
3631     if (r != ERROR_SUCCESS)
3632         return r;
3633
3634     TRACE("Copying to local package %s\n",debugstr_w(packagefile));
3635
3636     msiFilePath = msi_dup_property( package, szOriginalDatabase );
3637     r = CopyFileW( msiFilePath, packagefile, FALSE);
3638     msi_free( msiFilePath );
3639
3640     if (!r)
3641     {
3642         ERR("Unable to copy package (%s -> %s) (error %d)\n",
3643             debugstr_w(msiFilePath), debugstr_w(packagefile), GetLastError());
3644         return ERROR_FUNCTION_FAILED;
3645     }
3646
3647     /* FIXME: maybe set this key in ACTION_RegisterProduct instead */
3648     msi_reg_set_val_str( hkey, INSTALLPROPERTY_LOCALPACKAGEW, packagefile );
3649     return ERROR_SUCCESS;
3650 }
3651
3652 static UINT msi_write_uninstall_property_vals( MSIPACKAGE *package, HKEY hkey )
3653 {
3654     LPWSTR prop, val, key;
3655     static const LPCSTR propval[] = {
3656         "ARPAUTHORIZEDCDFPREFIX", "AuthorizedCDFPrefix",
3657         "ARPCONTACT",             "Contact",
3658         "ARPCOMMENTS",            "Comments",
3659         "ProductName",            "DisplayName",
3660         "ProductVersion",         "DisplayVersion",
3661         "ARPHELPLINK",            "HelpLink",
3662         "ARPHELPTELEPHONE",       "HelpTelephone",
3663         "ARPINSTALLLOCATION",     "InstallLocation",
3664         "SourceDir",              "InstallSource",
3665         "Manufacturer",           "Publisher",
3666         "ARPREADME",              "Readme",
3667         "ARPSIZE",                "Size",
3668         "ARPURLINFOABOUT",        "URLInfoAbout",
3669         "ARPURLUPDATEINFO",       "URLUpdateInfo",
3670         NULL,
3671     };
3672     const LPCSTR *p = propval;
3673
3674     while( *p )
3675     {
3676         prop = strdupAtoW( *p++ );
3677         key = strdupAtoW( *p++ );
3678         val = msi_dup_property( package, prop );
3679         msi_reg_set_val_str( hkey, key, val );
3680         msi_free(val);
3681         msi_free(key);
3682         msi_free(prop);
3683     }
3684     return ERROR_SUCCESS;
3685 }
3686
3687 static UINT ACTION_RegisterProduct(MSIPACKAGE *package)
3688 {
3689     HKEY hkey=0;
3690     LPWSTR buffer = NULL;
3691     UINT rc;
3692     DWORD size, langid;
3693     static const WCHAR szWindowsInstaller[] = 
3694         {'W','i','n','d','o','w','s','I','n','s','t','a','l','l','e','r',0};
3695     static const WCHAR szUpgradeCode[] = 
3696         {'U','p','g','r','a','d','e','C','o','d','e',0};
3697     static const WCHAR modpath_fmt[] = 
3698         {'M','s','i','E','x','e','c','.','e','x','e',' ',
3699          '/','I','[','P','r','o','d','u','c','t','C','o','d','e',']',0};
3700     static const WCHAR szModifyPath[] = 
3701         {'M','o','d','i','f','y','P','a','t','h',0};
3702     static const WCHAR szUninstallString[] = 
3703         {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0};
3704     static const WCHAR szEstimatedSize[] = 
3705         {'E','s','t','i','m','a','t','e','d','S','i','z','e',0};
3706     static const WCHAR szProductLanguage[] =
3707         {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
3708     static const WCHAR szProductVersion[] =
3709         {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
3710
3711     SYSTEMTIME systime;
3712     static const WCHAR date_fmt[] = {'%','i','%','i','%','i',0};
3713     LPWSTR upgrade_code;
3714     WCHAR szDate[9]; 
3715
3716     rc = MSIREG_OpenUninstallKey(package->ProductCode,&hkey,TRUE);
3717     if (rc != ERROR_SUCCESS)
3718         return rc;
3719
3720     /* dump all the info i can grab */
3721     /* FIXME: Flesh out more information */
3722
3723     msi_write_uninstall_property_vals( package, hkey );
3724
3725     msi_reg_set_val_dword( hkey, szWindowsInstaller, 1 );
3726     
3727     msi_make_package_local( package, hkey );
3728
3729     /* do ModifyPath and UninstallString */
3730     size = deformat_string(package,modpath_fmt,&buffer);
3731     RegSetValueExW(hkey,szModifyPath,0,REG_EXPAND_SZ,(LPBYTE)buffer,size);
3732     RegSetValueExW(hkey,szUninstallString,0,REG_EXPAND_SZ,(LPBYTE)buffer,size);
3733     msi_free(buffer);
3734
3735     /* FIXME: Write real Estimated Size when we have it */
3736     msi_reg_set_val_dword( hkey, szEstimatedSize, 0 );
3737    
3738     GetLocalTime(&systime);
3739     sprintfW(szDate,date_fmt,systime.wYear,systime.wMonth,systime.wDay);
3740     msi_reg_set_val_str( hkey, INSTALLPROPERTY_INSTALLDATEW, szDate );
3741    
3742     langid = msi_get_property_int( package, szProductLanguage, 0 );
3743     msi_reg_set_val_dword( hkey, INSTALLPROPERTY_LANGUAGEW, langid );
3744
3745     buffer = msi_dup_property( package, szProductVersion );
3746     if (buffer)
3747     {
3748         DWORD verdword = msi_version_str_to_dword(buffer);
3749
3750         msi_reg_set_val_dword( hkey, INSTALLPROPERTY_VERSIONW, verdword );
3751         msi_reg_set_val_dword( hkey, INSTALLPROPERTY_VERSIONMAJORW, verdword>>24 );
3752         msi_reg_set_val_dword( hkey, INSTALLPROPERTY_VERSIONMINORW, (verdword>>16)&0x00FF );
3753     }
3754     msi_free(buffer);
3755     
3756     /* Handle Upgrade Codes */
3757     upgrade_code = msi_dup_property( package, szUpgradeCode );
3758     if (upgrade_code)
3759     {
3760         HKEY hkey2;
3761         WCHAR squashed[33];
3762         MSIREG_OpenUpgradeCodesKey(upgrade_code, &hkey2, TRUE);
3763         squash_guid(package->ProductCode,squashed);
3764         msi_reg_set_val_str( hkey2, squashed, NULL );
3765         RegCloseKey(hkey2);
3766         MSIREG_OpenUserUpgradeCodesKey(upgrade_code, &hkey2, TRUE);
3767         squash_guid(package->ProductCode,squashed);
3768         msi_reg_set_val_str( hkey2, squashed, NULL );
3769         RegCloseKey(hkey2);
3770
3771         msi_free(upgrade_code);
3772     }
3773     
3774     RegCloseKey(hkey);
3775
3776     /* FIXME: call ui_actiondata */
3777
3778     return ERROR_SUCCESS;
3779 }
3780
3781 static UINT ACTION_InstallExecute(MSIPACKAGE *package)
3782 {
3783     return execute_script(package,INSTALL_SCRIPT);
3784 }
3785
3786 static UINT ACTION_InstallFinalize(MSIPACKAGE *package)
3787 {
3788     UINT rc;
3789
3790     /* turn off scheduleing */
3791     package->script->CurrentlyScripting= FALSE;
3792
3793     /* first do the same as an InstallExecute */
3794     rc = ACTION_InstallExecute(package);
3795     if (rc != ERROR_SUCCESS)
3796         return rc;
3797
3798     /* then handle Commit Actions */
3799     rc = execute_script(package,COMMIT_SCRIPT);
3800
3801     return rc;
3802 }
3803
3804 static UINT ACTION_ForceReboot(MSIPACKAGE *package)
3805 {
3806     static const WCHAR RunOnce[] = {
3807     'S','o','f','t','w','a','r','e','\\',
3808     'M','i','c','r','o','s','o','f','t','\\',
3809     'W','i','n','d','o','w','s','\\',
3810     'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
3811     'R','u','n','O','n','c','e',0};
3812     static const WCHAR InstallRunOnce[] = {
3813     'S','o','f','t','w','a','r','e','\\',
3814     'M','i','c','r','o','s','o','f','t','\\',
3815     'W','i','n','d','o','w','s','\\',
3816     'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
3817     'I','n','s','t','a','l','l','e','r','\\',
3818     'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
3819
3820     static const WCHAR msiexec_fmt[] = {
3821     '%','s',
3822     '\\','M','s','i','E','x','e','c','.','e','x','e',' ','/','@',' ',
3823     '\"','%','s','\"',0};
3824     static const WCHAR install_fmt[] = {
3825     '/','I',' ','\"','%','s','\"',' ',
3826     'A','F','T','E','R','R','E','B','O','O','T','=','1',' ',
3827     'R','U','N','O','N','C','E','E','N','T','R','Y','=','\"','%','s','\"',0};
3828     WCHAR buffer[256], sysdir[MAX_PATH];
3829     HKEY hkey;
3830     WCHAR squished_pc[100];
3831
3832     squash_guid(package->ProductCode,squished_pc);
3833
3834     GetSystemDirectoryW(sysdir, sizeof(sysdir)/sizeof(sysdir[0]));
3835     RegCreateKeyW(HKEY_LOCAL_MACHINE,RunOnce,&hkey);
3836     snprintfW(buffer,sizeof(buffer)/sizeof(buffer[0]),msiexec_fmt,sysdir,
3837      squished_pc);
3838
3839     msi_reg_set_val_str( hkey, squished_pc, buffer );
3840     RegCloseKey(hkey);
3841
3842     TRACE("Reboot command %s\n",debugstr_w(buffer));
3843
3844     RegCreateKeyW(HKEY_LOCAL_MACHINE,InstallRunOnce,&hkey);
3845     sprintfW(buffer,install_fmt,package->ProductCode,squished_pc);
3846
3847     msi_reg_set_val_str( hkey, squished_pc, buffer );
3848     RegCloseKey(hkey);
3849
3850     return ERROR_INSTALL_SUSPEND;
3851 }
3852
3853 static UINT ACTION_ResolveSource(MSIPACKAGE* package)
3854 {
3855     DWORD attrib, len;
3856     LPWSTR ptr, source;
3857     UINT rc;
3858     
3859     /*
3860      * we are currently doing what should be done here in the top level Install
3861      * however for Adminastrative and uninstalls this step will be needed
3862      */
3863     if (!package->PackagePath)
3864         return ERROR_SUCCESS;
3865
3866     ptr = strrchrW(package->PackagePath, '\\');
3867     if (!ptr)
3868         return ERROR_SUCCESS;
3869
3870     len = ptr - package->PackagePath + 2;
3871     source = msi_alloc(len * sizeof(WCHAR));
3872     lstrcpynW(source,  package->PackagePath, len);
3873
3874     MSI_SetPropertyW(package, cszSourceDir, source);
3875     MSI_SetPropertyW(package, cszSOURCEDIR, source);
3876
3877     msi_free(source);
3878
3879     attrib = GetFileAttributesW(package->PackagePath);
3880     if (attrib == INVALID_FILE_ATTRIBUTES)
3881     {
3882         LPWSTR prompt;
3883         LPWSTR msg;
3884         DWORD size = 0;
3885
3886         rc = MsiSourceListGetInfoW(package->ProductCode, NULL, 
3887                 MSIINSTALLCONTEXT_USERMANAGED, MSICODE_PRODUCT,
3888                 INSTALLPROPERTY_DISKPROMPTW,NULL,&size);
3889         if (rc == ERROR_MORE_DATA)
3890         {
3891             prompt = msi_alloc(size * sizeof(WCHAR));
3892             MsiSourceListGetInfoW(package->ProductCode, NULL, 
3893                     MSIINSTALLCONTEXT_USERMANAGED, MSICODE_PRODUCT,
3894                     INSTALLPROPERTY_DISKPROMPTW,prompt,&size);
3895         }
3896         else
3897             prompt = strdupW(package->PackagePath);
3898
3899         msg = generate_error_string(package,1302,1,prompt);
3900         while(attrib == INVALID_FILE_ATTRIBUTES)
3901         {
3902             rc = MessageBoxW(NULL,msg,NULL,MB_OKCANCEL);
3903             if (rc == IDCANCEL)
3904             {
3905                 rc = ERROR_INSTALL_USEREXIT;
3906                 break;
3907             }
3908             attrib = GetFileAttributesW(package->PackagePath);
3909         }
3910         msi_free(prompt);
3911         rc = ERROR_SUCCESS;
3912     }
3913     else
3914         return ERROR_SUCCESS;
3915
3916     return rc;
3917 }
3918
3919 static UINT ACTION_RegisterUser(MSIPACKAGE *package)
3920 {
3921     HKEY hkey=0;
3922     LPWSTR buffer;
3923     LPWSTR productid;
3924     UINT rc,i;
3925
3926     static const WCHAR szPropKeys[][80] = 
3927     {
3928         {'P','r','o','d','u','c','t','I','D',0},
3929         {'U','S','E','R','N','A','M','E',0},
3930         {'C','O','M','P','A','N','Y','N','A','M','E',0},
3931         {0},
3932     };
3933
3934     static const WCHAR szRegKeys[][80] = 
3935     {
3936         {'P','r','o','d','u','c','t','I','D',0},
3937         {'R','e','g','O','w','n','e','r',0},
3938         {'R','e','g','C','o','m','p','a','n','y',0},
3939         {0},
3940     };
3941
3942     productid = msi_dup_property( package, INSTALLPROPERTY_PRODUCTIDW );
3943     if (!productid)
3944         return ERROR_SUCCESS;
3945
3946     rc = MSIREG_OpenUninstallKey(package->ProductCode,&hkey,TRUE);
3947     if (rc != ERROR_SUCCESS)
3948         goto end;
3949
3950     for( i = 0; szPropKeys[i][0]; i++ )
3951     {
3952         buffer = msi_dup_property( package, szPropKeys[i] );
3953         msi_reg_set_val_str( hkey, szRegKeys[i], buffer );
3954         msi_free( buffer );
3955     }
3956
3957 end:
3958     msi_free(productid);
3959     RegCloseKey(hkey);
3960
3961     /* FIXME: call ui_actiondata */
3962
3963     return ERROR_SUCCESS;
3964 }
3965
3966
3967 static UINT ACTION_ExecuteAction(MSIPACKAGE *package)
3968 {
3969     UINT rc;
3970
3971     package->script->InWhatSequence |= SEQUENCE_EXEC;
3972     rc = ACTION_ProcessExecSequence(package,FALSE);
3973     return rc;
3974 }
3975
3976
3977 static UINT ITERATE_PublishComponent(MSIRECORD *rec, LPVOID param)
3978 {
3979     MSIPACKAGE *package = (MSIPACKAGE*)param;
3980     LPCWSTR compgroupid=NULL;
3981     LPCWSTR feature=NULL;
3982     LPCWSTR text = NULL;
3983     LPCWSTR qualifier = NULL;
3984     LPCWSTR component = NULL;
3985     LPWSTR advertise = NULL;
3986     LPWSTR output = NULL;
3987     HKEY hkey;
3988     UINT rc = ERROR_SUCCESS;
3989     MSICOMPONENT *comp;
3990     DWORD sz = 0;
3991     MSIRECORD *uirow;
3992
3993     component = MSI_RecordGetString(rec,3);
3994     comp = get_loaded_component(package,component);
3995
3996     if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL ) && 
3997        !ACTION_VerifyComponentForAction( comp, INSTALLSTATE_SOURCE ) &&
3998        !ACTION_VerifyComponentForAction( comp, INSTALLSTATE_ADVERTISED ))
3999     {
4000         TRACE("Skipping: Component %s not scheduled for install\n",
4001                         debugstr_w(component));
4002
4003         return ERROR_SUCCESS;
4004     }
4005
4006     compgroupid = MSI_RecordGetString(rec,1);
4007     qualifier = MSI_RecordGetString(rec,2);
4008
4009     rc = MSIREG_OpenUserComponentsKey(compgroupid, &hkey, TRUE);
4010     if (rc != ERROR_SUCCESS)
4011         goto end;
4012     
4013     text = MSI_RecordGetString(rec,4);
4014     feature = MSI_RecordGetString(rec,5);
4015   
4016     advertise = create_component_advertise_string(package, comp, feature);
4017
4018     sz = strlenW(advertise);
4019
4020     if (text)
4021         sz += lstrlenW(text);
4022
4023     sz+=3;
4024     sz *= sizeof(WCHAR);
4025            
4026     output = msi_alloc_zero(sz);
4027     strcpyW(output,advertise);
4028     msi_free(advertise);
4029
4030     if (text)
4031         strcatW(output,text);
4032
4033     msi_reg_set_val_multi_str( hkey, qualifier, output );
4034     
4035 end:
4036     RegCloseKey(hkey);
4037     msi_free(output);
4038
4039     /* the UI chunk */
4040     uirow = MSI_CreateRecord( 2 );
4041     MSI_RecordSetStringW( uirow, 1, compgroupid );
4042     MSI_RecordSetStringW( uirow, 2, qualifier);
4043     ui_actiondata( package, szPublishComponents, uirow);
4044     msiobj_release( &uirow->hdr );
4045     /* FIXME: call ui_progress? */
4046
4047     return rc;
4048 }
4049
4050 /*
4051  * At present I am ignorning the advertised components part of this and only
4052  * focusing on the qualified component sets
4053  */
4054 static UINT ACTION_PublishComponents(MSIPACKAGE *package)
4055 {
4056     UINT rc;
4057     MSIQUERY * view;
4058     static const WCHAR ExecSeqQuery[] =
4059         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4060          '`','P','u','b','l','i','s','h',
4061          'C','o','m','p','o','n','e','n','t','`',0};
4062     
4063     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4064     if (rc != ERROR_SUCCESS)
4065         return ERROR_SUCCESS;
4066
4067     rc = MSI_IterateRecords(view, NULL, ITERATE_PublishComponent, package);
4068     msiobj_release(&view->hdr);
4069
4070     return rc;
4071 }
4072
4073 static UINT ITERATE_InstallService(MSIRECORD *rec, LPVOID param)
4074 {
4075     MSIPACKAGE *package = (MSIPACKAGE*)param;
4076     MSIRECORD *row;
4077     MSIFILE *file;
4078     SC_HANDLE hscm, service = NULL;
4079     LPCWSTR name, disp, comp, depends, pass;
4080     LPCWSTR load_order, serv_name, key;
4081     DWORD serv_type, start_type;
4082     DWORD err_control;
4083
4084     static const WCHAR query[] =
4085         {'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
4086          '`','C','o','m','p','o','n','e','n','t','`',' ',
4087          'W','H','E','R','E',' ',
4088          '`','C','o','m','p','o','n','e','n','t','`',' ',
4089          '=','\'','%','s','\'',0};
4090
4091     hscm = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASEW, GENERIC_WRITE);
4092     if (!hscm)
4093     {
4094         ERR("Failed to open the SC Manager!\n");
4095         goto done;
4096     }
4097
4098     start_type = MSI_RecordGetInteger(rec, 5);
4099     if (start_type == SERVICE_BOOT_START || start_type == SERVICE_SYSTEM_START)
4100         goto done;
4101
4102     depends = MSI_RecordGetString(rec, 8);
4103     if (depends && *depends)
4104         FIXME("Dependency list unhandled!\n");
4105
4106     name = MSI_RecordGetString(rec, 2);
4107     disp = MSI_RecordGetString(rec, 3);
4108     serv_type = MSI_RecordGetInteger(rec, 4);
4109     err_control = MSI_RecordGetInteger(rec, 6);
4110     load_order = MSI_RecordGetString(rec, 7);
4111     serv_name = MSI_RecordGetString(rec, 9);
4112     pass = MSI_RecordGetString(rec, 10);
4113     comp = MSI_RecordGetString(rec, 12);
4114
4115     /* fetch the service path */
4116     row = MSI_QueryGetRecord(package->db, query, comp);
4117     if (!row)
4118     {
4119         ERR("Control query failed!\n");
4120         goto done;
4121     }
4122
4123     key = MSI_RecordGetString(row, 6);
4124     msiobj_release(&row->hdr);
4125
4126     file = get_loaded_file(package, key);
4127     if (!file)
4128     {
4129         ERR("Failed to load the service file\n");
4130         goto done;
4131     }
4132
4133     service = CreateServiceW(hscm, name, disp, GENERIC_ALL, serv_type,
4134                              start_type, err_control, file->TargetPath,
4135                              load_order, NULL, NULL, serv_name, pass);
4136     if (!service)
4137     {
4138         if (GetLastError() != ERROR_SERVICE_EXISTS)
4139             ERR("Failed to create service %s: %d\n", debugstr_w(name), GetLastError());
4140     }
4141
4142 done:
4143     CloseServiceHandle(service);
4144     CloseServiceHandle(hscm);
4145
4146     return ERROR_SUCCESS;
4147 }
4148
4149 static UINT ACTION_InstallServices( MSIPACKAGE *package )
4150 {
4151     UINT rc;
4152     MSIQUERY * view;
4153     static const WCHAR ExecSeqQuery[] =
4154         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4155          'S','e','r','v','i','c','e','I','n','s','t','a','l','l',0};
4156     
4157     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4158     if (rc != ERROR_SUCCESS)
4159         return ERROR_SUCCESS;
4160
4161     rc = MSI_IterateRecords(view, NULL, ITERATE_InstallService, package);
4162     msiobj_release(&view->hdr);
4163
4164     return rc;
4165 }
4166
4167 static UINT msi_unimplemented_action_stub( MSIPACKAGE *package,
4168                                            LPCSTR action, LPCWSTR table )
4169 {
4170     static const WCHAR query[] = {
4171         'S','E','L','E','C','T',' ','*',' ',
4172         'F','R','O','M',' ','`','%','s','`',0 };
4173     MSIQUERY *view = NULL;
4174     DWORD count = 0;
4175     UINT r;
4176     
4177     r = MSI_OpenQuery( package->db, &view, query, table );
4178     if (r == ERROR_SUCCESS)
4179     {
4180         r = MSI_IterateRecords(view, &count, NULL, package);
4181         msiobj_release(&view->hdr);
4182     }
4183
4184     if (count)
4185         FIXME("%s -> %u ignored %s table values\n",
4186               action, count, debugstr_w(table));
4187
4188     return ERROR_SUCCESS;
4189 }
4190
4191 static UINT ACTION_AllocateRegistrySpace( MSIPACKAGE *package )
4192 {
4193     TRACE("%p\n", package);
4194     return ERROR_SUCCESS;
4195 }
4196
4197 static UINT ACTION_RemoveIniValues( MSIPACKAGE *package )
4198 {
4199     static const WCHAR table[] =
4200          {'R','e','m','o','v','e','I','n','i','F','i','l','e',0 };
4201     return msi_unimplemented_action_stub( package, "RemoveIniValues", table );
4202 }
4203
4204 static UINT ACTION_MoveFiles( MSIPACKAGE *package )
4205 {
4206     static const WCHAR table[] = { 'M','o','v','e','F','i','l','e',0 };
4207     return msi_unimplemented_action_stub( package, "MoveFiles", table );
4208 }
4209
4210 static UINT ACTION_PatchFiles( MSIPACKAGE *package )
4211 {
4212     static const WCHAR table[] = { 'P','a','t','c','h',0 };
4213     return msi_unimplemented_action_stub( package, "PatchFiles", table );
4214 }
4215
4216 static UINT ACTION_BindImage( MSIPACKAGE *package )
4217 {
4218     static const WCHAR table[] = { 'B','i','n','d','I','m','a','g','e',0 };
4219     return msi_unimplemented_action_stub( package, "BindImage", table );
4220 }
4221
4222 static UINT ACTION_IsolateComponents( MSIPACKAGE *package )
4223 {
4224     static const WCHAR table[] = {
4225         'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t',0 };
4226     return msi_unimplemented_action_stub( package, "IsolateComponents", table );
4227 }
4228
4229 static UINT ACTION_MigrateFeatureStates( MSIPACKAGE *package )
4230 {
4231     static const WCHAR table[] = { 'U','p','g','r','a','d','e',0 };
4232     return msi_unimplemented_action_stub( package, "MigrateFeatureStates", table );
4233 }
4234
4235 static UINT ACTION_SelfUnregModules( MSIPACKAGE *package )
4236 {
4237     static const WCHAR table[] = { 'S','e','l','f','R','e','g',0 };
4238     return msi_unimplemented_action_stub( package, "SelfUnregModules", table );
4239 }
4240
4241 static UINT ACTION_StartServices( MSIPACKAGE *package )
4242 {
4243     static const WCHAR table[] = {
4244         'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
4245     return msi_unimplemented_action_stub( package, "StartServices", table );
4246 }
4247
4248 static UINT ACTION_StopServices( MSIPACKAGE *package )
4249 {
4250     static const WCHAR table[] = {
4251         'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
4252     return msi_unimplemented_action_stub( package, "StopServices", table );
4253 }
4254
4255 static UINT ACTION_DeleteServices( MSIPACKAGE *package )
4256 {
4257     static const WCHAR table[] = {
4258         'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
4259     return msi_unimplemented_action_stub( package, "DeleteServices", table );
4260 }
4261
4262 static UINT ACTION_WriteEnvironmentStrings( MSIPACKAGE *package )
4263 {
4264     static const WCHAR table[] = {
4265         'E','n','v','i','r','o','n','m','e','n','t',0 };
4266     return msi_unimplemented_action_stub( package, "WriteEnvironmentStrings", table );
4267 }
4268
4269 static UINT ACTION_RemoveEnvironmentStrings( MSIPACKAGE *package )
4270 {
4271     static const WCHAR table[] = {
4272         'E','n','v','i','r','o','n','m','e','n','t',0 };
4273     return msi_unimplemented_action_stub( package, "RemoveEnvironmentStrings", table );
4274 }
4275
4276 static UINT ACTION_MsiPublishAssemblies( MSIPACKAGE *package )
4277 {
4278     static const WCHAR table[] = {
4279         'M','s','i','A','s','s','e','m','b','l','y',0 };
4280     return msi_unimplemented_action_stub( package, "MsiPublishAssemblies", table );
4281 }
4282
4283 static UINT ACTION_MsiUnpublishAssemblies( MSIPACKAGE *package )
4284 {
4285     static const WCHAR table[] = {
4286         'M','s','i','A','s','s','e','m','b','l','y',0 };
4287     return msi_unimplemented_action_stub( package, "MsiUnpublishAssemblies", table );
4288 }
4289
4290 static UINT ACTION_UnregisterFonts( MSIPACKAGE *package )
4291 {
4292     static const WCHAR table[] = { 'F','o','n','t',0 };
4293     return msi_unimplemented_action_stub( package, "UnregisterFonts", table );
4294 }
4295
4296 static UINT ACTION_CCPSearch( MSIPACKAGE *package )
4297 {
4298     static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
4299     return msi_unimplemented_action_stub( package, "CCPSearch", table );
4300 }
4301
4302 static UINT ACTION_RMCCPSearch( MSIPACKAGE *package )
4303 {
4304     static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
4305     return msi_unimplemented_action_stub( package, "RMCCPSearch", table );
4306 }
4307
4308 static UINT ACTION_RegisterComPlus( MSIPACKAGE *package )
4309 {
4310     static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
4311     return msi_unimplemented_action_stub( package, "RegisterComPlus", table );
4312 }
4313
4314 static UINT ACTION_UnregisterComPlus( MSIPACKAGE *package )
4315 {
4316     static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
4317     return msi_unimplemented_action_stub( package, "UnregisterComPlus", table );
4318 }
4319
4320 static struct _actions StandardActions[] = {
4321     { szAllocateRegistrySpace, ACTION_AllocateRegistrySpace },
4322     { szAppSearch, ACTION_AppSearch },
4323     { szBindImage, ACTION_BindImage },
4324     { szCCPSearch, ACTION_CCPSearch},
4325     { szCostFinalize, ACTION_CostFinalize },
4326     { szCostInitialize, ACTION_CostInitialize },
4327     { szCreateFolders, ACTION_CreateFolders },
4328     { szCreateShortcuts, ACTION_CreateShortcuts },
4329     { szDeleteServices, ACTION_DeleteServices },
4330     { szDisableRollback, NULL},
4331     { szDuplicateFiles, ACTION_DuplicateFiles },
4332     { szExecuteAction, ACTION_ExecuteAction },
4333     { szFileCost, ACTION_FileCost },
4334     { szFindRelatedProducts, ACTION_FindRelatedProducts },
4335     { szForceReboot, ACTION_ForceReboot },
4336     { szInstallAdminPackage, NULL},
4337     { szInstallExecute, ACTION_InstallExecute },
4338     { szInstallExecuteAgain, ACTION_InstallExecute },
4339     { szInstallFiles, ACTION_InstallFiles},
4340     { szInstallFinalize, ACTION_InstallFinalize },
4341     { szInstallInitialize, ACTION_InstallInitialize },
4342     { szInstallSFPCatalogFile, NULL},
4343     { szInstallValidate, ACTION_InstallValidate },
4344     { szIsolateComponents, ACTION_IsolateComponents },
4345     { szLaunchConditions, ACTION_LaunchConditions },
4346     { szMigrateFeatureStates, ACTION_MigrateFeatureStates },
4347     { szMoveFiles, ACTION_MoveFiles },
4348     { szMsiPublishAssemblies, ACTION_MsiPublishAssemblies },
4349     { szMsiUnpublishAssemblies, ACTION_MsiUnpublishAssemblies },
4350     { szInstallODBC, NULL},
4351     { szInstallServices, ACTION_InstallServices },
4352     { szPatchFiles, ACTION_PatchFiles },
4353     { szProcessComponents, ACTION_ProcessComponents },
4354     { szPublishComponents, ACTION_PublishComponents },
4355     { szPublishFeatures, ACTION_PublishFeatures },
4356     { szPublishProduct, ACTION_PublishProduct },
4357     { szRegisterClassInfo, ACTION_RegisterClassInfo },
4358     { szRegisterComPlus, ACTION_RegisterComPlus},
4359     { szRegisterExtensionInfo, ACTION_RegisterExtensionInfo },
4360     { szRegisterFonts, ACTION_RegisterFonts },
4361     { szRegisterMIMEInfo, ACTION_RegisterMIMEInfo },
4362     { szRegisterProduct, ACTION_RegisterProduct },
4363     { szRegisterProgIdInfo, ACTION_RegisterProgIdInfo },
4364     { szRegisterTypeLibraries, ACTION_RegisterTypeLibraries },
4365     { szRegisterUser, ACTION_RegisterUser},
4366     { szRemoveDuplicateFiles, NULL},
4367     { szRemoveEnvironmentStrings, ACTION_RemoveEnvironmentStrings },
4368     { szRemoveExistingProducts, NULL},
4369     { szRemoveFiles, ACTION_RemoveFiles},
4370     { szRemoveFolders, NULL},
4371     { szRemoveIniValues, ACTION_RemoveIniValues },
4372     { szRemoveODBC, NULL},
4373     { szRemoveRegistryValues, NULL},
4374     { szRemoveShortcuts, NULL},
4375     { szResolveSource, ACTION_ResolveSource},
4376     { szRMCCPSearch, ACTION_RMCCPSearch},
4377     { szScheduleReboot, NULL},
4378     { szSelfRegModules, ACTION_SelfRegModules },
4379     { szSelfUnregModules, ACTION_SelfUnregModules },
4380     { szSetODBCFolders, NULL},
4381     { szStartServices, ACTION_StartServices },
4382     { szStopServices, ACTION_StopServices },
4383     { szUnpublishComponents, NULL},
4384     { szUnpublishFeatures, NULL},
4385     { szUnregisterClassInfo, NULL},
4386     { szUnregisterComPlus, ACTION_UnregisterComPlus},
4387     { szUnregisterExtensionInfo, NULL},
4388     { szUnregisterFonts, ACTION_UnregisterFonts },
4389     { szUnregisterMIMEInfo, NULL},
4390     { szUnregisterProgIdInfo, NULL},
4391     { szUnregisterTypeLibraries, NULL},
4392     { szValidateProductID, NULL},
4393     { szWriteEnvironmentStrings, ACTION_WriteEnvironmentStrings },
4394     { szWriteIniValues, ACTION_WriteIniValues },
4395     { szWriteRegistryValues, ACTION_WriteRegistryValues},
4396     { NULL, NULL},
4397 };