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