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