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