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