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