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