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