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