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