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