wined3d: Rework surface Blt a bit to only lock the part of the destination surface...
[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 & msidbSumInfoSourceTypeCompressed;
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     ITypeLib *tlib;
2998     HMODULE module;
2999     HRESULT hr;
3000
3001     static const WCHAR szTYPELIB[] = {'T','Y','P','E','L','I','B',0};
3002
3003     component = MSI_RecordGetString(row,3);
3004     comp = get_loaded_component(package,component);
3005     if (!comp)
3006         return ERROR_SUCCESS;
3007
3008     if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
3009     {
3010         TRACE("Skipping typelib reg due to disabled component\n");
3011
3012         comp->Action = comp->Installed;
3013
3014         return ERROR_SUCCESS;
3015     }
3016
3017     comp->Action = INSTALLSTATE_LOCAL;
3018
3019     file = get_loaded_file( package, comp->KeyPath ); 
3020     if (!file)
3021         return ERROR_SUCCESS;
3022
3023     module = LoadLibraryExW( file->TargetPath, NULL, LOAD_LIBRARY_AS_DATAFILE );
3024     if (module)
3025     {
3026         LPCWSTR guid;
3027         guid = MSI_RecordGetString(row,1);
3028         CLSIDFromString((LPWSTR)guid, &tl_struct.clsid);
3029         tl_struct.source = strdupW( file->TargetPath );
3030         tl_struct.path = NULL;
3031
3032         EnumResourceNamesW(module, szTYPELIB, Typelib_EnumResNameProc,
3033                         (LONG_PTR)&tl_struct);
3034
3035         if (tl_struct.path)
3036         {
3037             LPWSTR help = NULL;
3038             LPCWSTR helpid;
3039             HRESULT res;
3040
3041             helpid = MSI_RecordGetString(row,6);
3042
3043             if (helpid)
3044                 help = resolve_folder(package,helpid,FALSE,FALSE,TRUE,NULL);
3045             res = RegisterTypeLib(tl_struct.ptLib,tl_struct.path,help);
3046             msi_free(help);
3047
3048             if (!SUCCEEDED(res))
3049                 ERR("Failed to register type library %s\n",
3050                         debugstr_w(tl_struct.path));
3051             else
3052             {
3053                 ui_actiondata(package,szRegisterTypeLibraries,row);
3054
3055                 TRACE("Registered %s\n", debugstr_w(tl_struct.path));
3056             }
3057
3058             ITypeLib_Release(tl_struct.ptLib);
3059             msi_free(tl_struct.path);
3060         }
3061         else
3062             ERR("Failed to load type library %s\n",
3063                     debugstr_w(tl_struct.source));
3064
3065         FreeLibrary(module);
3066         msi_free(tl_struct.source);
3067     }
3068     else
3069     {
3070         hr = LoadTypeLibEx(file->TargetPath, REGKIND_REGISTER, &tlib);
3071         if (FAILED(hr))
3072         {
3073             ERR("Failed to load type library: %08x\n", hr);
3074             return ERROR_FUNCTION_FAILED;
3075         }
3076
3077         ITypeLib_Release(tlib);
3078     }
3079
3080     return ERROR_SUCCESS;
3081 }
3082
3083 static UINT ACTION_RegisterTypeLibraries(MSIPACKAGE *package)
3084 {
3085     /* 
3086      * OK this is a bit confusing.. I am given a _Component key and I believe
3087      * that the file that is being registered as a type library is the "key file
3088      * of that component" which I interpret to mean "The file in the KeyPath of
3089      * that component".
3090      */
3091     UINT rc;
3092     MSIQUERY * view;
3093     static const WCHAR Query[] =
3094         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3095          '`','T','y','p','e','L','i','b','`',0};
3096
3097     rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3098     if (rc != ERROR_SUCCESS)
3099         return ERROR_SUCCESS;
3100
3101     rc = MSI_IterateRecords(view, NULL, ITERATE_RegisterTypeLibraries, package);
3102     msiobj_release(&view->hdr);
3103     return rc;
3104 }
3105
3106 static UINT ITERATE_CreateShortcuts(MSIRECORD *row, LPVOID param)
3107 {
3108     MSIPACKAGE *package = (MSIPACKAGE*)param;
3109     LPWSTR target_file, target_folder, filename;
3110     LPCWSTR buffer, extension;
3111     MSICOMPONENT *comp;
3112     static const WCHAR szlnk[]={'.','l','n','k',0};
3113     IShellLinkW *sl = NULL;
3114     IPersistFile *pf = NULL;
3115     HRESULT res;
3116
3117     buffer = MSI_RecordGetString(row,4);
3118     comp = get_loaded_component(package,buffer);
3119     if (!comp)
3120         return ERROR_SUCCESS;
3121
3122     if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL ))
3123     {
3124         TRACE("Skipping shortcut creation due to disabled component\n");
3125
3126         comp->Action = comp->Installed;
3127
3128         return ERROR_SUCCESS;
3129     }
3130
3131     comp->Action = INSTALLSTATE_LOCAL;
3132
3133     ui_actiondata(package,szCreateShortcuts,row);
3134
3135     res = CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
3136                     &IID_IShellLinkW, (LPVOID *) &sl );
3137
3138     if (FAILED( res ))
3139     {
3140         ERR("CLSID_ShellLink not available\n");
3141         goto err;
3142     }
3143
3144     res = IShellLinkW_QueryInterface( sl, &IID_IPersistFile,(LPVOID*) &pf );
3145     if (FAILED( res ))
3146     {
3147         ERR("QueryInterface(IID_IPersistFile) failed\n");
3148         goto err;
3149     }
3150
3151     buffer = MSI_RecordGetString(row,2);
3152     target_folder = resolve_folder(package, buffer,FALSE,FALSE,TRUE,NULL);
3153
3154     /* may be needed because of a bug somewhere else */
3155     create_full_pathW(target_folder);
3156
3157     filename = msi_dup_record_field( row, 3 );
3158     reduce_to_longfilename(filename);
3159
3160     extension = strchrW(filename,'.');
3161     if (!extension || strcmpiW(extension,szlnk))
3162     {
3163         int len = strlenW(filename);
3164         filename = msi_realloc(filename, len * sizeof(WCHAR) + sizeof(szlnk));
3165         memcpy(filename + len, szlnk, sizeof(szlnk));
3166     }
3167     target_file = build_directory_name(2, target_folder, filename);
3168     msi_free(target_folder);
3169     msi_free(filename);
3170
3171     buffer = MSI_RecordGetString(row,5);
3172     if (strchrW(buffer,'['))
3173     {
3174         LPWSTR deformated;
3175         deformat_string(package,buffer,&deformated);
3176         IShellLinkW_SetPath(sl,deformated);
3177         msi_free(deformated);
3178     }
3179     else
3180     {
3181         FIXME("poorly handled shortcut format, advertised shortcut\n");
3182         IShellLinkW_SetPath(sl,comp->FullKeypath);
3183     }
3184
3185     if (!MSI_RecordIsNull(row,6))
3186     {
3187         LPWSTR deformated;
3188         buffer = MSI_RecordGetString(row,6);
3189         deformat_string(package,buffer,&deformated);
3190         IShellLinkW_SetArguments(sl,deformated);
3191         msi_free(deformated);
3192     }
3193
3194     if (!MSI_RecordIsNull(row,7))
3195     {
3196         buffer = MSI_RecordGetString(row,7);
3197         IShellLinkW_SetDescription(sl,buffer);
3198     }
3199
3200     if (!MSI_RecordIsNull(row,8))
3201         IShellLinkW_SetHotkey(sl,MSI_RecordGetInteger(row,8));
3202
3203     if (!MSI_RecordIsNull(row,9))
3204     {
3205         LPWSTR Path;
3206         INT index; 
3207
3208         buffer = MSI_RecordGetString(row,9);
3209
3210         Path = build_icon_path(package,buffer);
3211         index = MSI_RecordGetInteger(row,10);
3212
3213         /* no value means 0 */
3214         if (index == MSI_NULL_INTEGER)
3215             index = 0;
3216
3217         IShellLinkW_SetIconLocation(sl,Path,index);
3218         msi_free(Path);
3219     }
3220
3221     if (!MSI_RecordIsNull(row,11))
3222         IShellLinkW_SetShowCmd(sl,MSI_RecordGetInteger(row,11));
3223
3224     if (!MSI_RecordIsNull(row,12))
3225     {
3226         LPWSTR Path;
3227         buffer = MSI_RecordGetString(row,12);
3228         Path = resolve_folder(package, buffer, FALSE, FALSE, TRUE, NULL);
3229         if (Path)
3230             IShellLinkW_SetWorkingDirectory(sl,Path);
3231         msi_free(Path);
3232     }
3233
3234     TRACE("Writing shortcut to %s\n",debugstr_w(target_file));
3235     IPersistFile_Save(pf,target_file,FALSE);
3236
3237     msi_free(target_file);    
3238
3239 err:
3240     if (pf)
3241         IPersistFile_Release( pf );
3242     if (sl)
3243         IShellLinkW_Release( sl );
3244
3245     return ERROR_SUCCESS;
3246 }
3247
3248 static UINT ACTION_CreateShortcuts(MSIPACKAGE *package)
3249 {
3250     UINT rc;
3251     HRESULT res;
3252     MSIQUERY * view;
3253     static const WCHAR Query[] =
3254         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3255          '`','S','h','o','r','t','c','u','t','`',0};
3256
3257     rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3258     if (rc != ERROR_SUCCESS)
3259         return ERROR_SUCCESS;
3260
3261     res = CoInitialize( NULL );
3262     if (FAILED (res))
3263     {
3264         ERR("CoInitialize failed\n");
3265         return ERROR_FUNCTION_FAILED;
3266     }
3267
3268     rc = MSI_IterateRecords(view, NULL, ITERATE_CreateShortcuts, package);
3269     msiobj_release(&view->hdr);
3270
3271     CoUninitialize();
3272
3273     return rc;
3274 }
3275
3276 static UINT ITERATE_PublishIcon(MSIRECORD *row, LPVOID param)
3277 {
3278     MSIPACKAGE* package = (MSIPACKAGE*)param;
3279     HANDLE the_file;
3280     LPWSTR FilePath;
3281     LPCWSTR FileName;
3282     CHAR buffer[1024];
3283     DWORD sz;
3284     UINT rc;
3285     MSIRECORD *uirow;
3286
3287     FileName = MSI_RecordGetString(row,1);
3288     if (!FileName)
3289     {
3290         ERR("Unable to get FileName\n");
3291         return ERROR_SUCCESS;
3292     }
3293
3294     FilePath = build_icon_path(package,FileName);
3295
3296     TRACE("Creating icon file at %s\n",debugstr_w(FilePath));
3297
3298     the_file = CreateFileW(FilePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
3299                         FILE_ATTRIBUTE_NORMAL, NULL);
3300
3301     if (the_file == INVALID_HANDLE_VALUE)
3302     {
3303         ERR("Unable to create file %s\n",debugstr_w(FilePath));
3304         msi_free(FilePath);
3305         return ERROR_SUCCESS;
3306     }
3307
3308     do 
3309     {
3310         DWORD write;
3311         sz = 1024;
3312         rc = MSI_RecordReadStream(row,2,buffer,&sz);
3313         if (rc != ERROR_SUCCESS)
3314         {
3315             ERR("Failed to get stream\n");
3316             CloseHandle(the_file);  
3317             DeleteFileW(FilePath);
3318             break;
3319         }
3320         WriteFile(the_file,buffer,sz,&write,NULL);
3321     } while (sz == 1024);
3322
3323     msi_free(FilePath);
3324
3325     CloseHandle(the_file);
3326
3327     uirow = MSI_CreateRecord(1);
3328     MSI_RecordSetStringW(uirow,1,FileName);
3329     ui_actiondata(package,szPublishProduct,uirow);
3330     msiobj_release( &uirow->hdr );
3331
3332     return ERROR_SUCCESS;
3333 }
3334
3335 static UINT msi_publish_icons(MSIPACKAGE *package)
3336 {
3337     UINT r;
3338     MSIQUERY *view;
3339
3340     static const WCHAR query[]= {
3341         'S','E','L','E','C','T',' ','*',' ',
3342         'F','R','O','M',' ','`','I','c','o','n','`',0};
3343
3344     r = MSI_DatabaseOpenViewW(package->db, query, &view);
3345     if (r == ERROR_SUCCESS)
3346     {
3347         MSI_IterateRecords(view, NULL, ITERATE_PublishIcon, package);
3348         msiobj_release(&view->hdr);
3349     }
3350
3351     return ERROR_SUCCESS;
3352 }
3353
3354 static UINT msi_publish_sourcelist(MSIPACKAGE *package, HKEY hkey)
3355 {
3356     UINT r;
3357     HKEY source;
3358     LPWSTR buffer;
3359     MSIMEDIADISK *disk;
3360     MSISOURCELISTINFO *info;
3361
3362     static const WCHAR szEmpty[] = {0};
3363     static const WCHAR szSourceList[] = {'S','o','u','r','c','e','L','i','s','t',0};
3364
3365     r = RegCreateKeyW(hkey, szSourceList, &source);
3366     if (r != ERROR_SUCCESS)
3367         return r;
3368
3369     RegCloseKey(source);
3370
3371     buffer = strrchrW(package->PackagePath, '\\') + 1;
3372     r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3373                               package->Context, MSICODE_PRODUCT,
3374                               INSTALLPROPERTY_PACKAGENAMEW, buffer);
3375     if (r != ERROR_SUCCESS)
3376         return r;
3377
3378     r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3379                               package->Context, MSICODE_PRODUCT,
3380                               INSTALLPROPERTY_MEDIAPACKAGEPATHW, szEmpty);
3381     if (r != ERROR_SUCCESS)
3382         return r;
3383
3384     r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3385                               package->Context, MSICODE_PRODUCT,
3386                               INSTALLPROPERTY_DISKPROMPTW, szEmpty);
3387     if (r != ERROR_SUCCESS)
3388         return r;
3389
3390     LIST_FOR_EACH_ENTRY(info, &package->sourcelist_info, MSISOURCELISTINFO, entry)
3391     {
3392         if (!lstrcmpW(info->property, INSTALLPROPERTY_LASTUSEDSOURCEW))
3393             msi_set_last_used_source(package->ProductCode, NULL, info->context,
3394                                      info->options, info->value);
3395         else
3396             MsiSourceListSetInfoW(package->ProductCode, NULL,
3397                                   info->context, info->options,
3398                                   info->property, info->value);
3399     }
3400
3401     LIST_FOR_EACH_ENTRY(disk, &package->sourcelist_media, MSIMEDIADISK, entry)
3402     {
3403         MsiSourceListAddMediaDiskW(package->ProductCode, NULL,
3404                                    disk->context, disk->options,
3405                                    disk->disk_id, disk->volume_label, disk->disk_prompt);
3406     }
3407
3408     return ERROR_SUCCESS;
3409 }
3410
3411 static UINT msi_publish_product_properties(MSIPACKAGE *package, HKEY hkey)
3412 {
3413     MSIHANDLE hdb, suminfo;
3414     WCHAR guids[MAX_PATH];
3415     WCHAR packcode[SQUISH_GUID_SIZE];
3416     LPWSTR buffer;
3417     LPWSTR ptr;
3418     DWORD langid;
3419     DWORD size;
3420     UINT r;
3421
3422     static const WCHAR szProductLanguage[] =
3423         {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
3424     static const WCHAR szARPProductIcon[] =
3425         {'A','R','P','P','R','O','D','U','C','T','I','C','O','N',0};
3426     static const WCHAR szProductVersion[] =
3427         {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
3428     static const WCHAR szAssignment[] =
3429         {'A','s','s','i','g','n','m','e','n','t',0};
3430     static const WCHAR szAdvertiseFlags[] =
3431         {'A','d','v','e','r','t','i','s','e','F','l','a','g','s',0};
3432     static const WCHAR szClients[] =
3433         {'C','l','i','e','n','t','s',0};
3434     static const WCHAR szColon[] = {':',0};
3435
3436     buffer = msi_dup_property(package, INSTALLPROPERTY_PRODUCTNAMEW);
3437     msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTNAMEW, buffer);
3438     msi_free(buffer);
3439
3440     langid = msi_get_property_int(package, szProductLanguage, 0);
3441     msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
3442
3443     ptr = strrchrW(package->PackagePath, '\\' ) + 1;
3444     msi_reg_set_val_str(hkey, INSTALLPROPERTY_PACKAGENAMEW, ptr);
3445
3446     /* FIXME */
3447     msi_reg_set_val_dword(hkey, INSTALLPROPERTY_AUTHORIZED_LUA_APPW, 0);
3448
3449     buffer = msi_dup_property(package, szARPProductIcon);
3450     if (buffer)
3451     {
3452         LPWSTR path = build_icon_path(package,buffer);
3453         msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTICONW, path);
3454         msi_free(path);
3455         msi_free(buffer);
3456     }
3457
3458     buffer = msi_dup_property(package, szProductVersion);
3459     if (buffer)
3460     {
3461         DWORD verdword = msi_version_str_to_dword(buffer);
3462         msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
3463         msi_free(buffer);
3464     }
3465
3466     msi_reg_set_val_dword(hkey, szAssignment, 0);
3467     msi_reg_set_val_dword(hkey, szAdvertiseFlags, 0x184);
3468     msi_reg_set_val_dword(hkey, INSTALLPROPERTY_INSTANCETYPEW, 0);
3469     msi_reg_set_val_str(hkey, szClients, szColon);
3470
3471     hdb = alloc_msihandle(&package->db->hdr);
3472     if (!hdb)
3473         return ERROR_NOT_ENOUGH_MEMORY;
3474
3475     r = MsiGetSummaryInformationW(hdb, NULL, 0, &suminfo);
3476     MsiCloseHandle(hdb);
3477     if (r != ERROR_SUCCESS)
3478         goto done;
3479
3480     size = MAX_PATH;
3481     r = MsiSummaryInfoGetPropertyW(suminfo, PID_REVNUMBER, NULL, NULL,
3482                                    NULL, guids, &size);
3483     if (r != ERROR_SUCCESS)
3484         goto done;
3485
3486     ptr = strchrW(guids, ';');
3487     if (ptr) *ptr = 0;
3488     squash_guid(guids, packcode);
3489     msi_reg_set_val_str(hkey, INSTALLPROPERTY_PACKAGECODEW, packcode);
3490
3491 done:
3492     MsiCloseHandle(suminfo);
3493     return ERROR_SUCCESS;
3494 }
3495
3496 static UINT msi_publish_upgrade_code(MSIPACKAGE *package)
3497 {
3498     UINT r;
3499     HKEY hkey;
3500     LPWSTR upgrade;
3501     WCHAR squashed_pc[SQUISH_GUID_SIZE];
3502
3503     static const WCHAR szUpgradeCode[] =
3504         {'U','p','g','r','a','d','e','C','o','d','e',0};
3505
3506     upgrade = msi_dup_property(package, szUpgradeCode);
3507     if (!upgrade)
3508         return ERROR_SUCCESS;
3509
3510     if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3511     {
3512         r = MSIREG_OpenClassesUpgradeCodesKey(upgrade, &hkey, TRUE);
3513         if (r != ERROR_SUCCESS)
3514             goto done;
3515     }
3516     else
3517     {
3518         r = MSIREG_OpenUserUpgradeCodesKey(upgrade, &hkey, TRUE);
3519         if (r != ERROR_SUCCESS)
3520             goto done;
3521     }
3522
3523     squash_guid(package->ProductCode, squashed_pc);
3524     msi_reg_set_val_str(hkey, squashed_pc, NULL);
3525
3526     RegCloseKey(hkey);
3527
3528 done:
3529     msi_free(upgrade);
3530     return r;
3531 }
3532
3533 static BOOL msi_check_publish(MSIPACKAGE *package)
3534 {
3535     MSIFEATURE *feature;
3536
3537     LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3538     {
3539         if (feature->ActionRequest == INSTALLSTATE_LOCAL)
3540             return TRUE;
3541     }
3542
3543     return FALSE;
3544 }
3545
3546 static BOOL msi_check_unpublish(MSIPACKAGE *package)
3547 {
3548     MSIFEATURE *feature;
3549
3550     LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3551     {
3552         if (feature->ActionRequest != INSTALLSTATE_ABSENT)
3553             return FALSE;
3554     }
3555
3556     return TRUE;
3557 }
3558
3559 /*
3560  * 99% of the work done here is only done for 
3561  * advertised installs. However this is where the
3562  * Icon table is processed and written out
3563  * so that is what I am going to do here.
3564  */
3565 static UINT ACTION_PublishProduct(MSIPACKAGE *package)
3566 {
3567     UINT rc;
3568     HKEY hukey=0;
3569     HKEY hudkey=0;
3570
3571     /* FIXME: also need to publish if the product is in advertise mode */
3572     if (!msi_check_publish(package))
3573         return ERROR_SUCCESS;
3574
3575     if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3576     {
3577         rc = MSIREG_OpenLocalClassesProductKey(package->ProductCode, &hukey, TRUE);
3578         if (rc != ERROR_SUCCESS)
3579             goto end;
3580
3581         rc = MSIREG_OpenLocalUserDataProductKey(package->ProductCode, &hudkey, TRUE);
3582         if (rc != ERROR_SUCCESS)
3583             goto end;
3584     }
3585     else
3586     {
3587         rc = MSIREG_OpenUserProductsKey(package->ProductCode, &hukey, TRUE);
3588         if (rc != ERROR_SUCCESS)
3589             goto end;
3590
3591         rc = MSIREG_OpenUserDataProductKey(package->ProductCode, &hudkey, TRUE);
3592         if (rc != ERROR_SUCCESS)
3593             goto end;
3594     }
3595
3596     rc = msi_publish_upgrade_code(package);
3597     if (rc != ERROR_SUCCESS)
3598         goto end;
3599
3600     rc = msi_publish_product_properties(package, hukey);
3601     if (rc != ERROR_SUCCESS)
3602         goto end;
3603
3604     rc = msi_publish_sourcelist(package, hukey);
3605     if (rc != ERROR_SUCCESS)
3606         goto end;
3607
3608     rc = msi_publish_icons(package);
3609
3610 end:
3611     RegCloseKey(hukey);
3612     RegCloseKey(hudkey);
3613
3614     return rc;
3615 }
3616
3617 static UINT ITERATE_WriteIniValues(MSIRECORD *row, LPVOID param)
3618 {
3619     MSIPACKAGE *package = (MSIPACKAGE*)param;
3620     LPCWSTR component,section,key,value,identifier,filename,dirproperty;
3621     LPWSTR deformated_section, deformated_key, deformated_value;
3622     LPWSTR folder, fullname = NULL;
3623     MSIRECORD * uirow;
3624     INT action;
3625     MSICOMPONENT *comp;
3626     static const WCHAR szWindowsFolder[] =
3627           {'W','i','n','d','o','w','s','F','o','l','d','e','r',0};
3628
3629     component = MSI_RecordGetString(row, 8);
3630     comp = get_loaded_component(package,component);
3631
3632     if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
3633     {
3634         TRACE("Skipping ini file due to disabled component %s\n",
3635                         debugstr_w(component));
3636
3637         comp->Action = comp->Installed;
3638
3639         return ERROR_SUCCESS;
3640     }
3641
3642     comp->Action = INSTALLSTATE_LOCAL;
3643
3644     identifier = MSI_RecordGetString(row,1); 
3645     filename = MSI_RecordGetString(row,2);
3646     dirproperty = MSI_RecordGetString(row,3);
3647     section = MSI_RecordGetString(row,4);
3648     key = MSI_RecordGetString(row,5);
3649     value = MSI_RecordGetString(row,6);
3650     action = MSI_RecordGetInteger(row,7);
3651
3652     deformat_string(package,section,&deformated_section);
3653     deformat_string(package,key,&deformated_key);
3654     deformat_string(package,value,&deformated_value);
3655
3656     if (dirproperty)
3657     {
3658         folder = resolve_folder(package, dirproperty, FALSE, FALSE, TRUE, NULL);
3659         if (!folder)
3660             folder = msi_dup_property( package, dirproperty );
3661     }
3662     else
3663         folder = msi_dup_property( package, szWindowsFolder );
3664
3665     if (!folder)
3666     {
3667         ERR("Unable to resolve folder! (%s)\n",debugstr_w(dirproperty));
3668         goto cleanup;
3669     }
3670
3671     fullname = build_directory_name(2, folder, filename);
3672
3673     if (action == 0)
3674     {
3675         TRACE("Adding value %s to section %s in %s\n",
3676                 debugstr_w(deformated_key), debugstr_w(deformated_section),
3677                 debugstr_w(fullname));
3678         WritePrivateProfileStringW(deformated_section, deformated_key,
3679                                    deformated_value, fullname);
3680     }
3681     else if (action == 1)
3682     {
3683         WCHAR returned[10];
3684         GetPrivateProfileStringW(deformated_section, deformated_key, NULL,
3685                                  returned, 10, fullname);
3686         if (returned[0] == 0)
3687         {
3688             TRACE("Adding value %s to section %s in %s\n",
3689                     debugstr_w(deformated_key), debugstr_w(deformated_section),
3690                     debugstr_w(fullname));
3691
3692             WritePrivateProfileStringW(deformated_section, deformated_key,
3693                                        deformated_value, fullname);
3694         }
3695     }
3696     else if (action == 3)
3697         FIXME("Append to existing section not yet implemented\n");
3698
3699     uirow = MSI_CreateRecord(4);
3700     MSI_RecordSetStringW(uirow,1,identifier);
3701     MSI_RecordSetStringW(uirow,2,deformated_section);
3702     MSI_RecordSetStringW(uirow,3,deformated_key);
3703     MSI_RecordSetStringW(uirow,4,deformated_value);
3704     ui_actiondata(package,szWriteIniValues,uirow);
3705     msiobj_release( &uirow->hdr );
3706 cleanup:
3707     msi_free(fullname);
3708     msi_free(folder);
3709     msi_free(deformated_key);
3710     msi_free(deformated_value);
3711     msi_free(deformated_section);
3712     return ERROR_SUCCESS;
3713 }
3714
3715 static UINT ACTION_WriteIniValues(MSIPACKAGE *package)
3716 {
3717     UINT rc;
3718     MSIQUERY * view;
3719     static const WCHAR ExecSeqQuery[] = 
3720         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3721          '`','I','n','i','F','i','l','e','`',0};
3722
3723     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
3724     if (rc != ERROR_SUCCESS)
3725     {
3726         TRACE("no IniFile table\n");
3727         return ERROR_SUCCESS;
3728     }
3729
3730     rc = MSI_IterateRecords(view, NULL, ITERATE_WriteIniValues, package);
3731     msiobj_release(&view->hdr);
3732     return rc;
3733 }
3734
3735 static UINT ITERATE_SelfRegModules(MSIRECORD *row, LPVOID param)
3736 {
3737     MSIPACKAGE *package = (MSIPACKAGE*)param;
3738     LPCWSTR filename;
3739     LPWSTR FullName;
3740     MSIFILE *file;
3741     DWORD len;
3742     static const WCHAR ExeStr[] =
3743         {'r','e','g','s','v','r','3','2','.','e','x','e',' ','\"',0};
3744     static const WCHAR close[] =  {'\"',0};
3745     STARTUPINFOW si;
3746     PROCESS_INFORMATION info;
3747     BOOL brc;
3748     MSIRECORD *uirow;
3749     LPWSTR uipath, p;
3750
3751     memset(&si,0,sizeof(STARTUPINFOW));
3752
3753     filename = MSI_RecordGetString(row,1);
3754     file = get_loaded_file( package, filename );
3755
3756     if (!file)
3757     {
3758         ERR("Unable to find file id %s\n",debugstr_w(filename));
3759         return ERROR_SUCCESS;
3760     }
3761
3762     len = strlenW(ExeStr) + strlenW( file->TargetPath ) + 2;
3763
3764     FullName = msi_alloc(len*sizeof(WCHAR));
3765     strcpyW(FullName,ExeStr);
3766     strcatW( FullName, file->TargetPath );
3767     strcatW(FullName,close);
3768
3769     TRACE("Registering %s\n",debugstr_w(FullName));
3770     brc = CreateProcessW(NULL, FullName, NULL, NULL, FALSE, 0, NULL, c_colon,
3771                     &si, &info);
3772
3773     if (brc)
3774         msi_dialog_check_messages(info.hProcess);
3775
3776     msi_free(FullName);
3777
3778     /* the UI chunk */
3779     uirow = MSI_CreateRecord( 2 );
3780     uipath = strdupW( file->TargetPath );
3781     p = strrchrW(uipath,'\\');
3782     if (p)
3783         p[0]=0;
3784     MSI_RecordSetStringW( uirow, 1, &p[1] );
3785     MSI_RecordSetStringW( uirow, 2, uipath);
3786     ui_actiondata( package, szSelfRegModules, uirow);
3787     msiobj_release( &uirow->hdr );
3788     msi_free( uipath );
3789     /* FIXME: call ui_progress? */
3790
3791     return ERROR_SUCCESS;
3792 }
3793
3794 static UINT ACTION_SelfRegModules(MSIPACKAGE *package)
3795 {
3796     UINT rc;
3797     MSIQUERY * view;
3798     static const WCHAR ExecSeqQuery[] = 
3799         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3800          '`','S','e','l','f','R','e','g','`',0};
3801
3802     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
3803     if (rc != ERROR_SUCCESS)
3804     {
3805         TRACE("no SelfReg table\n");
3806         return ERROR_SUCCESS;
3807     }
3808
3809     MSI_IterateRecords(view, NULL, ITERATE_SelfRegModules, package);
3810     msiobj_release(&view->hdr);
3811
3812     return ERROR_SUCCESS;
3813 }
3814
3815 static UINT ACTION_PublishFeatures(MSIPACKAGE *package)
3816 {
3817     MSIFEATURE *feature;
3818     UINT rc;
3819     HKEY hkey;
3820     HKEY userdata;
3821
3822     if (!msi_check_publish(package))
3823         return ERROR_SUCCESS;
3824
3825     if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3826     {
3827         rc = MSIREG_OpenLocalClassesFeaturesKey(package->ProductCode,
3828                                                 &hkey, TRUE);
3829         if (rc != ERROR_SUCCESS)
3830             goto end;
3831
3832         rc = MSIREG_OpenLocalUserDataFeaturesKey(package->ProductCode,
3833                                                  &userdata, TRUE);
3834         if (rc != ERROR_SUCCESS)
3835             goto end;
3836     }
3837     else
3838     {
3839         rc = MSIREG_OpenUserFeaturesKey(package->ProductCode, &hkey, TRUE);
3840         if (rc != ERROR_SUCCESS)
3841             goto end;
3842
3843         rc = MSIREG_OpenUserDataFeaturesKey(package->ProductCode,
3844                                             &userdata, TRUE);
3845         if (rc != ERROR_SUCCESS)
3846             goto end;
3847     }
3848
3849     /* here the guids are base 85 encoded */
3850     LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3851     {
3852         ComponentList *cl;
3853         LPWSTR data = NULL;
3854         GUID clsid;
3855         INT size;
3856         BOOL absent = FALSE;
3857         MSIRECORD *uirow;
3858
3859         if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_LOCAL ) &&
3860             !ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_SOURCE ) &&
3861             !ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_ADVERTISED ))
3862             absent = TRUE;
3863
3864         size = 1;
3865         LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3866         {
3867             size += 21;
3868         }
3869         if (feature->Feature_Parent)
3870             size += strlenW( feature->Feature_Parent )+2;
3871
3872         data = msi_alloc(size * sizeof(WCHAR));
3873
3874         data[0] = 0;
3875         LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3876         {
3877             MSICOMPONENT* component = cl->component;
3878             WCHAR buf[21];
3879
3880             buf[0] = 0;
3881             if (component->ComponentId)
3882             {
3883                 TRACE("From %s\n",debugstr_w(component->ComponentId));
3884                 CLSIDFromString(component->ComponentId, &clsid);
3885                 encode_base85_guid(&clsid,buf);
3886                 TRACE("to %s\n",debugstr_w(buf));
3887                 strcatW(data,buf);
3888             }
3889         }
3890
3891         if (feature->Feature_Parent)
3892         {
3893             static const WCHAR sep[] = {'\2',0};
3894             strcatW(data,sep);
3895             strcatW(data,feature->Feature_Parent);
3896         }
3897
3898         msi_reg_set_val_str( userdata, feature->Feature, data );
3899         msi_free(data);
3900
3901         size = 0;
3902         if (feature->Feature_Parent)
3903             size = strlenW(feature->Feature_Parent)*sizeof(WCHAR);
3904         if (!absent)
3905         {
3906             static const WCHAR emptyW[] = {0};
3907             size += sizeof(WCHAR);
3908             RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
3909                            (LPBYTE)(feature->Feature_Parent ? feature->Feature_Parent : emptyW),size);
3910         }
3911         else
3912         {
3913             size += 2*sizeof(WCHAR);
3914             data = msi_alloc(size);
3915             data[0] = 0x6;
3916             data[1] = 0;
3917             if (feature->Feature_Parent)
3918                 strcpyW( &data[1], feature->Feature_Parent );
3919             RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
3920                        (LPBYTE)data,size);
3921             msi_free(data);
3922         }
3923
3924         /* the UI chunk */
3925         uirow = MSI_CreateRecord( 1 );
3926         MSI_RecordSetStringW( uirow, 1, feature->Feature );
3927         ui_actiondata( package, szPublishFeatures, uirow);
3928         msiobj_release( &uirow->hdr );
3929         /* FIXME: call ui_progress? */
3930     }
3931
3932 end:
3933     RegCloseKey(hkey);
3934     RegCloseKey(userdata);
3935     return rc;
3936 }
3937
3938 static UINT msi_unpublish_feature(MSIPACKAGE *package, MSIFEATURE *feature)
3939 {
3940     UINT r;
3941     HKEY hkey;
3942
3943     TRACE("unpublishing feature %s\n", debugstr_w(feature->Feature));
3944
3945     r = MSIREG_OpenUserFeaturesKey(package->ProductCode, &hkey, FALSE);
3946     if (r == ERROR_SUCCESS)
3947     {
3948         RegDeleteValueW(hkey, feature->Feature);
3949         RegCloseKey(hkey);
3950     }
3951
3952     r = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, &hkey, FALSE);
3953     if (r == ERROR_SUCCESS)
3954     {
3955         RegDeleteValueW(hkey, feature->Feature);
3956         RegCloseKey(hkey);
3957     }
3958
3959     return ERROR_SUCCESS;
3960 }
3961
3962 static UINT ACTION_UnpublishFeatures(MSIPACKAGE *package)
3963 {
3964     MSIFEATURE *feature;
3965
3966     if (!msi_check_unpublish(package))
3967         return ERROR_SUCCESS;
3968
3969     LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3970     {
3971         msi_unpublish_feature(package, feature);
3972     }
3973
3974     return ERROR_SUCCESS;
3975 }
3976
3977 static UINT msi_get_local_package_name( LPWSTR path )
3978 {
3979     static const WCHAR szInstaller[] = {
3980         '\\','I','n','s','t','a','l','l','e','r','\\',0};
3981     static const WCHAR fmt[] = { '%','x','.','m','s','i',0};
3982     DWORD time, len, i;
3983     HANDLE handle;
3984
3985     time = GetTickCount();
3986     GetWindowsDirectoryW( path, MAX_PATH );
3987     lstrcatW( path, szInstaller );
3988     CreateDirectoryW( path, NULL );
3989
3990     len = lstrlenW(path);
3991     for (i=0; i<0x10000; i++)
3992     {
3993         snprintfW( &path[len], MAX_PATH - len, fmt, (time+i)&0xffff );
3994         handle = CreateFileW( path, GENERIC_WRITE, 0, NULL,
3995                               CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0 );
3996         if (handle != INVALID_HANDLE_VALUE)
3997         {
3998             CloseHandle(handle);
3999             break;
4000         }
4001         if (GetLastError() != ERROR_FILE_EXISTS &&
4002             GetLastError() != ERROR_SHARING_VIOLATION)
4003             return ERROR_FUNCTION_FAILED;
4004     }
4005
4006     return ERROR_SUCCESS;
4007 }
4008
4009 static UINT msi_make_package_local( MSIPACKAGE *package, HKEY hkey )
4010 {
4011     WCHAR packagefile[MAX_PATH];
4012     UINT r;
4013
4014     r = msi_get_local_package_name( packagefile );
4015     if (r != ERROR_SUCCESS)
4016         return r;
4017
4018     TRACE("Copying to local package %s\n",debugstr_w(packagefile));
4019
4020     r = CopyFileW( package->db->path, packagefile, FALSE);
4021
4022     if (!r)
4023     {
4024         ERR("Unable to copy package (%s -> %s) (error %d)\n",
4025             debugstr_w(package->db->path), debugstr_w(packagefile), GetLastError());
4026         return ERROR_FUNCTION_FAILED;
4027     }
4028
4029     msi_reg_set_val_str( hkey, INSTALLPROPERTY_LOCALPACKAGEW, packagefile );
4030
4031     return ERROR_SUCCESS;
4032 }
4033
4034 static UINT msi_publish_install_properties(MSIPACKAGE *package, HKEY hkey)
4035 {
4036     LPWSTR prop, val, key;
4037     SYSTEMTIME systime;
4038     DWORD size, langid;
4039     WCHAR date[9];
4040     LPWSTR buffer;
4041
4042     static const WCHAR date_fmt[] = {'%','i','%','0','2','i','%','0','2','i',0};
4043     static const WCHAR szWindowsInstaller[] =
4044         {'W','i','n','d','o','w','s','I','n','s','t','a','l','l','e','r',0};
4045     static const WCHAR modpath_fmt[] =
4046         {'M','s','i','E','x','e','c','.','e','x','e',' ',
4047          '/','I','[','P','r','o','d','u','c','t','C','o','d','e',']',0};
4048     static const WCHAR szModifyPath[] =
4049         {'M','o','d','i','f','y','P','a','t','h',0};
4050     static const WCHAR szUninstallString[] =
4051         {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0};
4052     static const WCHAR szEstimatedSize[] =
4053         {'E','s','t','i','m','a','t','e','d','S','i','z','e',0};
4054     static const WCHAR szProductLanguage[] =
4055         {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
4056     static const WCHAR szProductVersion[] =
4057         {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
4058     static const WCHAR szProductName[] =
4059         {'P','r','o','d','u','c','t','N','a','m','e',0};
4060     static const WCHAR szDisplayName[] =
4061         {'D','i','s','p','l','a','y','N','a','m','e',0};
4062     static const WCHAR szDisplayVersion[] =
4063         {'D','i','s','p','l','a','y','V','e','r','s','i','o','n',0};
4064     static const WCHAR szManufacturer[] =
4065         {'M','a','n','u','f','a','c','t','u','r','e','r',0};
4066
4067     static const LPCSTR propval[] = {
4068         "ARPAUTHORIZEDCDFPREFIX", "AuthorizedCDFPrefix",
4069         "ARPCONTACT",             "Contact",
4070         "ARPCOMMENTS",            "Comments",
4071         "ProductName",            "DisplayName",
4072         "ProductVersion",         "DisplayVersion",
4073         "ARPHELPLINK",            "HelpLink",
4074         "ARPHELPTELEPHONE",       "HelpTelephone",
4075         "ARPINSTALLLOCATION",     "InstallLocation",
4076         "SourceDir",              "InstallSource",
4077         "Manufacturer",           "Publisher",
4078         "ARPREADME",              "Readme",
4079         "ARPSIZE",                "Size",
4080         "ARPURLINFOABOUT",        "URLInfoAbout",
4081         "ARPURLUPDATEINFO",       "URLUpdateInfo",
4082         NULL,
4083     };
4084     const LPCSTR *p = propval;
4085
4086     while (*p)
4087     {
4088         prop = strdupAtoW(*p++);
4089         key = strdupAtoW(*p++);
4090         val = msi_dup_property(package, prop);
4091         msi_reg_set_val_str(hkey, key, val);
4092         msi_free(val);
4093         msi_free(key);
4094         msi_free(prop);
4095     }
4096
4097     msi_reg_set_val_dword(hkey, szWindowsInstaller, 1);
4098
4099     size = deformat_string(package, modpath_fmt, &buffer);
4100     RegSetValueExW(hkey, szModifyPath, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
4101     RegSetValueExW(hkey, szUninstallString, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
4102     msi_free(buffer);
4103
4104     /* FIXME: Write real Estimated Size when we have it */
4105     msi_reg_set_val_dword(hkey, szEstimatedSize, 0);
4106
4107     buffer = msi_dup_property(package, szProductName);
4108     msi_reg_set_val_str(hkey, szDisplayName, buffer);
4109     msi_free(buffer);
4110
4111     buffer = msi_dup_property(package, cszSourceDir);
4112     msi_reg_set_val_str(hkey, INSTALLPROPERTY_INSTALLSOURCEW, buffer);
4113     msi_free(buffer);
4114
4115     buffer = msi_dup_property(package, szManufacturer);
4116     msi_reg_set_val_str(hkey, INSTALLPROPERTY_PUBLISHERW, buffer);
4117     msi_free(buffer);
4118
4119     GetLocalTime(&systime);
4120     sprintfW(date, date_fmt, systime.wYear, systime.wMonth, systime.wDay);
4121     msi_reg_set_val_str(hkey, INSTALLPROPERTY_INSTALLDATEW, date);
4122
4123     langid = msi_get_property_int(package, szProductLanguage, 0);
4124     msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
4125
4126     buffer = msi_dup_property(package, szProductVersion);
4127     msi_reg_set_val_str(hkey, szDisplayVersion, buffer);
4128     if (buffer)
4129     {
4130         DWORD verdword = msi_version_str_to_dword(buffer);
4131
4132         msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
4133         msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMAJORW, verdword >> 24);
4134         msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMINORW, (verdword >> 16) & 0xFF);
4135         msi_free(buffer);
4136     }
4137
4138     return ERROR_SUCCESS;
4139 }
4140
4141 static UINT ACTION_RegisterProduct(MSIPACKAGE *package)
4142 {
4143     WCHAR squashed_pc[SQUISH_GUID_SIZE];
4144     LPWSTR upgrade_code;
4145     HKEY hkey, props;
4146     HKEY upgrade;
4147     UINT rc;
4148
4149     static const WCHAR szUpgradeCode[] = {
4150         'U','p','g','r','a','d','e','C','o','d','e',0};
4151
4152     /* FIXME: also need to publish if the product is in advertise mode */
4153     if (!msi_check_publish(package))
4154         return ERROR_SUCCESS;
4155
4156     rc = MSIREG_OpenUninstallKey(package->ProductCode, &hkey, TRUE);
4157     if (rc != ERROR_SUCCESS)
4158         return rc;
4159
4160     if (package->Context == MSIINSTALLCONTEXT_MACHINE)
4161     {
4162         rc = MSIREG_OpenLocalSystemInstallProps(package->ProductCode, &props, TRUE);
4163         if (rc != ERROR_SUCCESS)
4164             goto done;
4165     }
4166     else
4167     {
4168         rc = MSIREG_OpenCurrentUserInstallProps(package->ProductCode, &props, TRUE);
4169         if (rc != ERROR_SUCCESS)
4170             goto done;
4171     }
4172
4173     msi_make_package_local(package, props);
4174
4175     rc = msi_publish_install_properties(package, hkey);
4176     if (rc != ERROR_SUCCESS)
4177         goto done;
4178
4179     rc = msi_publish_install_properties(package, props);
4180     if (rc != ERROR_SUCCESS)
4181         goto done;
4182
4183     upgrade_code = msi_dup_property(package, szUpgradeCode);
4184     if (upgrade_code)
4185     {
4186         MSIREG_OpenUpgradeCodesKey(upgrade_code, &upgrade, TRUE);
4187         squash_guid(package->ProductCode, squashed_pc);
4188         msi_reg_set_val_str(upgrade, squashed_pc, NULL);
4189         RegCloseKey(upgrade);
4190         msi_free(upgrade_code);
4191     }
4192
4193 done:
4194     RegCloseKey(hkey);
4195
4196     return ERROR_SUCCESS;
4197 }
4198
4199 static UINT ACTION_InstallExecute(MSIPACKAGE *package)
4200 {
4201     return execute_script(package,INSTALL_SCRIPT);
4202 }
4203
4204 static UINT msi_unpublish_product(MSIPACKAGE *package)
4205 {
4206     LPWSTR upgrade;
4207     LPWSTR remove = NULL;
4208     LPWSTR *features = NULL;
4209     BOOL full_uninstall = TRUE;
4210     MSIFEATURE *feature;
4211
4212     static const WCHAR szRemove[] = {'R','E','M','O','V','E',0};
4213     static const WCHAR szAll[] = {'A','L','L',0};
4214     static const WCHAR szUpgradeCode[] =
4215         {'U','p','g','r','a','d','e','C','o','d','e',0};
4216
4217     remove = msi_dup_property(package, szRemove);
4218     if (!remove)
4219         return ERROR_SUCCESS;
4220
4221     features = msi_split_string(remove, ',');
4222     if (!features)
4223     {
4224         msi_free(remove);
4225         ERR("REMOVE feature list is empty!\n");
4226         return ERROR_FUNCTION_FAILED;
4227     }
4228
4229     if (!lstrcmpW(features[0], szAll))
4230         full_uninstall = TRUE;
4231     else
4232     {
4233         LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4234         {
4235             if (feature->Action != INSTALLSTATE_ABSENT)
4236                 full_uninstall = FALSE;
4237         }
4238     }
4239
4240     if (!full_uninstall)
4241         goto done;
4242
4243     MSIREG_DeleteProductKey(package->ProductCode);
4244     MSIREG_DeleteUserDataProductKey(package->ProductCode);
4245     MSIREG_DeleteUninstallKey(package->ProductCode);
4246
4247     if (package->Context == MSIINSTALLCONTEXT_MACHINE)
4248     {
4249         MSIREG_DeleteLocalClassesProductKey(package->ProductCode);
4250         MSIREG_DeleteLocalClassesFeaturesKey(package->ProductCode);
4251     }
4252     else
4253     {
4254         MSIREG_DeleteUserProductKey(package->ProductCode);
4255         MSIREG_DeleteUserFeaturesKey(package->ProductCode);
4256     }
4257
4258     upgrade = msi_dup_property(package, szUpgradeCode);
4259     if (upgrade)
4260     {
4261         MSIREG_DeleteUserUpgradeCodesKey(upgrade);
4262         msi_free(upgrade);
4263     }
4264
4265 done:
4266     msi_free(remove);
4267     msi_free(features);
4268     return ERROR_SUCCESS;
4269 }
4270
4271 static UINT ACTION_InstallFinalize(MSIPACKAGE *package)
4272 {
4273     UINT rc;
4274
4275     rc = msi_unpublish_product(package);
4276     if (rc != ERROR_SUCCESS)
4277         return rc;
4278
4279     /* turn off scheduling */
4280     package->script->CurrentlyScripting= FALSE;
4281
4282     /* first do the same as an InstallExecute */
4283     rc = ACTION_InstallExecute(package);
4284     if (rc != ERROR_SUCCESS)
4285         return rc;
4286
4287     /* then handle Commit Actions */
4288     rc = execute_script(package,COMMIT_SCRIPT);
4289
4290     return rc;
4291 }
4292
4293 UINT ACTION_ForceReboot(MSIPACKAGE *package)
4294 {
4295     static const WCHAR RunOnce[] = {
4296     'S','o','f','t','w','a','r','e','\\',
4297     'M','i','c','r','o','s','o','f','t','\\',
4298     'W','i','n','d','o','w','s','\\',
4299     'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
4300     'R','u','n','O','n','c','e',0};
4301     static const WCHAR InstallRunOnce[] = {
4302     'S','o','f','t','w','a','r','e','\\',
4303     'M','i','c','r','o','s','o','f','t','\\',
4304     'W','i','n','d','o','w','s','\\',
4305     'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
4306     'I','n','s','t','a','l','l','e','r','\\',
4307     'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
4308
4309     static const WCHAR msiexec_fmt[] = {
4310     '%','s',
4311     '\\','M','s','i','E','x','e','c','.','e','x','e',' ','/','@',' ',
4312     '\"','%','s','\"',0};
4313     static const WCHAR install_fmt[] = {
4314     '/','I',' ','\"','%','s','\"',' ',
4315     'A','F','T','E','R','R','E','B','O','O','T','=','1',' ',
4316     'R','U','N','O','N','C','E','E','N','T','R','Y','=','\"','%','s','\"',0};
4317     WCHAR buffer[256], sysdir[MAX_PATH];
4318     HKEY hkey;
4319     WCHAR squished_pc[100];
4320
4321     squash_guid(package->ProductCode,squished_pc);
4322
4323     GetSystemDirectoryW(sysdir, sizeof(sysdir)/sizeof(sysdir[0]));
4324     RegCreateKeyW(HKEY_LOCAL_MACHINE,RunOnce,&hkey);
4325     snprintfW(buffer,sizeof(buffer)/sizeof(buffer[0]),msiexec_fmt,sysdir,
4326      squished_pc);
4327
4328     msi_reg_set_val_str( hkey, squished_pc, buffer );
4329     RegCloseKey(hkey);
4330
4331     TRACE("Reboot command %s\n",debugstr_w(buffer));
4332
4333     RegCreateKeyW(HKEY_LOCAL_MACHINE,InstallRunOnce,&hkey);
4334     sprintfW(buffer,install_fmt,package->ProductCode,squished_pc);
4335
4336     msi_reg_set_val_str( hkey, squished_pc, buffer );
4337     RegCloseKey(hkey);
4338
4339     return ERROR_INSTALL_SUSPEND;
4340 }
4341
4342 static UINT ACTION_ResolveSource(MSIPACKAGE* package)
4343 {
4344     DWORD attrib;
4345     UINT rc;
4346
4347     /*
4348      * We are currently doing what should be done here in the top level Install
4349      * however for Administrative and uninstalls this step will be needed
4350      */
4351     if (!package->PackagePath)
4352         return ERROR_SUCCESS;
4353
4354     msi_set_sourcedir_props(package, TRUE);
4355
4356     attrib = GetFileAttributesW(package->db->path);
4357     if (attrib == INVALID_FILE_ATTRIBUTES)
4358     {
4359         LPWSTR prompt;
4360         LPWSTR msg;
4361         DWORD size = 0;
4362
4363         rc = MsiSourceListGetInfoW(package->ProductCode, NULL, 
4364                 package->Context, MSICODE_PRODUCT,
4365                 INSTALLPROPERTY_DISKPROMPTW,NULL,&size);
4366         if (rc == ERROR_MORE_DATA)
4367         {
4368             prompt = msi_alloc(size * sizeof(WCHAR));
4369             MsiSourceListGetInfoW(package->ProductCode, NULL, 
4370                     package->Context, MSICODE_PRODUCT,
4371                     INSTALLPROPERTY_DISKPROMPTW,prompt,&size);
4372         }
4373         else
4374             prompt = strdupW(package->db->path);
4375
4376         msg = generate_error_string(package,1302,1,prompt);
4377         while(attrib == INVALID_FILE_ATTRIBUTES)
4378         {
4379             rc = MessageBoxW(NULL,msg,NULL,MB_OKCANCEL);
4380             if (rc == IDCANCEL)
4381             {
4382                 rc = ERROR_INSTALL_USEREXIT;
4383                 break;
4384             }
4385             attrib = GetFileAttributesW(package->db->path);
4386         }
4387         msi_free(prompt);
4388         rc = ERROR_SUCCESS;
4389     }
4390     else
4391         return ERROR_SUCCESS;
4392
4393     return rc;
4394 }
4395
4396 static UINT ACTION_RegisterUser(MSIPACKAGE *package)
4397 {
4398     HKEY hkey=0;
4399     LPWSTR buffer;
4400     LPWSTR productid;
4401     UINT rc,i;
4402
4403     static const WCHAR szPropKeys[][80] = 
4404     {
4405         {'P','r','o','d','u','c','t','I','D',0},
4406         {'U','S','E','R','N','A','M','E',0},
4407         {'C','O','M','P','A','N','Y','N','A','M','E',0},
4408         {0},
4409     };
4410
4411     static const WCHAR szRegKeys[][80] = 
4412     {
4413         {'P','r','o','d','u','c','t','I','D',0},
4414         {'R','e','g','O','w','n','e','r',0},
4415         {'R','e','g','C','o','m','p','a','n','y',0},
4416         {0},
4417     };
4418
4419     if (msi_check_unpublish(package))
4420     {
4421         MSIREG_DeleteUserDataProductKey(package->ProductCode);
4422         return ERROR_SUCCESS;
4423     }
4424
4425     productid = msi_dup_property( package, INSTALLPROPERTY_PRODUCTIDW );
4426     if (!productid)
4427         return ERROR_SUCCESS;
4428
4429     if (package->Context == MSIINSTALLCONTEXT_MACHINE)
4430         rc = MSIREG_OpenLocalSystemInstallProps(package->ProductCode, &hkey, TRUE);
4431     else
4432         rc = MSIREG_OpenCurrentUserInstallProps(package->ProductCode, &hkey, TRUE);
4433
4434     if (rc != ERROR_SUCCESS)
4435         goto end;
4436
4437     for( i = 0; szPropKeys[i][0]; i++ )
4438     {
4439         buffer = msi_dup_property( package, szPropKeys[i] );
4440         msi_reg_set_val_str( hkey, szRegKeys[i], buffer );
4441         msi_free( buffer );
4442     }
4443
4444 end:
4445     msi_free(productid);
4446     RegCloseKey(hkey);
4447
4448     /* FIXME: call ui_actiondata */
4449
4450     return rc;
4451 }
4452
4453
4454 static UINT ACTION_ExecuteAction(MSIPACKAGE *package)
4455 {
4456     UINT rc;
4457
4458     package->script->InWhatSequence |= SEQUENCE_EXEC;
4459     rc = ACTION_ProcessExecSequence(package,FALSE);
4460     return rc;
4461 }
4462
4463
4464 static UINT ITERATE_PublishComponent(MSIRECORD *rec, LPVOID param)
4465 {
4466     MSIPACKAGE *package = (MSIPACKAGE*)param;
4467     LPCWSTR compgroupid=NULL;
4468     LPCWSTR feature=NULL;
4469     LPCWSTR text = NULL;
4470     LPCWSTR qualifier = NULL;
4471     LPCWSTR component = NULL;
4472     LPWSTR advertise = NULL;
4473     LPWSTR output = NULL;
4474     HKEY hkey;
4475     UINT rc = ERROR_SUCCESS;
4476     MSICOMPONENT *comp;
4477     DWORD sz = 0;
4478     MSIRECORD *uirow;
4479
4480     component = MSI_RecordGetString(rec,3);
4481     comp = get_loaded_component(package,component);
4482
4483     if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL ) && 
4484        !ACTION_VerifyComponentForAction( comp, INSTALLSTATE_SOURCE ) &&
4485        !ACTION_VerifyComponentForAction( comp, INSTALLSTATE_ADVERTISED ))
4486     {
4487         TRACE("Skipping: Component %s not scheduled for install\n",
4488                         debugstr_w(component));
4489
4490         return ERROR_SUCCESS;
4491     }
4492
4493     compgroupid = MSI_RecordGetString(rec,1);
4494     qualifier = MSI_RecordGetString(rec,2);
4495
4496     rc = MSIREG_OpenUserComponentsKey(compgroupid, &hkey, TRUE);
4497     if (rc != ERROR_SUCCESS)
4498         goto end;
4499     
4500     text = MSI_RecordGetString(rec,4);
4501     feature = MSI_RecordGetString(rec,5);
4502   
4503     advertise = create_component_advertise_string(package, comp, feature);
4504
4505     sz = strlenW(advertise);
4506
4507     if (text)
4508         sz += lstrlenW(text);
4509
4510     sz+=3;
4511     sz *= sizeof(WCHAR);
4512            
4513     output = msi_alloc_zero(sz);
4514     strcpyW(output,advertise);
4515     msi_free(advertise);
4516
4517     if (text)
4518         strcatW(output,text);
4519
4520     msi_reg_set_val_multi_str( hkey, qualifier, output );
4521     
4522 end:
4523     RegCloseKey(hkey);
4524     msi_free(output);
4525
4526     /* the UI chunk */
4527     uirow = MSI_CreateRecord( 2 );
4528     MSI_RecordSetStringW( uirow, 1, compgroupid );
4529     MSI_RecordSetStringW( uirow, 2, qualifier);
4530     ui_actiondata( package, szPublishComponents, uirow);
4531     msiobj_release( &uirow->hdr );
4532     /* FIXME: call ui_progress? */
4533
4534     return rc;
4535 }
4536
4537 /*
4538  * At present I am ignorning the advertised components part of this and only
4539  * focusing on the qualified component sets
4540  */
4541 static UINT ACTION_PublishComponents(MSIPACKAGE *package)
4542 {
4543     UINT rc;
4544     MSIQUERY * view;
4545     static const WCHAR ExecSeqQuery[] =
4546         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4547          '`','P','u','b','l','i','s','h',
4548          'C','o','m','p','o','n','e','n','t','`',0};
4549     
4550     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4551     if (rc != ERROR_SUCCESS)
4552         return ERROR_SUCCESS;
4553
4554     rc = MSI_IterateRecords(view, NULL, ITERATE_PublishComponent, package);
4555     msiobj_release(&view->hdr);
4556
4557     return rc;
4558 }
4559
4560 static UINT ITERATE_InstallService(MSIRECORD *rec, LPVOID param)
4561 {
4562     MSIPACKAGE *package = (MSIPACKAGE*)param;
4563     MSIRECORD *row;
4564     MSIFILE *file;
4565     SC_HANDLE hscm, service = NULL;
4566     LPCWSTR comp, depends, pass;
4567     LPWSTR name = NULL, disp = NULL;
4568     LPCWSTR load_order, serv_name, key;
4569     DWORD serv_type, start_type;
4570     DWORD err_control;
4571
4572     static const WCHAR query[] =
4573         {'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
4574          '`','C','o','m','p','o','n','e','n','t','`',' ',
4575          'W','H','E','R','E',' ',
4576          '`','C','o','m','p','o','n','e','n','t','`',' ',
4577          '=','\'','%','s','\'',0};
4578
4579     hscm = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASEW, GENERIC_WRITE);
4580     if (!hscm)
4581     {
4582         ERR("Failed to open the SC Manager!\n");
4583         goto done;
4584     }
4585
4586     start_type = MSI_RecordGetInteger(rec, 5);
4587     if (start_type == SERVICE_BOOT_START || start_type == SERVICE_SYSTEM_START)
4588         goto done;
4589
4590     depends = MSI_RecordGetString(rec, 8);
4591     if (depends && *depends)
4592         FIXME("Dependency list unhandled!\n");
4593
4594     deformat_string(package, MSI_RecordGetString(rec, 2), &name);
4595     deformat_string(package, MSI_RecordGetString(rec, 3), &disp);
4596     serv_type = MSI_RecordGetInteger(rec, 4);
4597     err_control = MSI_RecordGetInteger(rec, 6);
4598     load_order = MSI_RecordGetString(rec, 7);
4599     serv_name = MSI_RecordGetString(rec, 9);
4600     pass = MSI_RecordGetString(rec, 10);
4601     comp = MSI_RecordGetString(rec, 12);
4602
4603     /* fetch the service path */
4604     row = MSI_QueryGetRecord(package->db, query, comp);
4605     if (!row)
4606     {
4607         ERR("Control query failed!\n");
4608         goto done;
4609     }
4610
4611     key = MSI_RecordGetString(row, 6);
4612
4613     file = get_loaded_file(package, key);
4614     msiobj_release(&row->hdr);
4615     if (!file)
4616     {
4617         ERR("Failed to load the service file\n");
4618         goto done;
4619     }
4620
4621     service = CreateServiceW(hscm, name, disp, GENERIC_ALL, serv_type,
4622                              start_type, err_control, file->TargetPath,
4623                              load_order, NULL, NULL, serv_name, pass);
4624     if (!service)
4625     {
4626         if (GetLastError() != ERROR_SERVICE_EXISTS)
4627             ERR("Failed to create service %s: %d\n", debugstr_w(name), GetLastError());
4628     }
4629
4630 done:
4631     CloseServiceHandle(service);
4632     CloseServiceHandle(hscm);
4633     msi_free(name);
4634     msi_free(disp);
4635
4636     return ERROR_SUCCESS;
4637 }
4638
4639 static UINT ACTION_InstallServices( MSIPACKAGE *package )
4640 {
4641     UINT rc;
4642     MSIQUERY * view;
4643     static const WCHAR ExecSeqQuery[] =
4644         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4645          'S','e','r','v','i','c','e','I','n','s','t','a','l','l',0};
4646     
4647     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4648     if (rc != ERROR_SUCCESS)
4649         return ERROR_SUCCESS;
4650
4651     rc = MSI_IterateRecords(view, NULL, ITERATE_InstallService, package);
4652     msiobj_release(&view->hdr);
4653
4654     return rc;
4655 }
4656
4657 /* converts arg1[~]arg2[~]arg3 to a list of ptrs to the strings */
4658 static LPCWSTR *msi_service_args_to_vector(LPWSTR args, DWORD *numargs)
4659 {
4660     LPCWSTR *vector, *temp_vector;
4661     LPWSTR p, q;
4662     DWORD sep_len;
4663
4664     static const WCHAR separator[] = {'[','~',']',0};
4665
4666     *numargs = 0;
4667     sep_len = sizeof(separator) / sizeof(WCHAR) - 1;
4668
4669     if (!args)
4670         return NULL;
4671
4672     vector = msi_alloc(sizeof(LPWSTR));
4673     if (!vector)
4674         return NULL;
4675
4676     p = args;
4677     do
4678     {
4679         (*numargs)++;
4680         vector[*numargs - 1] = p;
4681
4682         if ((q = strstrW(p, separator)))
4683         {
4684             *q = '\0';
4685
4686             temp_vector = msi_realloc(vector, (*numargs + 1) * sizeof(LPWSTR));
4687             if (!temp_vector)
4688             {
4689                 msi_free(vector);
4690                 return NULL;
4691             }
4692             vector = temp_vector;
4693
4694             p = q + sep_len;
4695         }
4696     } while (q);
4697
4698     return vector;
4699 }
4700
4701 static UINT ITERATE_StartService(MSIRECORD *rec, LPVOID param)
4702 {
4703     MSIPACKAGE *package = (MSIPACKAGE *)param;
4704     MSICOMPONENT *comp;
4705     SC_HANDLE scm, service = NULL;
4706     LPCWSTR name, *vector = NULL;
4707     LPWSTR args;
4708     DWORD event, numargs;
4709     UINT r = ERROR_FUNCTION_FAILED;
4710
4711     comp = get_loaded_component(package, MSI_RecordGetString(rec, 6));
4712     if (!comp || comp->Action == INSTALLSTATE_UNKNOWN || comp->Action == INSTALLSTATE_ABSENT)
4713         return ERROR_SUCCESS;
4714
4715     name = MSI_RecordGetString(rec, 2);
4716     event = MSI_RecordGetInteger(rec, 3);
4717     args = strdupW(MSI_RecordGetString(rec, 4));
4718
4719     if (!(event & msidbServiceControlEventStart))
4720         return ERROR_SUCCESS;
4721
4722     scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
4723     if (!scm)
4724     {
4725         ERR("Failed to open the service control manager\n");
4726         goto done;
4727     }
4728
4729     service = OpenServiceW(scm, name, SERVICE_START);
4730     if (!service)
4731     {
4732         ERR("Failed to open service %s\n", debugstr_w(name));
4733         goto done;
4734     }
4735
4736     vector = msi_service_args_to_vector(args, &numargs);
4737
4738     if (!StartServiceW(service, numargs, vector))
4739     {
4740         ERR("Failed to start service %s\n", debugstr_w(name));
4741         goto done;
4742     }
4743
4744     r = ERROR_SUCCESS;
4745
4746 done:
4747     CloseServiceHandle(service);
4748     CloseServiceHandle(scm);
4749
4750     msi_free(args);
4751     msi_free(vector);
4752     return r;
4753 }
4754
4755 static UINT ACTION_StartServices( MSIPACKAGE *package )
4756 {
4757     UINT rc;
4758     MSIQUERY *view;
4759
4760     static const WCHAR query[] = {
4761         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4762         'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
4763
4764     rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4765     if (rc != ERROR_SUCCESS)
4766         return ERROR_SUCCESS;
4767
4768     rc = MSI_IterateRecords(view, NULL, ITERATE_StartService, package);
4769     msiobj_release(&view->hdr);
4770
4771     return rc;
4772 }
4773
4774 static BOOL stop_service_dependents(SC_HANDLE scm, SC_HANDLE service)
4775 {
4776     DWORD i, needed, count;
4777     ENUM_SERVICE_STATUSW *dependencies;
4778     SERVICE_STATUS ss;
4779     SC_HANDLE depserv;
4780
4781     if (EnumDependentServicesW(service, SERVICE_ACTIVE, NULL,
4782                                0, &needed, &count))
4783         return TRUE;
4784
4785     if (GetLastError() != ERROR_MORE_DATA)
4786         return FALSE;
4787
4788     dependencies = msi_alloc(needed);
4789     if (!dependencies)
4790         return FALSE;
4791
4792     if (!EnumDependentServicesW(service, SERVICE_ACTIVE, dependencies,
4793                                 needed, &needed, &count))
4794         goto error;
4795
4796     for (i = 0; i < count; i++)
4797     {
4798         depserv = OpenServiceW(scm, dependencies[i].lpServiceName,
4799                                SERVICE_STOP | SERVICE_QUERY_STATUS);
4800         if (!depserv)
4801             goto error;
4802
4803         if (!ControlService(depserv, SERVICE_CONTROL_STOP, &ss))
4804             goto error;
4805     }
4806
4807     return TRUE;
4808
4809 error:
4810     msi_free(dependencies);
4811     return FALSE;
4812 }
4813
4814 static UINT ITERATE_StopService(MSIRECORD *rec, LPVOID param)
4815 {
4816     MSIPACKAGE *package = (MSIPACKAGE *)param;
4817     MSICOMPONENT *comp;
4818     SERVICE_STATUS status;
4819     SERVICE_STATUS_PROCESS ssp;
4820     SC_HANDLE scm = NULL, service = NULL;
4821     LPWSTR name, args;
4822     DWORD event, needed;
4823
4824     event = MSI_RecordGetInteger(rec, 3);
4825     if (!(event & msidbServiceControlEventStop))
4826         return ERROR_SUCCESS;
4827
4828     comp = get_loaded_component(package, MSI_RecordGetString(rec, 6));
4829     if (!comp || comp->Action == INSTALLSTATE_UNKNOWN || comp->Action == INSTALLSTATE_ABSENT)
4830         return ERROR_SUCCESS;
4831
4832     deformat_string(package, MSI_RecordGetString(rec, 2), &name);
4833     deformat_string(package, MSI_RecordGetString(rec, 4), &args);
4834     args = strdupW(MSI_RecordGetString(rec, 4));
4835
4836     scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
4837     if (!scm)
4838     {
4839         WARN("Failed to open the SCM: %d\n", GetLastError());
4840         goto done;
4841     }
4842
4843     service = OpenServiceW(scm, name,
4844                            SERVICE_STOP |
4845                            SERVICE_QUERY_STATUS |
4846                            SERVICE_ENUMERATE_DEPENDENTS);
4847     if (!service)
4848     {
4849         WARN("Failed to open service (%s): %d\n",
4850               debugstr_w(name), GetLastError());
4851         goto done;
4852     }
4853
4854     if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp,
4855                               sizeof(SERVICE_STATUS_PROCESS), &needed))
4856     {
4857         WARN("Failed to query service status (%s): %d\n",
4858              debugstr_w(name), GetLastError());
4859         goto done;
4860     }
4861
4862     if (ssp.dwCurrentState == SERVICE_STOPPED)
4863         goto done;
4864
4865     stop_service_dependents(scm, service);
4866
4867     if (!ControlService(service, SERVICE_CONTROL_STOP, &status))
4868         WARN("Failed to stop service (%s): %d\n", debugstr_w(name), GetLastError());
4869
4870 done:
4871     CloseServiceHandle(service);
4872     CloseServiceHandle(scm);
4873     msi_free(name);
4874     msi_free(args);
4875
4876     return ERROR_SUCCESS;
4877 }
4878
4879 static UINT ACTION_StopServices( MSIPACKAGE *package )
4880 {
4881     UINT rc;
4882     MSIQUERY *view;
4883
4884     static const WCHAR query[] = {
4885         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4886         'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
4887
4888     rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4889     if (rc != ERROR_SUCCESS)
4890         return ERROR_SUCCESS;
4891
4892     rc = MSI_IterateRecords(view, NULL, ITERATE_StopService, package);
4893     msiobj_release(&view->hdr);
4894
4895     return rc;
4896 }
4897
4898 static MSIFILE *msi_find_file( MSIPACKAGE *package, LPCWSTR filename )
4899 {
4900     MSIFILE *file;
4901
4902     LIST_FOR_EACH_ENTRY(file, &package->files, MSIFILE, entry)
4903     {
4904         if (!lstrcmpW(file->File, filename))
4905             return file;
4906     }
4907
4908     return NULL;
4909 }
4910
4911 static UINT ITERATE_InstallODBCDriver( MSIRECORD *rec, LPVOID param )
4912 {
4913     MSIPACKAGE *package = (MSIPACKAGE*)param;
4914     LPWSTR driver, driver_path, ptr;
4915     WCHAR outpath[MAX_PATH];
4916     MSIFILE *driver_file, *setup_file;
4917     LPCWSTR desc;
4918     DWORD len, usage;
4919     UINT r = ERROR_SUCCESS;
4920
4921     static const WCHAR driver_fmt[] = {
4922         'D','r','i','v','e','r','=','%','s',0};
4923     static const WCHAR setup_fmt[] = {
4924         'S','e','t','u','p','=','%','s',0};
4925     static const WCHAR usage_fmt[] = {
4926         'F','i','l','e','U','s','a','g','e','=','1',0};
4927
4928     desc = MSI_RecordGetString(rec, 3);
4929
4930     driver_file = msi_find_file(package, MSI_RecordGetString(rec, 4));
4931     setup_file = msi_find_file(package, MSI_RecordGetString(rec, 5));
4932
4933     if (!driver_file || !setup_file)
4934     {
4935         ERR("ODBC Driver entry not found!\n");
4936         return ERROR_FUNCTION_FAILED;
4937     }
4938
4939     len = lstrlenW(desc) + lstrlenW(driver_fmt) + lstrlenW(driver_file->FileName) +
4940           lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName) +
4941           lstrlenW(usage_fmt) + 1;
4942     driver = msi_alloc(len * sizeof(WCHAR));
4943     if (!driver)
4944         return ERROR_OUTOFMEMORY;
4945
4946     ptr = driver;
4947     lstrcpyW(ptr, desc);
4948     ptr += lstrlenW(ptr) + 1;
4949
4950     sprintfW(ptr, driver_fmt, driver_file->FileName);
4951     ptr += lstrlenW(ptr) + 1;
4952
4953     sprintfW(ptr, setup_fmt, setup_file->FileName);
4954     ptr += lstrlenW(ptr) + 1;
4955
4956     lstrcpyW(ptr, usage_fmt);
4957     ptr += lstrlenW(ptr) + 1;
4958     *ptr = '\0';
4959
4960     driver_path = strdupW(driver_file->TargetPath);
4961     ptr = strrchrW(driver_path, '\\');
4962     if (ptr) *ptr = '\0';
4963
4964     if (!SQLInstallDriverExW(driver, driver_path, outpath, MAX_PATH,
4965                              NULL, ODBC_INSTALL_COMPLETE, &usage))
4966     {
4967         ERR("Failed to install SQL driver!\n");
4968         r = ERROR_FUNCTION_FAILED;
4969     }
4970
4971     msi_free(driver);
4972     msi_free(driver_path);
4973
4974     return r;
4975 }
4976
4977 static UINT ITERATE_InstallODBCTranslator( MSIRECORD *rec, LPVOID param )
4978 {
4979     MSIPACKAGE *package = (MSIPACKAGE*)param;
4980     LPWSTR translator, translator_path, ptr;
4981     WCHAR outpath[MAX_PATH];
4982     MSIFILE *translator_file, *setup_file;
4983     LPCWSTR desc;
4984     DWORD len, usage;
4985     UINT r = ERROR_SUCCESS;
4986
4987     static const WCHAR translator_fmt[] = {
4988         'T','r','a','n','s','l','a','t','o','r','=','%','s',0};
4989     static const WCHAR setup_fmt[] = {
4990         'S','e','t','u','p','=','%','s',0};
4991
4992     desc = MSI_RecordGetString(rec, 3);
4993
4994     translator_file = msi_find_file(package, MSI_RecordGetString(rec, 4));
4995     setup_file = msi_find_file(package, MSI_RecordGetString(rec, 5));
4996
4997     if (!translator_file || !setup_file)
4998     {
4999         ERR("ODBC Translator entry not found!\n");
5000         return ERROR_FUNCTION_FAILED;
5001     }
5002
5003     len = lstrlenW(desc) + lstrlenW(translator_fmt) + lstrlenW(translator_file->FileName) +
5004           lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName) + 1;
5005     translator = msi_alloc(len * sizeof(WCHAR));
5006     if (!translator)
5007         return ERROR_OUTOFMEMORY;
5008
5009     ptr = translator;
5010     lstrcpyW(ptr, desc);
5011     ptr += lstrlenW(ptr) + 1;
5012
5013     sprintfW(ptr, translator_fmt, translator_file->FileName);
5014     ptr += lstrlenW(ptr) + 1;
5015
5016     sprintfW(ptr, setup_fmt, setup_file->FileName);
5017     ptr += lstrlenW(ptr) + 1;
5018     *ptr = '\0';
5019
5020     translator_path = strdupW(translator_file->TargetPath);
5021     ptr = strrchrW(translator_path, '\\');
5022     if (ptr) *ptr = '\0';
5023
5024     if (!SQLInstallTranslatorExW(translator, translator_path, outpath, MAX_PATH,
5025                                  NULL, ODBC_INSTALL_COMPLETE, &usage))
5026     {
5027         ERR("Failed to install SQL translator!\n");
5028         r = ERROR_FUNCTION_FAILED;
5029     }
5030
5031     msi_free(translator);
5032     msi_free(translator_path);
5033
5034     return r;
5035 }
5036
5037 static UINT ITERATE_InstallODBCDataSource( MSIRECORD *rec, LPVOID param )
5038 {
5039     LPWSTR attrs;
5040     LPCWSTR desc, driver;
5041     WORD request = ODBC_ADD_SYS_DSN;
5042     INT registration;
5043     DWORD len;
5044     UINT r = ERROR_SUCCESS;
5045
5046     static const WCHAR attrs_fmt[] = {
5047         'D','S','N','=','%','s',0 };
5048
5049     desc = MSI_RecordGetString(rec, 3);
5050     driver = MSI_RecordGetString(rec, 4);
5051     registration = MSI_RecordGetInteger(rec, 5);
5052
5053     if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_ADD_SYS_DSN;
5054     else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_ADD_DSN;
5055
5056     len = lstrlenW(attrs_fmt) + lstrlenW(desc) + 1 + 1;
5057     attrs = msi_alloc(len * sizeof(WCHAR));
5058     if (!attrs)
5059         return ERROR_OUTOFMEMORY;
5060
5061     sprintfW(attrs, attrs_fmt, desc);
5062     attrs[len - 1] = '\0';
5063
5064     if (!SQLConfigDataSourceW(NULL, request, driver, attrs))
5065     {
5066         ERR("Failed to install SQL data source!\n");
5067         r = ERROR_FUNCTION_FAILED;
5068     }
5069
5070     msi_free(attrs);
5071
5072     return r;
5073 }
5074
5075 static UINT ACTION_InstallODBC( MSIPACKAGE *package )
5076 {
5077     UINT rc;
5078     MSIQUERY *view;
5079
5080     static const WCHAR driver_query[] = {
5081         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5082         'O','D','B','C','D','r','i','v','e','r',0 };
5083
5084     static const WCHAR translator_query[] = {
5085         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5086         'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0 };
5087
5088     static const WCHAR source_query[] = {
5089         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5090         'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0 };
5091
5092     rc = MSI_DatabaseOpenViewW(package->db, driver_query, &view);
5093     if (rc != ERROR_SUCCESS)
5094         return ERROR_SUCCESS;
5095
5096     rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDriver, package);
5097     msiobj_release(&view->hdr);
5098
5099     rc = MSI_DatabaseOpenViewW(package->db, translator_query, &view);
5100     if (rc != ERROR_SUCCESS)
5101         return ERROR_SUCCESS;
5102
5103     rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCTranslator, package);
5104     msiobj_release(&view->hdr);
5105
5106     rc = MSI_DatabaseOpenViewW(package->db, source_query, &view);
5107     if (rc != ERROR_SUCCESS)
5108         return ERROR_SUCCESS;
5109
5110     rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDataSource, package);
5111     msiobj_release(&view->hdr);
5112
5113     return rc;
5114 }
5115
5116 #define ENV_ACT_SETALWAYS   0x1
5117 #define ENV_ACT_SETABSENT   0x2
5118 #define ENV_ACT_REMOVE      0x4
5119 #define ENV_ACT_REMOVEMATCH 0x8
5120
5121 #define ENV_MOD_MACHINE     0x20000000
5122 #define ENV_MOD_APPEND      0x40000000
5123 #define ENV_MOD_PREFIX      0x80000000
5124 #define ENV_MOD_MASK        0xC0000000
5125
5126 #define check_flag_combo(x, y) ((x) & ~(y)) == (y)
5127
5128 static LONG env_set_flags( LPCWSTR *name, LPCWSTR *value, DWORD *flags )
5129 {
5130     LPCWSTR cptr = *name;
5131     LPCWSTR ptr = *value;
5132
5133     static const WCHAR prefix[] = {'[','~',']',0};
5134     static const int prefix_len = 3;
5135
5136     *flags = 0;
5137     while (*cptr)
5138     {
5139         if (*cptr == '=')
5140             *flags |= ENV_ACT_SETALWAYS;
5141         else if (*cptr == '+')
5142             *flags |= ENV_ACT_SETABSENT;
5143         else if (*cptr == '-')
5144             *flags |= ENV_ACT_REMOVE;
5145         else if (*cptr == '!')
5146             *flags |= ENV_ACT_REMOVEMATCH;
5147         else if (*cptr == '*')
5148             *flags |= ENV_MOD_MACHINE;
5149         else
5150             break;
5151
5152         cptr++;
5153         (*name)++;
5154     }
5155
5156     if (!*cptr)
5157     {
5158         ERR("Missing environment variable\n");
5159         return ERROR_FUNCTION_FAILED;
5160     }
5161
5162     if (!strncmpW(ptr, prefix, prefix_len))
5163     {
5164         *flags |= ENV_MOD_APPEND;
5165         *value += lstrlenW(prefix);
5166     }
5167     else if (lstrlenW(*value) >= prefix_len)
5168     {
5169         ptr += lstrlenW(ptr) - prefix_len;
5170         if (!lstrcmpW(ptr, prefix))
5171         {
5172             *flags |= ENV_MOD_PREFIX;
5173             /* the "[~]" will be removed by deformat_string */;
5174         }
5175     }
5176
5177     if (!*flags ||
5178         check_flag_combo(*flags, ENV_ACT_SETALWAYS | ENV_ACT_SETABSENT) ||
5179         check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETABSENT) ||
5180         check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETALWAYS) ||
5181         check_flag_combo(*flags, ENV_ACT_SETABSENT | ENV_MOD_MASK))
5182     {
5183         ERR("Invalid flags: %08x\n", *flags);
5184         return ERROR_FUNCTION_FAILED;
5185     }
5186
5187     return ERROR_SUCCESS;
5188 }
5189
5190 static UINT ITERATE_WriteEnvironmentString( MSIRECORD *rec, LPVOID param )
5191 {
5192     MSIPACKAGE *package = param;
5193     LPCWSTR name, value;
5194     LPWSTR data = NULL, newval = NULL;
5195     LPWSTR deformatted = NULL, ptr;
5196     DWORD flags, type, size;
5197     LONG res;
5198     HKEY env = NULL, root;
5199     LPCWSTR environment;
5200
5201     static const WCHAR user_env[] =
5202         {'E','n','v','i','r','o','n','m','e','n','t',0};
5203     static const WCHAR machine_env[] =
5204         {'S','y','s','t','e','m','\\',
5205          'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
5206          'C','o','n','t','r','o','l','\\',
5207          'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r','\\',
5208          'E','n','v','i','r','o','n','m','e','n','t',0};
5209     static const WCHAR semicolon[] = {';',0};
5210
5211     name = MSI_RecordGetString(rec, 2);
5212     value = MSI_RecordGetString(rec, 3);
5213
5214     res = env_set_flags(&name, &value, &flags);
5215     if (res != ERROR_SUCCESS)
5216        goto done;
5217
5218     deformat_string(package, value, &deformatted);
5219     if (!deformatted)
5220     {
5221         res = ERROR_OUTOFMEMORY;
5222         goto done;
5223     }
5224
5225     value = deformatted;
5226
5227     if (flags & ENV_MOD_MACHINE)
5228     {
5229         environment = machine_env;
5230         root = HKEY_LOCAL_MACHINE;
5231     }
5232     else
5233     {
5234         environment = user_env;
5235         root = HKEY_CURRENT_USER;
5236     }
5237
5238     res = RegCreateKeyExW(root, environment, 0, NULL, 0,
5239                           KEY_ALL_ACCESS, NULL, &env, NULL);
5240     if (res != ERROR_SUCCESS)
5241         goto done;
5242
5243     if (flags & ENV_ACT_REMOVE)
5244         FIXME("Not removing environment variable on uninstall!\n");
5245
5246     size = 0;
5247     res = RegQueryValueExW(env, name, NULL, &type, NULL, &size);
5248     if ((res != ERROR_SUCCESS && res != ERROR_FILE_NOT_FOUND) ||
5249         (res == ERROR_SUCCESS && type != REG_SZ && type != REG_EXPAND_SZ))
5250         goto done;
5251
5252     if (res != ERROR_FILE_NOT_FOUND)
5253     {
5254         if (flags & ENV_ACT_SETABSENT)
5255         {
5256             res = ERROR_SUCCESS;
5257             goto done;
5258         }
5259
5260         data = msi_alloc(size);
5261         if (!data)
5262         {
5263             RegCloseKey(env);
5264             return ERROR_OUTOFMEMORY;
5265         }
5266
5267         res = RegQueryValueExW(env, name, NULL, &type, (LPVOID)data, &size);
5268         if (res != ERROR_SUCCESS)
5269             goto done;
5270
5271         if (flags & ENV_ACT_REMOVEMATCH && (!value || !lstrcmpW(data, value)))
5272         {
5273             res = RegDeleteKeyW(env, name);
5274             goto done;
5275         }
5276
5277         size =  (lstrlenW(value) + 1 + size) * sizeof(WCHAR);
5278         newval =  msi_alloc(size);
5279         ptr = newval;
5280         if (!newval)
5281         {
5282             res = ERROR_OUTOFMEMORY;
5283             goto done;
5284         }
5285
5286         if (!(flags & ENV_MOD_MASK))
5287             lstrcpyW(newval, value);
5288         else
5289         {
5290             if (flags & ENV_MOD_PREFIX)
5291             {
5292                 lstrcpyW(newval, value);
5293                 lstrcatW(newval, semicolon);
5294                 ptr = newval + lstrlenW(value) + 1;
5295             }
5296
5297             lstrcpyW(ptr, data);
5298
5299             if (flags & ENV_MOD_APPEND)
5300             {
5301                 lstrcatW(newval, semicolon);
5302                 lstrcatW(newval, value);
5303             }
5304         }
5305     }
5306     else
5307     {
5308         size = (lstrlenW(value) + 1) * sizeof(WCHAR);
5309         newval = msi_alloc(size);
5310         if (!newval)
5311         {
5312             res = ERROR_OUTOFMEMORY;
5313             goto done;
5314         }
5315
5316         lstrcpyW(newval, value);
5317     }
5318
5319     TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(newval));
5320     res = RegSetValueExW(env, name, 0, type, (LPVOID)newval, size);
5321
5322 done:
5323     if (env) RegCloseKey(env);
5324     msi_free(deformatted);
5325     msi_free(data);
5326     msi_free(newval);
5327     return res;
5328 }
5329
5330 static UINT ACTION_WriteEnvironmentStrings( MSIPACKAGE *package )
5331 {
5332     UINT rc;
5333     MSIQUERY * view;
5334     static const WCHAR ExecSeqQuery[] =
5335         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5336          '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
5337     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
5338     if (rc != ERROR_SUCCESS)
5339         return ERROR_SUCCESS;
5340
5341     rc = MSI_IterateRecords(view, NULL, ITERATE_WriteEnvironmentString, package);
5342     msiobj_release(&view->hdr);
5343
5344     return rc;
5345 }
5346
5347 #define is_dot_dir(x) ((x[0] == '.') && ((x[1] == 0) || ((x[1] == '.') && (x[2] == 0))))
5348
5349 typedef struct
5350 {
5351     struct list entry;
5352     LPWSTR sourcename;
5353     LPWSTR destname;
5354     LPWSTR source;
5355     LPWSTR dest;
5356 } FILE_LIST;
5357
5358 static BOOL msi_move_file(LPCWSTR source, LPCWSTR dest, int options)
5359 {
5360     BOOL ret;
5361
5362     if (GetFileAttributesW(source) == FILE_ATTRIBUTE_DIRECTORY ||
5363         GetFileAttributesW(dest) == FILE_ATTRIBUTE_DIRECTORY)
5364     {
5365         WARN("Source or dest is directory, not moving\n");
5366         return FALSE;
5367     }
5368
5369     if (options == msidbMoveFileOptionsMove)
5370     {
5371         TRACE("moving %s -> %s\n", debugstr_w(source), debugstr_w(dest));
5372         ret = MoveFileExW(source, dest, MOVEFILE_REPLACE_EXISTING);
5373         if (!ret)
5374         {
5375             WARN("MoveFile failed: %d\n", GetLastError());
5376             return FALSE;
5377         }
5378     }
5379     else
5380     {
5381         TRACE("copying %s -> %s\n", debugstr_w(source), debugstr_w(dest));
5382         ret = CopyFileW(source, dest, FALSE);
5383         if (!ret)
5384         {
5385             WARN("CopyFile failed: %d\n", GetLastError());
5386             return FALSE;
5387         }
5388     }
5389
5390     return TRUE;
5391 }
5392
5393 static LPWSTR wildcard_to_file(LPWSTR wildcard, LPWSTR filename)
5394 {
5395     LPWSTR path, ptr;
5396     DWORD dirlen, pathlen;
5397
5398     ptr = strrchrW(wildcard, '\\');
5399     dirlen = ptr - wildcard + 1;
5400
5401     pathlen = dirlen + lstrlenW(filename) + 1;
5402     path = msi_alloc(pathlen * sizeof(WCHAR));
5403
5404     lstrcpynW(path, wildcard, dirlen + 1);
5405     lstrcatW(path, filename);
5406
5407     return path;
5408 }
5409
5410 static void free_file_entry(FILE_LIST *file)
5411 {
5412     msi_free(file->source);
5413     msi_free(file->dest);
5414     msi_free(file);
5415 }
5416
5417 static void free_list(FILE_LIST *list)
5418 {
5419     while (!list_empty(&list->entry))
5420     {
5421         FILE_LIST *file = LIST_ENTRY(list_head(&list->entry), FILE_LIST, entry);
5422
5423         list_remove(&file->entry);
5424         free_file_entry(file);
5425     }
5426 }
5427
5428 static BOOL add_wildcard(FILE_LIST *files, LPWSTR source, LPWSTR dest)
5429 {
5430     FILE_LIST *new, *file;
5431     LPWSTR ptr, filename;
5432     DWORD size;
5433
5434     new = msi_alloc_zero(sizeof(FILE_LIST));
5435     if (!new)
5436         return FALSE;
5437
5438     new->source = strdupW(source);
5439     ptr = strrchrW(dest, '\\') + 1;
5440     filename = strrchrW(new->source, '\\') + 1;
5441
5442     new->sourcename = filename;
5443
5444     if (*ptr)
5445         new->destname = ptr;
5446     else
5447         new->destname = new->sourcename;
5448
5449     size = (ptr - dest) + lstrlenW(filename) + 1;
5450     new->dest = msi_alloc(size * sizeof(WCHAR));
5451     if (!new->dest)
5452     {
5453         free_file_entry(new);
5454         return FALSE;
5455     }
5456
5457     lstrcpynW(new->dest, dest, ptr - dest + 1);
5458     lstrcatW(new->dest, filename);
5459
5460     if (list_empty(&files->entry))
5461     {
5462         list_add_head(&files->entry, &new->entry);
5463         return TRUE;
5464     }
5465
5466     LIST_FOR_EACH_ENTRY(file, &files->entry, FILE_LIST, entry)
5467     {
5468         if (lstrcmpW(source, file->source) < 0)
5469         {
5470             list_add_before(&file->entry, &new->entry);
5471             return TRUE;
5472         }
5473     }
5474
5475     list_add_after(&file->entry, &new->entry);
5476     return TRUE;
5477 }
5478
5479 static BOOL move_files_wildcard(LPWSTR source, LPWSTR dest, int options)
5480 {
5481     WIN32_FIND_DATAW wfd;
5482     HANDLE hfile;
5483     LPWSTR path;
5484     BOOL res;
5485     FILE_LIST files, *file;
5486     DWORD size;
5487
5488     hfile = FindFirstFileW(source, &wfd);
5489     if (hfile == INVALID_HANDLE_VALUE) return FALSE;
5490
5491     list_init(&files.entry);
5492
5493     for (res = TRUE; res; res = FindNextFileW(hfile, &wfd))
5494     {
5495         if (is_dot_dir(wfd.cFileName)) continue;
5496
5497         path = wildcard_to_file(source, wfd.cFileName);
5498         if (!path)
5499         {
5500             res = FALSE;
5501             goto done;
5502         }
5503
5504         add_wildcard(&files, path, dest);
5505         msi_free(path);
5506     }
5507
5508     /* no files match the wildcard */
5509     if (list_empty(&files.entry))
5510         goto done;
5511
5512     /* only the first wildcard match gets renamed to dest */
5513     file = LIST_ENTRY(list_head(&files.entry), FILE_LIST, entry);
5514     size = (strrchrW(file->dest, '\\') - file->dest) + lstrlenW(file->destname) + 2;
5515     file->dest = msi_realloc(file->dest, size * sizeof(WCHAR));
5516     if (!file->dest)
5517     {
5518         res = FALSE;
5519         goto done;
5520     }
5521
5522     lstrcpyW(strrchrW(file->dest, '\\') + 1, file->destname);
5523
5524     while (!list_empty(&files.entry))
5525     {
5526         file = LIST_ENTRY(list_head(&files.entry), FILE_LIST, entry);
5527
5528         msi_move_file((LPCWSTR)file->source, (LPCWSTR)file->dest, options);
5529
5530         list_remove(&file->entry);
5531         free_file_entry(file);
5532     }
5533
5534     res = TRUE;
5535
5536 done:
5537     free_list(&files);
5538     FindClose(hfile);
5539     return res;
5540 }
5541
5542 static UINT ITERATE_MoveFiles( MSIRECORD *rec, LPVOID param )
5543 {
5544     MSIPACKAGE *package = param;
5545     MSICOMPONENT *comp;
5546     LPCWSTR sourcename, destname;
5547     LPWSTR sourcedir = NULL, destdir = NULL;
5548     LPWSTR source = NULL, dest = NULL;
5549     int options;
5550     DWORD size;
5551     BOOL ret, wildcards;
5552
5553     static const WCHAR backslash[] = {'\\',0};
5554
5555     comp = get_loaded_component(package, MSI_RecordGetString(rec, 2));
5556     if (!comp || !comp->Enabled ||
5557         !(comp->Action & (INSTALLSTATE_LOCAL | INSTALLSTATE_SOURCE)))
5558     {
5559         TRACE("Component not set for install, not moving file\n");
5560         return ERROR_SUCCESS;
5561     }
5562
5563     sourcename = MSI_RecordGetString(rec, 3);
5564     destname = MSI_RecordGetString(rec, 4);
5565     options = MSI_RecordGetInteger(rec, 7);
5566
5567     sourcedir = msi_dup_property(package, MSI_RecordGetString(rec, 5));
5568     if (!sourcedir)
5569         goto done;
5570
5571     destdir = msi_dup_property(package, MSI_RecordGetString(rec, 6));
5572     if (!destdir)
5573         goto done;
5574
5575     if (!sourcename)
5576     {
5577         if (GetFileAttributesW(sourcedir) == INVALID_FILE_ATTRIBUTES)
5578             goto done;
5579
5580         source = strdupW(sourcedir);
5581         if (!source)
5582             goto done;
5583     }
5584     else
5585     {
5586         size = lstrlenW(sourcedir) + lstrlenW(sourcename) + 2;
5587         source = msi_alloc(size * sizeof(WCHAR));
5588         if (!source)
5589             goto done;
5590
5591         lstrcpyW(source, sourcedir);
5592         if (source[lstrlenW(source) - 1] != '\\')
5593             lstrcatW(source, backslash);
5594         lstrcatW(source, sourcename);
5595     }
5596
5597     wildcards = strchrW(source, '*') || strchrW(source, '?');
5598
5599     if (!destname && !wildcards)
5600     {
5601         destname = strdupW(sourcename);
5602         if (!destname)
5603             goto done;
5604     }
5605
5606     size = 0;
5607     if (destname)
5608         size = lstrlenW(destname);
5609
5610     size += lstrlenW(destdir) + 2;
5611     dest = msi_alloc(size * sizeof(WCHAR));
5612     if (!dest)
5613         goto done;
5614
5615     lstrcpyW(dest, destdir);
5616     if (dest[lstrlenW(dest) - 1] != '\\')
5617         lstrcatW(dest, backslash);
5618
5619     if (destname)
5620         lstrcatW(dest, destname);
5621
5622     if (GetFileAttributesW(destdir) == INVALID_FILE_ATTRIBUTES)
5623     {
5624         ret = CreateDirectoryW(destdir, NULL);
5625         if (!ret)
5626         {
5627             WARN("CreateDirectory failed: %d\n", GetLastError());
5628             return ERROR_SUCCESS;
5629         }
5630     }
5631
5632     if (!wildcards)
5633         msi_move_file(source, dest, options);
5634     else
5635         move_files_wildcard(source, dest, options);
5636
5637 done:
5638     msi_free(sourcedir);
5639     msi_free(destdir);
5640     msi_free(source);
5641     msi_free(dest);
5642
5643     return ERROR_SUCCESS;
5644 }
5645
5646 static UINT ACTION_MoveFiles( MSIPACKAGE *package )
5647 {
5648     UINT rc;
5649     MSIQUERY *view;
5650
5651     static const WCHAR ExecSeqQuery[] =
5652         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5653          '`','M','o','v','e','F','i','l','e','`',0};
5654
5655     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
5656     if (rc != ERROR_SUCCESS)
5657         return ERROR_SUCCESS;
5658
5659     rc = MSI_IterateRecords(view, NULL, ITERATE_MoveFiles, package);
5660     msiobj_release(&view->hdr);
5661
5662     return rc;
5663 }
5664
5665 static HRESULT (WINAPI *pCreateAssemblyCache)(IAssemblyCache **ppAsmCache,
5666                                               DWORD dwReserved);
5667 static HRESULT (WINAPI *pLoadLibraryShim)(LPCWSTR szDllName, LPCWSTR szVersion,
5668                                           LPVOID pvReserved, HMODULE *phModDll);
5669
5670 static BOOL init_functionpointers(void)
5671 {
5672     HRESULT hr;
5673     HMODULE hfusion;
5674     HMODULE hmscoree;
5675
5676     static const WCHAR szFusion[] = {'f','u','s','i','o','n','.','d','l','l',0};
5677
5678     hmscoree = LoadLibraryA("mscoree.dll");
5679     if (!hmscoree)
5680     {
5681         WARN("mscoree.dll not available\n");
5682         return FALSE;
5683     }
5684
5685     pLoadLibraryShim = (void *)GetProcAddress(hmscoree, "LoadLibraryShim");
5686     if (!pLoadLibraryShim)
5687     {
5688         WARN("LoadLibraryShim not available\n");
5689         FreeLibrary(hmscoree);
5690         return FALSE;
5691     }
5692
5693     hr = pLoadLibraryShim(szFusion, NULL, NULL, &hfusion);
5694     if (FAILED(hr))
5695     {
5696         WARN("fusion.dll not available\n");
5697         FreeLibrary(hmscoree);
5698         return FALSE;
5699     }
5700
5701     pCreateAssemblyCache = (void *)GetProcAddress(hfusion, "CreateAssemblyCache");
5702
5703     FreeLibrary(hmscoree);
5704     return TRUE;
5705 }
5706
5707 static UINT install_assembly(LPWSTR path)
5708 {
5709     IAssemblyCache *cache;
5710     HRESULT hr;
5711     UINT r = ERROR_FUNCTION_FAILED;
5712
5713     if (!init_functionpointers() || !pCreateAssemblyCache)
5714         return ERROR_FUNCTION_FAILED;
5715
5716     hr = pCreateAssemblyCache(&cache, 0);
5717     if (FAILED(hr))
5718         goto done;
5719
5720     hr = IAssemblyCache_InstallAssembly(cache, 0, path, NULL);
5721     if (FAILED(hr))
5722         ERR("Failed to install assembly: %s %08x\n", debugstr_w(path), hr);
5723
5724     r = ERROR_SUCCESS;
5725
5726 done:
5727     IAssemblyCache_Release(cache);
5728     return r;
5729 }
5730
5731 static UINT ITERATE_PublishAssembly( MSIRECORD *rec, LPVOID param )
5732 {
5733     MSIPACKAGE *package = param;
5734     MSICOMPONENT *comp;
5735     MSIFEATURE *feature;
5736     MSIFILE *file;
5737     WCHAR path[MAX_PATH];
5738     LPCWSTR app;
5739     DWORD attr;
5740     UINT r;
5741
5742     comp = get_loaded_component(package, MSI_RecordGetString(rec, 1));
5743     if (!comp || !comp->Enabled ||
5744         !(comp->Action & (INSTALLSTATE_LOCAL | INSTALLSTATE_SOURCE)))
5745     {
5746         ERR("Component not set for install, not publishing assembly\n");
5747         return ERROR_SUCCESS;
5748     }
5749
5750     feature = find_feature_by_name(package, MSI_RecordGetString(rec, 2));
5751     if (feature)
5752         msi_feature_set_state(feature, INSTALLSTATE_LOCAL);
5753
5754     if (MSI_RecordGetString(rec, 3))
5755         FIXME("Manifest unhandled\n");
5756
5757     app = MSI_RecordGetString(rec, 4);
5758     if (app)
5759     {
5760         FIXME("Assembly should be privately installed\n");
5761         return ERROR_SUCCESS;
5762     }
5763
5764     attr = MSI_RecordGetInteger(rec, 5);
5765     if (attr == msidbAssemblyAttributesWin32)
5766     {
5767         FIXME("Win32 assemblies not handled\n");
5768         return ERROR_SUCCESS;
5769     }
5770
5771     /* FIXME: extract all files belonging to this component */
5772     file = msi_find_file(package, comp->KeyPath);
5773     if (!file)
5774     {
5775         ERR("File %s not found\n", debugstr_w(comp->KeyPath));
5776         return ERROR_FUNCTION_FAILED;
5777     }
5778
5779     GetTempPathW(MAX_PATH, path);
5780
5781     if (file->IsCompressed)
5782     {
5783         r = msi_extract_file(package, file, path);
5784         if (r != ERROR_SUCCESS)
5785         {
5786             ERR("Failed to extract temporary assembly\n");
5787             return r;
5788         }
5789
5790         PathAddBackslashW(path);
5791         lstrcatW(path, file->FileName);
5792     }
5793     else
5794     {
5795         PathAddBackslashW(path);
5796         lstrcatW(path, file->FileName);
5797
5798         if (!CopyFileW(file->SourcePath, path, FALSE))
5799         {
5800             ERR("Failed to copy temporary assembly: %d\n", GetLastError());
5801             return ERROR_FUNCTION_FAILED;
5802         }
5803     }
5804
5805     r = install_assembly(path);
5806     if (r != ERROR_SUCCESS)
5807         ERR("Failed to install assembly\n");
5808
5809     /* FIXME: write Installer assembly reg values */
5810
5811     return r;
5812 }
5813
5814 static UINT ACTION_MsiPublishAssemblies( MSIPACKAGE *package )
5815 {
5816     UINT rc;
5817     MSIQUERY *view;
5818
5819     static const WCHAR ExecSeqQuery[] =
5820         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5821          '`','M','s','i','A','s','s','e','m','b','l','y','`',0};
5822
5823     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
5824     if (rc != ERROR_SUCCESS)
5825         return ERROR_SUCCESS;
5826
5827     rc = MSI_IterateRecords(view, NULL, ITERATE_PublishAssembly, package);
5828     msiobj_release(&view->hdr);
5829
5830     return rc;
5831 }
5832
5833 static UINT msi_unimplemented_action_stub( MSIPACKAGE *package,
5834                                            LPCSTR action, LPCWSTR table )
5835 {
5836     static const WCHAR query[] = {
5837         'S','E','L','E','C','T',' ','*',' ',
5838         'F','R','O','M',' ','`','%','s','`',0 };
5839     MSIQUERY *view = NULL;
5840     DWORD count = 0;
5841     UINT r;
5842     
5843     r = MSI_OpenQuery( package->db, &view, query, table );
5844     if (r == ERROR_SUCCESS)
5845     {
5846         r = MSI_IterateRecords(view, &count, NULL, package);
5847         msiobj_release(&view->hdr);
5848     }
5849
5850     if (count)
5851         FIXME("%s -> %u ignored %s table values\n",
5852               action, count, debugstr_w(table));
5853
5854     return ERROR_SUCCESS;
5855 }
5856
5857 static UINT ACTION_AllocateRegistrySpace( MSIPACKAGE *package )
5858 {
5859     TRACE("%p\n", package);
5860     return ERROR_SUCCESS;
5861 }
5862
5863 static UINT ACTION_RemoveIniValues( MSIPACKAGE *package )
5864 {
5865     static const WCHAR table[] =
5866          {'R','e','m','o','v','e','I','n','i','F','i','l','e',0 };
5867     return msi_unimplemented_action_stub( package, "RemoveIniValues", table );
5868 }
5869
5870 static UINT ACTION_PatchFiles( MSIPACKAGE *package )
5871 {
5872     static const WCHAR table[] = { 'P','a','t','c','h',0 };
5873     return msi_unimplemented_action_stub( package, "PatchFiles", table );
5874 }
5875
5876 static UINT ACTION_BindImage( MSIPACKAGE *package )
5877 {
5878     static const WCHAR table[] = { 'B','i','n','d','I','m','a','g','e',0 };
5879     return msi_unimplemented_action_stub( package, "BindImage", table );
5880 }
5881
5882 static UINT ACTION_IsolateComponents( MSIPACKAGE *package )
5883 {
5884     static const WCHAR table[] = {
5885         'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t',0 };
5886     return msi_unimplemented_action_stub( package, "IsolateComponents", table );
5887 }
5888
5889 static UINT ACTION_MigrateFeatureStates( MSIPACKAGE *package )
5890 {
5891     static const WCHAR table[] = { 'U','p','g','r','a','d','e',0 };
5892     return msi_unimplemented_action_stub( package, "MigrateFeatureStates", table );
5893 }
5894
5895 static UINT ACTION_SelfUnregModules( MSIPACKAGE *package )
5896 {
5897     static const WCHAR table[] = { 'S','e','l','f','R','e','g',0 };
5898     return msi_unimplemented_action_stub( package, "SelfUnregModules", table );
5899 }
5900
5901 static UINT ACTION_DeleteServices( MSIPACKAGE *package )
5902 {
5903     static const WCHAR table[] = {
5904         'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
5905     return msi_unimplemented_action_stub( package, "DeleteServices", table );
5906 }
5907 static UINT ACTION_ValidateProductID( MSIPACKAGE *package )
5908 {
5909         static const WCHAR table[] = {
5910                 'P','r','o','d','u','c','t','I','D',0 };
5911         return msi_unimplemented_action_stub( package, "ValidateProductID", table );
5912 }
5913
5914 static UINT ACTION_RemoveEnvironmentStrings( MSIPACKAGE *package )
5915 {
5916     static const WCHAR table[] = {
5917         'E','n','v','i','r','o','n','m','e','n','t',0 };
5918     return msi_unimplemented_action_stub( package, "RemoveEnvironmentStrings", table );
5919 }
5920
5921 static UINT ACTION_MsiUnpublishAssemblies( MSIPACKAGE *package )
5922 {
5923     static const WCHAR table[] = {
5924         'M','s','i','A','s','s','e','m','b','l','y',0 };
5925     return msi_unimplemented_action_stub( package, "MsiUnpublishAssemblies", table );
5926 }
5927
5928 static UINT ACTION_UnregisterFonts( MSIPACKAGE *package )
5929 {
5930     static const WCHAR table[] = { 'F','o','n','t',0 };
5931     return msi_unimplemented_action_stub( package, "UnregisterFonts", table );
5932 }
5933
5934 static UINT ACTION_RMCCPSearch( MSIPACKAGE *package )
5935 {
5936     static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
5937     return msi_unimplemented_action_stub( package, "RMCCPSearch", table );
5938 }
5939
5940 static UINT ACTION_RegisterComPlus( MSIPACKAGE *package )
5941 {
5942     static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
5943     return msi_unimplemented_action_stub( package, "RegisterComPlus", table );
5944 }
5945
5946 static UINT ACTION_UnregisterComPlus( MSIPACKAGE *package )
5947 {
5948     static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
5949     return msi_unimplemented_action_stub( package, "UnregisterComPlus", table );
5950 }
5951
5952 static UINT ACTION_InstallSFPCatalogFile( MSIPACKAGE *package )
5953 {
5954     static const WCHAR table[] = { 'S','F','P','C','a','t','a','l','o','g',0 };
5955     return msi_unimplemented_action_stub( package, "InstallSFPCatalogFile", table );
5956 }
5957
5958 static UINT ACTION_RemoveDuplicateFiles( MSIPACKAGE *package )
5959 {
5960     static const WCHAR table[] = { 'D','u','p','l','i','c','a','t','e','F','i','l','e',0 };
5961     return msi_unimplemented_action_stub( package, "RemoveDuplicateFiles", table );
5962 }
5963
5964 static UINT ACTION_RemoveExistingProducts( MSIPACKAGE *package )
5965 {
5966     static const WCHAR table[] = { 'U','p','g','r','a','d','e',0 };
5967     return msi_unimplemented_action_stub( package, "RemoveExistingProducts", table );
5968 }
5969
5970 static UINT ACTION_RemoveFolders( MSIPACKAGE *package )
5971 {
5972     static const WCHAR table[] = { 'C','r','e','a','t','e','F','o','l','d','e','r',0 };
5973     return msi_unimplemented_action_stub( package, "RemoveFolders", table );
5974 }
5975
5976 static UINT ACTION_RemoveODBC( MSIPACKAGE *package )
5977 {
5978     static const WCHAR table[] = { 'O','D','B','C','D','r','i','v','e','r',0 };
5979     return msi_unimplemented_action_stub( package, "RemoveODBC", table );
5980 }
5981
5982 static UINT ACTION_RemoveRegistryValues( MSIPACKAGE *package )
5983 {
5984     static const WCHAR table[] = { 'R','e','m','o','v','e','R','e','g','i','s','t','r','y',0 };
5985     return msi_unimplemented_action_stub( package, "RemoveRegistryValues", table );
5986 }
5987
5988 static UINT ACTION_RemoveShortcuts( MSIPACKAGE *package )
5989 {
5990     static const WCHAR table[] = { 'S','h','o','r','t','c','u','t',0 };
5991     return msi_unimplemented_action_stub( package, "RemoveShortcuts", table );
5992 }
5993
5994 static UINT ACTION_UnpublishComponents( MSIPACKAGE *package )
5995 {
5996     static const WCHAR table[] = { 'P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t',0 };
5997     return msi_unimplemented_action_stub( package, "UnpublishComponents", table );
5998 }
5999
6000 static UINT ACTION_UnregisterClassInfo( MSIPACKAGE *package )
6001 {
6002     static const WCHAR table[] = { 'A','p','p','I','d',0 };
6003     return msi_unimplemented_action_stub( package, "UnregisterClassInfo", table );
6004 }
6005
6006 static UINT ACTION_UnregisterExtensionInfo( MSIPACKAGE *package )
6007 {
6008     static const WCHAR table[] = { 'E','x','t','e','n','s','i','o','n',0 };
6009     return msi_unimplemented_action_stub( package, "UnregisterExtensionInfo", table );
6010 }
6011
6012 static UINT ACTION_UnregisterMIMEInfo( MSIPACKAGE *package )
6013 {
6014     static const WCHAR table[] = { 'M','I','M','E',0 };
6015     return msi_unimplemented_action_stub( package, "UnregisterMIMEInfo", table );
6016 }
6017
6018 static UINT ACTION_UnregisterProgIdInfo( MSIPACKAGE *package )
6019 {
6020     static const WCHAR table[] = { 'P','r','o','g','I','d',0 };
6021     return msi_unimplemented_action_stub( package, "UnregisterProgIdInfo", table );
6022 }
6023
6024 static UINT ACTION_UnregisterTypeLibraries( MSIPACKAGE *package )
6025 {
6026     static const WCHAR table[] = { 'T','y','p','e','L','i','b',0 };
6027     return msi_unimplemented_action_stub( package, "UnregisterTypeLibraries", table );
6028 }
6029
6030 static const struct _actions StandardActions[] = {
6031     { szAllocateRegistrySpace, ACTION_AllocateRegistrySpace },
6032     { szAppSearch, ACTION_AppSearch },
6033     { szBindImage, ACTION_BindImage },
6034     { szCCPSearch, ACTION_CCPSearch },
6035     { szCostFinalize, ACTION_CostFinalize },
6036     { szCostInitialize, ACTION_CostInitialize },
6037     { szCreateFolders, ACTION_CreateFolders },
6038     { szCreateShortcuts, ACTION_CreateShortcuts },
6039     { szDeleteServices, ACTION_DeleteServices },
6040     { szDisableRollback, NULL },
6041     { szDuplicateFiles, ACTION_DuplicateFiles },
6042     { szExecuteAction, ACTION_ExecuteAction },
6043     { szFileCost, ACTION_FileCost },
6044     { szFindRelatedProducts, ACTION_FindRelatedProducts },
6045     { szForceReboot, ACTION_ForceReboot },
6046     { szInstallAdminPackage, NULL },
6047     { szInstallExecute, ACTION_InstallExecute },
6048     { szInstallExecuteAgain, ACTION_InstallExecute },
6049     { szInstallFiles, ACTION_InstallFiles},
6050     { szInstallFinalize, ACTION_InstallFinalize },
6051     { szInstallInitialize, ACTION_InstallInitialize },
6052     { szInstallSFPCatalogFile, ACTION_InstallSFPCatalogFile },
6053     { szInstallValidate, ACTION_InstallValidate },
6054     { szIsolateComponents, ACTION_IsolateComponents },
6055     { szLaunchConditions, ACTION_LaunchConditions },
6056     { szMigrateFeatureStates, ACTION_MigrateFeatureStates },
6057     { szMoveFiles, ACTION_MoveFiles },
6058     { szMsiPublishAssemblies, ACTION_MsiPublishAssemblies },
6059     { szMsiUnpublishAssemblies, ACTION_MsiUnpublishAssemblies },
6060     { szInstallODBC, ACTION_InstallODBC },
6061     { szInstallServices, ACTION_InstallServices },
6062     { szPatchFiles, ACTION_PatchFiles },
6063     { szProcessComponents, ACTION_ProcessComponents },
6064     { szPublishComponents, ACTION_PublishComponents },
6065     { szPublishFeatures, ACTION_PublishFeatures },
6066     { szPublishProduct, ACTION_PublishProduct },
6067     { szRegisterClassInfo, ACTION_RegisterClassInfo },
6068     { szRegisterComPlus, ACTION_RegisterComPlus},
6069     { szRegisterExtensionInfo, ACTION_RegisterExtensionInfo },
6070     { szRegisterFonts, ACTION_RegisterFonts },
6071     { szRegisterMIMEInfo, ACTION_RegisterMIMEInfo },
6072     { szRegisterProduct, ACTION_RegisterProduct },
6073     { szRegisterProgIdInfo, ACTION_RegisterProgIdInfo },
6074     { szRegisterTypeLibraries, ACTION_RegisterTypeLibraries },
6075     { szRegisterUser, ACTION_RegisterUser },
6076     { szRemoveDuplicateFiles, ACTION_RemoveDuplicateFiles },
6077     { szRemoveEnvironmentStrings, ACTION_RemoveEnvironmentStrings },
6078     { szRemoveExistingProducts, ACTION_RemoveExistingProducts },
6079     { szRemoveFiles, ACTION_RemoveFiles },
6080     { szRemoveFolders, ACTION_RemoveFolders },
6081     { szRemoveIniValues, ACTION_RemoveIniValues },
6082     { szRemoveODBC, ACTION_RemoveODBC },
6083     { szRemoveRegistryValues, ACTION_RemoveRegistryValues },
6084     { szRemoveShortcuts, ACTION_RemoveShortcuts },
6085     { szResolveSource, ACTION_ResolveSource },
6086     { szRMCCPSearch, ACTION_RMCCPSearch },
6087     { szScheduleReboot, NULL },
6088     { szSelfRegModules, ACTION_SelfRegModules },
6089     { szSelfUnregModules, ACTION_SelfUnregModules },
6090     { szSetODBCFolders, NULL },
6091     { szStartServices, ACTION_StartServices },
6092     { szStopServices, ACTION_StopServices },
6093     { szUnpublishComponents, ACTION_UnpublishComponents },
6094     { szUnpublishFeatures, ACTION_UnpublishFeatures },
6095     { szUnregisterClassInfo, ACTION_UnregisterClassInfo },
6096     { szUnregisterComPlus, ACTION_UnregisterComPlus },
6097     { szUnregisterExtensionInfo, ACTION_UnregisterExtensionInfo },
6098     { szUnregisterFonts, ACTION_UnregisterFonts },
6099     { szUnregisterMIMEInfo, ACTION_UnregisterMIMEInfo },
6100     { szUnregisterProgIdInfo, ACTION_UnregisterProgIdInfo },
6101     { szUnregisterTypeLibraries, ACTION_UnregisterTypeLibraries },
6102     { szValidateProductID, ACTION_ValidateProductID },
6103     { szWriteEnvironmentStrings, ACTION_WriteEnvironmentStrings },
6104     { szWriteIniValues, ACTION_WriteIniValues },
6105     { szWriteRegistryValues, ACTION_WriteRegistryValues },
6106     { NULL, NULL },
6107 };
6108
6109 static BOOL ACTION_HandleStandardAction(MSIPACKAGE *package, LPCWSTR action,
6110                                         UINT* rc, BOOL force )
6111 {
6112     BOOL ret = FALSE;
6113     BOOL run = force;
6114     int i;
6115
6116     if (!run && !package->script->CurrentlyScripting)
6117         run = TRUE;
6118
6119     if (!run)
6120     {
6121         if (strcmpW(action,szInstallFinalize) == 0 ||
6122             strcmpW(action,szInstallExecute) == 0 ||
6123             strcmpW(action,szInstallExecuteAgain) == 0)
6124                 run = TRUE;
6125     }
6126
6127     i = 0;
6128     while (StandardActions[i].action != NULL)
6129     {
6130         if (strcmpW(StandardActions[i].action, action)==0)
6131         {
6132             if (!run)
6133             {
6134                 ui_actioninfo(package, action, TRUE, 0);
6135                 *rc = schedule_action(package,INSTALL_SCRIPT,action);
6136                 ui_actioninfo(package, action, FALSE, *rc);
6137             }
6138             else
6139             {
6140                 ui_actionstart(package, action);
6141                 if (StandardActions[i].handler)
6142                 {
6143                     *rc = StandardActions[i].handler(package);
6144                 }
6145                 else
6146                 {
6147                     FIXME("unhandled standard action %s\n",debugstr_w(action));
6148                     *rc = ERROR_SUCCESS;
6149                 }
6150             }
6151             ret = TRUE;
6152             break;
6153         }
6154         i++;
6155     }
6156     return ret;
6157 }