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