msi: Move msi_get_property_int() to helpers.c
[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     rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
1319     if (rc != ERROR_SUCCESS)
1320         return ERROR_SUCCESS;
1321
1322     rc = MSI_IterateRecords(view, NULL, load_file, package);
1323     msiobj_release(&view->hdr);
1324
1325     return ERROR_SUCCESS;
1326 }
1327
1328
1329 /*
1330  * I am not doing any of the costing functionality yet. 
1331  * Mostly looking at doing the Component and Feature loading
1332  *
1333  * The native MSI does A LOT of modification to tables here. Mostly adding
1334  * a lot of temporary columns to the Feature and Component tables. 
1335  *
1336  *    note: Native msi also tracks the short filename. But I am only going to
1337  *          track the long ones.  Also looking at this directory table
1338  *          it appears that the directory table does not get the parents
1339  *          resolved base on property only based on their entries in the 
1340  *          directory table.
1341  */
1342 static UINT ACTION_CostInitialize(MSIPACKAGE *package)
1343 {
1344     MSIQUERY * view;
1345     UINT rc;
1346     static const WCHAR Query_all[] =
1347         {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1348          '`','F','e','a','t','u','r','e','`',0};
1349     static const WCHAR szCosting[] =
1350         {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
1351     static const WCHAR szZero[] = { '0', 0 };
1352
1353     if ( 1 == msi_get_property_int( package, szCosting, 0 ) )
1354         return ERROR_SUCCESS;
1355     
1356     MSI_SetPropertyW(package, szCosting, szZero);
1357     MSI_SetPropertyW(package, cszRootDrive , c_colon);
1358
1359     rc = MSI_DatabaseOpenViewW(package->db,Query_all,&view);
1360     if (rc != ERROR_SUCCESS)
1361         return rc;
1362
1363     rc = MSI_IterateRecords(view, NULL, load_feature, package);
1364     msiobj_release(&view->hdr);
1365
1366     load_all_files(package);
1367
1368     return ERROR_SUCCESS;
1369 }
1370
1371 static UINT execute_script(MSIPACKAGE *package, UINT script )
1372 {
1373     int i;
1374     UINT rc = ERROR_SUCCESS;
1375
1376     TRACE("Executing Script %i\n",script);
1377
1378     if (!package->script)
1379     {
1380         ERR("no script!\n");
1381         return ERROR_FUNCTION_FAILED;
1382     }
1383
1384     for (i = 0; i < package->script->ActionCount[script]; i++)
1385     {
1386         LPWSTR action;
1387         action = package->script->Actions[script][i];
1388         ui_actionstart(package, action);
1389         TRACE("Executing Action (%s)\n",debugstr_w(action));
1390         rc = ACTION_PerformAction(package, action, TRUE);
1391         msi_free(package->script->Actions[script][i]);
1392         if (rc != ERROR_SUCCESS)
1393             break;
1394     }
1395     msi_free(package->script->Actions[script]);
1396
1397     package->script->ActionCount[script] = 0;
1398     package->script->Actions[script] = NULL;
1399     return rc;
1400 }
1401
1402 static UINT ACTION_FileCost(MSIPACKAGE *package)
1403 {
1404     return ERROR_SUCCESS;
1405 }
1406
1407 static MSIFOLDER *load_folder( MSIPACKAGE *package, LPCWSTR dir )
1408 {
1409     static const WCHAR Query[] =
1410         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1411          '`','D','i','r','e','c', 't','o','r','y','`',' ',
1412          'W','H','E','R','E',' ', '`', 'D','i','r','e','c','t', 'o','r','y','`',
1413          ' ','=',' ','\'','%','s','\'',
1414          0};
1415     static const WCHAR szDot[] = { '.',0 };
1416     static WCHAR szEmpty[] = { 0 };
1417     LPWSTR p, tgt_short, tgt_long, src_short, src_long;
1418     LPCWSTR parent;
1419     MSIRECORD *row;
1420     MSIFOLDER *folder;
1421
1422     TRACE("Looking for dir %s\n",debugstr_w(dir));
1423
1424     folder = get_loaded_folder( package, dir );
1425     if (folder)
1426         return folder;
1427
1428     TRACE("Working to load %s\n",debugstr_w(dir));
1429
1430     folder = msi_alloc_zero( sizeof (MSIFOLDER) );
1431     if (!folder)
1432         return NULL;
1433
1434     folder->Directory = strdupW(dir);
1435
1436     row = MSI_QueryGetRecord(package->db, Query, dir);
1437     if (!row)
1438         return NULL;
1439
1440     p = msi_dup_record_field(row, 3);
1441
1442     /* split src and target dir */
1443     tgt_short = p;
1444     src_short = folder_split_path( p, ':' );
1445
1446     /* split the long and short paths */
1447     tgt_long = folder_split_path( tgt_short, '|' );
1448     src_long = folder_split_path( src_short, '|' );
1449
1450     /* check for no-op dirs */
1451     if (!lstrcmpW(szDot, tgt_short))
1452         tgt_short = szEmpty;
1453     if (!lstrcmpW(szDot, src_short))
1454         src_short = szEmpty;
1455
1456     if (!tgt_long)
1457         tgt_long = tgt_short;
1458         
1459     if (!src_short) {
1460         src_short = tgt_short;
1461         src_long = tgt_long;
1462     }
1463     
1464     if (!src_long)
1465         src_long = src_short;
1466
1467     /* FIXME: use the target short path too */
1468     folder->TargetDefault = strdupW(tgt_long);
1469     folder->SourceShortPath = strdupW(src_short);
1470     folder->SourceLongPath = strdupW(src_long);
1471     msi_free(p);
1472
1473     TRACE("TargetDefault = %s\n",debugstr_w( folder->TargetDefault ));
1474     TRACE("SourceLong = %s\n", debugstr_w( folder->SourceLongPath ));
1475     TRACE("SourceShort = %s\n", debugstr_w( folder->SourceShortPath ));
1476
1477     parent = MSI_RecordGetString(row, 2);
1478     if (parent) 
1479     {
1480         folder->Parent = load_folder( package, parent );
1481         if ( folder->Parent )
1482             TRACE("loaded parent %p %s\n", folder->Parent,
1483                   debugstr_w(folder->Parent->Directory));
1484         else
1485             ERR("failed to load parent folder %s\n", debugstr_w(parent));
1486     }
1487
1488     folder->Property = msi_dup_property( package, dir );
1489
1490     msiobj_release(&row->hdr);
1491
1492     list_add_tail( &package->folders, &folder->entry );
1493
1494     TRACE("%s returning %p\n",debugstr_w(dir),folder);
1495
1496     return folder;
1497 }
1498
1499 /* scan for and update current install states */
1500 static void ACTION_UpdateInstallStates(MSIPACKAGE *package)
1501 {
1502     MSICOMPONENT *comp;
1503     MSIFEATURE *feature;
1504
1505     LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
1506     {
1507         INSTALLSTATE res;
1508         res = MsiGetComponentPathW( package->ProductCode, 
1509                                     comp->ComponentId, NULL, NULL);
1510         if (res < 0)
1511             res = INSTALLSTATE_ABSENT;
1512         comp->Installed = res;
1513     }
1514
1515     LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1516     {
1517         ComponentList *cl;
1518         INSTALLSTATE res = -10;
1519
1520         LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1521         {
1522             comp= cl->component;
1523
1524             if (res == -10)
1525                 res = comp->Installed;
1526             else
1527             {
1528                 if (res == comp->Installed)
1529                     continue;
1530
1531                 if (res != comp->Installed)
1532                         res = INSTALLSTATE_INCOMPLETE;
1533             }
1534         }
1535         feature->Installed = res;
1536     }
1537 }
1538
1539 static BOOL process_state_property (MSIPACKAGE* package, LPCWSTR property, 
1540                                     INSTALLSTATE state)
1541 {
1542     static const WCHAR all[]={'A','L','L',0};
1543     LPWSTR override;
1544     MSIFEATURE *feature;
1545
1546     override = msi_dup_property( package, property );
1547     if (!override)
1548         return FALSE;
1549  
1550     LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1551     {
1552         if (strcmpiW(override,all)==0)
1553         {
1554             feature->ActionRequest= state;
1555             feature->Action = state;
1556         }
1557         else
1558         {
1559             LPWSTR ptr = override;
1560             LPWSTR ptr2 = strchrW(override,',');
1561
1562             while (ptr)
1563             {
1564                 if ((ptr2 && strncmpW(ptr,feature->Feature, ptr2-ptr)==0)
1565                     || (!ptr2 && strcmpW(ptr,feature->Feature)==0))
1566                 {
1567                     feature->ActionRequest= state;
1568                     feature->Action = state;
1569                     break;
1570                 }
1571                 if (ptr2)
1572                 {
1573                     ptr=ptr2+1;
1574                     ptr2 = strchrW(ptr,',');
1575                 }
1576                 else
1577                     break;
1578             }
1579         }
1580     } 
1581     msi_free(override);
1582
1583     return TRUE;
1584 }
1585
1586 static UINT SetFeatureStates(MSIPACKAGE *package)
1587 {
1588     int install_level;
1589     static const WCHAR szlevel[] =
1590         {'I','N','S','T','A','L','L','L','E','V','E','L',0};
1591     static const WCHAR szAddLocal[] =
1592         {'A','D','D','L','O','C','A','L',0};
1593     static const WCHAR szRemove[] =
1594         {'R','E','M','O','V','E',0};
1595     static const WCHAR szReinstall[] =
1596         {'R','E','I','N','S','T','A','L','L',0};
1597     BOOL override = FALSE;
1598     MSICOMPONENT* component;
1599     MSIFEATURE *feature;
1600
1601
1602     /* I do not know if this is where it should happen.. but */
1603
1604     TRACE("Checking Install Level\n");
1605
1606     install_level = msi_get_property_int( package, szlevel, 1 );
1607
1608     /* ok hereis the _real_ rub
1609      * all these activation/deactivation things happen in order and things
1610      * later on the list override things earlier on the list.
1611      * 1) INSTALLLEVEL processing
1612      * 2) ADDLOCAL
1613      * 3) REMOVE
1614      * 4) ADDSOURCE
1615      * 5) ADDDEFAULT
1616      * 6) REINSTALL
1617      * 7) COMPADDLOCAL
1618      * 8) COMPADDSOURCE
1619      * 9) FILEADDLOCAL
1620      * 10) FILEADDSOURCE
1621      * 11) FILEADDDEFAULT
1622      * I have confirmed that if ADDLOCAL is stated then the INSTALLLEVEL is
1623      * ignored for all the features. seems strange, especially since it is not
1624      * documented anywhere, but it is how it works. 
1625      *
1626      * I am still ignoring a lot of these. But that is ok for now, ADDLOCAL and
1627      * REMOVE are the big ones, since we don't handle administrative installs
1628      * yet anyway.
1629      */
1630     override |= process_state_property(package,szAddLocal,INSTALLSTATE_LOCAL);
1631     override |= process_state_property(package,szRemove,INSTALLSTATE_ABSENT);
1632     override |= process_state_property(package,szReinstall,INSTALLSTATE_LOCAL);
1633
1634     if (!override)
1635     {
1636         LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1637         {
1638             BOOL feature_state = ((feature->Level > 0) &&
1639                              (feature->Level <= install_level));
1640
1641             if ((feature_state) && (feature->Action == INSTALLSTATE_UNKNOWN))
1642             {
1643                 if (feature->Attributes & msidbFeatureAttributesFavorSource)
1644                 {
1645                     feature->ActionRequest = INSTALLSTATE_SOURCE;
1646                     feature->Action = INSTALLSTATE_SOURCE;
1647                 }
1648                 else if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1649                 {
1650                     feature->ActionRequest = INSTALLSTATE_ADVERTISED;
1651                     feature->Action = INSTALLSTATE_ADVERTISED;
1652                 }
1653                 else
1654                 {
1655                     feature->ActionRequest = INSTALLSTATE_LOCAL;
1656                     feature->Action = INSTALLSTATE_LOCAL;
1657                 }
1658             }
1659         }
1660     }
1661     else
1662     {
1663         /* set the Preselected Property */
1664         static const WCHAR szPreselected[] = {'P','r','e','s','e','l','e','c','t','e','d',0};
1665         static const WCHAR szOne[] = { '1', 0 };
1666
1667         MSI_SetPropertyW(package,szPreselected,szOne);
1668     }
1669
1670     /*
1671      * now we want to enable or disable components base on feature 
1672     */
1673
1674     LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1675     {
1676         ComponentList *cl;
1677
1678         TRACE("Examining Feature %s (Installed %i, Action %i, Request %i)\n",
1679             debugstr_w(feature->Feature), feature->Installed, feature->Action,
1680             feature->ActionRequest);
1681
1682         LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1683         {
1684             component = cl->component;
1685
1686             if (!component->Enabled)
1687             {
1688                 component->Action = INSTALLSTATE_UNKNOWN;
1689                 component->ActionRequest = INSTALLSTATE_UNKNOWN;
1690             }
1691             else
1692             {
1693                 if (feature->Action == INSTALLSTATE_LOCAL)
1694                 {
1695                     component->Action = INSTALLSTATE_LOCAL;
1696                     component->ActionRequest = INSTALLSTATE_LOCAL;
1697                 }
1698                 else if (feature->ActionRequest == INSTALLSTATE_SOURCE)
1699                 {
1700                     if ((component->Action == INSTALLSTATE_UNKNOWN) ||
1701                         (component->Action == INSTALLSTATE_ABSENT) ||
1702                         (component->Action == INSTALLSTATE_ADVERTISED))
1703                            
1704                     {
1705                         component->Action = INSTALLSTATE_SOURCE;
1706                         component->ActionRequest = INSTALLSTATE_SOURCE;
1707                     }
1708                 }
1709                 else if (feature->ActionRequest == INSTALLSTATE_ADVERTISED)
1710                 {
1711                     if ((component->Action == INSTALLSTATE_UNKNOWN) ||
1712                         (component->Action == INSTALLSTATE_ABSENT))
1713                            
1714                     {
1715                         component->Action = INSTALLSTATE_ADVERTISED;
1716                         component->ActionRequest = INSTALLSTATE_ADVERTISED;
1717                     }
1718                 }
1719                 else if (feature->ActionRequest == INSTALLSTATE_ABSENT)
1720                 {
1721                     if (component->Action == INSTALLSTATE_UNKNOWN)
1722                     {
1723                         component->Action = INSTALLSTATE_ABSENT;
1724                         component->ActionRequest = INSTALLSTATE_ABSENT;
1725                     }
1726                 }
1727             }
1728         }
1729     } 
1730
1731     LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1732     {
1733         TRACE("Result: Component %s (Installed %i, Action %i, Request %i)\n",
1734             debugstr_w(component->Component), component->Installed, 
1735             component->Action, component->ActionRequest);
1736     }
1737
1738
1739     return ERROR_SUCCESS;
1740 }
1741
1742 static UINT ITERATE_CostFinalizeDirectories(MSIRECORD *row, LPVOID param)
1743 {
1744     MSIPACKAGE *package = (MSIPACKAGE*)param;
1745     LPCWSTR name;
1746     LPWSTR path;
1747
1748     name = MSI_RecordGetString(row,1);
1749
1750     /* This helper function now does ALL the work */
1751     TRACE("Dir %s ...\n",debugstr_w(name));
1752     load_folder(package,name);
1753     path = resolve_folder(package,name,FALSE,TRUE,NULL);
1754     TRACE("resolves to %s\n",debugstr_w(path));
1755     msi_free(path);
1756
1757     return ERROR_SUCCESS;
1758 }
1759
1760 static UINT ITERATE_CostFinalizeConditions(MSIRECORD *row, LPVOID param)
1761 {
1762     MSIPACKAGE *package = (MSIPACKAGE*)param;
1763     LPCWSTR name;
1764     MSIFEATURE *feature;
1765
1766     name = MSI_RecordGetString( row, 1 );
1767
1768     feature = get_loaded_feature( package, name );
1769     if (!feature)
1770         ERR("FAILED to find loaded feature %s\n",debugstr_w(name));
1771     else
1772     {
1773         LPCWSTR Condition;
1774         Condition = MSI_RecordGetString(row,3);
1775
1776         if (MSI_EvaluateConditionW(package,Condition) == MSICONDITION_TRUE)
1777         {
1778             int level = MSI_RecordGetInteger(row,2);
1779             TRACE("Reseting feature %s to level %i\n", debugstr_w(name), level);
1780             feature->Level = level;
1781         }
1782     }
1783     return ERROR_SUCCESS;
1784 }
1785
1786
1787 /* 
1788  * A lot is done in this function aside from just the costing.
1789  * The costing needs to be implemented at some point but for now I am going
1790  * to focus on the directory building
1791  *
1792  */
1793 static UINT ACTION_CostFinalize(MSIPACKAGE *package)
1794 {
1795     static const WCHAR ExecSeqQuery[] =
1796         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1797          '`','D','i','r','e','c','t','o','r','y','`',0};
1798     static const WCHAR ConditionQuery[] =
1799         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1800          '`','C','o','n','d','i','t','i','o','n','`',0};
1801     static const WCHAR szCosting[] =
1802         {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
1803     static const WCHAR szlevel[] =
1804         {'I','N','S','T','A','L','L','L','E','V','E','L',0};
1805     static const WCHAR szOne[] = { '1', 0 };
1806     MSICOMPONENT *comp;
1807     MSIFILE *file;
1808     UINT rc;
1809     MSIQUERY * view;
1810     LPWSTR level;
1811
1812     if ( 1 == msi_get_property_int( package, szCosting, 0 ) )
1813         return ERROR_SUCCESS;
1814     
1815     TRACE("Building Directory properties\n");
1816
1817     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
1818     if (rc == ERROR_SUCCESS)
1819     {
1820         rc = MSI_IterateRecords(view, NULL, ITERATE_CostFinalizeDirectories,
1821                         package);
1822         msiobj_release(&view->hdr);
1823     }
1824
1825     TRACE("File calculations\n");
1826
1827     LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
1828     {
1829         MSICOMPONENT* comp = file->Component;
1830         LPWSTR p;
1831
1832         if (!comp)
1833             continue;
1834
1835         /* calculate target */
1836         p = resolve_folder(package, comp->Directory, FALSE, FALSE, NULL);
1837
1838         msi_free(file->TargetPath);
1839
1840         TRACE("file %s is named %s\n",
1841                debugstr_w(file->File),debugstr_w(file->FileName));       
1842
1843         file->TargetPath = build_directory_name(2, p, file->FileName);
1844
1845         msi_free(p);
1846
1847         TRACE("file %s resolves to %s\n",
1848                debugstr_w(file->File),debugstr_w(file->TargetPath));       
1849
1850         if (GetFileAttributesW(file->TargetPath) == INVALID_FILE_ATTRIBUTES)
1851         {
1852             file->state = msifs_missing;
1853             comp->Cost += file->FileSize;
1854             continue;
1855         }
1856
1857         if (file->Version)
1858         {
1859             DWORD handle;
1860             DWORD versize;
1861             UINT sz;
1862             LPVOID version;
1863             static WCHAR name[] = {'\\',0};
1864             static const WCHAR name_fmt[] = 
1865                 {'%','u','.','%','u','.','%','u','.','%','u',0};
1866             WCHAR filever[0x100];
1867             VS_FIXEDFILEINFO *lpVer;
1868
1869             TRACE("Version comparison..\n");
1870             versize = GetFileVersionInfoSizeW(file->TargetPath,&handle);
1871             version = msi_alloc(versize);
1872             GetFileVersionInfoW(file->TargetPath, 0, versize, version);
1873
1874             VerQueryValueW(version, name, (LPVOID*)&lpVer, &sz);
1875
1876             sprintfW(filever,name_fmt,
1877                 HIWORD(lpVer->dwFileVersionMS),
1878                 LOWORD(lpVer->dwFileVersionMS),
1879                 HIWORD(lpVer->dwFileVersionLS),
1880                 LOWORD(lpVer->dwFileVersionLS));
1881
1882             TRACE("new %s old %s\n", debugstr_w(file->Version),
1883                   debugstr_w(filever));
1884             if (strcmpiW(filever,file->Version)<0)
1885             {
1886                 file->state = msifs_overwrite;
1887                 /* FIXME: cost should be diff in size */
1888                 comp->Cost += file->FileSize;
1889             }
1890             else
1891                 file->state = msifs_present;
1892             msi_free(version);
1893         }
1894         else
1895             file->state = msifs_present;
1896     }
1897
1898     TRACE("Evaluating Condition Table\n");
1899
1900     rc = MSI_DatabaseOpenViewW(package->db, ConditionQuery, &view);
1901     if (rc == ERROR_SUCCESS)
1902     {
1903         rc = MSI_IterateRecords(view, NULL, ITERATE_CostFinalizeConditions,
1904                     package);
1905         msiobj_release(&view->hdr);
1906     }
1907
1908     TRACE("Enabling or Disabling Components\n");
1909     LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
1910     {
1911         if (comp->Condition)
1912         {
1913             if (MSI_EvaluateConditionW(package,
1914                 comp->Condition) == MSICONDITION_FALSE)
1915             {
1916                 TRACE("Disabling component %s\n", debugstr_w(comp->Component));
1917                 comp->Enabled = FALSE;
1918             }
1919         }
1920     }
1921
1922     MSI_SetPropertyW(package,szCosting,szOne);
1923     /* set default run level if not set */
1924     level = msi_dup_property( package, szlevel );
1925     if (!level)
1926         MSI_SetPropertyW(package,szlevel, szOne);
1927     msi_free(level);
1928
1929     ACTION_UpdateInstallStates(package);
1930
1931     return SetFeatureStates(package);
1932 }
1933
1934 /* OK this value is "interpreted" and then formatted based on the 
1935    first few characters */
1936 static LPSTR parse_value(MSIPACKAGE *package, LPCWSTR value, DWORD *type, 
1937                          DWORD *size)
1938 {
1939     LPSTR data = NULL;
1940     if (value[0]=='#' && value[1]!='#' && value[1]!='%')
1941     {
1942         if (value[1]=='x')
1943         {
1944             LPWSTR ptr;
1945             CHAR byte[5];
1946             LPWSTR deformated = NULL;
1947             int count;
1948
1949             deformat_string(package, &value[2], &deformated);
1950
1951             /* binary value type */
1952             ptr = deformated;
1953             *type = REG_BINARY;
1954             if (strlenW(ptr)%2)
1955                 *size = (strlenW(ptr)/2)+1;
1956             else
1957                 *size = strlenW(ptr)/2;
1958
1959             data = msi_alloc(*size);
1960
1961             byte[0] = '0'; 
1962             byte[1] = 'x'; 
1963             byte[4] = 0; 
1964             count = 0;
1965             /* if uneven pad with a zero in front */
1966             if (strlenW(ptr)%2)
1967             {
1968                 byte[2]= '0';
1969                 byte[3]= *ptr;
1970                 ptr++;
1971                 data[count] = (BYTE)strtol(byte,NULL,0);
1972                 count ++;
1973                 TRACE("Uneven byte count\n");
1974             }
1975             while (*ptr)
1976             {
1977                 byte[2]= *ptr;
1978                 ptr++;
1979                 byte[3]= *ptr;
1980                 ptr++;
1981                 data[count] = (BYTE)strtol(byte,NULL,0);
1982                 count ++;
1983             }
1984             msi_free(deformated);
1985
1986             TRACE("Data %li bytes(%i)\n",*size,count);
1987         }
1988         else
1989         {
1990             LPWSTR deformated;
1991             LPWSTR p;
1992             DWORD d = 0;
1993             deformat_string(package, &value[1], &deformated);
1994
1995             *type=REG_DWORD; 
1996             *size = sizeof(DWORD);
1997             data = msi_alloc(*size);
1998             p = deformated;
1999             if (*p == '-')
2000                 p++;
2001             while (*p)
2002             {
2003                 if ( (*p < '0') || (*p > '9') )
2004                     break;
2005                 d *= 10;
2006                 d += (*p - '0');
2007                 p++;
2008             }
2009             if (deformated[0] == '-')
2010                 d = -d;
2011             *(LPDWORD)data = d;
2012             TRACE("DWORD %li\n",*(LPDWORD)data);
2013
2014             msi_free(deformated);
2015         }
2016     }
2017     else
2018     {
2019         static const WCHAR szMulti[] = {'[','~',']',0};
2020         LPCWSTR ptr;
2021         *type=REG_SZ;
2022
2023         if (value[0]=='#')
2024         {
2025             if (value[1]=='%')
2026             {
2027                 ptr = &value[2];
2028                 *type=REG_EXPAND_SZ;
2029             }
2030             else
2031                 ptr = &value[1];
2032          }
2033          else
2034             ptr=value;
2035
2036         if (strstrW(value,szMulti))
2037             *type = REG_MULTI_SZ;
2038
2039         *size = deformat_string(package, ptr,(LPWSTR*)&data);
2040     }
2041     return data;
2042 }
2043
2044 static UINT ITERATE_WriteRegistryValues(MSIRECORD *row, LPVOID param)
2045 {
2046     MSIPACKAGE *package = (MSIPACKAGE*)param;
2047     static const WCHAR szHCR[] = 
2048         {'H','K','E','Y','_','C','L','A','S','S','E','S','_',
2049          'R','O','O','T','\\',0};
2050     static const WCHAR szHCU[] =
2051         {'H','K','E','Y','_','C','U','R','R','E','N','T','_',
2052          'U','S','E','R','\\',0};
2053     static const WCHAR szHLM[] =
2054         {'H','K','E','Y','_','L','O','C','A','L','_',
2055          'M','A','C','H','I','N','E','\\',0};
2056     static const WCHAR szHU[] =
2057         {'H','K','E','Y','_','U','S','E','R','S','\\',0};
2058
2059     LPSTR value_data = NULL;
2060     HKEY  root_key, hkey;
2061     DWORD type,size;
2062     LPWSTR  deformated;
2063     LPCWSTR szRoot, component, name, key, value;
2064     MSICOMPONENT *comp;
2065     MSIRECORD * uirow;
2066     LPWSTR uikey;
2067     INT   root;
2068     BOOL check_first = FALSE;
2069     UINT rc;
2070
2071     ui_progress(package,2,0,0,0);
2072
2073     value = NULL;
2074     key = NULL;
2075     uikey = NULL;
2076     name = NULL;
2077
2078     component = MSI_RecordGetString(row, 6);
2079     comp = get_loaded_component(package,component);
2080     if (!comp)
2081         return ERROR_SUCCESS;
2082
2083     if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
2084     {
2085         TRACE("Skipping write due to disabled component %s\n",
2086                         debugstr_w(component));
2087
2088         comp->Action = comp->Installed;
2089
2090         return ERROR_SUCCESS;
2091     }
2092
2093     comp->Action = INSTALLSTATE_LOCAL;
2094
2095     name = MSI_RecordGetString(row, 4);
2096     if( MSI_RecordIsNull(row,5) && name )
2097     {
2098         /* null values can have special meanings */
2099         if (name[0]=='-' && name[1] == 0)
2100                 return ERROR_SUCCESS;
2101         else if ((name[0]=='+' && name[1] == 0) || 
2102                  (name[0] == '*' && name[1] == 0))
2103                 name = NULL;
2104         check_first = TRUE;
2105     }
2106
2107     root = MSI_RecordGetInteger(row,2);
2108     key = MSI_RecordGetString(row, 3);
2109
2110     /* get the root key */
2111     switch (root)
2112     {
2113         case -1: 
2114             {
2115                 static const WCHAR szALLUSER[] = {'A','L','L','U','S','E','R','S',0};
2116                 LPWSTR all_users = msi_dup_property( package, szALLUSER );
2117                 if (all_users && all_users[0] == '1')
2118                 {
2119                     root_key = HKEY_LOCAL_MACHINE;
2120                     szRoot = szHLM;
2121                 }
2122                 else
2123                 {
2124                     root_key = HKEY_CURRENT_USER;
2125                     szRoot = szHCU;
2126                 }
2127                 msi_free(all_users);
2128             }
2129                  break;
2130         case 0:  root_key = HKEY_CLASSES_ROOT; 
2131                  szRoot = szHCR;
2132                  break;
2133         case 1:  root_key = HKEY_CURRENT_USER;
2134                  szRoot = szHCU;
2135                  break;
2136         case 2:  root_key = HKEY_LOCAL_MACHINE;
2137                  szRoot = szHLM;
2138                  break;
2139         case 3:  root_key = HKEY_USERS; 
2140                  szRoot = szHU;
2141                  break;
2142         default:
2143                  ERR("Unknown root %i\n",root);
2144                  root_key=NULL;
2145                  szRoot = NULL;
2146                  break;
2147     }
2148     if (!root_key)
2149         return ERROR_SUCCESS;
2150
2151     deformat_string(package, key , &deformated);
2152     size = strlenW(deformated) + strlenW(szRoot) + 1;
2153     uikey = msi_alloc(size*sizeof(WCHAR));
2154     strcpyW(uikey,szRoot);
2155     strcatW(uikey,deformated);
2156
2157     if (RegCreateKeyW( root_key, deformated, &hkey))
2158     {
2159         ERR("Could not create key %s\n",debugstr_w(deformated));
2160         msi_free(deformated);
2161         msi_free(uikey);
2162         return ERROR_SUCCESS;
2163     }
2164     msi_free(deformated);
2165
2166     value = MSI_RecordGetString(row,5);
2167     if (value)
2168         value_data = parse_value(package, value, &type, &size); 
2169     else
2170     {
2171         static const WCHAR szEmpty[] = {0};
2172         value_data = (LPSTR)strdupW(szEmpty);
2173         size = 0;
2174         type = REG_SZ;
2175     }
2176
2177     deformat_string(package, name, &deformated);
2178
2179     /* get the double nulls to terminate SZ_MULTI */
2180     if (type == REG_MULTI_SZ)
2181         size +=sizeof(WCHAR);
2182
2183     if (!check_first)
2184     {
2185         TRACE("Setting value %s of %s\n",debugstr_w(deformated),
2186                         debugstr_w(uikey));
2187         RegSetValueExW(hkey, deformated, 0, type, (LPBYTE)value_data, size);
2188     }
2189     else
2190     {
2191         DWORD sz = 0;
2192         rc = RegQueryValueExW(hkey, deformated, NULL, NULL, NULL, &sz);
2193         if (rc == ERROR_SUCCESS || rc == ERROR_MORE_DATA)
2194         {
2195             TRACE("value %s of %s checked already exists\n",
2196                             debugstr_w(deformated), debugstr_w(uikey));
2197         }
2198         else
2199         {
2200             TRACE("Checked and setting value %s of %s\n",
2201                             debugstr_w(deformated), debugstr_w(uikey));
2202             if (deformated || size)
2203                 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE) value_data, size);
2204         }
2205     }
2206     RegCloseKey(hkey);
2207
2208     uirow = MSI_CreateRecord(3);
2209     MSI_RecordSetStringW(uirow,2,deformated);
2210     MSI_RecordSetStringW(uirow,1,uikey);
2211
2212     if (type == REG_SZ)
2213         MSI_RecordSetStringW(uirow,3,(LPWSTR)value_data);
2214     else
2215         MSI_RecordSetStringW(uirow,3,value);
2216
2217     ui_actiondata(package,szWriteRegistryValues,uirow);
2218     msiobj_release( &uirow->hdr );
2219
2220     msi_free(value_data);
2221     msi_free(deformated);
2222     msi_free(uikey);
2223
2224     return ERROR_SUCCESS;
2225 }
2226
2227 static UINT ACTION_WriteRegistryValues(MSIPACKAGE *package)
2228 {
2229     UINT rc;
2230     MSIQUERY * view;
2231     static const WCHAR ExecSeqQuery[] =
2232         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2233          '`','R','e','g','i','s','t','r','y','`',0 };
2234
2235     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2236     if (rc != ERROR_SUCCESS)
2237         return ERROR_SUCCESS;
2238
2239     /* increment progress bar each time action data is sent */
2240     ui_progress(package,1,REG_PROGRESS_VALUE,1,0);
2241
2242     rc = MSI_IterateRecords(view, NULL, ITERATE_WriteRegistryValues, package);
2243
2244     msiobj_release(&view->hdr);
2245     return rc;
2246 }
2247
2248 static UINT ACTION_InstallInitialize(MSIPACKAGE *package)
2249 {
2250     package->script->CurrentlyScripting = TRUE;
2251
2252     return ERROR_SUCCESS;
2253 }
2254
2255
2256 static UINT ACTION_InstallValidate(MSIPACKAGE *package)
2257 {
2258     MSICOMPONENT *comp;
2259     DWORD progress = 0;
2260     DWORD total = 0;
2261     static const WCHAR q1[]=
2262         {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
2263          '`','R','e','g','i','s','t','r','y','`',0};
2264     UINT rc;
2265     MSIQUERY * view;
2266     MSIFEATURE *feature;
2267     MSIFILE *file;
2268
2269     TRACE("InstallValidate\n");
2270
2271     rc = MSI_DatabaseOpenViewW(package->db, q1, &view);
2272     if (rc == ERROR_SUCCESS)
2273     {
2274         MSI_IterateRecords( view, &progress, NULL, package );
2275         msiobj_release( &view->hdr );
2276         total += progress * REG_PROGRESS_VALUE;
2277     }
2278
2279     LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2280         total += COMPONENT_PROGRESS_VALUE;
2281
2282     LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2283         total += file->FileSize;
2284
2285     ui_progress(package,0,total,0,0);
2286
2287     LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2288     {
2289         TRACE("Feature: %s; Installed: %i; Action %i; Request %i\n",
2290             debugstr_w(feature->Feature), feature->Installed, feature->Action,
2291             feature->ActionRequest);
2292     }
2293     
2294     return ERROR_SUCCESS;
2295 }
2296
2297 static UINT ITERATE_LaunchConditions(MSIRECORD *row, LPVOID param)
2298 {
2299     MSIPACKAGE* package = (MSIPACKAGE*)param;
2300     LPCWSTR cond = NULL; 
2301     LPCWSTR message = NULL;
2302     static const WCHAR title[]=
2303         {'I','n','s','t','a','l','l',' ','F','a', 'i','l','e','d',0};
2304
2305     cond = MSI_RecordGetString(row,1);
2306
2307     if (MSI_EvaluateConditionW(package,cond) != MSICONDITION_TRUE)
2308     {
2309         LPWSTR deformated;
2310         message = MSI_RecordGetString(row,2);
2311         deformat_string(package,message,&deformated); 
2312         MessageBoxW(NULL,deformated,title,MB_OK);
2313         msi_free(deformated);
2314         return ERROR_FUNCTION_FAILED;
2315     }
2316
2317     return ERROR_SUCCESS;
2318 }
2319
2320 static UINT ACTION_LaunchConditions(MSIPACKAGE *package)
2321 {
2322     UINT rc;
2323     MSIQUERY * view = NULL;
2324     static const WCHAR ExecSeqQuery[] =
2325         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2326          '`','L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','`',0};
2327
2328     TRACE("Checking launch conditions\n");
2329
2330     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2331     if (rc != ERROR_SUCCESS)
2332         return ERROR_SUCCESS;
2333
2334     rc = MSI_IterateRecords(view, NULL, ITERATE_LaunchConditions, package);
2335     msiobj_release(&view->hdr);
2336
2337     return rc;
2338 }
2339
2340 static LPWSTR resolve_keypath( MSIPACKAGE* package, MSICOMPONENT *cmp )
2341 {
2342
2343     if (!cmp->KeyPath)
2344         return resolve_folder(package,cmp->Directory,FALSE,FALSE,NULL);
2345
2346     if (cmp->Attributes & msidbComponentAttributesRegistryKeyPath)
2347     {
2348         MSIRECORD * row = 0;
2349         UINT root,len;
2350         LPWSTR deformated,buffer,deformated_name;
2351         LPCWSTR key,name;
2352         static const WCHAR ExecSeqQuery[] =
2353             {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2354              '`','R','e','g','i','s','t','r','y','`',' ',
2355              'W','H','E','R','E',' ', '`','R','e','g','i','s','t','r','y','`',
2356              ' ','=',' ' ,'\'','%','s','\'',0 };
2357         static const WCHAR fmt[]={'%','0','2','i',':','\\','%','s','\\',0};
2358         static const WCHAR fmt2[]=
2359             {'%','0','2','i',':','\\','%','s','\\','%','s',0};
2360
2361         row = MSI_QueryGetRecord(package->db, ExecSeqQuery,cmp->KeyPath);
2362         if (!row)
2363             return NULL;
2364
2365         root = MSI_RecordGetInteger(row,2);
2366         key = MSI_RecordGetString(row, 3);
2367         name = MSI_RecordGetString(row, 4);
2368         deformat_string(package, key , &deformated);
2369         deformat_string(package, name, &deformated_name);
2370
2371         len = strlenW(deformated) + 6;
2372         if (deformated_name)
2373             len+=strlenW(deformated_name);
2374
2375         buffer = msi_alloc( len *sizeof(WCHAR));
2376
2377         if (deformated_name)
2378             sprintfW(buffer,fmt2,root,deformated,deformated_name);
2379         else
2380             sprintfW(buffer,fmt,root,deformated);
2381
2382         msi_free(deformated);
2383         msi_free(deformated_name);
2384         msiobj_release(&row->hdr);
2385
2386         return buffer;
2387     }
2388     else if (cmp->Attributes & msidbComponentAttributesODBCDataSource)
2389     {
2390         FIXME("UNIMPLEMENTED keypath as ODBC Source\n");
2391         return NULL;
2392     }
2393     else
2394     {
2395         MSIFILE *file = get_loaded_file( package, cmp->KeyPath );
2396
2397         if (file)
2398             return strdupW( file->TargetPath );
2399     }
2400     return NULL;
2401 }
2402
2403 static HKEY openSharedDLLsKey(void)
2404 {
2405     HKEY hkey=0;
2406     static const WCHAR path[] =
2407         {'S','o','f','t','w','a','r','e','\\',
2408          'M','i','c','r','o','s','o','f','t','\\',
2409          'W','i','n','d','o','w','s','\\',
2410          'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
2411          'S','h','a','r','e','d','D','L','L','s',0};
2412
2413     RegCreateKeyW(HKEY_LOCAL_MACHINE,path,&hkey);
2414     return hkey;
2415 }
2416
2417 static UINT ACTION_GetSharedDLLsCount(LPCWSTR dll)
2418 {
2419     HKEY hkey;
2420     DWORD count=0;
2421     DWORD type;
2422     DWORD sz = sizeof(count);
2423     DWORD rc;
2424     
2425     hkey = openSharedDLLsKey();
2426     rc = RegQueryValueExW(hkey, dll, NULL, &type, (LPBYTE)&count, &sz);
2427     if (rc != ERROR_SUCCESS)
2428         count = 0;
2429     RegCloseKey(hkey);
2430     return count;
2431 }
2432
2433 static UINT ACTION_WriteSharedDLLsCount(LPCWSTR path, UINT count)
2434 {
2435     HKEY hkey;
2436
2437     hkey = openSharedDLLsKey();
2438     if (count > 0)
2439         msi_reg_set_val_dword( hkey, path, count );
2440     else
2441         RegDeleteValueW(hkey,path);
2442     RegCloseKey(hkey);
2443     return count;
2444 }
2445
2446 /*
2447  * Return TRUE if the count should be written out and FALSE if not
2448  */
2449 static void ACTION_RefCountComponent( MSIPACKAGE* package, MSICOMPONENT *comp )
2450 {
2451     MSIFEATURE *feature;
2452     INT count = 0;
2453     BOOL write = FALSE;
2454
2455     /* only refcount DLLs */
2456     if (comp->KeyPath == NULL || 
2457         comp->Attributes & msidbComponentAttributesRegistryKeyPath || 
2458         comp->Attributes & msidbComponentAttributesODBCDataSource)
2459         write = FALSE;
2460     else
2461     {
2462         count = ACTION_GetSharedDLLsCount( comp->FullKeypath);
2463         write = (count > 0);
2464
2465         if (comp->Attributes & msidbComponentAttributesSharedDllRefCount)
2466             write = TRUE;
2467     }
2468
2469     /* increment counts */
2470     LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2471     {
2472         ComponentList *cl;
2473
2474         if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_LOCAL ))
2475             continue;
2476
2477         LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
2478         {
2479             if ( cl->component == comp )
2480                 count++;
2481         }
2482     }
2483
2484     /* decrement counts */
2485     LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2486     {
2487         ComponentList *cl;
2488
2489         if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_ABSENT ))
2490             continue;
2491
2492         LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
2493         {
2494             if ( cl->component == comp )
2495                 count--;
2496         }
2497     }
2498
2499     /* ref count all the files in the component */
2500     if (write)
2501     {
2502         MSIFILE *file;
2503
2504         LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2505         {
2506             if (file->Component == comp)
2507                 ACTION_WriteSharedDLLsCount( file->TargetPath, count );
2508         }
2509     }
2510     
2511     /* add a count for permenent */
2512     if (comp->Attributes & msidbComponentAttributesPermanent)
2513         count ++;
2514     
2515     comp->RefCount = count;
2516
2517     if (write)
2518         ACTION_WriteSharedDLLsCount( comp->FullKeypath, comp->RefCount );
2519 }
2520
2521 /*
2522  * Ok further analysis makes me think that this work is
2523  * actually done in the PublishComponents and PublishFeatures
2524  * step, and not here.  It appears like the keypath and all that is
2525  * resolved in this step, however actually written in the Publish steps.
2526  * But we will leave it here for now because it is unclear
2527  */
2528 static UINT ACTION_ProcessComponents(MSIPACKAGE *package)
2529 {
2530     WCHAR squished_pc[GUID_SIZE];
2531     WCHAR squished_cc[GUID_SIZE];
2532     UINT rc;
2533     MSICOMPONENT *comp;
2534     HKEY hkey=0,hkey2=0;
2535
2536     /* writes the Component and Features values to the registry */
2537
2538     rc = MSIREG_OpenComponents(&hkey);
2539     if (rc != ERROR_SUCCESS)
2540         return rc;
2541
2542     squash_guid(package->ProductCode,squished_pc);
2543     ui_progress(package,1,COMPONENT_PROGRESS_VALUE,1,0);
2544
2545     LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2546     {
2547         MSIRECORD * uirow;
2548
2549         ui_progress(package,2,0,0,0);
2550         if (!comp->ComponentId)
2551             continue;
2552
2553         squash_guid(comp->ComponentId,squished_cc);
2554            
2555         msi_free(comp->FullKeypath);
2556         comp->FullKeypath = resolve_keypath( package, comp );
2557
2558         /* do the refcounting */
2559         ACTION_RefCountComponent( package, comp );
2560
2561         TRACE("Component %s (%s), Keypath=%s, RefCount=%i\n",
2562                             debugstr_w(comp->Component),
2563                             debugstr_w(squished_cc),
2564                             debugstr_w(comp->FullKeypath),
2565                             comp->RefCount);
2566         /*
2567          * Write the keypath out if the component is to be registered
2568          * and delete the key if the component is to be deregistered
2569          */
2570         if (ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
2571         {
2572             rc = RegCreateKeyW(hkey,squished_cc,&hkey2);
2573             if (rc != ERROR_SUCCESS)
2574                 continue;
2575
2576             if (!comp->FullKeypath)
2577                 continue;
2578
2579             msi_reg_set_val_str( hkey2, squished_pc, comp->FullKeypath );
2580
2581             if (comp->Attributes & msidbComponentAttributesPermanent)
2582             {
2583                 static const WCHAR szPermKey[] =
2584                     { '0','0','0','0','0','0','0','0','0','0','0','0',
2585                       '0','0','0','0','0','0','0','0','0','0','0','0',
2586                       '0','0','0','0','0','0','0','0',0 };
2587
2588                 msi_reg_set_val_str( hkey2, szPermKey, comp->FullKeypath );
2589             }
2590
2591             RegCloseKey(hkey2);
2592
2593             /* UI stuff */
2594             uirow = MSI_CreateRecord(3);
2595             MSI_RecordSetStringW(uirow,1,package->ProductCode);
2596             MSI_RecordSetStringW(uirow,2,comp->ComponentId);
2597             MSI_RecordSetStringW(uirow,3,comp->FullKeypath);
2598             ui_actiondata(package,szProcessComponents,uirow);
2599             msiobj_release( &uirow->hdr );
2600         }
2601         else if (ACTION_VerifyComponentForAction( comp, INSTALLSTATE_ABSENT))
2602         {
2603             DWORD res;
2604
2605             rc = RegOpenKeyW(hkey,squished_cc,&hkey2);
2606             if (rc != ERROR_SUCCESS)
2607                 continue;
2608
2609             RegDeleteValueW(hkey2,squished_pc);
2610
2611             /* if the key is empty delete it */
2612             res = RegEnumKeyExW(hkey2,0,NULL,0,0,NULL,0,NULL);
2613             RegCloseKey(hkey2);
2614             if (res == ERROR_NO_MORE_ITEMS)
2615                 RegDeleteKeyW(hkey,squished_cc);
2616
2617             /* UI stuff */
2618             uirow = MSI_CreateRecord(2);
2619             MSI_RecordSetStringW(uirow,1,package->ProductCode);
2620             MSI_RecordSetStringW(uirow,2,comp->ComponentId);
2621             ui_actiondata(package,szProcessComponents,uirow);
2622             msiobj_release( &uirow->hdr );
2623         }
2624     } 
2625     RegCloseKey(hkey);
2626     return rc;
2627 }
2628
2629 typedef struct {
2630     CLSID       clsid;
2631     LPWSTR      source;
2632
2633     LPWSTR      path;
2634     ITypeLib    *ptLib;
2635 } typelib_struct;
2636
2637 static BOOL CALLBACK Typelib_EnumResNameProc( HMODULE hModule, LPCWSTR lpszType, 
2638                                        LPWSTR lpszName, LONG_PTR lParam)
2639 {
2640     TLIBATTR *attr;
2641     typelib_struct *tl_struct = (typelib_struct*) lParam;
2642     static const WCHAR fmt[] = {'%','s','\\','%','i',0};
2643     int sz; 
2644     HRESULT res;
2645
2646     if (!IS_INTRESOURCE(lpszName))
2647     {
2648         ERR("Not Int Resource Name %s\n",debugstr_w(lpszName));
2649         return TRUE;
2650     }
2651
2652     sz = strlenW(tl_struct->source)+4;
2653     sz *= sizeof(WCHAR);
2654
2655     if ((INT_PTR)lpszName == 1)
2656         tl_struct->path = strdupW(tl_struct->source);
2657     else
2658     {
2659         tl_struct->path = msi_alloc(sz);
2660         sprintfW(tl_struct->path,fmt,tl_struct->source, lpszName);
2661     }
2662
2663     TRACE("trying %s\n", debugstr_w(tl_struct->path));
2664     res = LoadTypeLib(tl_struct->path,&tl_struct->ptLib);
2665     if (!SUCCEEDED(res))
2666     {
2667         msi_free(tl_struct->path);
2668         tl_struct->path = NULL;
2669
2670         return TRUE;
2671     }
2672
2673     ITypeLib_GetLibAttr(tl_struct->ptLib, &attr);
2674     if (IsEqualGUID(&(tl_struct->clsid),&(attr->guid)))
2675     {
2676         ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
2677         return FALSE;
2678     }
2679
2680     msi_free(tl_struct->path);
2681     tl_struct->path = NULL;
2682
2683     ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
2684     ITypeLib_Release(tl_struct->ptLib);
2685
2686     return TRUE;
2687 }
2688
2689 static UINT ITERATE_RegisterTypeLibraries(MSIRECORD *row, LPVOID param)
2690 {
2691     MSIPACKAGE* package = (MSIPACKAGE*)param;
2692     LPCWSTR component;
2693     MSICOMPONENT *comp;
2694     MSIFILE *file;
2695     typelib_struct tl_struct;
2696     HMODULE module;
2697     static const WCHAR szTYPELIB[] = {'T','Y','P','E','L','I','B',0};
2698
2699     component = MSI_RecordGetString(row,3);
2700     comp = get_loaded_component(package,component);
2701     if (!comp)
2702         return ERROR_SUCCESS;
2703
2704     if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
2705     {
2706         TRACE("Skipping typelib reg due to disabled component\n");
2707
2708         comp->Action = comp->Installed;
2709
2710         return ERROR_SUCCESS;
2711     }
2712
2713     comp->Action = INSTALLSTATE_LOCAL;
2714
2715     file = get_loaded_file( package, comp->KeyPath ); 
2716     if (!file)
2717         return ERROR_SUCCESS;
2718
2719     module = LoadLibraryExW( file->TargetPath, NULL, LOAD_LIBRARY_AS_DATAFILE );
2720     if (module)
2721     {
2722         LPCWSTR guid;
2723         guid = MSI_RecordGetString(row,1);
2724         CLSIDFromString((LPWSTR)guid, &tl_struct.clsid);
2725         tl_struct.source = strdupW( file->TargetPath );
2726         tl_struct.path = NULL;
2727
2728         EnumResourceNamesW(module, szTYPELIB, Typelib_EnumResNameProc,
2729                         (LONG_PTR)&tl_struct);
2730
2731         if (tl_struct.path)
2732         {
2733             LPWSTR help = NULL;
2734             LPCWSTR helpid;
2735             HRESULT res;
2736
2737             helpid = MSI_RecordGetString(row,6);
2738
2739             if (helpid)
2740                 help = resolve_folder(package,helpid,FALSE,FALSE,NULL);
2741             res = RegisterTypeLib(tl_struct.ptLib,tl_struct.path,help);
2742             msi_free(help);
2743
2744             if (!SUCCEEDED(res))
2745                 ERR("Failed to register type library %s\n",
2746                         debugstr_w(tl_struct.path));
2747             else
2748             {
2749                 ui_actiondata(package,szRegisterTypeLibraries,row);
2750
2751                 TRACE("Registered %s\n", debugstr_w(tl_struct.path));
2752             }
2753
2754             ITypeLib_Release(tl_struct.ptLib);
2755             msi_free(tl_struct.path);
2756         }
2757         else
2758             ERR("Failed to load type library %s\n",
2759                     debugstr_w(tl_struct.source));
2760
2761         FreeLibrary(module);
2762         msi_free(tl_struct.source);
2763     }
2764     else
2765         ERR("Could not load file! %s\n", debugstr_w(file->TargetPath));
2766
2767     return ERROR_SUCCESS;
2768 }
2769
2770 static UINT ACTION_RegisterTypeLibraries(MSIPACKAGE *package)
2771 {
2772     /* 
2773      * OK this is a bit confusing.. I am given a _Component key and I believe
2774      * that the file that is being registered as a type library is the "key file
2775      * of that component" which I interpret to mean "The file in the KeyPath of
2776      * that component".
2777      */
2778     UINT rc;
2779     MSIQUERY * view;
2780     static const WCHAR Query[] =
2781         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2782          '`','T','y','p','e','L','i','b','`',0};
2783
2784     rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
2785     if (rc != ERROR_SUCCESS)
2786         return ERROR_SUCCESS;
2787
2788     rc = MSI_IterateRecords(view, NULL, ITERATE_RegisterTypeLibraries, package);
2789     msiobj_release(&view->hdr);
2790     return rc;
2791 }
2792
2793 static UINT ITERATE_CreateShortcuts(MSIRECORD *row, LPVOID param)
2794 {
2795     MSIPACKAGE *package = (MSIPACKAGE*)param;
2796     LPWSTR target_file, target_folder, filename;
2797     LPCWSTR buffer, extension;
2798     MSICOMPONENT *comp;
2799     static const WCHAR szlnk[]={'.','l','n','k',0};
2800     IShellLinkW *sl = NULL;
2801     IPersistFile *pf = NULL;
2802     HRESULT res;
2803
2804     buffer = MSI_RecordGetString(row,4);
2805     comp = get_loaded_component(package,buffer);
2806     if (!comp)
2807         return ERROR_SUCCESS;
2808
2809     if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL ))
2810     {
2811         TRACE("Skipping shortcut creation due to disabled component\n");
2812
2813         comp->Action = comp->Installed;
2814
2815         return ERROR_SUCCESS;
2816     }
2817
2818     comp->Action = INSTALLSTATE_LOCAL;
2819
2820     ui_actiondata(package,szCreateShortcuts,row);
2821
2822     res = CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
2823                     &IID_IShellLinkW, (LPVOID *) &sl );
2824
2825     if (FAILED( res ))
2826     {
2827         ERR("CLSID_ShellLink not available\n");
2828         goto err;
2829     }
2830
2831     res = IShellLinkW_QueryInterface( sl, &IID_IPersistFile,(LPVOID*) &pf );
2832     if (FAILED( res ))
2833     {
2834         ERR("QueryInterface(IID_IPersistFile) failed\n");
2835         goto err;
2836     }
2837
2838     buffer = MSI_RecordGetString(row,2);
2839     target_folder = resolve_folder(package, buffer,FALSE,FALSE,NULL);
2840
2841     /* may be needed because of a bug somehwere else */
2842     create_full_pathW(target_folder);
2843
2844     filename = msi_dup_record_field( row, 3 );
2845     reduce_to_longfilename(filename);
2846
2847     extension = strchrW(filename,'.');
2848     if (!extension || strcmpiW(extension,szlnk))
2849     {
2850         int len = strlenW(filename);
2851         filename = msi_realloc(filename, len * sizeof(WCHAR) + sizeof(szlnk));
2852         memcpy(filename + len, szlnk, sizeof(szlnk));
2853     }
2854     target_file = build_directory_name(2, target_folder, filename);
2855     msi_free(target_folder);
2856     msi_free(filename);
2857
2858     buffer = MSI_RecordGetString(row,5);
2859     if (strchrW(buffer,'['))
2860     {
2861         LPWSTR deformated;
2862         deformat_string(package,buffer,&deformated);
2863         IShellLinkW_SetPath(sl,deformated);
2864         msi_free(deformated);
2865     }
2866     else
2867     {
2868         FIXME("poorly handled shortcut format, advertised shortcut\n");
2869         IShellLinkW_SetPath(sl,comp->FullKeypath);
2870     }
2871
2872     if (!MSI_RecordIsNull(row,6))
2873     {
2874         LPWSTR deformated;
2875         buffer = MSI_RecordGetString(row,6);
2876         deformat_string(package,buffer,&deformated);
2877         IShellLinkW_SetArguments(sl,deformated);
2878         msi_free(deformated);
2879     }
2880
2881     if (!MSI_RecordIsNull(row,7))
2882     {
2883         buffer = MSI_RecordGetString(row,7);
2884         IShellLinkW_SetDescription(sl,buffer);
2885     }
2886
2887     if (!MSI_RecordIsNull(row,8))
2888         IShellLinkW_SetHotkey(sl,MSI_RecordGetInteger(row,8));
2889
2890     if (!MSI_RecordIsNull(row,9))
2891     {
2892         LPWSTR Path;
2893         INT index; 
2894
2895         buffer = MSI_RecordGetString(row,9);
2896
2897         Path = build_icon_path(package,buffer);
2898         index = MSI_RecordGetInteger(row,10);
2899
2900         IShellLinkW_SetIconLocation(sl,Path,index);
2901         msi_free(Path);
2902     }
2903
2904     if (!MSI_RecordIsNull(row,11))
2905         IShellLinkW_SetShowCmd(sl,MSI_RecordGetInteger(row,11));
2906
2907     if (!MSI_RecordIsNull(row,12))
2908     {
2909         LPWSTR Path;
2910         buffer = MSI_RecordGetString(row,12);
2911         Path = resolve_folder(package, buffer, FALSE, FALSE, NULL);
2912         IShellLinkW_SetWorkingDirectory(sl,Path);
2913         msi_free(Path);
2914     }
2915
2916     TRACE("Writing shortcut to %s\n",debugstr_w(target_file));
2917     IPersistFile_Save(pf,target_file,FALSE);
2918
2919     msi_free(target_file);    
2920
2921 err:
2922     if (pf)
2923         IPersistFile_Release( pf );
2924     if (sl)
2925         IShellLinkW_Release( sl );
2926
2927     return ERROR_SUCCESS;
2928 }
2929
2930 static UINT ACTION_CreateShortcuts(MSIPACKAGE *package)
2931 {
2932     UINT rc;
2933     HRESULT res;
2934     MSIQUERY * view;
2935     static const WCHAR Query[] =
2936         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2937          '`','S','h','o','r','t','c','u','t','`',0};
2938
2939     rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
2940     if (rc != ERROR_SUCCESS)
2941         return ERROR_SUCCESS;
2942
2943     res = CoInitialize( NULL );
2944     if (FAILED (res))
2945     {
2946         ERR("CoInitialize failed\n");
2947         return ERROR_FUNCTION_FAILED;
2948     }
2949
2950     rc = MSI_IterateRecords(view, NULL, ITERATE_CreateShortcuts, package);
2951     msiobj_release(&view->hdr);
2952
2953     CoUninitialize();
2954
2955     return rc;
2956 }
2957
2958 static UINT ITERATE_PublishProduct(MSIRECORD *row, LPVOID param)
2959 {
2960     MSIPACKAGE* package = (MSIPACKAGE*)param;
2961     HANDLE the_file;
2962     LPWSTR FilePath;
2963     LPCWSTR FileName;
2964     CHAR buffer[1024];
2965     DWORD sz;
2966     UINT rc;
2967     MSIRECORD *uirow;
2968
2969     FileName = MSI_RecordGetString(row,1);
2970     if (!FileName)
2971     {
2972         ERR("Unable to get FileName\n");
2973         return ERROR_SUCCESS;
2974     }
2975
2976     FilePath = build_icon_path(package,FileName);
2977
2978     TRACE("Creating icon file at %s\n",debugstr_w(FilePath));
2979
2980     the_file = CreateFileW(FilePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
2981                         FILE_ATTRIBUTE_NORMAL, NULL);
2982
2983     if (the_file == INVALID_HANDLE_VALUE)
2984     {
2985         ERR("Unable to create file %s\n",debugstr_w(FilePath));
2986         msi_free(FilePath);
2987         return ERROR_SUCCESS;
2988     }
2989
2990     do 
2991     {
2992         DWORD write;
2993         sz = 1024;
2994         rc = MSI_RecordReadStream(row,2,buffer,&sz);
2995         if (rc != ERROR_SUCCESS)
2996         {
2997             ERR("Failed to get stream\n");
2998             CloseHandle(the_file);  
2999             DeleteFileW(FilePath);
3000             break;
3001         }
3002         WriteFile(the_file,buffer,sz,&write,NULL);
3003     } while (sz == 1024);
3004
3005     msi_free(FilePath);
3006
3007     CloseHandle(the_file);
3008
3009     uirow = MSI_CreateRecord(1);
3010     MSI_RecordSetStringW(uirow,1,FileName);
3011     ui_actiondata(package,szPublishProduct,uirow);
3012     msiobj_release( &uirow->hdr );
3013
3014     return ERROR_SUCCESS;
3015 }
3016
3017 /*
3018  * 99% of the work done here is only done for 
3019  * advertised installs. However this is where the
3020  * Icon table is processed and written out
3021  * so that is what I am going to do here.
3022  */
3023 static UINT ACTION_PublishProduct(MSIPACKAGE *package)
3024 {
3025     UINT rc;
3026     MSIQUERY * view;
3027     static const WCHAR Query[]=
3028         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3029          '`','I','c','o','n','`',0};
3030     /* for registry stuff */
3031     HKEY hkey=0;
3032     HKEY hukey=0;
3033     static const WCHAR szProductLanguage[] =
3034         {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
3035     static const WCHAR szARPProductIcon[] =
3036         {'A','R','P','P','R','O','D','U','C','T','I','C','O','N',0};
3037     static const WCHAR szProductVersion[] =
3038         {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
3039     DWORD langid;
3040     LPWSTR buffer;
3041     DWORD size;
3042     MSIHANDLE hDb, hSumInfo;
3043
3044     /* write out icon files */
3045
3046     rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3047     if (rc == ERROR_SUCCESS)
3048     {
3049         MSI_IterateRecords(view, NULL, ITERATE_PublishProduct, package);
3050         msiobj_release(&view->hdr);
3051     }
3052
3053     /* ok there is a lot more done here but i need to figure out what */
3054
3055     rc = MSIREG_OpenProductsKey(package->ProductCode,&hkey,TRUE);
3056     if (rc != ERROR_SUCCESS)
3057         goto end;
3058
3059     rc = MSIREG_OpenUserProductsKey(package->ProductCode,&hukey,TRUE);
3060     if (rc != ERROR_SUCCESS)
3061         goto end;
3062
3063
3064     buffer = msi_dup_property( package, INSTALLPROPERTY_PRODUCTNAMEW );
3065     msi_reg_set_val_str( hukey, INSTALLPROPERTY_PRODUCTNAMEW, buffer );
3066     msi_free(buffer);
3067
3068     langid = msi_get_property_int( package, szProductLanguage, 0 );
3069     msi_reg_set_val_dword( hkey, INSTALLPROPERTY_LANGUAGEW, langid );
3070
3071     buffer = msi_dup_property( package, szARPProductIcon );
3072     if (buffer)
3073     {
3074         LPWSTR path = build_icon_path(package,buffer);
3075         msi_reg_set_val_str( hukey, INSTALLPROPERTY_PRODUCTICONW, path );
3076         msi_free( path );
3077     }
3078     msi_free(buffer);
3079
3080     buffer = msi_dup_property( package, szProductVersion );
3081     if (buffer)
3082     {
3083         DWORD verdword = msi_version_str_to_dword(buffer);
3084         msi_reg_set_val_dword( hkey, INSTALLPROPERTY_VERSIONW, verdword );
3085     }
3086     msi_free(buffer);
3087     
3088     /* FIXME: Need to write more keys to the user registry */
3089   
3090     hDb= alloc_msihandle( &package->db->hdr );
3091     rc = MsiGetSummaryInformationW(hDb, NULL, 0, &hSumInfo); 
3092     MsiCloseHandle(hDb);
3093     if (rc == ERROR_SUCCESS)
3094     {
3095         WCHAR guidbuffer[0x200];
3096         size = 0x200;
3097         rc = MsiSummaryInfoGetPropertyW(hSumInfo, 9, NULL, NULL, NULL,
3098                                         guidbuffer, &size);
3099         if (rc == ERROR_SUCCESS)
3100         {
3101             WCHAR squashed[GUID_SIZE];
3102             /* for now we only care about the first guid */
3103             LPWSTR ptr = strchrW(guidbuffer,';');
3104             if (ptr) *ptr = 0;
3105             squash_guid(guidbuffer,squashed);
3106             msi_reg_set_val_str( hukey, INSTALLPROPERTY_PACKAGECODEW, squashed );
3107         }
3108         else
3109         {
3110             ERR("Unable to query Revision_Number...\n");
3111             rc = ERROR_SUCCESS;
3112         }
3113         MsiCloseHandle(hSumInfo);
3114     }
3115     else
3116     {
3117         ERR("Unable to open Summary Information\n");
3118         rc = ERROR_SUCCESS;
3119     }
3120
3121 end:
3122
3123     RegCloseKey(hkey);
3124     RegCloseKey(hukey);
3125
3126     return rc;
3127 }
3128
3129 static UINT ITERATE_WriteIniValues(MSIRECORD *row, LPVOID param)
3130 {
3131     MSIPACKAGE *package = (MSIPACKAGE*)param;
3132     LPCWSTR component,section,key,value,identifier,filename,dirproperty;
3133     LPWSTR deformated_section, deformated_key, deformated_value;
3134     LPWSTR folder, fullname = NULL;
3135     MSIRECORD * uirow;
3136     INT action;
3137     MSICOMPONENT *comp;
3138     static const WCHAR szWindowsFolder[] =
3139           {'W','i','n','d','o','w','s','F','o','l','d','e','r',0};
3140
3141     component = MSI_RecordGetString(row, 8);
3142     comp = get_loaded_component(package,component);
3143
3144     if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
3145     {
3146         TRACE("Skipping ini file due to disabled component %s\n",
3147                         debugstr_w(component));
3148
3149         comp->Action = comp->Installed;
3150
3151         return ERROR_SUCCESS;
3152     }
3153
3154     comp->Action = INSTALLSTATE_LOCAL;
3155
3156     identifier = MSI_RecordGetString(row,1); 
3157     filename = MSI_RecordGetString(row,2);
3158     dirproperty = MSI_RecordGetString(row,3);
3159     section = MSI_RecordGetString(row,4);
3160     key = MSI_RecordGetString(row,5);
3161     value = MSI_RecordGetString(row,6);
3162     action = MSI_RecordGetInteger(row,7);
3163
3164     deformat_string(package,section,&deformated_section);
3165     deformat_string(package,key,&deformated_key);
3166     deformat_string(package,value,&deformated_value);
3167
3168     if (dirproperty)
3169     {
3170         folder = resolve_folder(package, dirproperty, FALSE, FALSE, NULL);
3171         if (!folder)
3172             folder = msi_dup_property( package, dirproperty );
3173     }
3174     else
3175         folder = msi_dup_property( package, szWindowsFolder );
3176
3177     if (!folder)
3178     {
3179         ERR("Unable to resolve folder! (%s)\n",debugstr_w(dirproperty));
3180         goto cleanup;
3181     }
3182
3183     fullname = build_directory_name(2, folder, filename);
3184
3185     if (action == 0)
3186     {
3187         TRACE("Adding value %s to section %s in %s\n",
3188                 debugstr_w(deformated_key), debugstr_w(deformated_section),
3189                 debugstr_w(fullname));
3190         WritePrivateProfileStringW(deformated_section, deformated_key,
3191                                    deformated_value, fullname);
3192     }
3193     else if (action == 1)
3194     {
3195         WCHAR returned[10];
3196         GetPrivateProfileStringW(deformated_section, deformated_key, NULL,
3197                                  returned, 10, fullname);
3198         if (returned[0] == 0)
3199         {
3200             TRACE("Adding value %s to section %s in %s\n",
3201                     debugstr_w(deformated_key), debugstr_w(deformated_section),
3202                     debugstr_w(fullname));
3203
3204             WritePrivateProfileStringW(deformated_section, deformated_key,
3205                                        deformated_value, fullname);
3206         }
3207     }
3208     else if (action == 3)
3209         FIXME("Append to existing section not yet implemented\n");
3210
3211     uirow = MSI_CreateRecord(4);
3212     MSI_RecordSetStringW(uirow,1,identifier);
3213     MSI_RecordSetStringW(uirow,2,deformated_section);
3214     MSI_RecordSetStringW(uirow,3,deformated_key);
3215     MSI_RecordSetStringW(uirow,4,deformated_value);
3216     ui_actiondata(package,szWriteIniValues,uirow);
3217     msiobj_release( &uirow->hdr );
3218 cleanup:
3219     msi_free(fullname);
3220     msi_free(folder);
3221     msi_free(deformated_key);
3222     msi_free(deformated_value);
3223     msi_free(deformated_section);
3224     return ERROR_SUCCESS;
3225 }
3226
3227 static UINT ACTION_WriteIniValues(MSIPACKAGE *package)
3228 {
3229     UINT rc;
3230     MSIQUERY * view;
3231     static const WCHAR ExecSeqQuery[] = 
3232         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3233          '`','I','n','i','F','i','l','e','`',0};
3234
3235     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
3236     if (rc != ERROR_SUCCESS)
3237     {
3238         TRACE("no IniFile table\n");
3239         return ERROR_SUCCESS;
3240     }
3241
3242     rc = MSI_IterateRecords(view, NULL, ITERATE_WriteIniValues, package);
3243     msiobj_release(&view->hdr);
3244     return rc;
3245 }
3246
3247 static UINT ITERATE_SelfRegModules(MSIRECORD *row, LPVOID param)
3248 {
3249     MSIPACKAGE *package = (MSIPACKAGE*)param;
3250     LPCWSTR filename;
3251     LPWSTR FullName;
3252     MSIFILE *file;
3253     DWORD len;
3254     static const WCHAR ExeStr[] =
3255         {'r','e','g','s','v','r','3','2','.','e','x','e',' ','\"',0};
3256     static const WCHAR close[] =  {'\"',0};
3257     STARTUPINFOW si;
3258     PROCESS_INFORMATION info;
3259     BOOL brc;
3260     MSIRECORD *uirow;
3261     LPWSTR uipath, p;
3262
3263     memset(&si,0,sizeof(STARTUPINFOW));
3264
3265     filename = MSI_RecordGetString(row,1);
3266     file = get_loaded_file( package, filename );
3267
3268     if (!file)
3269     {
3270         ERR("Unable to find file id %s\n",debugstr_w(filename));
3271         return ERROR_SUCCESS;
3272     }
3273
3274     len = strlenW(ExeStr) + strlenW( file->TargetPath ) + 2;
3275
3276     FullName = msi_alloc(len*sizeof(WCHAR));
3277     strcpyW(FullName,ExeStr);
3278     strcatW( FullName, file->TargetPath );
3279     strcatW(FullName,close);
3280
3281     TRACE("Registering %s\n",debugstr_w(FullName));
3282     brc = CreateProcessW(NULL, FullName, NULL, NULL, FALSE, 0, NULL, c_colon,
3283                     &si, &info);
3284
3285     if (brc)
3286         msi_dialog_check_messages(info.hProcess);
3287
3288     msi_free(FullName);
3289
3290     /* the UI chunk */
3291     uirow = MSI_CreateRecord( 2 );
3292     uipath = strdupW( file->TargetPath );
3293     p = strrchrW(uipath,'\\');
3294     if (p)
3295         p[1]=0;
3296     MSI_RecordSetStringW( uirow, 1, &p[2] );
3297     MSI_RecordSetStringW( uirow, 2, uipath);
3298     ui_actiondata( package, szSelfRegModules, uirow);
3299     msiobj_release( &uirow->hdr );
3300     msi_free( uipath );
3301     /* FIXME: call ui_progress? */
3302
3303     return ERROR_SUCCESS;
3304 }
3305
3306 static UINT ACTION_SelfRegModules(MSIPACKAGE *package)
3307 {
3308     UINT rc;
3309     MSIQUERY * view;
3310     static const WCHAR ExecSeqQuery[] = 
3311         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3312          '`','S','e','l','f','R','e','g','`',0};
3313
3314     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
3315     if (rc != ERROR_SUCCESS)
3316     {
3317         TRACE("no SelfReg table\n");
3318         return ERROR_SUCCESS;
3319     }
3320
3321     MSI_IterateRecords(view, NULL, ITERATE_SelfRegModules, package);
3322     msiobj_release(&view->hdr);
3323
3324     return ERROR_SUCCESS;
3325 }
3326
3327 static UINT ACTION_PublishFeatures(MSIPACKAGE *package)
3328 {
3329     MSIFEATURE *feature;
3330     UINT rc;
3331     HKEY hkey=0;
3332     HKEY hukey=0;
3333     
3334     rc = MSIREG_OpenFeaturesKey(package->ProductCode,&hkey,TRUE);
3335     if (rc != ERROR_SUCCESS)
3336         goto end;
3337
3338     rc = MSIREG_OpenUserFeaturesKey(package->ProductCode,&hukey,TRUE);
3339     if (rc != ERROR_SUCCESS)
3340         goto end;
3341
3342     /* here the guids are base 85 encoded */
3343     LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3344     {
3345         ComponentList *cl;
3346         LPWSTR data = NULL;
3347         GUID clsid;
3348         INT size;
3349         BOOL absent = FALSE;
3350         MSIRECORD *uirow;
3351
3352         if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_LOCAL ) &&
3353             !ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_SOURCE ) &&
3354             !ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_ADVERTISED ))
3355             absent = TRUE;
3356
3357         size = 1;
3358         LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3359         {
3360             size += 21;
3361         }
3362         if (feature->Feature_Parent)
3363             size += strlenW( feature->Feature_Parent )+2;
3364
3365         data = msi_alloc(size * sizeof(WCHAR));
3366
3367         data[0] = 0;
3368         LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3369         {
3370             MSICOMPONENT* component = cl->component;
3371             WCHAR buf[21];
3372
3373             buf[0] = 0;
3374             if (component->ComponentId)
3375             {
3376                 TRACE("From %s\n",debugstr_w(component->ComponentId));
3377                 CLSIDFromString(component->ComponentId, &clsid);
3378                 encode_base85_guid(&clsid,buf);
3379                 TRACE("to %s\n",debugstr_w(buf));
3380                 strcatW(data,buf);
3381             }
3382         }
3383         if (feature->Feature_Parent)
3384         {
3385             static const WCHAR sep[] = {'\2',0};
3386             strcatW(data,sep);
3387             strcatW(data,feature->Feature_Parent);
3388         }
3389
3390         msi_reg_set_val_str( hkey, feature->Feature, data );
3391         msi_free(data);
3392
3393         size = 0;
3394         if (feature->Feature_Parent)
3395             size = strlenW(feature->Feature_Parent)*sizeof(WCHAR);
3396         if (!absent)
3397         {
3398             RegSetValueExW(hukey,feature->Feature,0,REG_SZ,
3399                        (LPBYTE)feature->Feature_Parent,size);
3400         }
3401         else
3402         {
3403             size += 2*sizeof(WCHAR);
3404             data = msi_alloc(size);
3405             data[0] = 0x6;
3406             data[1] = 0;
3407             if (feature->Feature_Parent)
3408                 strcpyW( &data[1], feature->Feature_Parent );
3409             RegSetValueExW(hukey,feature->Feature,0,REG_SZ,
3410                        (LPBYTE)data,size);
3411             msi_free(data);
3412         }
3413
3414         /* the UI chunk */
3415         uirow = MSI_CreateRecord( 1 );
3416         MSI_RecordSetStringW( uirow, 1, feature->Feature );
3417         ui_actiondata( package, szPublishFeatures, uirow);
3418         msiobj_release( &uirow->hdr );
3419         /* FIXME: call ui_progress? */
3420     }
3421
3422 end:
3423     RegCloseKey(hkey);
3424     RegCloseKey(hukey);
3425     return rc;
3426 }
3427
3428 static UINT msi_make_package_local( MSIPACKAGE *package, HKEY hkey )
3429 {
3430     static const WCHAR installerPathFmt[] = {
3431         '%','s','\\','I','n','s','t','a','l','l','e','r','\\',0};
3432     static const WCHAR fmt[] = {
3433         '%','s','\\',
3434         'I','n','s','t','a','l','l','e','r','\\',
3435         '%','x','.','m','s','i',0};
3436     static const WCHAR szOriginalDatabase[] =
3437         {'O','r','i','g','i','n','a','l','D','a','t','a','b','a','s','e',0};
3438     WCHAR windir[MAX_PATH], path[MAX_PATH], packagefile[MAX_PATH];
3439     INT num, start;
3440     LPWSTR msiFilePath;
3441     BOOL r;
3442
3443     /* copy the package locally */
3444     num = GetTickCount() & 0xffff;
3445     if (!num) 
3446         num = 1;
3447     start = num;
3448     GetWindowsDirectoryW( windir, MAX_PATH );
3449     snprintfW( packagefile, MAX_PATH, fmt, windir, num );
3450     do 
3451     {
3452         HANDLE handle = CreateFileW(packagefile,GENERIC_WRITE, 0, NULL,
3453                                   CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0 );
3454         if (handle != INVALID_HANDLE_VALUE)
3455         {
3456             CloseHandle(handle);
3457             break;
3458         }
3459         if (GetLastError() != ERROR_FILE_EXISTS &&
3460             GetLastError() != ERROR_SHARING_VIOLATION)
3461             break;
3462         if (!(++num & 0xffff)) num = 1;
3463         sprintfW(packagefile,fmt,num);
3464     } while (num != start);
3465
3466     snprintfW( path, MAX_PATH, installerPathFmt, windir );
3467     create_full_pathW(path);
3468
3469     TRACE("Copying to local package %s\n",debugstr_w(packagefile));
3470
3471     msiFilePath = msi_dup_property( package, szOriginalDatabase );
3472     r = CopyFileW( msiFilePath, packagefile, FALSE);
3473     msi_free( msiFilePath );
3474
3475     if (!r)
3476     {
3477         ERR("Unable to copy package (%s -> %s) (error %ld)\n",
3478             debugstr_w(msiFilePath), debugstr_w(packagefile), GetLastError());
3479         return ERROR_FUNCTION_FAILED;
3480     }
3481
3482     /* FIXME: maybe set this key in ACTION_RegisterProduct instead */
3483     msi_reg_set_val_str( hkey, INSTALLPROPERTY_LOCALPACKAGEW, packagefile );
3484     return ERROR_SUCCESS;
3485 }
3486
3487 static UINT msi_write_uninstall_property_vals( MSIPACKAGE *package, HKEY hkey )
3488 {
3489     LPWSTR prop, val, key;
3490     static const LPCSTR propval[] = {
3491         "ARPAUTHORIZEDCDFPREFIX", "AuthorizedCDFPrefix",
3492         "ARPCONTACT",             "Contact",
3493         "ARPCOMMENTS",            "Comments",
3494         "ProductName",            "DisplayName",
3495         "ProductVersion",         "DisplayVersion",
3496         "ARPHELPLINK",            "HelpLink",
3497         "ARPHELPTELEPHONE",       "HelpTelephone",
3498         "ARPINSTALLLOCATION",     "InstallLocation",
3499         "SourceDir",              "InstallSource",
3500         "Manufacturer",           "Publisher",
3501         "ARPREADME",              "Readme",
3502         "ARPSIZE",                "Size",
3503         "ARPURLINFOABOUT",        "URLInfoAbout",
3504         "ARPURLUPDATEINFO",       "URLUpdateInfo",
3505         NULL,
3506     };
3507     const LPCSTR *p = propval;
3508
3509     while( *p )
3510     {
3511         prop = strdupAtoW( *p++ );
3512         key = strdupAtoW( *p++ );
3513         val = msi_dup_property( package, prop );
3514         msi_reg_set_val_str( hkey, key, val );
3515         msi_free(val);
3516         msi_free(key);
3517         msi_free(prop);
3518     }
3519     return ERROR_SUCCESS;
3520 }
3521
3522 static UINT ACTION_RegisterProduct(MSIPACKAGE *package)
3523 {
3524     HKEY hkey=0;
3525     LPWSTR buffer = NULL;
3526     UINT rc;
3527     DWORD size, langid;
3528     static const WCHAR szWindowsInstaller[] = 
3529         {'W','i','n','d','o','w','s','I','n','s','t','a','l','l','e','r',0};
3530     static const WCHAR szUpgradeCode[] = 
3531         {'U','p','g','r','a','d','e','C','o','d','e',0};
3532     static const WCHAR modpath_fmt[] = 
3533         {'M','s','i','E','x','e','c','.','e','x','e',' ',
3534          '/','I','[','P','r','o','d','u','c','t','C','o','d','e',']',0};
3535     static const WCHAR szModifyPath[] = 
3536         {'M','o','d','i','f','y','P','a','t','h',0};
3537     static const WCHAR szUninstallString[] = 
3538         {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0};
3539     static const WCHAR szEstimatedSize[] = 
3540         {'E','s','t','i','m','a','t','e','d','S','i','z','e',0};
3541     static const WCHAR szProductLanguage[] =
3542         {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
3543     static const WCHAR szProductVersion[] =
3544         {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
3545
3546     SYSTEMTIME systime;
3547     static const WCHAR date_fmt[] = {'%','i','%','i','%','i',0};
3548     LPWSTR upgrade_code;
3549     WCHAR szDate[9]; 
3550
3551     rc = MSIREG_OpenUninstallKey(package->ProductCode,&hkey,TRUE);
3552     if (rc != ERROR_SUCCESS)
3553         return rc;
3554
3555     /* dump all the info i can grab */
3556     /* FIXME: Flesh out more information */
3557
3558     msi_write_uninstall_property_vals( package, hkey );
3559
3560     msi_reg_set_val_dword( hkey, szWindowsInstaller, 1 );
3561     
3562     msi_make_package_local( package, hkey );
3563
3564     /* do ModifyPath and UninstallString */
3565     size = deformat_string(package,modpath_fmt,&buffer);
3566     RegSetValueExW(hkey,szModifyPath,0,REG_EXPAND_SZ,(LPBYTE)buffer,size);
3567     RegSetValueExW(hkey,szUninstallString,0,REG_EXPAND_SZ,(LPBYTE)buffer,size);
3568     msi_free(buffer);
3569
3570     /* FIXME: Write real Estimated Size when we have it */
3571     msi_reg_set_val_dword( hkey, szEstimatedSize, 0 );
3572    
3573     GetLocalTime(&systime);
3574     sprintfW(szDate,date_fmt,systime.wYear,systime.wMonth,systime.wDay);
3575     msi_reg_set_val_str( hkey, INSTALLPROPERTY_INSTALLDATEW, szDate );
3576    
3577     langid = msi_get_property_int( package, szProductLanguage, 0 );
3578     msi_reg_set_val_dword( hkey, INSTALLPROPERTY_LANGUAGEW, langid );
3579
3580     buffer = msi_dup_property( package, szProductVersion );
3581     if (buffer)
3582     {
3583         DWORD verdword = msi_version_str_to_dword(buffer);
3584
3585         msi_reg_set_val_dword( hkey, INSTALLPROPERTY_VERSIONW, verdword );
3586         msi_reg_set_val_dword( hkey, INSTALLPROPERTY_VERSIONMAJORW, verdword>>24 );
3587         msi_reg_set_val_dword( hkey, INSTALLPROPERTY_VERSIONMINORW, (verdword>>16)&0x00FF );
3588     }
3589     msi_free(buffer);
3590     
3591     /* Handle Upgrade Codes */
3592     upgrade_code = msi_dup_property( package, szUpgradeCode );
3593     if (upgrade_code)
3594     {
3595         HKEY hkey2;
3596         WCHAR squashed[33];
3597         MSIREG_OpenUpgradeCodesKey(upgrade_code, &hkey2, TRUE);
3598         squash_guid(package->ProductCode,squashed);
3599         msi_reg_set_val_str( hkey2, squashed, NULL );
3600         RegCloseKey(hkey2);
3601         MSIREG_OpenUserUpgradeCodesKey(upgrade_code, &hkey2, TRUE);
3602         squash_guid(package->ProductCode,squashed);
3603         msi_reg_set_val_str( hkey2, squashed, NULL );
3604         RegCloseKey(hkey2);
3605
3606         msi_free(upgrade_code);
3607     }
3608     
3609     RegCloseKey(hkey);
3610
3611     /* FIXME: call ui_actiondata */
3612
3613     return ERROR_SUCCESS;
3614 }
3615
3616 static UINT ACTION_InstallExecute(MSIPACKAGE *package)
3617 {
3618     return execute_script(package,INSTALL_SCRIPT);
3619 }
3620
3621 static UINT ACTION_InstallFinalize(MSIPACKAGE *package)
3622 {
3623     UINT rc;
3624
3625     /* turn off scheduleing */
3626     package->script->CurrentlyScripting= FALSE;
3627
3628     /* first do the same as an InstallExecute */
3629     rc = ACTION_InstallExecute(package);
3630     if (rc != ERROR_SUCCESS)
3631         return rc;
3632
3633     /* then handle Commit Actions */
3634     rc = execute_script(package,COMMIT_SCRIPT);
3635
3636     return rc;
3637 }
3638
3639 static UINT ACTION_ForceReboot(MSIPACKAGE *package)
3640 {
3641     static const WCHAR RunOnce[] = {
3642     'S','o','f','t','w','a','r','e','\\',
3643     'M','i','c','r','o','s','o','f','t','\\',
3644     'W','i','n','d','o','w','s','\\',
3645     'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
3646     'R','u','n','O','n','c','e',0};
3647     static const WCHAR InstallRunOnce[] = {
3648     'S','o','f','t','w','a','r','e','\\',
3649     'M','i','c','r','o','s','o','f','t','\\',
3650     'W','i','n','d','o','w','s','\\',
3651     'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
3652     'I','n','s','t','a','l','l','e','r','\\',
3653     'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
3654
3655     static const WCHAR msiexec_fmt[] = {
3656     '%','s',
3657     '\\','M','s','i','E','x','e','c','.','e','x','e',' ','/','@',' ',
3658     '\"','%','s','\"',0};
3659     static const WCHAR install_fmt[] = {
3660     '/','I',' ','\"','%','s','\"',' ',
3661     'A','F','T','E','R','R','E','B','O','O','T','=','1',' ',
3662     'R','U','N','O','N','C','E','E','N','T','R','Y','=','\"','%','s','\"',0};
3663     WCHAR buffer[256], sysdir[MAX_PATH];
3664     HKEY hkey;
3665     WCHAR squished_pc[100];
3666
3667     squash_guid(package->ProductCode,squished_pc);
3668
3669     GetSystemDirectoryW(sysdir, sizeof(sysdir)/sizeof(sysdir[0]));
3670     RegCreateKeyW(HKEY_LOCAL_MACHINE,RunOnce,&hkey);
3671     snprintfW(buffer,sizeof(buffer)/sizeof(buffer[0]),msiexec_fmt,sysdir,
3672      squished_pc);
3673
3674     msi_reg_set_val_str( hkey, squished_pc, buffer );
3675     RegCloseKey(hkey);
3676
3677     TRACE("Reboot command %s\n",debugstr_w(buffer));
3678
3679     RegCreateKeyW(HKEY_LOCAL_MACHINE,InstallRunOnce,&hkey);
3680     sprintfW(buffer,install_fmt,package->ProductCode,squished_pc);
3681
3682     msi_reg_set_val_str( hkey, squished_pc, buffer );
3683     RegCloseKey(hkey);
3684
3685     return ERROR_INSTALL_SUSPEND;
3686 }
3687
3688 UINT ACTION_ResolveSource(MSIPACKAGE* package)
3689 {
3690     DWORD attrib;
3691     UINT rc;
3692     /*
3693      * we are currently doing what should be done here in the top level Install
3694      * however for Adminastrative and uninstalls this step will be needed
3695      */
3696     if (!package->PackagePath)
3697         return ERROR_SUCCESS;
3698
3699     attrib = GetFileAttributesW(package->PackagePath);
3700     if (attrib == INVALID_FILE_ATTRIBUTES)
3701     {
3702         LPWSTR prompt;
3703         LPWSTR msg;
3704         DWORD size = 0;
3705
3706         rc = MsiSourceListGetInfoW(package->ProductCode, NULL, 
3707                 MSIINSTALLCONTEXT_USERMANAGED, MSICODE_PRODUCT,
3708                 INSTALLPROPERTY_DISKPROMPTW,NULL,&size);
3709         if (rc == ERROR_MORE_DATA)
3710         {
3711             prompt = msi_alloc(size * sizeof(WCHAR));
3712             MsiSourceListGetInfoW(package->ProductCode, NULL, 
3713                     MSIINSTALLCONTEXT_USERMANAGED, MSICODE_PRODUCT,
3714                     INSTALLPROPERTY_DISKPROMPTW,prompt,&size);
3715         }
3716         else
3717             prompt = strdupW(package->PackagePath);
3718
3719         msg = generate_error_string(package,1302,1,prompt);
3720         while(attrib == INVALID_FILE_ATTRIBUTES)
3721         {
3722             rc = MessageBoxW(NULL,msg,NULL,MB_OKCANCEL);
3723             if (rc == IDCANCEL)
3724             {
3725                 rc = ERROR_INSTALL_USEREXIT;
3726                 break;
3727             }
3728             attrib = GetFileAttributesW(package->PackagePath);
3729         }
3730         msi_free(prompt);
3731         rc = ERROR_SUCCESS;
3732     }
3733     else
3734         return ERROR_SUCCESS;
3735
3736     return rc;
3737 }
3738
3739 static UINT ACTION_RegisterUser(MSIPACKAGE *package)
3740 {
3741     HKEY hkey=0;
3742     LPWSTR buffer;
3743     LPWSTR productid;
3744     UINT rc,i;
3745
3746     static const WCHAR szPropKeys[][80] = 
3747     {
3748         {'P','r','o','d','u','c','t','I','D',0},
3749         {'U','S','E','R','N','A','M','E',0},
3750         {'C','O','M','P','A','N','Y','N','A','M','E',0},
3751         {0},
3752     };
3753
3754     static const WCHAR szRegKeys[][80] = 
3755     {
3756         {'P','r','o','d','u','c','t','I','D',0},
3757         {'R','e','g','O','w','n','e','r',0},
3758         {'R','e','g','C','o','m','p','a','n','y',0},
3759         {0},
3760     };
3761
3762     productid = msi_dup_property( package, INSTALLPROPERTY_PRODUCTIDW );
3763     if (!productid)
3764         return ERROR_SUCCESS;
3765
3766     rc = MSIREG_OpenUninstallKey(package->ProductCode,&hkey,TRUE);
3767     if (rc != ERROR_SUCCESS)
3768         goto end;
3769
3770     for( i = 0; szPropKeys[i][0]; i++ )
3771     {
3772         buffer = msi_dup_property( package, szPropKeys[i] );
3773         msi_reg_set_val_str( hkey, szRegKeys[i], buffer );
3774         msi_free( buffer );
3775     }
3776
3777 end:
3778     msi_free(productid);
3779     RegCloseKey(hkey);
3780
3781     /* FIXME: call ui_actiondata */
3782
3783     return ERROR_SUCCESS;
3784 }
3785
3786
3787 static UINT ACTION_ExecuteAction(MSIPACKAGE *package)
3788 {
3789     UINT rc;
3790
3791     package->script->InWhatSequence |= SEQUENCE_EXEC;
3792     rc = ACTION_ProcessExecSequence(package,FALSE);
3793     return rc;
3794 }
3795
3796
3797 /*
3798  * Code based off of code located here
3799  * http://www.codeproject.com/gdi/fontnamefromfile.asp
3800  *
3801  * Using string index 4 (full font name) instead of 1 (family name)
3802  */
3803 static LPWSTR load_ttfname_from(LPCWSTR filename)
3804 {
3805     HANDLE handle;
3806     LPWSTR ret = NULL;
3807     int i;
3808
3809     typedef struct _tagTT_OFFSET_TABLE{
3810         USHORT uMajorVersion;
3811         USHORT uMinorVersion;
3812         USHORT uNumOfTables;
3813         USHORT uSearchRange;
3814         USHORT uEntrySelector;
3815         USHORT uRangeShift;
3816     }TT_OFFSET_TABLE;
3817
3818     typedef struct _tagTT_TABLE_DIRECTORY{
3819         char szTag[4]; /* table name */
3820         ULONG uCheckSum; /* Check sum */
3821         ULONG uOffset; /* Offset from beginning of file */
3822         ULONG uLength; /* length of the table in bytes */
3823     }TT_TABLE_DIRECTORY;
3824
3825     typedef struct _tagTT_NAME_TABLE_HEADER{
3826     USHORT uFSelector; /* format selector. Always 0 */
3827     USHORT uNRCount; /* Name Records count */
3828     USHORT uStorageOffset; /* Offset for strings storage, 
3829                             * from start of the table */
3830     }TT_NAME_TABLE_HEADER;
3831    
3832     typedef struct _tagTT_NAME_RECORD{
3833         USHORT uPlatformID;
3834         USHORT uEncodingID;
3835         USHORT uLanguageID;
3836         USHORT uNameID;
3837         USHORT uStringLength;
3838         USHORT uStringOffset; /* from start of storage area */
3839     }TT_NAME_RECORD;
3840
3841 #define SWAPWORD(x) MAKEWORD(HIBYTE(x), LOBYTE(x))
3842 #define SWAPLONG(x) MAKELONG(SWAPWORD(HIWORD(x)), SWAPWORD(LOWORD(x)))
3843
3844     handle = CreateFileW(filename ,GENERIC_READ, 0, NULL, OPEN_EXISTING,
3845                     FILE_ATTRIBUTE_NORMAL, 0 );
3846     if (handle != INVALID_HANDLE_VALUE)
3847     {
3848         TT_TABLE_DIRECTORY tblDir;
3849         BOOL bFound = FALSE;
3850         TT_OFFSET_TABLE ttOffsetTable;
3851         DWORD dwRead;
3852
3853         ReadFile(handle,&ttOffsetTable, sizeof(TT_OFFSET_TABLE),&dwRead,NULL);
3854         ttOffsetTable.uNumOfTables = SWAPWORD(ttOffsetTable.uNumOfTables);
3855         ttOffsetTable.uMajorVersion = SWAPWORD(ttOffsetTable.uMajorVersion);
3856         ttOffsetTable.uMinorVersion = SWAPWORD(ttOffsetTable.uMinorVersion);
3857         
3858         if (ttOffsetTable.uMajorVersion != 1 || 
3859                         ttOffsetTable.uMinorVersion != 0)
3860             return NULL;
3861
3862         for (i=0; i< ttOffsetTable.uNumOfTables; i++)
3863         {
3864             ReadFile(handle,&tblDir, sizeof(TT_TABLE_DIRECTORY),&dwRead,NULL);
3865             if (strncmp(tblDir.szTag,"name",4)==0)
3866             {
3867                 bFound = TRUE;
3868                 tblDir.uLength = SWAPLONG(tblDir.uLength);
3869                 tblDir.uOffset = SWAPLONG(tblDir.uOffset);
3870                 break;
3871             }
3872         }
3873
3874         if (bFound)
3875         {
3876             TT_NAME_TABLE_HEADER ttNTHeader;
3877             TT_NAME_RECORD ttRecord;
3878
3879             SetFilePointer(handle, tblDir.uOffset, NULL, FILE_BEGIN);
3880             ReadFile(handle,&ttNTHeader, sizeof(TT_NAME_TABLE_HEADER),
3881                             &dwRead,NULL);
3882
3883             ttNTHeader.uNRCount = SWAPWORD(ttNTHeader.uNRCount);
3884             ttNTHeader.uStorageOffset = SWAPWORD(ttNTHeader.uStorageOffset);
3885             bFound = FALSE;
3886             for(i=0; i<ttNTHeader.uNRCount; i++)
3887             {
3888                 ReadFile(handle,&ttRecord, sizeof(TT_NAME_RECORD),&dwRead,NULL);
3889                 ttRecord.uNameID = SWAPWORD(ttRecord.uNameID);
3890                 /* 4 is the Full Font Name */
3891                 if(ttRecord.uNameID == 4)
3892                 {
3893                     int nPos;
3894                     LPSTR buf;
3895                     static LPCSTR tt = " (TrueType)";
3896
3897                     ttRecord.uStringLength = SWAPWORD(ttRecord.uStringLength);
3898                     ttRecord.uStringOffset = SWAPWORD(ttRecord.uStringOffset);
3899                     nPos = SetFilePointer(handle, 0, NULL, FILE_CURRENT);
3900                     SetFilePointer(handle, tblDir.uOffset + 
3901                                     ttRecord.uStringOffset + 
3902                                     ttNTHeader.uStorageOffset,
3903                                     NULL, FILE_BEGIN);
3904                     buf = msi_alloc_zero( ttRecord.uStringLength + 1 + strlen(tt) );
3905                     ReadFile(handle, buf, ttRecord.uStringLength, &dwRead, NULL);
3906                     if (strlen(buf) > 0)
3907                     {
3908                         strcat(buf,tt);
3909                         ret = strdupAtoW(buf);
3910                         msi_free(buf);
3911                         break;
3912                     }
3913
3914                     msi_free(buf);
3915                     SetFilePointer(handle,nPos, NULL, FILE_BEGIN);
3916                 }
3917             }
3918         }
3919         CloseHandle(handle);
3920     }
3921     else
3922         ERR("Unable to open font file %s\n", debugstr_w(filename));
3923
3924     TRACE("Returning fontname %s\n",debugstr_w(ret));
3925     return ret;
3926 }
3927
3928 static UINT ITERATE_RegisterFonts(MSIRECORD *row, LPVOID param)
3929 {
3930     MSIPACKAGE *package = (MSIPACKAGE*)param;
3931     LPWSTR name;
3932     LPCWSTR filename;
3933     MSIFILE *file;
3934     static const WCHAR regfont1[] =
3935         {'S','o','f','t','w','a','r','e','\\',
3936          'M','i','c','r','o','s','o','f','t','\\',
3937          'W','i','n','d','o','w','s',' ','N','T','\\',
3938          'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
3939          'F','o','n','t','s',0};
3940     static const WCHAR regfont2[] =
3941         {'S','o','f','t','w','a','r','e','\\',
3942          'M','i','c','r','o','s','o','f','t','\\',
3943          'W','i','n','d','o','w','s','\\',
3944          'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
3945          'F','o','n','t','s',0};
3946     HKEY hkey1;
3947     HKEY hkey2;
3948     MSIRECORD *uirow;
3949     LPWSTR uipath, p;
3950
3951     filename = MSI_RecordGetString( row, 1 );
3952     file = get_loaded_file( package, filename );
3953     if (!file)
3954     {
3955         ERR("Unable to load file\n");
3956         return ERROR_SUCCESS;
3957     }
3958
3959     /* check to make sure that component is installed */
3960     if (!ACTION_VerifyComponentForAction( file->Component, INSTALLSTATE_LOCAL))
3961     {
3962         TRACE("Skipping: Component not scheduled for install\n");
3963         return ERROR_SUCCESS;
3964     }
3965
3966     RegCreateKeyW(HKEY_LOCAL_MACHINE,regfont1,&hkey1);
3967     RegCreateKeyW(HKEY_LOCAL_MACHINE,regfont2,&hkey2);
3968
3969     if (MSI_RecordIsNull(row,2))
3970         name = load_ttfname_from( file->TargetPath );
3971     else
3972         name = msi_dup_record_field(row,2);
3973
3974     if (name)
3975     {
3976         msi_reg_set_val_str( hkey1, name, file->FileName );
3977         msi_reg_set_val_str( hkey2, name, file->FileName );
3978     }
3979
3980     msi_free(name);
3981     RegCloseKey(hkey1);
3982     RegCloseKey(hkey2);
3983
3984     /* the UI chunk */
3985     uirow = MSI_CreateRecord( 1 );
3986     uipath = strdupW( file->TargetPath );
3987     p = strrchrW(uipath,'\\');
3988     if (p) p++;
3989     else p = uipath;
3990     MSI_RecordSetStringW( uirow, 1, p );
3991     ui_actiondata( package, szRegisterFonts, uirow);
3992     msiobj_release( &uirow->hdr );
3993     msi_free( uipath );
3994     /* FIXME: call ui_progress? */
3995
3996     return ERROR_SUCCESS;
3997 }
3998
3999 static UINT ACTION_RegisterFonts(MSIPACKAGE *package)
4000 {
4001     UINT rc;
4002     MSIQUERY * view;
4003     static const WCHAR ExecSeqQuery[] =
4004         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4005          '`','F','o','n','t','`',0};
4006
4007     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4008     if (rc != ERROR_SUCCESS)
4009     {
4010         TRACE("MSI_DatabaseOpenViewW failed: %d\n", rc);
4011         return ERROR_SUCCESS;
4012     }
4013
4014     MSI_IterateRecords(view, NULL, ITERATE_RegisterFonts, package);
4015     msiobj_release(&view->hdr);
4016
4017     return ERROR_SUCCESS;
4018 }
4019
4020 static UINT ITERATE_PublishComponent(MSIRECORD *rec, LPVOID param)
4021 {
4022     MSIPACKAGE *package = (MSIPACKAGE*)param;
4023     LPCWSTR compgroupid=NULL;
4024     LPCWSTR feature=NULL;
4025     LPCWSTR text = NULL;
4026     LPCWSTR qualifier = NULL;
4027     LPCWSTR component = NULL;
4028     LPWSTR advertise = NULL;
4029     LPWSTR output = NULL;
4030     HKEY hkey;
4031     UINT rc = ERROR_SUCCESS;
4032     MSICOMPONENT *comp;
4033     DWORD sz = 0;
4034     MSIRECORD *uirow;
4035
4036     component = MSI_RecordGetString(rec,3);
4037     comp = get_loaded_component(package,component);
4038
4039     if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL ) && 
4040        !ACTION_VerifyComponentForAction( comp, INSTALLSTATE_SOURCE ) &&
4041        !ACTION_VerifyComponentForAction( comp, INSTALLSTATE_ADVERTISED ))
4042     {
4043         TRACE("Skipping: Component %s not scheduled for install\n",
4044                         debugstr_w(component));
4045
4046         return ERROR_SUCCESS;
4047     }
4048
4049     compgroupid = MSI_RecordGetString(rec,1);
4050     qualifier = MSI_RecordGetString(rec,2);
4051
4052     rc = MSIREG_OpenUserComponentsKey(compgroupid, &hkey, TRUE);
4053     if (rc != ERROR_SUCCESS)
4054         goto end;
4055     
4056     text = MSI_RecordGetString(rec,4);
4057     feature = MSI_RecordGetString(rec,5);
4058   
4059     advertise = create_component_advertise_string(package, comp, feature);
4060
4061     sz = strlenW(advertise);
4062
4063     if (text)
4064         sz += lstrlenW(text);
4065
4066     sz+=3;
4067     sz *= sizeof(WCHAR);
4068            
4069     output = msi_alloc_zero(sz);
4070     strcpyW(output,advertise);
4071     msi_free(advertise);
4072
4073     if (text)
4074         strcatW(output,text);
4075
4076     msi_reg_set_val_multi_str( hkey, qualifier, output );
4077     
4078 end:
4079     RegCloseKey(hkey);
4080     msi_free(output);
4081
4082     /* the UI chunk */
4083     uirow = MSI_CreateRecord( 2 );
4084     MSI_RecordSetStringW( uirow, 1, compgroupid );
4085     MSI_RecordSetStringW( uirow, 2, qualifier);
4086     ui_actiondata( package, szPublishComponents, uirow);
4087     msiobj_release( &uirow->hdr );
4088     /* FIXME: call ui_progress? */
4089
4090     return rc;
4091 }
4092
4093 /*
4094  * At present I am ignorning the advertised components part of this and only
4095  * focusing on the qualified component sets
4096  */
4097 static UINT ACTION_PublishComponents(MSIPACKAGE *package)
4098 {
4099     UINT rc;
4100     MSIQUERY * view;
4101     static const WCHAR ExecSeqQuery[] =
4102         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4103          '`','P','u','b','l','i','s','h',
4104          'C','o','m','p','o','n','e','n','t','`',0};
4105     
4106     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4107     if (rc != ERROR_SUCCESS)
4108         return ERROR_SUCCESS;
4109
4110     rc = MSI_IterateRecords(view, NULL, ITERATE_PublishComponent, package);
4111     msiobj_release(&view->hdr);
4112
4113     return rc;
4114 }
4115
4116 static UINT msi_unimplemented_action_stub( MSIPACKAGE *package,
4117                                            LPCSTR action, LPCWSTR table )
4118 {
4119     static const WCHAR query[] = {
4120         'S','E','L','E','C','T',' ','*',' ',
4121         'F','R','O','M',' ','`','%','s','`',0 };
4122     MSIQUERY *view = NULL;
4123     DWORD count = 0;
4124     UINT r;
4125     
4126     r = MSI_OpenQuery( package->db, &view, query, table );
4127     if (r == ERROR_SUCCESS)
4128     {
4129         r = MSI_IterateRecords(view, &count, NULL, package);
4130         msiobj_release(&view->hdr);
4131     }
4132
4133     if (count)
4134         FIXME("%s -> %lu ignored %s table values\n",
4135               action, count, debugstr_w(table));
4136
4137     return ERROR_SUCCESS;
4138 }
4139
4140 static UINT ACTION_AllocateRegistrySpace( MSIPACKAGE *package )
4141 {
4142     TRACE("%p\n", package);
4143     return ERROR_SUCCESS;
4144 }
4145
4146 static UINT ACTION_RemoveIniValues( MSIPACKAGE *package )
4147 {
4148     static const WCHAR table[] =
4149          {'R','e','m','o','v','e','I','n','i','F','i','l','e',0 };
4150     return msi_unimplemented_action_stub( package, "RemoveIniValues", table );
4151 }
4152
4153 static UINT ACTION_MoveFiles( MSIPACKAGE *package )
4154 {
4155     static const WCHAR table[] = { 'M','o','v','e','F','i','l','e',0 };
4156     return msi_unimplemented_action_stub( package, "MoveFiles", table );
4157 }
4158
4159 static UINT ACTION_PatchFiles( MSIPACKAGE *package )
4160 {
4161     static const WCHAR table[] = { 'P','a','t','c','h',0 };
4162     return msi_unimplemented_action_stub( package, "PatchFiles", table );
4163 }
4164
4165 static UINT ACTION_BindImage( MSIPACKAGE *package )
4166 {
4167     static const WCHAR table[] = { 'B','i','n','d','I','m','a','g','e',0 };
4168     return msi_unimplemented_action_stub( package, "BindImage", table );
4169 }
4170
4171 static UINT ACTION_IsolateComponents( MSIPACKAGE *package )
4172 {
4173     static const WCHAR table[] = {
4174         'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t',0 };
4175     return msi_unimplemented_action_stub( package, "IsolateComponents", table );
4176 }
4177
4178 static UINT ACTION_MigrateFeatureStates( MSIPACKAGE *package )
4179 {
4180     static const WCHAR table[] = { 'U','p','g','r','a','d','e',0 };
4181     return msi_unimplemented_action_stub( package, "MigrateFeatureStates", table );
4182 }
4183
4184 static UINT ACTION_SelfUnregModules( MSIPACKAGE *package )
4185 {
4186     static const WCHAR table[] = { 'S','e','l','f','R','e','g',0 };
4187     return msi_unimplemented_action_stub( package, "SelfUnregModules", table );
4188 }
4189
4190 static UINT ACTION_InstallServices( MSIPACKAGE *package )
4191 {
4192     static const WCHAR table[] = {
4193         'S','e','r','v','i','c','e','I','n','s','t','a','l','l',0 };
4194     return msi_unimplemented_action_stub( package, "InstallServices", table );
4195 }
4196
4197 static UINT ACTION_StartServices( MSIPACKAGE *package )
4198 {
4199     static const WCHAR table[] = {
4200         'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
4201     return msi_unimplemented_action_stub( package, "StartServices", table );
4202 }
4203
4204 static UINT ACTION_StopServices( 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, "StopServices", table );
4209 }
4210
4211 static UINT ACTION_DeleteServices( 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, "DeleteServices", table );
4216 }
4217
4218 static UINT ACTION_WriteEnvironmentStrings( MSIPACKAGE *package )
4219 {
4220     static const WCHAR table[] = {
4221         'E','n','v','i','r','o','n','m','e','n','t',0 };
4222     return msi_unimplemented_action_stub( package, "WriteEnvironmentStrings", table );
4223 }
4224
4225 static UINT ACTION_RemoveEnvironmentStrings( 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, "RemoveEnvironmentStrings", table );
4230 }
4231
4232 static UINT ACTION_MsiPublishAssemblies( MSIPACKAGE *package )
4233 {
4234     static const WCHAR table[] = {
4235         'M','s','i','A','s','s','e','m','b','l','y',0 };
4236     return msi_unimplemented_action_stub( package, "MsiPublishAssemblies", table );
4237 }
4238
4239 static UINT ACTION_MsiUnpublishAssemblies( 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, "MsiUnpublishAssemblies", table );
4244 }
4245
4246 static UINT ACTION_UnregisterFonts( MSIPACKAGE *package )
4247 {
4248     static const WCHAR table[] = { 'F','o','n','t',0 };
4249     return msi_unimplemented_action_stub( package, "UnregisterFonts", table );
4250 }
4251
4252 static UINT ACTION_CCPSearch( MSIPACKAGE *package )
4253 {
4254     static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
4255     return msi_unimplemented_action_stub( package, "CCPSearch", table );
4256 }
4257
4258 static UINT ACTION_RMCCPSearch( MSIPACKAGE *package )
4259 {
4260     static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
4261     return msi_unimplemented_action_stub( package, "RMCCPSearch", table );
4262 }
4263
4264 static UINT ACTION_RegisterComPlus( MSIPACKAGE *package )
4265 {
4266     static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
4267     return msi_unimplemented_action_stub( package, "RegisterComPlus", table );
4268 }
4269
4270 static UINT ACTION_UnregisterComPlus( MSIPACKAGE *package )
4271 {
4272     static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
4273     return msi_unimplemented_action_stub( package, "UnregisterComPlus", table );
4274 }
4275
4276 static struct _actions StandardActions[] = {
4277     { szAllocateRegistrySpace, ACTION_AllocateRegistrySpace },
4278     { szAppSearch, ACTION_AppSearch },
4279     { szBindImage, ACTION_BindImage },
4280     { szCCPSearch, ACTION_CCPSearch},
4281     { szCostFinalize, ACTION_CostFinalize },
4282     { szCostInitialize, ACTION_CostInitialize },
4283     { szCreateFolders, ACTION_CreateFolders },
4284     { szCreateShortcuts, ACTION_CreateShortcuts },
4285     { szDeleteServices, ACTION_DeleteServices },
4286     { szDisableRollback, NULL},
4287     { szDuplicateFiles, ACTION_DuplicateFiles },
4288     { szExecuteAction, ACTION_ExecuteAction },
4289     { szFileCost, ACTION_FileCost },
4290     { szFindRelatedProducts, ACTION_FindRelatedProducts },
4291     { szForceReboot, ACTION_ForceReboot },
4292     { szInstallAdminPackage, NULL},
4293     { szInstallExecute, ACTION_InstallExecute },
4294     { szInstallExecuteAgain, ACTION_InstallExecute },
4295     { szInstallFiles, ACTION_InstallFiles},
4296     { szInstallFinalize, ACTION_InstallFinalize },
4297     { szInstallInitialize, ACTION_InstallInitialize },
4298     { szInstallSFPCatalogFile, NULL},
4299     { szInstallValidate, ACTION_InstallValidate },
4300     { szIsolateComponents, ACTION_IsolateComponents },
4301     { szLaunchConditions, ACTION_LaunchConditions },
4302     { szMigrateFeatureStates, ACTION_MigrateFeatureStates },
4303     { szMoveFiles, ACTION_MoveFiles },
4304     { szMsiPublishAssemblies, ACTION_MsiPublishAssemblies },
4305     { szMsiUnpublishAssemblies, ACTION_MsiUnpublishAssemblies },
4306     { szInstallODBC, NULL},
4307     { szInstallServices, ACTION_InstallServices },
4308     { szPatchFiles, ACTION_PatchFiles },
4309     { szProcessComponents, ACTION_ProcessComponents },
4310     { szPublishComponents, ACTION_PublishComponents },
4311     { szPublishFeatures, ACTION_PublishFeatures },
4312     { szPublishProduct, ACTION_PublishProduct },
4313     { szRegisterClassInfo, ACTION_RegisterClassInfo },
4314     { szRegisterComPlus, ACTION_RegisterComPlus},
4315     { szRegisterExtensionInfo, ACTION_RegisterExtensionInfo },
4316     { szRegisterFonts, ACTION_RegisterFonts },
4317     { szRegisterMIMEInfo, ACTION_RegisterMIMEInfo },
4318     { szRegisterProduct, ACTION_RegisterProduct },
4319     { szRegisterProgIdInfo, ACTION_RegisterProgIdInfo },
4320     { szRegisterTypeLibraries, ACTION_RegisterTypeLibraries },
4321     { szRegisterUser, ACTION_RegisterUser},
4322     { szRemoveDuplicateFiles, NULL},
4323     { szRemoveEnvironmentStrings, ACTION_RemoveEnvironmentStrings },
4324     { szRemoveExistingProducts, NULL},
4325     { szRemoveFiles, ACTION_RemoveFiles},
4326     { szRemoveFolders, NULL},
4327     { szRemoveIniValues, ACTION_RemoveIniValues },
4328     { szRemoveODBC, NULL},
4329     { szRemoveRegistryValues, NULL},
4330     { szRemoveShortcuts, NULL},
4331     { szResolveSource, ACTION_ResolveSource},
4332     { szRMCCPSearch, ACTION_RMCCPSearch},
4333     { szScheduleReboot, NULL},
4334     { szSelfRegModules, ACTION_SelfRegModules },
4335     { szSelfUnregModules, ACTION_SelfUnregModules },
4336     { szSetODBCFolders, NULL},
4337     { szStartServices, ACTION_StartServices },
4338     { szStopServices, ACTION_StopServices },
4339     { szUnpublishComponents, NULL},
4340     { szUnpublishFeatures, NULL},
4341     { szUnregisterClassInfo, NULL},
4342     { szUnregisterComPlus, ACTION_UnregisterComPlus},
4343     { szUnregisterExtensionInfo, NULL},
4344     { szUnregisterFonts, ACTION_UnregisterFonts },
4345     { szUnregisterMIMEInfo, NULL},
4346     { szUnregisterProgIdInfo, NULL},
4347     { szUnregisterTypeLibraries, NULL},
4348     { szValidateProductID, NULL},
4349     { szWriteEnvironmentStrings, ACTION_WriteEnvironmentStrings },
4350     { szWriteIniValues, ACTION_WriteIniValues },
4351     { szWriteRegistryValues, ACTION_WriteRegistryValues},
4352     { NULL, NULL},
4353 };