crypt32/tests: Fix some test failures on Win9x.
[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     static const WCHAR reinstall[] = {'R','E','I','N','S','T','A','L','L',0};
1786     LPWSTR override;
1787     MSIFEATURE *feature;
1788
1789     override = msi_dup_property( package, property );
1790     if (!override)
1791         return FALSE;
1792
1793     LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1794     {
1795         if (lstrcmpW(property, remove) &&
1796             (feature->Level <= 0 || feature->Level > level))
1797             continue;
1798
1799         if (!strcmpW(property, reinstall)) state = feature->Installed;
1800
1801         if (strcmpiW(override,all)==0)
1802             msi_feature_set_state(package, feature, state);
1803         else
1804         {
1805             LPWSTR ptr = override;
1806             LPWSTR ptr2 = strchrW(override,',');
1807
1808             while (ptr)
1809             {
1810                 if ((ptr2 && strncmpW(ptr,feature->Feature, ptr2-ptr)==0)
1811                     || (!ptr2 && strcmpW(ptr,feature->Feature)==0))
1812                 {
1813                     msi_feature_set_state(package, feature, state);
1814                     break;
1815                 }
1816                 if (ptr2)
1817                 {
1818                     ptr=ptr2+1;
1819                     ptr2 = strchrW(ptr,',');
1820                 }
1821                 else
1822                     break;
1823             }
1824         }
1825     }
1826     msi_free(override);
1827
1828     return TRUE;
1829 }
1830
1831 UINT MSI_SetFeatureStates(MSIPACKAGE *package)
1832 {
1833     int level;
1834     static const WCHAR szlevel[] =
1835         {'I','N','S','T','A','L','L','L','E','V','E','L',0};
1836     static const WCHAR szAddLocal[] =
1837         {'A','D','D','L','O','C','A','L',0};
1838     static const WCHAR szAddSource[] =
1839         {'A','D','D','S','O','U','R','C','E',0};
1840     static const WCHAR szRemove[] =
1841         {'R','E','M','O','V','E',0};
1842     static const WCHAR szReinstall[] =
1843         {'R','E','I','N','S','T','A','L','L',0};
1844     BOOL override = FALSE;
1845     MSICOMPONENT* component;
1846     MSIFEATURE *feature;
1847
1848
1849     /* I do not know if this is where it should happen.. but */
1850
1851     TRACE("Checking Install Level\n");
1852
1853     level = msi_get_property_int(package, szlevel, 1);
1854
1855     /* ok here is the _real_ rub
1856      * all these activation/deactivation things happen in order and things
1857      * later on the list override things earlier on the list.
1858      * 1) INSTALLLEVEL processing
1859      * 2) ADDLOCAL
1860      * 3) REMOVE
1861      * 4) ADDSOURCE
1862      * 5) ADDDEFAULT
1863      * 6) REINSTALL
1864      * 7) COMPADDLOCAL
1865      * 8) COMPADDSOURCE
1866      * 9) FILEADDLOCAL
1867      * 10) FILEADDSOURCE
1868      * 11) FILEADDDEFAULT
1869      *
1870      * I am still ignoring a lot of these. But that is ok for now, ADDLOCAL and
1871      * REMOVE are the big ones, since we don't handle administrative installs
1872      * yet anyway.
1873      */
1874     override |= process_state_property(package, level, szAddLocal, INSTALLSTATE_LOCAL);
1875     override |= process_state_property(package, level, szRemove, INSTALLSTATE_ABSENT);
1876     override |= process_state_property(package, level, szAddSource, INSTALLSTATE_SOURCE);
1877     override |= process_state_property(package, level, szReinstall, INSTALLSTATE_UNKNOWN);
1878
1879     if (!override)
1880     {
1881         LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1882         {
1883             BOOL feature_state = ((feature->Level > 0) &&
1884                                   (feature->Level <= level));
1885
1886             if ((feature_state) && (feature->Action == INSTALLSTATE_UNKNOWN))
1887             {
1888                 if (feature->Attributes & msidbFeatureAttributesFavorSource)
1889                     msi_feature_set_state(package, feature, INSTALLSTATE_SOURCE);
1890                 else if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1891                     msi_feature_set_state(package, feature, INSTALLSTATE_ADVERTISED);
1892                 else
1893                     msi_feature_set_state(package, feature, INSTALLSTATE_LOCAL);
1894             }
1895         }
1896
1897         /* disable child features of unselected parent features */
1898         LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1899         {
1900             FeatureList *fl;
1901
1902             if (feature->Level > 0 && feature->Level <= level)
1903                 continue;
1904
1905             LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
1906                 msi_feature_set_state(package, fl->feature, INSTALLSTATE_UNKNOWN);
1907         }
1908     }
1909     else
1910     {
1911         /* set the Preselected Property */
1912         static const WCHAR szPreselected[] = {'P','r','e','s','e','l','e','c','t','e','d',0};
1913         static const WCHAR szOne[] = { '1', 0 };
1914
1915         MSI_SetPropertyW(package,szPreselected,szOne);
1916     }
1917
1918     /*
1919      * now we want to enable or disable components base on feature
1920      */
1921
1922     LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1923     {
1924         ComponentList *cl;
1925
1926         TRACE("Examining Feature %s (Level %i, Installed %i, Action %i)\n",
1927               debugstr_w(feature->Feature), feature->Level, feature->Installed, feature->Action);
1928
1929         if (!feature->Level)
1930             continue;
1931
1932         /* features with components that have compressed files are made local */
1933         LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1934         {
1935             if (cl->component->Enabled &&
1936                 cl->component->ForceLocalState &&
1937                 feature->Action == INSTALLSTATE_SOURCE)
1938             {
1939                 msi_feature_set_state(package, feature, INSTALLSTATE_LOCAL);
1940                 break;
1941             }
1942         }
1943
1944         LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1945         {
1946             component = cl->component;
1947
1948             if (!component->Enabled)
1949                 continue;
1950
1951             switch (feature->Action)
1952             {
1953             case INSTALLSTATE_ABSENT:
1954                 component->anyAbsent = 1;
1955                 break;
1956             case INSTALLSTATE_ADVERTISED:
1957                 component->hasAdvertiseFeature = 1;
1958                 break;
1959             case INSTALLSTATE_SOURCE:
1960                 component->hasSourceFeature = 1;
1961                 break;
1962             case INSTALLSTATE_LOCAL:
1963                 component->hasLocalFeature = 1;
1964                 break;
1965             case INSTALLSTATE_DEFAULT:
1966                 if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1967                     component->hasAdvertiseFeature = 1;
1968                 else if (feature->Attributes & msidbFeatureAttributesFavorSource)
1969                     component->hasSourceFeature = 1;
1970                 else
1971                     component->hasLocalFeature = 1;
1972                 break;
1973             default:
1974                 break;
1975             }
1976         }
1977     }
1978
1979     LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1980     {
1981         /* if the component isn't enabled, leave it alone */
1982         if (!component->Enabled)
1983             continue;
1984
1985         /* check if it's local or source */
1986         if (!(component->Attributes & msidbComponentAttributesOptional) &&
1987              (component->hasLocalFeature || component->hasSourceFeature))
1988         {
1989             if ((component->Attributes & msidbComponentAttributesSourceOnly) &&
1990                  !component->ForceLocalState)
1991                 msi_component_set_state(package, component, INSTALLSTATE_SOURCE);
1992             else
1993                 msi_component_set_state(package, component, INSTALLSTATE_LOCAL);
1994             continue;
1995         }
1996
1997         /* if any feature is local, the component must be local too */
1998         if (component->hasLocalFeature)
1999         {
2000             msi_component_set_state(package, component, INSTALLSTATE_LOCAL);
2001             continue;
2002         }
2003
2004         if (component->hasSourceFeature)
2005         {
2006             msi_component_set_state(package, component, INSTALLSTATE_SOURCE);
2007             continue;
2008         }
2009
2010         if (component->hasAdvertiseFeature)
2011         {
2012             msi_component_set_state(package, component, INSTALLSTATE_ADVERTISED);
2013             continue;
2014         }
2015
2016         TRACE("nobody wants component %s\n", debugstr_w(component->Component));
2017         if (component->anyAbsent)
2018             msi_component_set_state(package, component, INSTALLSTATE_ABSENT);
2019     }
2020
2021     LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
2022     {
2023         if (component->Action == INSTALLSTATE_DEFAULT)
2024         {
2025             TRACE("%s was default, setting to local\n", debugstr_w(component->Component));
2026             msi_component_set_state(package, component, INSTALLSTATE_LOCAL);
2027         }
2028
2029         TRACE("Result: Component %s (Installed %i, Action %i)\n",
2030             debugstr_w(component->Component), component->Installed, component->Action);
2031     }
2032
2033
2034     return ERROR_SUCCESS;
2035 }
2036
2037 static UINT ITERATE_CostFinalizeDirectories(MSIRECORD *row, LPVOID param)
2038 {
2039     MSIPACKAGE *package = param;
2040     LPCWSTR name;
2041     LPWSTR path;
2042     MSIFOLDER *f;
2043
2044     name = MSI_RecordGetString(row,1);
2045
2046     f = get_loaded_folder(package, name);
2047     if (!f) return ERROR_SUCCESS;
2048
2049     /* reset the ResolvedTarget */
2050     msi_free(f->ResolvedTarget);
2051     f->ResolvedTarget = NULL;
2052
2053     /* This helper function now does ALL the work */
2054     TRACE("Dir %s ...\n",debugstr_w(name));
2055     path = resolve_folder(package,name,FALSE,TRUE,TRUE,NULL);
2056     TRACE("resolves to %s\n",debugstr_w(path));
2057     msi_free(path);
2058
2059     return ERROR_SUCCESS;
2060 }
2061
2062 static UINT ITERATE_CostFinalizeConditions(MSIRECORD *row, LPVOID param)
2063 {
2064     MSIPACKAGE *package = param;
2065     LPCWSTR name;
2066     MSIFEATURE *feature;
2067
2068     name = MSI_RecordGetString( row, 1 );
2069
2070     feature = get_loaded_feature( package, name );
2071     if (!feature)
2072         ERR("FAILED to find loaded feature %s\n",debugstr_w(name));
2073     else
2074     {
2075         LPCWSTR Condition;
2076         Condition = MSI_RecordGetString(row,3);
2077
2078         if (MSI_EvaluateConditionW(package,Condition) == MSICONDITION_TRUE)
2079         {
2080             int level = MSI_RecordGetInteger(row,2);
2081             TRACE("Resetting feature %s to level %i\n", debugstr_w(name), level);
2082             feature->Level = level;
2083         }
2084     }
2085     return ERROR_SUCCESS;
2086 }
2087
2088 static LPWSTR msi_get_disk_file_version( LPCWSTR filename )
2089 {
2090     static const WCHAR name_fmt[] =
2091         {'%','u','.','%','u','.','%','u','.','%','u',0};
2092     static const WCHAR name[] = {'\\',0};
2093     VS_FIXEDFILEINFO *lpVer;
2094     WCHAR filever[0x100];
2095     LPVOID version;
2096     DWORD versize;
2097     DWORD handle;
2098     UINT sz;
2099
2100     TRACE("%s\n", debugstr_w(filename));
2101
2102     versize = GetFileVersionInfoSizeW( filename, &handle );
2103     if (!versize)
2104         return NULL;
2105
2106     version = msi_alloc( versize );
2107     GetFileVersionInfoW( filename, 0, versize, version );
2108
2109     if (!VerQueryValueW( version, name, (LPVOID*)&lpVer, &sz ))
2110     {
2111         msi_free( version );
2112         return NULL;
2113     }
2114
2115     sprintfW( filever, name_fmt,
2116         HIWORD(lpVer->dwFileVersionMS),
2117         LOWORD(lpVer->dwFileVersionMS),
2118         HIWORD(lpVer->dwFileVersionLS),
2119         LOWORD(lpVer->dwFileVersionLS));
2120
2121     msi_free( version );
2122
2123     return strdupW( filever );
2124 }
2125
2126 static UINT msi_check_file_install_states( MSIPACKAGE *package )
2127 {
2128     LPWSTR file_version;
2129     MSIFILE *file;
2130
2131     LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2132     {
2133         MSICOMPONENT* comp = file->Component;
2134         LPWSTR p;
2135
2136         if (!comp)
2137             continue;
2138
2139         if (file->IsCompressed)
2140             comp->ForceLocalState = TRUE;
2141
2142         /* calculate target */
2143         p = resolve_folder(package, comp->Directory, FALSE, FALSE, TRUE, NULL);
2144
2145         msi_free(file->TargetPath);
2146
2147         TRACE("file %s is named %s\n",
2148                debugstr_w(file->File), debugstr_w(file->FileName));
2149
2150         file->TargetPath = build_directory_name(2, p, file->FileName);
2151
2152         msi_free(p);
2153
2154         TRACE("file %s resolves to %s\n",
2155                debugstr_w(file->File), debugstr_w(file->TargetPath));
2156
2157         /* don't check files of components that aren't installed */
2158         if (comp->Installed == INSTALLSTATE_UNKNOWN ||
2159             comp->Installed == INSTALLSTATE_ABSENT)
2160         {
2161             file->state = msifs_missing;  /* assume files are missing */
2162             continue;
2163         }
2164
2165         if (GetFileAttributesW(file->TargetPath) == INVALID_FILE_ATTRIBUTES)
2166         {
2167             file->state = msifs_missing;
2168             comp->Cost += file->FileSize;
2169             continue;
2170         }
2171
2172         if (file->Version &&
2173             (file_version = msi_get_disk_file_version( file->TargetPath )))
2174         {
2175             TRACE("new %s old %s\n", debugstr_w(file->Version),
2176                   debugstr_w(file_version));
2177             /* FIXME: seems like a bad way to compare version numbers */
2178             if (lstrcmpiW(file_version, file->Version)<0)
2179             {
2180                 file->state = msifs_overwrite;
2181                 comp->Cost += file->FileSize;
2182             }
2183             else
2184                 file->state = msifs_present;
2185             msi_free( file_version );
2186         }
2187         else
2188             file->state = msifs_present;
2189     }
2190
2191     return ERROR_SUCCESS;
2192 }
2193
2194 /*
2195  * A lot is done in this function aside from just the costing.
2196  * The costing needs to be implemented at some point but for now I am going
2197  * to focus on the directory building
2198  *
2199  */
2200 static UINT ACTION_CostFinalize(MSIPACKAGE *package)
2201 {
2202     static const WCHAR ExecSeqQuery[] =
2203         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2204          '`','D','i','r','e','c','t','o','r','y','`',0};
2205     static const WCHAR ConditionQuery[] =
2206         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2207          '`','C','o','n','d','i','t','i','o','n','`',0};
2208     static const WCHAR szCosting[] =
2209         {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
2210     static const WCHAR szlevel[] =
2211         {'I','N','S','T','A','L','L','L','E','V','E','L',0};
2212     static const WCHAR szOutOfDiskSpace[] =
2213         {'O','u','t','O','f','D','i','s','k','S','p','a','c','e',0};
2214     static const WCHAR szOne[] = { '1', 0 };
2215     static const WCHAR szZero[] = { '0', 0 };
2216     MSICOMPONENT *comp;
2217     UINT rc;
2218     MSIQUERY * view;
2219     LPWSTR level;
2220
2221     TRACE("Building Directory properties\n");
2222
2223     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2224     if (rc == ERROR_SUCCESS)
2225     {
2226         rc = MSI_IterateRecords(view, NULL, ITERATE_CostFinalizeDirectories,
2227                         package);
2228         msiobj_release(&view->hdr);
2229     }
2230
2231     /* read components states from the registry */
2232     ACTION_GetComponentInstallStates(package);
2233     ACTION_GetFeatureInstallStates(package);
2234
2235     TRACE("File calculations\n");
2236     msi_check_file_install_states( package );
2237
2238     TRACE("Evaluating Condition Table\n");
2239
2240     rc = MSI_DatabaseOpenViewW(package->db, ConditionQuery, &view);
2241     if (rc == ERROR_SUCCESS)
2242     {
2243         rc = MSI_IterateRecords(view, NULL, ITERATE_CostFinalizeConditions,
2244                     package);
2245         msiobj_release(&view->hdr);
2246     }
2247
2248     TRACE("Enabling or Disabling Components\n");
2249     LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2250     {
2251         if (MSI_EvaluateConditionW(package, comp->Condition) == MSICONDITION_FALSE)
2252         {
2253             TRACE("Disabling component %s\n", debugstr_w(comp->Component));
2254             comp->Enabled = FALSE;
2255         }
2256         else
2257             comp->Enabled = TRUE;
2258     }
2259
2260     MSI_SetPropertyW(package,szCosting,szOne);
2261     /* set default run level if not set */
2262     level = msi_dup_property( package, szlevel );
2263     if (!level)
2264         MSI_SetPropertyW(package,szlevel, szOne);
2265     msi_free(level);
2266
2267     /* FIXME: check volume disk space */
2268     MSI_SetPropertyW(package, szOutOfDiskSpace, szZero);
2269
2270     return MSI_SetFeatureStates(package);
2271 }
2272
2273 /* OK this value is "interpreted" and then formatted based on the 
2274    first few characters */
2275 static LPSTR parse_value(MSIPACKAGE *package, LPCWSTR value, DWORD *type, 
2276                          DWORD *size)
2277 {
2278     LPSTR data = NULL;
2279
2280     if (value[0]=='#' && value[1]!='#' && value[1]!='%')
2281     {
2282         if (value[1]=='x')
2283         {
2284             LPWSTR ptr;
2285             CHAR byte[5];
2286             LPWSTR deformated = NULL;
2287             int count;
2288
2289             deformat_string(package, &value[2], &deformated);
2290
2291             /* binary value type */
2292             ptr = deformated;
2293             *type = REG_BINARY;
2294             if (strlenW(ptr)%2)
2295                 *size = (strlenW(ptr)/2)+1;
2296             else
2297                 *size = strlenW(ptr)/2;
2298
2299             data = msi_alloc(*size);
2300
2301             byte[0] = '0'; 
2302             byte[1] = 'x'; 
2303             byte[4] = 0; 
2304             count = 0;
2305             /* if uneven pad with a zero in front */
2306             if (strlenW(ptr)%2)
2307             {
2308                 byte[2]= '0';
2309                 byte[3]= *ptr;
2310                 ptr++;
2311                 data[count] = (BYTE)strtol(byte,NULL,0);
2312                 count ++;
2313                 TRACE("Uneven byte count\n");
2314             }
2315             while (*ptr)
2316             {
2317                 byte[2]= *ptr;
2318                 ptr++;
2319                 byte[3]= *ptr;
2320                 ptr++;
2321                 data[count] = (BYTE)strtol(byte,NULL,0);
2322                 count ++;
2323             }
2324             msi_free(deformated);
2325
2326             TRACE("Data %i bytes(%i)\n",*size,count);
2327         }
2328         else
2329         {
2330             LPWSTR deformated;
2331             LPWSTR p;
2332             DWORD d = 0;
2333             deformat_string(package, &value[1], &deformated);
2334
2335             *type=REG_DWORD; 
2336             *size = sizeof(DWORD);
2337             data = msi_alloc(*size);
2338             p = deformated;
2339             if (*p == '-')
2340                 p++;
2341             while (*p)
2342             {
2343                 if ( (*p < '0') || (*p > '9') )
2344                     break;
2345                 d *= 10;
2346                 d += (*p - '0');
2347                 p++;
2348             }
2349             if (deformated[0] == '-')
2350                 d = -d;
2351             *(LPDWORD)data = d;
2352             TRACE("DWORD %i\n",*(LPDWORD)data);
2353
2354             msi_free(deformated);
2355         }
2356     }
2357     else
2358     {
2359         static const WCHAR szMulti[] = {'[','~',']',0};
2360         LPCWSTR ptr;
2361         *type=REG_SZ;
2362
2363         if (value[0]=='#')
2364         {
2365             if (value[1]=='%')
2366             {
2367                 ptr = &value[2];
2368                 *type=REG_EXPAND_SZ;
2369             }
2370             else
2371                 ptr = &value[1];
2372          }
2373          else
2374             ptr=value;
2375
2376         if (strstrW(value,szMulti))
2377             *type = REG_MULTI_SZ;
2378
2379         /* remove initial delimiter */
2380         if (!strncmpW(value, szMulti, 3))
2381             ptr = value + 3;
2382
2383         *size = deformat_string(package, ptr,(LPWSTR*)&data);
2384
2385         /* add double NULL terminator */
2386         if (*type == REG_MULTI_SZ)
2387         {
2388             *size += 2 * sizeof(WCHAR); /* two NULL terminators */
2389             data = msi_realloc_zero(data, *size);
2390         }
2391     }
2392     return data;
2393 }
2394
2395 static UINT ITERATE_WriteRegistryValues(MSIRECORD *row, LPVOID param)
2396 {
2397     MSIPACKAGE *package = param;
2398     static const WCHAR szHCR[] = 
2399         {'H','K','E','Y','_','C','L','A','S','S','E','S','_',
2400          'R','O','O','T','\\',0};
2401     static const WCHAR szHCU[] =
2402         {'H','K','E','Y','_','C','U','R','R','E','N','T','_',
2403          'U','S','E','R','\\',0};
2404     static const WCHAR szHLM[] =
2405         {'H','K','E','Y','_','L','O','C','A','L','_',
2406          'M','A','C','H','I','N','E','\\',0};
2407     static const WCHAR szHU[] =
2408         {'H','K','E','Y','_','U','S','E','R','S','\\',0};
2409
2410     LPSTR value_data = NULL;
2411     HKEY  root_key, hkey;
2412     DWORD type,size;
2413     LPWSTR  deformated;
2414     LPCWSTR szRoot, component, name, key, value;
2415     MSICOMPONENT *comp;
2416     MSIRECORD * uirow;
2417     LPWSTR uikey;
2418     INT   root;
2419     BOOL check_first = FALSE;
2420     UINT rc;
2421
2422     ui_progress(package,2,0,0,0);
2423
2424     value = NULL;
2425     key = NULL;
2426     uikey = NULL;
2427     name = NULL;
2428
2429     component = MSI_RecordGetString(row, 6);
2430     comp = get_loaded_component(package,component);
2431     if (!comp)
2432         return ERROR_SUCCESS;
2433
2434     if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
2435     {
2436         TRACE("Skipping write due to disabled component %s\n",
2437                         debugstr_w(component));
2438
2439         comp->Action = comp->Installed;
2440
2441         return ERROR_SUCCESS;
2442     }
2443
2444     comp->Action = INSTALLSTATE_LOCAL;
2445
2446     name = MSI_RecordGetString(row, 4);
2447     if( MSI_RecordIsNull(row,5) && name )
2448     {
2449         /* null values can have special meanings */
2450         if (name[0]=='-' && name[1] == 0)
2451                 return ERROR_SUCCESS;
2452         else if ((name[0]=='+' && name[1] == 0) || 
2453                  (name[0] == '*' && name[1] == 0))
2454                 name = NULL;
2455         check_first = TRUE;
2456     }
2457
2458     root = MSI_RecordGetInteger(row,2);
2459     key = MSI_RecordGetString(row, 3);
2460
2461     /* get the root key */
2462     switch (root)
2463     {
2464         case -1: 
2465             {
2466                 static const WCHAR szALLUSER[] = {'A','L','L','U','S','E','R','S',0};
2467                 LPWSTR all_users = msi_dup_property( package, szALLUSER );
2468                 if (all_users && all_users[0] == '1')
2469                 {
2470                     root_key = HKEY_LOCAL_MACHINE;
2471                     szRoot = szHLM;
2472                 }
2473                 else
2474                 {
2475                     root_key = HKEY_CURRENT_USER;
2476                     szRoot = szHCU;
2477                 }
2478                 msi_free(all_users);
2479             }
2480                  break;
2481         case 0:  root_key = HKEY_CLASSES_ROOT; 
2482                  szRoot = szHCR;
2483                  break;
2484         case 1:  root_key = HKEY_CURRENT_USER;
2485                  szRoot = szHCU;
2486                  break;
2487         case 2:  root_key = HKEY_LOCAL_MACHINE;
2488                  szRoot = szHLM;
2489                  break;
2490         case 3:  root_key = HKEY_USERS; 
2491                  szRoot = szHU;
2492                  break;
2493         default:
2494                  ERR("Unknown root %i\n",root);
2495                  root_key=NULL;
2496                  szRoot = NULL;
2497                  break;
2498     }
2499     if (!root_key)
2500         return ERROR_SUCCESS;
2501
2502     deformat_string(package, key , &deformated);
2503     size = strlenW(deformated) + strlenW(szRoot) + 1;
2504     uikey = msi_alloc(size*sizeof(WCHAR));
2505     strcpyW(uikey,szRoot);
2506     strcatW(uikey,deformated);
2507
2508     if (RegCreateKeyW( root_key, deformated, &hkey))
2509     {
2510         ERR("Could not create key %s\n",debugstr_w(deformated));
2511         msi_free(deformated);
2512         msi_free(uikey);
2513         return ERROR_SUCCESS;
2514     }
2515     msi_free(deformated);
2516
2517     value = MSI_RecordGetString(row,5);
2518     if (value)
2519         value_data = parse_value(package, value, &type, &size); 
2520     else
2521     {
2522         static const WCHAR szEmpty[] = {0};
2523         value_data = (LPSTR)strdupW(szEmpty);
2524         size = sizeof(szEmpty);
2525         type = REG_SZ;
2526     }
2527
2528     deformat_string(package, name, &deformated);
2529
2530     if (!check_first)
2531     {
2532         TRACE("Setting value %s of %s\n",debugstr_w(deformated),
2533                         debugstr_w(uikey));
2534         RegSetValueExW(hkey, deformated, 0, type, (LPBYTE)value_data, size);
2535     }
2536     else
2537     {
2538         DWORD sz = 0;
2539         rc = RegQueryValueExW(hkey, deformated, NULL, NULL, NULL, &sz);
2540         if (rc == ERROR_SUCCESS || rc == ERROR_MORE_DATA)
2541         {
2542             TRACE("value %s of %s checked already exists\n",
2543                             debugstr_w(deformated), debugstr_w(uikey));
2544         }
2545         else
2546         {
2547             TRACE("Checked and setting value %s of %s\n",
2548                             debugstr_w(deformated), debugstr_w(uikey));
2549             if (deformated || size)
2550                 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE) value_data, size);
2551         }
2552     }
2553     RegCloseKey(hkey);
2554
2555     uirow = MSI_CreateRecord(3);
2556     MSI_RecordSetStringW(uirow,2,deformated);
2557     MSI_RecordSetStringW(uirow,1,uikey);
2558
2559     if (type == REG_SZ)
2560         MSI_RecordSetStringW(uirow,3,(LPWSTR)value_data);
2561     else
2562         MSI_RecordSetStringW(uirow,3,value);
2563
2564     ui_actiondata(package,szWriteRegistryValues,uirow);
2565     msiobj_release( &uirow->hdr );
2566
2567     msi_free(value_data);
2568     msi_free(deformated);
2569     msi_free(uikey);
2570
2571     return ERROR_SUCCESS;
2572 }
2573
2574 static UINT ACTION_WriteRegistryValues(MSIPACKAGE *package)
2575 {
2576     UINT rc;
2577     MSIQUERY * view;
2578     static const WCHAR ExecSeqQuery[] =
2579         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2580          '`','R','e','g','i','s','t','r','y','`',0 };
2581
2582     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2583     if (rc != ERROR_SUCCESS)
2584         return ERROR_SUCCESS;
2585
2586     /* increment progress bar each time action data is sent */
2587     ui_progress(package,1,REG_PROGRESS_VALUE,1,0);
2588
2589     rc = MSI_IterateRecords(view, NULL, ITERATE_WriteRegistryValues, package);
2590
2591     msiobj_release(&view->hdr);
2592     return rc;
2593 }
2594
2595 static UINT ACTION_InstallInitialize(MSIPACKAGE *package)
2596 {
2597     package->script->CurrentlyScripting = TRUE;
2598
2599     return ERROR_SUCCESS;
2600 }
2601
2602
2603 static UINT ACTION_InstallValidate(MSIPACKAGE *package)
2604 {
2605     MSICOMPONENT *comp;
2606     DWORD progress = 0;
2607     DWORD total = 0;
2608     static const WCHAR q1[]=
2609         {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
2610          '`','R','e','g','i','s','t','r','y','`',0};
2611     UINT rc;
2612     MSIQUERY * view;
2613     MSIFEATURE *feature;
2614     MSIFILE *file;
2615
2616     TRACE("InstallValidate\n");
2617
2618     rc = MSI_DatabaseOpenViewW(package->db, q1, &view);
2619     if (rc == ERROR_SUCCESS)
2620     {
2621         MSI_IterateRecords( view, &progress, NULL, package );
2622         msiobj_release( &view->hdr );
2623         total += progress * REG_PROGRESS_VALUE;
2624     }
2625
2626     LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2627         total += COMPONENT_PROGRESS_VALUE;
2628
2629     LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2630         total += file->FileSize;
2631
2632     ui_progress(package,0,total,0,0);
2633
2634     LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2635     {
2636         TRACE("Feature: %s; Installed: %i; Action %i; Request %i\n",
2637             debugstr_w(feature->Feature), feature->Installed, feature->Action,
2638             feature->ActionRequest);
2639     }
2640     
2641     return ERROR_SUCCESS;
2642 }
2643
2644 static UINT ITERATE_LaunchConditions(MSIRECORD *row, LPVOID param)
2645 {
2646     MSIPACKAGE* package = param;
2647     LPCWSTR cond = NULL; 
2648     LPCWSTR message = NULL;
2649     UINT r;
2650
2651     static const WCHAR title[]=
2652         {'I','n','s','t','a','l','l',' ','F','a', 'i','l','e','d',0};
2653
2654     cond = MSI_RecordGetString(row,1);
2655
2656     r = MSI_EvaluateConditionW(package,cond);
2657     if (r == MSICONDITION_FALSE)
2658     {
2659         if ((gUILevel & INSTALLUILEVEL_MASK) != INSTALLUILEVEL_NONE)
2660         {
2661             LPWSTR deformated;
2662             message = MSI_RecordGetString(row,2);
2663             deformat_string(package,message,&deformated);
2664             MessageBoxW(NULL,deformated,title,MB_OK);
2665             msi_free(deformated);
2666         }
2667
2668         return ERROR_INSTALL_FAILURE;
2669     }
2670
2671     return ERROR_SUCCESS;
2672 }
2673
2674 static UINT ACTION_LaunchConditions(MSIPACKAGE *package)
2675 {
2676     UINT rc;
2677     MSIQUERY * view = NULL;
2678     static const WCHAR ExecSeqQuery[] =
2679         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2680          '`','L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','`',0};
2681
2682     TRACE("Checking launch conditions\n");
2683
2684     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2685     if (rc != ERROR_SUCCESS)
2686         return ERROR_SUCCESS;
2687
2688     rc = MSI_IterateRecords(view, NULL, ITERATE_LaunchConditions, package);
2689     msiobj_release(&view->hdr);
2690
2691     return rc;
2692 }
2693
2694 static LPWSTR resolve_keypath( MSIPACKAGE* package, MSICOMPONENT *cmp )
2695 {
2696
2697     if (!cmp->KeyPath)
2698         return resolve_folder(package,cmp->Directory,FALSE,FALSE,TRUE,NULL);
2699
2700     if (cmp->Attributes & msidbComponentAttributesRegistryKeyPath)
2701     {
2702         MSIRECORD * row = 0;
2703         UINT root,len;
2704         LPWSTR deformated,buffer,deformated_name;
2705         LPCWSTR key,name;
2706         static const WCHAR ExecSeqQuery[] =
2707             {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2708              '`','R','e','g','i','s','t','r','y','`',' ',
2709              'W','H','E','R','E',' ', '`','R','e','g','i','s','t','r','y','`',
2710              ' ','=',' ' ,'\'','%','s','\'',0 };
2711         static const WCHAR fmt[]={'%','0','2','i',':','\\','%','s','\\',0};
2712         static const WCHAR fmt2[]=
2713             {'%','0','2','i',':','\\','%','s','\\','%','s',0};
2714
2715         row = MSI_QueryGetRecord(package->db, ExecSeqQuery,cmp->KeyPath);
2716         if (!row)
2717             return NULL;
2718
2719         root = MSI_RecordGetInteger(row,2);
2720         key = MSI_RecordGetString(row, 3);
2721         name = MSI_RecordGetString(row, 4);
2722         deformat_string(package, key , &deformated);
2723         deformat_string(package, name, &deformated_name);
2724
2725         len = strlenW(deformated) + 6;
2726         if (deformated_name)
2727             len+=strlenW(deformated_name);
2728
2729         buffer = msi_alloc( len *sizeof(WCHAR));
2730
2731         if (deformated_name)
2732             sprintfW(buffer,fmt2,root,deformated,deformated_name);
2733         else
2734             sprintfW(buffer,fmt,root,deformated);
2735
2736         msi_free(deformated);
2737         msi_free(deformated_name);
2738         msiobj_release(&row->hdr);
2739
2740         return buffer;
2741     }
2742     else if (cmp->Attributes & msidbComponentAttributesODBCDataSource)
2743     {
2744         FIXME("UNIMPLEMENTED keypath as ODBC Source\n");
2745         return NULL;
2746     }
2747     else
2748     {
2749         MSIFILE *file = get_loaded_file( package, cmp->KeyPath );
2750
2751         if (file)
2752             return strdupW( file->TargetPath );
2753     }
2754     return NULL;
2755 }
2756
2757 static HKEY openSharedDLLsKey(void)
2758 {
2759     HKEY hkey=0;
2760     static const WCHAR path[] =
2761         {'S','o','f','t','w','a','r','e','\\',
2762          'M','i','c','r','o','s','o','f','t','\\',
2763          'W','i','n','d','o','w','s','\\',
2764          'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
2765          'S','h','a','r','e','d','D','L','L','s',0};
2766
2767     RegCreateKeyW(HKEY_LOCAL_MACHINE,path,&hkey);
2768     return hkey;
2769 }
2770
2771 static UINT ACTION_GetSharedDLLsCount(LPCWSTR dll)
2772 {
2773     HKEY hkey;
2774     DWORD count=0;
2775     DWORD type;
2776     DWORD sz = sizeof(count);
2777     DWORD rc;
2778     
2779     hkey = openSharedDLLsKey();
2780     rc = RegQueryValueExW(hkey, dll, NULL, &type, (LPBYTE)&count, &sz);
2781     if (rc != ERROR_SUCCESS)
2782         count = 0;
2783     RegCloseKey(hkey);
2784     return count;
2785 }
2786
2787 static UINT ACTION_WriteSharedDLLsCount(LPCWSTR path, UINT count)
2788 {
2789     HKEY hkey;
2790
2791     hkey = openSharedDLLsKey();
2792     if (count > 0)
2793         msi_reg_set_val_dword( hkey, path, count );
2794     else
2795         RegDeleteValueW(hkey,path);
2796     RegCloseKey(hkey);
2797     return count;
2798 }
2799
2800 /*
2801  * Return TRUE if the count should be written out and FALSE if not
2802  */
2803 static void ACTION_RefCountComponent( MSIPACKAGE* package, MSICOMPONENT *comp )
2804 {
2805     MSIFEATURE *feature;
2806     INT count = 0;
2807     BOOL write = FALSE;
2808
2809     /* only refcount DLLs */
2810     if (comp->KeyPath == NULL || 
2811         comp->Attributes & msidbComponentAttributesRegistryKeyPath || 
2812         comp->Attributes & msidbComponentAttributesODBCDataSource)
2813         write = FALSE;
2814     else
2815     {
2816         count = ACTION_GetSharedDLLsCount( comp->FullKeypath);
2817         write = (count > 0);
2818
2819         if (comp->Attributes & msidbComponentAttributesSharedDllRefCount)
2820             write = TRUE;
2821     }
2822
2823     /* increment counts */
2824     LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2825     {
2826         ComponentList *cl;
2827
2828         if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_LOCAL ))
2829             continue;
2830
2831         LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
2832         {
2833             if ( cl->component == comp )
2834                 count++;
2835         }
2836     }
2837
2838     /* decrement counts */
2839     LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2840     {
2841         ComponentList *cl;
2842
2843         if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_ABSENT ))
2844             continue;
2845
2846         LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
2847         {
2848             if ( cl->component == comp )
2849                 count--;
2850         }
2851     }
2852
2853     /* ref count all the files in the component */
2854     if (write)
2855     {
2856         MSIFILE *file;
2857
2858         LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2859         {
2860             if (file->Component == comp)
2861                 ACTION_WriteSharedDLLsCount( file->TargetPath, count );
2862         }
2863     }
2864     
2865     /* add a count for permanent */
2866     if (comp->Attributes & msidbComponentAttributesPermanent)
2867         count ++;
2868     
2869     comp->RefCount = count;
2870
2871     if (write)
2872         ACTION_WriteSharedDLLsCount( comp->FullKeypath, comp->RefCount );
2873 }
2874
2875 static UINT ACTION_ProcessComponents(MSIPACKAGE *package)
2876 {
2877     WCHAR squished_pc[GUID_SIZE];
2878     WCHAR squished_cc[GUID_SIZE];
2879     UINT rc;
2880     MSICOMPONENT *comp;
2881     HKEY hkey;
2882
2883     TRACE("\n");
2884
2885     squash_guid(package->ProductCode,squished_pc);
2886     ui_progress(package,1,COMPONENT_PROGRESS_VALUE,1,0);
2887
2888     LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2889     {
2890         MSIRECORD * uirow;
2891
2892         ui_progress(package,2,0,0,0);
2893         if (!comp->ComponentId)
2894             continue;
2895
2896         squash_guid(comp->ComponentId,squished_cc);
2897
2898         msi_free(comp->FullKeypath);
2899         comp->FullKeypath = resolve_keypath( package, comp );
2900
2901         ACTION_RefCountComponent( package, comp );
2902
2903         TRACE("Component %s (%s), Keypath=%s, RefCount=%i\n",
2904                             debugstr_w(comp->Component),
2905                             debugstr_w(squished_cc),
2906                             debugstr_w(comp->FullKeypath),
2907                             comp->RefCount);
2908
2909         if (ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL) ||
2910             ACTION_VerifyComponentForAction( comp, INSTALLSTATE_SOURCE))
2911         {
2912             if (!comp->FullKeypath)
2913                 continue;
2914
2915             if (package->Context == MSIINSTALLCONTEXT_MACHINE)
2916                 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, szLocalSid,
2917                                                      &hkey, TRUE);
2918             else
2919                 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, NULL,
2920                                                      &hkey, TRUE);
2921
2922             if (rc != ERROR_SUCCESS)
2923                 continue;
2924
2925             if (comp->Attributes & msidbComponentAttributesPermanent)
2926             {
2927                 static const WCHAR szPermKey[] =
2928                     { '0','0','0','0','0','0','0','0','0','0','0','0',
2929                       '0','0','0','0','0','0','0','0','0','0','0','0',
2930                       '0','0','0','0','0','0','0','0',0 };
2931
2932                 msi_reg_set_val_str(hkey, szPermKey, comp->FullKeypath);
2933             }
2934
2935             if (comp->Action == INSTALLSTATE_LOCAL)
2936                 msi_reg_set_val_str(hkey, squished_pc, comp->FullKeypath);
2937             else
2938             {
2939                 MSIFILE *file;
2940                 MSIRECORD *row;
2941                 LPWSTR ptr, ptr2;
2942                 WCHAR source[MAX_PATH];
2943                 WCHAR base[MAX_PATH];
2944                 LPWSTR sourcepath;
2945
2946                 static const WCHAR fmt[] = {'%','0','2','d','\\',0};
2947                 static const WCHAR query[] = {
2948                     'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
2949                     '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
2950                     '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ',
2951                     '>','=',' ','%','i',' ','O','R','D','E','R',' ','B','Y',' ',
2952                     '`','D','i','s','k','I','d','`',0};
2953
2954                 file = get_loaded_file(package, comp->KeyPath);
2955                 if (!file)
2956                     continue;
2957
2958                 row = MSI_QueryGetRecord(package->db, query, file->Sequence);
2959                 sprintfW(source, fmt, MSI_RecordGetInteger(row, 1));
2960                 ptr2 = strrchrW(source, '\\') + 1;
2961                 msiobj_release(&row->hdr);
2962
2963                 lstrcpyW(base, package->PackagePath);
2964                 ptr = strrchrW(base, '\\');
2965                 *(ptr + 1) = '\0';
2966
2967                 sourcepath = resolve_file_source(package, file);
2968                 ptr = sourcepath + lstrlenW(base);
2969                 lstrcpyW(ptr2, ptr);
2970                 msi_free(sourcepath);
2971
2972                 msi_reg_set_val_str(hkey, squished_pc, source);
2973             }
2974             RegCloseKey(hkey);
2975         }
2976         else if (ACTION_VerifyComponentForAction(comp, INSTALLSTATE_ABSENT))
2977         {
2978             if (package->Context == MSIINSTALLCONTEXT_MACHINE)
2979                 MSIREG_DeleteUserDataComponentKey(comp->ComponentId, szLocalSid);
2980             else
2981                 MSIREG_DeleteUserDataComponentKey(comp->ComponentId, NULL);
2982         }
2983
2984         /* UI stuff */
2985         uirow = MSI_CreateRecord(3);
2986         MSI_RecordSetStringW(uirow,1,package->ProductCode);
2987         MSI_RecordSetStringW(uirow,2,comp->ComponentId);
2988         MSI_RecordSetStringW(uirow,3,comp->FullKeypath);
2989         ui_actiondata(package,szProcessComponents,uirow);
2990         msiobj_release( &uirow->hdr );
2991     }
2992
2993     return ERROR_SUCCESS;
2994 }
2995
2996 typedef struct {
2997     CLSID       clsid;
2998     LPWSTR      source;
2999
3000     LPWSTR      path;
3001     ITypeLib    *ptLib;
3002 } typelib_struct;
3003
3004 static BOOL CALLBACK Typelib_EnumResNameProc( HMODULE hModule, LPCWSTR lpszType, 
3005                                        LPWSTR lpszName, LONG_PTR lParam)
3006 {
3007     TLIBATTR *attr;
3008     typelib_struct *tl_struct = (typelib_struct*) lParam;
3009     static const WCHAR fmt[] = {'%','s','\\','%','i',0};
3010     int sz; 
3011     HRESULT res;
3012
3013     if (!IS_INTRESOURCE(lpszName))
3014     {
3015         ERR("Not Int Resource Name %s\n",debugstr_w(lpszName));
3016         return TRUE;
3017     }
3018
3019     sz = strlenW(tl_struct->source)+4;
3020     sz *= sizeof(WCHAR);
3021
3022     if ((INT_PTR)lpszName == 1)
3023         tl_struct->path = strdupW(tl_struct->source);
3024     else
3025     {
3026         tl_struct->path = msi_alloc(sz);
3027         sprintfW(tl_struct->path,fmt,tl_struct->source, lpszName);
3028     }
3029
3030     TRACE("trying %s\n", debugstr_w(tl_struct->path));
3031     res = LoadTypeLib(tl_struct->path,&tl_struct->ptLib);
3032     if (FAILED(res))
3033     {
3034         msi_free(tl_struct->path);
3035         tl_struct->path = NULL;
3036
3037         return TRUE;
3038     }
3039
3040     ITypeLib_GetLibAttr(tl_struct->ptLib, &attr);
3041     if (IsEqualGUID(&(tl_struct->clsid),&(attr->guid)))
3042     {
3043         ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
3044         return FALSE;
3045     }
3046
3047     msi_free(tl_struct->path);
3048     tl_struct->path = NULL;
3049
3050     ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
3051     ITypeLib_Release(tl_struct->ptLib);
3052
3053     return TRUE;
3054 }
3055
3056 static UINT ITERATE_RegisterTypeLibraries(MSIRECORD *row, LPVOID param)
3057 {
3058     MSIPACKAGE* package = param;
3059     LPCWSTR component;
3060     MSICOMPONENT *comp;
3061     MSIFILE *file;
3062     typelib_struct tl_struct;
3063     ITypeLib *tlib;
3064     HMODULE module;
3065     HRESULT hr;
3066
3067     static const WCHAR szTYPELIB[] = {'T','Y','P','E','L','I','B',0};
3068
3069     component = MSI_RecordGetString(row,3);
3070     comp = get_loaded_component(package,component);
3071     if (!comp)
3072         return ERROR_SUCCESS;
3073
3074     if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
3075     {
3076         TRACE("Skipping typelib reg due to disabled component\n");
3077
3078         comp->Action = comp->Installed;
3079
3080         return ERROR_SUCCESS;
3081     }
3082
3083     comp->Action = INSTALLSTATE_LOCAL;
3084
3085     file = get_loaded_file( package, comp->KeyPath ); 
3086     if (!file)
3087         return ERROR_SUCCESS;
3088
3089     module = LoadLibraryExW( file->TargetPath, NULL, LOAD_LIBRARY_AS_DATAFILE );
3090     if (module)
3091     {
3092         LPCWSTR guid;
3093         guid = MSI_RecordGetString(row,1);
3094         CLSIDFromString((LPWSTR)guid, &tl_struct.clsid);
3095         tl_struct.source = strdupW( file->TargetPath );
3096         tl_struct.path = NULL;
3097
3098         EnumResourceNamesW(module, szTYPELIB, Typelib_EnumResNameProc,
3099                         (LONG_PTR)&tl_struct);
3100
3101         if (tl_struct.path)
3102         {
3103             LPWSTR help = NULL;
3104             LPCWSTR helpid;
3105             HRESULT res;
3106
3107             helpid = MSI_RecordGetString(row,6);
3108
3109             if (helpid)
3110                 help = resolve_folder(package,helpid,FALSE,FALSE,TRUE,NULL);
3111             res = RegisterTypeLib(tl_struct.ptLib,tl_struct.path,help);
3112             msi_free(help);
3113
3114             if (FAILED(res))
3115                 ERR("Failed to register type library %s\n",
3116                         debugstr_w(tl_struct.path));
3117             else
3118             {
3119                 ui_actiondata(package,szRegisterTypeLibraries,row);
3120
3121                 TRACE("Registered %s\n", debugstr_w(tl_struct.path));
3122             }
3123
3124             ITypeLib_Release(tl_struct.ptLib);
3125             msi_free(tl_struct.path);
3126         }
3127         else
3128             ERR("Failed to load type library %s\n",
3129                     debugstr_w(tl_struct.source));
3130
3131         FreeLibrary(module);
3132         msi_free(tl_struct.source);
3133     }
3134     else
3135     {
3136         hr = LoadTypeLibEx(file->TargetPath, REGKIND_REGISTER, &tlib);
3137         if (FAILED(hr))
3138         {
3139             ERR("Failed to load type library: %08x\n", hr);
3140             return ERROR_FUNCTION_FAILED;
3141         }
3142
3143         ITypeLib_Release(tlib);
3144     }
3145
3146     return ERROR_SUCCESS;
3147 }
3148
3149 static UINT ACTION_RegisterTypeLibraries(MSIPACKAGE *package)
3150 {
3151     /* 
3152      * OK this is a bit confusing.. I am given a _Component key and I believe
3153      * that the file that is being registered as a type library is the "key file
3154      * of that component" which I interpret to mean "The file in the KeyPath of
3155      * that component".
3156      */
3157     UINT rc;
3158     MSIQUERY * view;
3159     static const WCHAR Query[] =
3160         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3161          '`','T','y','p','e','L','i','b','`',0};
3162
3163     rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3164     if (rc != ERROR_SUCCESS)
3165         return ERROR_SUCCESS;
3166
3167     rc = MSI_IterateRecords(view, NULL, ITERATE_RegisterTypeLibraries, package);
3168     msiobj_release(&view->hdr);
3169     return rc;
3170 }
3171
3172 static UINT ITERATE_CreateShortcuts(MSIRECORD *row, LPVOID param)
3173 {
3174     MSIPACKAGE *package = param;
3175     LPWSTR target_file, target_folder, filename;
3176     LPCWSTR buffer, extension;
3177     MSICOMPONENT *comp;
3178     static const WCHAR szlnk[]={'.','l','n','k',0};
3179     IShellLinkW *sl = NULL;
3180     IPersistFile *pf = NULL;
3181     HRESULT res;
3182
3183     buffer = MSI_RecordGetString(row,4);
3184     comp = get_loaded_component(package,buffer);
3185     if (!comp)
3186         return ERROR_SUCCESS;
3187
3188     if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL ))
3189     {
3190         TRACE("Skipping shortcut creation due to disabled component\n");
3191
3192         comp->Action = comp->Installed;
3193
3194         return ERROR_SUCCESS;
3195     }
3196
3197     comp->Action = INSTALLSTATE_LOCAL;
3198
3199     ui_actiondata(package,szCreateShortcuts,row);
3200
3201     res = CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
3202                     &IID_IShellLinkW, (LPVOID *) &sl );
3203
3204     if (FAILED( res ))
3205     {
3206         ERR("CLSID_ShellLink not available\n");
3207         goto err;
3208     }
3209
3210     res = IShellLinkW_QueryInterface( sl, &IID_IPersistFile,(LPVOID*) &pf );
3211     if (FAILED( res ))
3212     {
3213         ERR("QueryInterface(IID_IPersistFile) failed\n");
3214         goto err;
3215     }
3216
3217     buffer = MSI_RecordGetString(row,2);
3218     target_folder = resolve_folder(package, buffer,FALSE,FALSE,TRUE,NULL);
3219
3220     /* may be needed because of a bug somewhere else */
3221     create_full_pathW(target_folder);
3222
3223     filename = msi_dup_record_field( row, 3 );
3224     reduce_to_longfilename(filename);
3225
3226     extension = strchrW(filename,'.');
3227     if (!extension || strcmpiW(extension,szlnk))
3228     {
3229         int len = strlenW(filename);
3230         filename = msi_realloc(filename, len * sizeof(WCHAR) + sizeof(szlnk));
3231         memcpy(filename + len, szlnk, sizeof(szlnk));
3232     }
3233     target_file = build_directory_name(2, target_folder, filename);
3234     msi_free(target_folder);
3235     msi_free(filename);
3236
3237     buffer = MSI_RecordGetString(row,5);
3238     if (strchrW(buffer,'['))
3239     {
3240         LPWSTR deformated;
3241         deformat_string(package,buffer,&deformated);
3242         IShellLinkW_SetPath(sl,deformated);
3243         msi_free(deformated);
3244     }
3245     else
3246     {
3247         FIXME("poorly handled shortcut format, advertised shortcut\n");
3248         IShellLinkW_SetPath(sl,comp->FullKeypath);
3249     }
3250
3251     if (!MSI_RecordIsNull(row,6))
3252     {
3253         LPWSTR deformated;
3254         buffer = MSI_RecordGetString(row,6);
3255         deformat_string(package,buffer,&deformated);
3256         IShellLinkW_SetArguments(sl,deformated);
3257         msi_free(deformated);
3258     }
3259
3260     if (!MSI_RecordIsNull(row,7))
3261     {
3262         buffer = MSI_RecordGetString(row,7);
3263         IShellLinkW_SetDescription(sl,buffer);
3264     }
3265
3266     if (!MSI_RecordIsNull(row,8))
3267         IShellLinkW_SetHotkey(sl,MSI_RecordGetInteger(row,8));
3268
3269     if (!MSI_RecordIsNull(row,9))
3270     {
3271         LPWSTR Path;
3272         INT index; 
3273
3274         buffer = MSI_RecordGetString(row,9);
3275
3276         Path = build_icon_path(package,buffer);
3277         index = MSI_RecordGetInteger(row,10);
3278
3279         /* no value means 0 */
3280         if (index == MSI_NULL_INTEGER)
3281             index = 0;
3282
3283         IShellLinkW_SetIconLocation(sl,Path,index);
3284         msi_free(Path);
3285     }
3286
3287     if (!MSI_RecordIsNull(row,11))
3288         IShellLinkW_SetShowCmd(sl,MSI_RecordGetInteger(row,11));
3289
3290     if (!MSI_RecordIsNull(row,12))
3291     {
3292         LPWSTR Path;
3293         buffer = MSI_RecordGetString(row,12);
3294         Path = resolve_folder(package, buffer, FALSE, FALSE, TRUE, NULL);
3295         if (Path)
3296             IShellLinkW_SetWorkingDirectory(sl,Path);
3297         msi_free(Path);
3298     }
3299
3300     TRACE("Writing shortcut to %s\n",debugstr_w(target_file));
3301     IPersistFile_Save(pf,target_file,FALSE);
3302
3303     msi_free(target_file);    
3304
3305 err:
3306     if (pf)
3307         IPersistFile_Release( pf );
3308     if (sl)
3309         IShellLinkW_Release( sl );
3310
3311     return ERROR_SUCCESS;
3312 }
3313
3314 static UINT ACTION_CreateShortcuts(MSIPACKAGE *package)
3315 {
3316     UINT rc;
3317     HRESULT res;
3318     MSIQUERY * view;
3319     static const WCHAR Query[] =
3320         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3321          '`','S','h','o','r','t','c','u','t','`',0};
3322
3323     rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3324     if (rc != ERROR_SUCCESS)
3325         return ERROR_SUCCESS;
3326
3327     res = CoInitialize( NULL );
3328     if (FAILED (res))
3329     {
3330         ERR("CoInitialize failed\n");
3331         return ERROR_FUNCTION_FAILED;
3332     }
3333
3334     rc = MSI_IterateRecords(view, NULL, ITERATE_CreateShortcuts, package);
3335     msiobj_release(&view->hdr);
3336
3337     CoUninitialize();
3338
3339     return rc;
3340 }
3341
3342 static UINT ITERATE_PublishIcon(MSIRECORD *row, LPVOID param)
3343 {
3344     MSIPACKAGE* package = param;
3345     HANDLE the_file;
3346     LPWSTR FilePath;
3347     LPCWSTR FileName;
3348     CHAR buffer[1024];
3349     DWORD sz;
3350     UINT rc;
3351     MSIRECORD *uirow;
3352
3353     FileName = MSI_RecordGetString(row,1);
3354     if (!FileName)
3355     {
3356         ERR("Unable to get FileName\n");
3357         return ERROR_SUCCESS;
3358     }
3359
3360     FilePath = build_icon_path(package,FileName);
3361
3362     TRACE("Creating icon file at %s\n",debugstr_w(FilePath));
3363
3364     the_file = CreateFileW(FilePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
3365                         FILE_ATTRIBUTE_NORMAL, NULL);
3366
3367     if (the_file == INVALID_HANDLE_VALUE)
3368     {
3369         ERR("Unable to create file %s\n",debugstr_w(FilePath));
3370         msi_free(FilePath);
3371         return ERROR_SUCCESS;
3372     }
3373
3374     do 
3375     {
3376         DWORD write;
3377         sz = 1024;
3378         rc = MSI_RecordReadStream(row,2,buffer,&sz);
3379         if (rc != ERROR_SUCCESS)
3380         {
3381             ERR("Failed to get stream\n");
3382             CloseHandle(the_file);  
3383             DeleteFileW(FilePath);
3384             break;
3385         }
3386         WriteFile(the_file,buffer,sz,&write,NULL);
3387     } while (sz == 1024);
3388
3389     msi_free(FilePath);
3390
3391     CloseHandle(the_file);
3392
3393     uirow = MSI_CreateRecord(1);
3394     MSI_RecordSetStringW(uirow,1,FileName);
3395     ui_actiondata(package,szPublishProduct,uirow);
3396     msiobj_release( &uirow->hdr );
3397
3398     return ERROR_SUCCESS;
3399 }
3400
3401 static UINT msi_publish_icons(MSIPACKAGE *package)
3402 {
3403     UINT r;
3404     MSIQUERY *view;
3405
3406     static const WCHAR query[]= {
3407         'S','E','L','E','C','T',' ','*',' ',
3408         'F','R','O','M',' ','`','I','c','o','n','`',0};
3409
3410     r = MSI_DatabaseOpenViewW(package->db, query, &view);
3411     if (r == ERROR_SUCCESS)
3412     {
3413         MSI_IterateRecords(view, NULL, ITERATE_PublishIcon, package);
3414         msiobj_release(&view->hdr);
3415     }
3416
3417     return ERROR_SUCCESS;
3418 }
3419
3420 static UINT msi_publish_sourcelist(MSIPACKAGE *package, HKEY hkey)
3421 {
3422     UINT r;
3423     HKEY source;
3424     LPWSTR buffer;
3425     MSIMEDIADISK *disk;
3426     MSISOURCELISTINFO *info;
3427
3428     static const WCHAR szEmpty[] = {0};
3429     static const WCHAR szSourceList[] = {'S','o','u','r','c','e','L','i','s','t',0};
3430
3431     r = RegCreateKeyW(hkey, szSourceList, &source);
3432     if (r != ERROR_SUCCESS)
3433         return r;
3434
3435     RegCloseKey(source);
3436
3437     buffer = strrchrW(package->PackagePath, '\\') + 1;
3438     r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3439                               package->Context, MSICODE_PRODUCT,
3440                               INSTALLPROPERTY_PACKAGENAMEW, buffer);
3441     if (r != ERROR_SUCCESS)
3442         return r;
3443
3444     r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3445                               package->Context, MSICODE_PRODUCT,
3446                               INSTALLPROPERTY_MEDIAPACKAGEPATHW, szEmpty);
3447     if (r != ERROR_SUCCESS)
3448         return r;
3449
3450     r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3451                               package->Context, MSICODE_PRODUCT,
3452                               INSTALLPROPERTY_DISKPROMPTW, szEmpty);
3453     if (r != ERROR_SUCCESS)
3454         return r;
3455
3456     LIST_FOR_EACH_ENTRY(info, &package->sourcelist_info, MSISOURCELISTINFO, entry)
3457     {
3458         if (!lstrcmpW(info->property, INSTALLPROPERTY_LASTUSEDSOURCEW))
3459             msi_set_last_used_source(package->ProductCode, NULL, info->context,
3460                                      info->options, info->value);
3461         else
3462             MsiSourceListSetInfoW(package->ProductCode, NULL,
3463                                   info->context, info->options,
3464                                   info->property, info->value);
3465     }
3466
3467     LIST_FOR_EACH_ENTRY(disk, &package->sourcelist_media, MSIMEDIADISK, entry)
3468     {
3469         MsiSourceListAddMediaDiskW(package->ProductCode, NULL,
3470                                    disk->context, disk->options,
3471                                    disk->disk_id, disk->volume_label, disk->disk_prompt);
3472     }
3473
3474     return ERROR_SUCCESS;
3475 }
3476
3477 static UINT msi_publish_product_properties(MSIPACKAGE *package, HKEY hkey)
3478 {
3479     MSIHANDLE hdb, suminfo;
3480     WCHAR guids[MAX_PATH];
3481     WCHAR packcode[SQUISH_GUID_SIZE];
3482     LPWSTR buffer;
3483     LPWSTR ptr;
3484     DWORD langid;
3485     DWORD size;
3486     UINT r;
3487
3488     static const WCHAR szProductLanguage[] =
3489         {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
3490     static const WCHAR szARPProductIcon[] =
3491         {'A','R','P','P','R','O','D','U','C','T','I','C','O','N',0};
3492     static const WCHAR szProductVersion[] =
3493         {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
3494     static const WCHAR szAssignment[] =
3495         {'A','s','s','i','g','n','m','e','n','t',0};
3496     static const WCHAR szAdvertiseFlags[] =
3497         {'A','d','v','e','r','t','i','s','e','F','l','a','g','s',0};
3498     static const WCHAR szClients[] =
3499         {'C','l','i','e','n','t','s',0};
3500     static const WCHAR szColon[] = {':',0};
3501
3502     buffer = msi_dup_property(package, INSTALLPROPERTY_PRODUCTNAMEW);
3503     msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTNAMEW, buffer);
3504     msi_free(buffer);
3505
3506     langid = msi_get_property_int(package, szProductLanguage, 0);
3507     msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
3508
3509     ptr = strrchrW(package->PackagePath, '\\' ) + 1;
3510     msi_reg_set_val_str(hkey, INSTALLPROPERTY_PACKAGENAMEW, ptr);
3511
3512     /* FIXME */
3513     msi_reg_set_val_dword(hkey, INSTALLPROPERTY_AUTHORIZED_LUA_APPW, 0);
3514
3515     buffer = msi_dup_property(package, szARPProductIcon);
3516     if (buffer)
3517     {
3518         LPWSTR path = build_icon_path(package,buffer);
3519         msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTICONW, path);
3520         msi_free(path);
3521         msi_free(buffer);
3522     }
3523
3524     buffer = msi_dup_property(package, szProductVersion);
3525     if (buffer)
3526     {
3527         DWORD verdword = msi_version_str_to_dword(buffer);
3528         msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
3529         msi_free(buffer);
3530     }
3531
3532     msi_reg_set_val_dword(hkey, szAssignment, 0);
3533     msi_reg_set_val_dword(hkey, szAdvertiseFlags, 0x184);
3534     msi_reg_set_val_dword(hkey, INSTALLPROPERTY_INSTANCETYPEW, 0);
3535     msi_reg_set_val_str(hkey, szClients, szColon);
3536
3537     hdb = alloc_msihandle(&package->db->hdr);
3538     if (!hdb)
3539         return ERROR_NOT_ENOUGH_MEMORY;
3540
3541     r = MsiGetSummaryInformationW(hdb, NULL, 0, &suminfo);
3542     MsiCloseHandle(hdb);
3543     if (r != ERROR_SUCCESS)
3544         goto done;
3545
3546     size = MAX_PATH;
3547     r = MsiSummaryInfoGetPropertyW(suminfo, PID_REVNUMBER, NULL, NULL,
3548                                    NULL, guids, &size);
3549     if (r != ERROR_SUCCESS)
3550         goto done;
3551
3552     ptr = strchrW(guids, ';');
3553     if (ptr) *ptr = 0;
3554     squash_guid(guids, packcode);
3555     msi_reg_set_val_str(hkey, INSTALLPROPERTY_PACKAGECODEW, packcode);
3556
3557 done:
3558     MsiCloseHandle(suminfo);
3559     return ERROR_SUCCESS;
3560 }
3561
3562 static UINT msi_publish_upgrade_code(MSIPACKAGE *package)
3563 {
3564     UINT r;
3565     HKEY hkey;
3566     LPWSTR upgrade;
3567     WCHAR squashed_pc[SQUISH_GUID_SIZE];
3568
3569     static const WCHAR szUpgradeCode[] =
3570         {'U','p','g','r','a','d','e','C','o','d','e',0};
3571
3572     upgrade = msi_dup_property(package, szUpgradeCode);
3573     if (!upgrade)
3574         return ERROR_SUCCESS;
3575
3576     if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3577     {
3578         r = MSIREG_OpenClassesUpgradeCodesKey(upgrade, &hkey, TRUE);
3579         if (r != ERROR_SUCCESS)
3580             goto done;
3581     }
3582     else
3583     {
3584         r = MSIREG_OpenUserUpgradeCodesKey(upgrade, &hkey, TRUE);
3585         if (r != ERROR_SUCCESS)
3586             goto done;
3587     }
3588
3589     squash_guid(package->ProductCode, squashed_pc);
3590     msi_reg_set_val_str(hkey, squashed_pc, NULL);
3591
3592     RegCloseKey(hkey);
3593
3594 done:
3595     msi_free(upgrade);
3596     return r;
3597 }
3598
3599 static BOOL msi_check_publish(MSIPACKAGE *package)
3600 {
3601     MSIFEATURE *feature;
3602
3603     LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3604     {
3605         if (feature->ActionRequest == INSTALLSTATE_LOCAL)
3606             return TRUE;
3607     }
3608
3609     return FALSE;
3610 }
3611
3612 static BOOL msi_check_unpublish(MSIPACKAGE *package)
3613 {
3614     MSIFEATURE *feature;
3615
3616     LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3617     {
3618         if (feature->ActionRequest != INSTALLSTATE_ABSENT)
3619             return FALSE;
3620     }
3621
3622     return TRUE;
3623 }
3624
3625 static UINT msi_publish_patch(MSIPACKAGE *package, HKEY prodkey, HKEY hudkey)
3626 {
3627     WCHAR patch_squashed[GUID_SIZE];
3628     HKEY patches;
3629     LONG res;
3630     UINT r = ERROR_FUNCTION_FAILED;
3631
3632     static const WCHAR szPatches[] = {'P','a','t','c','h','e','s',0};
3633
3634     res = RegCreateKeyExW(prodkey, szPatches, 0, NULL, 0, KEY_ALL_ACCESS, NULL,
3635                           &patches, NULL);
3636     if (res != ERROR_SUCCESS)
3637         return ERROR_FUNCTION_FAILED;
3638
3639     squash_guid(package->patch->patchcode, patch_squashed);
3640
3641     res = RegSetValueExW(patches, szPatches, 0, REG_MULTI_SZ,
3642                          (const BYTE *)patch_squashed,
3643                          (lstrlenW(patch_squashed) + 1) * sizeof(WCHAR));
3644     if (res != ERROR_SUCCESS)
3645         goto done;
3646
3647     res = RegSetValueExW(patches, patch_squashed, 0, REG_SZ,
3648                          (const BYTE *)package->patch->transforms,
3649                          (lstrlenW(package->patch->transforms) + 1) * sizeof(WCHAR));
3650     if (res == ERROR_SUCCESS)
3651         r = ERROR_SUCCESS;
3652
3653 done:
3654     RegCloseKey(patches);
3655     return r;
3656 }
3657
3658 /*
3659  * 99% of the work done here is only done for 
3660  * advertised installs. However this is where the
3661  * Icon table is processed and written out
3662  * so that is what I am going to do here.
3663  */
3664 static UINT ACTION_PublishProduct(MSIPACKAGE *package)
3665 {
3666     UINT rc;
3667     HKEY hukey=0;
3668     HKEY hudkey=0;
3669
3670     /* FIXME: also need to publish if the product is in advertise mode */
3671     if (!msi_check_publish(package))
3672         return ERROR_SUCCESS;
3673
3674     rc = MSIREG_OpenProductKey(package->ProductCode, package->Context,
3675                                &hukey, TRUE);
3676     if (rc != ERROR_SUCCESS)
3677         goto end;
3678
3679     rc = MSIREG_OpenUserDataProductKey(package->ProductCode, package->Context,
3680                                        NULL, &hudkey, TRUE);
3681     if (rc != ERROR_SUCCESS)
3682         goto end;
3683
3684     rc = msi_publish_upgrade_code(package);
3685     if (rc != ERROR_SUCCESS)
3686         goto end;
3687
3688     if (package->patch)
3689     {
3690         rc = msi_publish_patch(package, hukey, hudkey);
3691         if (rc != ERROR_SUCCESS)
3692             goto end;
3693     }
3694
3695     rc = msi_publish_product_properties(package, hukey);
3696     if (rc != ERROR_SUCCESS)
3697         goto end;
3698
3699     rc = msi_publish_sourcelist(package, hukey);
3700     if (rc != ERROR_SUCCESS)
3701         goto end;
3702
3703     rc = msi_publish_icons(package);
3704
3705 end:
3706     RegCloseKey(hukey);
3707     RegCloseKey(hudkey);
3708
3709     return rc;
3710 }
3711
3712 static UINT ITERATE_WriteIniValues(MSIRECORD *row, LPVOID param)
3713 {
3714     MSIPACKAGE *package = param;
3715     LPCWSTR component, section, key, value, identifier, dirproperty;
3716     LPWSTR deformated_section, deformated_key, deformated_value;
3717     LPWSTR folder, filename, fullname = NULL;
3718     LPCWSTR filenameptr;
3719     MSIRECORD * uirow;
3720     INT action;
3721     MSICOMPONENT *comp;
3722     static const WCHAR szWindowsFolder[] =
3723           {'W','i','n','d','o','w','s','F','o','l','d','e','r',0};
3724
3725     component = MSI_RecordGetString(row, 8);
3726     comp = get_loaded_component(package,component);
3727
3728     if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
3729     {
3730         TRACE("Skipping ini file due to disabled component %s\n",
3731                         debugstr_w(component));
3732
3733         comp->Action = comp->Installed;
3734
3735         return ERROR_SUCCESS;
3736     }
3737
3738     comp->Action = INSTALLSTATE_LOCAL;
3739
3740     identifier = MSI_RecordGetString(row,1); 
3741     dirproperty = MSI_RecordGetString(row,3);
3742     section = MSI_RecordGetString(row,4);
3743     key = MSI_RecordGetString(row,5);
3744     value = MSI_RecordGetString(row,6);
3745     action = MSI_RecordGetInteger(row,7);
3746
3747     deformat_string(package,section,&deformated_section);
3748     deformat_string(package,key,&deformated_key);
3749     deformat_string(package,value,&deformated_value);
3750
3751     filename = msi_dup_record_field(row, 2);
3752     if (filename && (filenameptr = strchrW(filename, '|')))
3753         filenameptr++;
3754     else
3755         filenameptr = filename;
3756
3757     if (dirproperty)
3758     {
3759         folder = resolve_folder(package, dirproperty, FALSE, FALSE, TRUE, NULL);
3760         if (!folder)
3761             folder = msi_dup_property( package, dirproperty );
3762     }
3763     else
3764         folder = msi_dup_property( package, szWindowsFolder );
3765
3766     if (!folder)
3767     {
3768         ERR("Unable to resolve folder! (%s)\n",debugstr_w(dirproperty));
3769         goto cleanup;
3770     }
3771
3772     fullname = build_directory_name(2, folder, filenameptr);
3773
3774     if (action == 0)
3775     {
3776         TRACE("Adding value %s to section %s in %s\n",
3777                 debugstr_w(deformated_key), debugstr_w(deformated_section),
3778                 debugstr_w(fullname));
3779         WritePrivateProfileStringW(deformated_section, deformated_key,
3780                                    deformated_value, fullname);
3781     }
3782     else if (action == 1)
3783     {
3784         WCHAR returned[10];
3785         GetPrivateProfileStringW(deformated_section, deformated_key, NULL,
3786                                  returned, 10, fullname);
3787         if (returned[0] == 0)
3788         {
3789             TRACE("Adding value %s to section %s in %s\n",
3790                     debugstr_w(deformated_key), debugstr_w(deformated_section),
3791                     debugstr_w(fullname));
3792
3793             WritePrivateProfileStringW(deformated_section, deformated_key,
3794                                        deformated_value, fullname);
3795         }
3796     }
3797     else if (action == 3)
3798         FIXME("Append to existing section not yet implemented\n");
3799
3800     uirow = MSI_CreateRecord(4);
3801     MSI_RecordSetStringW(uirow,1,identifier);
3802     MSI_RecordSetStringW(uirow,2,deformated_section);
3803     MSI_RecordSetStringW(uirow,3,deformated_key);
3804     MSI_RecordSetStringW(uirow,4,deformated_value);
3805     ui_actiondata(package,szWriteIniValues,uirow);
3806     msiobj_release( &uirow->hdr );
3807
3808 cleanup:
3809     msi_free(filename);
3810     msi_free(fullname);
3811     msi_free(folder);
3812     msi_free(deformated_key);
3813     msi_free(deformated_value);
3814     msi_free(deformated_section);
3815     return ERROR_SUCCESS;
3816 }
3817
3818 static UINT ACTION_WriteIniValues(MSIPACKAGE *package)
3819 {
3820     UINT rc;
3821     MSIQUERY * view;
3822     static const WCHAR ExecSeqQuery[] = 
3823         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3824          '`','I','n','i','F','i','l','e','`',0};
3825
3826     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
3827     if (rc != ERROR_SUCCESS)
3828     {
3829         TRACE("no IniFile table\n");
3830         return ERROR_SUCCESS;
3831     }
3832
3833     rc = MSI_IterateRecords(view, NULL, ITERATE_WriteIniValues, package);
3834     msiobj_release(&view->hdr);
3835     return rc;
3836 }
3837
3838 static UINT ITERATE_SelfRegModules(MSIRECORD *row, LPVOID param)
3839 {
3840     MSIPACKAGE *package = param;
3841     LPCWSTR filename;
3842     LPWSTR FullName;
3843     MSIFILE *file;
3844     DWORD len;
3845     static const WCHAR ExeStr[] =
3846         {'r','e','g','s','v','r','3','2','.','e','x','e',' ','\"',0};
3847     static const WCHAR close[] =  {'\"',0};
3848     STARTUPINFOW si;
3849     PROCESS_INFORMATION info;
3850     BOOL brc;
3851     MSIRECORD *uirow;
3852     LPWSTR uipath, p;
3853
3854     memset(&si,0,sizeof(STARTUPINFOW));
3855
3856     filename = MSI_RecordGetString(row,1);
3857     file = get_loaded_file( package, filename );
3858
3859     if (!file)
3860     {
3861         ERR("Unable to find file id %s\n",debugstr_w(filename));
3862         return ERROR_SUCCESS;
3863     }
3864
3865     len = strlenW(ExeStr) + strlenW( file->TargetPath ) + 2;
3866
3867     FullName = msi_alloc(len*sizeof(WCHAR));
3868     strcpyW(FullName,ExeStr);
3869     strcatW( FullName, file->TargetPath );
3870     strcatW(FullName,close);
3871
3872     TRACE("Registering %s\n",debugstr_w(FullName));
3873     brc = CreateProcessW(NULL, FullName, NULL, NULL, FALSE, 0, NULL, c_colon,
3874                     &si, &info);
3875
3876     if (brc)
3877     {
3878         CloseHandle(info.hThread);
3879         msi_dialog_check_messages(info.hProcess);
3880         CloseHandle(info.hProcess);
3881     }
3882
3883     msi_free(FullName);
3884
3885     /* the UI chunk */
3886     uirow = MSI_CreateRecord( 2 );
3887     uipath = strdupW( file->TargetPath );
3888     p = strrchrW(uipath,'\\');
3889     if (p)
3890         p[0]=0;
3891     MSI_RecordSetStringW( uirow, 1, &p[1] );
3892     MSI_RecordSetStringW( uirow, 2, uipath);
3893     ui_actiondata( package, szSelfRegModules, uirow);
3894     msiobj_release( &uirow->hdr );
3895     msi_free( uipath );
3896     /* FIXME: call ui_progress? */
3897
3898     return ERROR_SUCCESS;
3899 }
3900
3901 static UINT ACTION_SelfRegModules(MSIPACKAGE *package)
3902 {
3903     UINT rc;
3904     MSIQUERY * view;
3905     static const WCHAR ExecSeqQuery[] = 
3906         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3907          '`','S','e','l','f','R','e','g','`',0};
3908
3909     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
3910     if (rc != ERROR_SUCCESS)
3911     {
3912         TRACE("no SelfReg table\n");
3913         return ERROR_SUCCESS;
3914     }
3915
3916     MSI_IterateRecords(view, NULL, ITERATE_SelfRegModules, package);
3917     msiobj_release(&view->hdr);
3918
3919     return ERROR_SUCCESS;
3920 }
3921
3922 static UINT ACTION_PublishFeatures(MSIPACKAGE *package)
3923 {
3924     MSIFEATURE *feature;
3925     UINT rc;
3926     HKEY hkey;
3927     HKEY userdata = NULL;
3928
3929     if (!msi_check_publish(package))
3930         return ERROR_SUCCESS;
3931
3932     rc = MSIREG_OpenFeaturesKey(package->ProductCode, package->Context,
3933                                 &hkey, TRUE);
3934     if (rc != ERROR_SUCCESS)
3935         goto end;
3936
3937     rc = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, package->Context,
3938                                         &userdata, TRUE);
3939     if (rc != ERROR_SUCCESS)
3940         goto end;
3941
3942     /* here the guids are base 85 encoded */
3943     LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3944     {
3945         ComponentList *cl;
3946         LPWSTR data = NULL;
3947         GUID clsid;
3948         INT size;
3949         BOOL absent = FALSE;
3950         MSIRECORD *uirow;
3951
3952         if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_LOCAL ) &&
3953             !ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_SOURCE ) &&
3954             !ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_ADVERTISED ))
3955             absent = TRUE;
3956
3957         size = 1;
3958         LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3959         {
3960             size += 21;
3961         }
3962         if (feature->Feature_Parent)
3963             size += strlenW( feature->Feature_Parent )+2;
3964
3965         data = msi_alloc(size * sizeof(WCHAR));
3966
3967         data[0] = 0;
3968         LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3969         {
3970             MSICOMPONENT* component = cl->component;
3971             WCHAR buf[21];
3972
3973             buf[0] = 0;
3974             if (component->ComponentId)
3975             {
3976                 TRACE("From %s\n",debugstr_w(component->ComponentId));
3977                 CLSIDFromString(component->ComponentId, &clsid);
3978                 encode_base85_guid(&clsid,buf);
3979                 TRACE("to %s\n",debugstr_w(buf));
3980                 strcatW(data,buf);
3981             }
3982         }
3983
3984         if (feature->Feature_Parent)
3985         {
3986             static const WCHAR sep[] = {'\2',0};
3987             strcatW(data,sep);
3988             strcatW(data,feature->Feature_Parent);
3989         }
3990
3991         msi_reg_set_val_str( userdata, feature->Feature, data );
3992         msi_free(data);
3993
3994         size = 0;
3995         if (feature->Feature_Parent)
3996             size = strlenW(feature->Feature_Parent)*sizeof(WCHAR);
3997         if (!absent)
3998         {
3999             static const WCHAR emptyW[] = {0};
4000             size += sizeof(WCHAR);
4001             RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
4002                            (LPBYTE)(feature->Feature_Parent ? feature->Feature_Parent : emptyW),size);
4003         }
4004         else
4005         {
4006             size += 2*sizeof(WCHAR);
4007             data = msi_alloc(size);
4008             data[0] = 0x6;
4009             data[1] = 0;
4010             if (feature->Feature_Parent)
4011                 strcpyW( &data[1], feature->Feature_Parent );
4012             RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
4013                        (LPBYTE)data,size);
4014             msi_free(data);
4015         }
4016
4017         /* the UI chunk */
4018         uirow = MSI_CreateRecord( 1 );
4019         MSI_RecordSetStringW( uirow, 1, feature->Feature );
4020         ui_actiondata( package, szPublishFeatures, uirow);
4021         msiobj_release( &uirow->hdr );
4022         /* FIXME: call ui_progress? */
4023     }
4024
4025 end:
4026     RegCloseKey(hkey);
4027     RegCloseKey(userdata);
4028     return rc;
4029 }
4030
4031 static UINT msi_unpublish_feature(MSIPACKAGE *package, MSIFEATURE *feature)
4032 {
4033     UINT r;
4034     HKEY hkey;
4035
4036     TRACE("unpublishing feature %s\n", debugstr_w(feature->Feature));
4037
4038     r = MSIREG_OpenFeaturesKey(package->ProductCode, package->Context,
4039                                &hkey, FALSE);
4040     if (r == ERROR_SUCCESS)
4041     {
4042         RegDeleteValueW(hkey, feature->Feature);
4043         RegCloseKey(hkey);
4044     }
4045
4046     r = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, package->Context,
4047                                        &hkey, FALSE);
4048     if (r == ERROR_SUCCESS)
4049     {
4050         RegDeleteValueW(hkey, feature->Feature);
4051         RegCloseKey(hkey);
4052     }
4053
4054     return ERROR_SUCCESS;
4055 }
4056
4057 static UINT ACTION_UnpublishFeatures(MSIPACKAGE *package)
4058 {
4059     MSIFEATURE *feature;
4060
4061     if (!msi_check_unpublish(package))
4062         return ERROR_SUCCESS;
4063
4064     LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4065     {
4066         msi_unpublish_feature(package, feature);
4067     }
4068
4069     return ERROR_SUCCESS;
4070 }
4071
4072 static UINT msi_get_local_package_name( LPWSTR path )
4073 {
4074     static const WCHAR szInstaller[] = {
4075         '\\','I','n','s','t','a','l','l','e','r','\\',0};
4076     static const WCHAR fmt[] = { '%','x','.','m','s','i',0};
4077     DWORD time, len, i;
4078     HANDLE handle;
4079
4080     time = GetTickCount();
4081     GetWindowsDirectoryW( path, MAX_PATH );
4082     lstrcatW( path, szInstaller );
4083     CreateDirectoryW( path, NULL );
4084
4085     len = lstrlenW(path);
4086     for (i=0; i<0x10000; i++)
4087     {
4088         snprintfW( &path[len], MAX_PATH - len, fmt, (time+i)&0xffff );
4089         handle = CreateFileW( path, GENERIC_WRITE, 0, NULL,
4090                               CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0 );
4091         if (handle != INVALID_HANDLE_VALUE)
4092         {
4093             CloseHandle(handle);
4094             break;
4095         }
4096         if (GetLastError() != ERROR_FILE_EXISTS &&
4097             GetLastError() != ERROR_SHARING_VIOLATION)
4098             return ERROR_FUNCTION_FAILED;
4099     }
4100
4101     return ERROR_SUCCESS;
4102 }
4103
4104 static UINT msi_make_package_local( MSIPACKAGE *package, HKEY hkey )
4105 {
4106     WCHAR packagefile[MAX_PATH];
4107     UINT r;
4108
4109     r = msi_get_local_package_name( packagefile );
4110     if (r != ERROR_SUCCESS)
4111         return r;
4112
4113     TRACE("Copying to local package %s\n",debugstr_w(packagefile));
4114
4115     r = CopyFileW( package->db->path, packagefile, FALSE);
4116
4117     if (!r)
4118     {
4119         ERR("Unable to copy package (%s -> %s) (error %d)\n",
4120             debugstr_w(package->db->path), debugstr_w(packagefile), GetLastError());
4121         return ERROR_FUNCTION_FAILED;
4122     }
4123
4124     msi_reg_set_val_str( hkey, INSTALLPROPERTY_LOCALPACKAGEW, packagefile );
4125
4126     return ERROR_SUCCESS;
4127 }
4128
4129 static UINT msi_publish_install_properties(MSIPACKAGE *package, HKEY hkey)
4130 {
4131     LPWSTR prop, val, key;
4132     SYSTEMTIME systime;
4133     DWORD size, langid;
4134     WCHAR date[9];
4135     LPWSTR buffer;
4136
4137     static const WCHAR date_fmt[] = {'%','i','%','0','2','i','%','0','2','i',0};
4138     static const WCHAR szWindowsInstaller[] =
4139         {'W','i','n','d','o','w','s','I','n','s','t','a','l','l','e','r',0};
4140     static const WCHAR modpath_fmt[] =
4141         {'M','s','i','E','x','e','c','.','e','x','e',' ',
4142          '/','I','[','P','r','o','d','u','c','t','C','o','d','e',']',0};
4143     static const WCHAR szModifyPath[] =
4144         {'M','o','d','i','f','y','P','a','t','h',0};
4145     static const WCHAR szUninstallString[] =
4146         {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0};
4147     static const WCHAR szEstimatedSize[] =
4148         {'E','s','t','i','m','a','t','e','d','S','i','z','e',0};
4149     static const WCHAR szProductLanguage[] =
4150         {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
4151     static const WCHAR szProductVersion[] =
4152         {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
4153     static const WCHAR szProductName[] =
4154         {'P','r','o','d','u','c','t','N','a','m','e',0};
4155     static const WCHAR szDisplayName[] =
4156         {'D','i','s','p','l','a','y','N','a','m','e',0};
4157     static const WCHAR szDisplayVersion[] =
4158         {'D','i','s','p','l','a','y','V','e','r','s','i','o','n',0};
4159     static const WCHAR szManufacturer[] =
4160         {'M','a','n','u','f','a','c','t','u','r','e','r',0};
4161
4162     static const LPCSTR propval[] = {
4163         "ARPAUTHORIZEDCDFPREFIX", "AuthorizedCDFPrefix",
4164         "ARPCONTACT",             "Contact",
4165         "ARPCOMMENTS",            "Comments",
4166         "ProductName",            "DisplayName",
4167         "ProductVersion",         "DisplayVersion",
4168         "ARPHELPLINK",            "HelpLink",
4169         "ARPHELPTELEPHONE",       "HelpTelephone",
4170         "ARPINSTALLLOCATION",     "InstallLocation",
4171         "SourceDir",              "InstallSource",
4172         "Manufacturer",           "Publisher",
4173         "ARPREADME",              "Readme",
4174         "ARPSIZE",                "Size",
4175         "ARPURLINFOABOUT",        "URLInfoAbout",
4176         "ARPURLUPDATEINFO",       "URLUpdateInfo",
4177         NULL,
4178     };
4179     const LPCSTR *p = propval;
4180
4181     while (*p)
4182     {
4183         prop = strdupAtoW(*p++);
4184         key = strdupAtoW(*p++);
4185         val = msi_dup_property(package, prop);
4186         msi_reg_set_val_str(hkey, key, val);
4187         msi_free(val);
4188         msi_free(key);
4189         msi_free(prop);
4190     }
4191
4192     msi_reg_set_val_dword(hkey, szWindowsInstaller, 1);
4193
4194     size = deformat_string(package, modpath_fmt, &buffer);
4195     RegSetValueExW(hkey, szModifyPath, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
4196     RegSetValueExW(hkey, szUninstallString, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
4197     msi_free(buffer);
4198
4199     /* FIXME: Write real Estimated Size when we have it */
4200     msi_reg_set_val_dword(hkey, szEstimatedSize, 0);
4201
4202     buffer = msi_dup_property(package, szProductName);
4203     msi_reg_set_val_str(hkey, szDisplayName, buffer);
4204     msi_free(buffer);
4205
4206     buffer = msi_dup_property(package, cszSourceDir);
4207     msi_reg_set_val_str(hkey, INSTALLPROPERTY_INSTALLSOURCEW, buffer);
4208     msi_free(buffer);
4209
4210     buffer = msi_dup_property(package, szManufacturer);
4211     msi_reg_set_val_str(hkey, INSTALLPROPERTY_PUBLISHERW, buffer);
4212     msi_free(buffer);
4213
4214     GetLocalTime(&systime);
4215     sprintfW(date, date_fmt, systime.wYear, systime.wMonth, systime.wDay);
4216     msi_reg_set_val_str(hkey, INSTALLPROPERTY_INSTALLDATEW, date);
4217
4218     langid = msi_get_property_int(package, szProductLanguage, 0);
4219     msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
4220
4221     buffer = msi_dup_property(package, szProductVersion);
4222     msi_reg_set_val_str(hkey, szDisplayVersion, buffer);
4223     if (buffer)
4224     {
4225         DWORD verdword = msi_version_str_to_dword(buffer);
4226
4227         msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
4228         msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMAJORW, verdword >> 24);
4229         msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMINORW, (verdword >> 16) & 0xFF);
4230         msi_free(buffer);
4231     }
4232
4233     return ERROR_SUCCESS;
4234 }
4235
4236 static UINT ACTION_RegisterProduct(MSIPACKAGE *package)
4237 {
4238     WCHAR squashed_pc[SQUISH_GUID_SIZE];
4239     LPWSTR upgrade_code;
4240     HKEY hkey, props;
4241     HKEY upgrade;
4242     UINT rc;
4243
4244     static const WCHAR szUpgradeCode[] = {
4245         'U','p','g','r','a','d','e','C','o','d','e',0};
4246
4247     /* FIXME: also need to publish if the product is in advertise mode */
4248     if (!msi_check_publish(package))
4249         return ERROR_SUCCESS;
4250
4251     rc = MSIREG_OpenUninstallKey(package->ProductCode, &hkey, TRUE);
4252     if (rc != ERROR_SUCCESS)
4253         return rc;
4254
4255     rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context,
4256                                  NULL, &props, TRUE);
4257     if (rc != ERROR_SUCCESS)
4258         goto done;
4259
4260     msi_make_package_local(package, props);
4261
4262     rc = msi_publish_install_properties(package, hkey);
4263     if (rc != ERROR_SUCCESS)
4264         goto done;
4265
4266     rc = msi_publish_install_properties(package, props);
4267     if (rc != ERROR_SUCCESS)
4268         goto done;
4269
4270     upgrade_code = msi_dup_property(package, szUpgradeCode);
4271     if (upgrade_code)
4272     {
4273         MSIREG_OpenUpgradeCodesKey(upgrade_code, &upgrade, TRUE);
4274         squash_guid(package->ProductCode, squashed_pc);
4275         msi_reg_set_val_str(upgrade, squashed_pc, NULL);
4276         RegCloseKey(upgrade);
4277         msi_free(upgrade_code);
4278     }
4279
4280 done:
4281     RegCloseKey(hkey);
4282
4283     return ERROR_SUCCESS;
4284 }
4285
4286 static UINT ACTION_InstallExecute(MSIPACKAGE *package)
4287 {
4288     return execute_script(package,INSTALL_SCRIPT);
4289 }
4290
4291 static UINT msi_unpublish_product(MSIPACKAGE *package)
4292 {
4293     LPWSTR upgrade;
4294     LPWSTR remove = NULL;
4295     LPWSTR *features = NULL;
4296     BOOL full_uninstall = TRUE;
4297     MSIFEATURE *feature;
4298
4299     static const WCHAR szRemove[] = {'R','E','M','O','V','E',0};
4300     static const WCHAR szAll[] = {'A','L','L',0};
4301     static const WCHAR szUpgradeCode[] =
4302         {'U','p','g','r','a','d','e','C','o','d','e',0};
4303
4304     remove = msi_dup_property(package, szRemove);
4305     if (!remove)
4306         return ERROR_SUCCESS;
4307
4308     features = msi_split_string(remove, ',');
4309     if (!features)
4310     {
4311         msi_free(remove);
4312         ERR("REMOVE feature list is empty!\n");
4313         return ERROR_FUNCTION_FAILED;
4314     }
4315
4316     if (!lstrcmpW(features[0], szAll))
4317         full_uninstall = TRUE;
4318     else
4319     {
4320         LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4321         {
4322             if (feature->Action != INSTALLSTATE_ABSENT)
4323                 full_uninstall = FALSE;
4324         }
4325     }
4326
4327     if (!full_uninstall)
4328         goto done;
4329
4330     MSIREG_DeleteProductKey(package->ProductCode);
4331     MSIREG_DeleteUserDataProductKey(package->ProductCode);
4332     MSIREG_DeleteUninstallKey(package->ProductCode);
4333
4334     if (package->Context == MSIINSTALLCONTEXT_MACHINE)
4335     {
4336         MSIREG_DeleteLocalClassesProductKey(package->ProductCode);
4337         MSIREG_DeleteLocalClassesFeaturesKey(package->ProductCode);
4338     }
4339     else
4340     {
4341         MSIREG_DeleteUserProductKey(package->ProductCode);
4342         MSIREG_DeleteUserFeaturesKey(package->ProductCode);
4343     }
4344
4345     upgrade = msi_dup_property(package, szUpgradeCode);
4346     if (upgrade)
4347     {
4348         MSIREG_DeleteUserUpgradeCodesKey(upgrade);
4349         msi_free(upgrade);
4350     }
4351
4352 done:
4353     msi_free(remove);
4354     msi_free(features);
4355     return ERROR_SUCCESS;
4356 }
4357
4358 static UINT ACTION_InstallFinalize(MSIPACKAGE *package)
4359 {
4360     UINT rc;
4361
4362     rc = msi_unpublish_product(package);
4363     if (rc != ERROR_SUCCESS)
4364         return rc;
4365
4366     /* turn off scheduling */
4367     package->script->CurrentlyScripting= FALSE;
4368
4369     /* first do the same as an InstallExecute */
4370     rc = ACTION_InstallExecute(package);
4371     if (rc != ERROR_SUCCESS)
4372         return rc;
4373
4374     /* then handle Commit Actions */
4375     rc = execute_script(package,COMMIT_SCRIPT);
4376
4377     return rc;
4378 }
4379
4380 UINT ACTION_ForceReboot(MSIPACKAGE *package)
4381 {
4382     static const WCHAR RunOnce[] = {
4383     'S','o','f','t','w','a','r','e','\\',
4384     'M','i','c','r','o','s','o','f','t','\\',
4385     'W','i','n','d','o','w','s','\\',
4386     'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
4387     'R','u','n','O','n','c','e',0};
4388     static const WCHAR InstallRunOnce[] = {
4389     'S','o','f','t','w','a','r','e','\\',
4390     'M','i','c','r','o','s','o','f','t','\\',
4391     'W','i','n','d','o','w','s','\\',
4392     'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
4393     'I','n','s','t','a','l','l','e','r','\\',
4394     'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
4395
4396     static const WCHAR msiexec_fmt[] = {
4397     '%','s',
4398     '\\','M','s','i','E','x','e','c','.','e','x','e',' ','/','@',' ',
4399     '\"','%','s','\"',0};
4400     static const WCHAR install_fmt[] = {
4401     '/','I',' ','\"','%','s','\"',' ',
4402     'A','F','T','E','R','R','E','B','O','O','T','=','1',' ',
4403     'R','U','N','O','N','C','E','E','N','T','R','Y','=','\"','%','s','\"',0};
4404     WCHAR buffer[256], sysdir[MAX_PATH];
4405     HKEY hkey;
4406     WCHAR squished_pc[100];
4407
4408     squash_guid(package->ProductCode,squished_pc);
4409
4410     GetSystemDirectoryW(sysdir, sizeof(sysdir)/sizeof(sysdir[0]));
4411     RegCreateKeyW(HKEY_LOCAL_MACHINE,RunOnce,&hkey);
4412     snprintfW(buffer,sizeof(buffer)/sizeof(buffer[0]),msiexec_fmt,sysdir,
4413      squished_pc);
4414
4415     msi_reg_set_val_str( hkey, squished_pc, buffer );
4416     RegCloseKey(hkey);
4417
4418     TRACE("Reboot command %s\n",debugstr_w(buffer));
4419
4420     RegCreateKeyW(HKEY_LOCAL_MACHINE,InstallRunOnce,&hkey);
4421     sprintfW(buffer,install_fmt,package->ProductCode,squished_pc);
4422
4423     msi_reg_set_val_str( hkey, squished_pc, buffer );
4424     RegCloseKey(hkey);
4425
4426     return ERROR_INSTALL_SUSPEND;
4427 }
4428
4429 static UINT ACTION_ResolveSource(MSIPACKAGE* package)
4430 {
4431     DWORD attrib;
4432     UINT rc;
4433
4434     /*
4435      * We are currently doing what should be done here in the top level Install
4436      * however for Administrative and uninstalls this step will be needed
4437      */
4438     if (!package->PackagePath)
4439         return ERROR_SUCCESS;
4440
4441     msi_set_sourcedir_props(package, TRUE);
4442
4443     attrib = GetFileAttributesW(package->db->path);
4444     if (attrib == INVALID_FILE_ATTRIBUTES)
4445     {
4446         LPWSTR prompt;
4447         LPWSTR msg;
4448         DWORD size = 0;
4449
4450         rc = MsiSourceListGetInfoW(package->ProductCode, NULL, 
4451                 package->Context, MSICODE_PRODUCT,
4452                 INSTALLPROPERTY_DISKPROMPTW,NULL,&size);
4453         if (rc == ERROR_MORE_DATA)
4454         {
4455             prompt = msi_alloc(size * sizeof(WCHAR));
4456             MsiSourceListGetInfoW(package->ProductCode, NULL, 
4457                     package->Context, MSICODE_PRODUCT,
4458                     INSTALLPROPERTY_DISKPROMPTW,prompt,&size);
4459         }
4460         else
4461             prompt = strdupW(package->db->path);
4462
4463         msg = generate_error_string(package,1302,1,prompt);
4464         while(attrib == INVALID_FILE_ATTRIBUTES)
4465         {
4466             rc = MessageBoxW(NULL,msg,NULL,MB_OKCANCEL);
4467             if (rc == IDCANCEL)
4468             {
4469                 rc = ERROR_INSTALL_USEREXIT;
4470                 break;
4471             }
4472             attrib = GetFileAttributesW(package->db->path);
4473         }
4474         msi_free(prompt);
4475         rc = ERROR_SUCCESS;
4476     }
4477     else
4478         return ERROR_SUCCESS;
4479
4480     return rc;
4481 }
4482
4483 static UINT ACTION_RegisterUser(MSIPACKAGE *package)
4484 {
4485     HKEY hkey=0;
4486     LPWSTR buffer;
4487     LPWSTR productid;
4488     UINT rc,i;
4489
4490     static const WCHAR szPropKeys[][80] = 
4491     {
4492         {'P','r','o','d','u','c','t','I','D',0},
4493         {'U','S','E','R','N','A','M','E',0},
4494         {'C','O','M','P','A','N','Y','N','A','M','E',0},
4495         {0},
4496     };
4497
4498     static const WCHAR szRegKeys[][80] = 
4499     {
4500         {'P','r','o','d','u','c','t','I','D',0},
4501         {'R','e','g','O','w','n','e','r',0},
4502         {'R','e','g','C','o','m','p','a','n','y',0},
4503         {0},
4504     };
4505
4506     if (msi_check_unpublish(package))
4507     {
4508         MSIREG_DeleteUserDataProductKey(package->ProductCode);
4509         return ERROR_SUCCESS;
4510     }
4511
4512     productid = msi_dup_property( package, INSTALLPROPERTY_PRODUCTIDW );
4513     if (!productid)
4514         return ERROR_SUCCESS;
4515
4516     rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context,
4517                                  NULL, &hkey, TRUE);
4518     if (rc != ERROR_SUCCESS)
4519         goto end;
4520
4521     for( i = 0; szPropKeys[i][0]; i++ )
4522     {
4523         buffer = msi_dup_property( package, szPropKeys[i] );
4524         msi_reg_set_val_str( hkey, szRegKeys[i], buffer );
4525         msi_free( buffer );
4526     }
4527
4528 end:
4529     msi_free(productid);
4530     RegCloseKey(hkey);
4531
4532     /* FIXME: call ui_actiondata */
4533
4534     return rc;
4535 }
4536
4537
4538 static UINT ACTION_ExecuteAction(MSIPACKAGE *package)
4539 {
4540     UINT rc;
4541
4542     package->script->InWhatSequence |= SEQUENCE_EXEC;
4543     rc = ACTION_ProcessExecSequence(package,FALSE);
4544     return rc;
4545 }
4546
4547
4548 static UINT ITERATE_PublishComponent(MSIRECORD *rec, LPVOID param)
4549 {
4550     MSIPACKAGE *package = param;
4551     LPCWSTR compgroupid=NULL;
4552     LPCWSTR feature=NULL;
4553     LPCWSTR text = NULL;
4554     LPCWSTR qualifier = NULL;
4555     LPCWSTR component = NULL;
4556     LPWSTR advertise = NULL;
4557     LPWSTR output = NULL;
4558     HKEY hkey;
4559     UINT rc = ERROR_SUCCESS;
4560     MSICOMPONENT *comp;
4561     DWORD sz = 0;
4562     MSIRECORD *uirow;
4563
4564     component = MSI_RecordGetString(rec,3);
4565     comp = get_loaded_component(package,component);
4566
4567     if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL ) && 
4568        !ACTION_VerifyComponentForAction( comp, INSTALLSTATE_SOURCE ) &&
4569        !ACTION_VerifyComponentForAction( comp, INSTALLSTATE_ADVERTISED ))
4570     {
4571         TRACE("Skipping: Component %s not scheduled for install\n",
4572                         debugstr_w(component));
4573
4574         return ERROR_SUCCESS;
4575     }
4576
4577     compgroupid = MSI_RecordGetString(rec,1);
4578     qualifier = MSI_RecordGetString(rec,2);
4579
4580     rc = MSIREG_OpenUserComponentsKey(compgroupid, &hkey, TRUE);
4581     if (rc != ERROR_SUCCESS)
4582         goto end;
4583     
4584     text = MSI_RecordGetString(rec,4);
4585     feature = MSI_RecordGetString(rec,5);
4586   
4587     advertise = create_component_advertise_string(package, comp, feature);
4588
4589     sz = strlenW(advertise);
4590
4591     if (text)
4592         sz += lstrlenW(text);
4593
4594     sz+=3;
4595     sz *= sizeof(WCHAR);
4596            
4597     output = msi_alloc_zero(sz);
4598     strcpyW(output,advertise);
4599     msi_free(advertise);
4600
4601     if (text)
4602         strcatW(output,text);
4603
4604     msi_reg_set_val_multi_str( hkey, qualifier, output );
4605     
4606 end:
4607     RegCloseKey(hkey);
4608     msi_free(output);
4609
4610     /* the UI chunk */
4611     uirow = MSI_CreateRecord( 2 );
4612     MSI_RecordSetStringW( uirow, 1, compgroupid );
4613     MSI_RecordSetStringW( uirow, 2, qualifier);
4614     ui_actiondata( package, szPublishComponents, uirow);
4615     msiobj_release( &uirow->hdr );
4616     /* FIXME: call ui_progress? */
4617
4618     return rc;
4619 }
4620
4621 /*
4622  * At present I am ignorning the advertised components part of this and only
4623  * focusing on the qualified component sets
4624  */
4625 static UINT ACTION_PublishComponents(MSIPACKAGE *package)
4626 {
4627     UINT rc;
4628     MSIQUERY * view;
4629     static const WCHAR ExecSeqQuery[] =
4630         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4631          '`','P','u','b','l','i','s','h',
4632          'C','o','m','p','o','n','e','n','t','`',0};
4633     
4634     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4635     if (rc != ERROR_SUCCESS)
4636         return ERROR_SUCCESS;
4637
4638     rc = MSI_IterateRecords(view, NULL, ITERATE_PublishComponent, package);
4639     msiobj_release(&view->hdr);
4640
4641     return rc;
4642 }
4643
4644 static UINT ITERATE_InstallService(MSIRECORD *rec, LPVOID param)
4645 {
4646     MSIPACKAGE *package = param;
4647     MSIRECORD *row;
4648     MSIFILE *file;
4649     SC_HANDLE hscm, service = NULL;
4650     LPCWSTR comp, depends, pass;
4651     LPWSTR name = NULL, disp = NULL;
4652     LPCWSTR load_order, serv_name, key;
4653     DWORD serv_type, start_type;
4654     DWORD err_control;
4655
4656     static const WCHAR query[] =
4657         {'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
4658          '`','C','o','m','p','o','n','e','n','t','`',' ',
4659          'W','H','E','R','E',' ',
4660          '`','C','o','m','p','o','n','e','n','t','`',' ',
4661          '=','\'','%','s','\'',0};
4662
4663     hscm = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASEW, GENERIC_WRITE);
4664     if (!hscm)
4665     {
4666         ERR("Failed to open the SC Manager!\n");
4667         goto done;
4668     }
4669
4670     start_type = MSI_RecordGetInteger(rec, 5);
4671     if (start_type == SERVICE_BOOT_START || start_type == SERVICE_SYSTEM_START)
4672         goto done;
4673
4674     depends = MSI_RecordGetString(rec, 8);
4675     if (depends && *depends)
4676         FIXME("Dependency list unhandled!\n");
4677
4678     deformat_string(package, MSI_RecordGetString(rec, 2), &name);
4679     deformat_string(package, MSI_RecordGetString(rec, 3), &disp);
4680     serv_type = MSI_RecordGetInteger(rec, 4);
4681     err_control = MSI_RecordGetInteger(rec, 6);
4682     load_order = MSI_RecordGetString(rec, 7);
4683     serv_name = MSI_RecordGetString(rec, 9);
4684     pass = MSI_RecordGetString(rec, 10);
4685     comp = MSI_RecordGetString(rec, 12);
4686
4687     /* fetch the service path */
4688     row = MSI_QueryGetRecord(package->db, query, comp);
4689     if (!row)
4690     {
4691         ERR("Control query failed!\n");
4692         goto done;
4693     }
4694
4695     key = MSI_RecordGetString(row, 6);
4696
4697     file = get_loaded_file(package, key);
4698     msiobj_release(&row->hdr);
4699     if (!file)
4700     {
4701         ERR("Failed to load the service file\n");
4702         goto done;
4703     }
4704
4705     service = CreateServiceW(hscm, name, disp, GENERIC_ALL, serv_type,
4706                              start_type, err_control, file->TargetPath,
4707                              load_order, NULL, NULL, serv_name, pass);
4708     if (!service)
4709     {
4710         if (GetLastError() != ERROR_SERVICE_EXISTS)
4711             ERR("Failed to create service %s: %d\n", debugstr_w(name), GetLastError());
4712     }
4713
4714 done:
4715     CloseServiceHandle(service);
4716     CloseServiceHandle(hscm);
4717     msi_free(name);
4718     msi_free(disp);
4719
4720     return ERROR_SUCCESS;
4721 }
4722
4723 static UINT ACTION_InstallServices( MSIPACKAGE *package )
4724 {
4725     UINT rc;
4726     MSIQUERY * view;
4727     static const WCHAR ExecSeqQuery[] =
4728         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4729          'S','e','r','v','i','c','e','I','n','s','t','a','l','l',0};
4730     
4731     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4732     if (rc != ERROR_SUCCESS)
4733         return ERROR_SUCCESS;
4734
4735     rc = MSI_IterateRecords(view, NULL, ITERATE_InstallService, package);
4736     msiobj_release(&view->hdr);
4737
4738     return rc;
4739 }
4740
4741 /* converts arg1[~]arg2[~]arg3 to a list of ptrs to the strings */
4742 static LPCWSTR *msi_service_args_to_vector(LPWSTR args, DWORD *numargs)
4743 {
4744     LPCWSTR *vector, *temp_vector;
4745     LPWSTR p, q;
4746     DWORD sep_len;
4747
4748     static const WCHAR separator[] = {'[','~',']',0};
4749
4750     *numargs = 0;
4751     sep_len = sizeof(separator) / sizeof(WCHAR) - 1;
4752
4753     if (!args)
4754         return NULL;
4755
4756     vector = msi_alloc(sizeof(LPWSTR));
4757     if (!vector)
4758         return NULL;
4759
4760     p = args;
4761     do
4762     {
4763         (*numargs)++;
4764         vector[*numargs - 1] = p;
4765
4766         if ((q = strstrW(p, separator)))
4767         {
4768             *q = '\0';
4769
4770             temp_vector = msi_realloc(vector, (*numargs + 1) * sizeof(LPWSTR));
4771             if (!temp_vector)
4772             {
4773                 msi_free(vector);
4774                 return NULL;
4775             }
4776             vector = temp_vector;
4777
4778             p = q + sep_len;
4779         }
4780     } while (q);
4781
4782     return vector;
4783 }
4784
4785 static UINT ITERATE_StartService(MSIRECORD *rec, LPVOID param)
4786 {
4787     MSIPACKAGE *package = param;
4788     MSICOMPONENT *comp;
4789     SC_HANDLE scm, service = NULL;
4790     LPCWSTR name, *vector = NULL;
4791     LPWSTR args;
4792     DWORD event, numargs;
4793     UINT r = ERROR_FUNCTION_FAILED;
4794
4795     comp = get_loaded_component(package, MSI_RecordGetString(rec, 6));
4796     if (!comp || comp->Action == INSTALLSTATE_UNKNOWN || comp->Action == INSTALLSTATE_ABSENT)
4797         return ERROR_SUCCESS;
4798
4799     name = MSI_RecordGetString(rec, 2);
4800     event = MSI_RecordGetInteger(rec, 3);
4801     args = strdupW(MSI_RecordGetString(rec, 4));
4802
4803     if (!(event & msidbServiceControlEventStart))
4804         return ERROR_SUCCESS;
4805
4806     scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
4807     if (!scm)
4808     {
4809         ERR("Failed to open the service control manager\n");
4810         goto done;
4811     }
4812
4813     service = OpenServiceW(scm, name, SERVICE_START);
4814     if (!service)
4815     {
4816         ERR("Failed to open service %s\n", debugstr_w(name));
4817         goto done;
4818     }
4819
4820     vector = msi_service_args_to_vector(args, &numargs);
4821
4822     if (!StartServiceW(service, numargs, vector))
4823     {
4824         ERR("Failed to start service %s\n", debugstr_w(name));
4825         goto done;
4826     }
4827
4828     r = ERROR_SUCCESS;
4829
4830 done:
4831     CloseServiceHandle(service);
4832     CloseServiceHandle(scm);
4833
4834     msi_free(args);
4835     msi_free(vector);
4836     return r;
4837 }
4838
4839 static UINT ACTION_StartServices( MSIPACKAGE *package )
4840 {
4841     UINT rc;
4842     MSIQUERY *view;
4843
4844     static const WCHAR query[] = {
4845         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4846         'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
4847
4848     rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4849     if (rc != ERROR_SUCCESS)
4850         return ERROR_SUCCESS;
4851
4852     rc = MSI_IterateRecords(view, NULL, ITERATE_StartService, package);
4853     msiobj_release(&view->hdr);
4854
4855     return rc;
4856 }
4857
4858 static BOOL stop_service_dependents(SC_HANDLE scm, SC_HANDLE service)
4859 {
4860     DWORD i, needed, count;
4861     ENUM_SERVICE_STATUSW *dependencies;
4862     SERVICE_STATUS ss;
4863     SC_HANDLE depserv;
4864
4865     if (EnumDependentServicesW(service, SERVICE_ACTIVE, NULL,
4866                                0, &needed, &count))
4867         return TRUE;
4868
4869     if (GetLastError() != ERROR_MORE_DATA)
4870         return FALSE;
4871
4872     dependencies = msi_alloc(needed);
4873     if (!dependencies)
4874         return FALSE;
4875
4876     if (!EnumDependentServicesW(service, SERVICE_ACTIVE, dependencies,
4877                                 needed, &needed, &count))
4878         goto error;
4879
4880     for (i = 0; i < count; i++)
4881     {
4882         depserv = OpenServiceW(scm, dependencies[i].lpServiceName,
4883                                SERVICE_STOP | SERVICE_QUERY_STATUS);
4884         if (!depserv)
4885             goto error;
4886
4887         if (!ControlService(depserv, SERVICE_CONTROL_STOP, &ss))
4888             goto error;
4889     }
4890
4891     return TRUE;
4892
4893 error:
4894     msi_free(dependencies);
4895     return FALSE;
4896 }
4897
4898 static UINT ITERATE_StopService(MSIRECORD *rec, LPVOID param)
4899 {
4900     MSIPACKAGE *package = param;
4901     MSICOMPONENT *comp;
4902     SERVICE_STATUS status;
4903     SERVICE_STATUS_PROCESS ssp;
4904     SC_HANDLE scm = NULL, service = NULL;
4905     LPWSTR name, args;
4906     DWORD event, needed;
4907
4908     event = MSI_RecordGetInteger(rec, 3);
4909     if (!(event & msidbServiceControlEventStop))
4910         return ERROR_SUCCESS;
4911
4912     comp = get_loaded_component(package, MSI_RecordGetString(rec, 6));
4913     if (!comp || comp->Action == INSTALLSTATE_UNKNOWN || comp->Action == INSTALLSTATE_ABSENT)
4914         return ERROR_SUCCESS;
4915
4916     deformat_string(package, MSI_RecordGetString(rec, 2), &name);
4917     deformat_string(package, MSI_RecordGetString(rec, 4), &args);
4918     args = strdupW(MSI_RecordGetString(rec, 4));
4919
4920     scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
4921     if (!scm)
4922     {
4923         WARN("Failed to open the SCM: %d\n", GetLastError());
4924         goto done;
4925     }
4926
4927     service = OpenServiceW(scm, name,
4928                            SERVICE_STOP |
4929                            SERVICE_QUERY_STATUS |
4930                            SERVICE_ENUMERATE_DEPENDENTS);
4931     if (!service)
4932     {
4933         WARN("Failed to open service (%s): %d\n",
4934               debugstr_w(name), GetLastError());
4935         goto done;
4936     }
4937
4938     if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp,
4939                               sizeof(SERVICE_STATUS_PROCESS), &needed))
4940     {
4941         WARN("Failed to query service status (%s): %d\n",
4942              debugstr_w(name), GetLastError());
4943         goto done;
4944     }
4945
4946     if (ssp.dwCurrentState == SERVICE_STOPPED)
4947         goto done;
4948
4949     stop_service_dependents(scm, service);
4950
4951     if (!ControlService(service, SERVICE_CONTROL_STOP, &status))
4952         WARN("Failed to stop service (%s): %d\n", debugstr_w(name), GetLastError());
4953
4954 done:
4955     CloseServiceHandle(service);
4956     CloseServiceHandle(scm);
4957     msi_free(name);
4958     msi_free(args);
4959
4960     return ERROR_SUCCESS;
4961 }
4962
4963 static UINT ACTION_StopServices( MSIPACKAGE *package )
4964 {
4965     UINT rc;
4966     MSIQUERY *view;
4967
4968     static const WCHAR query[] = {
4969         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4970         'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
4971
4972     rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4973     if (rc != ERROR_SUCCESS)
4974         return ERROR_SUCCESS;
4975
4976     rc = MSI_IterateRecords(view, NULL, ITERATE_StopService, package);
4977     msiobj_release(&view->hdr);
4978
4979     return rc;
4980 }
4981
4982 static MSIFILE *msi_find_file( MSIPACKAGE *package, LPCWSTR filename )
4983 {
4984     MSIFILE *file;
4985
4986     LIST_FOR_EACH_ENTRY(file, &package->files, MSIFILE, entry)
4987     {
4988         if (!lstrcmpW(file->File, filename))
4989             return file;
4990     }
4991
4992     return NULL;
4993 }
4994
4995 static UINT ITERATE_InstallODBCDriver( MSIRECORD *rec, LPVOID param )
4996 {
4997     MSIPACKAGE *package = param;
4998     LPWSTR driver, driver_path, ptr;
4999     WCHAR outpath[MAX_PATH];
5000     MSIFILE *driver_file, *setup_file;
5001     LPCWSTR desc;
5002     DWORD len, usage;
5003     UINT r = ERROR_SUCCESS;
5004
5005     static const WCHAR driver_fmt[] = {
5006         'D','r','i','v','e','r','=','%','s',0};
5007     static const WCHAR setup_fmt[] = {
5008         'S','e','t','u','p','=','%','s',0};
5009     static const WCHAR usage_fmt[] = {
5010         'F','i','l','e','U','s','a','g','e','=','1',0};
5011
5012     desc = MSI_RecordGetString(rec, 3);
5013
5014     driver_file = msi_find_file(package, MSI_RecordGetString(rec, 4));
5015     setup_file = msi_find_file(package, MSI_RecordGetString(rec, 5));
5016
5017     if (!driver_file || !setup_file)
5018     {
5019         ERR("ODBC Driver entry not found!\n");
5020         return ERROR_FUNCTION_FAILED;
5021     }
5022
5023     len = lstrlenW(desc) + lstrlenW(driver_fmt) + lstrlenW(driver_file->FileName) +
5024           lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName) +
5025           lstrlenW(usage_fmt) + 1;
5026     driver = msi_alloc(len * sizeof(WCHAR));
5027     if (!driver)
5028         return ERROR_OUTOFMEMORY;
5029
5030     ptr = driver;
5031     lstrcpyW(ptr, desc);
5032     ptr += lstrlenW(ptr) + 1;
5033
5034     sprintfW(ptr, driver_fmt, driver_file->FileName);
5035     ptr += lstrlenW(ptr) + 1;
5036
5037     sprintfW(ptr, setup_fmt, setup_file->FileName);
5038     ptr += lstrlenW(ptr) + 1;
5039
5040     lstrcpyW(ptr, usage_fmt);
5041     ptr += lstrlenW(ptr) + 1;
5042     *ptr = '\0';
5043
5044     driver_path = strdupW(driver_file->TargetPath);
5045     ptr = strrchrW(driver_path, '\\');
5046     if (ptr) *ptr = '\0';
5047
5048     if (!SQLInstallDriverExW(driver, driver_path, outpath, MAX_PATH,
5049                              NULL, ODBC_INSTALL_COMPLETE, &usage))
5050     {
5051         ERR("Failed to install SQL driver!\n");
5052         r = ERROR_FUNCTION_FAILED;
5053     }
5054
5055     msi_free(driver);
5056     msi_free(driver_path);
5057
5058     return r;
5059 }
5060
5061 static UINT ITERATE_InstallODBCTranslator( MSIRECORD *rec, LPVOID param )
5062 {
5063     MSIPACKAGE *package = param;
5064     LPWSTR translator, translator_path, ptr;
5065     WCHAR outpath[MAX_PATH];
5066     MSIFILE *translator_file, *setup_file;
5067     LPCWSTR desc;
5068     DWORD len, usage;
5069     UINT r = ERROR_SUCCESS;
5070
5071     static const WCHAR translator_fmt[] = {
5072         'T','r','a','n','s','l','a','t','o','r','=','%','s',0};
5073     static const WCHAR setup_fmt[] = {
5074         'S','e','t','u','p','=','%','s',0};
5075
5076     desc = MSI_RecordGetString(rec, 3);
5077
5078     translator_file = msi_find_file(package, MSI_RecordGetString(rec, 4));
5079     setup_file = msi_find_file(package, MSI_RecordGetString(rec, 5));
5080
5081     if (!translator_file || !setup_file)
5082     {
5083         ERR("ODBC Translator entry not found!\n");
5084         return ERROR_FUNCTION_FAILED;
5085     }
5086
5087     len = lstrlenW(desc) + lstrlenW(translator_fmt) + lstrlenW(translator_file->FileName) +
5088           lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName) + 1;
5089     translator = msi_alloc(len * sizeof(WCHAR));
5090     if (!translator)
5091         return ERROR_OUTOFMEMORY;
5092
5093     ptr = translator;
5094     lstrcpyW(ptr, desc);
5095     ptr += lstrlenW(ptr) + 1;
5096
5097     sprintfW(ptr, translator_fmt, translator_file->FileName);
5098     ptr += lstrlenW(ptr) + 1;
5099
5100     sprintfW(ptr, setup_fmt, setup_file->FileName);
5101     ptr += lstrlenW(ptr) + 1;
5102     *ptr = '\0';
5103
5104     translator_path = strdupW(translator_file->TargetPath);
5105     ptr = strrchrW(translator_path, '\\');
5106     if (ptr) *ptr = '\0';
5107
5108     if (!SQLInstallTranslatorExW(translator, translator_path, outpath, MAX_PATH,
5109                                  NULL, ODBC_INSTALL_COMPLETE, &usage))
5110     {
5111         ERR("Failed to install SQL translator!\n");
5112         r = ERROR_FUNCTION_FAILED;
5113     }
5114
5115     msi_free(translator);
5116     msi_free(translator_path);
5117
5118     return r;
5119 }
5120
5121 static UINT ITERATE_InstallODBCDataSource( MSIRECORD *rec, LPVOID param )
5122 {
5123     LPWSTR attrs;
5124     LPCWSTR desc, driver;
5125     WORD request = ODBC_ADD_SYS_DSN;
5126     INT registration;
5127     DWORD len;
5128     UINT r = ERROR_SUCCESS;
5129
5130     static const WCHAR attrs_fmt[] = {
5131         'D','S','N','=','%','s',0 };
5132
5133     desc = MSI_RecordGetString(rec, 3);
5134     driver = MSI_RecordGetString(rec, 4);
5135     registration = MSI_RecordGetInteger(rec, 5);
5136
5137     if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_ADD_SYS_DSN;
5138     else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_ADD_DSN;
5139
5140     len = lstrlenW(attrs_fmt) + lstrlenW(desc) + 1 + 1;
5141     attrs = msi_alloc(len * sizeof(WCHAR));
5142     if (!attrs)
5143         return ERROR_OUTOFMEMORY;
5144
5145     sprintfW(attrs, attrs_fmt, desc);
5146     attrs[len - 1] = '\0';
5147
5148     if (!SQLConfigDataSourceW(NULL, request, driver, attrs))
5149     {
5150         ERR("Failed to install SQL data source!\n");
5151         r = ERROR_FUNCTION_FAILED;
5152     }
5153
5154     msi_free(attrs);
5155
5156     return r;
5157 }
5158
5159 static UINT ACTION_InstallODBC( MSIPACKAGE *package )
5160 {
5161     UINT rc;
5162     MSIQUERY *view;
5163
5164     static const WCHAR driver_query[] = {
5165         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5166         'O','D','B','C','D','r','i','v','e','r',0 };
5167
5168     static const WCHAR translator_query[] = {
5169         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5170         'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0 };
5171
5172     static const WCHAR source_query[] = {
5173         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5174         'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0 };
5175
5176     rc = MSI_DatabaseOpenViewW(package->db, driver_query, &view);
5177     if (rc != ERROR_SUCCESS)
5178         return ERROR_SUCCESS;
5179
5180     rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDriver, package);
5181     msiobj_release(&view->hdr);
5182
5183     rc = MSI_DatabaseOpenViewW(package->db, translator_query, &view);
5184     if (rc != ERROR_SUCCESS)
5185         return ERROR_SUCCESS;
5186
5187     rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCTranslator, package);
5188     msiobj_release(&view->hdr);
5189
5190     rc = MSI_DatabaseOpenViewW(package->db, source_query, &view);
5191     if (rc != ERROR_SUCCESS)
5192         return ERROR_SUCCESS;
5193
5194     rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDataSource, package);
5195     msiobj_release(&view->hdr);
5196
5197     return rc;
5198 }
5199
5200 #define ENV_ACT_SETALWAYS   0x1
5201 #define ENV_ACT_SETABSENT   0x2
5202 #define ENV_ACT_REMOVE      0x4
5203 #define ENV_ACT_REMOVEMATCH 0x8
5204
5205 #define ENV_MOD_MACHINE     0x20000000
5206 #define ENV_MOD_APPEND      0x40000000
5207 #define ENV_MOD_PREFIX      0x80000000
5208 #define ENV_MOD_MASK        0xC0000000
5209
5210 #define check_flag_combo(x, y) ((x) & ~(y)) == (y)
5211
5212 static LONG env_set_flags( LPCWSTR *name, LPCWSTR *value, DWORD *flags )
5213 {
5214     LPCWSTR cptr = *name;
5215     LPCWSTR ptr = *value;
5216
5217     static const WCHAR prefix[] = {'[','~',']',0};
5218     static const int prefix_len = 3;
5219
5220     *flags = 0;
5221     while (*cptr)
5222     {
5223         if (*cptr == '=')
5224             *flags |= ENV_ACT_SETALWAYS;
5225         else if (*cptr == '+')
5226             *flags |= ENV_ACT_SETABSENT;
5227         else if (*cptr == '-')
5228             *flags |= ENV_ACT_REMOVE;
5229         else if (*cptr == '!')
5230             *flags |= ENV_ACT_REMOVEMATCH;
5231         else if (*cptr == '*')
5232             *flags |= ENV_MOD_MACHINE;
5233         else
5234             break;
5235
5236         cptr++;
5237         (*name)++;
5238     }
5239
5240     if (!*cptr)
5241     {
5242         ERR("Missing environment variable\n");
5243         return ERROR_FUNCTION_FAILED;
5244     }
5245
5246     if (!strncmpW(ptr, prefix, prefix_len))
5247     {
5248         *flags |= ENV_MOD_APPEND;
5249         *value += lstrlenW(prefix);
5250     }
5251     else if (lstrlenW(*value) >= prefix_len)
5252     {
5253         ptr += lstrlenW(ptr) - prefix_len;
5254         if (!lstrcmpW(ptr, prefix))
5255         {
5256             *flags |= ENV_MOD_PREFIX;
5257             /* the "[~]" will be removed by deformat_string */;
5258         }
5259     }
5260
5261     if (!*flags ||
5262         check_flag_combo(*flags, ENV_ACT_SETALWAYS | ENV_ACT_SETABSENT) ||
5263         check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETABSENT) ||
5264         check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETALWAYS) ||
5265         check_flag_combo(*flags, ENV_ACT_SETABSENT | ENV_MOD_MASK))
5266     {
5267         ERR("Invalid flags: %08x\n", *flags);
5268         return ERROR_FUNCTION_FAILED;
5269     }
5270
5271     return ERROR_SUCCESS;
5272 }
5273
5274 static UINT ITERATE_WriteEnvironmentString( MSIRECORD *rec, LPVOID param )
5275 {
5276     MSIPACKAGE *package = param;
5277     LPCWSTR name, value;
5278     LPWSTR data = NULL, newval = NULL;
5279     LPWSTR deformatted = NULL, ptr;
5280     DWORD flags, type, size;
5281     LONG res;
5282     HKEY env = NULL, root;
5283     LPCWSTR environment;
5284
5285     static const WCHAR user_env[] =
5286         {'E','n','v','i','r','o','n','m','e','n','t',0};
5287     static const WCHAR machine_env[] =
5288         {'S','y','s','t','e','m','\\',
5289          'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
5290          'C','o','n','t','r','o','l','\\',
5291          'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r','\\',
5292          'E','n','v','i','r','o','n','m','e','n','t',0};
5293     static const WCHAR semicolon[] = {';',0};
5294
5295     name = MSI_RecordGetString(rec, 2);
5296     value = MSI_RecordGetString(rec, 3);
5297
5298     res = env_set_flags(&name, &value, &flags);
5299     if (res != ERROR_SUCCESS)
5300        goto done;
5301
5302     deformat_string(package, value, &deformatted);
5303     if (!deformatted)
5304     {
5305         res = ERROR_OUTOFMEMORY;
5306         goto done;
5307     }
5308
5309     value = deformatted;
5310
5311     if (flags & ENV_MOD_MACHINE)
5312     {
5313         environment = machine_env;
5314         root = HKEY_LOCAL_MACHINE;
5315     }
5316     else
5317     {
5318         environment = user_env;
5319         root = HKEY_CURRENT_USER;
5320     }
5321
5322     res = RegCreateKeyExW(root, environment, 0, NULL, 0,
5323                           KEY_ALL_ACCESS, NULL, &env, NULL);
5324     if (res != ERROR_SUCCESS)
5325         goto done;
5326
5327     if (flags & ENV_ACT_REMOVE)
5328         FIXME("Not removing environment variable on uninstall!\n");
5329
5330     size = 0;
5331     res = RegQueryValueExW(env, name, NULL, &type, NULL, &size);
5332     if ((res != ERROR_SUCCESS && res != ERROR_FILE_NOT_FOUND) ||
5333         (res == ERROR_SUCCESS && type != REG_SZ && type != REG_EXPAND_SZ))
5334         goto done;
5335
5336     if (res != ERROR_FILE_NOT_FOUND)
5337     {
5338         if (flags & ENV_ACT_SETABSENT)
5339         {
5340             res = ERROR_SUCCESS;
5341             goto done;
5342         }
5343
5344         data = msi_alloc(size);
5345         if (!data)
5346         {
5347             RegCloseKey(env);
5348             return ERROR_OUTOFMEMORY;
5349         }
5350
5351         res = RegQueryValueExW(env, name, NULL, &type, (LPVOID)data, &size);
5352         if (res != ERROR_SUCCESS)
5353             goto done;
5354
5355         if (flags & ENV_ACT_REMOVEMATCH && (!value || !lstrcmpW(data, value)))
5356         {
5357             res = RegDeleteKeyW(env, name);
5358             goto done;
5359         }
5360
5361         size =  (lstrlenW(value) + 1 + size) * sizeof(WCHAR);
5362         newval =  msi_alloc(size);
5363         ptr = newval;
5364         if (!newval)
5365         {
5366             res = ERROR_OUTOFMEMORY;
5367             goto done;
5368         }
5369
5370         if (!(flags & ENV_MOD_MASK))
5371             lstrcpyW(newval, value);
5372         else
5373         {
5374             if (flags & ENV_MOD_PREFIX)
5375             {
5376                 lstrcpyW(newval, value);
5377                 lstrcatW(newval, semicolon);
5378                 ptr = newval + lstrlenW(value) + 1;
5379             }
5380
5381             lstrcpyW(ptr, data);
5382
5383             if (flags & ENV_MOD_APPEND)
5384             {
5385                 lstrcatW(newval, semicolon);
5386                 lstrcatW(newval, value);
5387             }
5388         }
5389     }
5390     else
5391     {
5392         size = (lstrlenW(value) + 1) * sizeof(WCHAR);
5393         newval = msi_alloc(size);
5394         if (!newval)
5395         {
5396             res = ERROR_OUTOFMEMORY;
5397             goto done;
5398         }
5399
5400         lstrcpyW(newval, value);
5401     }
5402
5403     TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(newval));
5404     res = RegSetValueExW(env, name, 0, type, (LPVOID)newval, size);
5405
5406 done:
5407     if (env) RegCloseKey(env);
5408     msi_free(deformatted);
5409     msi_free(data);
5410     msi_free(newval);
5411     return res;
5412 }
5413
5414 static UINT ACTION_WriteEnvironmentStrings( MSIPACKAGE *package )
5415 {
5416     UINT rc;
5417     MSIQUERY * view;
5418     static const WCHAR ExecSeqQuery[] =
5419         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5420          '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
5421     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
5422     if (rc != ERROR_SUCCESS)
5423         return ERROR_SUCCESS;
5424
5425     rc = MSI_IterateRecords(view, NULL, ITERATE_WriteEnvironmentString, package);
5426     msiobj_release(&view->hdr);
5427
5428     return rc;
5429 }
5430
5431 #define is_dot_dir(x) ((x[0] == '.') && ((x[1] == 0) || ((x[1] == '.') && (x[2] == 0))))
5432
5433 typedef struct
5434 {
5435     struct list entry;
5436     LPWSTR sourcename;
5437     LPWSTR destname;
5438     LPWSTR source;
5439     LPWSTR dest;
5440 } FILE_LIST;
5441
5442 static BOOL msi_move_file(LPCWSTR source, LPCWSTR dest, int options)
5443 {
5444     BOOL ret;
5445
5446     if (GetFileAttributesW(source) == FILE_ATTRIBUTE_DIRECTORY ||
5447         GetFileAttributesW(dest) == FILE_ATTRIBUTE_DIRECTORY)
5448     {
5449         WARN("Source or dest is directory, not moving\n");
5450         return FALSE;
5451     }
5452
5453     if (options == msidbMoveFileOptionsMove)
5454     {
5455         TRACE("moving %s -> %s\n", debugstr_w(source), debugstr_w(dest));
5456         ret = MoveFileExW(source, dest, MOVEFILE_REPLACE_EXISTING);
5457         if (!ret)
5458         {
5459             WARN("MoveFile failed: %d\n", GetLastError());
5460             return FALSE;
5461         }
5462     }
5463     else
5464     {
5465         TRACE("copying %s -> %s\n", debugstr_w(source), debugstr_w(dest));
5466         ret = CopyFileW(source, dest, FALSE);
5467         if (!ret)
5468         {
5469             WARN("CopyFile failed: %d\n", GetLastError());
5470             return FALSE;
5471         }
5472     }
5473
5474     return TRUE;
5475 }
5476
5477 static LPWSTR wildcard_to_file(LPWSTR wildcard, LPWSTR filename)
5478 {
5479     LPWSTR path, ptr;
5480     DWORD dirlen, pathlen;
5481
5482     ptr = strrchrW(wildcard, '\\');
5483     dirlen = ptr - wildcard + 1;
5484
5485     pathlen = dirlen + lstrlenW(filename) + 1;
5486     path = msi_alloc(pathlen * sizeof(WCHAR));
5487
5488     lstrcpynW(path, wildcard, dirlen + 1);
5489     lstrcatW(path, filename);
5490
5491     return path;
5492 }
5493
5494 static void free_file_entry(FILE_LIST *file)
5495 {
5496     msi_free(file->source);
5497     msi_free(file->dest);
5498     msi_free(file);
5499 }
5500
5501 static void free_list(FILE_LIST *list)
5502 {
5503     while (!list_empty(&list->entry))
5504     {
5505         FILE_LIST *file = LIST_ENTRY(list_head(&list->entry), FILE_LIST, entry);
5506
5507         list_remove(&file->entry);
5508         free_file_entry(file);
5509     }
5510 }
5511
5512 static BOOL add_wildcard(FILE_LIST *files, LPWSTR source, LPWSTR dest)
5513 {
5514     FILE_LIST *new, *file;
5515     LPWSTR ptr, filename;
5516     DWORD size;
5517
5518     new = msi_alloc_zero(sizeof(FILE_LIST));
5519     if (!new)
5520         return FALSE;
5521
5522     new->source = strdupW(source);
5523     ptr = strrchrW(dest, '\\') + 1;
5524     filename = strrchrW(new->source, '\\') + 1;
5525
5526     new->sourcename = filename;
5527
5528     if (*ptr)
5529         new->destname = ptr;
5530     else
5531         new->destname = new->sourcename;
5532
5533     size = (ptr - dest) + lstrlenW(filename) + 1;
5534     new->dest = msi_alloc(size * sizeof(WCHAR));
5535     if (!new->dest)
5536     {
5537         free_file_entry(new);
5538         return FALSE;
5539     }
5540
5541     lstrcpynW(new->dest, dest, ptr - dest + 1);
5542     lstrcatW(new->dest, filename);
5543
5544     if (list_empty(&files->entry))
5545     {
5546         list_add_head(&files->entry, &new->entry);
5547         return TRUE;
5548     }
5549
5550     LIST_FOR_EACH_ENTRY(file, &files->entry, FILE_LIST, entry)
5551     {
5552         if (lstrcmpW(source, file->source) < 0)
5553         {
5554             list_add_before(&file->entry, &new->entry);
5555             return TRUE;
5556         }
5557     }
5558
5559     list_add_after(&file->entry, &new->entry);
5560     return TRUE;
5561 }
5562
5563 static BOOL move_files_wildcard(LPWSTR source, LPWSTR dest, int options)
5564 {
5565     WIN32_FIND_DATAW wfd;
5566     HANDLE hfile;
5567     LPWSTR path;
5568     BOOL res;
5569     FILE_LIST files, *file;
5570     DWORD size;
5571
5572     hfile = FindFirstFileW(source, &wfd);
5573     if (hfile == INVALID_HANDLE_VALUE) return FALSE;
5574
5575     list_init(&files.entry);
5576
5577     for (res = TRUE; res; res = FindNextFileW(hfile, &wfd))
5578     {
5579         if (is_dot_dir(wfd.cFileName)) continue;
5580
5581         path = wildcard_to_file(source, wfd.cFileName);
5582         if (!path)
5583         {
5584             res = FALSE;
5585             goto done;
5586         }
5587
5588         add_wildcard(&files, path, dest);
5589         msi_free(path);
5590     }
5591
5592     /* no files match the wildcard */
5593     if (list_empty(&files.entry))
5594         goto done;
5595
5596     /* only the first wildcard match gets renamed to dest */
5597     file = LIST_ENTRY(list_head(&files.entry), FILE_LIST, entry);
5598     size = (strrchrW(file->dest, '\\') - file->dest) + lstrlenW(file->destname) + 2;
5599     file->dest = msi_realloc(file->dest, size * sizeof(WCHAR));
5600     if (!file->dest)
5601     {
5602         res = FALSE;
5603         goto done;
5604     }
5605
5606     lstrcpyW(strrchrW(file->dest, '\\') + 1, file->destname);
5607
5608     while (!list_empty(&files.entry))
5609     {
5610         file = LIST_ENTRY(list_head(&files.entry), FILE_LIST, entry);
5611
5612         msi_move_file(file->source, file->dest, options);
5613
5614         list_remove(&file->entry);
5615         free_file_entry(file);
5616     }
5617
5618     res = TRUE;
5619
5620 done:
5621     free_list(&files);
5622     FindClose(hfile);
5623     return res;
5624 }
5625
5626 static UINT ITERATE_MoveFiles( MSIRECORD *rec, LPVOID param )
5627 {
5628     MSIPACKAGE *package = param;
5629     MSICOMPONENT *comp;
5630     LPCWSTR sourcename;
5631     LPWSTR destname = NULL;
5632     LPWSTR sourcedir = NULL, destdir = NULL;
5633     LPWSTR source = NULL, dest = NULL;
5634     int options;
5635     DWORD size;
5636     BOOL ret, wildcards;
5637
5638     static const WCHAR backslash[] = {'\\',0};
5639
5640     comp = get_loaded_component(package, MSI_RecordGetString(rec, 2));
5641     if (!comp || !comp->Enabled ||
5642         !(comp->Action & (INSTALLSTATE_LOCAL | INSTALLSTATE_SOURCE)))
5643     {
5644         TRACE("Component not set for install, not moving file\n");
5645         return ERROR_SUCCESS;
5646     }
5647
5648     sourcename = MSI_RecordGetString(rec, 3);
5649     options = MSI_RecordGetInteger(rec, 7);
5650
5651     sourcedir = msi_dup_property(package, MSI_RecordGetString(rec, 5));
5652     if (!sourcedir)
5653         goto done;
5654
5655     destdir = msi_dup_property(package, MSI_RecordGetString(rec, 6));
5656     if (!destdir)
5657         goto done;
5658
5659     if (!sourcename)
5660     {
5661         if (GetFileAttributesW(sourcedir) == INVALID_FILE_ATTRIBUTES)
5662             goto done;
5663
5664         source = strdupW(sourcedir);
5665         if (!source)
5666             goto done;
5667     }
5668     else
5669     {
5670         size = lstrlenW(sourcedir) + lstrlenW(sourcename) + 2;
5671         source = msi_alloc(size * sizeof(WCHAR));
5672         if (!source)
5673             goto done;
5674
5675         lstrcpyW(source, sourcedir);
5676         if (source[lstrlenW(source) - 1] != '\\')
5677             lstrcatW(source, backslash);
5678         lstrcatW(source, sourcename);
5679     }
5680
5681     wildcards = strchrW(source, '*') || strchrW(source, '?');
5682
5683     if (MSI_RecordIsNull(rec, 4))
5684     {
5685         if (!wildcards)
5686         {
5687             destname = strdupW(sourcename);
5688             if (!destname)
5689                 goto done;
5690         }
5691     }
5692     else
5693     {
5694         destname = strdupW(MSI_RecordGetString(rec, 4));
5695         if (destname)
5696             reduce_to_longfilename(destname);
5697     }
5698
5699     size = 0;
5700     if (destname)
5701         size = lstrlenW(destname);
5702
5703     size += lstrlenW(destdir) + 2;
5704     dest = msi_alloc(size * sizeof(WCHAR));
5705     if (!dest)
5706         goto done;
5707
5708     lstrcpyW(dest, destdir);
5709     if (dest[lstrlenW(dest) - 1] != '\\')
5710         lstrcatW(dest, backslash);
5711
5712     if (destname)
5713         lstrcatW(dest, destname);
5714
5715     if (GetFileAttributesW(destdir) == INVALID_FILE_ATTRIBUTES)
5716     {
5717         ret = CreateDirectoryW(destdir, NULL);
5718         if (!ret)
5719         {
5720             WARN("CreateDirectory failed: %d\n", GetLastError());
5721             return ERROR_SUCCESS;
5722         }
5723     }
5724
5725     if (!wildcards)
5726         msi_move_file(source, dest, options);
5727     else
5728         move_files_wildcard(source, dest, options);
5729
5730 done:
5731     msi_free(sourcedir);
5732     msi_free(destdir);
5733     msi_free(destname);
5734     msi_free(source);
5735     msi_free(dest);
5736
5737     return ERROR_SUCCESS;
5738 }
5739
5740 static UINT ACTION_MoveFiles( MSIPACKAGE *package )
5741 {
5742     UINT rc;
5743     MSIQUERY *view;
5744
5745     static const WCHAR ExecSeqQuery[] =
5746         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5747          '`','M','o','v','e','F','i','l','e','`',0};
5748
5749     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
5750     if (rc != ERROR_SUCCESS)
5751         return ERROR_SUCCESS;
5752
5753     rc = MSI_IterateRecords(view, NULL, ITERATE_MoveFiles, package);
5754     msiobj_release(&view->hdr);
5755
5756     return rc;
5757 }
5758
5759 typedef struct tagMSIASSEMBLY
5760 {
5761     struct list entry;
5762     MSICOMPONENT *component;
5763     MSIFEATURE *feature;
5764     MSIFILE *file;
5765     LPWSTR manifest;
5766     LPWSTR application;
5767     DWORD attributes;
5768     BOOL installed;
5769 } MSIASSEMBLY;
5770
5771 static HRESULT (WINAPI *pCreateAssemblyCache)(IAssemblyCache **ppAsmCache,
5772                                               DWORD dwReserved);
5773 static HRESULT (WINAPI *pLoadLibraryShim)(LPCWSTR szDllName, LPCWSTR szVersion,
5774                                           LPVOID pvReserved, HMODULE *phModDll);
5775
5776 static BOOL init_functionpointers(void)
5777 {
5778     HRESULT hr;
5779     HMODULE hfusion;
5780     HMODULE hmscoree;
5781
5782     static const WCHAR szFusion[] = {'f','u','s','i','o','n','.','d','l','l',0};
5783
5784     hmscoree = LoadLibraryA("mscoree.dll");
5785     if (!hmscoree)
5786     {
5787         WARN("mscoree.dll not available\n");
5788         return FALSE;
5789     }
5790
5791     pLoadLibraryShim = (void *)GetProcAddress(hmscoree, "LoadLibraryShim");
5792     if (!pLoadLibraryShim)
5793     {
5794         WARN("LoadLibraryShim not available\n");
5795         FreeLibrary(hmscoree);
5796         return FALSE;
5797     }
5798
5799     hr = pLoadLibraryShim(szFusion, NULL, NULL, &hfusion);
5800     if (FAILED(hr))
5801     {
5802         WARN("fusion.dll not available\n");
5803         FreeLibrary(hmscoree);
5804         return FALSE;
5805     }
5806
5807     pCreateAssemblyCache = (void *)GetProcAddress(hfusion, "CreateAssemblyCache");
5808
5809     FreeLibrary(hmscoree);
5810     return TRUE;
5811 }
5812
5813 static UINT install_assembly(MSIPACKAGE *package, MSIASSEMBLY *assembly,
5814                              LPWSTR path)
5815 {
5816     IAssemblyCache *cache;
5817     HRESULT hr;
5818     UINT r = ERROR_FUNCTION_FAILED;
5819
5820     TRACE("installing assembly: %s\n", debugstr_w(path));
5821
5822     if (assembly->feature)
5823         msi_feature_set_state(package, assembly->feature, INSTALLSTATE_LOCAL);
5824
5825     if (assembly->manifest)
5826         FIXME("Manifest unhandled\n");
5827
5828     if (assembly->application)
5829     {
5830         FIXME("Assembly should be privately installed\n");
5831         return ERROR_SUCCESS;
5832     }
5833
5834     if (assembly->attributes == msidbAssemblyAttributesWin32)
5835     {
5836         FIXME("Win32 assemblies not handled\n");
5837         return ERROR_SUCCESS;
5838     }
5839
5840     hr = pCreateAssemblyCache(&cache, 0);
5841     if (FAILED(hr))
5842         goto done;
5843
5844     hr = IAssemblyCache_InstallAssembly(cache, 0, path, NULL);
5845     if (FAILED(hr))
5846         ERR("Failed to install assembly: %s %08x\n", debugstr_w(path), hr);
5847
5848     r = ERROR_SUCCESS;
5849
5850 done:
5851     IAssemblyCache_Release(cache);
5852     return r;
5853 }
5854
5855 typedef struct tagASSEMBLY_LIST
5856 {
5857     MSIPACKAGE *package;
5858     IAssemblyCache *cache;
5859     struct list *assemblies;
5860 } ASSEMBLY_LIST;
5861
5862 typedef struct tagASSEMBLY_NAME
5863 {
5864     LPWSTR name;
5865     LPWSTR version;
5866     LPWSTR culture;
5867     LPWSTR pubkeytoken;
5868 } ASSEMBLY_NAME;
5869
5870 static UINT parse_assembly_name(MSIRECORD *rec, LPVOID param)
5871 {
5872     ASSEMBLY_NAME *asmname = param;
5873     LPCWSTR name = MSI_RecordGetString(rec, 2);
5874     LPWSTR val = msi_dup_record_field(rec, 3);
5875
5876     static const WCHAR Name[] = {'N','a','m','e',0};
5877     static const WCHAR Version[] = {'V','e','r','s','i','o','n',0};
5878     static const WCHAR Culture[] = {'C','u','l','t','u','r','e',0};
5879     static const WCHAR PublicKeyToken[] = {
5880         'P','u','b','l','i','c','K','e','y','T','o','k','e','n',0};
5881
5882     if (!strcmpiW(name, Name))
5883         asmname->name = val;
5884     else if (!strcmpiW(name, Version))
5885         asmname->version = val;
5886     else if (!strcmpiW(name, Culture))
5887         asmname->culture = val;
5888     else if (!strcmpiW(name, PublicKeyToken))
5889         asmname->pubkeytoken = val;
5890     else
5891         msi_free(val);
5892
5893     return ERROR_SUCCESS;
5894 }
5895
5896 static void append_str(LPWSTR *str, DWORD *size, LPCWSTR append)
5897 {
5898     if (!*str)
5899     {
5900         *size = lstrlenW(append) + 1;
5901         *str = msi_alloc((*size) * sizeof(WCHAR));
5902         lstrcpyW(*str, append);
5903         return;
5904     }
5905
5906     (*size) += lstrlenW(append);
5907     *str = msi_realloc(*str, (*size) * sizeof(WCHAR));
5908     lstrcatW(*str, append);
5909 }
5910
5911 static BOOL check_assembly_installed(MSIDATABASE *db, IAssemblyCache *cache,
5912                                      MSICOMPONENT *comp)
5913 {
5914     ASSEMBLY_INFO asminfo;
5915     ASSEMBLY_NAME name;
5916     MSIQUERY *view;
5917     LPWSTR disp;
5918     DWORD size;
5919     BOOL found;
5920     UINT r;
5921
5922     static const WCHAR separator[] = {',',' ',0};
5923     static const WCHAR Version[] = {'V','e','r','s','i','o','n','=',0};
5924     static const WCHAR Culture[] = {'C','u','l','t','u','r','e','=',0};
5925     static const WCHAR PublicKeyToken[] = {
5926         'P','u','b','l','i','c','K','e','y','T','o','k','e','n','=',0};
5927     static const WCHAR query[] = {
5928         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5929         '`','M','s','i','A','s','s','e','m','b','l','y','N','a','m','e','`',' ',
5930         'W','H','E','R','E',' ','`','C','o','m','p','o','n','e','n','t','_','`',
5931         '=','\'','%','s','\'',0};
5932
5933     disp = NULL;
5934     found = FALSE;
5935     ZeroMemory(&name, sizeof(ASSEMBLY_NAME));
5936     ZeroMemory(&asminfo, sizeof(ASSEMBLY_INFO));
5937
5938     r = MSI_OpenQuery(db, &view, query, comp->Component);
5939     if (r != ERROR_SUCCESS)
5940         return ERROR_SUCCESS;
5941
5942     MSI_IterateRecords(view, NULL, parse_assembly_name, &name);
5943     msiobj_release(&view->hdr);
5944
5945     if (!name.name)
5946     {
5947         ERR("No assembly name specified!\n");
5948         goto done;
5949     }
5950
5951     append_str(&disp, &size, name.name);
5952
5953     if (name.version)
5954     {
5955         append_str(&disp, &size, separator);
5956         append_str(&disp, &size, Version);
5957         append_str(&disp, &size, name.version);
5958     }
5959
5960     if (name.culture)
5961     {
5962         append_str(&disp, &size, separator);
5963         append_str(&disp, &size, Culture);
5964         append_str(&disp, &size, name.culture);
5965     }
5966
5967     if (name.pubkeytoken)
5968     {
5969         append_str(&disp, &size, separator);
5970         append_str(&disp, &size, PublicKeyToken);
5971         append_str(&disp, &size, name.pubkeytoken);
5972     }
5973
5974     asminfo.cbAssemblyInfo = sizeof(ASSEMBLY_INFO);
5975     IAssemblyCache_QueryAssemblyInfo(cache, QUERYASMINFO_FLAG_VALIDATE,
5976                                      disp, &asminfo);
5977     found = (asminfo.dwAssemblyFlags == ASSEMBLYINFO_FLAG_INSTALLED);
5978
5979 done:
5980     msi_free(disp);
5981     msi_free(name.name);
5982     msi_free(name.version);
5983     msi_free(name.culture);
5984     msi_free(name.pubkeytoken);
5985
5986     return found;
5987 }
5988
5989 static UINT load_assembly(MSIRECORD *rec, LPVOID param)
5990 {
5991     ASSEMBLY_LIST *list = param;
5992     MSIASSEMBLY *assembly;
5993
5994     assembly = msi_alloc_zero(sizeof(MSIASSEMBLY));
5995     if (!assembly)
5996         return ERROR_OUTOFMEMORY;
5997
5998     assembly->component = get_loaded_component(list->package, MSI_RecordGetString(rec, 1));
5999
6000     if (!assembly->component || !assembly->component->Enabled ||
6001         !(assembly->component->Action & (INSTALLSTATE_LOCAL | INSTALLSTATE_SOURCE)))
6002     {
6003         TRACE("Component not set for install, not publishing assembly\n");
6004         msi_free(assembly);
6005         return ERROR_SUCCESS;
6006     }
6007
6008     assembly->feature = find_feature_by_name(list->package, MSI_RecordGetString(rec, 2));
6009     assembly->file = msi_find_file(list->package, assembly->component->KeyPath);
6010
6011     if (!assembly->file)
6012     {
6013         ERR("File %s not found\n", debugstr_w(assembly->component->KeyPath));
6014         return ERROR_FUNCTION_FAILED;
6015     }
6016
6017     assembly->manifest = strdupW(MSI_RecordGetString(rec, 3));
6018     assembly->application = strdupW(MSI_RecordGetString(rec, 4));
6019     assembly->attributes = MSI_RecordGetInteger(rec, 5);
6020     assembly->installed = check_assembly_installed(list->package->db,
6021                                                    list->cache,
6022                                                    assembly->component);
6023
6024     list_add_head(list->assemblies, &assembly->entry);
6025     return ERROR_SUCCESS;
6026 }
6027
6028 static UINT load_assemblies(MSIPACKAGE *package, struct list *assemblies)
6029 {
6030     IAssemblyCache *cache = NULL;
6031     ASSEMBLY_LIST list;
6032     MSIQUERY *view;
6033     HRESULT hr;
6034     UINT r;
6035
6036     static const WCHAR query[] =
6037         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6038          '`','M','s','i','A','s','s','e','m','b','l','y','`',0};
6039
6040     r = MSI_DatabaseOpenViewW(package->db, query, &view);
6041     if (r != ERROR_SUCCESS)
6042         return ERROR_SUCCESS;
6043
6044     hr = pCreateAssemblyCache(&cache, 0);
6045     if (FAILED(hr))
6046         return ERROR_FUNCTION_FAILED;
6047
6048     list.package = package;
6049     list.cache = cache;
6050     list.assemblies = assemblies;
6051
6052     r = MSI_IterateRecords(view, NULL, load_assembly, &list);
6053     msiobj_release(&view->hdr);
6054
6055     IAssemblyCache_Release(cache);
6056
6057     return r;
6058 }
6059
6060 static void free_assemblies(struct list *assemblies)
6061 {
6062     struct list *item, *cursor;
6063
6064     LIST_FOR_EACH_SAFE(item, cursor, assemblies)
6065     {
6066         MSIASSEMBLY *assembly = LIST_ENTRY(item, MSIASSEMBLY, entry);
6067
6068         list_remove(&assembly->entry);
6069         msi_free(assembly->application);
6070         msi_free(assembly->manifest);
6071         msi_free(assembly);
6072     }
6073 }
6074
6075 static BOOL find_assembly(struct list *assemblies, LPCWSTR file, MSIASSEMBLY **out)
6076 {
6077     MSIASSEMBLY *assembly;
6078
6079     LIST_FOR_EACH_ENTRY(assembly, assemblies, MSIASSEMBLY, entry)
6080     {
6081         if (!lstrcmpW(assembly->file->File, file))
6082         {
6083             *out = assembly;
6084             return TRUE;
6085         }
6086     }
6087
6088     return FALSE;
6089 }
6090
6091 static BOOL installassembly_cb(MSIPACKAGE *package, LPCWSTR file, DWORD action,
6092                                LPWSTR *path, DWORD *attrs, PVOID user)
6093 {
6094     MSIASSEMBLY *assembly;
6095     WCHAR temppath[MAX_PATH];
6096     struct list *assemblies = user;
6097     UINT r;
6098
6099     if (!find_assembly(assemblies, file, &assembly))
6100         return FALSE;
6101
6102     GetTempPathW(MAX_PATH, temppath);
6103     PathAddBackslashW(temppath);
6104     lstrcatW(temppath, assembly->file->FileName);
6105
6106     if (action == MSICABEXTRACT_BEGINEXTRACT)
6107     {
6108         if (assembly->installed)
6109             return FALSE;
6110
6111         *path = strdupW(temppath);
6112         *attrs = assembly->file->Attributes;
6113     }
6114     else if (action == MSICABEXTRACT_FILEEXTRACTED)
6115     {
6116         assembly->installed = TRUE;
6117
6118         r = install_assembly(package, assembly, temppath);
6119         if (r != ERROR_SUCCESS)
6120             ERR("Failed to install assembly\n");
6121     }
6122
6123     return TRUE;
6124 }
6125
6126 static UINT ACTION_MsiPublishAssemblies( MSIPACKAGE *package )
6127 {
6128     UINT r;
6129     struct list assemblies = LIST_INIT(assemblies);
6130     MSIASSEMBLY *assembly;
6131     MSIMEDIAINFO *mi;
6132
6133     if (!init_functionpointers() || !pCreateAssemblyCache)
6134         return ERROR_FUNCTION_FAILED;
6135
6136     r = load_assemblies(package, &assemblies);
6137     if (r != ERROR_SUCCESS)
6138         goto done;
6139
6140     if (list_empty(&assemblies))
6141         goto done;
6142
6143     mi = msi_alloc_zero(sizeof(MSIMEDIAINFO));
6144     if (!mi)
6145     {
6146         r = ERROR_OUTOFMEMORY;
6147         goto done;
6148     }
6149
6150     LIST_FOR_EACH_ENTRY(assembly, &assemblies, MSIASSEMBLY, entry)
6151     {
6152         if (assembly->installed && !mi->is_continuous)
6153             continue;
6154
6155         if (assembly->file->Sequence > mi->last_sequence || mi->is_continuous ||
6156             (assembly->file->IsCompressed && !mi->is_extracted))
6157         {
6158             MSICABDATA data;
6159
6160             r = ready_media(package, assembly->file, mi);
6161             if (r != ERROR_SUCCESS)
6162             {
6163                 ERR("Failed to ready media\n");
6164                 break;
6165             }
6166
6167             data.mi = mi;
6168             data.package = package;
6169             data.cb = installassembly_cb;
6170             data.user = &assemblies;
6171
6172             if (assembly->file->IsCompressed &&
6173                 !msi_cabextract(package, mi, &data))
6174             {
6175                 ERR("Failed to extract cabinet: %s\n", debugstr_w(mi->cabinet));
6176                 r = ERROR_FUNCTION_FAILED;
6177                 break;
6178             }
6179         }
6180
6181         if (!assembly->file->IsCompressed)
6182         {
6183             LPWSTR source = resolve_file_source(package, assembly->file);
6184
6185             r = install_assembly(package, assembly, source);
6186             if (r != ERROR_SUCCESS)
6187                 ERR("Failed to install assembly\n");
6188
6189             msi_free(source);
6190         }
6191
6192         /* FIXME: write Installer assembly reg values */
6193     }
6194
6195 done:
6196     free_assemblies(&assemblies);
6197     return r;
6198 }
6199
6200 static UINT msi_unimplemented_action_stub( MSIPACKAGE *package,
6201                                            LPCSTR action, LPCWSTR table )
6202 {
6203     static const WCHAR query[] = {
6204         'S','E','L','E','C','T',' ','*',' ',
6205         'F','R','O','M',' ','`','%','s','`',0 };
6206     MSIQUERY *view = NULL;
6207     DWORD count = 0;
6208     UINT r;
6209     
6210     r = MSI_OpenQuery( package->db, &view, query, table );
6211     if (r == ERROR_SUCCESS)
6212     {
6213         r = MSI_IterateRecords(view, &count, NULL, package);
6214         msiobj_release(&view->hdr);
6215     }
6216
6217     if (count)
6218         FIXME("%s -> %u ignored %s table values\n",
6219               action, count, debugstr_w(table));
6220
6221     return ERROR_SUCCESS;
6222 }
6223
6224 static UINT ACTION_AllocateRegistrySpace( MSIPACKAGE *package )
6225 {
6226     TRACE("%p\n", package);
6227     return ERROR_SUCCESS;
6228 }
6229
6230 static UINT ACTION_RemoveIniValues( MSIPACKAGE *package )
6231 {
6232     static const WCHAR table[] =
6233          {'R','e','m','o','v','e','I','n','i','F','i','l','e',0 };
6234     return msi_unimplemented_action_stub( package, "RemoveIniValues", table );
6235 }
6236
6237 static UINT ACTION_PatchFiles( MSIPACKAGE *package )
6238 {
6239     static const WCHAR table[] = { 'P','a','t','c','h',0 };
6240     return msi_unimplemented_action_stub( package, "PatchFiles", table );
6241 }
6242
6243 static UINT ACTION_BindImage( MSIPACKAGE *package )
6244 {
6245     static const WCHAR table[] = { 'B','i','n','d','I','m','a','g','e',0 };
6246     return msi_unimplemented_action_stub( package, "BindImage", table );
6247 }
6248
6249 static UINT ACTION_IsolateComponents( MSIPACKAGE *package )
6250 {
6251     static const WCHAR table[] = {
6252         'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t',0 };
6253     return msi_unimplemented_action_stub( package, "IsolateComponents", table );
6254 }
6255
6256 static UINT ACTION_MigrateFeatureStates( MSIPACKAGE *package )
6257 {
6258     static const WCHAR table[] = { 'U','p','g','r','a','d','e',0 };
6259     return msi_unimplemented_action_stub( package, "MigrateFeatureStates", table );
6260 }
6261
6262 static UINT ACTION_SelfUnregModules( MSIPACKAGE *package )
6263 {
6264     static const WCHAR table[] = { 'S','e','l','f','R','e','g',0 };
6265     return msi_unimplemented_action_stub( package, "SelfUnregModules", table );
6266 }
6267
6268 static UINT ACTION_DeleteServices( MSIPACKAGE *package )
6269 {
6270     static const WCHAR table[] = {
6271         'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
6272     return msi_unimplemented_action_stub( package, "DeleteServices", table );
6273 }
6274 static UINT ACTION_ValidateProductID( MSIPACKAGE *package )
6275 {
6276         static const WCHAR table[] = {
6277                 'P','r','o','d','u','c','t','I','D',0 };
6278         return msi_unimplemented_action_stub( package, "ValidateProductID", table );
6279 }
6280
6281 static UINT ACTION_RemoveEnvironmentStrings( MSIPACKAGE *package )
6282 {
6283     static const WCHAR table[] = {
6284         'E','n','v','i','r','o','n','m','e','n','t',0 };
6285     return msi_unimplemented_action_stub( package, "RemoveEnvironmentStrings", table );
6286 }
6287
6288 static UINT ACTION_MsiUnpublishAssemblies( MSIPACKAGE *package )
6289 {
6290     static const WCHAR table[] = {
6291         'M','s','i','A','s','s','e','m','b','l','y',0 };
6292     return msi_unimplemented_action_stub( package, "MsiUnpublishAssemblies", table );
6293 }
6294
6295 static UINT ACTION_UnregisterFonts( MSIPACKAGE *package )
6296 {
6297     static const WCHAR table[] = { 'F','o','n','t',0 };
6298     return msi_unimplemented_action_stub( package, "UnregisterFonts", table );
6299 }
6300
6301 static UINT ACTION_RMCCPSearch( MSIPACKAGE *package )
6302 {
6303     static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
6304     return msi_unimplemented_action_stub( package, "RMCCPSearch", table );
6305 }
6306
6307 static UINT ACTION_RegisterComPlus( MSIPACKAGE *package )
6308 {
6309     static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
6310     return msi_unimplemented_action_stub( package, "RegisterComPlus", table );
6311 }
6312
6313 static UINT ACTION_UnregisterComPlus( MSIPACKAGE *package )
6314 {
6315     static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
6316     return msi_unimplemented_action_stub( package, "UnregisterComPlus", table );
6317 }
6318
6319 static UINT ACTION_InstallSFPCatalogFile( MSIPACKAGE *package )
6320 {
6321     static const WCHAR table[] = { 'S','F','P','C','a','t','a','l','o','g',0 };
6322     return msi_unimplemented_action_stub( package, "InstallSFPCatalogFile", table );
6323 }
6324
6325 static UINT ACTION_RemoveDuplicateFiles( MSIPACKAGE *package )
6326 {
6327     static const WCHAR table[] = { 'D','u','p','l','i','c','a','t','e','F','i','l','e',0 };
6328     return msi_unimplemented_action_stub( package, "RemoveDuplicateFiles", table );
6329 }
6330
6331 static UINT ACTION_RemoveExistingProducts( MSIPACKAGE *package )
6332 {
6333     static const WCHAR table[] = { 'U','p','g','r','a','d','e',0 };
6334     return msi_unimplemented_action_stub( package, "RemoveExistingProducts", table );
6335 }
6336
6337 static UINT ACTION_RemoveFolders( MSIPACKAGE *package )
6338 {
6339     static const WCHAR table[] = { 'C','r','e','a','t','e','F','o','l','d','e','r',0 };
6340     return msi_unimplemented_action_stub( package, "RemoveFolders", table );
6341 }
6342
6343 static UINT ACTION_RemoveODBC( MSIPACKAGE *package )
6344 {
6345     static const WCHAR table[] = { 'O','D','B','C','D','r','i','v','e','r',0 };
6346     return msi_unimplemented_action_stub( package, "RemoveODBC", table );
6347 }
6348
6349 static UINT ACTION_RemoveRegistryValues( MSIPACKAGE *package )
6350 {
6351     static const WCHAR table[] = { 'R','e','m','o','v','e','R','e','g','i','s','t','r','y',0 };
6352     return msi_unimplemented_action_stub( package, "RemoveRegistryValues", table );
6353 }
6354
6355 static UINT ACTION_RemoveShortcuts( MSIPACKAGE *package )
6356 {
6357     static const WCHAR table[] = { 'S','h','o','r','t','c','u','t',0 };
6358     return msi_unimplemented_action_stub( package, "RemoveShortcuts", table );
6359 }
6360
6361 static UINT ACTION_UnpublishComponents( MSIPACKAGE *package )
6362 {
6363     static const WCHAR table[] = { 'P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t',0 };
6364     return msi_unimplemented_action_stub( package, "UnpublishComponents", table );
6365 }
6366
6367 static UINT ACTION_UnregisterClassInfo( MSIPACKAGE *package )
6368 {
6369     static const WCHAR table[] = { 'A','p','p','I','d',0 };
6370     return msi_unimplemented_action_stub( package, "UnregisterClassInfo", table );
6371 }
6372
6373 static UINT ACTION_UnregisterExtensionInfo( MSIPACKAGE *package )
6374 {
6375     static const WCHAR table[] = { 'E','x','t','e','n','s','i','o','n',0 };
6376     return msi_unimplemented_action_stub( package, "UnregisterExtensionInfo", table );
6377 }
6378
6379 static UINT ACTION_UnregisterMIMEInfo( MSIPACKAGE *package )
6380 {
6381     static const WCHAR table[] = { 'M','I','M','E',0 };
6382     return msi_unimplemented_action_stub( package, "UnregisterMIMEInfo", table );
6383 }
6384
6385 static UINT ACTION_UnregisterProgIdInfo( MSIPACKAGE *package )
6386 {
6387     static const WCHAR table[] = { 'P','r','o','g','I','d',0 };
6388     return msi_unimplemented_action_stub( package, "UnregisterProgIdInfo", table );
6389 }
6390
6391 static UINT ACTION_UnregisterTypeLibraries( MSIPACKAGE *package )
6392 {
6393     static const WCHAR table[] = { 'T','y','p','e','L','i','b',0 };
6394     return msi_unimplemented_action_stub( package, "UnregisterTypeLibraries", table );
6395 }
6396
6397 static const struct _actions StandardActions[] = {
6398     { szAllocateRegistrySpace, ACTION_AllocateRegistrySpace },
6399     { szAppSearch, ACTION_AppSearch },
6400     { szBindImage, ACTION_BindImage },
6401     { szCCPSearch, ACTION_CCPSearch },
6402     { szCostFinalize, ACTION_CostFinalize },
6403     { szCostInitialize, ACTION_CostInitialize },
6404     { szCreateFolders, ACTION_CreateFolders },
6405     { szCreateShortcuts, ACTION_CreateShortcuts },
6406     { szDeleteServices, ACTION_DeleteServices },
6407     { szDisableRollback, NULL },
6408     { szDuplicateFiles, ACTION_DuplicateFiles },
6409     { szExecuteAction, ACTION_ExecuteAction },
6410     { szFileCost, ACTION_FileCost },
6411     { szFindRelatedProducts, ACTION_FindRelatedProducts },
6412     { szForceReboot, ACTION_ForceReboot },
6413     { szInstallAdminPackage, NULL },
6414     { szInstallExecute, ACTION_InstallExecute },
6415     { szInstallExecuteAgain, ACTION_InstallExecute },
6416     { szInstallFiles, ACTION_InstallFiles},
6417     { szInstallFinalize, ACTION_InstallFinalize },
6418     { szInstallInitialize, ACTION_InstallInitialize },
6419     { szInstallSFPCatalogFile, ACTION_InstallSFPCatalogFile },
6420     { szInstallValidate, ACTION_InstallValidate },
6421     { szIsolateComponents, ACTION_IsolateComponents },
6422     { szLaunchConditions, ACTION_LaunchConditions },
6423     { szMigrateFeatureStates, ACTION_MigrateFeatureStates },
6424     { szMoveFiles, ACTION_MoveFiles },
6425     { szMsiPublishAssemblies, ACTION_MsiPublishAssemblies },
6426     { szMsiUnpublishAssemblies, ACTION_MsiUnpublishAssemblies },
6427     { szInstallODBC, ACTION_InstallODBC },
6428     { szInstallServices, ACTION_InstallServices },
6429     { szPatchFiles, ACTION_PatchFiles },
6430     { szProcessComponents, ACTION_ProcessComponents },
6431     { szPublishComponents, ACTION_PublishComponents },
6432     { szPublishFeatures, ACTION_PublishFeatures },
6433     { szPublishProduct, ACTION_PublishProduct },
6434     { szRegisterClassInfo, ACTION_RegisterClassInfo },
6435     { szRegisterComPlus, ACTION_RegisterComPlus},
6436     { szRegisterExtensionInfo, ACTION_RegisterExtensionInfo },
6437     { szRegisterFonts, ACTION_RegisterFonts },
6438     { szRegisterMIMEInfo, ACTION_RegisterMIMEInfo },
6439     { szRegisterProduct, ACTION_RegisterProduct },
6440     { szRegisterProgIdInfo, ACTION_RegisterProgIdInfo },
6441     { szRegisterTypeLibraries, ACTION_RegisterTypeLibraries },
6442     { szRegisterUser, ACTION_RegisterUser },
6443     { szRemoveDuplicateFiles, ACTION_RemoveDuplicateFiles },
6444     { szRemoveEnvironmentStrings, ACTION_RemoveEnvironmentStrings },
6445     { szRemoveExistingProducts, ACTION_RemoveExistingProducts },
6446     { szRemoveFiles, ACTION_RemoveFiles },
6447     { szRemoveFolders, ACTION_RemoveFolders },
6448     { szRemoveIniValues, ACTION_RemoveIniValues },
6449     { szRemoveODBC, ACTION_RemoveODBC },
6450     { szRemoveRegistryValues, ACTION_RemoveRegistryValues },
6451     { szRemoveShortcuts, ACTION_RemoveShortcuts },
6452     { szResolveSource, ACTION_ResolveSource },
6453     { szRMCCPSearch, ACTION_RMCCPSearch },
6454     { szScheduleReboot, NULL },
6455     { szSelfRegModules, ACTION_SelfRegModules },
6456     { szSelfUnregModules, ACTION_SelfUnregModules },
6457     { szSetODBCFolders, NULL },
6458     { szStartServices, ACTION_StartServices },
6459     { szStopServices, ACTION_StopServices },
6460     { szUnpublishComponents, ACTION_UnpublishComponents },
6461     { szUnpublishFeatures, ACTION_UnpublishFeatures },
6462     { szUnregisterClassInfo, ACTION_UnregisterClassInfo },
6463     { szUnregisterComPlus, ACTION_UnregisterComPlus },
6464     { szUnregisterExtensionInfo, ACTION_UnregisterExtensionInfo },
6465     { szUnregisterFonts, ACTION_UnregisterFonts },
6466     { szUnregisterMIMEInfo, ACTION_UnregisterMIMEInfo },
6467     { szUnregisterProgIdInfo, ACTION_UnregisterProgIdInfo },
6468     { szUnregisterTypeLibraries, ACTION_UnregisterTypeLibraries },
6469     { szValidateProductID, ACTION_ValidateProductID },
6470     { szWriteEnvironmentStrings, ACTION_WriteEnvironmentStrings },
6471     { szWriteIniValues, ACTION_WriteIniValues },
6472     { szWriteRegistryValues, ACTION_WriteRegistryValues },
6473     { NULL, NULL },
6474 };
6475
6476 static BOOL ACTION_HandleStandardAction(MSIPACKAGE *package, LPCWSTR action,
6477                                         UINT* rc, BOOL force )
6478 {
6479     BOOL ret = FALSE;
6480     BOOL run = force;
6481     int i;
6482
6483     if (!run && !package->script->CurrentlyScripting)
6484         run = TRUE;
6485
6486     if (!run)
6487     {
6488         if (strcmpW(action,szInstallFinalize) == 0 ||
6489             strcmpW(action,szInstallExecute) == 0 ||
6490             strcmpW(action,szInstallExecuteAgain) == 0)
6491                 run = TRUE;
6492     }
6493
6494     i = 0;
6495     while (StandardActions[i].action != NULL)
6496     {
6497         if (strcmpW(StandardActions[i].action, action)==0)
6498         {
6499             if (!run)
6500             {
6501                 ui_actioninfo(package, action, TRUE, 0);
6502                 *rc = schedule_action(package,INSTALL_SCRIPT,action);
6503                 ui_actioninfo(package, action, FALSE, *rc);
6504             }
6505             else
6506             {
6507                 ui_actionstart(package, action);
6508                 if (StandardActions[i].handler)
6509                 {
6510                     *rc = StandardActions[i].handler(package);
6511                 }
6512                 else
6513                 {
6514                     FIXME("unhandled standard action %s\n",debugstr_w(action));
6515                     *rc = ERROR_SUCCESS;
6516                 }
6517             }
6518             ret = TRUE;
6519             break;
6520         }
6521         i++;
6522     }
6523     return ret;
6524 }