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