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