riched32/tests: Remove the todo_wine logic where appropriate.
[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 #include <stdarg.h>
22
23 #define COBJMACROS
24
25 #include "windef.h"
26 #include "winbase.h"
27 #include "winerror.h"
28 #include "winreg.h"
29 #include "winsvc.h"
30 #include "odbcinst.h"
31 #include "wine/debug.h"
32 #include "msidefs.h"
33 #include "msipriv.h"
34 #include "winuser.h"
35 #include "shlobj.h"
36 #include "objbase.h"
37 #include "mscoree.h"
38 #include "fusion.h"
39 #include "shlwapi.h"
40 #include "wine/unicode.h"
41 #include "winver.h"
42
43 #define REG_PROGRESS_VALUE 13200
44 #define COMPONENT_PROGRESS_VALUE 24000
45
46 WINE_DEFAULT_DEBUG_CHANNEL(msi);
47
48 /*
49  * Prototypes
50  */
51 static UINT ACTION_ProcessExecSequence(MSIPACKAGE *package, BOOL UIran);
52 static UINT ACTION_ProcessUISequence(MSIPACKAGE *package);
53 static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq, BOOL UI);
54 static BOOL ACTION_HandleStandardAction(MSIPACKAGE *package, LPCWSTR action, UINT* rc, BOOL force);
55
56 /*
57  * consts and values used
58  */
59 static const WCHAR c_colon[] = {'C',':','\\',0};
60
61 static const WCHAR szCreateFolders[] =
62     {'C','r','e','a','t','e','F','o','l','d','e','r','s',0};
63 static const WCHAR szCostFinalize[] =
64     {'C','o','s','t','F','i','n','a','l','i','z','e',0};
65 const WCHAR szInstallFiles[] =
66     {'I','n','s','t','a','l','l','F','i','l','e','s',0};
67 const WCHAR szDuplicateFiles[] =
68     {'D','u','p','l','i','c','a','t','e','F','i','l','e','s',0};
69 static const WCHAR szWriteRegistryValues[] =
70     {'W','r','i','t','e','R','e','g','i','s','t','r','y',
71             'V','a','l','u','e','s',0};
72 static const WCHAR szCostInitialize[] =
73     {'C','o','s','t','I','n','i','t','i','a','l','i','z','e',0};
74 static const WCHAR szFileCost[] = 
75     {'F','i','l','e','C','o','s','t',0};
76 static const WCHAR szInstallInitialize[] = 
77     {'I','n','s','t','a','l','l','I','n','i','t','i','a','l','i','z','e',0};
78 static const WCHAR szInstallValidate[] = 
79     {'I','n','s','t','a','l','l','V','a','l','i','d','a','t','e',0};
80 static const WCHAR szLaunchConditions[] = 
81     {'L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','s',0};
82 static const WCHAR szProcessComponents[] = 
83     {'P','r','o','c','e','s','s','C','o','m','p','o','n','e','n','t','s',0};
84 static const WCHAR szRegisterTypeLibraries[] = 
85     {'R','e','g','i','s','t','e','r','T','y','p','e',
86             'L','i','b','r','a','r','i','e','s',0};
87 const WCHAR szRegisterClassInfo[] = 
88     {'R','e','g','i','s','t','e','r','C','l','a','s','s','I','n','f','o',0};
89 const WCHAR szRegisterProgIdInfo[] = 
90     {'R','e','g','i','s','t','e','r','P','r','o','g','I','d','I','n','f','o',0};
91 static const WCHAR szCreateShortcuts[] = 
92     {'C','r','e','a','t','e','S','h','o','r','t','c','u','t','s',0};
93 static const WCHAR szPublishProduct[] = 
94     {'P','u','b','l','i','s','h','P','r','o','d','u','c','t',0};
95 static const WCHAR szWriteIniValues[] = 
96     {'W','r','i','t','e','I','n','i','V','a','l','u','e','s',0};
97 static const WCHAR szSelfRegModules[] = 
98     {'S','e','l','f','R','e','g','M','o','d','u','l','e','s',0};
99 static const WCHAR szPublishFeatures[] = 
100     {'P','u','b','l','i','s','h','F','e','a','t','u','r','e','s',0};
101 static const WCHAR szRegisterProduct[] = 
102     {'R','e','g','i','s','t','e','r','P','r','o','d','u','c','t',0};
103 static const WCHAR szInstallExecute[] = 
104     {'I','n','s','t','a','l','l','E','x','e','c','u','t','e',0};
105 static const WCHAR szInstallExecuteAgain[] = 
106     {'I','n','s','t','a','l','l','E','x','e','c','u','t','e',
107             'A','g','a','i','n',0};
108 static const WCHAR szInstallFinalize[] = 
109     {'I','n','s','t','a','l','l','F','i','n','a','l','i','z','e',0};
110 static const WCHAR szForceReboot[] = 
111     {'F','o','r','c','e','R','e','b','o','o','t',0};
112 static const WCHAR szResolveSource[] =
113     {'R','e','s','o','l','v','e','S','o','u','r','c','e',0};
114 static const WCHAR szAppSearch[] = 
115     {'A','p','p','S','e','a','r','c','h',0};
116 static const WCHAR szAllocateRegistrySpace[] = 
117     {'A','l','l','o','c','a','t','e','R','e','g','i','s','t','r','y',
118             'S','p','a','c','e',0};
119 static const WCHAR szBindImage[] = 
120     {'B','i','n','d','I','m','a','g','e',0};
121 static const WCHAR szCCPSearch[] = 
122     {'C','C','P','S','e','a','r','c','h',0};
123 static const WCHAR szDeleteServices[] = 
124     {'D','e','l','e','t','e','S','e','r','v','i','c','e','s',0};
125 static const WCHAR szDisableRollback[] = 
126     {'D','i','s','a','b','l','e','R','o','l','l','b','a','c','k',0};
127 static const WCHAR szExecuteAction[] = 
128     {'E','x','e','c','u','t','e','A','c','t','i','o','n',0};
129 const WCHAR szFindRelatedProducts[] = 
130     {'F','i','n','d','R','e','l','a','t','e','d',
131             'P','r','o','d','u','c','t','s',0};
132 static const WCHAR szInstallAdminPackage[] = 
133     {'I','n','s','t','a','l','l','A','d','m','i','n',
134             'P','a','c','k','a','g','e',0};
135 static const WCHAR szInstallSFPCatalogFile[] = 
136     {'I','n','s','t','a','l','l','S','F','P','C','a','t','a','l','o','g',
137             'F','i','l','e',0};
138 static const WCHAR szIsolateComponents[] = 
139     {'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t','s',0};
140 const WCHAR szMigrateFeatureStates[] = 
141     {'M','i','g','r','a','t','e','F','e','a','t','u','r','e',
142             'S','t','a','t','e','s',0};
143 const WCHAR szMoveFiles[] = 
144     {'M','o','v','e','F','i','l','e','s',0};
145 static const WCHAR szMsiPublishAssemblies[] = 
146     {'M','s','i','P','u','b','l','i','s','h',
147             'A','s','s','e','m','b','l','i','e','s',0};
148 static const WCHAR szMsiUnpublishAssemblies[] = 
149     {'M','s','i','U','n','p','u','b','l','i','s','h',
150             'A','s','s','e','m','b','l','i','e','s',0};
151 static const WCHAR szInstallODBC[] = 
152     {'I','n','s','t','a','l','l','O','D','B','C',0};
153 static const WCHAR szInstallServices[] = 
154     {'I','n','s','t','a','l','l','S','e','r','v','i','c','e','s',0};
155 const WCHAR szPatchFiles[] = 
156     {'P','a','t','c','h','F','i','l','e','s',0};
157 static const WCHAR szPublishComponents[] = 
158     {'P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','s',0};
159 static const WCHAR szRegisterComPlus[] =
160     {'R','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0};
161 const WCHAR szRegisterExtensionInfo[] =
162     {'R','e','g','i','s','t','e','r','E','x','t','e','n','s','i','o','n',
163             'I','n','f','o',0};
164 static const WCHAR szRegisterFonts[] =
165     {'R','e','g','i','s','t','e','r','F','o','n','t','s',0};
166 const WCHAR szRegisterMIMEInfo[] =
167     {'R','e','g','i','s','t','e','r','M','I','M','E','I','n','f','o',0};
168 static const WCHAR szRegisterUser[] =
169     {'R','e','g','i','s','t','e','r','U','s','e','r',0};
170 const WCHAR szRemoveDuplicateFiles[] =
171     {'R','e','m','o','v','e','D','u','p','l','i','c','a','t','e',
172             'F','i','l','e','s',0};
173 static const WCHAR szRemoveEnvironmentStrings[] =
174     {'R','e','m','o','v','e','E','n','v','i','r','o','n','m','e','n','t',
175             'S','t','r','i','n','g','s',0};
176 const WCHAR szRemoveExistingProducts[] =
177     {'R','e','m','o','v','e','E','x','i','s','t','i','n','g',
178             'P','r','o','d','u','c','t','s',0};
179 const WCHAR szRemoveFiles[] =
180     {'R','e','m','o','v','e','F','i','l','e','s',0};
181 static const WCHAR szRemoveFolders[] =
182     {'R','e','m','o','v','e','F','o','l','d','e','r','s',0};
183 static const WCHAR szRemoveIniValues[] =
184     {'R','e','m','o','v','e','I','n','i','V','a','l','u','e','s',0};
185 static const WCHAR szRemoveODBC[] =
186     {'R','e','m','o','v','e','O','D','B','C',0};
187 static const WCHAR szRemoveRegistryValues[] =
188     {'R','e','m','o','v','e','R','e','g','i','s','t','r','y',
189             'V','a','l','u','e','s',0};
190 static const WCHAR szRemoveShortcuts[] =
191     {'R','e','m','o','v','e','S','h','o','r','t','c','u','t','s',0};
192 static const WCHAR szRMCCPSearch[] =
193     {'R','M','C','C','P','S','e','a','r','c','h',0};
194 static const WCHAR szScheduleReboot[] =
195     {'S','c','h','e','d','u','l','e','R','e','b','o','o','t',0};
196 static const WCHAR szSelfUnregModules[] =
197     {'S','e','l','f','U','n','r','e','g','M','o','d','u','l','e','s',0};
198 static const WCHAR szSetODBCFolders[] =
199     {'S','e','t','O','D','B','C','F','o','l','d','e','r','s',0};
200 static const WCHAR szStartServices[] =
201     {'S','t','a','r','t','S','e','r','v','i','c','e','s',0};
202 static const WCHAR szStopServices[] =
203     {'S','t','o','p','S','e','r','v','i','c','e','s',0};
204 static const WCHAR szUnpublishComponents[] =
205     {'U','n','p','u','b','l','i','s','h',
206             'C','o','m','p','o','n','e','n','t','s',0};
207 static const WCHAR szUnpublishFeatures[] =
208     {'U','n','p','u','b','l','i','s','h','F','e','a','t','u','r','e','s',0};
209 const WCHAR szUnregisterClassInfo[] =
210     {'U','n','r','e','g','i','s','t','e','r','C','l','a','s','s',
211             'I','n','f','o',0};
212 static const WCHAR szUnregisterComPlus[] =
213     {'U','n','r','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0};
214 const WCHAR szUnregisterExtensionInfo[] =
215     {'U','n','r','e','g','i','s','t','e','r',
216             'E','x','t','e','n','s','i','o','n','I','n','f','o',0};
217 static const WCHAR szUnregisterFonts[] =
218     {'U','n','r','e','g','i','s','t','e','r','F','o','n','t','s',0};
219 const WCHAR szUnregisterMIMEInfo[] =
220     {'U','n','r','e','g','i','s','t','e','r','M','I','M','E','I','n','f','o',0};
221 const WCHAR szUnregisterProgIdInfo[] =
222     {'U','n','r','e','g','i','s','t','e','r','P','r','o','g','I','d',
223             'I','n','f','o',0};
224 static const WCHAR szUnregisterTypeLibraries[] =
225     {'U','n','r','e','g','i','s','t','e','r','T','y','p','e',
226             'L','i','b','r','a','r','i','e','s',0};
227 static const WCHAR szValidateProductID[] =
228     {'V','a','l','i','d','a','t','e','P','r','o','d','u','c','t','I','D',0};
229 static const WCHAR szWriteEnvironmentStrings[] =
230     {'W','r','i','t','e','E','n','v','i','r','o','n','m','e','n','t',
231             'S','t','r','i','n','g','s',0};
232
233 /* action handlers */
234 typedef UINT (*STANDARDACTIONHANDLER)(MSIPACKAGE*);
235
236 struct _actions {
237     LPCWSTR action;
238     STANDARDACTIONHANDLER handler;
239 };
240
241
242 /********************************************************
243  * helper functions
244  ********************************************************/
245
246 static void ui_actionstart(MSIPACKAGE *package, LPCWSTR action)
247 {
248     static const WCHAR Query_t[] = 
249         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
250          '`','A','c','t','i','o', 'n','T','e','x','t','`',' ',
251          'W','H','E','R','E', ' ','`','A','c','t','i','o','n','`',' ','=', 
252          ' ','\'','%','s','\'',0};
253     MSIRECORD * row;
254
255     row = MSI_QueryGetRecord( package->db, Query_t, action );
256     if (!row)
257         return;
258     MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONSTART, row);
259     msiobj_release(&row->hdr);
260 }
261
262 static void ui_actioninfo(MSIPACKAGE *package, LPCWSTR action, BOOL start, 
263                           UINT rc)
264 {
265     MSIRECORD * row;
266     static const WCHAR template_s[]=
267         {'A','c','t','i','o','n',' ','s','t','a','r','t',' ','%','s',':',' ',
268          '%','s', '.',0};
269     static const WCHAR template_e[]=
270         {'A','c','t','i','o','n',' ','e','n','d','e','d',' ','%','s',':',' ',
271          '%','s', '.',' ','R','e','t','u','r','n',' ','v','a','l','u','e',' ',
272          '%','i','.',0};
273     static const WCHAR format[] = 
274         {'H','H','\'',':','\'','m','m','\'',':','\'','s','s',0};
275     WCHAR message[1024];
276     WCHAR timet[0x100];
277
278     GetTimeFormatW(LOCALE_USER_DEFAULT, 0, NULL, format, timet, 0x100);
279     if (start)
280         sprintfW(message,template_s,timet,action);
281     else
282         sprintfW(message,template_e,timet,action,rc);
283     
284     row = MSI_CreateRecord(1);
285     MSI_RecordSetStringW(row,1,message);
286  
287     MSI_ProcessMessage(package, INSTALLMESSAGE_INFO, row);
288     msiobj_release(&row->hdr);
289 }
290
291 UINT msi_parse_command_line( MSIPACKAGE *package, LPCWSTR szCommandLine,
292                              BOOL preserve_case )
293 {
294     LPCWSTR ptr,ptr2;
295     BOOL quote;
296     DWORD len;
297     LPWSTR prop = NULL, val = NULL;
298
299     if (!szCommandLine)
300         return ERROR_SUCCESS;
301
302     ptr = szCommandLine;
303        
304     while (*ptr)
305     {
306         if (*ptr==' ')
307         {
308             ptr++;
309             continue;
310         }
311
312         TRACE("Looking at %s\n",debugstr_w(ptr));
313
314         ptr2 = strchrW(ptr,'=');
315         if (!ptr2)
316         {
317             ERR("command line contains unknown string : %s\n", debugstr_w(ptr));
318             break;
319         }
320  
321         quote = FALSE;
322
323         len = ptr2-ptr;
324         prop = msi_alloc((len+1)*sizeof(WCHAR));
325         memcpy(prop,ptr,len*sizeof(WCHAR));
326         prop[len]=0;
327
328         if (!preserve_case)
329             struprW(prop);
330
331         ptr2++;
332        
333         len = 0; 
334         ptr = ptr2; 
335         while (*ptr && (quote || (!quote && *ptr!=' ')))
336         {
337             if (*ptr == '"')
338                 quote = !quote;
339             ptr++;
340             len++;
341         }
342        
343         if (*ptr2=='"')
344         {
345             ptr2++;
346             len -= 2;
347         }
348         val = msi_alloc((len+1)*sizeof(WCHAR));
349         memcpy(val,ptr2,len*sizeof(WCHAR));
350         val[len] = 0;
351
352         if (lstrlenW(prop) > 0)
353         {
354             TRACE("Found commandline property (%s) = (%s)\n", 
355                    debugstr_w(prop), debugstr_w(val));
356             MSI_SetPropertyW(package,prop,val);
357         }
358         msi_free(val);
359         msi_free(prop);
360     }
361
362     return ERROR_SUCCESS;
363 }
364
365
366 static LPWSTR* msi_split_string( LPCWSTR str, WCHAR sep )
367 {
368     LPCWSTR pc;
369     LPWSTR p, *ret = NULL;
370     UINT count = 0;
371
372     if (!str)
373         return ret;
374
375     /* count the number of substrings */
376     for ( pc = str, count = 0; pc; count++ )
377     {
378         pc = strchrW( pc, sep );
379         if (pc)
380             pc++;
381     }
382
383     /* allocate space for an array of substring pointers and the substrings */
384     ret = msi_alloc( (count+1) * sizeof (LPWSTR) +
385                      (lstrlenW(str)+1) * sizeof(WCHAR) );
386     if (!ret)
387         return ret;
388
389     /* copy the string and set the pointers */
390     p = (LPWSTR) &ret[count+1];
391     lstrcpyW( p, str );
392     for( count = 0; (ret[count] = p); count++ )
393     {
394         p = strchrW( p, sep );
395         if (p)
396             *p++ = 0;
397     }
398
399     return ret;
400 }
401
402 static UINT msi_check_transform_applicable( MSIPACKAGE *package, IStorage *patch )
403 {
404     WCHAR szProductCode[] = { 'P','r','o','d','u','c','t','C','o','d','e',0 };
405     LPWSTR prod_code, patch_product;
406     UINT ret;
407
408     prod_code = msi_dup_property( package, szProductCode );
409     patch_product = msi_get_suminfo_product( patch );
410
411     TRACE("db = %s patch = %s\n", debugstr_w(prod_code), debugstr_w(patch_product));
412
413     if ( strstrW( patch_product, prod_code ) )
414         ret = ERROR_SUCCESS;
415     else
416         ret = ERROR_FUNCTION_FAILED;
417
418     msi_free( patch_product );
419     msi_free( prod_code );
420
421     return ret;
422 }
423
424 static UINT msi_apply_substorage_transform( MSIPACKAGE *package,
425                                  MSIDATABASE *patch_db, LPCWSTR name )
426 {
427     UINT ret = ERROR_FUNCTION_FAILED;
428     IStorage *stg = NULL;
429     HRESULT r;
430
431     TRACE("%p %s\n", package, debugstr_w(name) );
432
433     if (*name++ != ':')
434     {
435         ERR("expected a colon in %s\n", debugstr_w(name));
436         return ERROR_FUNCTION_FAILED;
437     }
438
439     r = IStorage_OpenStorage( patch_db->storage, name, NULL, STGM_SHARE_EXCLUSIVE, NULL, 0, &stg );
440     if (SUCCEEDED(r))
441     {
442         ret = msi_check_transform_applicable( package, stg );
443         if (ret == ERROR_SUCCESS)
444             msi_table_apply_transform( package->db, stg );
445         else
446             TRACE("substorage transform %s wasn't applicable\n", debugstr_w(name));
447         IStorage_Release( stg );
448     }
449     else
450         ERR("failed to open substorage %s\n", debugstr_w(name));
451
452     return ERROR_SUCCESS;
453 }
454
455 static UINT msi_check_patch_applicable( MSIPACKAGE *package, MSISUMMARYINFO *si )
456 {
457     static const WCHAR szProdCode[] = { 'P','r','o','d','u','c','t','C','o','d','e',0 };
458     LPWSTR guid_list, *guids, product_code;
459     UINT i, ret = ERROR_FUNCTION_FAILED;
460
461     product_code = msi_dup_property( package, szProdCode );
462     if (!product_code)
463     {
464         /* FIXME: the property ProductCode should be written into the DB somewhere */
465         ERR("no product code to check\n");
466         return ERROR_SUCCESS;
467     }
468
469     guid_list = msi_suminfo_dup_string( si, PID_TEMPLATE );
470     guids = msi_split_string( guid_list, ';' );
471     for ( i = 0; guids[i] && ret != ERROR_SUCCESS; i++ )
472     {
473         if (!lstrcmpW( guids[i], product_code ))
474             ret = ERROR_SUCCESS;
475     }
476     msi_free( guids );
477     msi_free( guid_list );
478     msi_free( product_code );
479
480     return ret;
481 }
482
483 static UINT msi_parse_patch_summary( MSIPACKAGE *package, MSIDATABASE *patch_db )
484 {
485     MSISUMMARYINFO *si;
486     LPWSTR str, *substorage;
487     UINT i, r = ERROR_SUCCESS;
488
489     si = MSI_GetSummaryInformationW( patch_db->storage, 0 );
490     if (!si)
491         return ERROR_FUNCTION_FAILED;
492
493     msi_check_patch_applicable( package, si );
494
495     /* enumerate the substorage */
496     str = msi_suminfo_dup_string( si, PID_LASTAUTHOR );
497     substorage = msi_split_string( str, ';' );
498     for ( i = 0; substorage && substorage[i] && r == ERROR_SUCCESS; i++ )
499         r = msi_apply_substorage_transform( package, patch_db, substorage[i] );
500     msi_free( substorage );
501     msi_free( str );
502
503     /* FIXME: parse the sources in PID_REVNUMBER and do something with them... */
504
505     msiobj_release( &si->hdr );
506
507     return r;
508 }
509
510 static UINT msi_apply_patch_package( MSIPACKAGE *package, LPCWSTR file )
511 {
512     MSIDATABASE *patch_db = NULL;
513     UINT r;
514
515     TRACE("%p %s\n", package, debugstr_w( file ) );
516
517     /* FIXME:
518      *  We probably want to make sure we only open a patch collection here.
519      *  Patch collections (.msp) and databases (.msi) have different GUIDs
520      *  but currently MSI_OpenDatabaseW will accept both.
521      */
522     r = MSI_OpenDatabaseW( file, MSIDBOPEN_READONLY, &patch_db );
523     if ( r != ERROR_SUCCESS )
524     {
525         ERR("failed to open patch collection %s\n", debugstr_w( file ) );
526         return r;
527     }
528
529     msi_parse_patch_summary( package, patch_db );
530
531     /*
532      * There might be a CAB file in the patch package,
533      * so append it to the list of storage to search for streams.
534      */
535     append_storage_to_db( package->db, patch_db->storage );
536
537     msiobj_release( &patch_db->hdr );
538
539     return ERROR_SUCCESS;
540 }
541
542 /* get the PATCH property, and apply all the patches it specifies */
543 static UINT msi_apply_patches( MSIPACKAGE *package )
544 {
545     static const WCHAR szPatch[] = { 'P','A','T','C','H',0 };
546     LPWSTR patch_list, *patches;
547     UINT i, r = ERROR_SUCCESS;
548
549     patch_list = msi_dup_property( package, szPatch );
550
551     TRACE("patches to be applied: %s\n", debugstr_w( patch_list ) );
552
553     patches = msi_split_string( patch_list, ';' );
554     for( i=0; patches && patches[i] && r == ERROR_SUCCESS; i++ )
555         r = msi_apply_patch_package( package, patches[i] );
556
557     msi_free( patches );
558     msi_free( patch_list );
559
560     return r;
561 }
562
563 static UINT msi_apply_transforms( MSIPACKAGE *package )
564 {
565     static const WCHAR szTransforms[] = {
566         'T','R','A','N','S','F','O','R','M','S',0 };
567     LPWSTR xform_list, *xforms;
568     UINT i, r = ERROR_SUCCESS;
569
570     xform_list = msi_dup_property( package, szTransforms );
571     xforms = msi_split_string( xform_list, ';' );
572
573     for( i=0; xforms && xforms[i] && r == ERROR_SUCCESS; i++ )
574     {
575         if (xforms[i][0] == ':')
576             r = msi_apply_substorage_transform( package, package->db, xforms[i] );
577         else
578             r = MSI_DatabaseApplyTransformW( package->db, xforms[i], 0 );
579     }
580
581     msi_free( xforms );
582     msi_free( xform_list );
583
584     return r;
585 }
586
587 static BOOL ui_sequence_exists( MSIPACKAGE *package )
588 {
589     MSIQUERY *view;
590     UINT rc;
591
592     static const WCHAR ExecSeqQuery [] =
593         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
594          '`','I','n','s','t','a','l','l',
595          'U','I','S','e','q','u','e','n','c','e','`',
596          ' ','W','H','E','R','E',' ',
597          '`','S','e','q','u','e','n','c','e','`',' ',
598          '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
599          '`','S','e','q','u','e','n','c','e','`',0};
600
601     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
602     if (rc == ERROR_SUCCESS)
603     {
604         msiobj_release(&view->hdr);
605         return TRUE;
606     }
607
608     return FALSE;
609 }
610
611 static UINT msi_set_sourcedir_props(MSIPACKAGE *package, BOOL replace)
612 {
613     LPWSTR p, db;
614     LPWSTR source, check;
615     DWORD len;
616
617     static const WCHAR szOriginalDatabase[] =
618         {'O','r','i','g','i','n','a','l','D','a','t','a','b','a','s','e',0};
619
620     db = msi_dup_property( package, szOriginalDatabase );
621     if (!db)
622         return ERROR_OUTOFMEMORY;
623
624     p = strrchrW( db, '\\' );
625     if (!p)
626     {
627         p = strrchrW( db, '/' );
628         if (!p)
629         {
630             msi_free(db);
631             return ERROR_SUCCESS;
632         }
633     }
634
635     len = p - db + 2;
636     source = msi_alloc( len * sizeof(WCHAR) );
637     lstrcpynW( source, db, len );
638
639     check = msi_dup_property( package, cszSourceDir );
640     if (!check || replace)
641         MSI_SetPropertyW( package, cszSourceDir, source );
642
643     msi_free( check );
644
645     check = msi_dup_property( package, cszSOURCEDIR );
646     if (!check || replace)
647         MSI_SetPropertyW( package, cszSOURCEDIR, source );
648
649     msi_free( check );
650     msi_free( source );
651     msi_free( db );
652
653     return ERROR_SUCCESS;
654 }
655
656 static UINT msi_set_context(MSIPACKAGE *package)
657 {
658     WCHAR val[10];
659     DWORD sz = 10;
660     DWORD num;
661     UINT r;
662
663     static const WCHAR szOne[] = {'1',0};
664     static const WCHAR szAllUsers[] = {'A','L','L','U','S','E','R','S',0};
665
666     package->Context = MSIINSTALLCONTEXT_USERUNMANAGED;
667
668     r = MSI_GetPropertyW(package, szAllUsers, val, &sz);
669     if (r == ERROR_SUCCESS)
670     {
671         num = atolW(val);
672         if (num == 1 || num == 2)
673             package->Context = MSIINSTALLCONTEXT_MACHINE;
674     }
675
676     MSI_SetPropertyW(package, szAllUsers, szOne);
677     return ERROR_SUCCESS;
678 }
679
680 /****************************************************
681  * TOP level entry points 
682  *****************************************************/
683
684 UINT MSI_InstallPackage( MSIPACKAGE *package, LPCWSTR szPackagePath,
685                          LPCWSTR szCommandLine )
686 {
687     UINT rc;
688     BOOL ui = FALSE, ui_exists;
689     static const WCHAR szUILevel[] = {'U','I','L','e','v','e','l',0};
690     static const WCHAR szAction[] = {'A','C','T','I','O','N',0};
691     static const WCHAR szInstall[] = {'I','N','S','T','A','L','L',0};
692
693     MSI_SetPropertyW(package, szAction, szInstall);
694
695     package->script = msi_alloc_zero(sizeof(MSISCRIPT));
696
697     package->script->InWhatSequence = SEQUENCE_INSTALL;
698
699     if (szPackagePath)   
700     {
701         LPWSTR p, dir;
702         LPCWSTR file;
703
704         dir = strdupW(szPackagePath);
705         p = strrchrW(dir, '\\');
706         if (p)
707         {
708             *(++p) = 0;
709             file = szPackagePath + (p - dir);
710         }
711         else
712         {
713             msi_free(dir);
714             dir = msi_alloc(MAX_PATH*sizeof(WCHAR));
715             GetCurrentDirectoryW(MAX_PATH, dir);
716             lstrcatW(dir, cszbs);
717             file = szPackagePath;
718         }
719
720         msi_free( package->PackagePath );
721         package->PackagePath = msi_alloc((lstrlenW(dir) + lstrlenW(file) + 1) * sizeof(WCHAR));
722         if (!package->PackagePath)
723         {
724             msi_free(dir);
725             return ERROR_OUTOFMEMORY;
726         }
727
728         lstrcpyW(package->PackagePath, dir);
729         lstrcatW(package->PackagePath, file);
730         msi_free(dir);
731
732         msi_set_sourcedir_props(package, FALSE);
733     }
734
735     msi_parse_command_line( package, szCommandLine, FALSE );
736
737     msi_apply_transforms( package );
738     msi_apply_patches( package );
739
740     /* properties may have been added by a transform */
741     msi_clone_properties( package );
742     msi_set_context( package );
743
744     if ( (msi_get_property_int(package, szUILevel, 0) & INSTALLUILEVEL_MASK) >= INSTALLUILEVEL_REDUCED )
745     {
746         package->script->InWhatSequence |= SEQUENCE_UI;
747         rc = ACTION_ProcessUISequence(package);
748         ui = TRUE;
749         ui_exists = ui_sequence_exists(package);
750         if (rc == ERROR_SUCCESS || !ui_exists)
751         {
752             package->script->InWhatSequence |= SEQUENCE_EXEC;
753             rc = ACTION_ProcessExecSequence(package,ui_exists);
754         }
755     }
756     else
757         rc = ACTION_ProcessExecSequence(package,FALSE);
758
759     package->script->CurrentlyScripting= FALSE;
760
761     /* process the ending type action */
762     if (rc == ERROR_SUCCESS)
763         ACTION_PerformActionSequence(package,-1,ui);
764     else if (rc == ERROR_INSTALL_USEREXIT) 
765         ACTION_PerformActionSequence(package,-2,ui);
766     else if (rc == ERROR_INSTALL_SUSPEND) 
767         ACTION_PerformActionSequence(package,-4,ui);
768     else  /* failed */
769         ACTION_PerformActionSequence(package,-3,ui);
770
771     /* finish up running custom actions */
772     ACTION_FinishCustomActions(package);
773     
774     return rc;
775 }
776
777 static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq, BOOL UI)
778 {
779     UINT rc = ERROR_SUCCESS;
780     MSIRECORD * row = 0;
781     static const WCHAR ExecSeqQuery[] =
782         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
783          '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
784          'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
785          '`','S','e','q','u','e','n','c','e','`',' ', '=',' ','%','i',0};
786
787     static const WCHAR UISeqQuery[] =
788         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
789      '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e',
790      '`', ' ', 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',
791          ' ', '=',' ','%','i',0};
792
793     if (UI)
794         row = MSI_QueryGetRecord(package->db, UISeqQuery, seq);
795     else
796         row = MSI_QueryGetRecord(package->db, ExecSeqQuery, seq);
797
798     if (row)
799     {
800         LPCWSTR action, cond;
801
802         TRACE("Running the actions\n"); 
803
804         /* check conditions */
805         cond = MSI_RecordGetString(row,2);
806
807         /* this is a hack to skip errors in the condition code */
808         if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
809             goto end;
810
811         action = MSI_RecordGetString(row,1);
812         if (!action)
813         {
814             ERR("failed to fetch action\n");
815             rc = ERROR_FUNCTION_FAILED;
816             goto end;
817         }
818
819         if (UI)
820             rc = ACTION_PerformUIAction(package,action,-1);
821         else
822             rc = ACTION_PerformAction(package,action,-1,FALSE);
823 end:
824         msiobj_release(&row->hdr);
825     }
826     else
827         rc = ERROR_SUCCESS;
828
829     return rc;
830 }
831
832 typedef struct {
833     MSIPACKAGE* package;
834     BOOL UI;
835 } iterate_action_param;
836
837 static UINT ITERATE_Actions(MSIRECORD *row, LPVOID param)
838 {
839     iterate_action_param *iap= (iterate_action_param*)param;
840     UINT rc;
841     LPCWSTR cond, action;
842
843     action = MSI_RecordGetString(row,1);
844     if (!action)
845     {
846         ERR("Error is retrieving action name\n");
847         return ERROR_FUNCTION_FAILED;
848     }
849
850     /* check conditions */
851     cond = MSI_RecordGetString(row,2);
852
853     /* this is a hack to skip errors in the condition code */
854     if (MSI_EvaluateConditionW(iap->package, cond) == MSICONDITION_FALSE)
855     {
856         TRACE("Skipping action: %s (condition is false)\n", debugstr_w(action));
857         return ERROR_SUCCESS;
858     }
859
860     if (iap->UI)
861         rc = ACTION_PerformUIAction(iap->package,action,-1);
862     else
863         rc = ACTION_PerformAction(iap->package,action,-1,FALSE);
864
865     msi_dialog_check_messages( NULL );
866
867     if (iap->package->CurrentInstallState != ERROR_SUCCESS )
868         rc = iap->package->CurrentInstallState;
869
870     if (rc == ERROR_FUNCTION_NOT_CALLED)
871         rc = ERROR_SUCCESS;
872
873     if (rc != ERROR_SUCCESS)
874         ERR("Execution halted, action %s returned %i\n", debugstr_w(action), rc);
875
876     return rc;
877 }
878
879 UINT MSI_Sequence( MSIPACKAGE *package, LPCWSTR szTable, INT iSequenceMode )
880 {
881     MSIQUERY * view;
882     UINT r;
883     static const WCHAR query[] =
884         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
885          '`','%','s','`',
886          ' ','W','H','E','R','E',' ', 
887          '`','S','e','q','u','e','n','c','e','`',' ',
888          '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
889          '`','S','e','q','u','e','n','c','e','`',0};
890     iterate_action_param iap;
891
892     /*
893      * FIXME: probably should be checking UILevel in the
894      *       ACTION_PerformUIAction/ACTION_PerformAction
895      *       rather than saving the UI level here. Those
896      *       two functions can be merged too.
897      */
898     iap.package = package;
899     iap.UI = TRUE;
900
901     TRACE("%p %s %i\n", package, debugstr_w(szTable), iSequenceMode );
902
903     r = MSI_OpenQuery( package->db, &view, query, szTable );
904     if (r == ERROR_SUCCESS)
905     {
906         r = MSI_IterateRecords( view, NULL, ITERATE_Actions, &iap );
907         msiobj_release(&view->hdr);
908     }
909
910     return r;
911 }
912
913 static UINT ACTION_ProcessExecSequence(MSIPACKAGE *package, BOOL UIran)
914 {
915     MSIQUERY * view;
916     UINT rc;
917     static const WCHAR ExecSeqQuery[] =
918         {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
919          '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
920          'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
921          '`','S','e','q','u','e','n','c','e','`',' ', '>',' ','%','i',' ',
922          'O','R','D','E','R',' ', 'B','Y',' ',
923          '`','S','e','q','u','e','n','c','e','`',0 };
924     MSIRECORD * row = 0;
925     static const WCHAR IVQuery[] =
926         {'S','E','L','E','C','T',' ','`','S','e','q','u','e','n','c','e','`',
927          ' ', 'F','R','O','M',' ','`','I','n','s','t','a','l','l',
928          'E','x','e','c','u','t','e','S','e','q','u','e','n','c','e','`',' ',
929          'W','H','E','R','E',' ','`','A','c','t','i','o','n','`',' ','=',
930          ' ','\'', 'I','n','s','t','a','l','l',
931          'V','a','l','i','d','a','t','e','\'', 0};
932     INT seq = 0;
933     iterate_action_param iap;
934
935     iap.package = package;
936     iap.UI = FALSE;
937
938     if (package->script->ExecuteSequenceRun)
939     {
940         TRACE("Execute Sequence already Run\n");
941         return ERROR_SUCCESS;
942     }
943
944     package->script->ExecuteSequenceRun = TRUE;
945
946     /* get the sequence number */
947     if (UIran)
948     {
949         row = MSI_QueryGetRecord(package->db, IVQuery);
950         if( !row )
951             return ERROR_FUNCTION_FAILED;
952         seq = MSI_RecordGetInteger(row,1);
953         msiobj_release(&row->hdr);
954     }
955
956     rc = MSI_OpenQuery(package->db, &view, ExecSeqQuery, seq);
957     if (rc == ERROR_SUCCESS)
958     {
959         TRACE("Running the actions\n");
960
961         rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, &iap);
962         msiobj_release(&view->hdr);
963     }
964
965     return rc;
966 }
967
968 static UINT ACTION_ProcessUISequence(MSIPACKAGE *package)
969 {
970     MSIQUERY * view;
971     UINT rc;
972     static const WCHAR ExecSeqQuery [] =
973         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
974          '`','I','n','s','t','a','l','l',
975          'U','I','S','e','q','u','e','n','c','e','`',
976          ' ','W','H','E','R','E',' ', 
977          '`','S','e','q','u','e','n','c','e','`',' ',
978          '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
979          '`','S','e','q','u','e','n','c','e','`',0};
980     iterate_action_param iap;
981
982     iap.package = package;
983     iap.UI = TRUE;
984
985     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
986     
987     if (rc == ERROR_SUCCESS)
988     {
989         TRACE("Running the actions\n"); 
990
991         rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, &iap);
992         msiobj_release(&view->hdr);
993     }
994
995     return rc;
996 }
997
998 /********************************************************
999  * ACTION helper functions and functions that perform the actions
1000  *******************************************************/
1001 static BOOL ACTION_HandleCustomAction( MSIPACKAGE* package, LPCWSTR action,
1002                                        UINT* rc, UINT script, BOOL force )
1003 {
1004     BOOL ret=FALSE;
1005     UINT arc;
1006
1007     arc = ACTION_CustomAction(package, action, script, force);
1008
1009     if (arc != ERROR_CALL_NOT_IMPLEMENTED)
1010     {
1011         *rc = arc;
1012         ret = TRUE;
1013     }
1014     return ret;
1015 }
1016
1017 /* 
1018  * A lot of actions are really important even if they don't do anything
1019  * explicit... Lots of properties are set at the beginning of the installation
1020  * CostFinalize does a bunch of work to translate the directories and such
1021  * 
1022  * But until I get write access to the database that is hard, so I am going to
1023  * hack it to see if I can get something to run.
1024  */
1025 UINT ACTION_PerformAction(MSIPACKAGE *package, const WCHAR *action, UINT script, BOOL force)
1026 {
1027     UINT rc = ERROR_SUCCESS; 
1028     BOOL handled;
1029
1030     TRACE("Performing action (%s)\n",debugstr_w(action));
1031
1032     handled = ACTION_HandleStandardAction(package, action, &rc, force);
1033
1034     if (!handled)
1035         handled = ACTION_HandleCustomAction(package, action, &rc, script, force);
1036
1037     if (!handled)
1038     {
1039         WARN("unhandled msi action %s\n",debugstr_w(action));
1040         rc = ERROR_FUNCTION_NOT_CALLED;
1041     }
1042
1043     return rc;
1044 }
1045
1046 UINT ACTION_PerformUIAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
1047 {
1048     UINT rc = ERROR_SUCCESS;
1049     BOOL handled = FALSE;
1050
1051     TRACE("Performing action (%s)\n",debugstr_w(action));
1052
1053     handled = ACTION_HandleStandardAction(package, action, &rc,TRUE);
1054
1055     if (!handled)
1056         handled = ACTION_HandleCustomAction(package, action, &rc, script, FALSE);
1057
1058     if( !handled && ACTION_DialogBox(package,action) == ERROR_SUCCESS )
1059         handled = TRUE;
1060
1061     if (!handled)
1062     {
1063         WARN("unhandled msi action %s\n",debugstr_w(action));
1064         rc = ERROR_FUNCTION_NOT_CALLED;
1065     }
1066
1067     return rc;
1068 }
1069
1070
1071 /*
1072  * Actual Action Handlers
1073  */
1074
1075 static UINT ITERATE_CreateFolders(MSIRECORD *row, LPVOID param)
1076 {
1077     MSIPACKAGE *package = (MSIPACKAGE*)param;
1078     LPCWSTR dir;
1079     LPWSTR full_path;
1080     MSIRECORD *uirow;
1081     MSIFOLDER *folder;
1082
1083     dir = MSI_RecordGetString(row,1);
1084     if (!dir)
1085     {
1086         ERR("Unable to get folder id\n");
1087         return ERROR_SUCCESS;
1088     }
1089
1090     full_path = resolve_folder(package,dir,FALSE,FALSE,TRUE,&folder);
1091     if (!full_path)
1092     {
1093         ERR("Unable to resolve folder id %s\n",debugstr_w(dir));
1094         return ERROR_SUCCESS;
1095     }
1096
1097     TRACE("Folder is %s\n",debugstr_w(full_path));
1098
1099     /* UI stuff */
1100     uirow = MSI_CreateRecord(1);
1101     MSI_RecordSetStringW(uirow,1,full_path);
1102     ui_actiondata(package,szCreateFolders,uirow);
1103     msiobj_release( &uirow->hdr );
1104
1105     if (folder->State == 0)
1106         create_full_pathW(full_path);
1107
1108     folder->State = 3;
1109
1110     msi_free(full_path);
1111     return ERROR_SUCCESS;
1112 }
1113
1114 /* FIXME: probably should merge this with the above function */
1115 static UINT msi_create_directory( MSIPACKAGE* package, LPCWSTR dir )
1116 {
1117     UINT rc = ERROR_SUCCESS;
1118     MSIFOLDER *folder;
1119     LPWSTR install_path;
1120
1121     install_path = resolve_folder(package, dir, FALSE, FALSE, TRUE, &folder);
1122     if (!install_path)
1123         return ERROR_FUNCTION_FAILED; 
1124
1125     /* create the path */
1126     if (folder->State == 0)
1127     {
1128         create_full_pathW(install_path);
1129         folder->State = 2;
1130     }
1131     msi_free(install_path);
1132
1133     return rc;
1134 }
1135
1136 UINT msi_create_component_directories( MSIPACKAGE *package )
1137 {
1138     MSICOMPONENT *comp;
1139
1140     /* create all the folders required by the components are going to install */
1141     LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
1142     {
1143         if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
1144             continue;
1145         msi_create_directory( package, comp->Directory );
1146     }
1147
1148     return ERROR_SUCCESS;
1149 }
1150
1151 /*
1152  * Also we cannot enable/disable components either, so for now I am just going 
1153  * to do all the directories for all the components.
1154  */
1155 static UINT ACTION_CreateFolders(MSIPACKAGE *package)
1156 {
1157     static const WCHAR ExecSeqQuery[] =
1158         {'S','E','L','E','C','T',' ',
1159          '`','D','i','r','e','c','t','o','r','y','_','`',
1160          ' ','F','R','O','M',' ',
1161          '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0 };
1162     UINT rc;
1163     MSIQUERY *view;
1164
1165     /* create all the empty folders specified in the CreateFolder table */
1166     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view );
1167     if (rc != ERROR_SUCCESS)
1168         return ERROR_SUCCESS;
1169
1170     rc = MSI_IterateRecords(view, NULL, ITERATE_CreateFolders, package);
1171     msiobj_release(&view->hdr);
1172
1173     msi_create_component_directories( package );
1174
1175     return rc;
1176 }
1177
1178 static UINT load_component( MSIRECORD *row, LPVOID param )
1179 {
1180     MSIPACKAGE *package = param;
1181     MSICOMPONENT *comp;
1182
1183     comp = msi_alloc_zero( sizeof(MSICOMPONENT) );
1184     if (!comp)
1185         return ERROR_FUNCTION_FAILED;
1186
1187     list_add_tail( &package->components, &comp->entry );
1188
1189     /* fill in the data */
1190     comp->Component = msi_dup_record_field( row, 1 );
1191
1192     TRACE("Loading Component %s\n", debugstr_w(comp->Component));
1193
1194     comp->ComponentId = msi_dup_record_field( row, 2 );
1195     comp->Directory = msi_dup_record_field( row, 3 );
1196     comp->Attributes = MSI_RecordGetInteger(row,4);
1197     comp->Condition = msi_dup_record_field( row, 5 );
1198     comp->KeyPath = msi_dup_record_field( row, 6 );
1199
1200     comp->Installed = INSTALLSTATE_UNKNOWN;
1201     msi_component_set_state(package, comp, INSTALLSTATE_UNKNOWN);
1202
1203     return ERROR_SUCCESS;
1204 }
1205
1206 static UINT load_all_components( MSIPACKAGE *package )
1207 {
1208     static const WCHAR query[] = {
1209         'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ', 
1210          '`','C','o','m','p','o','n','e','n','t','`',0 };
1211     MSIQUERY *view;
1212     UINT r;
1213
1214     if (!list_empty(&package->components))
1215         return ERROR_SUCCESS;
1216
1217     r = MSI_DatabaseOpenViewW( package->db, query, &view );
1218     if (r != ERROR_SUCCESS)
1219         return r;
1220
1221     r = MSI_IterateRecords(view, NULL, load_component, package);
1222     msiobj_release(&view->hdr);
1223     return r;
1224 }
1225
1226 typedef struct {
1227     MSIPACKAGE *package;
1228     MSIFEATURE *feature;
1229 } _ilfs;
1230
1231 static UINT add_feature_component( MSIFEATURE *feature, MSICOMPONENT *comp )
1232 {
1233     ComponentList *cl;
1234
1235     cl = msi_alloc( sizeof (*cl) );
1236     if ( !cl )
1237         return ERROR_NOT_ENOUGH_MEMORY;
1238     cl->component = comp;
1239     list_add_tail( &feature->Components, &cl->entry );
1240
1241     return ERROR_SUCCESS;
1242 }
1243
1244 static UINT add_feature_child( MSIFEATURE *parent, MSIFEATURE *child )
1245 {
1246     FeatureList *fl;
1247
1248     fl = msi_alloc( sizeof(*fl) );
1249     if ( !fl )
1250         return ERROR_NOT_ENOUGH_MEMORY;
1251     fl->feature = child;
1252     list_add_tail( &parent->Children, &fl->entry );
1253
1254     return ERROR_SUCCESS;
1255 }
1256
1257 static UINT iterate_load_featurecomponents(MSIRECORD *row, LPVOID param)
1258 {
1259     _ilfs* ilfs= (_ilfs*)param;
1260     LPCWSTR component;
1261     MSICOMPONENT *comp;
1262
1263     component = MSI_RecordGetString(row,1);
1264
1265     /* check to see if the component is already loaded */
1266     comp = get_loaded_component( ilfs->package, component );
1267     if (!comp)
1268     {
1269         ERR("unknown component %s\n", debugstr_w(component));
1270         return ERROR_FUNCTION_FAILED;
1271     }
1272
1273     add_feature_component( ilfs->feature, comp );
1274     comp->Enabled = TRUE;
1275
1276     return ERROR_SUCCESS;
1277 }
1278
1279 static MSIFEATURE *find_feature_by_name( MSIPACKAGE *package, LPCWSTR name )
1280 {
1281     MSIFEATURE *feature;
1282
1283     if ( !name )
1284         return NULL;
1285
1286     LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1287     {
1288         if ( !lstrcmpW( feature->Feature, name ) )
1289             return feature;
1290     }
1291
1292     return NULL;
1293 }
1294
1295 static UINT load_feature(MSIRECORD * row, LPVOID param)
1296 {
1297     MSIPACKAGE* package = (MSIPACKAGE*)param;
1298     MSIFEATURE* feature;
1299     static const WCHAR Query1[] = 
1300         {'S','E','L','E','C','T',' ',
1301          '`','C','o','m','p','o','n','e','n','t','_','`',
1302          ' ','F','R','O','M',' ','`','F','e','a','t','u','r','e',
1303          'C','o','m','p','o','n','e','n','t','s','`',' ',
1304          'W','H','E','R','E',' ',
1305          '`','F','e', 'a','t','u','r','e','_','`',' ','=','\'','%','s','\'',0};
1306     MSIQUERY * view;
1307     UINT    rc;
1308     _ilfs ilfs;
1309
1310     /* fill in the data */
1311
1312     feature = msi_alloc_zero( sizeof (MSIFEATURE) );
1313     if (!feature)
1314         return ERROR_NOT_ENOUGH_MEMORY;
1315
1316     list_init( &feature->Children );
1317     list_init( &feature->Components );
1318     
1319     feature->Feature = msi_dup_record_field( row, 1 );
1320
1321     TRACE("Loading feature %s\n",debugstr_w(feature->Feature));
1322
1323     feature->Feature_Parent = msi_dup_record_field( row, 2 );
1324     feature->Title = msi_dup_record_field( row, 3 );
1325     feature->Description = msi_dup_record_field( row, 4 );
1326
1327     if (!MSI_RecordIsNull(row,5))
1328         feature->Display = MSI_RecordGetInteger(row,5);
1329   
1330     feature->Level= MSI_RecordGetInteger(row,6);
1331     feature->Directory = msi_dup_record_field( row, 7 );
1332     feature->Attributes = MSI_RecordGetInteger(row,8);
1333
1334     feature->Installed = INSTALLSTATE_UNKNOWN;
1335     msi_feature_set_state(package, feature, INSTALLSTATE_UNKNOWN);
1336
1337     list_add_tail( &package->features, &feature->entry );
1338
1339     /* load feature components */
1340
1341     rc = MSI_OpenQuery( package->db, &view, Query1, feature->Feature );
1342     if (rc != ERROR_SUCCESS)
1343         return ERROR_SUCCESS;
1344
1345     ilfs.package = package;
1346     ilfs.feature = feature;
1347
1348     MSI_IterateRecords(view, NULL, iterate_load_featurecomponents , &ilfs);
1349     msiobj_release(&view->hdr);
1350
1351     return ERROR_SUCCESS;
1352 }
1353
1354 static UINT find_feature_children(MSIRECORD * row, LPVOID param)
1355 {
1356     MSIPACKAGE* package = (MSIPACKAGE*)param;
1357     MSIFEATURE *parent, *child;
1358
1359     child = find_feature_by_name( package, MSI_RecordGetString( row, 1 ) );
1360     if (!child)
1361         return ERROR_FUNCTION_FAILED;
1362
1363     if (!child->Feature_Parent)
1364         return ERROR_SUCCESS;
1365
1366     parent = find_feature_by_name( package, child->Feature_Parent );
1367     if (!parent)
1368         return ERROR_FUNCTION_FAILED;
1369
1370     add_feature_child( parent, child );
1371     return ERROR_SUCCESS;
1372 }
1373
1374 static UINT load_all_features( MSIPACKAGE *package )
1375 {
1376     static const WCHAR query[] = {
1377         'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1378         '`','F','e','a','t','u','r','e','`',' ','O','R','D','E','R',
1379         ' ','B','Y',' ','`','D','i','s','p','l','a','y','`',0};
1380     MSIQUERY *view;
1381     UINT r;
1382
1383     if (!list_empty(&package->features))
1384         return ERROR_SUCCESS;
1385  
1386     r = MSI_DatabaseOpenViewW( package->db, query, &view );
1387     if (r != ERROR_SUCCESS)
1388         return r;
1389
1390     r = MSI_IterateRecords( view, NULL, load_feature, package );
1391     if (r != ERROR_SUCCESS)
1392         return r;
1393
1394     r = MSI_IterateRecords( view, NULL, find_feature_children, package );
1395     msiobj_release( &view->hdr );
1396
1397     return r;
1398 }
1399
1400 static LPWSTR folder_split_path(LPWSTR p, WCHAR ch)
1401 {
1402     if (!p)
1403         return p;
1404     p = strchrW(p, ch);
1405     if (!p)
1406         return p;
1407     *p = 0;
1408     return p+1;
1409 }
1410
1411 static UINT load_file_hash(MSIPACKAGE *package, MSIFILE *file)
1412 {
1413     static const WCHAR query[] = {
1414         'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1415         '`','M','s','i','F','i','l','e','H','a','s','h','`',' ',
1416         'W','H','E','R','E',' ','`','F','i','l','e','_','`',' ','=',' ','\'','%','s','\'',0};
1417     MSIQUERY *view = NULL;
1418     MSIRECORD *row = NULL;
1419     UINT r;
1420
1421     TRACE("%s\n", debugstr_w(file->File));
1422
1423     r = MSI_OpenQuery(package->db, &view, query, file->File);
1424     if (r != ERROR_SUCCESS)
1425         goto done;
1426
1427     r = MSI_ViewExecute(view, NULL);
1428     if (r != ERROR_SUCCESS)
1429         goto done;
1430
1431     r = MSI_ViewFetch(view, &row);
1432     if (r != ERROR_SUCCESS)
1433         goto done;
1434
1435     file->hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
1436     file->hash.dwData[0] = MSI_RecordGetInteger(row, 3);
1437     file->hash.dwData[1] = MSI_RecordGetInteger(row, 4);
1438     file->hash.dwData[2] = MSI_RecordGetInteger(row, 5);
1439     file->hash.dwData[3] = MSI_RecordGetInteger(row, 6);
1440
1441 done:
1442     if (view) msiobj_release(&view->hdr);
1443     if (row) msiobj_release(&row->hdr);
1444     return r;
1445 }
1446
1447 static UINT load_file(MSIRECORD *row, LPVOID param)
1448 {
1449     MSIPACKAGE* package = (MSIPACKAGE*)param;
1450     LPCWSTR component;
1451     MSIFILE *file;
1452
1453     /* fill in the data */
1454
1455     file = msi_alloc_zero( sizeof (MSIFILE) );
1456     if (!file)
1457         return ERROR_NOT_ENOUGH_MEMORY;
1458  
1459     file->File = msi_dup_record_field( row, 1 );
1460
1461     component = MSI_RecordGetString( row, 2 );
1462     file->Component = get_loaded_component( package, component );
1463
1464     if (!file->Component)
1465     {
1466         WARN("Component not found: %s\n", debugstr_w(component));
1467         msi_free(file->File);
1468         msi_free(file);
1469         return ERROR_SUCCESS;
1470     }
1471
1472     file->FileName = msi_dup_record_field( row, 3 );
1473     reduce_to_longfilename( file->FileName );
1474
1475     file->ShortName = msi_dup_record_field( row, 3 );
1476     file->LongName = strdupW( folder_split_path(file->ShortName, '|'));
1477     
1478     file->FileSize = MSI_RecordGetInteger( row, 4 );
1479     file->Version = msi_dup_record_field( row, 5 );
1480     file->Language = msi_dup_record_field( row, 6 );
1481     file->Attributes = MSI_RecordGetInteger( row, 7 );
1482     file->Sequence = MSI_RecordGetInteger( row, 8 );
1483
1484     file->state = msifs_invalid;
1485
1486     /* if the compressed bits are not set in the file attributes,
1487      * then read the information from the package word count property
1488      */
1489     if (package->WordCount & msidbSumInfoSourceTypeAdminImage)
1490     {
1491         file->IsCompressed = FALSE;
1492     }
1493     else if (file->Attributes &
1494              (msidbFileAttributesCompressed | msidbFileAttributesPatchAdded))
1495     {
1496         file->IsCompressed = TRUE;
1497     }
1498     else if (file->Attributes & msidbFileAttributesNoncompressed)
1499     {
1500         file->IsCompressed = FALSE;
1501     }
1502     else
1503     {
1504         file->IsCompressed = package->WordCount & msidbSumInfoSourceTypeCompressed;
1505     }
1506
1507     load_file_hash(package, file);
1508
1509     TRACE("File Loaded (%s)\n",debugstr_w(file->File));  
1510
1511     list_add_tail( &package->files, &file->entry );
1512  
1513     return ERROR_SUCCESS;
1514 }
1515
1516 static UINT load_all_files(MSIPACKAGE *package)
1517 {
1518     MSIQUERY * view;
1519     UINT rc;
1520     static const WCHAR Query[] =
1521         {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1522          '`','F','i','l','e','`',' ', 'O','R','D','E','R',' ','B','Y',' ',
1523          '`','S','e','q','u','e','n','c','e','`', 0};
1524
1525     if (!list_empty(&package->files))
1526         return ERROR_SUCCESS;
1527
1528     rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
1529     if (rc != ERROR_SUCCESS)
1530         return ERROR_SUCCESS;
1531
1532     rc = MSI_IterateRecords(view, NULL, load_file, package);
1533     msiobj_release(&view->hdr);
1534
1535     return ERROR_SUCCESS;
1536 }
1537
1538 static UINT load_folder( MSIRECORD *row, LPVOID param )
1539 {
1540     MSIPACKAGE *package = param;
1541     static const WCHAR szDot[] = { '.',0 };
1542     static WCHAR szEmpty[] = { 0 };
1543     LPWSTR p, tgt_short, tgt_long, src_short, src_long;
1544     MSIFOLDER *folder;
1545
1546     folder = msi_alloc_zero( sizeof (MSIFOLDER) );
1547     if (!folder)
1548         return ERROR_NOT_ENOUGH_MEMORY;
1549
1550     folder->Directory = msi_dup_record_field( row, 1 );
1551
1552     TRACE("%s\n", debugstr_w(folder->Directory));
1553
1554     p = msi_dup_record_field(row, 3);
1555
1556     /* split src and target dir */
1557     tgt_short = p;
1558     src_short = folder_split_path( p, ':' );
1559
1560     /* split the long and short paths */
1561     tgt_long = folder_split_path( tgt_short, '|' );
1562     src_long = folder_split_path( src_short, '|' );
1563
1564     /* check for no-op dirs */
1565     if (!lstrcmpW(szDot, tgt_short))
1566         tgt_short = szEmpty;
1567     if (!lstrcmpW(szDot, src_short))
1568         src_short = szEmpty;
1569
1570     if (!tgt_long)
1571         tgt_long = tgt_short;
1572
1573     if (!src_short) {
1574         src_short = tgt_short;
1575         src_long = tgt_long;
1576     }
1577
1578     if (!src_long)
1579         src_long = src_short;
1580
1581     /* FIXME: use the target short path too */
1582     folder->TargetDefault = strdupW(tgt_long);
1583     folder->SourceShortPath = strdupW(src_short);
1584     folder->SourceLongPath = strdupW(src_long);
1585     msi_free(p);
1586
1587     TRACE("TargetDefault = %s\n",debugstr_w( folder->TargetDefault ));
1588     TRACE("SourceLong = %s\n", debugstr_w( folder->SourceLongPath ));
1589     TRACE("SourceShort = %s\n", debugstr_w( folder->SourceShortPath ));
1590
1591     folder->Parent = msi_dup_record_field( row, 2 );
1592
1593     folder->Property = msi_dup_property( package, folder->Directory );
1594
1595     list_add_tail( &package->folders, &folder->entry );
1596
1597     TRACE("returning %p\n", folder);
1598
1599     return ERROR_SUCCESS;
1600 }
1601
1602 static UINT load_all_folders( MSIPACKAGE *package )
1603 {
1604     static const WCHAR query[] = {
1605         'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
1606          '`','D','i','r','e','c','t','o','r','y','`',0 };
1607     MSIQUERY *view;
1608     UINT r;
1609
1610     if (!list_empty(&package->folders))
1611         return ERROR_SUCCESS;
1612
1613     r = MSI_DatabaseOpenViewW( package->db, query, &view );
1614     if (r != ERROR_SUCCESS)
1615         return r;
1616
1617     r = MSI_IterateRecords(view, NULL, load_folder, package);
1618     msiobj_release(&view->hdr);
1619     return r;
1620 }
1621
1622 /*
1623  * I am not doing any of the costing functionality yet.
1624  * Mostly looking at doing the Component and Feature loading
1625  *
1626  * The native MSI does A LOT of modification to tables here. Mostly adding
1627  * a lot of temporary columns to the Feature and Component tables.
1628  *
1629  *    note: Native msi also tracks the short filename. But I am only going to
1630  *          track the long ones.  Also looking at this directory table
1631  *          it appears that the directory table does not get the parents
1632  *          resolved base on property only based on their entries in the
1633  *          directory table.
1634  */
1635 static UINT ACTION_CostInitialize(MSIPACKAGE *package)
1636 {
1637     static const WCHAR szCosting[] =
1638         {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
1639     static const WCHAR szZero[] = { '0', 0 };
1640
1641     MSI_SetPropertyW(package, szCosting, szZero);
1642     MSI_SetPropertyW(package, cszRootDrive, c_colon);
1643
1644     load_all_folders( package );
1645     load_all_components( package );
1646     load_all_features( package );
1647     load_all_files( package );
1648
1649     return ERROR_SUCCESS;
1650 }
1651
1652 static UINT execute_script(MSIPACKAGE *package, UINT script )
1653 {
1654     UINT i;
1655     UINT rc = ERROR_SUCCESS;
1656
1657     TRACE("Executing Script %i\n",script);
1658
1659     if (!package->script)
1660     {
1661         ERR("no script!\n");
1662         return ERROR_FUNCTION_FAILED;
1663     }
1664
1665     for (i = 0; i < package->script->ActionCount[script]; i++)
1666     {
1667         LPWSTR action;
1668         action = package->script->Actions[script][i];
1669         ui_actionstart(package, action);
1670         TRACE("Executing Action (%s)\n",debugstr_w(action));
1671         rc = ACTION_PerformAction(package, action, script, TRUE);
1672         if (rc != ERROR_SUCCESS)
1673             break;
1674     }
1675     msi_free_action_script(package, script);
1676     return rc;
1677 }
1678
1679 static UINT ACTION_FileCost(MSIPACKAGE *package)
1680 {
1681     return ERROR_SUCCESS;
1682 }
1683
1684 static void ACTION_GetComponentInstallStates(MSIPACKAGE *package)
1685 {
1686     MSICOMPONENT *comp;
1687     INSTALLSTATE state;
1688     UINT r;
1689
1690     state = MsiQueryProductStateW(package->ProductCode);
1691
1692     LIST_FOR_EACH_ENTRY(comp, &package->components, MSICOMPONENT, entry)
1693     {
1694         if (!comp->ComponentId)
1695             continue;
1696
1697         if (state != INSTALLSTATE_LOCAL && state != INSTALLSTATE_DEFAULT)
1698             comp->Installed = INSTALLSTATE_ABSENT;
1699         else
1700         {
1701             r = MsiQueryComponentStateW(package->ProductCode, NULL,
1702                                         package->Context, comp->ComponentId,
1703                                         &comp->Installed);
1704             if (r != ERROR_SUCCESS)
1705                 comp->Installed = INSTALLSTATE_ABSENT;
1706         }
1707     }
1708 }
1709
1710 static void ACTION_GetFeatureInstallStates(MSIPACKAGE *package)
1711 {
1712     MSIFEATURE *feature;
1713     INSTALLSTATE state;
1714
1715     state = MsiQueryProductStateW(package->ProductCode);
1716
1717     LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1718     {
1719         if (state != INSTALLSTATE_LOCAL && state != INSTALLSTATE_DEFAULT)
1720             feature->Installed = INSTALLSTATE_ABSENT;
1721         else
1722         {
1723             feature->Installed = MsiQueryFeatureStateW(package->ProductCode,
1724                                                        feature->Feature);
1725         }
1726     }
1727 }
1728
1729 static BOOL process_state_property(MSIPACKAGE* package, int level,
1730                                    LPCWSTR property, INSTALLSTATE state)
1731 {
1732     static const WCHAR all[]={'A','L','L',0};
1733     static const WCHAR remove[] = {'R','E','M','O','V','E',0};
1734     LPWSTR override;
1735     MSIFEATURE *feature;
1736
1737     override = msi_dup_property( package, property );
1738     if (!override)
1739         return FALSE;
1740
1741     LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1742     {
1743         if (lstrcmpW(property, remove) &&
1744             (feature->Level <= 0 || feature->Level > level))
1745             continue;
1746
1747         if (strcmpiW(override,all)==0)
1748             msi_feature_set_state(package, feature, state);
1749         else
1750         {
1751             LPWSTR ptr = override;
1752             LPWSTR ptr2 = strchrW(override,',');
1753
1754             while (ptr)
1755             {
1756                 if ((ptr2 && strncmpW(ptr,feature->Feature, ptr2-ptr)==0)
1757                     || (!ptr2 && strcmpW(ptr,feature->Feature)==0))
1758                 {
1759                     msi_feature_set_state(package, feature, state);
1760                     break;
1761                 }
1762                 if (ptr2)
1763                 {
1764                     ptr=ptr2+1;
1765                     ptr2 = strchrW(ptr,',');
1766                 }
1767                 else
1768                     break;
1769             }
1770         }
1771     }
1772     msi_free(override);
1773
1774     return TRUE;
1775 }
1776
1777 UINT MSI_SetFeatureStates(MSIPACKAGE *package)
1778 {
1779     int level;
1780     static const WCHAR szlevel[] =
1781         {'I','N','S','T','A','L','L','L','E','V','E','L',0};
1782     static const WCHAR szAddLocal[] =
1783         {'A','D','D','L','O','C','A','L',0};
1784     static const WCHAR szAddSource[] =
1785         {'A','D','D','S','O','U','R','C','E',0};
1786     static const WCHAR szRemove[] =
1787         {'R','E','M','O','V','E',0};
1788     static const WCHAR szReinstall[] =
1789         {'R','E','I','N','S','T','A','L','L',0};
1790     BOOL override = FALSE;
1791     MSICOMPONENT* component;
1792     MSIFEATURE *feature;
1793
1794
1795     /* I do not know if this is where it should happen.. but */
1796
1797     TRACE("Checking Install Level\n");
1798
1799     level = msi_get_property_int(package, szlevel, 1);
1800
1801     /* ok here is the _real_ rub
1802      * all these activation/deactivation things happen in order and things
1803      * later on the list override things earlier on the list.
1804      * 1) INSTALLLEVEL processing
1805      * 2) ADDLOCAL
1806      * 3) REMOVE
1807      * 4) ADDSOURCE
1808      * 5) ADDDEFAULT
1809      * 6) REINSTALL
1810      * 7) COMPADDLOCAL
1811      * 8) COMPADDSOURCE
1812      * 9) FILEADDLOCAL
1813      * 10) FILEADDSOURCE
1814      * 11) FILEADDDEFAULT
1815      *
1816      * I am still ignoring a lot of these. But that is ok for now, ADDLOCAL and
1817      * REMOVE are the big ones, since we don't handle administrative installs
1818      * yet anyway.
1819      */
1820     override |= process_state_property(package, level, szAddLocal, INSTALLSTATE_LOCAL);
1821     override |= process_state_property(package, level, szRemove, INSTALLSTATE_ABSENT);
1822     override |= process_state_property(package, level, szAddSource, INSTALLSTATE_SOURCE);
1823     override |= process_state_property(package, level, szReinstall, INSTALLSTATE_LOCAL);
1824
1825     if (!override)
1826     {
1827         LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1828         {
1829             BOOL feature_state = ((feature->Level > 0) &&
1830                                   (feature->Level <= level));
1831
1832             if ((feature_state) && (feature->Action == INSTALLSTATE_UNKNOWN))
1833             {
1834                 if (feature->Attributes & msidbFeatureAttributesFavorSource)
1835                     msi_feature_set_state(package, feature, INSTALLSTATE_SOURCE);
1836                 else if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1837                     msi_feature_set_state(package, feature, INSTALLSTATE_ADVERTISED);
1838                 else
1839                     msi_feature_set_state(package, feature, INSTALLSTATE_LOCAL);
1840             }
1841         }
1842
1843         /* disable child features of unselected parent features */
1844         LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1845         {
1846             FeatureList *fl;
1847
1848             if (feature->Level > 0 && feature->Level <= level)
1849                 continue;
1850
1851             LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
1852                 msi_feature_set_state(package, fl->feature, INSTALLSTATE_UNKNOWN);
1853         }
1854     }
1855     else
1856     {
1857         /* set the Preselected Property */
1858         static const WCHAR szPreselected[] = {'P','r','e','s','e','l','e','c','t','e','d',0};
1859         static const WCHAR szOne[] = { '1', 0 };
1860
1861         MSI_SetPropertyW(package,szPreselected,szOne);
1862     }
1863
1864     /*
1865      * now we want to enable or disable components base on feature
1866      */
1867
1868     LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1869     {
1870         ComponentList *cl;
1871
1872         TRACE("Examining Feature %s (Level %i, Installed %i, Action %i)\n",
1873               debugstr_w(feature->Feature), feature->Level, feature->Installed, feature->Action);
1874
1875         if (!feature->Level)
1876             continue;
1877
1878         /* features with components that have compressed files are made local */
1879         LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1880         {
1881             if (cl->component->Enabled &&
1882                 cl->component->ForceLocalState &&
1883                 feature->Action == INSTALLSTATE_SOURCE)
1884             {
1885                 msi_feature_set_state(package, feature, INSTALLSTATE_LOCAL);
1886                 break;
1887             }
1888         }
1889
1890         LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1891         {
1892             component = cl->component;
1893
1894             if (!component->Enabled)
1895                 continue;
1896
1897             switch (feature->Action)
1898             {
1899             case INSTALLSTATE_ABSENT:
1900                 component->anyAbsent = 1;
1901                 break;
1902             case INSTALLSTATE_ADVERTISED:
1903                 component->hasAdvertiseFeature = 1;
1904                 break;
1905             case INSTALLSTATE_SOURCE:
1906                 component->hasSourceFeature = 1;
1907                 break;
1908             case INSTALLSTATE_LOCAL:
1909                 component->hasLocalFeature = 1;
1910                 break;
1911             case INSTALLSTATE_DEFAULT:
1912                 if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1913                     component->hasAdvertiseFeature = 1;
1914                 else if (feature->Attributes & msidbFeatureAttributesFavorSource)
1915                     component->hasSourceFeature = 1;
1916                 else
1917                     component->hasLocalFeature = 1;
1918                 break;
1919             default:
1920                 break;
1921             }
1922         }
1923     }
1924
1925     LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1926     {
1927         /* if the component isn't enabled, leave it alone */
1928         if (!component->Enabled)
1929             continue;
1930
1931         /* check if it's local or source */
1932         if (!(component->Attributes & msidbComponentAttributesOptional) &&
1933              (component->hasLocalFeature || component->hasSourceFeature))
1934         {
1935             if ((component->Attributes & msidbComponentAttributesSourceOnly) &&
1936                  !component->ForceLocalState)
1937                 msi_component_set_state(package, component, INSTALLSTATE_SOURCE);
1938             else
1939                 msi_component_set_state(package, component, INSTALLSTATE_LOCAL);
1940             continue;
1941         }
1942
1943         /* if any feature is local, the component must be local too */
1944         if (component->hasLocalFeature)
1945         {
1946             msi_component_set_state(package, component, INSTALLSTATE_LOCAL);
1947             continue;
1948         }
1949
1950         if (component->hasSourceFeature)
1951         {
1952             msi_component_set_state(package, component, INSTALLSTATE_SOURCE);
1953             continue;
1954         }
1955
1956         if (component->hasAdvertiseFeature)
1957         {
1958             msi_component_set_state(package, component, INSTALLSTATE_ADVERTISED);
1959             continue;
1960         }
1961
1962         TRACE("nobody wants component %s\n", debugstr_w(component->Component));
1963         if (component->anyAbsent)
1964             msi_component_set_state(package, component, INSTALLSTATE_ABSENT);
1965     }
1966
1967     LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1968     {
1969         if (component->Action == INSTALLSTATE_DEFAULT)
1970         {
1971             TRACE("%s was default, setting to local\n", debugstr_w(component->Component));
1972             msi_component_set_state(package, component, INSTALLSTATE_LOCAL);
1973         }
1974
1975         TRACE("Result: Component %s (Installed %i, Action %i)\n",
1976             debugstr_w(component->Component), component->Installed, component->Action);
1977     }
1978
1979
1980     return ERROR_SUCCESS;
1981 }
1982
1983 static UINT ITERATE_CostFinalizeDirectories(MSIRECORD *row, LPVOID param)
1984 {
1985     MSIPACKAGE *package = (MSIPACKAGE*)param;
1986     LPCWSTR name;
1987     LPWSTR path;
1988     MSIFOLDER *f;
1989
1990     name = MSI_RecordGetString(row,1);
1991
1992     f = get_loaded_folder(package, name);
1993     if (!f) return ERROR_SUCCESS;
1994
1995     /* reset the ResolvedTarget */
1996     msi_free(f->ResolvedTarget);
1997     f->ResolvedTarget = NULL;
1998
1999     /* This helper function now does ALL the work */
2000     TRACE("Dir %s ...\n",debugstr_w(name));
2001     path = resolve_folder(package,name,FALSE,TRUE,TRUE,NULL);
2002     TRACE("resolves to %s\n",debugstr_w(path));
2003     msi_free(path);
2004
2005     return ERROR_SUCCESS;
2006 }
2007
2008 static UINT ITERATE_CostFinalizeConditions(MSIRECORD *row, LPVOID param)
2009 {
2010     MSIPACKAGE *package = (MSIPACKAGE*)param;
2011     LPCWSTR name;
2012     MSIFEATURE *feature;
2013
2014     name = MSI_RecordGetString( row, 1 );
2015
2016     feature = get_loaded_feature( package, name );
2017     if (!feature)
2018         ERR("FAILED to find loaded feature %s\n",debugstr_w(name));
2019     else
2020     {
2021         LPCWSTR Condition;
2022         Condition = MSI_RecordGetString(row,3);
2023
2024         if (MSI_EvaluateConditionW(package,Condition) == MSICONDITION_TRUE)
2025         {
2026             int level = MSI_RecordGetInteger(row,2);
2027             TRACE("Resetting feature %s to level %i\n", debugstr_w(name), level);
2028             feature->Level = level;
2029         }
2030     }
2031     return ERROR_SUCCESS;
2032 }
2033
2034 static LPWSTR msi_get_disk_file_version( LPCWSTR filename )
2035 {
2036     static const WCHAR name_fmt[] =
2037         {'%','u','.','%','u','.','%','u','.','%','u',0};
2038     static const WCHAR name[] = {'\\',0};
2039     VS_FIXEDFILEINFO *lpVer;
2040     WCHAR filever[0x100];
2041     LPVOID version;
2042     DWORD versize;
2043     DWORD handle;
2044     UINT sz;
2045
2046     TRACE("%s\n", debugstr_w(filename));
2047
2048     versize = GetFileVersionInfoSizeW( filename, &handle );
2049     if (!versize)
2050         return NULL;
2051
2052     version = msi_alloc( versize );
2053     GetFileVersionInfoW( filename, 0, versize, version );
2054
2055     if (!VerQueryValueW( version, name, (LPVOID*)&lpVer, &sz ))
2056     {
2057         msi_free( version );
2058         return NULL;
2059     }
2060
2061     sprintfW( filever, name_fmt,
2062         HIWORD(lpVer->dwFileVersionMS),
2063         LOWORD(lpVer->dwFileVersionMS),
2064         HIWORD(lpVer->dwFileVersionLS),
2065         LOWORD(lpVer->dwFileVersionLS));
2066
2067     msi_free( version );
2068
2069     return strdupW( filever );
2070 }
2071
2072 static UINT msi_check_file_install_states( MSIPACKAGE *package )
2073 {
2074     LPWSTR file_version;
2075     MSIFILE *file;
2076
2077     LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2078     {
2079         MSICOMPONENT* comp = file->Component;
2080         LPWSTR p;
2081
2082         if (!comp)
2083             continue;
2084
2085         if (file->IsCompressed)
2086             comp->ForceLocalState = TRUE;
2087
2088         /* calculate target */
2089         p = resolve_folder(package, comp->Directory, FALSE, FALSE, TRUE, NULL);
2090
2091         msi_free(file->TargetPath);
2092
2093         TRACE("file %s is named %s\n",
2094                debugstr_w(file->File), debugstr_w(file->FileName));
2095
2096         file->TargetPath = build_directory_name(2, p, file->FileName);
2097
2098         msi_free(p);
2099
2100         TRACE("file %s resolves to %s\n",
2101                debugstr_w(file->File), debugstr_w(file->TargetPath));
2102
2103         /* don't check files of components that aren't installed */
2104         if (comp->Installed == INSTALLSTATE_UNKNOWN ||
2105             comp->Installed == INSTALLSTATE_ABSENT)
2106         {
2107             file->state = msifs_missing;  /* assume files are missing */
2108             continue;
2109         }
2110
2111         if (GetFileAttributesW(file->TargetPath) == INVALID_FILE_ATTRIBUTES)
2112         {
2113             file->state = msifs_missing;
2114             comp->Cost += file->FileSize;
2115             continue;
2116         }
2117
2118         if (file->Version &&
2119             (file_version = msi_get_disk_file_version( file->TargetPath )))
2120         {
2121             TRACE("new %s old %s\n", debugstr_w(file->Version),
2122                   debugstr_w(file_version));
2123             /* FIXME: seems like a bad way to compare version numbers */
2124             if (lstrcmpiW(file_version, file->Version)<0)
2125             {
2126                 file->state = msifs_overwrite;
2127                 comp->Cost += file->FileSize;
2128             }
2129             else
2130                 file->state = msifs_present;
2131             msi_free( file_version );
2132         }
2133         else
2134             file->state = msifs_present;
2135     }
2136
2137     return ERROR_SUCCESS;
2138 }
2139
2140 /*
2141  * A lot is done in this function aside from just the costing.
2142  * The costing needs to be implemented at some point but for now I am going
2143  * to focus on the directory building
2144  *
2145  */
2146 static UINT ACTION_CostFinalize(MSIPACKAGE *package)
2147 {
2148     static const WCHAR ExecSeqQuery[] =
2149         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2150          '`','D','i','r','e','c','t','o','r','y','`',0};
2151     static const WCHAR ConditionQuery[] =
2152         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2153          '`','C','o','n','d','i','t','i','o','n','`',0};
2154     static const WCHAR szCosting[] =
2155         {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
2156     static const WCHAR szlevel[] =
2157         {'I','N','S','T','A','L','L','L','E','V','E','L',0};
2158     static const WCHAR szOutOfDiskSpace[] =
2159         {'O','u','t','O','f','D','i','s','k','S','p','a','c','e',0};
2160     static const WCHAR szOne[] = { '1', 0 };
2161     static const WCHAR szZero[] = { '0', 0 };
2162     MSICOMPONENT *comp;
2163     UINT rc;
2164     MSIQUERY * view;
2165     LPWSTR level;
2166
2167     TRACE("Building Directory properties\n");
2168
2169     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2170     if (rc == ERROR_SUCCESS)
2171     {
2172         rc = MSI_IterateRecords(view, NULL, ITERATE_CostFinalizeDirectories,
2173                         package);
2174         msiobj_release(&view->hdr);
2175     }
2176
2177     /* read components states from the registry */
2178     ACTION_GetComponentInstallStates(package);
2179     ACTION_GetFeatureInstallStates(package);
2180
2181     TRACE("File calculations\n");
2182     msi_check_file_install_states( package );
2183
2184     TRACE("Evaluating Condition Table\n");
2185
2186     rc = MSI_DatabaseOpenViewW(package->db, ConditionQuery, &view);
2187     if (rc == ERROR_SUCCESS)
2188     {
2189         rc = MSI_IterateRecords(view, NULL, ITERATE_CostFinalizeConditions,
2190                     package);
2191         msiobj_release(&view->hdr);
2192     }
2193
2194     TRACE("Enabling or Disabling Components\n");
2195     LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2196     {
2197         if (MSI_EvaluateConditionW(package, comp->Condition) == MSICONDITION_FALSE)
2198         {
2199             TRACE("Disabling component %s\n", debugstr_w(comp->Component));
2200             comp->Enabled = FALSE;
2201         }
2202         else
2203             comp->Enabled = TRUE;
2204     }
2205
2206     MSI_SetPropertyW(package,szCosting,szOne);
2207     /* set default run level if not set */
2208     level = msi_dup_property( package, szlevel );
2209     if (!level)
2210         MSI_SetPropertyW(package,szlevel, szOne);
2211     msi_free(level);
2212
2213     /* FIXME: check volume disk space */
2214     MSI_SetPropertyW(package, szOutOfDiskSpace, szZero);
2215
2216     return MSI_SetFeatureStates(package);
2217 }
2218
2219 /* OK this value is "interpreted" and then formatted based on the 
2220    first few characters */
2221 static LPSTR parse_value(MSIPACKAGE *package, LPCWSTR value, DWORD *type, 
2222                          DWORD *size)
2223 {
2224     LPSTR data = NULL;
2225
2226     if (value[0]=='#' && value[1]!='#' && value[1]!='%')
2227     {
2228         if (value[1]=='x')
2229         {
2230             LPWSTR ptr;
2231             CHAR byte[5];
2232             LPWSTR deformated = NULL;
2233             int count;
2234
2235             deformat_string(package, &value[2], &deformated);
2236
2237             /* binary value type */
2238             ptr = deformated;
2239             *type = REG_BINARY;
2240             if (strlenW(ptr)%2)
2241                 *size = (strlenW(ptr)/2)+1;
2242             else
2243                 *size = strlenW(ptr)/2;
2244
2245             data = msi_alloc(*size);
2246
2247             byte[0] = '0'; 
2248             byte[1] = 'x'; 
2249             byte[4] = 0; 
2250             count = 0;
2251             /* if uneven pad with a zero in front */
2252             if (strlenW(ptr)%2)
2253             {
2254                 byte[2]= '0';
2255                 byte[3]= *ptr;
2256                 ptr++;
2257                 data[count] = (BYTE)strtol(byte,NULL,0);
2258                 count ++;
2259                 TRACE("Uneven byte count\n");
2260             }
2261             while (*ptr)
2262             {
2263                 byte[2]= *ptr;
2264                 ptr++;
2265                 byte[3]= *ptr;
2266                 ptr++;
2267                 data[count] = (BYTE)strtol(byte,NULL,0);
2268                 count ++;
2269             }
2270             msi_free(deformated);
2271
2272             TRACE("Data %i bytes(%i)\n",*size,count);
2273         }
2274         else
2275         {
2276             LPWSTR deformated;
2277             LPWSTR p;
2278             DWORD d = 0;
2279             deformat_string(package, &value[1], &deformated);
2280
2281             *type=REG_DWORD; 
2282             *size = sizeof(DWORD);
2283             data = msi_alloc(*size);
2284             p = deformated;
2285             if (*p == '-')
2286                 p++;
2287             while (*p)
2288             {
2289                 if ( (*p < '0') || (*p > '9') )
2290                     break;
2291                 d *= 10;
2292                 d += (*p - '0');
2293                 p++;
2294             }
2295             if (deformated[0] == '-')
2296                 d = -d;
2297             *(LPDWORD)data = d;
2298             TRACE("DWORD %i\n",*(LPDWORD)data);
2299
2300             msi_free(deformated);
2301         }
2302     }
2303     else
2304     {
2305         static const WCHAR szMulti[] = {'[','~',']',0};
2306         LPCWSTR ptr;
2307         *type=REG_SZ;
2308
2309         if (value[0]=='#')
2310         {
2311             if (value[1]=='%')
2312             {
2313                 ptr = &value[2];
2314                 *type=REG_EXPAND_SZ;
2315             }
2316             else
2317                 ptr = &value[1];
2318          }
2319          else
2320             ptr=value;
2321
2322         if (strstrW(value,szMulti))
2323             *type = REG_MULTI_SZ;
2324
2325         /* remove initial delimiter */
2326         if (!strncmpW(value, szMulti, 3))
2327             ptr = value + 3;
2328
2329         *size = deformat_string(package, ptr,(LPWSTR*)&data);
2330
2331         /* add double NULL terminator */
2332         if (*type == REG_MULTI_SZ)
2333         {
2334             *size += 2 * sizeof(WCHAR); /* two NULL terminators */
2335             data = msi_realloc_zero(data, *size);
2336         }
2337     }
2338     return data;
2339 }
2340
2341 static UINT ITERATE_WriteRegistryValues(MSIRECORD *row, LPVOID param)
2342 {
2343     MSIPACKAGE *package = (MSIPACKAGE*)param;
2344     static const WCHAR szHCR[] = 
2345         {'H','K','E','Y','_','C','L','A','S','S','E','S','_',
2346          'R','O','O','T','\\',0};
2347     static const WCHAR szHCU[] =
2348         {'H','K','E','Y','_','C','U','R','R','E','N','T','_',
2349          'U','S','E','R','\\',0};
2350     static const WCHAR szHLM[] =
2351         {'H','K','E','Y','_','L','O','C','A','L','_',
2352          'M','A','C','H','I','N','E','\\',0};
2353     static const WCHAR szHU[] =
2354         {'H','K','E','Y','_','U','S','E','R','S','\\',0};
2355
2356     LPSTR value_data = NULL;
2357     HKEY  root_key, hkey;
2358     DWORD type,size;
2359     LPWSTR  deformated;
2360     LPCWSTR szRoot, component, name, key, value;
2361     MSICOMPONENT *comp;
2362     MSIRECORD * uirow;
2363     LPWSTR uikey;
2364     INT   root;
2365     BOOL check_first = FALSE;
2366     UINT rc;
2367
2368     ui_progress(package,2,0,0,0);
2369
2370     value = NULL;
2371     key = NULL;
2372     uikey = NULL;
2373     name = NULL;
2374
2375     component = MSI_RecordGetString(row, 6);
2376     comp = get_loaded_component(package,component);
2377     if (!comp)
2378         return ERROR_SUCCESS;
2379
2380     if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
2381     {
2382         TRACE("Skipping write due to disabled component %s\n",
2383                         debugstr_w(component));
2384
2385         comp->Action = comp->Installed;
2386
2387         return ERROR_SUCCESS;
2388     }
2389
2390     comp->Action = INSTALLSTATE_LOCAL;
2391
2392     name = MSI_RecordGetString(row, 4);
2393     if( MSI_RecordIsNull(row,5) && name )
2394     {
2395         /* null values can have special meanings */
2396         if (name[0]=='-' && name[1] == 0)
2397                 return ERROR_SUCCESS;
2398         else if ((name[0]=='+' && name[1] == 0) || 
2399                  (name[0] == '*' && name[1] == 0))
2400                 name = NULL;
2401         check_first = TRUE;
2402     }
2403
2404     root = MSI_RecordGetInteger(row,2);
2405     key = MSI_RecordGetString(row, 3);
2406
2407     /* get the root key */
2408     switch (root)
2409     {
2410         case -1: 
2411             {
2412                 static const WCHAR szALLUSER[] = {'A','L','L','U','S','E','R','S',0};
2413                 LPWSTR all_users = msi_dup_property( package, szALLUSER );
2414                 if (all_users && all_users[0] == '1')
2415                 {
2416                     root_key = HKEY_LOCAL_MACHINE;
2417                     szRoot = szHLM;
2418                 }
2419                 else
2420                 {
2421                     root_key = HKEY_CURRENT_USER;
2422                     szRoot = szHCU;
2423                 }
2424                 msi_free(all_users);
2425             }
2426                  break;
2427         case 0:  root_key = HKEY_CLASSES_ROOT; 
2428                  szRoot = szHCR;
2429                  break;
2430         case 1:  root_key = HKEY_CURRENT_USER;
2431                  szRoot = szHCU;
2432                  break;
2433         case 2:  root_key = HKEY_LOCAL_MACHINE;
2434                  szRoot = szHLM;
2435                  break;
2436         case 3:  root_key = HKEY_USERS; 
2437                  szRoot = szHU;
2438                  break;
2439         default:
2440                  ERR("Unknown root %i\n",root);
2441                  root_key=NULL;
2442                  szRoot = NULL;
2443                  break;
2444     }
2445     if (!root_key)
2446         return ERROR_SUCCESS;
2447
2448     deformat_string(package, key , &deformated);
2449     size = strlenW(deformated) + strlenW(szRoot) + 1;
2450     uikey = msi_alloc(size*sizeof(WCHAR));
2451     strcpyW(uikey,szRoot);
2452     strcatW(uikey,deformated);
2453
2454     if (RegCreateKeyW( root_key, deformated, &hkey))
2455     {
2456         ERR("Could not create key %s\n",debugstr_w(deformated));
2457         msi_free(deformated);
2458         msi_free(uikey);
2459         return ERROR_SUCCESS;
2460     }
2461     msi_free(deformated);
2462
2463     value = MSI_RecordGetString(row,5);
2464     if (value)
2465         value_data = parse_value(package, value, &type, &size); 
2466     else
2467     {
2468         static const WCHAR szEmpty[] = {0};
2469         value_data = (LPSTR)strdupW(szEmpty);
2470         size = sizeof(szEmpty);
2471         type = REG_SZ;
2472     }
2473
2474     deformat_string(package, name, &deformated);
2475
2476     if (!check_first)
2477     {
2478         TRACE("Setting value %s of %s\n",debugstr_w(deformated),
2479                         debugstr_w(uikey));
2480         RegSetValueExW(hkey, deformated, 0, type, (LPBYTE)value_data, size);
2481     }
2482     else
2483     {
2484         DWORD sz = 0;
2485         rc = RegQueryValueExW(hkey, deformated, NULL, NULL, NULL, &sz);
2486         if (rc == ERROR_SUCCESS || rc == ERROR_MORE_DATA)
2487         {
2488             TRACE("value %s of %s checked already exists\n",
2489                             debugstr_w(deformated), debugstr_w(uikey));
2490         }
2491         else
2492         {
2493             TRACE("Checked and setting value %s of %s\n",
2494                             debugstr_w(deformated), debugstr_w(uikey));
2495             if (deformated || size)
2496                 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE) value_data, size);
2497         }
2498     }
2499     RegCloseKey(hkey);
2500
2501     uirow = MSI_CreateRecord(3);
2502     MSI_RecordSetStringW(uirow,2,deformated);
2503     MSI_RecordSetStringW(uirow,1,uikey);
2504
2505     if (type == REG_SZ)
2506         MSI_RecordSetStringW(uirow,3,(LPWSTR)value_data);
2507     else
2508         MSI_RecordSetStringW(uirow,3,value);
2509
2510     ui_actiondata(package,szWriteRegistryValues,uirow);
2511     msiobj_release( &uirow->hdr );
2512
2513     msi_free(value_data);
2514     msi_free(deformated);
2515     msi_free(uikey);
2516
2517     return ERROR_SUCCESS;
2518 }
2519
2520 static UINT ACTION_WriteRegistryValues(MSIPACKAGE *package)
2521 {
2522     UINT rc;
2523     MSIQUERY * view;
2524     static const WCHAR ExecSeqQuery[] =
2525         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2526          '`','R','e','g','i','s','t','r','y','`',0 };
2527
2528     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2529     if (rc != ERROR_SUCCESS)
2530         return ERROR_SUCCESS;
2531
2532     /* increment progress bar each time action data is sent */
2533     ui_progress(package,1,REG_PROGRESS_VALUE,1,0);
2534
2535     rc = MSI_IterateRecords(view, NULL, ITERATE_WriteRegistryValues, package);
2536
2537     msiobj_release(&view->hdr);
2538     return rc;
2539 }
2540
2541 static UINT ACTION_InstallInitialize(MSIPACKAGE *package)
2542 {
2543     package->script->CurrentlyScripting = TRUE;
2544
2545     return ERROR_SUCCESS;
2546 }
2547
2548
2549 static UINT ACTION_InstallValidate(MSIPACKAGE *package)
2550 {
2551     MSICOMPONENT *comp;
2552     DWORD progress = 0;
2553     DWORD total = 0;
2554     static const WCHAR q1[]=
2555         {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
2556          '`','R','e','g','i','s','t','r','y','`',0};
2557     UINT rc;
2558     MSIQUERY * view;
2559     MSIFEATURE *feature;
2560     MSIFILE *file;
2561
2562     TRACE("InstallValidate\n");
2563
2564     rc = MSI_DatabaseOpenViewW(package->db, q1, &view);
2565     if (rc == ERROR_SUCCESS)
2566     {
2567         MSI_IterateRecords( view, &progress, NULL, package );
2568         msiobj_release( &view->hdr );
2569         total += progress * REG_PROGRESS_VALUE;
2570     }
2571
2572     LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2573         total += COMPONENT_PROGRESS_VALUE;
2574
2575     LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2576         total += file->FileSize;
2577
2578     ui_progress(package,0,total,0,0);
2579
2580     LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2581     {
2582         TRACE("Feature: %s; Installed: %i; Action %i; Request %i\n",
2583             debugstr_w(feature->Feature), feature->Installed, feature->Action,
2584             feature->ActionRequest);
2585     }
2586     
2587     return ERROR_SUCCESS;
2588 }
2589
2590 static UINT ITERATE_LaunchConditions(MSIRECORD *row, LPVOID param)
2591 {
2592     MSIPACKAGE* package = (MSIPACKAGE*)param;
2593     LPCWSTR cond = NULL; 
2594     LPCWSTR message = NULL;
2595     UINT r;
2596
2597     static const WCHAR title[]=
2598         {'I','n','s','t','a','l','l',' ','F','a', 'i','l','e','d',0};
2599
2600     cond = MSI_RecordGetString(row,1);
2601
2602     r = MSI_EvaluateConditionW(package,cond);
2603     if (r == MSICONDITION_FALSE)
2604     {
2605         if ((gUILevel & INSTALLUILEVEL_MASK) != INSTALLUILEVEL_NONE)
2606         {
2607             LPWSTR deformated;
2608             message = MSI_RecordGetString(row,2);
2609             deformat_string(package,message,&deformated);
2610             MessageBoxW(NULL,deformated,title,MB_OK);
2611             msi_free(deformated);
2612         }
2613
2614         return ERROR_INSTALL_FAILURE;
2615     }
2616
2617     return ERROR_SUCCESS;
2618 }
2619
2620 static UINT ACTION_LaunchConditions(MSIPACKAGE *package)
2621 {
2622     UINT rc;
2623     MSIQUERY * view = NULL;
2624     static const WCHAR ExecSeqQuery[] =
2625         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2626          '`','L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','`',0};
2627
2628     TRACE("Checking launch conditions\n");
2629
2630     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2631     if (rc != ERROR_SUCCESS)
2632         return ERROR_SUCCESS;
2633
2634     rc = MSI_IterateRecords(view, NULL, ITERATE_LaunchConditions, package);
2635     msiobj_release(&view->hdr);
2636
2637     return rc;
2638 }
2639
2640 static LPWSTR resolve_keypath( MSIPACKAGE* package, MSICOMPONENT *cmp )
2641 {
2642
2643     if (!cmp->KeyPath)
2644         return resolve_folder(package,cmp->Directory,FALSE,FALSE,TRUE,NULL);
2645
2646     if (cmp->Attributes & msidbComponentAttributesRegistryKeyPath)
2647     {
2648         MSIRECORD * row = 0;
2649         UINT root,len;
2650         LPWSTR deformated,buffer,deformated_name;
2651         LPCWSTR key,name;
2652         static const WCHAR ExecSeqQuery[] =
2653             {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2654              '`','R','e','g','i','s','t','r','y','`',' ',
2655              'W','H','E','R','E',' ', '`','R','e','g','i','s','t','r','y','`',
2656              ' ','=',' ' ,'\'','%','s','\'',0 };
2657         static const WCHAR fmt[]={'%','0','2','i',':','\\','%','s','\\',0};
2658         static const WCHAR fmt2[]=
2659             {'%','0','2','i',':','\\','%','s','\\','%','s',0};
2660
2661         row = MSI_QueryGetRecord(package->db, ExecSeqQuery,cmp->KeyPath);
2662         if (!row)
2663             return NULL;
2664
2665         root = MSI_RecordGetInteger(row,2);
2666         key = MSI_RecordGetString(row, 3);
2667         name = MSI_RecordGetString(row, 4);
2668         deformat_string(package, key , &deformated);
2669         deformat_string(package, name, &deformated_name);
2670
2671         len = strlenW(deformated) + 6;
2672         if (deformated_name)
2673             len+=strlenW(deformated_name);
2674
2675         buffer = msi_alloc( len *sizeof(WCHAR));
2676
2677         if (deformated_name)
2678             sprintfW(buffer,fmt2,root,deformated,deformated_name);
2679         else
2680             sprintfW(buffer,fmt,root,deformated);
2681
2682         msi_free(deformated);
2683         msi_free(deformated_name);
2684         msiobj_release(&row->hdr);
2685
2686         return buffer;
2687     }
2688     else if (cmp->Attributes & msidbComponentAttributesODBCDataSource)
2689     {
2690         FIXME("UNIMPLEMENTED keypath as ODBC Source\n");
2691         return NULL;
2692     }
2693     else
2694     {
2695         MSIFILE *file = get_loaded_file( package, cmp->KeyPath );
2696
2697         if (file)
2698             return strdupW( file->TargetPath );
2699     }
2700     return NULL;
2701 }
2702
2703 static HKEY openSharedDLLsKey(void)
2704 {
2705     HKEY hkey=0;
2706     static const WCHAR path[] =
2707         {'S','o','f','t','w','a','r','e','\\',
2708          'M','i','c','r','o','s','o','f','t','\\',
2709          'W','i','n','d','o','w','s','\\',
2710          'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
2711          'S','h','a','r','e','d','D','L','L','s',0};
2712
2713     RegCreateKeyW(HKEY_LOCAL_MACHINE,path,&hkey);
2714     return hkey;
2715 }
2716
2717 static UINT ACTION_GetSharedDLLsCount(LPCWSTR dll)
2718 {
2719     HKEY hkey;
2720     DWORD count=0;
2721     DWORD type;
2722     DWORD sz = sizeof(count);
2723     DWORD rc;
2724     
2725     hkey = openSharedDLLsKey();
2726     rc = RegQueryValueExW(hkey, dll, NULL, &type, (LPBYTE)&count, &sz);
2727     if (rc != ERROR_SUCCESS)
2728         count = 0;
2729     RegCloseKey(hkey);
2730     return count;
2731 }
2732
2733 static UINT ACTION_WriteSharedDLLsCount(LPCWSTR path, UINT count)
2734 {
2735     HKEY hkey;
2736
2737     hkey = openSharedDLLsKey();
2738     if (count > 0)
2739         msi_reg_set_val_dword( hkey, path, count );
2740     else
2741         RegDeleteValueW(hkey,path);
2742     RegCloseKey(hkey);
2743     return count;
2744 }
2745
2746 /*
2747  * Return TRUE if the count should be written out and FALSE if not
2748  */
2749 static void ACTION_RefCountComponent( MSIPACKAGE* package, MSICOMPONENT *comp )
2750 {
2751     MSIFEATURE *feature;
2752     INT count = 0;
2753     BOOL write = FALSE;
2754
2755     /* only refcount DLLs */
2756     if (comp->KeyPath == NULL || 
2757         comp->Attributes & msidbComponentAttributesRegistryKeyPath || 
2758         comp->Attributes & msidbComponentAttributesODBCDataSource)
2759         write = FALSE;
2760     else
2761     {
2762         count = ACTION_GetSharedDLLsCount( comp->FullKeypath);
2763         write = (count > 0);
2764
2765         if (comp->Attributes & msidbComponentAttributesSharedDllRefCount)
2766             write = TRUE;
2767     }
2768
2769     /* increment counts */
2770     LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2771     {
2772         ComponentList *cl;
2773
2774         if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_LOCAL ))
2775             continue;
2776
2777         LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
2778         {
2779             if ( cl->component == comp )
2780                 count++;
2781         }
2782     }
2783
2784     /* decrement counts */
2785     LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2786     {
2787         ComponentList *cl;
2788
2789         if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_ABSENT ))
2790             continue;
2791
2792         LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
2793         {
2794             if ( cl->component == comp )
2795                 count--;
2796         }
2797     }
2798
2799     /* ref count all the files in the component */
2800     if (write)
2801     {
2802         MSIFILE *file;
2803
2804         LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2805         {
2806             if (file->Component == comp)
2807                 ACTION_WriteSharedDLLsCount( file->TargetPath, count );
2808         }
2809     }
2810     
2811     /* add a count for permanent */
2812     if (comp->Attributes & msidbComponentAttributesPermanent)
2813         count ++;
2814     
2815     comp->RefCount = count;
2816
2817     if (write)
2818         ACTION_WriteSharedDLLsCount( comp->FullKeypath, comp->RefCount );
2819 }
2820
2821 static UINT ACTION_ProcessComponents(MSIPACKAGE *package)
2822 {
2823     WCHAR squished_pc[GUID_SIZE];
2824     WCHAR squished_cc[GUID_SIZE];
2825     UINT rc;
2826     MSICOMPONENT *comp;
2827     HKEY hkey;
2828
2829     TRACE("\n");
2830
2831     squash_guid(package->ProductCode,squished_pc);
2832     ui_progress(package,1,COMPONENT_PROGRESS_VALUE,1,0);
2833
2834     LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2835     {
2836         MSIRECORD * uirow;
2837
2838         ui_progress(package,2,0,0,0);
2839         if (!comp->ComponentId)
2840             continue;
2841
2842         squash_guid(comp->ComponentId,squished_cc);
2843
2844         msi_free(comp->FullKeypath);
2845         comp->FullKeypath = resolve_keypath( package, comp );
2846
2847         ACTION_RefCountComponent( package, comp );
2848
2849         TRACE("Component %s (%s), Keypath=%s, RefCount=%i\n",
2850                             debugstr_w(comp->Component),
2851                             debugstr_w(squished_cc),
2852                             debugstr_w(comp->FullKeypath),
2853                             comp->RefCount);
2854
2855         if (ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL) ||
2856             ACTION_VerifyComponentForAction( comp, INSTALLSTATE_SOURCE))
2857         {
2858             if (!comp->FullKeypath)
2859                 continue;
2860
2861             if (package->Context == MSIINSTALLCONTEXT_MACHINE)
2862                 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, szLocalSid,
2863                                                      &hkey, TRUE);
2864             else
2865                 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, NULL,
2866                                                      &hkey, TRUE);
2867
2868             if (rc != ERROR_SUCCESS)
2869                 continue;
2870
2871             if (comp->Attributes & msidbComponentAttributesPermanent)
2872             {
2873                 static const WCHAR szPermKey[] =
2874                     { '0','0','0','0','0','0','0','0','0','0','0','0',
2875                       '0','0','0','0','0','0','0','0','0','0','0','0',
2876                       '0','0','0','0','0','0','0','0',0 };
2877
2878                 msi_reg_set_val_str(hkey, szPermKey, comp->FullKeypath);
2879             }
2880
2881             if (comp->Action == INSTALLSTATE_LOCAL)
2882                 msi_reg_set_val_str(hkey, squished_pc, comp->FullKeypath);
2883             else
2884             {
2885                 MSIFILE *file;
2886                 MSIRECORD *row;
2887                 LPWSTR ptr, ptr2;
2888                 WCHAR source[MAX_PATH];
2889                 WCHAR base[MAX_PATH];
2890                 LPWSTR sourcepath;
2891
2892                 static const WCHAR fmt[] = {'%','0','2','d','\\',0};
2893                 static const WCHAR query[] = {
2894                     'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
2895                     '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
2896                     '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ',
2897                     '>','=',' ','%','i',' ','O','R','D','E','R',' ','B','Y',' ',
2898                     '`','D','i','s','k','I','d','`',0};
2899
2900                 file = get_loaded_file(package, comp->KeyPath);
2901                 if (!file)
2902                     continue;
2903
2904                 row = MSI_QueryGetRecord(package->db, query, file->Sequence);
2905                 sprintfW(source, fmt, MSI_RecordGetInteger(row, 1));
2906                 ptr2 = strrchrW(source, '\\') + 1;
2907                 msiobj_release(&row->hdr);
2908
2909                 lstrcpyW(base, package->PackagePath);
2910                 ptr = strrchrW(base, '\\');
2911                 *(ptr + 1) = '\0';
2912
2913                 sourcepath = resolve_file_source(package, file);
2914                 ptr = sourcepath + lstrlenW(base);
2915                 lstrcpyW(ptr2, ptr);
2916                 msi_free(sourcepath);
2917
2918                 msi_reg_set_val_str(hkey, squished_pc, source);
2919             }
2920             RegCloseKey(hkey);
2921         }
2922         else if (ACTION_VerifyComponentForAction(comp, INSTALLSTATE_ABSENT))
2923         {
2924             if (package->Context == MSIINSTALLCONTEXT_MACHINE)
2925                 MSIREG_DeleteUserDataComponentKey(comp->ComponentId, szLocalSid);
2926             else
2927                 MSIREG_DeleteUserDataComponentKey(comp->ComponentId, NULL);
2928         }
2929
2930         /* UI stuff */
2931         uirow = MSI_CreateRecord(3);
2932         MSI_RecordSetStringW(uirow,1,package->ProductCode);
2933         MSI_RecordSetStringW(uirow,2,comp->ComponentId);
2934         MSI_RecordSetStringW(uirow,3,comp->FullKeypath);
2935         ui_actiondata(package,szProcessComponents,uirow);
2936         msiobj_release( &uirow->hdr );
2937     }
2938
2939     return ERROR_SUCCESS;
2940 }
2941
2942 typedef struct {
2943     CLSID       clsid;
2944     LPWSTR      source;
2945
2946     LPWSTR      path;
2947     ITypeLib    *ptLib;
2948 } typelib_struct;
2949
2950 static BOOL CALLBACK Typelib_EnumResNameProc( HMODULE hModule, LPCWSTR lpszType, 
2951                                        LPWSTR lpszName, LONG_PTR lParam)
2952 {
2953     TLIBATTR *attr;
2954     typelib_struct *tl_struct = (typelib_struct*) lParam;
2955     static const WCHAR fmt[] = {'%','s','\\','%','i',0};
2956     int sz; 
2957     HRESULT res;
2958
2959     if (!IS_INTRESOURCE(lpszName))
2960     {
2961         ERR("Not Int Resource Name %s\n",debugstr_w(lpszName));
2962         return TRUE;
2963     }
2964
2965     sz = strlenW(tl_struct->source)+4;
2966     sz *= sizeof(WCHAR);
2967
2968     if ((INT_PTR)lpszName == 1)
2969         tl_struct->path = strdupW(tl_struct->source);
2970     else
2971     {
2972         tl_struct->path = msi_alloc(sz);
2973         sprintfW(tl_struct->path,fmt,tl_struct->source, lpszName);
2974     }
2975
2976     TRACE("trying %s\n", debugstr_w(tl_struct->path));
2977     res = LoadTypeLib(tl_struct->path,&tl_struct->ptLib);
2978     if (FAILED(res))
2979     {
2980         msi_free(tl_struct->path);
2981         tl_struct->path = NULL;
2982
2983         return TRUE;
2984     }
2985
2986     ITypeLib_GetLibAttr(tl_struct->ptLib, &attr);
2987     if (IsEqualGUID(&(tl_struct->clsid),&(attr->guid)))
2988     {
2989         ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
2990         return FALSE;
2991     }
2992
2993     msi_free(tl_struct->path);
2994     tl_struct->path = NULL;
2995
2996     ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
2997     ITypeLib_Release(tl_struct->ptLib);
2998
2999     return TRUE;
3000 }
3001
3002 static UINT ITERATE_RegisterTypeLibraries(MSIRECORD *row, LPVOID param)
3003 {
3004     MSIPACKAGE* package = (MSIPACKAGE*)param;
3005     LPCWSTR component;
3006     MSICOMPONENT *comp;
3007     MSIFILE *file;
3008     typelib_struct tl_struct;
3009     ITypeLib *tlib;
3010     HMODULE module;
3011     HRESULT hr;
3012
3013     static const WCHAR szTYPELIB[] = {'T','Y','P','E','L','I','B',0};
3014
3015     component = MSI_RecordGetString(row,3);
3016     comp = get_loaded_component(package,component);
3017     if (!comp)
3018         return ERROR_SUCCESS;
3019
3020     if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
3021     {
3022         TRACE("Skipping typelib reg due to disabled component\n");
3023
3024         comp->Action = comp->Installed;
3025
3026         return ERROR_SUCCESS;
3027     }
3028
3029     comp->Action = INSTALLSTATE_LOCAL;
3030
3031     file = get_loaded_file( package, comp->KeyPath ); 
3032     if (!file)
3033         return ERROR_SUCCESS;
3034
3035     module = LoadLibraryExW( file->TargetPath, NULL, LOAD_LIBRARY_AS_DATAFILE );
3036     if (module)
3037     {
3038         LPCWSTR guid;
3039         guid = MSI_RecordGetString(row,1);
3040         CLSIDFromString((LPWSTR)guid, &tl_struct.clsid);
3041         tl_struct.source = strdupW( file->TargetPath );
3042         tl_struct.path = NULL;
3043
3044         EnumResourceNamesW(module, szTYPELIB, Typelib_EnumResNameProc,
3045                         (LONG_PTR)&tl_struct);
3046
3047         if (tl_struct.path)
3048         {
3049             LPWSTR help = NULL;
3050             LPCWSTR helpid;
3051             HRESULT res;
3052
3053             helpid = MSI_RecordGetString(row,6);
3054
3055             if (helpid)
3056                 help = resolve_folder(package,helpid,FALSE,FALSE,TRUE,NULL);
3057             res = RegisterTypeLib(tl_struct.ptLib,tl_struct.path,help);
3058             msi_free(help);
3059
3060             if (FAILED(res))
3061                 ERR("Failed to register type library %s\n",
3062                         debugstr_w(tl_struct.path));
3063             else
3064             {
3065                 ui_actiondata(package,szRegisterTypeLibraries,row);
3066
3067                 TRACE("Registered %s\n", debugstr_w(tl_struct.path));
3068             }
3069
3070             ITypeLib_Release(tl_struct.ptLib);
3071             msi_free(tl_struct.path);
3072         }
3073         else
3074             ERR("Failed to load type library %s\n",
3075                     debugstr_w(tl_struct.source));
3076
3077         FreeLibrary(module);
3078         msi_free(tl_struct.source);
3079     }
3080     else
3081     {
3082         hr = LoadTypeLibEx(file->TargetPath, REGKIND_REGISTER, &tlib);
3083         if (FAILED(hr))
3084         {
3085             ERR("Failed to load type library: %08x\n", hr);
3086             return ERROR_FUNCTION_FAILED;
3087         }
3088
3089         ITypeLib_Release(tlib);
3090     }
3091
3092     return ERROR_SUCCESS;
3093 }
3094
3095 static UINT ACTION_RegisterTypeLibraries(MSIPACKAGE *package)
3096 {
3097     /* 
3098      * OK this is a bit confusing.. I am given a _Component key and I believe
3099      * that the file that is being registered as a type library is the "key file
3100      * of that component" which I interpret to mean "The file in the KeyPath of
3101      * that component".
3102      */
3103     UINT rc;
3104     MSIQUERY * view;
3105     static const WCHAR Query[] =
3106         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3107          '`','T','y','p','e','L','i','b','`',0};
3108
3109     rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3110     if (rc != ERROR_SUCCESS)
3111         return ERROR_SUCCESS;
3112
3113     rc = MSI_IterateRecords(view, NULL, ITERATE_RegisterTypeLibraries, package);
3114     msiobj_release(&view->hdr);
3115     return rc;
3116 }
3117
3118 static UINT ITERATE_CreateShortcuts(MSIRECORD *row, LPVOID param)
3119 {
3120     MSIPACKAGE *package = (MSIPACKAGE*)param;
3121     LPWSTR target_file, target_folder, filename;
3122     LPCWSTR buffer, extension;
3123     MSICOMPONENT *comp;
3124     static const WCHAR szlnk[]={'.','l','n','k',0};
3125     IShellLinkW *sl = NULL;
3126     IPersistFile *pf = NULL;
3127     HRESULT res;
3128
3129     buffer = MSI_RecordGetString(row,4);
3130     comp = get_loaded_component(package,buffer);
3131     if (!comp)
3132         return ERROR_SUCCESS;
3133
3134     if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL ))
3135     {
3136         TRACE("Skipping shortcut creation due to disabled component\n");
3137
3138         comp->Action = comp->Installed;
3139
3140         return ERROR_SUCCESS;
3141     }
3142
3143     comp->Action = INSTALLSTATE_LOCAL;
3144
3145     ui_actiondata(package,szCreateShortcuts,row);
3146
3147     res = CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
3148                     &IID_IShellLinkW, (LPVOID *) &sl );
3149
3150     if (FAILED( res ))
3151     {
3152         ERR("CLSID_ShellLink not available\n");
3153         goto err;
3154     }
3155
3156     res = IShellLinkW_QueryInterface( sl, &IID_IPersistFile,(LPVOID*) &pf );
3157     if (FAILED( res ))
3158     {
3159         ERR("QueryInterface(IID_IPersistFile) failed\n");
3160         goto err;
3161     }
3162
3163     buffer = MSI_RecordGetString(row,2);
3164     target_folder = resolve_folder(package, buffer,FALSE,FALSE,TRUE,NULL);
3165
3166     /* may be needed because of a bug somewhere else */
3167     create_full_pathW(target_folder);
3168
3169     filename = msi_dup_record_field( row, 3 );
3170     reduce_to_longfilename(filename);
3171
3172     extension = strchrW(filename,'.');
3173     if (!extension || strcmpiW(extension,szlnk))
3174     {
3175         int len = strlenW(filename);
3176         filename = msi_realloc(filename, len * sizeof(WCHAR) + sizeof(szlnk));
3177         memcpy(filename + len, szlnk, sizeof(szlnk));
3178     }
3179     target_file = build_directory_name(2, target_folder, filename);
3180     msi_free(target_folder);
3181     msi_free(filename);
3182
3183     buffer = MSI_RecordGetString(row,5);
3184     if (strchrW(buffer,'['))
3185     {
3186         LPWSTR deformated;
3187         deformat_string(package,buffer,&deformated);
3188         IShellLinkW_SetPath(sl,deformated);
3189         msi_free(deformated);
3190     }
3191     else
3192     {
3193         FIXME("poorly handled shortcut format, advertised shortcut\n");
3194         IShellLinkW_SetPath(sl,comp->FullKeypath);
3195     }
3196
3197     if (!MSI_RecordIsNull(row,6))
3198     {
3199         LPWSTR deformated;
3200         buffer = MSI_RecordGetString(row,6);
3201         deformat_string(package,buffer,&deformated);
3202         IShellLinkW_SetArguments(sl,deformated);
3203         msi_free(deformated);
3204     }
3205
3206     if (!MSI_RecordIsNull(row,7))
3207     {
3208         buffer = MSI_RecordGetString(row,7);
3209         IShellLinkW_SetDescription(sl,buffer);
3210     }
3211
3212     if (!MSI_RecordIsNull(row,8))
3213         IShellLinkW_SetHotkey(sl,MSI_RecordGetInteger(row,8));
3214
3215     if (!MSI_RecordIsNull(row,9))
3216     {
3217         LPWSTR Path;
3218         INT index; 
3219
3220         buffer = MSI_RecordGetString(row,9);
3221
3222         Path = build_icon_path(package,buffer);
3223         index = MSI_RecordGetInteger(row,10);
3224
3225         /* no value means 0 */
3226         if (index == MSI_NULL_INTEGER)
3227             index = 0;
3228
3229         IShellLinkW_SetIconLocation(sl,Path,index);
3230         msi_free(Path);
3231     }
3232
3233     if (!MSI_RecordIsNull(row,11))
3234         IShellLinkW_SetShowCmd(sl,MSI_RecordGetInteger(row,11));
3235
3236     if (!MSI_RecordIsNull(row,12))
3237     {
3238         LPWSTR Path;
3239         buffer = MSI_RecordGetString(row,12);
3240         Path = resolve_folder(package, buffer, FALSE, FALSE, TRUE, NULL);
3241         if (Path)
3242             IShellLinkW_SetWorkingDirectory(sl,Path);
3243         msi_free(Path);
3244     }
3245
3246     TRACE("Writing shortcut to %s\n",debugstr_w(target_file));
3247     IPersistFile_Save(pf,target_file,FALSE);
3248
3249     msi_free(target_file);    
3250
3251 err:
3252     if (pf)
3253         IPersistFile_Release( pf );
3254     if (sl)
3255         IShellLinkW_Release( sl );
3256
3257     return ERROR_SUCCESS;
3258 }
3259
3260 static UINT ACTION_CreateShortcuts(MSIPACKAGE *package)
3261 {
3262     UINT rc;
3263     HRESULT res;
3264     MSIQUERY * view;
3265     static const WCHAR Query[] =
3266         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3267          '`','S','h','o','r','t','c','u','t','`',0};
3268
3269     rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3270     if (rc != ERROR_SUCCESS)
3271         return ERROR_SUCCESS;
3272
3273     res = CoInitialize( NULL );
3274     if (FAILED (res))
3275     {
3276         ERR("CoInitialize failed\n");
3277         return ERROR_FUNCTION_FAILED;
3278     }
3279
3280     rc = MSI_IterateRecords(view, NULL, ITERATE_CreateShortcuts, package);
3281     msiobj_release(&view->hdr);
3282
3283     CoUninitialize();
3284
3285     return rc;
3286 }
3287
3288 static UINT ITERATE_PublishIcon(MSIRECORD *row, LPVOID param)
3289 {
3290     MSIPACKAGE* package = (MSIPACKAGE*)param;
3291     HANDLE the_file;
3292     LPWSTR FilePath;
3293     LPCWSTR FileName;
3294     CHAR buffer[1024];
3295     DWORD sz;
3296     UINT rc;
3297     MSIRECORD *uirow;
3298
3299     FileName = MSI_RecordGetString(row,1);
3300     if (!FileName)
3301     {
3302         ERR("Unable to get FileName\n");
3303         return ERROR_SUCCESS;
3304     }
3305
3306     FilePath = build_icon_path(package,FileName);
3307
3308     TRACE("Creating icon file at %s\n",debugstr_w(FilePath));
3309
3310     the_file = CreateFileW(FilePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
3311                         FILE_ATTRIBUTE_NORMAL, NULL);
3312
3313     if (the_file == INVALID_HANDLE_VALUE)
3314     {
3315         ERR("Unable to create file %s\n",debugstr_w(FilePath));
3316         msi_free(FilePath);
3317         return ERROR_SUCCESS;
3318     }
3319
3320     do 
3321     {
3322         DWORD write;
3323         sz = 1024;
3324         rc = MSI_RecordReadStream(row,2,buffer,&sz);
3325         if (rc != ERROR_SUCCESS)
3326         {
3327             ERR("Failed to get stream\n");
3328             CloseHandle(the_file);  
3329             DeleteFileW(FilePath);
3330             break;
3331         }
3332         WriteFile(the_file,buffer,sz,&write,NULL);
3333     } while (sz == 1024);
3334
3335     msi_free(FilePath);
3336
3337     CloseHandle(the_file);
3338
3339     uirow = MSI_CreateRecord(1);
3340     MSI_RecordSetStringW(uirow,1,FileName);
3341     ui_actiondata(package,szPublishProduct,uirow);
3342     msiobj_release( &uirow->hdr );
3343
3344     return ERROR_SUCCESS;
3345 }
3346
3347 static UINT msi_publish_icons(MSIPACKAGE *package)
3348 {
3349     UINT r;
3350     MSIQUERY *view;
3351
3352     static const WCHAR query[]= {
3353         'S','E','L','E','C','T',' ','*',' ',
3354         'F','R','O','M',' ','`','I','c','o','n','`',0};
3355
3356     r = MSI_DatabaseOpenViewW(package->db, query, &view);
3357     if (r == ERROR_SUCCESS)
3358     {
3359         MSI_IterateRecords(view, NULL, ITERATE_PublishIcon, package);
3360         msiobj_release(&view->hdr);
3361     }
3362
3363     return ERROR_SUCCESS;
3364 }
3365
3366 static UINT msi_publish_sourcelist(MSIPACKAGE *package, HKEY hkey)
3367 {
3368     UINT r;
3369     HKEY source;
3370     LPWSTR buffer;
3371     MSIMEDIADISK *disk;
3372     MSISOURCELISTINFO *info;
3373
3374     static const WCHAR szEmpty[] = {0};
3375     static const WCHAR szSourceList[] = {'S','o','u','r','c','e','L','i','s','t',0};
3376
3377     r = RegCreateKeyW(hkey, szSourceList, &source);
3378     if (r != ERROR_SUCCESS)
3379         return r;
3380
3381     RegCloseKey(source);
3382
3383     buffer = strrchrW(package->PackagePath, '\\') + 1;
3384     r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3385                               package->Context, MSICODE_PRODUCT,
3386                               INSTALLPROPERTY_PACKAGENAMEW, buffer);
3387     if (r != ERROR_SUCCESS)
3388         return r;
3389
3390     r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3391                               package->Context, MSICODE_PRODUCT,
3392                               INSTALLPROPERTY_MEDIAPACKAGEPATHW, szEmpty);
3393     if (r != ERROR_SUCCESS)
3394         return r;
3395
3396     r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3397                               package->Context, MSICODE_PRODUCT,
3398                               INSTALLPROPERTY_DISKPROMPTW, szEmpty);
3399     if (r != ERROR_SUCCESS)
3400         return r;
3401
3402     LIST_FOR_EACH_ENTRY(info, &package->sourcelist_info, MSISOURCELISTINFO, entry)
3403     {
3404         if (!lstrcmpW(info->property, INSTALLPROPERTY_LASTUSEDSOURCEW))
3405             msi_set_last_used_source(package->ProductCode, NULL, info->context,
3406                                      info->options, info->value);
3407         else
3408             MsiSourceListSetInfoW(package->ProductCode, NULL,
3409                                   info->context, info->options,
3410                                   info->property, info->value);
3411     }
3412
3413     LIST_FOR_EACH_ENTRY(disk, &package->sourcelist_media, MSIMEDIADISK, entry)
3414     {
3415         MsiSourceListAddMediaDiskW(package->ProductCode, NULL,
3416                                    disk->context, disk->options,
3417                                    disk->disk_id, disk->volume_label, disk->disk_prompt);
3418     }
3419
3420     return ERROR_SUCCESS;
3421 }
3422
3423 static UINT msi_publish_product_properties(MSIPACKAGE *package, HKEY hkey)
3424 {
3425     MSIHANDLE hdb, suminfo;
3426     WCHAR guids[MAX_PATH];
3427     WCHAR packcode[SQUISH_GUID_SIZE];
3428     LPWSTR buffer;
3429     LPWSTR ptr;
3430     DWORD langid;
3431     DWORD size;
3432     UINT r;
3433
3434     static const WCHAR szProductLanguage[] =
3435         {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
3436     static const WCHAR szARPProductIcon[] =
3437         {'A','R','P','P','R','O','D','U','C','T','I','C','O','N',0};
3438     static const WCHAR szProductVersion[] =
3439         {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
3440     static const WCHAR szAssignment[] =
3441         {'A','s','s','i','g','n','m','e','n','t',0};
3442     static const WCHAR szAdvertiseFlags[] =
3443         {'A','d','v','e','r','t','i','s','e','F','l','a','g','s',0};
3444     static const WCHAR szClients[] =
3445         {'C','l','i','e','n','t','s',0};
3446     static const WCHAR szColon[] = {':',0};
3447
3448     buffer = msi_dup_property(package, INSTALLPROPERTY_PRODUCTNAMEW);
3449     msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTNAMEW, buffer);
3450     msi_free(buffer);
3451
3452     langid = msi_get_property_int(package, szProductLanguage, 0);
3453     msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
3454
3455     ptr = strrchrW(package->PackagePath, '\\' ) + 1;
3456     msi_reg_set_val_str(hkey, INSTALLPROPERTY_PACKAGENAMEW, ptr);
3457
3458     /* FIXME */
3459     msi_reg_set_val_dword(hkey, INSTALLPROPERTY_AUTHORIZED_LUA_APPW, 0);
3460
3461     buffer = msi_dup_property(package, szARPProductIcon);
3462     if (buffer)
3463     {
3464         LPWSTR path = build_icon_path(package,buffer);
3465         msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTICONW, path);
3466         msi_free(path);
3467         msi_free(buffer);
3468     }
3469
3470     buffer = msi_dup_property(package, szProductVersion);
3471     if (buffer)
3472     {
3473         DWORD verdword = msi_version_str_to_dword(buffer);
3474         msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
3475         msi_free(buffer);
3476     }
3477
3478     msi_reg_set_val_dword(hkey, szAssignment, 0);
3479     msi_reg_set_val_dword(hkey, szAdvertiseFlags, 0x184);
3480     msi_reg_set_val_dword(hkey, INSTALLPROPERTY_INSTANCETYPEW, 0);
3481     msi_reg_set_val_str(hkey, szClients, szColon);
3482
3483     hdb = alloc_msihandle(&package->db->hdr);
3484     if (!hdb)
3485         return ERROR_NOT_ENOUGH_MEMORY;
3486
3487     r = MsiGetSummaryInformationW(hdb, NULL, 0, &suminfo);
3488     MsiCloseHandle(hdb);
3489     if (r != ERROR_SUCCESS)
3490         goto done;
3491
3492     size = MAX_PATH;
3493     r = MsiSummaryInfoGetPropertyW(suminfo, PID_REVNUMBER, NULL, NULL,
3494                                    NULL, guids, &size);
3495     if (r != ERROR_SUCCESS)
3496         goto done;
3497
3498     ptr = strchrW(guids, ';');
3499     if (ptr) *ptr = 0;
3500     squash_guid(guids, packcode);
3501     msi_reg_set_val_str(hkey, INSTALLPROPERTY_PACKAGECODEW, packcode);
3502
3503 done:
3504     MsiCloseHandle(suminfo);
3505     return ERROR_SUCCESS;
3506 }
3507
3508 static UINT msi_publish_upgrade_code(MSIPACKAGE *package)
3509 {
3510     UINT r;
3511     HKEY hkey;
3512     LPWSTR upgrade;
3513     WCHAR squashed_pc[SQUISH_GUID_SIZE];
3514
3515     static const WCHAR szUpgradeCode[] =
3516         {'U','p','g','r','a','d','e','C','o','d','e',0};
3517
3518     upgrade = msi_dup_property(package, szUpgradeCode);
3519     if (!upgrade)
3520         return ERROR_SUCCESS;
3521
3522     if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3523     {
3524         r = MSIREG_OpenClassesUpgradeCodesKey(upgrade, &hkey, TRUE);
3525         if (r != ERROR_SUCCESS)
3526             goto done;
3527     }
3528     else
3529     {
3530         r = MSIREG_OpenUserUpgradeCodesKey(upgrade, &hkey, TRUE);
3531         if (r != ERROR_SUCCESS)
3532             goto done;
3533     }
3534
3535     squash_guid(package->ProductCode, squashed_pc);
3536     msi_reg_set_val_str(hkey, squashed_pc, NULL);
3537
3538     RegCloseKey(hkey);
3539
3540 done:
3541     msi_free(upgrade);
3542     return r;
3543 }
3544
3545 static BOOL msi_check_publish(MSIPACKAGE *package)
3546 {
3547     MSIFEATURE *feature;
3548
3549     LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3550     {
3551         if (feature->ActionRequest == INSTALLSTATE_LOCAL)
3552             return TRUE;
3553     }
3554
3555     return FALSE;
3556 }
3557
3558 static BOOL msi_check_unpublish(MSIPACKAGE *package)
3559 {
3560     MSIFEATURE *feature;
3561
3562     LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3563     {
3564         if (feature->ActionRequest != INSTALLSTATE_ABSENT)
3565             return FALSE;
3566     }
3567
3568     return TRUE;
3569 }
3570
3571 /*
3572  * 99% of the work done here is only done for 
3573  * advertised installs. However this is where the
3574  * Icon table is processed and written out
3575  * so that is what I am going to do here.
3576  */
3577 static UINT ACTION_PublishProduct(MSIPACKAGE *package)
3578 {
3579     UINT rc;
3580     HKEY hukey=0;
3581     HKEY hudkey=0;
3582
3583     /* FIXME: also need to publish if the product is in advertise mode */
3584     if (!msi_check_publish(package))
3585         return ERROR_SUCCESS;
3586
3587     rc = MSIREG_OpenProductKey(package->ProductCode, package->Context,
3588                                &hukey, TRUE);
3589     if (rc != ERROR_SUCCESS)
3590         goto end;
3591
3592     if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3593     {
3594         rc = MSIREG_OpenUserDataProductKey(package->ProductCode, szLocalSid,
3595                                            &hudkey, TRUE);
3596         if (rc != ERROR_SUCCESS)
3597             goto end;
3598     }
3599     else
3600     {
3601         rc = MSIREG_OpenUserDataProductKey(package->ProductCode, NULL,
3602                                            &hudkey, TRUE);
3603         if (rc != ERROR_SUCCESS)
3604             goto end;
3605     }
3606
3607     rc = msi_publish_upgrade_code(package);
3608     if (rc != ERROR_SUCCESS)
3609         goto end;
3610
3611     rc = msi_publish_product_properties(package, hukey);
3612     if (rc != ERROR_SUCCESS)
3613         goto end;
3614
3615     rc = msi_publish_sourcelist(package, hukey);
3616     if (rc != ERROR_SUCCESS)
3617         goto end;
3618
3619     rc = msi_publish_icons(package);
3620
3621 end:
3622     RegCloseKey(hukey);
3623     RegCloseKey(hudkey);
3624
3625     return rc;
3626 }
3627
3628 static UINT ITERATE_WriteIniValues(MSIRECORD *row, LPVOID param)
3629 {
3630     MSIPACKAGE *package = (MSIPACKAGE*)param;
3631     LPCWSTR component, section, key, value, identifier, dirproperty;
3632     LPWSTR deformated_section, deformated_key, deformated_value;
3633     LPWSTR folder, filename, fullname = NULL;
3634     LPCWSTR filenameptr;
3635     MSIRECORD * uirow;
3636     INT action;
3637     MSICOMPONENT *comp;
3638     static const WCHAR szWindowsFolder[] =
3639           {'W','i','n','d','o','w','s','F','o','l','d','e','r',0};
3640
3641     component = MSI_RecordGetString(row, 8);
3642     comp = get_loaded_component(package,component);
3643
3644     if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
3645     {
3646         TRACE("Skipping ini file due to disabled component %s\n",
3647                         debugstr_w(component));
3648
3649         comp->Action = comp->Installed;
3650
3651         return ERROR_SUCCESS;
3652     }
3653
3654     comp->Action = INSTALLSTATE_LOCAL;
3655
3656     identifier = MSI_RecordGetString(row,1); 
3657     dirproperty = MSI_RecordGetString(row,3);
3658     section = MSI_RecordGetString(row,4);
3659     key = MSI_RecordGetString(row,5);
3660     value = MSI_RecordGetString(row,6);
3661     action = MSI_RecordGetInteger(row,7);
3662
3663     deformat_string(package,section,&deformated_section);
3664     deformat_string(package,key,&deformated_key);
3665     deformat_string(package,value,&deformated_value);
3666
3667     filename = msi_dup_record_field(row, 2);
3668     if (filename && (filenameptr = strchrW(filename, '|')))
3669         filenameptr++;
3670     else
3671         filenameptr = filename;
3672
3673     if (dirproperty)
3674     {
3675         folder = resolve_folder(package, dirproperty, FALSE, FALSE, TRUE, NULL);
3676         if (!folder)
3677             folder = msi_dup_property( package, dirproperty );
3678     }
3679     else
3680         folder = msi_dup_property( package, szWindowsFolder );
3681
3682     if (!folder)
3683     {
3684         ERR("Unable to resolve folder! (%s)\n",debugstr_w(dirproperty));
3685         goto cleanup;
3686     }
3687
3688     fullname = build_directory_name(2, folder, filenameptr);
3689
3690     if (action == 0)
3691     {
3692         TRACE("Adding value %s to section %s in %s\n",
3693                 debugstr_w(deformated_key), debugstr_w(deformated_section),
3694                 debugstr_w(fullname));
3695         WritePrivateProfileStringW(deformated_section, deformated_key,
3696                                    deformated_value, fullname);
3697     }
3698     else if (action == 1)
3699     {
3700         WCHAR returned[10];
3701         GetPrivateProfileStringW(deformated_section, deformated_key, NULL,
3702                                  returned, 10, fullname);
3703         if (returned[0] == 0)
3704         {
3705             TRACE("Adding value %s to section %s in %s\n",
3706                     debugstr_w(deformated_key), debugstr_w(deformated_section),
3707                     debugstr_w(fullname));
3708
3709             WritePrivateProfileStringW(deformated_section, deformated_key,
3710                                        deformated_value, fullname);
3711         }
3712     }
3713     else if (action == 3)
3714         FIXME("Append to existing section not yet implemented\n");
3715
3716     uirow = MSI_CreateRecord(4);
3717     MSI_RecordSetStringW(uirow,1,identifier);
3718     MSI_RecordSetStringW(uirow,2,deformated_section);
3719     MSI_RecordSetStringW(uirow,3,deformated_key);
3720     MSI_RecordSetStringW(uirow,4,deformated_value);
3721     ui_actiondata(package,szWriteIniValues,uirow);
3722     msiobj_release( &uirow->hdr );
3723
3724 cleanup:
3725     msi_free(filename);
3726     msi_free(fullname);
3727     msi_free(folder);
3728     msi_free(deformated_key);
3729     msi_free(deformated_value);
3730     msi_free(deformated_section);
3731     return ERROR_SUCCESS;
3732 }
3733
3734 static UINT ACTION_WriteIniValues(MSIPACKAGE *package)
3735 {
3736     UINT rc;
3737     MSIQUERY * view;
3738     static const WCHAR ExecSeqQuery[] = 
3739         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3740          '`','I','n','i','F','i','l','e','`',0};
3741
3742     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
3743     if (rc != ERROR_SUCCESS)
3744     {
3745         TRACE("no IniFile table\n");
3746         return ERROR_SUCCESS;
3747     }
3748
3749     rc = MSI_IterateRecords(view, NULL, ITERATE_WriteIniValues, package);
3750     msiobj_release(&view->hdr);
3751     return rc;
3752 }
3753
3754 static UINT ITERATE_SelfRegModules(MSIRECORD *row, LPVOID param)
3755 {
3756     MSIPACKAGE *package = (MSIPACKAGE*)param;
3757     LPCWSTR filename;
3758     LPWSTR FullName;
3759     MSIFILE *file;
3760     DWORD len;
3761     static const WCHAR ExeStr[] =
3762         {'r','e','g','s','v','r','3','2','.','e','x','e',' ','\"',0};
3763     static const WCHAR close[] =  {'\"',0};
3764     STARTUPINFOW si;
3765     PROCESS_INFORMATION info;
3766     BOOL brc;
3767     MSIRECORD *uirow;
3768     LPWSTR uipath, p;
3769
3770     memset(&si,0,sizeof(STARTUPINFOW));
3771
3772     filename = MSI_RecordGetString(row,1);
3773     file = get_loaded_file( package, filename );
3774
3775     if (!file)
3776     {
3777         ERR("Unable to find file id %s\n",debugstr_w(filename));
3778         return ERROR_SUCCESS;
3779     }
3780
3781     len = strlenW(ExeStr) + strlenW( file->TargetPath ) + 2;
3782
3783     FullName = msi_alloc(len*sizeof(WCHAR));
3784     strcpyW(FullName,ExeStr);
3785     strcatW( FullName, file->TargetPath );
3786     strcatW(FullName,close);
3787
3788     TRACE("Registering %s\n",debugstr_w(FullName));
3789     brc = CreateProcessW(NULL, FullName, NULL, NULL, FALSE, 0, NULL, c_colon,
3790                     &si, &info);
3791
3792     if (brc)
3793     {
3794         CloseHandle(info.hThread);
3795         msi_dialog_check_messages(info.hProcess);
3796         CloseHandle(info.hProcess);
3797     }
3798
3799     msi_free(FullName);
3800
3801     /* the UI chunk */
3802     uirow = MSI_CreateRecord( 2 );
3803     uipath = strdupW( file->TargetPath );
3804     p = strrchrW(uipath,'\\');
3805     if (p)
3806         p[0]=0;
3807     MSI_RecordSetStringW( uirow, 1, &p[1] );
3808     MSI_RecordSetStringW( uirow, 2, uipath);
3809     ui_actiondata( package, szSelfRegModules, uirow);
3810     msiobj_release( &uirow->hdr );
3811     msi_free( uipath );
3812     /* FIXME: call ui_progress? */
3813
3814     return ERROR_SUCCESS;
3815 }
3816
3817 static UINT ACTION_SelfRegModules(MSIPACKAGE *package)
3818 {
3819     UINT rc;
3820     MSIQUERY * view;
3821     static const WCHAR ExecSeqQuery[] = 
3822         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3823          '`','S','e','l','f','R','e','g','`',0};
3824
3825     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
3826     if (rc != ERROR_SUCCESS)
3827     {
3828         TRACE("no SelfReg table\n");
3829         return ERROR_SUCCESS;
3830     }
3831
3832     MSI_IterateRecords(view, NULL, ITERATE_SelfRegModules, package);
3833     msiobj_release(&view->hdr);
3834
3835     return ERROR_SUCCESS;
3836 }
3837
3838 static UINT ACTION_PublishFeatures(MSIPACKAGE *package)
3839 {
3840     MSIFEATURE *feature;
3841     UINT rc;
3842     HKEY hkey;
3843     HKEY userdata = NULL;
3844
3845     if (!msi_check_publish(package))
3846         return ERROR_SUCCESS;
3847
3848     rc = MSIREG_OpenFeaturesKey(package->ProductCode, package->Context,
3849                                 &hkey, TRUE);
3850     if (rc != ERROR_SUCCESS)
3851         goto end;
3852
3853     rc = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, package->Context,
3854                                         &userdata, TRUE);
3855     if (rc != ERROR_SUCCESS)
3856         goto end;
3857
3858     /* here the guids are base 85 encoded */
3859     LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3860     {
3861         ComponentList *cl;
3862         LPWSTR data = NULL;
3863         GUID clsid;
3864         INT size;
3865         BOOL absent = FALSE;
3866         MSIRECORD *uirow;
3867
3868         if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_LOCAL ) &&
3869             !ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_SOURCE ) &&
3870             !ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_ADVERTISED ))
3871             absent = TRUE;
3872
3873         size = 1;
3874         LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3875         {
3876             size += 21;
3877         }
3878         if (feature->Feature_Parent)
3879             size += strlenW( feature->Feature_Parent )+2;
3880
3881         data = msi_alloc(size * sizeof(WCHAR));
3882
3883         data[0] = 0;
3884         LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3885         {
3886             MSICOMPONENT* component = cl->component;
3887             WCHAR buf[21];
3888
3889             buf[0] = 0;
3890             if (component->ComponentId)
3891             {
3892                 TRACE("From %s\n",debugstr_w(component->ComponentId));
3893                 CLSIDFromString(component->ComponentId, &clsid);
3894                 encode_base85_guid(&clsid,buf);
3895                 TRACE("to %s\n",debugstr_w(buf));
3896                 strcatW(data,buf);
3897             }
3898         }
3899
3900         if (feature->Feature_Parent)
3901         {
3902             static const WCHAR sep[] = {'\2',0};
3903             strcatW(data,sep);
3904             strcatW(data,feature->Feature_Parent);
3905         }
3906
3907         msi_reg_set_val_str( userdata, feature->Feature, data );
3908         msi_free(data);
3909
3910         size = 0;
3911         if (feature->Feature_Parent)
3912             size = strlenW(feature->Feature_Parent)*sizeof(WCHAR);
3913         if (!absent)
3914         {
3915             static const WCHAR emptyW[] = {0};
3916             size += sizeof(WCHAR);
3917             RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
3918                            (LPBYTE)(feature->Feature_Parent ? feature->Feature_Parent : emptyW),size);
3919         }
3920         else
3921         {
3922             size += 2*sizeof(WCHAR);
3923             data = msi_alloc(size);
3924             data[0] = 0x6;
3925             data[1] = 0;
3926             if (feature->Feature_Parent)
3927                 strcpyW( &data[1], feature->Feature_Parent );
3928             RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
3929                        (LPBYTE)data,size);
3930             msi_free(data);
3931         }
3932
3933         /* the UI chunk */
3934         uirow = MSI_CreateRecord( 1 );
3935         MSI_RecordSetStringW( uirow, 1, feature->Feature );
3936         ui_actiondata( package, szPublishFeatures, uirow);
3937         msiobj_release( &uirow->hdr );
3938         /* FIXME: call ui_progress? */
3939     }
3940
3941 end:
3942     RegCloseKey(hkey);
3943     RegCloseKey(userdata);
3944     return rc;
3945 }
3946
3947 static UINT msi_unpublish_feature(MSIPACKAGE *package, MSIFEATURE *feature)
3948 {
3949     UINT r;
3950     HKEY hkey;
3951
3952     TRACE("unpublishing feature %s\n", debugstr_w(feature->Feature));
3953
3954     r = MSIREG_OpenFeaturesKey(package->ProductCode, package->Context,
3955                                &hkey, FALSE);
3956     if (r == ERROR_SUCCESS)
3957     {
3958         RegDeleteValueW(hkey, feature->Feature);
3959         RegCloseKey(hkey);
3960     }
3961
3962     r = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, package->Context,
3963                                        &hkey, FALSE);
3964     if (r == ERROR_SUCCESS)
3965     {
3966         RegDeleteValueW(hkey, feature->Feature);
3967         RegCloseKey(hkey);
3968     }
3969
3970     return ERROR_SUCCESS;
3971 }
3972
3973 static UINT ACTION_UnpublishFeatures(MSIPACKAGE *package)
3974 {
3975     MSIFEATURE *feature;
3976
3977     if (!msi_check_unpublish(package))
3978         return ERROR_SUCCESS;
3979
3980     LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3981     {
3982         msi_unpublish_feature(package, feature);
3983     }
3984
3985     return ERROR_SUCCESS;
3986 }
3987
3988 static UINT msi_get_local_package_name( LPWSTR path )
3989 {
3990     static const WCHAR szInstaller[] = {
3991         '\\','I','n','s','t','a','l','l','e','r','\\',0};
3992     static const WCHAR fmt[] = { '%','x','.','m','s','i',0};
3993     DWORD time, len, i;
3994     HANDLE handle;
3995
3996     time = GetTickCount();
3997     GetWindowsDirectoryW( path, MAX_PATH );
3998     lstrcatW( path, szInstaller );
3999     CreateDirectoryW( path, NULL );
4000
4001     len = lstrlenW(path);
4002     for (i=0; i<0x10000; i++)
4003     {
4004         snprintfW( &path[len], MAX_PATH - len, fmt, (time+i)&0xffff );
4005         handle = CreateFileW( path, GENERIC_WRITE, 0, NULL,
4006                               CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0 );
4007         if (handle != INVALID_HANDLE_VALUE)
4008         {
4009             CloseHandle(handle);
4010             break;
4011         }
4012         if (GetLastError() != ERROR_FILE_EXISTS &&
4013             GetLastError() != ERROR_SHARING_VIOLATION)
4014             return ERROR_FUNCTION_FAILED;
4015     }
4016
4017     return ERROR_SUCCESS;
4018 }
4019
4020 static UINT msi_make_package_local( MSIPACKAGE *package, HKEY hkey )
4021 {
4022     WCHAR packagefile[MAX_PATH];
4023     UINT r;
4024
4025     r = msi_get_local_package_name( packagefile );
4026     if (r != ERROR_SUCCESS)
4027         return r;
4028
4029     TRACE("Copying to local package %s\n",debugstr_w(packagefile));
4030
4031     r = CopyFileW( package->db->path, packagefile, FALSE);
4032
4033     if (!r)
4034     {
4035         ERR("Unable to copy package (%s -> %s) (error %d)\n",
4036             debugstr_w(package->db->path), debugstr_w(packagefile), GetLastError());
4037         return ERROR_FUNCTION_FAILED;
4038     }
4039
4040     msi_reg_set_val_str( hkey, INSTALLPROPERTY_LOCALPACKAGEW, packagefile );
4041
4042     return ERROR_SUCCESS;
4043 }
4044
4045 static UINT msi_publish_install_properties(MSIPACKAGE *package, HKEY hkey)
4046 {
4047     LPWSTR prop, val, key;
4048     SYSTEMTIME systime;
4049     DWORD size, langid;
4050     WCHAR date[9];
4051     LPWSTR buffer;
4052
4053     static const WCHAR date_fmt[] = {'%','i','%','0','2','i','%','0','2','i',0};
4054     static const WCHAR szWindowsInstaller[] =
4055         {'W','i','n','d','o','w','s','I','n','s','t','a','l','l','e','r',0};
4056     static const WCHAR modpath_fmt[] =
4057         {'M','s','i','E','x','e','c','.','e','x','e',' ',
4058          '/','I','[','P','r','o','d','u','c','t','C','o','d','e',']',0};
4059     static const WCHAR szModifyPath[] =
4060         {'M','o','d','i','f','y','P','a','t','h',0};
4061     static const WCHAR szUninstallString[] =
4062         {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0};
4063     static const WCHAR szEstimatedSize[] =
4064         {'E','s','t','i','m','a','t','e','d','S','i','z','e',0};
4065     static const WCHAR szProductLanguage[] =
4066         {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
4067     static const WCHAR szProductVersion[] =
4068         {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
4069     static const WCHAR szProductName[] =
4070         {'P','r','o','d','u','c','t','N','a','m','e',0};
4071     static const WCHAR szDisplayName[] =
4072         {'D','i','s','p','l','a','y','N','a','m','e',0};
4073     static const WCHAR szDisplayVersion[] =
4074         {'D','i','s','p','l','a','y','V','e','r','s','i','o','n',0};
4075     static const WCHAR szManufacturer[] =
4076         {'M','a','n','u','f','a','c','t','u','r','e','r',0};
4077
4078     static const LPCSTR propval[] = {
4079         "ARPAUTHORIZEDCDFPREFIX", "AuthorizedCDFPrefix",
4080         "ARPCONTACT",             "Contact",
4081         "ARPCOMMENTS",            "Comments",
4082         "ProductName",            "DisplayName",
4083         "ProductVersion",         "DisplayVersion",
4084         "ARPHELPLINK",            "HelpLink",
4085         "ARPHELPTELEPHONE",       "HelpTelephone",
4086         "ARPINSTALLLOCATION",     "InstallLocation",
4087         "SourceDir",              "InstallSource",
4088         "Manufacturer",           "Publisher",
4089         "ARPREADME",              "Readme",
4090         "ARPSIZE",                "Size",
4091         "ARPURLINFOABOUT",        "URLInfoAbout",
4092         "ARPURLUPDATEINFO",       "URLUpdateInfo",
4093         NULL,
4094     };
4095     const LPCSTR *p = propval;
4096
4097     while (*p)
4098     {
4099         prop = strdupAtoW(*p++);
4100         key = strdupAtoW(*p++);
4101         val = msi_dup_property(package, prop);
4102         msi_reg_set_val_str(hkey, key, val);
4103         msi_free(val);
4104         msi_free(key);
4105         msi_free(prop);
4106     }
4107
4108     msi_reg_set_val_dword(hkey, szWindowsInstaller, 1);
4109
4110     size = deformat_string(package, modpath_fmt, &buffer);
4111     RegSetValueExW(hkey, szModifyPath, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
4112     RegSetValueExW(hkey, szUninstallString, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
4113     msi_free(buffer);
4114
4115     /* FIXME: Write real Estimated Size when we have it */
4116     msi_reg_set_val_dword(hkey, szEstimatedSize, 0);
4117
4118     buffer = msi_dup_property(package, szProductName);
4119     msi_reg_set_val_str(hkey, szDisplayName, buffer);
4120     msi_free(buffer);
4121
4122     buffer = msi_dup_property(package, cszSourceDir);
4123     msi_reg_set_val_str(hkey, INSTALLPROPERTY_INSTALLSOURCEW, buffer);
4124     msi_free(buffer);
4125
4126     buffer = msi_dup_property(package, szManufacturer);
4127     msi_reg_set_val_str(hkey, INSTALLPROPERTY_PUBLISHERW, buffer);
4128     msi_free(buffer);
4129
4130     GetLocalTime(&systime);
4131     sprintfW(date, date_fmt, systime.wYear, systime.wMonth, systime.wDay);
4132     msi_reg_set_val_str(hkey, INSTALLPROPERTY_INSTALLDATEW, date);
4133
4134     langid = msi_get_property_int(package, szProductLanguage, 0);
4135     msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
4136
4137     buffer = msi_dup_property(package, szProductVersion);
4138     msi_reg_set_val_str(hkey, szDisplayVersion, buffer);
4139     if (buffer)
4140     {
4141         DWORD verdword = msi_version_str_to_dword(buffer);
4142
4143         msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
4144         msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMAJORW, verdword >> 24);
4145         msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMINORW, (verdword >> 16) & 0xFF);
4146         msi_free(buffer);
4147     }
4148
4149     return ERROR_SUCCESS;
4150 }
4151
4152 static UINT ACTION_RegisterProduct(MSIPACKAGE *package)
4153 {
4154     WCHAR squashed_pc[SQUISH_GUID_SIZE];
4155     LPWSTR upgrade_code;
4156     HKEY hkey, props;
4157     HKEY upgrade;
4158     UINT rc;
4159
4160     static const WCHAR szUpgradeCode[] = {
4161         'U','p','g','r','a','d','e','C','o','d','e',0};
4162
4163     /* FIXME: also need to publish if the product is in advertise mode */
4164     if (!msi_check_publish(package))
4165         return ERROR_SUCCESS;
4166
4167     rc = MSIREG_OpenUninstallKey(package->ProductCode, &hkey, TRUE);
4168     if (rc != ERROR_SUCCESS)
4169         return rc;
4170
4171     if (package->Context == MSIINSTALLCONTEXT_MACHINE)
4172     {
4173         rc = MSIREG_OpenInstallProps(package->ProductCode, szLocalSid,
4174                                      &props, TRUE);
4175         if (rc != ERROR_SUCCESS)
4176             goto done;
4177     }
4178     else
4179     {
4180         rc = MSIREG_OpenCurrentUserInstallProps(package->ProductCode, &props, TRUE);
4181         if (rc != ERROR_SUCCESS)
4182             goto done;
4183     }
4184
4185     msi_make_package_local(package, props);
4186
4187     rc = msi_publish_install_properties(package, hkey);
4188     if (rc != ERROR_SUCCESS)
4189         goto done;
4190
4191     rc = msi_publish_install_properties(package, props);
4192     if (rc != ERROR_SUCCESS)
4193         goto done;
4194
4195     upgrade_code = msi_dup_property(package, szUpgradeCode);
4196     if (upgrade_code)
4197     {
4198         MSIREG_OpenUpgradeCodesKey(upgrade_code, &upgrade, TRUE);
4199         squash_guid(package->ProductCode, squashed_pc);
4200         msi_reg_set_val_str(upgrade, squashed_pc, NULL);
4201         RegCloseKey(upgrade);
4202         msi_free(upgrade_code);
4203     }
4204
4205 done:
4206     RegCloseKey(hkey);
4207
4208     return ERROR_SUCCESS;
4209 }
4210
4211 static UINT ACTION_InstallExecute(MSIPACKAGE *package)
4212 {
4213     return execute_script(package,INSTALL_SCRIPT);
4214 }
4215
4216 static UINT msi_unpublish_product(MSIPACKAGE *package)
4217 {
4218     LPWSTR upgrade;
4219     LPWSTR remove = NULL;
4220     LPWSTR *features = NULL;
4221     BOOL full_uninstall = TRUE;
4222     MSIFEATURE *feature;
4223
4224     static const WCHAR szRemove[] = {'R','E','M','O','V','E',0};
4225     static const WCHAR szAll[] = {'A','L','L',0};
4226     static const WCHAR szUpgradeCode[] =
4227         {'U','p','g','r','a','d','e','C','o','d','e',0};
4228
4229     remove = msi_dup_property(package, szRemove);
4230     if (!remove)
4231         return ERROR_SUCCESS;
4232
4233     features = msi_split_string(remove, ',');
4234     if (!features)
4235     {
4236         msi_free(remove);
4237         ERR("REMOVE feature list is empty!\n");
4238         return ERROR_FUNCTION_FAILED;
4239     }
4240
4241     if (!lstrcmpW(features[0], szAll))
4242         full_uninstall = TRUE;
4243     else
4244     {
4245         LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4246         {
4247             if (feature->Action != INSTALLSTATE_ABSENT)
4248                 full_uninstall = FALSE;
4249         }
4250     }
4251
4252     if (!full_uninstall)
4253         goto done;
4254
4255     MSIREG_DeleteProductKey(package->ProductCode);
4256     MSIREG_DeleteUserDataProductKey(package->ProductCode);
4257     MSIREG_DeleteUninstallKey(package->ProductCode);
4258
4259     if (package->Context == MSIINSTALLCONTEXT_MACHINE)
4260     {
4261         MSIREG_DeleteLocalClassesProductKey(package->ProductCode);
4262         MSIREG_DeleteLocalClassesFeaturesKey(package->ProductCode);
4263     }
4264     else
4265     {
4266         MSIREG_DeleteUserProductKey(package->ProductCode);
4267         MSIREG_DeleteUserFeaturesKey(package->ProductCode);
4268     }
4269
4270     upgrade = msi_dup_property(package, szUpgradeCode);
4271     if (upgrade)
4272     {
4273         MSIREG_DeleteUserUpgradeCodesKey(upgrade);
4274         msi_free(upgrade);
4275     }
4276
4277 done:
4278     msi_free(remove);
4279     msi_free(features);
4280     return ERROR_SUCCESS;
4281 }
4282
4283 static UINT ACTION_InstallFinalize(MSIPACKAGE *package)
4284 {
4285     UINT rc;
4286
4287     rc = msi_unpublish_product(package);
4288     if (rc != ERROR_SUCCESS)
4289         return rc;
4290
4291     /* turn off scheduling */
4292     package->script->CurrentlyScripting= FALSE;
4293
4294     /* first do the same as an InstallExecute */
4295     rc = ACTION_InstallExecute(package);
4296     if (rc != ERROR_SUCCESS)
4297         return rc;
4298
4299     /* then handle Commit Actions */
4300     rc = execute_script(package,COMMIT_SCRIPT);
4301
4302     return rc;
4303 }
4304
4305 UINT ACTION_ForceReboot(MSIPACKAGE *package)
4306 {
4307     static const WCHAR RunOnce[] = {
4308     'S','o','f','t','w','a','r','e','\\',
4309     'M','i','c','r','o','s','o','f','t','\\',
4310     'W','i','n','d','o','w','s','\\',
4311     'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
4312     'R','u','n','O','n','c','e',0};
4313     static const WCHAR InstallRunOnce[] = {
4314     'S','o','f','t','w','a','r','e','\\',
4315     'M','i','c','r','o','s','o','f','t','\\',
4316     'W','i','n','d','o','w','s','\\',
4317     'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
4318     'I','n','s','t','a','l','l','e','r','\\',
4319     'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
4320
4321     static const WCHAR msiexec_fmt[] = {
4322     '%','s',
4323     '\\','M','s','i','E','x','e','c','.','e','x','e',' ','/','@',' ',
4324     '\"','%','s','\"',0};
4325     static const WCHAR install_fmt[] = {
4326     '/','I',' ','\"','%','s','\"',' ',
4327     'A','F','T','E','R','R','E','B','O','O','T','=','1',' ',
4328     'R','U','N','O','N','C','E','E','N','T','R','Y','=','\"','%','s','\"',0};
4329     WCHAR buffer[256], sysdir[MAX_PATH];
4330     HKEY hkey;
4331     WCHAR squished_pc[100];
4332
4333     squash_guid(package->ProductCode,squished_pc);
4334
4335     GetSystemDirectoryW(sysdir, sizeof(sysdir)/sizeof(sysdir[0]));
4336     RegCreateKeyW(HKEY_LOCAL_MACHINE,RunOnce,&hkey);
4337     snprintfW(buffer,sizeof(buffer)/sizeof(buffer[0]),msiexec_fmt,sysdir,
4338      squished_pc);
4339
4340     msi_reg_set_val_str( hkey, squished_pc, buffer );
4341     RegCloseKey(hkey);
4342
4343     TRACE("Reboot command %s\n",debugstr_w(buffer));
4344
4345     RegCreateKeyW(HKEY_LOCAL_MACHINE,InstallRunOnce,&hkey);
4346     sprintfW(buffer,install_fmt,package->ProductCode,squished_pc);
4347
4348     msi_reg_set_val_str( hkey, squished_pc, buffer );
4349     RegCloseKey(hkey);
4350
4351     return ERROR_INSTALL_SUSPEND;
4352 }
4353
4354 static UINT ACTION_ResolveSource(MSIPACKAGE* package)
4355 {
4356     DWORD attrib;
4357     UINT rc;
4358
4359     /*
4360      * We are currently doing what should be done here in the top level Install
4361      * however for Administrative and uninstalls this step will be needed
4362      */
4363     if (!package->PackagePath)
4364         return ERROR_SUCCESS;
4365
4366     msi_set_sourcedir_props(package, TRUE);
4367
4368     attrib = GetFileAttributesW(package->db->path);
4369     if (attrib == INVALID_FILE_ATTRIBUTES)
4370     {
4371         LPWSTR prompt;
4372         LPWSTR msg;
4373         DWORD size = 0;
4374
4375         rc = MsiSourceListGetInfoW(package->ProductCode, NULL, 
4376                 package->Context, MSICODE_PRODUCT,
4377                 INSTALLPROPERTY_DISKPROMPTW,NULL,&size);
4378         if (rc == ERROR_MORE_DATA)
4379         {
4380             prompt = msi_alloc(size * sizeof(WCHAR));
4381             MsiSourceListGetInfoW(package->ProductCode, NULL, 
4382                     package->Context, MSICODE_PRODUCT,
4383                     INSTALLPROPERTY_DISKPROMPTW,prompt,&size);
4384         }
4385         else
4386             prompt = strdupW(package->db->path);
4387
4388         msg = generate_error_string(package,1302,1,prompt);
4389         while(attrib == INVALID_FILE_ATTRIBUTES)
4390         {
4391             rc = MessageBoxW(NULL,msg,NULL,MB_OKCANCEL);
4392             if (rc == IDCANCEL)
4393             {
4394                 rc = ERROR_INSTALL_USEREXIT;
4395                 break;
4396             }
4397             attrib = GetFileAttributesW(package->db->path);
4398         }
4399         msi_free(prompt);
4400         rc = ERROR_SUCCESS;
4401     }
4402     else
4403         return ERROR_SUCCESS;
4404
4405     return rc;
4406 }
4407
4408 static UINT ACTION_RegisterUser(MSIPACKAGE *package)
4409 {
4410     HKEY hkey=0;
4411     LPWSTR buffer;
4412     LPWSTR productid;
4413     UINT rc,i;
4414
4415     static const WCHAR szPropKeys[][80] = 
4416     {
4417         {'P','r','o','d','u','c','t','I','D',0},
4418         {'U','S','E','R','N','A','M','E',0},
4419         {'C','O','M','P','A','N','Y','N','A','M','E',0},
4420         {0},
4421     };
4422
4423     static const WCHAR szRegKeys[][80] = 
4424     {
4425         {'P','r','o','d','u','c','t','I','D',0},
4426         {'R','e','g','O','w','n','e','r',0},
4427         {'R','e','g','C','o','m','p','a','n','y',0},
4428         {0},
4429     };
4430
4431     if (msi_check_unpublish(package))
4432     {
4433         MSIREG_DeleteUserDataProductKey(package->ProductCode);
4434         return ERROR_SUCCESS;
4435     }
4436
4437     productid = msi_dup_property( package, INSTALLPROPERTY_PRODUCTIDW );
4438     if (!productid)
4439         return ERROR_SUCCESS;
4440
4441     if (package->Context == MSIINSTALLCONTEXT_MACHINE)
4442         rc = MSIREG_OpenInstallProps(package->ProductCode, szLocalSid, &hkey, TRUE);
4443     else
4444         rc = MSIREG_OpenCurrentUserInstallProps(package->ProductCode, &hkey, TRUE);
4445
4446     if (rc != ERROR_SUCCESS)
4447         goto end;
4448
4449     for( i = 0; szPropKeys[i][0]; i++ )
4450     {
4451         buffer = msi_dup_property( package, szPropKeys[i] );
4452         msi_reg_set_val_str( hkey, szRegKeys[i], buffer );
4453         msi_free( buffer );
4454     }
4455
4456 end:
4457     msi_free(productid);
4458     RegCloseKey(hkey);
4459
4460     /* FIXME: call ui_actiondata */
4461
4462     return rc;
4463 }
4464
4465
4466 static UINT ACTION_ExecuteAction(MSIPACKAGE *package)
4467 {
4468     UINT rc;
4469
4470     package->script->InWhatSequence |= SEQUENCE_EXEC;
4471     rc = ACTION_ProcessExecSequence(package,FALSE);
4472     return rc;
4473 }
4474
4475
4476 static UINT ITERATE_PublishComponent(MSIRECORD *rec, LPVOID param)
4477 {
4478     MSIPACKAGE *package = (MSIPACKAGE*)param;
4479     LPCWSTR compgroupid=NULL;
4480     LPCWSTR feature=NULL;
4481     LPCWSTR text = NULL;
4482     LPCWSTR qualifier = NULL;
4483     LPCWSTR component = NULL;
4484     LPWSTR advertise = NULL;
4485     LPWSTR output = NULL;
4486     HKEY hkey;
4487     UINT rc = ERROR_SUCCESS;
4488     MSICOMPONENT *comp;
4489     DWORD sz = 0;
4490     MSIRECORD *uirow;
4491
4492     component = MSI_RecordGetString(rec,3);
4493     comp = get_loaded_component(package,component);
4494
4495     if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL ) && 
4496        !ACTION_VerifyComponentForAction( comp, INSTALLSTATE_SOURCE ) &&
4497        !ACTION_VerifyComponentForAction( comp, INSTALLSTATE_ADVERTISED ))
4498     {
4499         TRACE("Skipping: Component %s not scheduled for install\n",
4500                         debugstr_w(component));
4501
4502         return ERROR_SUCCESS;
4503     }
4504
4505     compgroupid = MSI_RecordGetString(rec,1);
4506     qualifier = MSI_RecordGetString(rec,2);
4507
4508     rc = MSIREG_OpenUserComponentsKey(compgroupid, &hkey, TRUE);
4509     if (rc != ERROR_SUCCESS)
4510         goto end;
4511     
4512     text = MSI_RecordGetString(rec,4);
4513     feature = MSI_RecordGetString(rec,5);
4514   
4515     advertise = create_component_advertise_string(package, comp, feature);
4516
4517     sz = strlenW(advertise);
4518
4519     if (text)
4520         sz += lstrlenW(text);
4521
4522     sz+=3;
4523     sz *= sizeof(WCHAR);
4524            
4525     output = msi_alloc_zero(sz);
4526     strcpyW(output,advertise);
4527     msi_free(advertise);
4528
4529     if (text)
4530         strcatW(output,text);
4531
4532     msi_reg_set_val_multi_str( hkey, qualifier, output );
4533     
4534 end:
4535     RegCloseKey(hkey);
4536     msi_free(output);
4537
4538     /* the UI chunk */
4539     uirow = MSI_CreateRecord( 2 );
4540     MSI_RecordSetStringW( uirow, 1, compgroupid );
4541     MSI_RecordSetStringW( uirow, 2, qualifier);
4542     ui_actiondata( package, szPublishComponents, uirow);
4543     msiobj_release( &uirow->hdr );
4544     /* FIXME: call ui_progress? */
4545
4546     return rc;
4547 }
4548
4549 /*
4550  * At present I am ignorning the advertised components part of this and only
4551  * focusing on the qualified component sets
4552  */
4553 static UINT ACTION_PublishComponents(MSIPACKAGE *package)
4554 {
4555     UINT rc;
4556     MSIQUERY * view;
4557     static const WCHAR ExecSeqQuery[] =
4558         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4559          '`','P','u','b','l','i','s','h',
4560          'C','o','m','p','o','n','e','n','t','`',0};
4561     
4562     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4563     if (rc != ERROR_SUCCESS)
4564         return ERROR_SUCCESS;
4565
4566     rc = MSI_IterateRecords(view, NULL, ITERATE_PublishComponent, package);
4567     msiobj_release(&view->hdr);
4568
4569     return rc;
4570 }
4571
4572 static UINT ITERATE_InstallService(MSIRECORD *rec, LPVOID param)
4573 {
4574     MSIPACKAGE *package = (MSIPACKAGE*)param;
4575     MSIRECORD *row;
4576     MSIFILE *file;
4577     SC_HANDLE hscm, service = NULL;
4578     LPCWSTR comp, depends, pass;
4579     LPWSTR name = NULL, disp = NULL;
4580     LPCWSTR load_order, serv_name, key;
4581     DWORD serv_type, start_type;
4582     DWORD err_control;
4583
4584     static const WCHAR query[] =
4585         {'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
4586          '`','C','o','m','p','o','n','e','n','t','`',' ',
4587          'W','H','E','R','E',' ',
4588          '`','C','o','m','p','o','n','e','n','t','`',' ',
4589          '=','\'','%','s','\'',0};
4590
4591     hscm = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASEW, GENERIC_WRITE);
4592     if (!hscm)
4593     {
4594         ERR("Failed to open the SC Manager!\n");
4595         goto done;
4596     }
4597
4598     start_type = MSI_RecordGetInteger(rec, 5);
4599     if (start_type == SERVICE_BOOT_START || start_type == SERVICE_SYSTEM_START)
4600         goto done;
4601
4602     depends = MSI_RecordGetString(rec, 8);
4603     if (depends && *depends)
4604         FIXME("Dependency list unhandled!\n");
4605
4606     deformat_string(package, MSI_RecordGetString(rec, 2), &name);
4607     deformat_string(package, MSI_RecordGetString(rec, 3), &disp);
4608     serv_type = MSI_RecordGetInteger(rec, 4);
4609     err_control = MSI_RecordGetInteger(rec, 6);
4610     load_order = MSI_RecordGetString(rec, 7);
4611     serv_name = MSI_RecordGetString(rec, 9);
4612     pass = MSI_RecordGetString(rec, 10);
4613     comp = MSI_RecordGetString(rec, 12);
4614
4615     /* fetch the service path */
4616     row = MSI_QueryGetRecord(package->db, query, comp);
4617     if (!row)
4618     {
4619         ERR("Control query failed!\n");
4620         goto done;
4621     }
4622
4623     key = MSI_RecordGetString(row, 6);
4624
4625     file = get_loaded_file(package, key);
4626     msiobj_release(&row->hdr);
4627     if (!file)
4628     {
4629         ERR("Failed to load the service file\n");
4630         goto done;
4631     }
4632
4633     service = CreateServiceW(hscm, name, disp, GENERIC_ALL, serv_type,
4634                              start_type, err_control, file->TargetPath,
4635                              load_order, NULL, NULL, serv_name, pass);
4636     if (!service)
4637     {
4638         if (GetLastError() != ERROR_SERVICE_EXISTS)
4639             ERR("Failed to create service %s: %d\n", debugstr_w(name), GetLastError());
4640     }
4641
4642 done:
4643     CloseServiceHandle(service);
4644     CloseServiceHandle(hscm);
4645     msi_free(name);
4646     msi_free(disp);
4647
4648     return ERROR_SUCCESS;
4649 }
4650
4651 static UINT ACTION_InstallServices( MSIPACKAGE *package )
4652 {
4653     UINT rc;
4654     MSIQUERY * view;
4655     static const WCHAR ExecSeqQuery[] =
4656         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4657          'S','e','r','v','i','c','e','I','n','s','t','a','l','l',0};
4658     
4659     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4660     if (rc != ERROR_SUCCESS)
4661         return ERROR_SUCCESS;
4662
4663     rc = MSI_IterateRecords(view, NULL, ITERATE_InstallService, package);
4664     msiobj_release(&view->hdr);
4665
4666     return rc;
4667 }
4668
4669 /* converts arg1[~]arg2[~]arg3 to a list of ptrs to the strings */
4670 static LPCWSTR *msi_service_args_to_vector(LPWSTR args, DWORD *numargs)
4671 {
4672     LPCWSTR *vector, *temp_vector;
4673     LPWSTR p, q;
4674     DWORD sep_len;
4675
4676     static const WCHAR separator[] = {'[','~',']',0};
4677
4678     *numargs = 0;
4679     sep_len = sizeof(separator) / sizeof(WCHAR) - 1;
4680
4681     if (!args)
4682         return NULL;
4683
4684     vector = msi_alloc(sizeof(LPWSTR));
4685     if (!vector)
4686         return NULL;
4687
4688     p = args;
4689     do
4690     {
4691         (*numargs)++;
4692         vector[*numargs - 1] = p;
4693
4694         if ((q = strstrW(p, separator)))
4695         {
4696             *q = '\0';
4697
4698             temp_vector = msi_realloc(vector, (*numargs + 1) * sizeof(LPWSTR));
4699             if (!temp_vector)
4700             {
4701                 msi_free(vector);
4702                 return NULL;
4703             }
4704             vector = temp_vector;
4705
4706             p = q + sep_len;
4707         }
4708     } while (q);
4709
4710     return vector;
4711 }
4712
4713 static UINT ITERATE_StartService(MSIRECORD *rec, LPVOID param)
4714 {
4715     MSIPACKAGE *package = (MSIPACKAGE *)param;
4716     MSICOMPONENT *comp;
4717     SC_HANDLE scm, service = NULL;
4718     LPCWSTR name, *vector = NULL;
4719     LPWSTR args;
4720     DWORD event, numargs;
4721     UINT r = ERROR_FUNCTION_FAILED;
4722
4723     comp = get_loaded_component(package, MSI_RecordGetString(rec, 6));
4724     if (!comp || comp->Action == INSTALLSTATE_UNKNOWN || comp->Action == INSTALLSTATE_ABSENT)
4725         return ERROR_SUCCESS;
4726
4727     name = MSI_RecordGetString(rec, 2);
4728     event = MSI_RecordGetInteger(rec, 3);
4729     args = strdupW(MSI_RecordGetString(rec, 4));
4730
4731     if (!(event & msidbServiceControlEventStart))
4732         return ERROR_SUCCESS;
4733
4734     scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
4735     if (!scm)
4736     {
4737         ERR("Failed to open the service control manager\n");
4738         goto done;
4739     }
4740
4741     service = OpenServiceW(scm, name, SERVICE_START);
4742     if (!service)
4743     {
4744         ERR("Failed to open service %s\n", debugstr_w(name));
4745         goto done;
4746     }
4747
4748     vector = msi_service_args_to_vector(args, &numargs);
4749
4750     if (!StartServiceW(service, numargs, vector))
4751     {
4752         ERR("Failed to start service %s\n", debugstr_w(name));
4753         goto done;
4754     }
4755
4756     r = ERROR_SUCCESS;
4757
4758 done:
4759     CloseServiceHandle(service);
4760     CloseServiceHandle(scm);
4761
4762     msi_free(args);
4763     msi_free(vector);
4764     return r;
4765 }
4766
4767 static UINT ACTION_StartServices( MSIPACKAGE *package )
4768 {
4769     UINT rc;
4770     MSIQUERY *view;
4771
4772     static const WCHAR query[] = {
4773         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4774         'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
4775
4776     rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4777     if (rc != ERROR_SUCCESS)
4778         return ERROR_SUCCESS;
4779
4780     rc = MSI_IterateRecords(view, NULL, ITERATE_StartService, package);
4781     msiobj_release(&view->hdr);
4782
4783     return rc;
4784 }
4785
4786 static BOOL stop_service_dependents(SC_HANDLE scm, SC_HANDLE service)
4787 {
4788     DWORD i, needed, count;
4789     ENUM_SERVICE_STATUSW *dependencies;
4790     SERVICE_STATUS ss;
4791     SC_HANDLE depserv;
4792
4793     if (EnumDependentServicesW(service, SERVICE_ACTIVE, NULL,
4794                                0, &needed, &count))
4795         return TRUE;
4796
4797     if (GetLastError() != ERROR_MORE_DATA)
4798         return FALSE;
4799
4800     dependencies = msi_alloc(needed);
4801     if (!dependencies)
4802         return FALSE;
4803
4804     if (!EnumDependentServicesW(service, SERVICE_ACTIVE, dependencies,
4805                                 needed, &needed, &count))
4806         goto error;
4807
4808     for (i = 0; i < count; i++)
4809     {
4810         depserv = OpenServiceW(scm, dependencies[i].lpServiceName,
4811                                SERVICE_STOP | SERVICE_QUERY_STATUS);
4812         if (!depserv)
4813             goto error;
4814
4815         if (!ControlService(depserv, SERVICE_CONTROL_STOP, &ss))
4816             goto error;
4817     }
4818
4819     return TRUE;
4820
4821 error:
4822     msi_free(dependencies);
4823     return FALSE;
4824 }
4825
4826 static UINT ITERATE_StopService(MSIRECORD *rec, LPVOID param)
4827 {
4828     MSIPACKAGE *package = (MSIPACKAGE *)param;
4829     MSICOMPONENT *comp;
4830     SERVICE_STATUS status;
4831     SERVICE_STATUS_PROCESS ssp;
4832     SC_HANDLE scm = NULL, service = NULL;
4833     LPWSTR name, args;
4834     DWORD event, needed;
4835
4836     event = MSI_RecordGetInteger(rec, 3);
4837     if (!(event & msidbServiceControlEventStop))
4838         return ERROR_SUCCESS;
4839
4840     comp = get_loaded_component(package, MSI_RecordGetString(rec, 6));
4841     if (!comp || comp->Action == INSTALLSTATE_UNKNOWN || comp->Action == INSTALLSTATE_ABSENT)
4842         return ERROR_SUCCESS;
4843
4844     deformat_string(package, MSI_RecordGetString(rec, 2), &name);
4845     deformat_string(package, MSI_RecordGetString(rec, 4), &args);
4846     args = strdupW(MSI_RecordGetString(rec, 4));
4847
4848     scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
4849     if (!scm)
4850     {
4851         WARN("Failed to open the SCM: %d\n", GetLastError());
4852         goto done;
4853     }
4854
4855     service = OpenServiceW(scm, name,
4856                            SERVICE_STOP |
4857                            SERVICE_QUERY_STATUS |
4858                            SERVICE_ENUMERATE_DEPENDENTS);
4859     if (!service)
4860     {
4861         WARN("Failed to open service (%s): %d\n",
4862               debugstr_w(name), GetLastError());
4863         goto done;
4864     }
4865
4866     if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp,
4867                               sizeof(SERVICE_STATUS_PROCESS), &needed))
4868     {
4869         WARN("Failed to query service status (%s): %d\n",
4870              debugstr_w(name), GetLastError());
4871         goto done;
4872     }
4873
4874     if (ssp.dwCurrentState == SERVICE_STOPPED)
4875         goto done;
4876
4877     stop_service_dependents(scm, service);
4878
4879     if (!ControlService(service, SERVICE_CONTROL_STOP, &status))
4880         WARN("Failed to stop service (%s): %d\n", debugstr_w(name), GetLastError());
4881
4882 done:
4883     CloseServiceHandle(service);
4884     CloseServiceHandle(scm);
4885     msi_free(name);
4886     msi_free(args);
4887
4888     return ERROR_SUCCESS;
4889 }
4890
4891 static UINT ACTION_StopServices( MSIPACKAGE *package )
4892 {
4893     UINT rc;
4894     MSIQUERY *view;
4895
4896     static const WCHAR query[] = {
4897         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4898         'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
4899
4900     rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4901     if (rc != ERROR_SUCCESS)
4902         return ERROR_SUCCESS;
4903
4904     rc = MSI_IterateRecords(view, NULL, ITERATE_StopService, package);
4905     msiobj_release(&view->hdr);
4906
4907     return rc;
4908 }
4909
4910 static MSIFILE *msi_find_file( MSIPACKAGE *package, LPCWSTR filename )
4911 {
4912     MSIFILE *file;
4913
4914     LIST_FOR_EACH_ENTRY(file, &package->files, MSIFILE, entry)
4915     {
4916         if (!lstrcmpW(file->File, filename))
4917             return file;
4918     }
4919
4920     return NULL;
4921 }
4922
4923 static UINT ITERATE_InstallODBCDriver( MSIRECORD *rec, LPVOID param )
4924 {
4925     MSIPACKAGE *package = (MSIPACKAGE*)param;
4926     LPWSTR driver, driver_path, ptr;
4927     WCHAR outpath[MAX_PATH];
4928     MSIFILE *driver_file, *setup_file;
4929     LPCWSTR desc;
4930     DWORD len, usage;
4931     UINT r = ERROR_SUCCESS;
4932
4933     static const WCHAR driver_fmt[] = {
4934         'D','r','i','v','e','r','=','%','s',0};
4935     static const WCHAR setup_fmt[] = {
4936         'S','e','t','u','p','=','%','s',0};
4937     static const WCHAR usage_fmt[] = {
4938         'F','i','l','e','U','s','a','g','e','=','1',0};
4939
4940     desc = MSI_RecordGetString(rec, 3);
4941
4942     driver_file = msi_find_file(package, MSI_RecordGetString(rec, 4));
4943     setup_file = msi_find_file(package, MSI_RecordGetString(rec, 5));
4944
4945     if (!driver_file || !setup_file)
4946     {
4947         ERR("ODBC Driver entry not found!\n");
4948         return ERROR_FUNCTION_FAILED;
4949     }
4950
4951     len = lstrlenW(desc) + lstrlenW(driver_fmt) + lstrlenW(driver_file->FileName) +
4952           lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName) +
4953           lstrlenW(usage_fmt) + 1;
4954     driver = msi_alloc(len * sizeof(WCHAR));
4955     if (!driver)
4956         return ERROR_OUTOFMEMORY;
4957
4958     ptr = driver;
4959     lstrcpyW(ptr, desc);
4960     ptr += lstrlenW(ptr) + 1;
4961
4962     sprintfW(ptr, driver_fmt, driver_file->FileName);
4963     ptr += lstrlenW(ptr) + 1;
4964
4965     sprintfW(ptr, setup_fmt, setup_file->FileName);
4966     ptr += lstrlenW(ptr) + 1;
4967
4968     lstrcpyW(ptr, usage_fmt);
4969     ptr += lstrlenW(ptr) + 1;
4970     *ptr = '\0';
4971
4972     driver_path = strdupW(driver_file->TargetPath);
4973     ptr = strrchrW(driver_path, '\\');
4974     if (ptr) *ptr = '\0';
4975
4976     if (!SQLInstallDriverExW(driver, driver_path, outpath, MAX_PATH,
4977                              NULL, ODBC_INSTALL_COMPLETE, &usage))
4978     {
4979         ERR("Failed to install SQL driver!\n");
4980         r = ERROR_FUNCTION_FAILED;
4981     }
4982
4983     msi_free(driver);
4984     msi_free(driver_path);
4985
4986     return r;
4987 }
4988
4989 static UINT ITERATE_InstallODBCTranslator( MSIRECORD *rec, LPVOID param )
4990 {
4991     MSIPACKAGE *package = (MSIPACKAGE*)param;
4992     LPWSTR translator, translator_path, ptr;
4993     WCHAR outpath[MAX_PATH];
4994     MSIFILE *translator_file, *setup_file;
4995     LPCWSTR desc;
4996     DWORD len, usage;
4997     UINT r = ERROR_SUCCESS;
4998
4999     static const WCHAR translator_fmt[] = {
5000         'T','r','a','n','s','l','a','t','o','r','=','%','s',0};
5001     static const WCHAR setup_fmt[] = {
5002         'S','e','t','u','p','=','%','s',0};
5003
5004     desc = MSI_RecordGetString(rec, 3);
5005
5006     translator_file = msi_find_file(package, MSI_RecordGetString(rec, 4));
5007     setup_file = msi_find_file(package, MSI_RecordGetString(rec, 5));
5008
5009     if (!translator_file || !setup_file)
5010     {
5011         ERR("ODBC Translator entry not found!\n");
5012         return ERROR_FUNCTION_FAILED;
5013     }
5014
5015     len = lstrlenW(desc) + lstrlenW(translator_fmt) + lstrlenW(translator_file->FileName) +
5016           lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName) + 1;
5017     translator = msi_alloc(len * sizeof(WCHAR));
5018     if (!translator)
5019         return ERROR_OUTOFMEMORY;
5020
5021     ptr = translator;
5022     lstrcpyW(ptr, desc);
5023     ptr += lstrlenW(ptr) + 1;
5024
5025     sprintfW(ptr, translator_fmt, translator_file->FileName);
5026     ptr += lstrlenW(ptr) + 1;
5027
5028     sprintfW(ptr, setup_fmt, setup_file->FileName);
5029     ptr += lstrlenW(ptr) + 1;
5030     *ptr = '\0';
5031
5032     translator_path = strdupW(translator_file->TargetPath);
5033     ptr = strrchrW(translator_path, '\\');
5034     if (ptr) *ptr = '\0';
5035
5036     if (!SQLInstallTranslatorExW(translator, translator_path, outpath, MAX_PATH,
5037                                  NULL, ODBC_INSTALL_COMPLETE, &usage))
5038     {
5039         ERR("Failed to install SQL translator!\n");
5040         r = ERROR_FUNCTION_FAILED;
5041     }
5042
5043     msi_free(translator);
5044     msi_free(translator_path);
5045
5046     return r;
5047 }
5048
5049 static UINT ITERATE_InstallODBCDataSource( MSIRECORD *rec, LPVOID param )
5050 {
5051     LPWSTR attrs;
5052     LPCWSTR desc, driver;
5053     WORD request = ODBC_ADD_SYS_DSN;
5054     INT registration;
5055     DWORD len;
5056     UINT r = ERROR_SUCCESS;
5057
5058     static const WCHAR attrs_fmt[] = {
5059         'D','S','N','=','%','s',0 };
5060
5061     desc = MSI_RecordGetString(rec, 3);
5062     driver = MSI_RecordGetString(rec, 4);
5063     registration = MSI_RecordGetInteger(rec, 5);
5064
5065     if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_ADD_SYS_DSN;
5066     else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_ADD_DSN;
5067
5068     len = lstrlenW(attrs_fmt) + lstrlenW(desc) + 1 + 1;
5069     attrs = msi_alloc(len * sizeof(WCHAR));
5070     if (!attrs)
5071         return ERROR_OUTOFMEMORY;
5072
5073     sprintfW(attrs, attrs_fmt, desc);
5074     attrs[len - 1] = '\0';
5075
5076     if (!SQLConfigDataSourceW(NULL, request, driver, attrs))
5077     {
5078         ERR("Failed to install SQL data source!\n");
5079         r = ERROR_FUNCTION_FAILED;
5080     }
5081
5082     msi_free(attrs);
5083
5084     return r;
5085 }
5086
5087 static UINT ACTION_InstallODBC( MSIPACKAGE *package )
5088 {
5089     UINT rc;
5090     MSIQUERY *view;
5091
5092     static const WCHAR driver_query[] = {
5093         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5094         'O','D','B','C','D','r','i','v','e','r',0 };
5095
5096     static const WCHAR translator_query[] = {
5097         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5098         'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0 };
5099
5100     static const WCHAR source_query[] = {
5101         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5102         'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0 };
5103
5104     rc = MSI_DatabaseOpenViewW(package->db, driver_query, &view);
5105     if (rc != ERROR_SUCCESS)
5106         return ERROR_SUCCESS;
5107
5108     rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDriver, package);
5109     msiobj_release(&view->hdr);
5110
5111     rc = MSI_DatabaseOpenViewW(package->db, translator_query, &view);
5112     if (rc != ERROR_SUCCESS)
5113         return ERROR_SUCCESS;
5114
5115     rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCTranslator, package);
5116     msiobj_release(&view->hdr);
5117
5118     rc = MSI_DatabaseOpenViewW(package->db, source_query, &view);
5119     if (rc != ERROR_SUCCESS)
5120         return ERROR_SUCCESS;
5121
5122     rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDataSource, package);
5123     msiobj_release(&view->hdr);
5124
5125     return rc;
5126 }
5127
5128 #define ENV_ACT_SETALWAYS   0x1
5129 #define ENV_ACT_SETABSENT   0x2
5130 #define ENV_ACT_REMOVE      0x4
5131 #define ENV_ACT_REMOVEMATCH 0x8
5132
5133 #define ENV_MOD_MACHINE     0x20000000
5134 #define ENV_MOD_APPEND      0x40000000
5135 #define ENV_MOD_PREFIX      0x80000000
5136 #define ENV_MOD_MASK        0xC0000000
5137
5138 #define check_flag_combo(x, y) ((x) & ~(y)) == (y)
5139
5140 static LONG env_set_flags( LPCWSTR *name, LPCWSTR *value, DWORD *flags )
5141 {
5142     LPCWSTR cptr = *name;
5143     LPCWSTR ptr = *value;
5144
5145     static const WCHAR prefix[] = {'[','~',']',0};
5146     static const int prefix_len = 3;
5147
5148     *flags = 0;
5149     while (*cptr)
5150     {
5151         if (*cptr == '=')
5152             *flags |= ENV_ACT_SETALWAYS;
5153         else if (*cptr == '+')
5154             *flags |= ENV_ACT_SETABSENT;
5155         else if (*cptr == '-')
5156             *flags |= ENV_ACT_REMOVE;
5157         else if (*cptr == '!')
5158             *flags |= ENV_ACT_REMOVEMATCH;
5159         else if (*cptr == '*')
5160             *flags |= ENV_MOD_MACHINE;
5161         else
5162             break;
5163
5164         cptr++;
5165         (*name)++;
5166     }
5167
5168     if (!*cptr)
5169     {
5170         ERR("Missing environment variable\n");
5171         return ERROR_FUNCTION_FAILED;
5172     }
5173
5174     if (!strncmpW(ptr, prefix, prefix_len))
5175     {
5176         *flags |= ENV_MOD_APPEND;
5177         *value += lstrlenW(prefix);
5178     }
5179     else if (lstrlenW(*value) >= prefix_len)
5180     {
5181         ptr += lstrlenW(ptr) - prefix_len;
5182         if (!lstrcmpW(ptr, prefix))
5183         {
5184             *flags |= ENV_MOD_PREFIX;
5185             /* the "[~]" will be removed by deformat_string */;
5186         }
5187     }
5188
5189     if (!*flags ||
5190         check_flag_combo(*flags, ENV_ACT_SETALWAYS | ENV_ACT_SETABSENT) ||
5191         check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETABSENT) ||
5192         check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETALWAYS) ||
5193         check_flag_combo(*flags, ENV_ACT_SETABSENT | ENV_MOD_MASK))
5194     {
5195         ERR("Invalid flags: %08x\n", *flags);
5196         return ERROR_FUNCTION_FAILED;
5197     }
5198
5199     return ERROR_SUCCESS;
5200 }
5201
5202 static UINT ITERATE_WriteEnvironmentString( MSIRECORD *rec, LPVOID param )
5203 {
5204     MSIPACKAGE *package = param;
5205     LPCWSTR name, value;
5206     LPWSTR data = NULL, newval = NULL;
5207     LPWSTR deformatted = NULL, ptr;
5208     DWORD flags, type, size;
5209     LONG res;
5210     HKEY env = NULL, root;
5211     LPCWSTR environment;
5212
5213     static const WCHAR user_env[] =
5214         {'E','n','v','i','r','o','n','m','e','n','t',0};
5215     static const WCHAR machine_env[] =
5216         {'S','y','s','t','e','m','\\',
5217          'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
5218          'C','o','n','t','r','o','l','\\',
5219          'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r','\\',
5220          'E','n','v','i','r','o','n','m','e','n','t',0};
5221     static const WCHAR semicolon[] = {';',0};
5222
5223     name = MSI_RecordGetString(rec, 2);
5224     value = MSI_RecordGetString(rec, 3);
5225
5226     res = env_set_flags(&name, &value, &flags);
5227     if (res != ERROR_SUCCESS)
5228        goto done;
5229
5230     deformat_string(package, value, &deformatted);
5231     if (!deformatted)
5232     {
5233         res = ERROR_OUTOFMEMORY;
5234         goto done;
5235     }
5236
5237     value = deformatted;
5238
5239     if (flags & ENV_MOD_MACHINE)
5240     {
5241         environment = machine_env;
5242         root = HKEY_LOCAL_MACHINE;
5243     }
5244     else
5245     {
5246         environment = user_env;
5247         root = HKEY_CURRENT_USER;
5248     }
5249
5250     res = RegCreateKeyExW(root, environment, 0, NULL, 0,
5251                           KEY_ALL_ACCESS, NULL, &env, NULL);
5252     if (res != ERROR_SUCCESS)
5253         goto done;
5254
5255     if (flags & ENV_ACT_REMOVE)
5256         FIXME("Not removing environment variable on uninstall!\n");
5257
5258     size = 0;
5259     res = RegQueryValueExW(env, name, NULL, &type, NULL, &size);
5260     if ((res != ERROR_SUCCESS && res != ERROR_FILE_NOT_FOUND) ||
5261         (res == ERROR_SUCCESS && type != REG_SZ && type != REG_EXPAND_SZ))
5262         goto done;
5263
5264     if (res != ERROR_FILE_NOT_FOUND)
5265     {
5266         if (flags & ENV_ACT_SETABSENT)
5267         {
5268             res = ERROR_SUCCESS;
5269             goto done;
5270         }
5271
5272         data = msi_alloc(size);
5273         if (!data)
5274         {
5275             RegCloseKey(env);
5276             return ERROR_OUTOFMEMORY;
5277         }
5278
5279         res = RegQueryValueExW(env, name, NULL, &type, (LPVOID)data, &size);
5280         if (res != ERROR_SUCCESS)
5281             goto done;
5282
5283         if (flags & ENV_ACT_REMOVEMATCH && (!value || !lstrcmpW(data, value)))
5284         {
5285             res = RegDeleteKeyW(env, name);
5286             goto done;
5287         }
5288
5289         size =  (lstrlenW(value) + 1 + size) * sizeof(WCHAR);
5290         newval =  msi_alloc(size);
5291         ptr = newval;
5292         if (!newval)
5293         {
5294             res = ERROR_OUTOFMEMORY;
5295             goto done;
5296         }
5297
5298         if (!(flags & ENV_MOD_MASK))
5299             lstrcpyW(newval, value);
5300         else
5301         {
5302             if (flags & ENV_MOD_PREFIX)
5303             {
5304                 lstrcpyW(newval, value);
5305                 lstrcatW(newval, semicolon);
5306                 ptr = newval + lstrlenW(value) + 1;
5307             }
5308
5309             lstrcpyW(ptr, data);
5310
5311             if (flags & ENV_MOD_APPEND)
5312             {
5313                 lstrcatW(newval, semicolon);
5314                 lstrcatW(newval, value);
5315             }
5316         }
5317     }
5318     else
5319     {
5320         size = (lstrlenW(value) + 1) * sizeof(WCHAR);
5321         newval = msi_alloc(size);
5322         if (!newval)
5323         {
5324             res = ERROR_OUTOFMEMORY;
5325             goto done;
5326         }
5327
5328         lstrcpyW(newval, value);
5329     }
5330
5331     TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(newval));
5332     res = RegSetValueExW(env, name, 0, type, (LPVOID)newval, size);
5333
5334 done:
5335     if (env) RegCloseKey(env);
5336     msi_free(deformatted);
5337     msi_free(data);
5338     msi_free(newval);
5339     return res;
5340 }
5341
5342 static UINT ACTION_WriteEnvironmentStrings( MSIPACKAGE *package )
5343 {
5344     UINT rc;
5345     MSIQUERY * view;
5346     static const WCHAR ExecSeqQuery[] =
5347         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5348          '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
5349     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
5350     if (rc != ERROR_SUCCESS)
5351         return ERROR_SUCCESS;
5352
5353     rc = MSI_IterateRecords(view, NULL, ITERATE_WriteEnvironmentString, package);
5354     msiobj_release(&view->hdr);
5355
5356     return rc;
5357 }
5358
5359 #define is_dot_dir(x) ((x[0] == '.') && ((x[1] == 0) || ((x[1] == '.') && (x[2] == 0))))
5360
5361 typedef struct
5362 {
5363     struct list entry;
5364     LPWSTR sourcename;
5365     LPWSTR destname;
5366     LPWSTR source;
5367     LPWSTR dest;
5368 } FILE_LIST;
5369
5370 static BOOL msi_move_file(LPCWSTR source, LPCWSTR dest, int options)
5371 {
5372     BOOL ret;
5373
5374     if (GetFileAttributesW(source) == FILE_ATTRIBUTE_DIRECTORY ||
5375         GetFileAttributesW(dest) == FILE_ATTRIBUTE_DIRECTORY)
5376     {
5377         WARN("Source or dest is directory, not moving\n");
5378         return FALSE;
5379     }
5380
5381     if (options == msidbMoveFileOptionsMove)
5382     {
5383         TRACE("moving %s -> %s\n", debugstr_w(source), debugstr_w(dest));
5384         ret = MoveFileExW(source, dest, MOVEFILE_REPLACE_EXISTING);
5385         if (!ret)
5386         {
5387             WARN("MoveFile failed: %d\n", GetLastError());
5388             return FALSE;
5389         }
5390     }
5391     else
5392     {
5393         TRACE("copying %s -> %s\n", debugstr_w(source), debugstr_w(dest));
5394         ret = CopyFileW(source, dest, FALSE);
5395         if (!ret)
5396         {
5397             WARN("CopyFile failed: %d\n", GetLastError());
5398             return FALSE;
5399         }
5400     }
5401
5402     return TRUE;
5403 }
5404
5405 static LPWSTR wildcard_to_file(LPWSTR wildcard, LPWSTR filename)
5406 {
5407     LPWSTR path, ptr;
5408     DWORD dirlen, pathlen;
5409
5410     ptr = strrchrW(wildcard, '\\');
5411     dirlen = ptr - wildcard + 1;
5412
5413     pathlen = dirlen + lstrlenW(filename) + 1;
5414     path = msi_alloc(pathlen * sizeof(WCHAR));
5415
5416     lstrcpynW(path, wildcard, dirlen + 1);
5417     lstrcatW(path, filename);
5418
5419     return path;
5420 }
5421
5422 static void free_file_entry(FILE_LIST *file)
5423 {
5424     msi_free(file->source);
5425     msi_free(file->dest);
5426     msi_free(file);
5427 }
5428
5429 static void free_list(FILE_LIST *list)
5430 {
5431     while (!list_empty(&list->entry))
5432     {
5433         FILE_LIST *file = LIST_ENTRY(list_head(&list->entry), FILE_LIST, entry);
5434
5435         list_remove(&file->entry);
5436         free_file_entry(file);
5437     }
5438 }
5439
5440 static BOOL add_wildcard(FILE_LIST *files, LPWSTR source, LPWSTR dest)
5441 {
5442     FILE_LIST *new, *file;
5443     LPWSTR ptr, filename;
5444     DWORD size;
5445
5446     new = msi_alloc_zero(sizeof(FILE_LIST));
5447     if (!new)
5448         return FALSE;
5449
5450     new->source = strdupW(source);
5451     ptr = strrchrW(dest, '\\') + 1;
5452     filename = strrchrW(new->source, '\\') + 1;
5453
5454     new->sourcename = filename;
5455
5456     if (*ptr)
5457         new->destname = ptr;
5458     else
5459         new->destname = new->sourcename;
5460
5461     size = (ptr - dest) + lstrlenW(filename) + 1;
5462     new->dest = msi_alloc(size * sizeof(WCHAR));
5463     if (!new->dest)
5464     {
5465         free_file_entry(new);
5466         return FALSE;
5467     }
5468
5469     lstrcpynW(new->dest, dest, ptr - dest + 1);
5470     lstrcatW(new->dest, filename);
5471
5472     if (list_empty(&files->entry))
5473     {
5474         list_add_head(&files->entry, &new->entry);
5475         return TRUE;
5476     }
5477
5478     LIST_FOR_EACH_ENTRY(file, &files->entry, FILE_LIST, entry)
5479     {
5480         if (lstrcmpW(source, file->source) < 0)
5481         {
5482             list_add_before(&file->entry, &new->entry);
5483             return TRUE;
5484         }
5485     }
5486
5487     list_add_after(&file->entry, &new->entry);
5488     return TRUE;
5489 }
5490
5491 static BOOL move_files_wildcard(LPWSTR source, LPWSTR dest, int options)
5492 {
5493     WIN32_FIND_DATAW wfd;
5494     HANDLE hfile;
5495     LPWSTR path;
5496     BOOL res;
5497     FILE_LIST files, *file;
5498     DWORD size;
5499
5500     hfile = FindFirstFileW(source, &wfd);
5501     if (hfile == INVALID_HANDLE_VALUE) return FALSE;
5502
5503     list_init(&files.entry);
5504
5505     for (res = TRUE; res; res = FindNextFileW(hfile, &wfd))
5506     {
5507         if (is_dot_dir(wfd.cFileName)) continue;
5508
5509         path = wildcard_to_file(source, wfd.cFileName);
5510         if (!path)
5511         {
5512             res = FALSE;
5513             goto done;
5514         }
5515
5516         add_wildcard(&files, path, dest);
5517         msi_free(path);
5518     }
5519
5520     /* no files match the wildcard */
5521     if (list_empty(&files.entry))
5522         goto done;
5523
5524     /* only the first wildcard match gets renamed to dest */
5525     file = LIST_ENTRY(list_head(&files.entry), FILE_LIST, entry);
5526     size = (strrchrW(file->dest, '\\') - file->dest) + lstrlenW(file->destname) + 2;
5527     file->dest = msi_realloc(file->dest, size * sizeof(WCHAR));
5528     if (!file->dest)
5529     {
5530         res = FALSE;
5531         goto done;
5532     }
5533
5534     lstrcpyW(strrchrW(file->dest, '\\') + 1, file->destname);
5535
5536     while (!list_empty(&files.entry))
5537     {
5538         file = LIST_ENTRY(list_head(&files.entry), FILE_LIST, entry);
5539
5540         msi_move_file((LPCWSTR)file->source, (LPCWSTR)file->dest, options);
5541
5542         list_remove(&file->entry);
5543         free_file_entry(file);
5544     }
5545
5546     res = TRUE;
5547
5548 done:
5549     free_list(&files);
5550     FindClose(hfile);
5551     return res;
5552 }
5553
5554 static UINT ITERATE_MoveFiles( MSIRECORD *rec, LPVOID param )
5555 {
5556     MSIPACKAGE *package = param;
5557     MSICOMPONENT *comp;
5558     LPCWSTR sourcename;
5559     LPWSTR destname = NULL;
5560     LPWSTR sourcedir = NULL, destdir = NULL;
5561     LPWSTR source = NULL, dest = NULL;
5562     int options;
5563     DWORD size;
5564     BOOL ret, wildcards;
5565
5566     static const WCHAR backslash[] = {'\\',0};
5567
5568     comp = get_loaded_component(package, MSI_RecordGetString(rec, 2));
5569     if (!comp || !comp->Enabled ||
5570         !(comp->Action & (INSTALLSTATE_LOCAL | INSTALLSTATE_SOURCE)))
5571     {
5572         TRACE("Component not set for install, not moving file\n");
5573         return ERROR_SUCCESS;
5574     }
5575
5576     sourcename = MSI_RecordGetString(rec, 3);
5577     options = MSI_RecordGetInteger(rec, 7);
5578
5579     sourcedir = msi_dup_property(package, MSI_RecordGetString(rec, 5));
5580     if (!sourcedir)
5581         goto done;
5582
5583     destdir = msi_dup_property(package, MSI_RecordGetString(rec, 6));
5584     if (!destdir)
5585         goto done;
5586
5587     if (!sourcename)
5588     {
5589         if (GetFileAttributesW(sourcedir) == INVALID_FILE_ATTRIBUTES)
5590             goto done;
5591
5592         source = strdupW(sourcedir);
5593         if (!source)
5594             goto done;
5595     }
5596     else
5597     {
5598         size = lstrlenW(sourcedir) + lstrlenW(sourcename) + 2;
5599         source = msi_alloc(size * sizeof(WCHAR));
5600         if (!source)
5601             goto done;
5602
5603         lstrcpyW(source, sourcedir);
5604         if (source[lstrlenW(source) - 1] != '\\')
5605             lstrcatW(source, backslash);
5606         lstrcatW(source, sourcename);
5607     }
5608
5609     wildcards = strchrW(source, '*') || strchrW(source, '?');
5610
5611     if (MSI_RecordIsNull(rec, 4))
5612     {
5613         if (!wildcards)
5614         {
5615             destname = strdupW(sourcename);
5616             if (!destname)
5617                 goto done;
5618         }
5619     }
5620     else
5621     {
5622         destname = strdupW(MSI_RecordGetString(rec, 4));
5623         if (destname)
5624             reduce_to_longfilename(destname);
5625     }
5626
5627     size = 0;
5628     if (destname)
5629         size = lstrlenW(destname);
5630
5631     size += lstrlenW(destdir) + 2;
5632     dest = msi_alloc(size * sizeof(WCHAR));
5633     if (!dest)
5634         goto done;
5635
5636     lstrcpyW(dest, destdir);
5637     if (dest[lstrlenW(dest) - 1] != '\\')
5638         lstrcatW(dest, backslash);
5639
5640     if (destname)
5641         lstrcatW(dest, destname);
5642
5643     if (GetFileAttributesW(destdir) == INVALID_FILE_ATTRIBUTES)
5644     {
5645         ret = CreateDirectoryW(destdir, NULL);
5646         if (!ret)
5647         {
5648             WARN("CreateDirectory failed: %d\n", GetLastError());
5649             return ERROR_SUCCESS;
5650         }
5651     }
5652
5653     if (!wildcards)
5654         msi_move_file(source, dest, options);
5655     else
5656         move_files_wildcard(source, dest, options);
5657
5658 done:
5659     msi_free(sourcedir);
5660     msi_free(destdir);
5661     msi_free(destname);
5662     msi_free(source);
5663     msi_free(dest);
5664
5665     return ERROR_SUCCESS;
5666 }
5667
5668 static UINT ACTION_MoveFiles( MSIPACKAGE *package )
5669 {
5670     UINT rc;
5671     MSIQUERY *view;
5672
5673     static const WCHAR ExecSeqQuery[] =
5674         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5675          '`','M','o','v','e','F','i','l','e','`',0};
5676
5677     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
5678     if (rc != ERROR_SUCCESS)
5679         return ERROR_SUCCESS;
5680
5681     rc = MSI_IterateRecords(view, NULL, ITERATE_MoveFiles, package);
5682     msiobj_release(&view->hdr);
5683
5684     return rc;
5685 }
5686
5687 typedef struct tagMSIASSEMBLY
5688 {
5689     struct list entry;
5690     MSICOMPONENT *component;
5691     MSIFEATURE *feature;
5692     MSIFILE *file;
5693     LPWSTR manifest;
5694     LPWSTR application;
5695     DWORD attributes;
5696     BOOL installed;
5697 } MSIASSEMBLY;
5698
5699 static HRESULT (WINAPI *pCreateAssemblyCache)(IAssemblyCache **ppAsmCache,
5700                                               DWORD dwReserved);
5701 static HRESULT (WINAPI *pLoadLibraryShim)(LPCWSTR szDllName, LPCWSTR szVersion,
5702                                           LPVOID pvReserved, HMODULE *phModDll);
5703
5704 static BOOL init_functionpointers(void)
5705 {
5706     HRESULT hr;
5707     HMODULE hfusion;
5708     HMODULE hmscoree;
5709
5710     static const WCHAR szFusion[] = {'f','u','s','i','o','n','.','d','l','l',0};
5711
5712     hmscoree = LoadLibraryA("mscoree.dll");
5713     if (!hmscoree)
5714     {
5715         WARN("mscoree.dll not available\n");
5716         return FALSE;
5717     }
5718
5719     pLoadLibraryShim = (void *)GetProcAddress(hmscoree, "LoadLibraryShim");
5720     if (!pLoadLibraryShim)
5721     {
5722         WARN("LoadLibraryShim not available\n");
5723         FreeLibrary(hmscoree);
5724         return FALSE;
5725     }
5726
5727     hr = pLoadLibraryShim(szFusion, NULL, NULL, &hfusion);
5728     if (FAILED(hr))
5729     {
5730         WARN("fusion.dll not available\n");
5731         FreeLibrary(hmscoree);
5732         return FALSE;
5733     }
5734
5735     pCreateAssemblyCache = (void *)GetProcAddress(hfusion, "CreateAssemblyCache");
5736
5737     FreeLibrary(hmscoree);
5738     return TRUE;
5739 }
5740
5741 static UINT install_assembly(MSIPACKAGE *package, MSIASSEMBLY *assembly,
5742                              LPWSTR path)
5743 {
5744     IAssemblyCache *cache;
5745     HRESULT hr;
5746     UINT r = ERROR_FUNCTION_FAILED;
5747
5748     TRACE("installing assembly: %s\n", debugstr_w(path));
5749
5750     if (assembly->feature)
5751         msi_feature_set_state(package, assembly->feature, INSTALLSTATE_LOCAL);
5752
5753     if (assembly->manifest)
5754         FIXME("Manifest unhandled\n");
5755
5756     if (assembly->application)
5757     {
5758         FIXME("Assembly should be privately installed\n");
5759         return ERROR_SUCCESS;
5760     }
5761
5762     if (assembly->attributes == msidbAssemblyAttributesWin32)
5763     {
5764         FIXME("Win32 assemblies not handled\n");
5765         return ERROR_SUCCESS;
5766     }
5767
5768     hr = pCreateAssemblyCache(&cache, 0);
5769     if (FAILED(hr))
5770         goto done;
5771
5772     hr = IAssemblyCache_InstallAssembly(cache, 0, path, NULL);
5773     if (FAILED(hr))
5774         ERR("Failed to install assembly: %s %08x\n", debugstr_w(path), hr);
5775
5776     r = ERROR_SUCCESS;
5777
5778 done:
5779     IAssemblyCache_Release(cache);
5780     return r;
5781 }
5782
5783 typedef struct tagASSEMBLY_LIST
5784 {
5785     MSIPACKAGE *package;
5786     IAssemblyCache *cache;
5787     struct list *assemblies;
5788 } ASSEMBLY_LIST;
5789
5790 typedef struct tagASSEMBLY_NAME
5791 {
5792     LPWSTR name;
5793     LPWSTR version;
5794     LPWSTR culture;
5795     LPWSTR pubkeytoken;
5796 } ASSEMBLY_NAME;
5797
5798 static UINT parse_assembly_name(MSIRECORD *rec, LPVOID param)
5799 {
5800     ASSEMBLY_NAME *asmname = (ASSEMBLY_NAME *)param;
5801     LPCWSTR name = MSI_RecordGetString(rec, 2);
5802     LPWSTR val = msi_dup_record_field(rec, 3);
5803
5804     static const WCHAR Name[] = {'N','a','m','e',0};
5805     static const WCHAR Version[] = {'V','e','r','s','i','o','n',0};
5806     static const WCHAR Culture[] = {'C','u','l','t','u','r','e',0};
5807     static const WCHAR PublicKeyToken[] = {
5808         'P','u','b','l','i','c','K','e','y','T','o','k','e','n',0};
5809
5810     if (!lstrcmpW(name, Name))
5811         asmname->name = val;
5812     else if (!lstrcmpW(name, Version))
5813         asmname->version = val;
5814     else if (!lstrcmpW(name, Culture))
5815         asmname->culture = val;
5816     else if (!lstrcmpW(name, PublicKeyToken))
5817         asmname->pubkeytoken = val;
5818     else
5819         msi_free(val);
5820
5821     return ERROR_SUCCESS;
5822 }
5823
5824 static void append_str(LPWSTR *str, DWORD *size, LPCWSTR append)
5825 {
5826     if (!*str)
5827     {
5828         *size = lstrlenW(append) + 1;
5829         *str = msi_alloc((*size) * sizeof(WCHAR));
5830         lstrcpyW(*str, append);
5831         return;
5832     }
5833
5834     (*size) += lstrlenW(append);
5835     *str = msi_realloc(*str, (*size) * sizeof(WCHAR));
5836     lstrcatW(*str, append);
5837 }
5838
5839 static BOOL check_assembly_installed(MSIDATABASE *db, IAssemblyCache *cache,
5840                                      MSICOMPONENT *comp)
5841 {
5842     ASSEMBLY_INFO asminfo;
5843     ASSEMBLY_NAME name;
5844     MSIQUERY *view;
5845     LPWSTR disp;
5846     DWORD size;
5847     BOOL found;
5848     UINT r;
5849
5850     static const WCHAR separator[] = {',',' ',0};
5851     static const WCHAR Version[] = {'V','e','r','s','i','o','n','=',0};
5852     static const WCHAR Culture[] = {'C','u','l','t','u','r','e','=',0};
5853     static const WCHAR PublicKeyToken[] = {
5854         'P','u','b','l','i','c','K','e','y','T','o','k','e','n','=',0};
5855     static const WCHAR query[] = {
5856         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5857         '`','M','s','i','A','s','s','e','m','b','l','y','N','a','m','e','`',' ',
5858         'W','H','E','R','E',' ','`','C','o','m','p','o','n','e','n','t','_','`',
5859         '=','\'','%','s','\'',0};
5860
5861     disp = NULL;
5862     found = FALSE;
5863     ZeroMemory(&name, sizeof(ASSEMBLY_NAME));
5864     ZeroMemory(&asminfo, sizeof(ASSEMBLY_INFO));
5865
5866     r = MSI_OpenQuery(db, &view, query, comp->Component);
5867     if (r != ERROR_SUCCESS)
5868         return ERROR_SUCCESS;
5869
5870     MSI_IterateRecords(view, NULL, parse_assembly_name, &name);
5871     msiobj_release(&view->hdr);
5872
5873     if (!name.name)
5874     {
5875         ERR("No assembly name specified!\n");
5876         goto done;
5877     }
5878
5879     append_str(&disp, &size, name.name);
5880
5881     if (name.version)
5882     {
5883         append_str(&disp, &size, separator);
5884         append_str(&disp, &size, Version);
5885         append_str(&disp, &size, name.version);
5886     }
5887
5888     if (name.culture)
5889     {
5890         append_str(&disp, &size, separator);
5891         append_str(&disp, &size, Culture);
5892         append_str(&disp, &size, name.culture);
5893     }
5894
5895     if (name.pubkeytoken)
5896     {
5897         append_str(&disp, &size, separator);
5898         append_str(&disp, &size, PublicKeyToken);
5899         append_str(&disp, &size, name.pubkeytoken);
5900     }
5901
5902     asminfo.cbAssemblyInfo = sizeof(ASSEMBLY_INFO);
5903     IAssemblyCache_QueryAssemblyInfo(cache, QUERYASMINFO_FLAG_VALIDATE,
5904                                      disp, &asminfo);
5905     found = (asminfo.dwAssemblyFlags == ASSEMBLYINFO_FLAG_INSTALLED);
5906
5907 done:
5908     msiobj_release(&view->hdr);
5909     msi_free(disp);
5910     msi_free(name.name);
5911     msi_free(name.version);
5912     msi_free(name.culture);
5913     msi_free(name.pubkeytoken);
5914
5915     return found;
5916 }
5917
5918 static UINT load_assembly(MSIRECORD *rec, LPVOID param)
5919 {
5920     ASSEMBLY_LIST *list = (ASSEMBLY_LIST *)param;
5921     MSIASSEMBLY *assembly;
5922
5923     assembly = msi_alloc_zero(sizeof(MSIASSEMBLY));
5924     if (!assembly)
5925         return ERROR_OUTOFMEMORY;
5926
5927     assembly->component = get_loaded_component(list->package, MSI_RecordGetString(rec, 1));
5928
5929     if (!assembly->component || !assembly->component->Enabled ||
5930         !(assembly->component->Action & (INSTALLSTATE_LOCAL | INSTALLSTATE_SOURCE)))
5931     {
5932         TRACE("Component not set for install, not publishing assembly\n");
5933         msi_free(assembly);
5934         return ERROR_SUCCESS;
5935     }
5936
5937     assembly->feature = find_feature_by_name(list->package, MSI_RecordGetString(rec, 2));
5938     assembly->file = msi_find_file(list->package, assembly->component->KeyPath);
5939
5940     if (!assembly->file)
5941     {
5942         ERR("File %s not found\n", debugstr_w(assembly->component->KeyPath));
5943         return ERROR_FUNCTION_FAILED;
5944     }
5945
5946     assembly->manifest = strdupW(MSI_RecordGetString(rec, 3));
5947     assembly->application = strdupW(MSI_RecordGetString(rec, 4));
5948     assembly->attributes = MSI_RecordGetInteger(rec, 5);
5949     assembly->installed = check_assembly_installed(list->package->db,
5950                                                    list->cache,
5951                                                    assembly->component);
5952
5953     list_add_head(list->assemblies, &assembly->entry);
5954     return ERROR_SUCCESS;
5955 }
5956
5957 static UINT load_assemblies(MSIPACKAGE *package, struct list *assemblies)
5958 {
5959     IAssemblyCache *cache = NULL;
5960     ASSEMBLY_LIST list;
5961     MSIQUERY *view;
5962     HRESULT hr;
5963     UINT r;
5964
5965     static const WCHAR query[] =
5966         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5967          '`','M','s','i','A','s','s','e','m','b','l','y','`',0};
5968
5969     r = MSI_DatabaseOpenViewW(package->db, query, &view);
5970     if (r != ERROR_SUCCESS)
5971         return ERROR_SUCCESS;
5972
5973     hr = pCreateAssemblyCache(&cache, 0);
5974     if (FAILED(hr))
5975         return ERROR_FUNCTION_FAILED;
5976
5977     list.package = package;
5978     list.cache = cache;
5979     list.assemblies = assemblies;
5980
5981     r = MSI_IterateRecords(view, NULL, load_assembly, &list);
5982     msiobj_release(&view->hdr);
5983
5984     IAssemblyCache_Release(cache);
5985
5986     return r;
5987 }
5988
5989 static void free_assemblies(struct list *assemblies)
5990 {
5991     struct list *item, *cursor;
5992
5993     LIST_FOR_EACH_SAFE(item, cursor, assemblies)
5994     {
5995         MSIASSEMBLY *assembly = LIST_ENTRY(item, MSIASSEMBLY, entry);
5996
5997         list_remove(&assembly->entry);
5998         msi_free(assembly->application);
5999         msi_free(assembly->manifest);
6000         msi_free(assembly);
6001     }
6002 }
6003
6004 static BOOL find_assembly(struct list *assemblies, LPCWSTR file, MSIASSEMBLY **out)
6005 {
6006     MSIASSEMBLY *assembly;
6007
6008     LIST_FOR_EACH_ENTRY(assembly, assemblies, MSIASSEMBLY, entry)
6009     {
6010         if (!lstrcmpW(assembly->file->File, file))
6011         {
6012             *out = assembly;
6013             return TRUE;
6014         }
6015     }
6016
6017     return FALSE;
6018 }
6019
6020 static BOOL installassembly_cb(MSIPACKAGE *package, LPCWSTR file, DWORD action,
6021                                LPWSTR *path, DWORD *attrs, PVOID user)
6022 {
6023     MSIASSEMBLY *assembly;
6024     WCHAR temppath[MAX_PATH];
6025     struct list *assemblies = (struct list *)user;
6026     UINT r;
6027
6028     if (!find_assembly(assemblies, file, &assembly))
6029         return FALSE;
6030
6031     GetTempPathW(MAX_PATH, temppath);
6032     PathAddBackslashW(temppath);
6033     lstrcatW(temppath, assembly->file->FileName);
6034
6035     if (action == MSICABEXTRACT_BEGINEXTRACT)
6036     {
6037         if (assembly->installed)
6038             return FALSE;
6039
6040         *path = strdupW(temppath);
6041         *attrs = assembly->file->Attributes;
6042     }
6043     else if (action == MSICABEXTRACT_FILEEXTRACTED)
6044     {
6045         assembly->installed = TRUE;
6046
6047         r = install_assembly(package, assembly, temppath);
6048         if (r != ERROR_SUCCESS)
6049             ERR("Failed to install assembly\n");
6050     }
6051
6052     return TRUE;
6053 }
6054
6055 static UINT ACTION_MsiPublishAssemblies( MSIPACKAGE *package )
6056 {
6057     UINT r;
6058     struct list assemblies = LIST_INIT(assemblies);
6059     MSIASSEMBLY *assembly;
6060     MSIMEDIAINFO *mi;
6061
6062     if (!init_functionpointers() || !pCreateAssemblyCache)
6063         return ERROR_FUNCTION_FAILED;
6064
6065     r = load_assemblies(package, &assemblies);
6066     if (r != ERROR_SUCCESS)
6067         goto done;
6068
6069     if (list_empty(&assemblies))
6070         goto done;
6071
6072     mi = msi_alloc_zero(sizeof(MSIMEDIAINFO));
6073     if (!mi)
6074     {
6075         r = ERROR_OUTOFMEMORY;
6076         goto done;
6077     }
6078
6079     LIST_FOR_EACH_ENTRY(assembly, &assemblies, MSIASSEMBLY, entry)
6080     {
6081         if (assembly->installed && !mi->is_continuous)
6082             continue;
6083
6084         if (assembly->file->Sequence > mi->last_sequence || mi->is_continuous ||
6085             (assembly->file->IsCompressed && !mi->is_extracted))
6086         {
6087             MSICABDATA data;
6088
6089             r = ready_media(package, assembly->file, mi);
6090             if (r != ERROR_SUCCESS)
6091             {
6092                 ERR("Failed to ready media\n");
6093                 break;
6094             }
6095
6096             data.mi = mi;
6097             data.package = package;
6098             data.cb = installassembly_cb;
6099             data.user = &assemblies;
6100
6101             if (assembly->file->IsCompressed &&
6102                 !msi_cabextract(package, mi, &data))
6103             {
6104                 ERR("Failed to extract cabinet: %s\n", debugstr_w(mi->cabinet));
6105                 r = ERROR_FUNCTION_FAILED;
6106                 break;
6107             }
6108         }
6109
6110         if (!assembly->file->IsCompressed)
6111         {
6112             LPWSTR source = resolve_file_source(package, assembly->file);
6113
6114             r = install_assembly(package, assembly, source);
6115             if (r != ERROR_SUCCESS)
6116                 ERR("Failed to install assembly\n");
6117
6118             msi_free(source);
6119         }
6120
6121         /* FIXME: write Installer assembly reg values */
6122     }
6123
6124 done:
6125     free_assemblies(&assemblies);
6126     return r;
6127 }
6128
6129 static UINT msi_unimplemented_action_stub( MSIPACKAGE *package,
6130                                            LPCSTR action, LPCWSTR table )
6131 {
6132     static const WCHAR query[] = {
6133         'S','E','L','E','C','T',' ','*',' ',
6134         'F','R','O','M',' ','`','%','s','`',0 };
6135     MSIQUERY *view = NULL;
6136     DWORD count = 0;
6137     UINT r;
6138     
6139     r = MSI_OpenQuery( package->db, &view, query, table );
6140     if (r == ERROR_SUCCESS)
6141     {
6142         r = MSI_IterateRecords(view, &count, NULL, package);
6143         msiobj_release(&view->hdr);
6144     }
6145
6146     if (count)
6147         FIXME("%s -> %u ignored %s table values\n",
6148               action, count, debugstr_w(table));
6149
6150     return ERROR_SUCCESS;
6151 }
6152
6153 static UINT ACTION_AllocateRegistrySpace( MSIPACKAGE *package )
6154 {
6155     TRACE("%p\n", package);
6156     return ERROR_SUCCESS;
6157 }
6158
6159 static UINT ACTION_RemoveIniValues( MSIPACKAGE *package )
6160 {
6161     static const WCHAR table[] =
6162          {'R','e','m','o','v','e','I','n','i','F','i','l','e',0 };
6163     return msi_unimplemented_action_stub( package, "RemoveIniValues", table );
6164 }
6165
6166 static UINT ACTION_PatchFiles( MSIPACKAGE *package )
6167 {
6168     static const WCHAR table[] = { 'P','a','t','c','h',0 };
6169     return msi_unimplemented_action_stub( package, "PatchFiles", table );
6170 }
6171
6172 static UINT ACTION_BindImage( MSIPACKAGE *package )
6173 {
6174     static const WCHAR table[] = { 'B','i','n','d','I','m','a','g','e',0 };
6175     return msi_unimplemented_action_stub( package, "BindImage", table );
6176 }
6177
6178 static UINT ACTION_IsolateComponents( MSIPACKAGE *package )
6179 {
6180     static const WCHAR table[] = {
6181         'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t',0 };
6182     return msi_unimplemented_action_stub( package, "IsolateComponents", table );
6183 }
6184
6185 static UINT ACTION_MigrateFeatureStates( MSIPACKAGE *package )
6186 {
6187     static const WCHAR table[] = { 'U','p','g','r','a','d','e',0 };
6188     return msi_unimplemented_action_stub( package, "MigrateFeatureStates", table );
6189 }
6190
6191 static UINT ACTION_SelfUnregModules( MSIPACKAGE *package )
6192 {
6193     static const WCHAR table[] = { 'S','e','l','f','R','e','g',0 };
6194     return msi_unimplemented_action_stub( package, "SelfUnregModules", table );
6195 }
6196
6197 static UINT ACTION_DeleteServices( MSIPACKAGE *package )
6198 {
6199     static const WCHAR table[] = {
6200         'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
6201     return msi_unimplemented_action_stub( package, "DeleteServices", table );
6202 }
6203 static UINT ACTION_ValidateProductID( MSIPACKAGE *package )
6204 {
6205         static const WCHAR table[] = {
6206                 'P','r','o','d','u','c','t','I','D',0 };
6207         return msi_unimplemented_action_stub( package, "ValidateProductID", table );
6208 }
6209
6210 static UINT ACTION_RemoveEnvironmentStrings( MSIPACKAGE *package )
6211 {
6212     static const WCHAR table[] = {
6213         'E','n','v','i','r','o','n','m','e','n','t',0 };
6214     return msi_unimplemented_action_stub( package, "RemoveEnvironmentStrings", table );
6215 }
6216
6217 static UINT ACTION_MsiUnpublishAssemblies( MSIPACKAGE *package )
6218 {
6219     static const WCHAR table[] = {
6220         'M','s','i','A','s','s','e','m','b','l','y',0 };
6221     return msi_unimplemented_action_stub( package, "MsiUnpublishAssemblies", table );
6222 }
6223
6224 static UINT ACTION_UnregisterFonts( MSIPACKAGE *package )
6225 {
6226     static const WCHAR table[] = { 'F','o','n','t',0 };
6227     return msi_unimplemented_action_stub( package, "UnregisterFonts", table );
6228 }
6229
6230 static UINT ACTION_RMCCPSearch( MSIPACKAGE *package )
6231 {
6232     static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
6233     return msi_unimplemented_action_stub( package, "RMCCPSearch", table );
6234 }
6235
6236 static UINT ACTION_RegisterComPlus( MSIPACKAGE *package )
6237 {
6238     static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
6239     return msi_unimplemented_action_stub( package, "RegisterComPlus", table );
6240 }
6241
6242 static UINT ACTION_UnregisterComPlus( MSIPACKAGE *package )
6243 {
6244     static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
6245     return msi_unimplemented_action_stub( package, "UnregisterComPlus", table );
6246 }
6247
6248 static UINT ACTION_InstallSFPCatalogFile( MSIPACKAGE *package )
6249 {
6250     static const WCHAR table[] = { 'S','F','P','C','a','t','a','l','o','g',0 };
6251     return msi_unimplemented_action_stub( package, "InstallSFPCatalogFile", table );
6252 }
6253
6254 static UINT ACTION_RemoveDuplicateFiles( MSIPACKAGE *package )
6255 {
6256     static const WCHAR table[] = { 'D','u','p','l','i','c','a','t','e','F','i','l','e',0 };
6257     return msi_unimplemented_action_stub( package, "RemoveDuplicateFiles", table );
6258 }
6259
6260 static UINT ACTION_RemoveExistingProducts( MSIPACKAGE *package )
6261 {
6262     static const WCHAR table[] = { 'U','p','g','r','a','d','e',0 };
6263     return msi_unimplemented_action_stub( package, "RemoveExistingProducts", table );
6264 }
6265
6266 static UINT ACTION_RemoveFolders( MSIPACKAGE *package )
6267 {
6268     static const WCHAR table[] = { 'C','r','e','a','t','e','F','o','l','d','e','r',0 };
6269     return msi_unimplemented_action_stub( package, "RemoveFolders", table );
6270 }
6271
6272 static UINT ACTION_RemoveODBC( MSIPACKAGE *package )
6273 {
6274     static const WCHAR table[] = { 'O','D','B','C','D','r','i','v','e','r',0 };
6275     return msi_unimplemented_action_stub( package, "RemoveODBC", table );
6276 }
6277
6278 static UINT ACTION_RemoveRegistryValues( MSIPACKAGE *package )
6279 {
6280     static const WCHAR table[] = { 'R','e','m','o','v','e','R','e','g','i','s','t','r','y',0 };
6281     return msi_unimplemented_action_stub( package, "RemoveRegistryValues", table );
6282 }
6283
6284 static UINT ACTION_RemoveShortcuts( MSIPACKAGE *package )
6285 {
6286     static const WCHAR table[] = { 'S','h','o','r','t','c','u','t',0 };
6287     return msi_unimplemented_action_stub( package, "RemoveShortcuts", table );
6288 }
6289
6290 static UINT ACTION_UnpublishComponents( MSIPACKAGE *package )
6291 {
6292     static const WCHAR table[] = { 'P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t',0 };
6293     return msi_unimplemented_action_stub( package, "UnpublishComponents", table );
6294 }
6295
6296 static UINT ACTION_UnregisterClassInfo( MSIPACKAGE *package )
6297 {
6298     static const WCHAR table[] = { 'A','p','p','I','d',0 };
6299     return msi_unimplemented_action_stub( package, "UnregisterClassInfo", table );
6300 }
6301
6302 static UINT ACTION_UnregisterExtensionInfo( MSIPACKAGE *package )
6303 {
6304     static const WCHAR table[] = { 'E','x','t','e','n','s','i','o','n',0 };
6305     return msi_unimplemented_action_stub( package, "UnregisterExtensionInfo", table );
6306 }
6307
6308 static UINT ACTION_UnregisterMIMEInfo( MSIPACKAGE *package )
6309 {
6310     static const WCHAR table[] = { 'M','I','M','E',0 };
6311     return msi_unimplemented_action_stub( package, "UnregisterMIMEInfo", table );
6312 }
6313
6314 static UINT ACTION_UnregisterProgIdInfo( MSIPACKAGE *package )
6315 {
6316     static const WCHAR table[] = { 'P','r','o','g','I','d',0 };
6317     return msi_unimplemented_action_stub( package, "UnregisterProgIdInfo", table );
6318 }
6319
6320 static UINT ACTION_UnregisterTypeLibraries( MSIPACKAGE *package )
6321 {
6322     static const WCHAR table[] = { 'T','y','p','e','L','i','b',0 };
6323     return msi_unimplemented_action_stub( package, "UnregisterTypeLibraries", table );
6324 }
6325
6326 static const struct _actions StandardActions[] = {
6327     { szAllocateRegistrySpace, ACTION_AllocateRegistrySpace },
6328     { szAppSearch, ACTION_AppSearch },
6329     { szBindImage, ACTION_BindImage },
6330     { szCCPSearch, ACTION_CCPSearch },
6331     { szCostFinalize, ACTION_CostFinalize },
6332     { szCostInitialize, ACTION_CostInitialize },
6333     { szCreateFolders, ACTION_CreateFolders },
6334     { szCreateShortcuts, ACTION_CreateShortcuts },
6335     { szDeleteServices, ACTION_DeleteServices },
6336     { szDisableRollback, NULL },
6337     { szDuplicateFiles, ACTION_DuplicateFiles },
6338     { szExecuteAction, ACTION_ExecuteAction },
6339     { szFileCost, ACTION_FileCost },
6340     { szFindRelatedProducts, ACTION_FindRelatedProducts },
6341     { szForceReboot, ACTION_ForceReboot },
6342     { szInstallAdminPackage, NULL },
6343     { szInstallExecute, ACTION_InstallExecute },
6344     { szInstallExecuteAgain, ACTION_InstallExecute },
6345     { szInstallFiles, ACTION_InstallFiles},
6346     { szInstallFinalize, ACTION_InstallFinalize },
6347     { szInstallInitialize, ACTION_InstallInitialize },
6348     { szInstallSFPCatalogFile, ACTION_InstallSFPCatalogFile },
6349     { szInstallValidate, ACTION_InstallValidate },
6350     { szIsolateComponents, ACTION_IsolateComponents },
6351     { szLaunchConditions, ACTION_LaunchConditions },
6352     { szMigrateFeatureStates, ACTION_MigrateFeatureStates },
6353     { szMoveFiles, ACTION_MoveFiles },
6354     { szMsiPublishAssemblies, ACTION_MsiPublishAssemblies },
6355     { szMsiUnpublishAssemblies, ACTION_MsiUnpublishAssemblies },
6356     { szInstallODBC, ACTION_InstallODBC },
6357     { szInstallServices, ACTION_InstallServices },
6358     { szPatchFiles, ACTION_PatchFiles },
6359     { szProcessComponents, ACTION_ProcessComponents },
6360     { szPublishComponents, ACTION_PublishComponents },
6361     { szPublishFeatures, ACTION_PublishFeatures },
6362     { szPublishProduct, ACTION_PublishProduct },
6363     { szRegisterClassInfo, ACTION_RegisterClassInfo },
6364     { szRegisterComPlus, ACTION_RegisterComPlus},
6365     { szRegisterExtensionInfo, ACTION_RegisterExtensionInfo },
6366     { szRegisterFonts, ACTION_RegisterFonts },
6367     { szRegisterMIMEInfo, ACTION_RegisterMIMEInfo },
6368     { szRegisterProduct, ACTION_RegisterProduct },
6369     { szRegisterProgIdInfo, ACTION_RegisterProgIdInfo },
6370     { szRegisterTypeLibraries, ACTION_RegisterTypeLibraries },
6371     { szRegisterUser, ACTION_RegisterUser },
6372     { szRemoveDuplicateFiles, ACTION_RemoveDuplicateFiles },
6373     { szRemoveEnvironmentStrings, ACTION_RemoveEnvironmentStrings },
6374     { szRemoveExistingProducts, ACTION_RemoveExistingProducts },
6375     { szRemoveFiles, ACTION_RemoveFiles },
6376     { szRemoveFolders, ACTION_RemoveFolders },
6377     { szRemoveIniValues, ACTION_RemoveIniValues },
6378     { szRemoveODBC, ACTION_RemoveODBC },
6379     { szRemoveRegistryValues, ACTION_RemoveRegistryValues },
6380     { szRemoveShortcuts, ACTION_RemoveShortcuts },
6381     { szResolveSource, ACTION_ResolveSource },
6382     { szRMCCPSearch, ACTION_RMCCPSearch },
6383     { szScheduleReboot, NULL },
6384     { szSelfRegModules, ACTION_SelfRegModules },
6385     { szSelfUnregModules, ACTION_SelfUnregModules },
6386     { szSetODBCFolders, NULL },
6387     { szStartServices, ACTION_StartServices },
6388     { szStopServices, ACTION_StopServices },
6389     { szUnpublishComponents, ACTION_UnpublishComponents },
6390     { szUnpublishFeatures, ACTION_UnpublishFeatures },
6391     { szUnregisterClassInfo, ACTION_UnregisterClassInfo },
6392     { szUnregisterComPlus, ACTION_UnregisterComPlus },
6393     { szUnregisterExtensionInfo, ACTION_UnregisterExtensionInfo },
6394     { szUnregisterFonts, ACTION_UnregisterFonts },
6395     { szUnregisterMIMEInfo, ACTION_UnregisterMIMEInfo },
6396     { szUnregisterProgIdInfo, ACTION_UnregisterProgIdInfo },
6397     { szUnregisterTypeLibraries, ACTION_UnregisterTypeLibraries },
6398     { szValidateProductID, ACTION_ValidateProductID },
6399     { szWriteEnvironmentStrings, ACTION_WriteEnvironmentStrings },
6400     { szWriteIniValues, ACTION_WriteIniValues },
6401     { szWriteRegistryValues, ACTION_WriteRegistryValues },
6402     { NULL, NULL },
6403 };
6404
6405 static BOOL ACTION_HandleStandardAction(MSIPACKAGE *package, LPCWSTR action,
6406                                         UINT* rc, BOOL force )
6407 {
6408     BOOL ret = FALSE;
6409     BOOL run = force;
6410     int i;
6411
6412     if (!run && !package->script->CurrentlyScripting)
6413         run = TRUE;
6414
6415     if (!run)
6416     {
6417         if (strcmpW(action,szInstallFinalize) == 0 ||
6418             strcmpW(action,szInstallExecute) == 0 ||
6419             strcmpW(action,szInstallExecuteAgain) == 0)
6420                 run = TRUE;
6421     }
6422
6423     i = 0;
6424     while (StandardActions[i].action != NULL)
6425     {
6426         if (strcmpW(StandardActions[i].action, action)==0)
6427         {
6428             if (!run)
6429             {
6430                 ui_actioninfo(package, action, TRUE, 0);
6431                 *rc = schedule_action(package,INSTALL_SCRIPT,action);
6432                 ui_actioninfo(package, action, FALSE, *rc);
6433             }
6434             else
6435             {
6436                 ui_actionstart(package, action);
6437                 if (StandardActions[i].handler)
6438                 {
6439                     *rc = StandardActions[i].handler(package);
6440                 }
6441                 else
6442                 {
6443                     FIXME("unhandled standard action %s\n",debugstr_w(action));
6444                     *rc = ERROR_SUCCESS;
6445                 }
6446             }
6447             ret = TRUE;
6448             break;
6449         }
6450         i++;
6451     }
6452     return ret;
6453 }