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