oleaut32: Implement GetTypeAttr_Proxy and friends.
[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_zero(sizeof(MSISCRIPT));
577
578     package->script->InWhatSequence = SEQUENCE_INSTALL;
579
580     if (szPackagePath)   
581     {
582         LPWSTR p, check, path;
583  
584         package->PackagePath = strdupW(szPackagePath);
585         path = strdupW(szPackagePath);
586         p = strrchrW(path,'\\');    
587         if (p)
588         {
589             p++;
590             *p=0;
591         }
592         else
593         {
594             msi_free(path);
595             path = msi_alloc(MAX_PATH*sizeof(WCHAR));
596             GetCurrentDirectoryW(MAX_PATH,path);
597             strcatW(path,cszbs);
598         }
599
600         check = msi_dup_property( package, cszSourceDir );
601         if (!check)
602             MSI_SetPropertyW(package, cszSourceDir, path);
603         msi_free(check);
604         msi_free(path);
605     }
606
607     msi_parse_command_line( package, szCommandLine );
608
609     msi_apply_transforms( package );
610     msi_apply_patches( package );
611
612     if ( msi_get_property_int(package, szUILevel, 0) >= INSTALLUILEVEL_REDUCED )
613     {
614         package->script->InWhatSequence |= SEQUENCE_UI;
615         rc = ACTION_ProcessUISequence(package);
616         ui = TRUE;
617         if (rc == ERROR_SUCCESS)
618         {
619             package->script->InWhatSequence |= SEQUENCE_EXEC;
620             rc = ACTION_ProcessExecSequence(package,TRUE);
621         }
622     }
623     else
624         rc = ACTION_ProcessExecSequence(package,FALSE);
625     
626     if (rc == -1)
627     {
628         /* install was halted but should be considered a success */
629         rc = ERROR_SUCCESS;
630     }
631
632     package->script->CurrentlyScripting= FALSE;
633
634     /* process the ending type action */
635     if (rc == ERROR_SUCCESS)
636         ACTION_PerformActionSequence(package,-1,ui);
637     else if (rc == ERROR_INSTALL_USEREXIT) 
638         ACTION_PerformActionSequence(package,-2,ui);
639     else if (rc == ERROR_INSTALL_SUSPEND) 
640         ACTION_PerformActionSequence(package,-4,ui);
641     else  /* failed */
642         ACTION_PerformActionSequence(package,-3,ui);
643
644     /* finish up running custom actions */
645     ACTION_FinishCustomActions(package);
646     
647     return rc;
648 }
649
650 static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq, BOOL UI)
651 {
652     UINT rc = ERROR_SUCCESS;
653     MSIRECORD * row = 0;
654     static const WCHAR ExecSeqQuery[] =
655         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
656          '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
657          'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
658          '`','S','e','q','u','e','n','c','e','`',' ', '=',' ','%','i',0};
659
660     static const WCHAR UISeqQuery[] =
661         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
662      '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e',
663      '`', ' ', 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',
664          ' ', '=',' ','%','i',0};
665
666     if (UI)
667         row = MSI_QueryGetRecord(package->db, UISeqQuery, seq);
668     else
669         row = MSI_QueryGetRecord(package->db, ExecSeqQuery, seq);
670
671     if (row)
672     {
673         LPCWSTR action, cond;
674
675         TRACE("Running the actions\n"); 
676
677         /* check conditions */
678         cond = MSI_RecordGetString(row,2);
679         if (cond)
680         {
681             /* this is a hack to skip errors in the condition code */
682             if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
683                 goto end;
684         }
685
686         action = MSI_RecordGetString(row,1);
687         if (!action)
688         {
689             ERR("failed to fetch action\n");
690             rc = ERROR_FUNCTION_FAILED;
691             goto end;
692         }
693
694         if (UI)
695             rc = ACTION_PerformUIAction(package,action);
696         else
697             rc = ACTION_PerformAction(package,action,FALSE);
698 end:
699         msiobj_release(&row->hdr);
700     }
701     else
702         rc = ERROR_SUCCESS;
703
704     return rc;
705 }
706
707 typedef struct {
708     MSIPACKAGE* package;
709     BOOL UI;
710 } iterate_action_param;
711
712 static UINT ITERATE_Actions(MSIRECORD *row, LPVOID param)
713 {
714     iterate_action_param *iap= (iterate_action_param*)param;
715     UINT rc;
716     LPCWSTR cond, action;
717
718     action = MSI_RecordGetString(row,1);
719     if (!action)
720     {
721         ERR("Error is retrieving action name\n");
722         return  ERROR_FUNCTION_FAILED;
723     }
724
725     /* check conditions */
726     cond = MSI_RecordGetString(row,2);
727     if (cond)
728     {
729         /* this is a hack to skip errors in the condition code */
730         if (MSI_EvaluateConditionW(iap->package, cond) == MSICONDITION_FALSE)
731         {
732             TRACE("Skipping action: %s (condition is false)\n",
733                             debugstr_w(action));
734             return ERROR_SUCCESS;
735         }
736     }
737
738     if (iap->UI)
739         rc = ACTION_PerformUIAction(iap->package,action);
740     else
741         rc = ACTION_PerformAction(iap->package,action,FALSE);
742
743     msi_dialog_check_messages( NULL );
744
745     if (iap->package->CurrentInstallState != ERROR_SUCCESS )
746         rc = iap->package->CurrentInstallState;
747
748     if (rc == ERROR_FUNCTION_NOT_CALLED)
749         rc = ERROR_SUCCESS;
750
751     if (rc != ERROR_SUCCESS)
752         ERR("Execution halted, action %s returned %i\n", debugstr_w(action), rc);
753
754     return rc;
755 }
756
757 UINT MSI_Sequence( MSIPACKAGE *package, LPCWSTR szTable, INT iSequenceMode )
758 {
759     MSIQUERY * view;
760     UINT r;
761     static const WCHAR query[] =
762         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
763          '`','%','s','`',
764          ' ','W','H','E','R','E',' ', 
765          '`','S','e','q','u','e','n','c','e','`',' ',
766          '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
767          '`','S','e','q','u','e','n','c','e','`',0};
768     iterate_action_param iap;
769
770     /*
771      * FIXME: probably should be checking UILevel in the
772      *       ACTION_PerformUIAction/ACTION_PerformAction
773      *       rather than saving the UI level here. Those
774      *       two functions can be merged too.
775      */
776     iap.package = package;
777     iap.UI = TRUE;
778
779     TRACE("%p %s %i\n", package, debugstr_w(szTable), iSequenceMode );
780
781     r = MSI_OpenQuery( package->db, &view, query, szTable );
782     if (r == ERROR_SUCCESS)
783     {
784         r = MSI_IterateRecords( view, NULL, ITERATE_Actions, &iap );
785         msiobj_release(&view->hdr);
786     }
787
788     return r;
789 }
790
791 static UINT ACTION_ProcessExecSequence(MSIPACKAGE *package, BOOL UIran)
792 {
793     MSIQUERY * view;
794     UINT rc;
795     static const WCHAR ExecSeqQuery[] =
796         {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
797          '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
798          'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
799          '`','S','e','q','u','e','n','c','e','`',' ', '>',' ','%','i',' ',
800          'O','R','D','E','R',' ', 'B','Y',' ',
801          '`','S','e','q','u','e','n','c','e','`',0 };
802     MSIRECORD * row = 0;
803     static const WCHAR IVQuery[] =
804         {'S','E','L','E','C','T',' ','`','S','e','q','u','e','n','c','e','`',
805          ' ', 'F','R','O','M',' ','`','I','n','s','t','a','l','l',
806          'E','x','e','c','u','t','e','S','e','q','u','e','n','c','e','`',' ',
807          'W','H','E','R','E',' ','`','A','c','t','i','o','n','`',' ','=',
808          ' ','\'', 'I','n','s','t','a','l','l',
809          'V','a','l','i','d','a','t','e','\'', 0};
810     INT seq = 0;
811     iterate_action_param iap;
812
813     iap.package = package;
814     iap.UI = FALSE;
815
816     if (package->script->ExecuteSequenceRun)
817     {
818         TRACE("Execute Sequence already Run\n");
819         return ERROR_SUCCESS;
820     }
821
822     package->script->ExecuteSequenceRun = TRUE;
823
824     /* get the sequence number */
825     if (UIran)
826     {
827         row = MSI_QueryGetRecord(package->db, IVQuery);
828         if( !row )
829             return ERROR_FUNCTION_FAILED;
830         seq = MSI_RecordGetInteger(row,1);
831         msiobj_release(&row->hdr);
832     }
833
834     rc = MSI_OpenQuery(package->db, &view, ExecSeqQuery, seq);
835     if (rc == ERROR_SUCCESS)
836     {
837         TRACE("Running the actions\n");
838
839         rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, &iap);
840         msiobj_release(&view->hdr);
841     }
842
843     return rc;
844 }
845
846 static UINT ACTION_ProcessUISequence(MSIPACKAGE *package)
847 {
848     MSIQUERY * view;
849     UINT rc;
850     static const WCHAR ExecSeqQuery [] =
851         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
852          '`','I','n','s','t','a','l','l',
853          'U','I','S','e','q','u','e','n','c','e','`',
854          ' ','W','H','E','R','E',' ', 
855          '`','S','e','q','u','e','n','c','e','`',' ',
856          '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
857          '`','S','e','q','u','e','n','c','e','`',0};
858     iterate_action_param iap;
859
860     iap.package = package;
861     iap.UI = TRUE;
862
863     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
864     
865     if (rc == ERROR_SUCCESS)
866     {
867         TRACE("Running the actions\n"); 
868
869         rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, &iap);
870         msiobj_release(&view->hdr);
871     }
872
873     return rc;
874 }
875
876 /********************************************************
877  * ACTION helper functions and functions that perform the actions
878  *******************************************************/
879 static BOOL ACTION_HandleStandardAction(MSIPACKAGE *package, LPCWSTR action, 
880                                         UINT* rc, BOOL force )
881 {
882     BOOL ret = FALSE; 
883     BOOL run = force;
884     int i;
885
886     if (!package)
887     {
888         ERR("package was null!\n");
889         return FALSE;
890     }
891
892     if (!run && !package->script->CurrentlyScripting)
893         run = TRUE;
894    
895     if (!run)
896     {
897         if (strcmpW(action,szInstallFinalize) == 0 ||
898             strcmpW(action,szInstallExecute) == 0 ||
899             strcmpW(action,szInstallExecuteAgain) == 0) 
900                 run = TRUE;
901     }
902     
903     i = 0;
904     while (StandardActions[i].action != NULL)
905     {
906         if (strcmpW(StandardActions[i].action, action)==0)
907         {
908             if (!run)
909             {
910                 ui_actioninfo(package, action, TRUE, 0);
911                 *rc = schedule_action(package,INSTALL_SCRIPT,action);
912                 ui_actioninfo(package, action, FALSE, *rc);
913             }
914             else
915             {
916                 ui_actionstart(package, action);
917                 if (StandardActions[i].handler)
918                 {
919                     *rc = StandardActions[i].handler(package);
920                 }
921                 else
922                 {
923                     FIXME("unhandled standard action %s\n",debugstr_w(action));
924                     *rc = ERROR_SUCCESS;
925                 }
926             }
927             ret = TRUE;
928             break;
929         }
930         i++;
931     }
932     return ret;
933 }
934
935 static BOOL ACTION_HandleCustomAction( MSIPACKAGE* package, LPCWSTR action,
936                                        UINT* rc, BOOL force )
937 {
938     BOOL ret=FALSE;
939     UINT arc;
940
941     arc = ACTION_CustomAction(package,action, force);
942
943     if (arc != ERROR_CALL_NOT_IMPLEMENTED)
944     {
945         *rc = arc;
946         ret = TRUE;
947     }
948     return ret;
949 }
950
951 /* 
952  * A lot of actions are really important even if they don't do anything
953  * explicit... Lots of properties are set at the beginning of the installation
954  * CostFinalize does a bunch of work to translate the directories and such
955  * 
956  * But until I get write access to the database that is hard, so I am going to
957  * hack it to see if I can get something to run.
958  */
959 UINT ACTION_PerformAction(MSIPACKAGE *package, const WCHAR *action, BOOL force)
960 {
961     UINT rc = ERROR_SUCCESS; 
962     BOOL handled;
963
964     TRACE("Performing action (%s)\n",debugstr_w(action));
965
966     handled = ACTION_HandleStandardAction(package, action, &rc, force);
967
968     if (!handled)
969         handled = ACTION_HandleCustomAction(package, action, &rc, force);
970
971     if (!handled)
972     {
973         FIXME("unhandled msi action %s\n",debugstr_w(action));
974         rc = ERROR_FUNCTION_NOT_CALLED;
975     }
976
977     return rc;
978 }
979
980 UINT ACTION_PerformUIAction(MSIPACKAGE *package, const WCHAR *action)
981 {
982     UINT rc = ERROR_SUCCESS;
983     BOOL handled = FALSE;
984
985     TRACE("Performing action (%s)\n",debugstr_w(action));
986
987     handled = ACTION_HandleStandardAction(package, action, &rc,TRUE);
988
989     if (!handled)
990         handled = ACTION_HandleCustomAction(package, action, &rc, FALSE);
991
992     if( !handled && ACTION_DialogBox(package,action) == ERROR_SUCCESS )
993         handled = TRUE;
994
995     if (!handled)
996     {
997         FIXME("unhandled msi action %s\n",debugstr_w(action));
998         rc = ERROR_FUNCTION_NOT_CALLED;
999     }
1000
1001     return rc;
1002 }
1003
1004
1005 /*
1006  * Actual Action Handlers
1007  */
1008
1009 static UINT ITERATE_CreateFolders(MSIRECORD *row, LPVOID param)
1010 {
1011     MSIPACKAGE *package = (MSIPACKAGE*)param;
1012     LPCWSTR dir;
1013     LPWSTR full_path;
1014     MSIRECORD *uirow;
1015     MSIFOLDER *folder;
1016
1017     dir = MSI_RecordGetString(row,1);
1018     if (!dir)
1019     {
1020         ERR("Unable to get folder id\n");
1021         return ERROR_SUCCESS;
1022     }
1023
1024     full_path = resolve_folder(package,dir,FALSE,FALSE,&folder);
1025     if (!full_path)
1026     {
1027         ERR("Unable to resolve folder id %s\n",debugstr_w(dir));
1028         return ERROR_SUCCESS;
1029     }
1030
1031     TRACE("Folder is %s\n",debugstr_w(full_path));
1032
1033     /* UI stuff */
1034     uirow = MSI_CreateRecord(1);
1035     MSI_RecordSetStringW(uirow,1,full_path);
1036     ui_actiondata(package,szCreateFolders,uirow);
1037     msiobj_release( &uirow->hdr );
1038
1039     if (folder->State == 0)
1040         create_full_pathW(full_path);
1041
1042     folder->State = 3;
1043
1044     msi_free(full_path);
1045     return ERROR_SUCCESS;
1046 }
1047
1048 /* FIXME: probably should merge this with the above function */
1049 static UINT msi_create_directory( MSIPACKAGE* package, LPCWSTR dir )
1050 {
1051     UINT rc = ERROR_SUCCESS;
1052     MSIFOLDER *folder;
1053     LPWSTR install_path;
1054
1055     install_path = resolve_folder(package, dir, FALSE, FALSE, &folder);
1056     if (!install_path)
1057         return ERROR_FUNCTION_FAILED; 
1058
1059     /* create the path */
1060     if (folder->State == 0)
1061     {
1062         create_full_pathW(install_path);
1063         folder->State = 2;
1064     }
1065     msi_free(install_path);
1066
1067     return rc;
1068 }
1069
1070 UINT msi_create_component_directories( MSIPACKAGE *package )
1071 {
1072     MSICOMPONENT *comp;
1073
1074     /* create all the folders required by the components are going to install */
1075     LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
1076     {
1077         if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
1078             continue;
1079         msi_create_directory( package, comp->Directory );
1080     }
1081
1082     return ERROR_SUCCESS;
1083 }
1084
1085 /*
1086  * Also we cannot enable/disable components either, so for now I am just going 
1087  * to do all the directories for all the components.
1088  */
1089 static UINT ACTION_CreateFolders(MSIPACKAGE *package)
1090 {
1091     static const WCHAR ExecSeqQuery[] =
1092         {'S','E','L','E','C','T',' ',
1093          '`','D','i','r','e','c','t','o','r','y','_','`',
1094          ' ','F','R','O','M',' ',
1095          '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0 };
1096     UINT rc;
1097     MSIQUERY *view;
1098
1099     /* create all the empty folders specified in the CreateFolder table */
1100     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view );
1101     if (rc != ERROR_SUCCESS)
1102         return ERROR_SUCCESS;
1103
1104     rc = MSI_IterateRecords(view, NULL, ITERATE_CreateFolders, package);
1105     msiobj_release(&view->hdr);
1106
1107     msi_create_component_directories( package );
1108
1109     return rc;
1110 }
1111
1112 static MSICOMPONENT* load_component( MSIRECORD * row )
1113 {
1114     MSICOMPONENT *comp;
1115
1116     comp = msi_alloc_zero( sizeof(MSICOMPONENT) );
1117     if (!comp)
1118         return comp;
1119
1120     /* fill in the data */
1121     comp->Component = msi_dup_record_field( row, 1 );
1122
1123     TRACE("Loading Component %s\n", debugstr_w(comp->Component));
1124
1125     comp->ComponentId = msi_dup_record_field( row, 2 );
1126     comp->Directory = msi_dup_record_field( row, 3 );
1127     comp->Attributes = MSI_RecordGetInteger(row,4);
1128     comp->Condition = msi_dup_record_field( row, 5 );
1129     comp->KeyPath = msi_dup_record_field( row, 6 );
1130
1131     comp->Installed = INSTALLSTATE_ABSENT;
1132     comp->Action = INSTALLSTATE_UNKNOWN;
1133     comp->ActionRequest = INSTALLSTATE_UNKNOWN;
1134
1135     comp->Enabled = TRUE;
1136
1137     return comp;
1138 }
1139
1140 typedef struct {
1141     MSIPACKAGE *package;
1142     MSIFEATURE *feature;
1143 } _ilfs;
1144
1145 static UINT add_feature_component( MSIFEATURE *feature, MSICOMPONENT *comp )
1146 {
1147     ComponentList *cl;
1148
1149     cl = msi_alloc( sizeof (*cl) );
1150     if ( !cl )
1151         return ERROR_NOT_ENOUGH_MEMORY;
1152     cl->component = comp;
1153     list_add_tail( &feature->Components, &cl->entry );
1154
1155     return ERROR_SUCCESS;
1156 }
1157
1158 static UINT iterate_component_check( MSIRECORD *row, LPVOID param )
1159 {
1160     _ilfs* ilfs= (_ilfs*)param;
1161     MSIPACKAGE *package = ilfs->package;
1162     MSIFEATURE *feature = ilfs->feature;
1163     MSICOMPONENT *comp;
1164
1165     comp = load_component( row );
1166     if (!comp)
1167         return ERROR_FUNCTION_FAILED;
1168
1169     list_add_tail( &package->components, &comp->entry );
1170     add_feature_component( feature, comp );
1171
1172     TRACE("Loaded new component %p\n", comp);
1173
1174     return ERROR_SUCCESS;
1175 }
1176
1177 static UINT iterate_load_featurecomponents(MSIRECORD *row, LPVOID param)
1178 {
1179     _ilfs* ilfs= (_ilfs*)param;
1180     LPCWSTR component;
1181     DWORD rc;
1182     MSICOMPONENT *comp;
1183     MSIQUERY * view;
1184     static const WCHAR Query[] = 
1185         {'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ', 
1186          '`','C','o','m','p','o','n','e','n','t','`',' ',
1187          'W','H','E','R','E',' ', 
1188          '`','C','o','m','p','o','n','e','n','t','`',' ',
1189          '=','\'','%','s','\'',0};
1190
1191     component = MSI_RecordGetString(row,1);
1192
1193     /* check to see if the component is already loaded */
1194     comp = get_loaded_component( ilfs->package, component );
1195     if (comp)
1196     {
1197         TRACE("Component %s already loaded\n", debugstr_w(component) );
1198         add_feature_component( ilfs->feature, comp );
1199         return ERROR_SUCCESS;
1200     }
1201
1202     rc = MSI_OpenQuery(ilfs->package->db, &view, Query, component);
1203     if (rc != ERROR_SUCCESS)
1204         return ERROR_SUCCESS;
1205
1206     rc = MSI_IterateRecords(view, NULL, iterate_component_check, ilfs);
1207     msiobj_release( &view->hdr );
1208
1209     return ERROR_SUCCESS;
1210 }
1211
1212 static UINT load_feature(MSIRECORD * row, LPVOID param)
1213 {
1214     MSIPACKAGE* package = (MSIPACKAGE*)param;
1215     MSIFEATURE* feature;
1216     static const WCHAR Query1[] = 
1217         {'S','E','L','E','C','T',' ',
1218          '`','C','o','m','p','o','n','e','n','t','_','`',
1219          ' ','F','R','O','M',' ','`','F','e','a','t','u','r','e',
1220          'C','o','m','p','o','n','e','n','t','s','`',' ',
1221          'W','H','E','R','E',' ',
1222          '`','F','e', 'a','t','u','r','e','_','`',' ','=','\'','%','s','\'',0};
1223     MSIQUERY * view;
1224     UINT    rc;
1225     _ilfs ilfs;
1226
1227     /* fill in the data */
1228
1229     feature = msi_alloc_zero( sizeof (MSIFEATURE) );
1230     if (!feature)
1231         return ERROR_NOT_ENOUGH_MEMORY;
1232
1233     list_init( &feature->Components );
1234     
1235     feature->Feature = msi_dup_record_field( row, 1 );
1236
1237     TRACE("Loading feature %s\n",debugstr_w(feature->Feature));
1238
1239     feature->Feature_Parent = msi_dup_record_field( row, 2 );
1240     feature->Title = msi_dup_record_field( row, 3 );
1241     feature->Description = msi_dup_record_field( row, 4 );
1242
1243     if (!MSI_RecordIsNull(row,5))
1244         feature->Display = MSI_RecordGetInteger(row,5);
1245   
1246     feature->Level= MSI_RecordGetInteger(row,6);
1247     feature->Directory = msi_dup_record_field( row, 7 );
1248     feature->Attributes = MSI_RecordGetInteger(row,8);
1249
1250     feature->Installed = INSTALLSTATE_ABSENT;
1251     feature->Action = INSTALLSTATE_UNKNOWN;
1252     feature->ActionRequest = INSTALLSTATE_UNKNOWN;
1253
1254     list_add_tail( &package->features, &feature->entry );
1255
1256     /* load feature components */
1257
1258     rc = MSI_OpenQuery( package->db, &view, Query1, feature->Feature );
1259     if (rc != ERROR_SUCCESS)
1260         return ERROR_SUCCESS;
1261
1262     ilfs.package = package;
1263     ilfs.feature = feature;
1264
1265     MSI_IterateRecords(view, NULL, iterate_load_featurecomponents , &ilfs);
1266     msiobj_release(&view->hdr);
1267
1268     return ERROR_SUCCESS;
1269 }
1270
1271 static LPWSTR folder_split_path(LPWSTR p, WCHAR ch)
1272 {
1273     if (!p)
1274         return p;
1275     p = strchrW(p, ch);
1276     if (!p)
1277         return p;
1278     *p = 0;
1279     return p+1;
1280 }
1281
1282 static UINT load_file(MSIRECORD *row, LPVOID param)
1283 {
1284     MSIPACKAGE* package = (MSIPACKAGE*)param;
1285     LPCWSTR component;
1286     MSIFILE *file;
1287
1288     /* fill in the data */
1289
1290     file = msi_alloc_zero( sizeof (MSIFILE) );
1291     if (!file)
1292         return ERROR_NOT_ENOUGH_MEMORY;
1293  
1294     file->File = msi_dup_record_field( row, 1 );
1295
1296     component = MSI_RecordGetString( row, 2 );
1297     file->Component = get_loaded_component( package, component );
1298
1299     if (!file->Component)
1300         ERR("Unfound Component %s\n",debugstr_w(component));
1301
1302     file->FileName = msi_dup_record_field( row, 3 );
1303     reduce_to_longfilename( file->FileName );
1304
1305     file->ShortName = msi_dup_record_field( row, 3 );
1306     file->LongName = strdupW( folder_split_path(file->ShortName, '|'));
1307     
1308     file->FileSize = MSI_RecordGetInteger( row, 4 );
1309     file->Version = msi_dup_record_field( row, 5 );
1310     file->Language = msi_dup_record_field( row, 6 );
1311     file->Attributes = MSI_RecordGetInteger( row, 7 );
1312     file->Sequence = MSI_RecordGetInteger( row, 8 );
1313
1314     file->state = msifs_invalid;
1315
1316     TRACE("File Loaded (%s)\n",debugstr_w(file->File));  
1317
1318     list_add_tail( &package->files, &file->entry );
1319  
1320     return ERROR_SUCCESS;
1321 }
1322
1323 static UINT load_all_files(MSIPACKAGE *package)
1324 {
1325     MSIQUERY * view;
1326     UINT rc;
1327     static const WCHAR Query[] =
1328         {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1329          '`','F','i','l','e','`',' ', 'O','R','D','E','R',' ','B','Y',' ',
1330          '`','S','e','q','u','e','n','c','e','`', 0};
1331
1332     if (!package)
1333         return ERROR_INVALID_HANDLE;
1334
1335     rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
1336     if (rc != ERROR_SUCCESS)
1337         return ERROR_SUCCESS;
1338
1339     rc = MSI_IterateRecords(view, NULL, load_file, package);
1340     msiobj_release(&view->hdr);
1341
1342     return ERROR_SUCCESS;
1343 }
1344
1345
1346 /*
1347  * I am not doing any of the costing functionality yet. 
1348  * Mostly looking at doing the Component and Feature loading
1349  *
1350  * The native MSI does A LOT of modification to tables here. Mostly adding
1351  * a lot of temporary columns to the Feature and Component tables. 
1352  *
1353  *    note: Native msi also tracks the short filename. But I am only going to
1354  *          track the long ones.  Also looking at this directory table
1355  *          it appears that the directory table does not get the parents
1356  *          resolved base on property only based on their entries in the 
1357  *          directory table.
1358  */
1359 static UINT ACTION_CostInitialize(MSIPACKAGE *package)
1360 {
1361     MSIQUERY * view;
1362     UINT rc;
1363     static const WCHAR Query_all[] =
1364         {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1365          '`','F','e','a','t','u','r','e','`',0};
1366     static const WCHAR szCosting[] =
1367         {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
1368     static const WCHAR szZero[] = { '0', 0 };
1369
1370     if ( 1 == msi_get_property_int( package, szCosting, 0 ) )
1371         return ERROR_SUCCESS;
1372     
1373     MSI_SetPropertyW(package, szCosting, szZero);
1374     MSI_SetPropertyW(package, cszRootDrive , c_colon);
1375
1376     rc = MSI_DatabaseOpenViewW(package->db,Query_all,&view);
1377     if (rc != ERROR_SUCCESS)
1378         return rc;
1379
1380     rc = MSI_IterateRecords(view, NULL, load_feature, package);
1381     msiobj_release(&view->hdr);
1382
1383     load_all_files(package);
1384
1385     return ERROR_SUCCESS;
1386 }
1387
1388 static UINT execute_script(MSIPACKAGE *package, UINT script )
1389 {
1390     int i;
1391     UINT rc = ERROR_SUCCESS;
1392
1393     TRACE("Executing Script %i\n",script);
1394
1395     if (!package->script)
1396     {
1397         ERR("no script!\n");
1398         return ERROR_FUNCTION_FAILED;
1399     }
1400
1401     for (i = 0; i < package->script->ActionCount[script]; i++)
1402     {
1403         LPWSTR action;
1404         action = package->script->Actions[script][i];
1405         ui_actionstart(package, action);
1406         TRACE("Executing Action (%s)\n",debugstr_w(action));
1407         rc = ACTION_PerformAction(package, action, TRUE);
1408         msi_free(package->script->Actions[script][i]);
1409         if (rc != ERROR_SUCCESS)
1410             break;
1411     }
1412     msi_free(package->script->Actions[script]);
1413
1414     package->script->ActionCount[script] = 0;
1415     package->script->Actions[script] = NULL;
1416     return rc;
1417 }
1418
1419 static UINT ACTION_FileCost(MSIPACKAGE *package)
1420 {
1421     return ERROR_SUCCESS;
1422 }
1423
1424 static MSIFOLDER *load_folder( MSIPACKAGE *package, LPCWSTR dir )
1425 {
1426     static const WCHAR Query[] =
1427         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1428          '`','D','i','r','e','c', 't','o','r','y','`',' ',
1429          'W','H','E','R','E',' ', '`', 'D','i','r','e','c','t', 'o','r','y','`',
1430          ' ','=',' ','\'','%','s','\'',
1431          0};
1432     static const WCHAR szDot[] = { '.',0 };
1433     LPWSTR p, tgt_short, tgt_long, src_short, src_long;
1434     LPCWSTR parent;
1435     MSIRECORD *row;
1436     MSIFOLDER *folder;
1437
1438     TRACE("Looking for dir %s\n",debugstr_w(dir));
1439
1440     folder = get_loaded_folder( package, dir );
1441     if (folder)
1442         return folder;
1443
1444     TRACE("Working to load %s\n",debugstr_w(dir));
1445
1446     folder = msi_alloc_zero( sizeof (MSIFOLDER) );
1447     if (!folder)
1448         return NULL;
1449
1450     folder->Directory = strdupW(dir);
1451
1452     row = MSI_QueryGetRecord(package->db, Query, dir);
1453     if (!row)
1454         return NULL;
1455
1456     p = msi_dup_record_field(row, 3);
1457
1458     /* split src and target dir */
1459     tgt_short = p;
1460     src_short = folder_split_path( p, ':' );
1461
1462     /* split the long and short pathes */
1463     tgt_long = folder_split_path( tgt_short, '|' );
1464     src_long = folder_split_path( src_short, '|' );
1465
1466     /* check for root dirs */
1467     if (!lstrcmpW(szDot, tgt_short))
1468         tgt_short = NULL;
1469     if (!lstrcmpW(szDot, tgt_long))
1470         tgt_long = NULL;
1471
1472     if (!tgt_long)
1473         tgt_long = tgt_short;
1474     if (!src_short)
1475         src_short = tgt_long;
1476     if (!src_long)
1477         src_long = src_short;
1478
1479     /* FIXME: use the target short path too */
1480     folder->TargetDefault = strdupW(tgt_long);
1481     folder->SourceShortPath = strdupW(src_long);
1482     folder->SourceLongPath = strdupW(src_long);
1483     msi_free(p);
1484
1485     TRACE("TargetDefault = %s\n",debugstr_w( folder->TargetDefault ));
1486     TRACE("SourceLong = %s\n", debugstr_w( folder->SourceLongPath ));
1487     TRACE("SourceShort = %s\n", debugstr_w( folder->SourceShortPath ));
1488
1489     parent = MSI_RecordGetString(row, 2);
1490     if (parent) 
1491     {
1492         folder->Parent = load_folder( package, parent );
1493         if ( folder->Parent )
1494             TRACE("loaded parent %p %s\n", folder->Parent,
1495                   debugstr_w(folder->Parent->Directory));
1496         else
1497             ERR("failed to load parent folder %s\n", debugstr_w(parent));
1498     }
1499
1500     folder->Property = msi_dup_property( package, dir );
1501
1502     msiobj_release(&row->hdr);
1503
1504     list_add_tail( &package->folders, &folder->entry );
1505
1506     TRACE("%s returning %p\n",debugstr_w(dir),folder);
1507
1508     return folder;
1509 }
1510
1511 /* scan for and update current install states */
1512 static void ACTION_UpdateInstallStates(MSIPACKAGE *package)
1513 {
1514     MSICOMPONENT *comp;
1515     MSIFEATURE *feature;
1516
1517     LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
1518     {
1519         INSTALLSTATE res;
1520         res = MsiGetComponentPathW( package->ProductCode, 
1521                                     comp->ComponentId, NULL, NULL);
1522         if (res < 0)
1523             res = INSTALLSTATE_ABSENT;
1524         comp->Installed = res;
1525     }
1526
1527     LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1528     {
1529         ComponentList *cl;
1530         INSTALLSTATE res = -10;
1531
1532         LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1533         {
1534             comp= cl->component;
1535
1536             if (res == -10)
1537                 res = comp->Installed;
1538             else
1539             {
1540                 if (res == comp->Installed)
1541                     continue;
1542
1543                 if (res != comp->Installed)
1544                         res = INSTALLSTATE_INCOMPLETE;
1545             }
1546         }
1547         feature->Installed = res;
1548     }
1549 }
1550
1551 static BOOL process_state_property (MSIPACKAGE* package, LPCWSTR property, 
1552                                     INSTALLSTATE state)
1553 {
1554     static const WCHAR all[]={'A','L','L',0};
1555     LPWSTR override;
1556     MSIFEATURE *feature;
1557
1558     override = msi_dup_property( package, property );
1559     if (!override)
1560         return FALSE;
1561  
1562     LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1563     {
1564         if (strcmpiW(override,all)==0)
1565         {
1566             feature->ActionRequest= state;
1567             feature->Action = state;
1568         }
1569         else
1570         {
1571             LPWSTR ptr = override;
1572             LPWSTR ptr2 = strchrW(override,',');
1573
1574             while (ptr)
1575             {
1576                 if ((ptr2 && strncmpW(ptr,feature->Feature, ptr2-ptr)==0)
1577                     || (!ptr2 && strcmpW(ptr,feature->Feature)==0))
1578                 {
1579                     feature->ActionRequest= state;
1580                     feature->Action = state;
1581                     break;
1582                 }
1583                 if (ptr2)
1584                 {
1585                     ptr=ptr2+1;
1586                     ptr2 = strchrW(ptr,',');
1587                 }
1588                 else
1589                     break;
1590             }
1591         }
1592     } 
1593     msi_free(override);
1594
1595     return TRUE;
1596 }
1597
1598 static UINT SetFeatureStates(MSIPACKAGE *package)
1599 {
1600     int install_level;
1601     static const WCHAR szlevel[] =
1602         {'I','N','S','T','A','L','L','L','E','V','E','L',0};
1603     static const WCHAR szAddLocal[] =
1604         {'A','D','D','L','O','C','A','L',0};
1605     static const WCHAR szRemove[] =
1606         {'R','E','M','O','V','E',0};
1607     static const WCHAR szReinstall[] =
1608         {'R','E','I','N','S','T','A','L','L',0};
1609     BOOL override = FALSE;
1610     MSICOMPONENT* component;
1611     MSIFEATURE *feature;
1612
1613
1614     /* I do not know if this is where it should happen.. but */
1615
1616     TRACE("Checking Install Level\n");
1617
1618     install_level = msi_get_property_int( package, szlevel, 1 );
1619
1620     /* ok hereis the _real_ rub
1621      * all these activation/deactivation things happen in order and things
1622      * later on the list override things earlier on the list.
1623      * 1) INSTALLLEVEL processing
1624      * 2) ADDLOCAL
1625      * 3) REMOVE
1626      * 4) ADDSOURCE
1627      * 5) ADDDEFAULT
1628      * 6) REINSTALL
1629      * 7) COMPADDLOCAL
1630      * 8) COMPADDSOURCE
1631      * 9) FILEADDLOCAL
1632      * 10) FILEADDSOURCE
1633      * 11) FILEADDDEFAULT
1634      * I have confirmed that if ADDLOCAL is stated then the INSTALLLEVEL is
1635      * ignored for all the features. seems strange, especially since it is not
1636      * documented anywhere, but it is how it works. 
1637      *
1638      * I am still ignoring a lot of these. But that is ok for now, ADDLOCAL and
1639      * REMOVE are the big ones, since we don't handle administrative installs
1640      * yet anyway.
1641      */
1642     override |= process_state_property(package,szAddLocal,INSTALLSTATE_LOCAL);
1643     override |= process_state_property(package,szRemove,INSTALLSTATE_ABSENT);
1644     override |= process_state_property(package,szReinstall,INSTALLSTATE_LOCAL);
1645
1646     if (!override)
1647     {
1648         LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1649         {
1650             BOOL feature_state = ((feature->Level > 0) &&
1651                              (feature->Level <= install_level));
1652
1653             if ((feature_state) && (feature->Action == INSTALLSTATE_UNKNOWN))
1654             {
1655                 if (feature->Attributes & msidbFeatureAttributesFavorSource)
1656                 {
1657                     feature->ActionRequest = INSTALLSTATE_SOURCE;
1658                     feature->Action = INSTALLSTATE_SOURCE;
1659                 }
1660                 else if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1661                 {
1662                     feature->ActionRequest = INSTALLSTATE_ADVERTISED;
1663                     feature->Action = INSTALLSTATE_ADVERTISED;
1664                 }
1665                 else
1666                 {
1667                     feature->ActionRequest = INSTALLSTATE_LOCAL;
1668                     feature->Action = INSTALLSTATE_LOCAL;
1669                 }
1670             }
1671         }
1672     }
1673     else
1674     {
1675         /* set the Preselected Property */
1676         static const WCHAR szPreselected[] = {'P','r','e','s','e','l','e','c','t','e','d',0};
1677         static const WCHAR szOne[] = { '1', 0 };
1678
1679         MSI_SetPropertyW(package,szPreselected,szOne);
1680     }
1681
1682     /*
1683      * now we want to enable or disable components base on feature 
1684     */
1685
1686     LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1687     {
1688         ComponentList *cl;
1689
1690         TRACE("Examining Feature %s (Installed %i, Action %i, Request %i)\n",
1691             debugstr_w(feature->Feature), feature->Installed, feature->Action,
1692             feature->ActionRequest);
1693
1694         LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1695         {
1696             component = cl->component;
1697
1698             if (!component->Enabled)
1699             {
1700                 component->Action = INSTALLSTATE_UNKNOWN;
1701                 component->ActionRequest = INSTALLSTATE_UNKNOWN;
1702             }
1703             else
1704             {
1705                 if (feature->Action == INSTALLSTATE_LOCAL)
1706                 {
1707                     component->Action = INSTALLSTATE_LOCAL;
1708                     component->ActionRequest = INSTALLSTATE_LOCAL;
1709                 }
1710                 else if (feature->ActionRequest == INSTALLSTATE_SOURCE)
1711                 {
1712                     if ((component->Action == INSTALLSTATE_UNKNOWN) ||
1713                         (component->Action == INSTALLSTATE_ABSENT) ||
1714                         (component->Action == INSTALLSTATE_ADVERTISED))
1715                            
1716                     {
1717                         component->Action = INSTALLSTATE_SOURCE;
1718                         component->ActionRequest = INSTALLSTATE_SOURCE;
1719                     }
1720                 }
1721                 else if (feature->ActionRequest == INSTALLSTATE_ADVERTISED)
1722                 {
1723                     if ((component->Action == INSTALLSTATE_UNKNOWN) ||
1724                         (component->Action == INSTALLSTATE_ABSENT))
1725                            
1726                     {
1727                         component->Action = INSTALLSTATE_ADVERTISED;
1728                         component->ActionRequest = INSTALLSTATE_ADVERTISED;
1729                     }
1730                 }
1731                 else if (feature->ActionRequest == INSTALLSTATE_ABSENT)
1732                 {
1733                     if (component->Action == INSTALLSTATE_UNKNOWN)
1734                     {
1735                         component->Action = INSTALLSTATE_ABSENT;
1736                         component->ActionRequest = INSTALLSTATE_ABSENT;
1737                     }
1738                 }
1739             }
1740         }
1741     } 
1742
1743     LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1744     {
1745         TRACE("Result: Component %s (Installed %i, Action %i, Request %i)\n",
1746             debugstr_w(component->Component), component->Installed, 
1747             component->Action, component->ActionRequest);
1748     }
1749
1750
1751     return ERROR_SUCCESS;
1752 }
1753
1754 static UINT ITERATE_CostFinalizeDirectories(MSIRECORD *row, LPVOID param)
1755 {
1756     MSIPACKAGE *package = (MSIPACKAGE*)param;
1757     LPCWSTR name;
1758     LPWSTR path;
1759
1760     name = MSI_RecordGetString(row,1);
1761
1762     /* This helper function now does ALL the work */
1763     TRACE("Dir %s ...\n",debugstr_w(name));
1764     load_folder(package,name);
1765     path = resolve_folder(package,name,FALSE,TRUE,NULL);
1766     TRACE("resolves to %s\n",debugstr_w(path));
1767     msi_free(path);
1768
1769     return ERROR_SUCCESS;
1770 }
1771
1772 static UINT ITERATE_CostFinalizeConditions(MSIRECORD *row, LPVOID param)
1773 {
1774     MSIPACKAGE *package = (MSIPACKAGE*)param;
1775     LPCWSTR name;
1776     MSIFEATURE *feature;
1777
1778     name = MSI_RecordGetString( row, 1 );
1779
1780     feature = get_loaded_feature( package, name );
1781     if (!feature)
1782         ERR("FAILED to find loaded feature %s\n",debugstr_w(name));
1783     else
1784     {
1785         LPCWSTR Condition;
1786         Condition = MSI_RecordGetString(row,3);
1787
1788         if (MSI_EvaluateConditionW(package,Condition) == MSICONDITION_TRUE)
1789         {
1790             int level = MSI_RecordGetInteger(row,2);
1791             TRACE("Reseting feature %s to level %i\n", debugstr_w(name), level);
1792             feature->Level = level;
1793         }
1794     }
1795     return ERROR_SUCCESS;
1796 }
1797
1798
1799 /* 
1800  * A lot is done in this function aside from just the costing.
1801  * The costing needs to be implemented at some point but for now I am going
1802  * to focus on the directory building
1803  *
1804  */
1805 static UINT ACTION_CostFinalize(MSIPACKAGE *package)
1806 {
1807     static const WCHAR ExecSeqQuery[] =
1808         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1809          '`','D','i','r','e','c','t','o','r','y','`',0};
1810     static const WCHAR ConditionQuery[] =
1811         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1812          '`','C','o','n','d','i','t','i','o','n','`',0};
1813     static const WCHAR szCosting[] =
1814         {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
1815     static const WCHAR szlevel[] =
1816         {'I','N','S','T','A','L','L','L','E','V','E','L',0};
1817     static const WCHAR szOne[] = { '1', 0 };
1818     MSICOMPONENT *comp;
1819     MSIFILE *file;
1820     UINT rc;
1821     MSIQUERY * view;
1822     LPWSTR level;
1823
1824     if ( 1 == msi_get_property_int( package, szCosting, 0 ) )
1825         return ERROR_SUCCESS;
1826     
1827     TRACE("Building Directory properties\n");
1828
1829     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
1830     if (rc == ERROR_SUCCESS)
1831     {
1832         rc = MSI_IterateRecords(view, NULL, ITERATE_CostFinalizeDirectories,
1833                         package);
1834         msiobj_release(&view->hdr);
1835     }
1836
1837     TRACE("File calculations\n");
1838
1839     LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
1840     {
1841         MSICOMPONENT* comp = file->Component;
1842         LPWSTR p;
1843
1844         if (!comp)
1845             continue;
1846
1847         /* calculate target */
1848         p = resolve_folder(package, comp->Directory, FALSE, FALSE, NULL);
1849
1850         msi_free(file->TargetPath);
1851
1852         TRACE("file %s is named %s\n",
1853                debugstr_w(file->File),debugstr_w(file->FileName));       
1854
1855         file->TargetPath = build_directory_name(2, p, file->FileName);
1856
1857         msi_free(p);
1858
1859         TRACE("file %s resolves to %s\n",
1860                debugstr_w(file->File),debugstr_w(file->TargetPath));       
1861
1862         if (GetFileAttributesW(file->TargetPath) == INVALID_FILE_ATTRIBUTES)
1863         {
1864             file->state = msifs_missing;
1865             comp->Cost += file->FileSize;
1866             continue;
1867         }
1868
1869         if (file->Version)
1870         {
1871             DWORD handle;
1872             DWORD versize;
1873             UINT sz;
1874             LPVOID version;
1875             static WCHAR name[] = {'\\',0};
1876             static const WCHAR name_fmt[] = 
1877                 {'%','u','.','%','u','.','%','u','.','%','u',0};
1878             WCHAR filever[0x100];
1879             VS_FIXEDFILEINFO *lpVer;
1880
1881             TRACE("Version comparison..\n");
1882             versize = GetFileVersionInfoSizeW(file->TargetPath,&handle);
1883             version = msi_alloc(versize);
1884             GetFileVersionInfoW(file->TargetPath, 0, versize, version);
1885
1886             VerQueryValueW(version, name, (LPVOID*)&lpVer, &sz);
1887
1888             sprintfW(filever,name_fmt,
1889                 HIWORD(lpVer->dwFileVersionMS),
1890                 LOWORD(lpVer->dwFileVersionMS),
1891                 HIWORD(lpVer->dwFileVersionLS),
1892                 LOWORD(lpVer->dwFileVersionLS));
1893
1894             TRACE("new %s old %s\n", debugstr_w(file->Version),
1895                   debugstr_w(filever));
1896             if (strcmpiW(filever,file->Version)<0)
1897             {
1898                 file->state = msifs_overwrite;
1899                 /* FIXME: cost should be diff in size */
1900                 comp->Cost += file->FileSize;
1901             }
1902             else
1903                 file->state = msifs_present;
1904             msi_free(version);
1905         }
1906         else
1907             file->state = msifs_present;
1908     }
1909
1910     TRACE("Evaluating Condition Table\n");
1911
1912     rc = MSI_DatabaseOpenViewW(package->db, ConditionQuery, &view);
1913     if (rc == ERROR_SUCCESS)
1914     {
1915         rc = MSI_IterateRecords(view, NULL, ITERATE_CostFinalizeConditions,
1916                     package);
1917         msiobj_release(&view->hdr);
1918     }
1919
1920     TRACE("Enabling or Disabling Components\n");
1921     LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
1922     {
1923         if (comp->Condition)
1924         {
1925             if (MSI_EvaluateConditionW(package,
1926                 comp->Condition) == MSICONDITION_FALSE)
1927             {
1928                 TRACE("Disabling component %s\n", debugstr_w(comp->Component));
1929                 comp->Enabled = FALSE;
1930             }
1931         }
1932     }
1933
1934     MSI_SetPropertyW(package,szCosting,szOne);
1935     /* set default run level if not set */
1936     level = msi_dup_property( package, szlevel );
1937     if (!level)
1938         MSI_SetPropertyW(package,szlevel, szOne);
1939     msi_free(level);
1940
1941     ACTION_UpdateInstallStates(package);
1942
1943     return SetFeatureStates(package);
1944 }
1945
1946 /* OK this value is "interpreted" and then formatted based on the 
1947    first few characters */
1948 static LPSTR parse_value(MSIPACKAGE *package, LPCWSTR value, DWORD *type, 
1949                          DWORD *size)
1950 {
1951     LPSTR data = NULL;
1952     if (value[0]=='#' && value[1]!='#' && value[1]!='%')
1953     {
1954         if (value[1]=='x')
1955         {
1956             LPWSTR ptr;
1957             CHAR byte[5];
1958             LPWSTR deformated = NULL;
1959             int count;
1960
1961             deformat_string(package, &value[2], &deformated);
1962
1963             /* binary value type */
1964             ptr = deformated;
1965             *type = REG_BINARY;
1966             if (strlenW(ptr)%2)
1967                 *size = (strlenW(ptr)/2)+1;
1968             else
1969                 *size = strlenW(ptr)/2;
1970
1971             data = msi_alloc(*size);
1972
1973             byte[0] = '0'; 
1974             byte[1] = 'x'; 
1975             byte[4] = 0; 
1976             count = 0;
1977             /* if uneven pad with a zero in front */
1978             if (strlenW(ptr)%2)
1979             {
1980                 byte[2]= '0';
1981                 byte[3]= *ptr;
1982                 ptr++;
1983                 data[count] = (BYTE)strtol(byte,NULL,0);
1984                 count ++;
1985                 TRACE("Uneven byte count\n");
1986             }
1987             while (*ptr)
1988             {
1989                 byte[2]= *ptr;
1990                 ptr++;
1991                 byte[3]= *ptr;
1992                 ptr++;
1993                 data[count] = (BYTE)strtol(byte,NULL,0);
1994                 count ++;
1995             }
1996             msi_free(deformated);
1997
1998             TRACE("Data %li bytes(%i)\n",*size,count);
1999         }
2000         else
2001         {
2002             LPWSTR deformated;
2003             LPWSTR p;
2004             DWORD d = 0;
2005             deformat_string(package, &value[1], &deformated);
2006
2007             *type=REG_DWORD; 
2008             *size = sizeof(DWORD);
2009             data = msi_alloc(*size);
2010             p = deformated;
2011             if (*p == '-')
2012                 p++;
2013             while (*p)
2014             {
2015                 if ( (*p < '0') || (*p > '9') )
2016                     break;
2017                 d *= 10;
2018                 d += (*p - '0');
2019                 p++;
2020             }
2021             if (deformated[0] == '-')
2022                 d = -d;
2023             *(LPDWORD)data = d;
2024             TRACE("DWORD %li\n",*(LPDWORD)data);
2025
2026             msi_free(deformated);
2027         }
2028     }
2029     else
2030     {
2031         static const WCHAR szMulti[] = {'[','~',']',0};
2032         LPCWSTR ptr;
2033         *type=REG_SZ;
2034
2035         if (value[0]=='#')
2036         {
2037             if (value[1]=='%')
2038             {
2039                 ptr = &value[2];
2040                 *type=REG_EXPAND_SZ;
2041             }
2042             else
2043                 ptr = &value[1];
2044          }
2045          else
2046             ptr=value;
2047
2048         if (strstrW(value,szMulti))
2049             *type = REG_MULTI_SZ;
2050
2051         *size = deformat_string(package, ptr,(LPWSTR*)&data);
2052     }
2053     return data;
2054 }
2055
2056 static UINT ITERATE_WriteRegistryValues(MSIRECORD *row, LPVOID param)
2057 {
2058     MSIPACKAGE *package = (MSIPACKAGE*)param;
2059     static const WCHAR szHCR[] = 
2060         {'H','K','E','Y','_','C','L','A','S','S','E','S','_',
2061          'R','O','O','T','\\',0};
2062     static const WCHAR szHCU[] =
2063         {'H','K','E','Y','_','C','U','R','R','E','N','T','_',
2064          'U','S','E','R','\\',0};
2065     static const WCHAR szHLM[] =
2066         {'H','K','E','Y','_','L','O','C','A','L','_',
2067          'M','A','C','H','I','N','E','\\',0};
2068     static const WCHAR szHU[] =
2069         {'H','K','E','Y','_','U','S','E','R','S','\\',0};
2070
2071     LPSTR value_data = NULL;
2072     HKEY  root_key, hkey;
2073     DWORD type,size;
2074     LPWSTR  deformated;
2075     LPCWSTR szRoot, component, name, key, value;
2076     MSICOMPONENT *comp;
2077     MSIRECORD * uirow;
2078     LPWSTR uikey;
2079     INT   root;
2080     BOOL check_first = FALSE;
2081     UINT rc;
2082
2083     ui_progress(package,2,0,0,0);
2084
2085     value = NULL;
2086     key = NULL;
2087     uikey = NULL;
2088     name = NULL;
2089
2090     component = MSI_RecordGetString(row, 6);
2091     comp = get_loaded_component(package,component);
2092     if (!comp)
2093         return ERROR_SUCCESS;
2094
2095     if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
2096     {
2097         TRACE("Skipping write due to disabled component %s\n",
2098                         debugstr_w(component));
2099
2100         comp->Action = comp->Installed;
2101
2102         return ERROR_SUCCESS;
2103     }
2104
2105     comp->Action = INSTALLSTATE_LOCAL;
2106
2107     name = MSI_RecordGetString(row, 4);
2108     if( MSI_RecordIsNull(row,5) && name )
2109     {
2110         /* null values can have special meanings */
2111         if (name[0]=='-' && name[1] == 0)
2112                 return ERROR_SUCCESS;
2113         else if ((name[0]=='+' && name[1] == 0) || 
2114                  (name[0] == '*' && name[1] == 0))
2115                 name = NULL;
2116         check_first = TRUE;
2117     }
2118
2119     root = MSI_RecordGetInteger(row,2);
2120     key = MSI_RecordGetString(row, 3);
2121
2122     /* get the root key */
2123     switch (root)
2124     {
2125         case -1: 
2126             {
2127                 static const WCHAR szALLUSER[] = {'A','L','L','U','S','E','R','S',0};
2128                 LPWSTR all_users = msi_dup_property( package, szALLUSER );
2129                 if (all_users && all_users[0] == '1')
2130                 {
2131                     root_key = HKEY_LOCAL_MACHINE;
2132                     szRoot = szHLM;
2133                 }
2134                 else
2135                 {
2136                     root_key = HKEY_CURRENT_USER;
2137                     szRoot = szHCU;
2138                 }
2139                 msi_free(all_users);
2140             }
2141                  break;
2142         case 0:  root_key = HKEY_CLASSES_ROOT; 
2143                  szRoot = szHCR;
2144                  break;
2145         case 1:  root_key = HKEY_CURRENT_USER;
2146                  szRoot = szHCU;
2147                  break;
2148         case 2:  root_key = HKEY_LOCAL_MACHINE;
2149                  szRoot = szHLM;
2150                  break;
2151         case 3:  root_key = HKEY_USERS; 
2152                  szRoot = szHU;
2153                  break;
2154         default:
2155                  ERR("Unknown root %i\n",root);
2156                  root_key=NULL;
2157                  szRoot = NULL;
2158                  break;
2159     }
2160     if (!root_key)
2161         return ERROR_SUCCESS;
2162
2163     deformat_string(package, key , &deformated);
2164     size = strlenW(deformated) + strlenW(szRoot) + 1;
2165     uikey = msi_alloc(size*sizeof(WCHAR));
2166     strcpyW(uikey,szRoot);
2167     strcatW(uikey,deformated);
2168
2169     if (RegCreateKeyW( root_key, deformated, &hkey))
2170     {
2171         ERR("Could not create key %s\n",debugstr_w(deformated));
2172         msi_free(deformated);
2173         msi_free(uikey);
2174         return ERROR_SUCCESS;
2175     }
2176     msi_free(deformated);
2177
2178     value = MSI_RecordGetString(row,5);
2179     if (value)
2180         value_data = parse_value(package, value, &type, &size); 
2181     else
2182     {
2183         static const WCHAR szEmpty[] = {0};
2184         value_data = (LPSTR)strdupW(szEmpty);
2185         size = 0;
2186         type = REG_SZ;
2187     }
2188
2189     deformat_string(package, name, &deformated);
2190
2191     /* get the double nulls to terminate SZ_MULTI */
2192     if (type == REG_MULTI_SZ)
2193         size +=sizeof(WCHAR);
2194
2195     if (!check_first)
2196     {
2197         TRACE("Setting value %s of %s\n",debugstr_w(deformated),
2198                         debugstr_w(uikey));
2199         RegSetValueExW(hkey, deformated, 0, type, (LPBYTE)value_data, size);
2200     }
2201     else
2202     {
2203         DWORD sz = 0;
2204         rc = RegQueryValueExW(hkey, deformated, NULL, NULL, NULL, &sz);
2205         if (rc == ERROR_SUCCESS || rc == ERROR_MORE_DATA)
2206         {
2207             TRACE("value %s of %s checked already exists\n",
2208                             debugstr_w(deformated), debugstr_w(uikey));
2209         }
2210         else
2211         {
2212             TRACE("Checked and setting value %s of %s\n",
2213                             debugstr_w(deformated), debugstr_w(uikey));
2214             if (deformated || size)
2215                 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE) value_data, size);
2216         }
2217     }
2218     RegCloseKey(hkey);
2219
2220     uirow = MSI_CreateRecord(3);
2221     MSI_RecordSetStringW(uirow,2,deformated);
2222     MSI_RecordSetStringW(uirow,1,uikey);
2223
2224     if (type == REG_SZ)
2225         MSI_RecordSetStringW(uirow,3,(LPWSTR)value_data);
2226     else
2227         MSI_RecordSetStringW(uirow,3,value);
2228
2229     ui_actiondata(package,szWriteRegistryValues,uirow);
2230     msiobj_release( &uirow->hdr );
2231
2232     msi_free(value_data);
2233     msi_free(deformated);
2234     msi_free(uikey);
2235
2236     return ERROR_SUCCESS;
2237 }
2238
2239 static UINT ACTION_WriteRegistryValues(MSIPACKAGE *package)
2240 {
2241     UINT rc;
2242     MSIQUERY * view;
2243     static const WCHAR ExecSeqQuery[] =
2244         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2245          '`','R','e','g','i','s','t','r','y','`',0 };
2246
2247     if (!package)
2248         return ERROR_INVALID_HANDLE;
2249
2250     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2251     if (rc != ERROR_SUCCESS)
2252         return ERROR_SUCCESS;
2253
2254     /* increment progress bar each time action data is sent */
2255     ui_progress(package,1,REG_PROGRESS_VALUE,1,0);
2256
2257     rc = MSI_IterateRecords(view, NULL, ITERATE_WriteRegistryValues, package);
2258
2259     msiobj_release(&view->hdr);
2260     return rc;
2261 }
2262
2263 static UINT ACTION_InstallInitialize(MSIPACKAGE *package)
2264 {
2265     package->script->CurrentlyScripting = TRUE;
2266
2267     return ERROR_SUCCESS;
2268 }
2269
2270
2271 static UINT ACTION_InstallValidate(MSIPACKAGE *package)
2272 {
2273     MSICOMPONENT *comp;
2274     DWORD progress = 0;
2275     DWORD total = 0;
2276     static const WCHAR q1[]=
2277         {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
2278          '`','R','e','g','i','s','t','r','y','`',0};
2279     UINT rc;
2280     MSIQUERY * view;
2281     MSIFEATURE *feature;
2282     MSIFILE *file;
2283
2284     TRACE("InstallValidate\n");
2285
2286     rc = MSI_DatabaseOpenViewW(package->db, q1, &view);
2287     if (rc == ERROR_SUCCESS)
2288     {
2289         MSI_IterateRecords( view, &progress, NULL, package );
2290         msiobj_release( &view->hdr );
2291         total += progress * REG_PROGRESS_VALUE;
2292     }
2293
2294     LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2295         total += COMPONENT_PROGRESS_VALUE;
2296
2297     LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2298         total += file->FileSize;
2299
2300     ui_progress(package,0,total,0,0);
2301
2302     LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2303     {
2304         TRACE("Feature: %s; Installed: %i; Action %i; Request %i\n",
2305             debugstr_w(feature->Feature), feature->Installed, feature->Action,
2306             feature->ActionRequest);
2307     }
2308     
2309     return ERROR_SUCCESS;
2310 }
2311
2312 static UINT ITERATE_LaunchConditions(MSIRECORD *row, LPVOID param)
2313 {
2314     MSIPACKAGE* package = (MSIPACKAGE*)param;
2315     LPCWSTR cond = NULL; 
2316     LPCWSTR message = NULL;
2317     static const WCHAR title[]=
2318         {'I','n','s','t','a','l','l',' ','F','a', 'i','l','e','d',0};
2319
2320     cond = MSI_RecordGetString(row,1);
2321
2322     if (MSI_EvaluateConditionW(package,cond) != MSICONDITION_TRUE)
2323     {
2324         LPWSTR deformated;
2325         message = MSI_RecordGetString(row,2);
2326         deformat_string(package,message,&deformated); 
2327         MessageBoxW(NULL,deformated,title,MB_OK);
2328         msi_free(deformated);
2329         return ERROR_FUNCTION_FAILED;
2330     }
2331
2332     return ERROR_SUCCESS;
2333 }
2334
2335 static UINT ACTION_LaunchConditions(MSIPACKAGE *package)
2336 {
2337     UINT rc;
2338     MSIQUERY * view = NULL;
2339     static const WCHAR ExecSeqQuery[] =
2340         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2341          '`','L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','`',0};
2342
2343     TRACE("Checking launch conditions\n");
2344
2345     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2346     if (rc != ERROR_SUCCESS)
2347         return ERROR_SUCCESS;
2348
2349     rc = MSI_IterateRecords(view, NULL, ITERATE_LaunchConditions, package);
2350     msiobj_release(&view->hdr);
2351
2352     return rc;
2353 }
2354
2355 static LPWSTR resolve_keypath( MSIPACKAGE* package, MSICOMPONENT *cmp )
2356 {
2357
2358     if (!cmp->KeyPath)
2359         return resolve_folder(package,cmp->Directory,FALSE,FALSE,NULL);
2360
2361     if (cmp->Attributes & msidbComponentAttributesRegistryKeyPath)
2362     {
2363         MSIRECORD * row = 0;
2364         UINT root,len;
2365         LPWSTR deformated,buffer,deformated_name;
2366         LPCWSTR key,name;
2367         static const WCHAR ExecSeqQuery[] =
2368             {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2369              '`','R','e','g','i','s','t','r','y','`',' ',
2370              'W','H','E','R','E',' ', '`','R','e','g','i','s','t','r','y','`',
2371              ' ','=',' ' ,'\'','%','s','\'',0 };
2372         static const WCHAR fmt[]={'%','0','2','i',':','\\','%','s','\\',0};
2373         static const WCHAR fmt2[]=
2374             {'%','0','2','i',':','\\','%','s','\\','%','s',0};
2375
2376         row = MSI_QueryGetRecord(package->db, ExecSeqQuery,cmp->KeyPath);
2377         if (!row)
2378             return NULL;
2379
2380         root = MSI_RecordGetInteger(row,2);
2381         key = MSI_RecordGetString(row, 3);
2382         name = MSI_RecordGetString(row, 4);
2383         deformat_string(package, key , &deformated);
2384         deformat_string(package, name, &deformated_name);
2385
2386         len = strlenW(deformated) + 6;
2387         if (deformated_name)
2388             len+=strlenW(deformated_name);
2389
2390         buffer = msi_alloc( len *sizeof(WCHAR));
2391
2392         if (deformated_name)
2393             sprintfW(buffer,fmt2,root,deformated,deformated_name);
2394         else
2395             sprintfW(buffer,fmt,root,deformated);
2396
2397         msi_free(deformated);
2398         msi_free(deformated_name);
2399         msiobj_release(&row->hdr);
2400
2401         return buffer;
2402     }
2403     else if (cmp->Attributes & msidbComponentAttributesODBCDataSource)
2404     {
2405         FIXME("UNIMPLEMENTED keypath as ODBC Source\n");
2406         return NULL;
2407     }
2408     else
2409     {
2410         MSIFILE *file = get_loaded_file( package, cmp->KeyPath );
2411
2412         if (file)
2413             return strdupW( file->TargetPath );
2414     }
2415     return NULL;
2416 }
2417
2418 static HKEY openSharedDLLsKey(void)
2419 {
2420     HKEY hkey=0;
2421     static const WCHAR path[] =
2422         {'S','o','f','t','w','a','r','e','\\',
2423          'M','i','c','r','o','s','o','f','t','\\',
2424          'W','i','n','d','o','w','s','\\',
2425          'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
2426          'S','h','a','r','e','d','D','L','L','s',0};
2427
2428     RegCreateKeyW(HKEY_LOCAL_MACHINE,path,&hkey);
2429     return hkey;
2430 }
2431
2432 static UINT ACTION_GetSharedDLLsCount(LPCWSTR dll)
2433 {
2434     HKEY hkey;
2435     DWORD count=0;
2436     DWORD type;
2437     DWORD sz = sizeof(count);
2438     DWORD rc;
2439     
2440     hkey = openSharedDLLsKey();
2441     rc = RegQueryValueExW(hkey, dll, NULL, &type, (LPBYTE)&count, &sz);
2442     if (rc != ERROR_SUCCESS)
2443         count = 0;
2444     RegCloseKey(hkey);
2445     return count;
2446 }
2447
2448 static UINT ACTION_WriteSharedDLLsCount(LPCWSTR path, UINT count)
2449 {
2450     HKEY hkey;
2451
2452     hkey = openSharedDLLsKey();
2453     if (count > 0)
2454         msi_reg_set_val_dword( hkey, path, count );
2455     else
2456         RegDeleteValueW(hkey,path);
2457     RegCloseKey(hkey);
2458     return count;
2459 }
2460
2461 /*
2462  * Return TRUE if the count should be written out and FALSE if not
2463  */
2464 static void ACTION_RefCountComponent( MSIPACKAGE* package, MSICOMPONENT *comp )
2465 {
2466     MSIFEATURE *feature;
2467     INT count = 0;
2468     BOOL write = FALSE;
2469
2470     /* only refcount DLLs */
2471     if (comp->KeyPath == NULL || 
2472         comp->Attributes & msidbComponentAttributesRegistryKeyPath || 
2473         comp->Attributes & msidbComponentAttributesODBCDataSource)
2474         write = FALSE;
2475     else
2476     {
2477         count = ACTION_GetSharedDLLsCount( comp->FullKeypath);
2478         write = (count > 0);
2479
2480         if (comp->Attributes & msidbComponentAttributesSharedDllRefCount)
2481             write = TRUE;
2482     }
2483
2484     /* increment counts */
2485     LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2486     {
2487         ComponentList *cl;
2488
2489         if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_LOCAL ))
2490             continue;
2491
2492         LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
2493         {
2494             if ( cl->component == comp )
2495                 count++;
2496         }
2497     }
2498
2499     /* decrement counts */
2500     LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2501     {
2502         ComponentList *cl;
2503
2504         if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_ABSENT ))
2505             continue;
2506
2507         LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
2508         {
2509             if ( cl->component == comp )
2510                 count--;
2511         }
2512     }
2513
2514     /* ref count all the files in the component */
2515     if (write)
2516     {
2517         MSIFILE *file;
2518
2519         LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2520         {
2521             if (file->Component == comp)
2522                 ACTION_WriteSharedDLLsCount( file->TargetPath, count );
2523         }
2524     }
2525     
2526     /* add a count for permenent */
2527     if (comp->Attributes & msidbComponentAttributesPermanent)
2528         count ++;
2529     
2530     comp->RefCount = count;
2531
2532     if (write)
2533         ACTION_WriteSharedDLLsCount( comp->FullKeypath, comp->RefCount );
2534 }
2535
2536 /*
2537  * Ok further analysis makes me think that this work is
2538  * actually done in the PublishComponents and PublishFeatures
2539  * step, and not here.  It appears like the keypath and all that is
2540  * resolved in this step, however actually written in the Publish steps.
2541  * But we will leave it here for now because it is unclear
2542  */
2543 static UINT ACTION_ProcessComponents(MSIPACKAGE *package)
2544 {
2545     WCHAR squished_pc[GUID_SIZE];
2546     WCHAR squished_cc[GUID_SIZE];
2547     UINT rc;
2548     MSICOMPONENT *comp;
2549     HKEY hkey=0,hkey2=0;
2550
2551     /* writes the Component and Features values to the registry */
2552
2553     rc = MSIREG_OpenComponents(&hkey);
2554     if (rc != ERROR_SUCCESS)
2555         return rc;
2556
2557     squash_guid(package->ProductCode,squished_pc);
2558     ui_progress(package,1,COMPONENT_PROGRESS_VALUE,1,0);
2559
2560     LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2561     {
2562         MSIRECORD * uirow;
2563
2564         ui_progress(package,2,0,0,0);
2565         if (!comp->ComponentId)
2566             continue;
2567
2568         squash_guid(comp->ComponentId,squished_cc);
2569            
2570         msi_free(comp->FullKeypath);
2571         comp->FullKeypath = resolve_keypath( package, comp );
2572
2573         /* do the refcounting */
2574         ACTION_RefCountComponent( package, comp );
2575
2576         TRACE("Component %s (%s), Keypath=%s, RefCount=%i\n",
2577                             debugstr_w(comp->Component),
2578                             debugstr_w(squished_cc),
2579                             debugstr_w(comp->FullKeypath),
2580                             comp->RefCount);
2581         /*
2582          * Write the keypath out if the component is to be registered
2583          * and delete the key if the component is to be deregistered
2584          */
2585         if (ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
2586         {
2587             rc = RegCreateKeyW(hkey,squished_cc,&hkey2);
2588             if (rc != ERROR_SUCCESS)
2589                 continue;
2590
2591             if (!comp->FullKeypath)
2592                 continue;
2593
2594             msi_reg_set_val_str( hkey2, squished_pc, comp->FullKeypath );
2595
2596             if (comp->Attributes & msidbComponentAttributesPermanent)
2597             {
2598                 static const WCHAR szPermKey[] =
2599                     { '0','0','0','0','0','0','0','0','0','0','0','0',
2600                       '0','0','0','0','0','0','0','0','0','0','0','0',
2601                       '0','0','0','0','0','0','0','0',0 };
2602
2603                 msi_reg_set_val_str( hkey2, szPermKey, comp->FullKeypath );
2604             }
2605
2606             RegCloseKey(hkey2);
2607
2608             /* UI stuff */
2609             uirow = MSI_CreateRecord(3);
2610             MSI_RecordSetStringW(uirow,1,package->ProductCode);
2611             MSI_RecordSetStringW(uirow,2,comp->ComponentId);
2612             MSI_RecordSetStringW(uirow,3,comp->FullKeypath);
2613             ui_actiondata(package,szProcessComponents,uirow);
2614             msiobj_release( &uirow->hdr );
2615         }
2616         else if (ACTION_VerifyComponentForAction( comp, INSTALLSTATE_ABSENT))
2617         {
2618             DWORD res;
2619
2620             rc = RegOpenKeyW(hkey,squished_cc,&hkey2);
2621             if (rc != ERROR_SUCCESS)
2622                 continue;
2623
2624             RegDeleteValueW(hkey2,squished_pc);
2625
2626             /* if the key is empty delete it */
2627             res = RegEnumKeyExW(hkey2,0,NULL,0,0,NULL,0,NULL);
2628             RegCloseKey(hkey2);
2629             if (res == ERROR_NO_MORE_ITEMS)
2630                 RegDeleteKeyW(hkey,squished_cc);
2631
2632             /* UI stuff */
2633             uirow = MSI_CreateRecord(2);
2634             MSI_RecordSetStringW(uirow,1,package->ProductCode);
2635             MSI_RecordSetStringW(uirow,2,comp->ComponentId);
2636             ui_actiondata(package,szProcessComponents,uirow);
2637             msiobj_release( &uirow->hdr );
2638         }
2639     } 
2640     RegCloseKey(hkey);
2641     return rc;
2642 }
2643
2644 typedef struct {
2645     CLSID       clsid;
2646     LPWSTR      source;
2647
2648     LPWSTR      path;
2649     ITypeLib    *ptLib;
2650 } typelib_struct;
2651
2652 static BOOL CALLBACK Typelib_EnumResNameProc( HMODULE hModule, LPCWSTR lpszType, 
2653                                        LPWSTR lpszName, LONG_PTR lParam)
2654 {
2655     TLIBATTR *attr;
2656     typelib_struct *tl_struct = (typelib_struct*) lParam;
2657     static const WCHAR fmt[] = {'%','s','\\','%','i',0};
2658     int sz; 
2659     HRESULT res;
2660
2661     if (!IS_INTRESOURCE(lpszName))
2662     {
2663         ERR("Not Int Resource Name %s\n",debugstr_w(lpszName));
2664         return TRUE;
2665     }
2666
2667     sz = strlenW(tl_struct->source)+4;
2668     sz *= sizeof(WCHAR);
2669
2670     if ((INT)lpszName == 1)
2671         tl_struct->path = strdupW(tl_struct->source);
2672     else
2673     {
2674         tl_struct->path = msi_alloc(sz);
2675         sprintfW(tl_struct->path,fmt,tl_struct->source, lpszName);
2676     }
2677
2678     TRACE("trying %s\n", debugstr_w(tl_struct->path));
2679     res = LoadTypeLib(tl_struct->path,&tl_struct->ptLib);
2680     if (!SUCCEEDED(res))
2681     {
2682         msi_free(tl_struct->path);
2683         tl_struct->path = NULL;
2684
2685         return TRUE;
2686     }
2687
2688     ITypeLib_GetLibAttr(tl_struct->ptLib, &attr);
2689     if (IsEqualGUID(&(tl_struct->clsid),&(attr->guid)))
2690     {
2691         ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
2692         return FALSE;
2693     }
2694
2695     msi_free(tl_struct->path);
2696     tl_struct->path = NULL;
2697
2698     ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
2699     ITypeLib_Release(tl_struct->ptLib);
2700
2701     return TRUE;
2702 }
2703
2704 static UINT ITERATE_RegisterTypeLibraries(MSIRECORD *row, LPVOID param)
2705 {
2706     MSIPACKAGE* package = (MSIPACKAGE*)param;
2707     LPCWSTR component;
2708     MSICOMPONENT *comp;
2709     MSIFILE *file;
2710     typelib_struct tl_struct;
2711     HMODULE module;
2712     static const WCHAR szTYPELIB[] = {'T','Y','P','E','L','I','B',0};
2713
2714     component = MSI_RecordGetString(row,3);
2715     comp = get_loaded_component(package,component);
2716     if (!comp)
2717         return ERROR_SUCCESS;
2718
2719     if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
2720     {
2721         TRACE("Skipping typelib reg due to disabled component\n");
2722
2723         comp->Action = comp->Installed;
2724
2725         return ERROR_SUCCESS;
2726     }
2727
2728     comp->Action = INSTALLSTATE_LOCAL;
2729
2730     file = get_loaded_file( package, comp->KeyPath ); 
2731     if (!file)
2732         return ERROR_SUCCESS;
2733
2734     module = LoadLibraryExW( file->TargetPath, NULL, LOAD_LIBRARY_AS_DATAFILE );
2735     if (module)
2736     {
2737         LPCWSTR guid;
2738         guid = MSI_RecordGetString(row,1);
2739         CLSIDFromString((LPWSTR)guid, &tl_struct.clsid);
2740         tl_struct.source = strdupW( file->TargetPath );
2741         tl_struct.path = NULL;
2742
2743         EnumResourceNamesW(module, szTYPELIB, Typelib_EnumResNameProc,
2744                         (LONG_PTR)&tl_struct);
2745
2746         if (tl_struct.path)
2747         {
2748             LPWSTR help = NULL;
2749             LPCWSTR helpid;
2750             HRESULT res;
2751
2752             helpid = MSI_RecordGetString(row,6);
2753
2754             if (helpid)
2755                 help = resolve_folder(package,helpid,FALSE,FALSE,NULL);
2756             res = RegisterTypeLib(tl_struct.ptLib,tl_struct.path,help);
2757             msi_free(help);
2758
2759             if (!SUCCEEDED(res))
2760                 ERR("Failed to register type library %s\n",
2761                         debugstr_w(tl_struct.path));
2762             else
2763             {
2764                 ui_actiondata(package,szRegisterTypeLibraries,row);
2765
2766                 TRACE("Registered %s\n", debugstr_w(tl_struct.path));
2767             }
2768
2769             ITypeLib_Release(tl_struct.ptLib);
2770             msi_free(tl_struct.path);
2771         }
2772         else
2773             ERR("Failed to load type library %s\n",
2774                     debugstr_w(tl_struct.source));
2775
2776         FreeLibrary(module);
2777         msi_free(tl_struct.source);
2778     }
2779     else
2780         ERR("Could not load file! %s\n", debugstr_w(file->TargetPath));
2781
2782     return ERROR_SUCCESS;
2783 }
2784
2785 static UINT ACTION_RegisterTypeLibraries(MSIPACKAGE *package)
2786 {
2787     /* 
2788      * OK this is a bit confusing.. I am given a _Component key and I believe
2789      * that the file that is being registered as a type library is the "key file
2790      * of that component" which I interpret to mean "The file in the KeyPath of
2791      * that component".
2792      */
2793     UINT rc;
2794     MSIQUERY * view;
2795     static const WCHAR Query[] =
2796         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2797          '`','T','y','p','e','L','i','b','`',0};
2798
2799     rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
2800     if (rc != ERROR_SUCCESS)
2801         return ERROR_SUCCESS;
2802
2803     rc = MSI_IterateRecords(view, NULL, ITERATE_RegisterTypeLibraries, package);
2804     msiobj_release(&view->hdr);
2805     return rc;
2806 }
2807
2808 static UINT ITERATE_CreateShortcuts(MSIRECORD *row, LPVOID param)
2809 {
2810     MSIPACKAGE *package = (MSIPACKAGE*)param;
2811     LPWSTR target_file, target_folder, filename;
2812     LPCWSTR buffer, extension;
2813     MSICOMPONENT *comp;
2814     static const WCHAR szlnk[]={'.','l','n','k',0};
2815     IShellLinkW *sl;
2816     IPersistFile *pf;
2817     HRESULT res;
2818
2819     buffer = MSI_RecordGetString(row,4);
2820     comp = get_loaded_component(package,buffer);
2821     if (!comp)
2822         return ERROR_SUCCESS;
2823
2824     if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL ))
2825     {
2826         TRACE("Skipping shortcut creation due to disabled component\n");
2827
2828         comp->Action = comp->Installed;
2829
2830         return ERROR_SUCCESS;
2831     }
2832
2833     comp->Action = INSTALLSTATE_LOCAL;
2834
2835     ui_actiondata(package,szCreateShortcuts,row);
2836
2837     res = CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
2838                     &IID_IShellLinkW, (LPVOID *) &sl );
2839
2840     if (FAILED(res))
2841     {
2842         ERR("Is IID_IShellLink\n");
2843         return ERROR_SUCCESS;
2844     }
2845
2846     res = IShellLinkW_QueryInterface( sl, &IID_IPersistFile,(LPVOID*) &pf );
2847     if( FAILED( res ) )
2848     {
2849         ERR("Is IID_IPersistFile\n");
2850         return ERROR_SUCCESS;
2851     }
2852
2853     buffer = MSI_RecordGetString(row,2);
2854     target_folder = resolve_folder(package, buffer,FALSE,FALSE,NULL);
2855
2856     /* may be needed because of a bug somehwere else */
2857     create_full_pathW(target_folder);
2858
2859     filename = msi_dup_record_field( row, 3 );
2860     reduce_to_longfilename(filename);
2861
2862     extension = strchrW(filename,'.');
2863     if (!extension || strcmpiW(extension,szlnk))
2864     {
2865         int len = strlenW(filename);
2866         filename = msi_realloc(filename, len * sizeof(WCHAR) + sizeof(szlnk));
2867         memcpy(filename + len, szlnk, sizeof(szlnk));
2868     }
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             buf[0] = 0;
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_zero( ttRecord.uStringLength + 1 + strlen(tt) );
3917                     ReadFile(handle, buf, ttRecord.uStringLength, &dwRead, NULL);
3918                     if (strlen(buf) > 0)
3919                     {
3920                         strcat(buf,tt);
3921                         ret = strdupAtoW(buf);
3922                         msi_free(buf);
3923                         break;
3924                     }
3925
3926                     msi_free(buf);
3927                     SetFilePointer(handle,nPos, NULL, FILE_BEGIN);
3928                 }
3929             }
3930         }
3931         CloseHandle(handle);
3932     }
3933     else
3934         ERR("Unable to open font file %s\n", debugstr_w(filename));
3935
3936     TRACE("Returning fontname %s\n",debugstr_w(ret));
3937     return ret;
3938 }
3939
3940 static UINT ITERATE_RegisterFonts(MSIRECORD *row, LPVOID param)
3941 {
3942     MSIPACKAGE *package = (MSIPACKAGE*)param;
3943     LPWSTR name;
3944     LPCWSTR filename;
3945     MSIFILE *file;
3946     static const WCHAR regfont1[] =
3947         {'S','o','f','t','w','a','r','e','\\',
3948          'M','i','c','r','o','s','o','f','t','\\',
3949          'W','i','n','d','o','w','s',' ','N','T','\\',
3950          'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
3951          'F','o','n','t','s',0};
3952     static const WCHAR regfont2[] =
3953         {'S','o','f','t','w','a','r','e','\\',
3954          'M','i','c','r','o','s','o','f','t','\\',
3955          'W','i','n','d','o','w','s','\\',
3956          'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
3957          'F','o','n','t','s',0};
3958     HKEY hkey1;
3959     HKEY hkey2;
3960     MSIRECORD *uirow;
3961     LPWSTR uipath, p;
3962
3963     filename = MSI_RecordGetString( row, 1 );
3964     file = get_loaded_file( package, filename );
3965     if (!file)
3966     {
3967         ERR("Unable to load file\n");
3968         return ERROR_SUCCESS;
3969     }
3970
3971     /* check to make sure that component is installed */
3972     if (!ACTION_VerifyComponentForAction( file->Component, INSTALLSTATE_LOCAL))
3973     {
3974         TRACE("Skipping: Component not scheduled for install\n");
3975         return ERROR_SUCCESS;
3976     }
3977
3978     RegCreateKeyW(HKEY_LOCAL_MACHINE,regfont1,&hkey1);
3979     RegCreateKeyW(HKEY_LOCAL_MACHINE,regfont2,&hkey2);
3980
3981     if (MSI_RecordIsNull(row,2))
3982         name = load_ttfname_from( file->TargetPath );
3983     else
3984         name = msi_dup_record_field(row,2);
3985
3986     if (name)
3987     {
3988         msi_reg_set_val_str( hkey1, name, file->FileName );
3989         msi_reg_set_val_str( hkey2, name, file->FileName );
3990     }
3991
3992     msi_free(name);
3993     RegCloseKey(hkey1);
3994     RegCloseKey(hkey2);
3995
3996     /* the UI chunk */
3997     uirow = MSI_CreateRecord( 1 );
3998     uipath = strdupW( file->TargetPath );
3999     p = strrchrW(uipath,'\\');
4000     if (p) p++;
4001     else p = uipath;
4002     MSI_RecordSetStringW( uirow, 1, p );
4003     ui_actiondata( package, szRegisterFonts, uirow);
4004     msiobj_release( &uirow->hdr );
4005     msi_free( uipath );
4006     /* FIXME: call ui_progress? */
4007
4008     return ERROR_SUCCESS;
4009 }
4010
4011 static UINT ACTION_RegisterFonts(MSIPACKAGE *package)
4012 {
4013     UINT rc;
4014     MSIQUERY * view;
4015     static const WCHAR ExecSeqQuery[] =
4016         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4017          '`','F','o','n','t','`',0};
4018
4019     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4020     if (rc != ERROR_SUCCESS)
4021     {
4022         TRACE("MSI_DatabaseOpenViewW failed: %d\n", rc);
4023         return ERROR_SUCCESS;
4024     }
4025
4026     MSI_IterateRecords(view, NULL, ITERATE_RegisterFonts, package);
4027     msiobj_release(&view->hdr);
4028
4029     return ERROR_SUCCESS;
4030 }
4031
4032 static UINT ITERATE_PublishComponent(MSIRECORD *rec, LPVOID param)
4033 {
4034     MSIPACKAGE *package = (MSIPACKAGE*)param;
4035     LPCWSTR compgroupid=NULL;
4036     LPCWSTR feature=NULL;
4037     LPCWSTR text = NULL;
4038     LPCWSTR qualifier = NULL;
4039     LPCWSTR component = NULL;
4040     LPWSTR advertise = NULL;
4041     LPWSTR output = NULL;
4042     HKEY hkey;
4043     UINT rc = ERROR_SUCCESS;
4044     MSICOMPONENT *comp;
4045     DWORD sz = 0;
4046     MSIRECORD *uirow;
4047
4048     component = MSI_RecordGetString(rec,3);
4049     comp = get_loaded_component(package,component);
4050
4051     if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL ) && 
4052        !ACTION_VerifyComponentForAction( comp, INSTALLSTATE_SOURCE ) &&
4053        !ACTION_VerifyComponentForAction( comp, INSTALLSTATE_ADVERTISED ))
4054     {
4055         TRACE("Skipping: Component %s not scheduled for install\n",
4056                         debugstr_w(component));
4057
4058         return ERROR_SUCCESS;
4059     }
4060
4061     compgroupid = MSI_RecordGetString(rec,1);
4062     qualifier = MSI_RecordGetString(rec,2);
4063
4064     rc = MSIREG_OpenUserComponentsKey(compgroupid, &hkey, TRUE);
4065     if (rc != ERROR_SUCCESS)
4066         goto end;
4067     
4068     text = MSI_RecordGetString(rec,4);
4069     feature = MSI_RecordGetString(rec,5);
4070   
4071     advertise = create_component_advertise_string(package, comp, feature);
4072
4073     sz = strlenW(advertise);
4074
4075     if (text)
4076         sz += lstrlenW(text);
4077
4078     sz+=3;
4079     sz *= sizeof(WCHAR);
4080            
4081     output = msi_alloc_zero(sz);
4082     strcpyW(output,advertise);
4083     msi_free(advertise);
4084
4085     if (text)
4086         strcatW(output,text);
4087
4088     msi_reg_set_val_multi_str( hkey, qualifier, output );
4089     
4090 end:
4091     RegCloseKey(hkey);
4092     msi_free(output);
4093
4094     /* the UI chunk */
4095     uirow = MSI_CreateRecord( 2 );
4096     MSI_RecordSetStringW( uirow, 1, compgroupid );
4097     MSI_RecordSetStringW( uirow, 2, qualifier);
4098     ui_actiondata( package, szPublishComponents, uirow);
4099     msiobj_release( &uirow->hdr );
4100     /* FIXME: call ui_progress? */
4101
4102     return rc;
4103 }
4104
4105 /*
4106  * At present I am ignorning the advertised components part of this and only
4107  * focusing on the qualified component sets
4108  */
4109 static UINT ACTION_PublishComponents(MSIPACKAGE *package)
4110 {
4111     UINT rc;
4112     MSIQUERY * view;
4113     static const WCHAR ExecSeqQuery[] =
4114         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4115          '`','P','u','b','l','i','s','h',
4116          'C','o','m','p','o','n','e','n','t','`',0};
4117     
4118     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4119     if (rc != ERROR_SUCCESS)
4120         return ERROR_SUCCESS;
4121
4122     rc = MSI_IterateRecords(view, NULL, ITERATE_PublishComponent, package);
4123     msiobj_release(&view->hdr);
4124
4125     return rc;
4126 }
4127
4128 static UINT msi_unimplemented_action_stub( MSIPACKAGE *package,
4129                                            LPCSTR action, LPCWSTR table )
4130 {
4131     static const WCHAR query[] = {
4132         'S','E','L','E','C','T',' ','*',' ',
4133         'F','R','O','M',' ','`','%','s','`',0 };
4134     MSIQUERY *view = NULL;
4135     DWORD count = 0;
4136     UINT r;
4137     
4138     r = MSI_OpenQuery( package->db, &view, query, table );
4139     if (r == ERROR_SUCCESS)
4140     {
4141         r = MSI_IterateRecords(view, &count, NULL, package);
4142         msiobj_release(&view->hdr);
4143     }
4144
4145     if (count)
4146         FIXME("%s -> %lu ignored %s table values\n",
4147               action, count, debugstr_w(table));
4148
4149     return ERROR_SUCCESS;
4150 }
4151
4152 static UINT ACTION_AllocateRegistrySpace( MSIPACKAGE *package )
4153 {
4154     TRACE("%p\n", package);
4155     return ERROR_SUCCESS;
4156 }
4157
4158 static UINT ACTION_RemoveIniValues( MSIPACKAGE *package )
4159 {
4160     static const WCHAR table[] =
4161          {'R','e','m','o','v','e','I','n','i','F','i','l','e',0 };
4162     return msi_unimplemented_action_stub( package, "RemoveIniValues", table );
4163 }
4164
4165 static UINT ACTION_MoveFiles( MSIPACKAGE *package )
4166 {
4167     static const WCHAR table[] = { 'M','o','v','e','F','i','l','e',0 };
4168     return msi_unimplemented_action_stub( package, "MoveFiles", table );
4169 }
4170
4171 static UINT ACTION_PatchFiles( MSIPACKAGE *package )
4172 {
4173     static const WCHAR table[] = { 'P','a','t','c','h',0 };
4174     return msi_unimplemented_action_stub( package, "PatchFiles", table );
4175 }
4176
4177 static UINT ACTION_BindImage( MSIPACKAGE *package )
4178 {
4179     static const WCHAR table[] = { 'B','i','n','d','I','m','a','g','e',0 };
4180     return msi_unimplemented_action_stub( package, "BindImage", table );
4181 }
4182
4183 static UINT ACTION_IsolateComponents( MSIPACKAGE *package )
4184 {
4185     static const WCHAR table[] = {
4186         'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t',0 };
4187     return msi_unimplemented_action_stub( package, "IsolateComponents", table );
4188 }
4189
4190 static UINT ACTION_MigrateFeatureStates( MSIPACKAGE *package )
4191 {
4192     static const WCHAR table[] = { 'U','p','g','r','a','d','e',0 };
4193     return msi_unimplemented_action_stub( package, "MigrateFeatureStates", table );
4194 }
4195
4196 static UINT ACTION_SelfUnregModules( MSIPACKAGE *package )
4197 {
4198     static const WCHAR table[] = { 'S','e','l','f','R','e','g',0 };
4199     return msi_unimplemented_action_stub( package, "SelfUnregModules", table );
4200 }
4201
4202 static UINT ACTION_InstallServices( MSIPACKAGE *package )
4203 {
4204     static const WCHAR table[] = {
4205         'S','e','r','v','i','c','e','I','n','s','t','a','l','l',0 };
4206     return msi_unimplemented_action_stub( package, "InstallServices", table );
4207 }
4208
4209 static UINT ACTION_StartServices( MSIPACKAGE *package )
4210 {
4211     static const WCHAR table[] = {
4212         'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
4213     return msi_unimplemented_action_stub( package, "StartServices", table );
4214 }
4215
4216 static UINT ACTION_StopServices( MSIPACKAGE *package )
4217 {
4218     static const WCHAR table[] = {
4219         'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
4220     return msi_unimplemented_action_stub( package, "StopServices", table );
4221 }
4222
4223 static UINT ACTION_DeleteServices( MSIPACKAGE *package )
4224 {
4225     static const WCHAR table[] = {
4226         'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
4227     return msi_unimplemented_action_stub( package, "DeleteServices", table );
4228 }
4229
4230 static UINT ACTION_WriteEnvironmentStrings( MSIPACKAGE *package )
4231 {
4232     static const WCHAR table[] = {
4233         'E','n','v','i','r','o','n','m','e','n','t',0 };
4234     return msi_unimplemented_action_stub( package, "WriteEnvironmentStrings", table );
4235 }
4236
4237 static UINT ACTION_RemoveEnvironmentStrings( MSIPACKAGE *package )
4238 {
4239     static const WCHAR table[] = {
4240         'E','n','v','i','r','o','n','m','e','n','t',0 };
4241     return msi_unimplemented_action_stub( package, "RemoveEnvironmentStrings", table );
4242 }
4243
4244 static UINT ACTION_MsiPublishAssemblies( MSIPACKAGE *package )
4245 {
4246     static const WCHAR table[] = {
4247         'M','s','i','A','s','s','e','m','b','l','y',0 };
4248     return msi_unimplemented_action_stub( package, "MsiPublishAssemblies", table );
4249 }
4250
4251 static UINT ACTION_MsiUnpublishAssemblies( MSIPACKAGE *package )
4252 {
4253     static const WCHAR table[] = {
4254         'M','s','i','A','s','s','e','m','b','l','y',0 };
4255     return msi_unimplemented_action_stub( package, "MsiUnpublishAssemblies", table );
4256 }
4257
4258 static UINT ACTION_UnregisterFonts( MSIPACKAGE *package )
4259 {
4260     static const WCHAR table[] = { 'F','o','n','t',0 };
4261     return msi_unimplemented_action_stub( package, "UnregisterFonts", table );
4262 }
4263
4264 static UINT ACTION_CCPSearch( MSIPACKAGE *package )
4265 {
4266     static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
4267     return msi_unimplemented_action_stub( package, "CCPSearch", table );
4268 }
4269
4270 static UINT ACTION_RMCCPSearch( MSIPACKAGE *package )
4271 {
4272     static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
4273     return msi_unimplemented_action_stub( package, "RMCCPSearch", table );
4274 }
4275
4276 static UINT ACTION_RegisterComPlus( MSIPACKAGE *package )
4277 {
4278     static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
4279     return msi_unimplemented_action_stub( package, "RegisterComPlus", table );
4280 }
4281
4282 static UINT ACTION_UnregisterComPlus( MSIPACKAGE *package )
4283 {
4284     static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
4285     return msi_unimplemented_action_stub( package, "UnregisterComPlus", table );
4286 }
4287
4288 static struct _actions StandardActions[] = {
4289     { szAllocateRegistrySpace, ACTION_AllocateRegistrySpace },
4290     { szAppSearch, ACTION_AppSearch },
4291     { szBindImage, ACTION_BindImage },
4292     { szCCPSearch, ACTION_CCPSearch},
4293     { szCostFinalize, ACTION_CostFinalize },
4294     { szCostInitialize, ACTION_CostInitialize },
4295     { szCreateFolders, ACTION_CreateFolders },
4296     { szCreateShortcuts, ACTION_CreateShortcuts },
4297     { szDeleteServices, ACTION_DeleteServices },
4298     { szDisableRollback, NULL},
4299     { szDuplicateFiles, ACTION_DuplicateFiles },
4300     { szExecuteAction, ACTION_ExecuteAction },
4301     { szFileCost, ACTION_FileCost },
4302     { szFindRelatedProducts, ACTION_FindRelatedProducts },
4303     { szForceReboot, ACTION_ForceReboot },
4304     { szInstallAdminPackage, NULL},
4305     { szInstallExecute, ACTION_InstallExecute },
4306     { szInstallExecuteAgain, ACTION_InstallExecute },
4307     { szInstallFiles, ACTION_InstallFiles},
4308     { szInstallFinalize, ACTION_InstallFinalize },
4309     { szInstallInitialize, ACTION_InstallInitialize },
4310     { szInstallSFPCatalogFile, NULL},
4311     { szInstallValidate, ACTION_InstallValidate },
4312     { szIsolateComponents, ACTION_IsolateComponents },
4313     { szLaunchConditions, ACTION_LaunchConditions },
4314     { szMigrateFeatureStates, ACTION_MigrateFeatureStates },
4315     { szMoveFiles, ACTION_MoveFiles },
4316     { szMsiPublishAssemblies, ACTION_MsiPublishAssemblies },
4317     { szMsiUnpublishAssemblies, ACTION_MsiUnpublishAssemblies },
4318     { szInstallODBC, NULL},
4319     { szInstallServices, ACTION_InstallServices },
4320     { szPatchFiles, ACTION_PatchFiles },
4321     { szProcessComponents, ACTION_ProcessComponents },
4322     { szPublishComponents, ACTION_PublishComponents },
4323     { szPublishFeatures, ACTION_PublishFeatures },
4324     { szPublishProduct, ACTION_PublishProduct },
4325     { szRegisterClassInfo, ACTION_RegisterClassInfo },
4326     { szRegisterComPlus, ACTION_RegisterComPlus},
4327     { szRegisterExtensionInfo, ACTION_RegisterExtensionInfo },
4328     { szRegisterFonts, ACTION_RegisterFonts },
4329     { szRegisterMIMEInfo, ACTION_RegisterMIMEInfo },
4330     { szRegisterProduct, ACTION_RegisterProduct },
4331     { szRegisterProgIdInfo, ACTION_RegisterProgIdInfo },
4332     { szRegisterTypeLibraries, ACTION_RegisterTypeLibraries },
4333     { szRegisterUser, ACTION_RegisterUser},
4334     { szRemoveDuplicateFiles, NULL},
4335     { szRemoveEnvironmentStrings, ACTION_RemoveEnvironmentStrings },
4336     { szRemoveExistingProducts, NULL},
4337     { szRemoveFiles, ACTION_RemoveFiles},
4338     { szRemoveFolders, NULL},
4339     { szRemoveIniValues, ACTION_RemoveIniValues },
4340     { szRemoveODBC, NULL},
4341     { szRemoveRegistryValues, NULL},
4342     { szRemoveShortcuts, NULL},
4343     { szResolveSource, ACTION_ResolveSource},
4344     { szRMCCPSearch, ACTION_RMCCPSearch},
4345     { szScheduleReboot, NULL},
4346     { szSelfRegModules, ACTION_SelfRegModules },
4347     { szSelfUnregModules, ACTION_SelfUnregModules },
4348     { szSetODBCFolders, NULL},
4349     { szStartServices, ACTION_StartServices },
4350     { szStopServices, ACTION_StopServices },
4351     { szUnpublishComponents, NULL},
4352     { szUnpublishFeatures, NULL},
4353     { szUnregisterClassInfo, NULL},
4354     { szUnregisterComPlus, ACTION_UnregisterComPlus},
4355     { szUnregisterExtensionInfo, NULL},
4356     { szUnregisterFonts, ACTION_UnregisterFonts },
4357     { szUnregisterMIMEInfo, NULL},
4358     { szUnregisterProgIdInfo, NULL},
4359     { szUnregisterTypeLibraries, NULL},
4360     { szValidateProductID, NULL},
4361     { szWriteEnvironmentStrings, ACTION_WriteEnvironmentStrings },
4362     { szWriteIniValues, ACTION_WriteIniValues },
4363     { szWriteRegistryValues, ACTION_WriteRegistryValues},
4364     { NULL, NULL},
4365 };