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