msi: Set the action taken in the ProcessComponents and InstallFiles actions.
[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
2366     if (type == REG_SZ)
2367         MSI_RecordSetStringW(uirow,3,(LPWSTR)value_data);
2368     else
2369         MSI_RecordSetStringW(uirow,3,value);
2370
2371     ui_actiondata(package,szWriteRegistryValues,uirow);
2372     msiobj_release( &uirow->hdr );
2373
2374     msi_free(value_data);
2375     msi_free(deformated);
2376     msi_free(uikey);
2377
2378     return ERROR_SUCCESS;
2379 }
2380
2381 static UINT ACTION_WriteRegistryValues(MSIPACKAGE *package)
2382 {
2383     UINT rc;
2384     MSIQUERY * view;
2385     static const WCHAR ExecSeqQuery[] =
2386         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2387          '`','R','e','g','i','s','t','r','y','`',0 };
2388
2389     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2390     if (rc != ERROR_SUCCESS)
2391         return ERROR_SUCCESS;
2392
2393     /* increment progress bar each time action data is sent */
2394     ui_progress(package,1,REG_PROGRESS_VALUE,1,0);
2395
2396     rc = MSI_IterateRecords(view, NULL, ITERATE_WriteRegistryValues, package);
2397
2398     msiobj_release(&view->hdr);
2399     return rc;
2400 }
2401
2402 static void delete_reg_key_or_value( HKEY hkey_root, LPCWSTR key, LPCWSTR value, BOOL delete_key )
2403 {
2404     LONG res;
2405     HKEY hkey;
2406     DWORD num_subkeys, num_values;
2407
2408     if (delete_key)
2409     {
2410         if ((res = RegDeleteTreeW( hkey_root, key )))
2411         {
2412             WARN("Failed to delete key %s (%d)\n", debugstr_w(key), res);
2413         }
2414         return;
2415     }
2416
2417     if (!(res = RegOpenKeyW( hkey_root, key, &hkey )))
2418     {
2419         if ((res = RegDeleteValueW( hkey, value )))
2420         {
2421             WARN("Failed to delete value %s (%d)\n", debugstr_w(value), res);
2422         }
2423         res = RegQueryInfoKeyW( hkey, NULL, NULL, NULL, &num_subkeys, NULL, NULL, &num_values,
2424                                 NULL, NULL, NULL, NULL );
2425         RegCloseKey( hkey );
2426
2427         if (!res && !num_subkeys && !num_values)
2428         {
2429             TRACE("Removing empty key %s\n", debugstr_w(key));
2430             RegDeleteKeyW( hkey_root, key );
2431         }
2432         return;
2433     }
2434     WARN("Failed to open key %s (%d)\n", debugstr_w(key), res);
2435 }
2436
2437
2438 static UINT ITERATE_RemoveRegistryValuesOnUninstall( MSIRECORD *row, LPVOID param )
2439 {
2440     MSIPACKAGE *package = param;
2441     LPCWSTR component, name, key_str, root_key_str;
2442     LPWSTR deformated_key, deformated_name, ui_key_str;
2443     MSICOMPONENT *comp;
2444     MSIRECORD *uirow;
2445     BOOL delete_key = FALSE;
2446     HKEY hkey_root;
2447     UINT size;
2448     INT root;
2449
2450     ui_progress( package, 2, 0, 0, 0 );
2451
2452     component = MSI_RecordGetString( row, 6 );
2453     comp = get_loaded_component( package, component );
2454     if (!comp)
2455         return ERROR_SUCCESS;
2456
2457     if (comp->ActionRequest != INSTALLSTATE_ABSENT)
2458     {
2459         TRACE("Component not scheduled for removal: %s\n", debugstr_w(component));
2460         comp->Action = comp->Installed;
2461         return ERROR_SUCCESS;
2462     }
2463     comp->Action = INSTALLSTATE_ABSENT;
2464
2465     name = MSI_RecordGetString( row, 4 );
2466     if (MSI_RecordIsNull( row, 5 ) && name )
2467     {
2468         if (name[0] == '+' && !name[1])
2469             return ERROR_SUCCESS;
2470         else if ((name[0] == '-' && !name[1]) || (name[0] == '*' && !name[1]))
2471         {
2472             delete_key = TRUE;
2473             name = NULL;
2474         }
2475     }
2476
2477     root = MSI_RecordGetInteger( row, 2 );
2478     key_str = MSI_RecordGetString( row, 3 );
2479
2480     root_key_str = get_root_key( package, root, &hkey_root );
2481     if (!root_key_str)
2482         return ERROR_SUCCESS;
2483
2484     deformat_string( package, key_str, &deformated_key );
2485     size = strlenW( deformated_key ) + strlenW( root_key_str ) + 1;
2486     ui_key_str = msi_alloc( size * sizeof(WCHAR) );
2487     strcpyW( ui_key_str, root_key_str );
2488     strcatW( ui_key_str, deformated_key );
2489
2490     deformat_string( package, name, &deformated_name );
2491
2492     delete_reg_key_or_value( hkey_root, deformated_key, deformated_name, delete_key );
2493     msi_free( deformated_key );
2494
2495     uirow = MSI_CreateRecord( 2 );
2496     MSI_RecordSetStringW( uirow, 1, ui_key_str );
2497     MSI_RecordSetStringW( uirow, 2, deformated_name );
2498
2499     ui_actiondata( package, szRemoveRegistryValues, uirow );
2500     msiobj_release( &uirow->hdr );
2501
2502     msi_free( ui_key_str );
2503     msi_free( deformated_name );
2504     return ERROR_SUCCESS;
2505 }
2506
2507 static UINT ITERATE_RemoveRegistryValuesOnInstall( MSIRECORD *row, LPVOID param )
2508 {
2509     MSIPACKAGE *package = param;
2510     LPCWSTR component, name, key_str, root_key_str;
2511     LPWSTR deformated_key, deformated_name, ui_key_str;
2512     MSICOMPONENT *comp;
2513     MSIRECORD *uirow;
2514     BOOL delete_key = FALSE;
2515     HKEY hkey_root;
2516     UINT size;
2517     INT root;
2518
2519     ui_progress( package, 2, 0, 0, 0 );
2520
2521     component = MSI_RecordGetString( row, 5 );
2522     comp = get_loaded_component( package, component );
2523     if (!comp)
2524         return ERROR_SUCCESS;
2525
2526     if (comp->ActionRequest != INSTALLSTATE_LOCAL)
2527     {
2528         TRACE("Component not scheduled for installation: %s\n", debugstr_w(component));
2529         comp->Action = comp->Installed;
2530         return ERROR_SUCCESS;
2531     }
2532     comp->Action = INSTALLSTATE_LOCAL;
2533
2534     if ((name = MSI_RecordGetString( row, 4 )))
2535     {
2536         if (name[0] == '-' && !name[1])
2537         {
2538             delete_key = TRUE;
2539             name = NULL;
2540         }
2541     }
2542
2543     root = MSI_RecordGetInteger( row, 2 );
2544     key_str = MSI_RecordGetString( row, 3 );
2545
2546     root_key_str = get_root_key( package, root, &hkey_root );
2547     if (!root_key_str)
2548         return ERROR_SUCCESS;
2549
2550     deformat_string( package, key_str, &deformated_key );
2551     size = strlenW( deformated_key ) + strlenW( root_key_str ) + 1;
2552     ui_key_str = msi_alloc( size * sizeof(WCHAR) );
2553     strcpyW( ui_key_str, root_key_str );
2554     strcatW( ui_key_str, deformated_key );
2555
2556     deformat_string( package, name, &deformated_name );
2557
2558     delete_reg_key_or_value( hkey_root, deformated_key, deformated_name, delete_key );
2559     msi_free( deformated_key );
2560
2561     uirow = MSI_CreateRecord( 2 );
2562     MSI_RecordSetStringW( uirow, 1, ui_key_str );
2563     MSI_RecordSetStringW( uirow, 2, deformated_name );
2564
2565     ui_actiondata( package, szRemoveRegistryValues, uirow );
2566     msiobj_release( &uirow->hdr );
2567
2568     msi_free( ui_key_str );
2569     msi_free( deformated_name );
2570     return ERROR_SUCCESS;
2571 }
2572
2573 static UINT ACTION_RemoveRegistryValues( MSIPACKAGE *package )
2574 {
2575     UINT rc;
2576     MSIQUERY *view;
2577     static const WCHAR registry_query[] =
2578         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2579          '`','R','e','g','i','s','t','r','y','`',0 };
2580     static const WCHAR remove_registry_query[] =
2581         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2582          '`','R','e','m','o','v','e','R','e','g','i','s','t','r','y','`',0 };
2583
2584     /* increment progress bar each time action data is sent */
2585     ui_progress( package, 1, REG_PROGRESS_VALUE, 1, 0 );
2586
2587     rc = MSI_DatabaseOpenViewW( package->db, registry_query, &view );
2588     if (rc == ERROR_SUCCESS)
2589     {
2590         rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveRegistryValuesOnUninstall, package );
2591         msiobj_release( &view->hdr );
2592         if (rc != ERROR_SUCCESS)
2593             return rc;
2594     }
2595
2596     rc = MSI_DatabaseOpenViewW( package->db, remove_registry_query, &view );
2597     if (rc == ERROR_SUCCESS)
2598     {
2599         rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveRegistryValuesOnInstall, package );
2600         msiobj_release( &view->hdr );
2601         if (rc != ERROR_SUCCESS)
2602             return rc;
2603     }
2604
2605     return ERROR_SUCCESS;
2606 }
2607
2608 static UINT ACTION_InstallInitialize(MSIPACKAGE *package)
2609 {
2610     package->script->CurrentlyScripting = TRUE;
2611
2612     return ERROR_SUCCESS;
2613 }
2614
2615
2616 static UINT ACTION_InstallValidate(MSIPACKAGE *package)
2617 {
2618     MSICOMPONENT *comp;
2619     DWORD progress = 0;
2620     DWORD total = 0;
2621     static const WCHAR q1[]=
2622         {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
2623          '`','R','e','g','i','s','t','r','y','`',0};
2624     UINT rc;
2625     MSIQUERY * view;
2626     MSIFEATURE *feature;
2627     MSIFILE *file;
2628
2629     TRACE("InstallValidate\n");
2630
2631     rc = MSI_DatabaseOpenViewW(package->db, q1, &view);
2632     if (rc == ERROR_SUCCESS)
2633     {
2634         MSI_IterateRecords( view, &progress, NULL, package );
2635         msiobj_release( &view->hdr );
2636         total += progress * REG_PROGRESS_VALUE;
2637     }
2638
2639     LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2640         total += COMPONENT_PROGRESS_VALUE;
2641
2642     LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2643         total += file->FileSize;
2644
2645     ui_progress(package,0,total,0,0);
2646
2647     LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2648     {
2649         TRACE("Feature: %s; Installed: %i; Action %i; Request %i\n",
2650             debugstr_w(feature->Feature), feature->Installed, feature->Action,
2651             feature->ActionRequest);
2652     }
2653     
2654     return ERROR_SUCCESS;
2655 }
2656
2657 static UINT ITERATE_LaunchConditions(MSIRECORD *row, LPVOID param)
2658 {
2659     MSIPACKAGE* package = param;
2660     LPCWSTR cond = NULL; 
2661     LPCWSTR message = NULL;
2662     UINT r;
2663
2664     static const WCHAR title[]=
2665         {'I','n','s','t','a','l','l',' ','F','a', 'i','l','e','d',0};
2666
2667     cond = MSI_RecordGetString(row,1);
2668
2669     r = MSI_EvaluateConditionW(package,cond);
2670     if (r == MSICONDITION_FALSE)
2671     {
2672         if ((gUILevel & INSTALLUILEVEL_MASK) != INSTALLUILEVEL_NONE)
2673         {
2674             LPWSTR deformated;
2675             message = MSI_RecordGetString(row,2);
2676             deformat_string(package,message,&deformated);
2677             MessageBoxW(NULL,deformated,title,MB_OK);
2678             msi_free(deformated);
2679         }
2680
2681         return ERROR_INSTALL_FAILURE;
2682     }
2683
2684     return ERROR_SUCCESS;
2685 }
2686
2687 static UINT ACTION_LaunchConditions(MSIPACKAGE *package)
2688 {
2689     UINT rc;
2690     MSIQUERY * view = NULL;
2691     static const WCHAR ExecSeqQuery[] =
2692         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2693          '`','L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','`',0};
2694
2695     TRACE("Checking launch conditions\n");
2696
2697     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2698     if (rc != ERROR_SUCCESS)
2699         return ERROR_SUCCESS;
2700
2701     rc = MSI_IterateRecords(view, NULL, ITERATE_LaunchConditions, package);
2702     msiobj_release(&view->hdr);
2703
2704     return rc;
2705 }
2706
2707 static LPWSTR resolve_keypath( MSIPACKAGE* package, MSICOMPONENT *cmp )
2708 {
2709
2710     if (!cmp->KeyPath)
2711         return resolve_folder(package,cmp->Directory,FALSE,FALSE,TRUE,NULL);
2712
2713     if (cmp->Attributes & msidbComponentAttributesRegistryKeyPath)
2714     {
2715         MSIRECORD * row = 0;
2716         UINT root,len;
2717         LPWSTR deformated,buffer,deformated_name;
2718         LPCWSTR key,name;
2719         static const WCHAR ExecSeqQuery[] =
2720             {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2721              '`','R','e','g','i','s','t','r','y','`',' ',
2722              'W','H','E','R','E',' ', '`','R','e','g','i','s','t','r','y','`',
2723              ' ','=',' ' ,'\'','%','s','\'',0 };
2724         static const WCHAR fmt[]={'%','0','2','i',':','\\','%','s','\\',0};
2725         static const WCHAR fmt2[]=
2726             {'%','0','2','i',':','\\','%','s','\\','%','s',0};
2727
2728         row = MSI_QueryGetRecord(package->db, ExecSeqQuery,cmp->KeyPath);
2729         if (!row)
2730             return NULL;
2731
2732         root = MSI_RecordGetInteger(row,2);
2733         key = MSI_RecordGetString(row, 3);
2734         name = MSI_RecordGetString(row, 4);
2735         deformat_string(package, key , &deformated);
2736         deformat_string(package, name, &deformated_name);
2737
2738         len = strlenW(deformated) + 6;
2739         if (deformated_name)
2740             len+=strlenW(deformated_name);
2741
2742         buffer = msi_alloc( len *sizeof(WCHAR));
2743
2744         if (deformated_name)
2745             sprintfW(buffer,fmt2,root,deformated,deformated_name);
2746         else
2747             sprintfW(buffer,fmt,root,deformated);
2748
2749         msi_free(deformated);
2750         msi_free(deformated_name);
2751         msiobj_release(&row->hdr);
2752
2753         return buffer;
2754     }
2755     else if (cmp->Attributes & msidbComponentAttributesODBCDataSource)
2756     {
2757         FIXME("UNIMPLEMENTED keypath as ODBC Source\n");
2758         return NULL;
2759     }
2760     else
2761     {
2762         MSIFILE *file = get_loaded_file( package, cmp->KeyPath );
2763
2764         if (file)
2765             return strdupW( file->TargetPath );
2766     }
2767     return NULL;
2768 }
2769
2770 static HKEY openSharedDLLsKey(void)
2771 {
2772     HKEY hkey=0;
2773     static const WCHAR path[] =
2774         {'S','o','f','t','w','a','r','e','\\',
2775          'M','i','c','r','o','s','o','f','t','\\',
2776          'W','i','n','d','o','w','s','\\',
2777          'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
2778          'S','h','a','r','e','d','D','L','L','s',0};
2779
2780     RegCreateKeyW(HKEY_LOCAL_MACHINE,path,&hkey);
2781     return hkey;
2782 }
2783
2784 static UINT ACTION_GetSharedDLLsCount(LPCWSTR dll)
2785 {
2786     HKEY hkey;
2787     DWORD count=0;
2788     DWORD type;
2789     DWORD sz = sizeof(count);
2790     DWORD rc;
2791     
2792     hkey = openSharedDLLsKey();
2793     rc = RegQueryValueExW(hkey, dll, NULL, &type, (LPBYTE)&count, &sz);
2794     if (rc != ERROR_SUCCESS)
2795         count = 0;
2796     RegCloseKey(hkey);
2797     return count;
2798 }
2799
2800 static UINT ACTION_WriteSharedDLLsCount(LPCWSTR path, UINT count)
2801 {
2802     HKEY hkey;
2803
2804     hkey = openSharedDLLsKey();
2805     if (count > 0)
2806         msi_reg_set_val_dword( hkey, path, count );
2807     else
2808         RegDeleteValueW(hkey,path);
2809     RegCloseKey(hkey);
2810     return count;
2811 }
2812
2813 /*
2814  * Return TRUE if the count should be written out and FALSE if not
2815  */
2816 static void ACTION_RefCountComponent( MSIPACKAGE* package, MSICOMPONENT *comp )
2817 {
2818     MSIFEATURE *feature;
2819     INT count = 0;
2820     BOOL write = FALSE;
2821
2822     /* only refcount DLLs */
2823     if (comp->KeyPath == NULL || 
2824         comp->Attributes & msidbComponentAttributesRegistryKeyPath || 
2825         comp->Attributes & msidbComponentAttributesODBCDataSource)
2826         write = FALSE;
2827     else
2828     {
2829         count = ACTION_GetSharedDLLsCount( comp->FullKeypath);
2830         write = (count > 0);
2831
2832         if (comp->Attributes & msidbComponentAttributesSharedDllRefCount)
2833             write = TRUE;
2834     }
2835
2836     /* increment counts */
2837     LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2838     {
2839         ComponentList *cl;
2840
2841         if (feature->ActionRequest != INSTALLSTATE_LOCAL)
2842             continue;
2843
2844         LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
2845         {
2846             if ( cl->component == comp )
2847                 count++;
2848         }
2849     }
2850
2851     /* decrement counts */
2852     LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2853     {
2854         ComponentList *cl;
2855
2856         if (feature->ActionRequest != INSTALLSTATE_ABSENT)
2857             continue;
2858
2859         LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
2860         {
2861             if ( cl->component == comp )
2862                 count--;
2863         }
2864     }
2865
2866     /* ref count all the files in the component */
2867     if (write)
2868     {
2869         MSIFILE *file;
2870
2871         LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2872         {
2873             if (file->Component == comp)
2874                 ACTION_WriteSharedDLLsCount( file->TargetPath, count );
2875         }
2876     }
2877     
2878     /* add a count for permanent */
2879     if (comp->Attributes & msidbComponentAttributesPermanent)
2880         count ++;
2881     
2882     comp->RefCount = count;
2883
2884     if (write)
2885         ACTION_WriteSharedDLLsCount( comp->FullKeypath, comp->RefCount );
2886 }
2887
2888 static UINT ACTION_ProcessComponents(MSIPACKAGE *package)
2889 {
2890     WCHAR squished_pc[GUID_SIZE];
2891     WCHAR squished_cc[GUID_SIZE];
2892     UINT rc;
2893     MSICOMPONENT *comp;
2894     HKEY hkey;
2895
2896     TRACE("\n");
2897
2898     squash_guid(package->ProductCode,squished_pc);
2899     ui_progress(package,1,COMPONENT_PROGRESS_VALUE,1,0);
2900
2901     LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2902     {
2903         MSIRECORD * uirow;
2904
2905         ui_progress(package,2,0,0,0);
2906         if (!comp->ComponentId)
2907             continue;
2908
2909         squash_guid(comp->ComponentId,squished_cc);
2910
2911         msi_free(comp->FullKeypath);
2912         comp->FullKeypath = resolve_keypath( package, comp );
2913
2914         ACTION_RefCountComponent( package, comp );
2915
2916         TRACE("Component %s (%s), Keypath=%s, RefCount=%i\n",
2917                             debugstr_w(comp->Component),
2918                             debugstr_w(squished_cc),
2919                             debugstr_w(comp->FullKeypath),
2920                             comp->RefCount);
2921
2922         if (comp->ActionRequest == INSTALLSTATE_LOCAL ||
2923             comp->ActionRequest == INSTALLSTATE_SOURCE)
2924         {
2925             if (!comp->FullKeypath)
2926                 continue;
2927
2928             if (package->Context == MSIINSTALLCONTEXT_MACHINE)
2929                 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, szLocalSid,
2930                                                      &hkey, TRUE);
2931             else
2932                 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, NULL,
2933                                                      &hkey, TRUE);
2934
2935             if (rc != ERROR_SUCCESS)
2936                 continue;
2937
2938             if (comp->Attributes & msidbComponentAttributesPermanent)
2939             {
2940                 static const WCHAR szPermKey[] =
2941                     { '0','0','0','0','0','0','0','0','0','0','0','0',
2942                       '0','0','0','0','0','0','0','0','0','0','0','0',
2943                       '0','0','0','0','0','0','0','0',0 };
2944
2945                 msi_reg_set_val_str(hkey, szPermKey, comp->FullKeypath);
2946             }
2947
2948             if (comp->Action == INSTALLSTATE_LOCAL)
2949                 msi_reg_set_val_str(hkey, squished_pc, comp->FullKeypath);
2950             else
2951             {
2952                 MSIFILE *file;
2953                 MSIRECORD *row;
2954                 LPWSTR ptr, ptr2;
2955                 WCHAR source[MAX_PATH];
2956                 WCHAR base[MAX_PATH];
2957                 LPWSTR sourcepath;
2958
2959                 static const WCHAR fmt[] = {'%','0','2','d','\\',0};
2960                 static const WCHAR query[] = {
2961                     'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
2962                     '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
2963                     '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ',
2964                     '>','=',' ','%','i',' ','O','R','D','E','R',' ','B','Y',' ',
2965                     '`','D','i','s','k','I','d','`',0};
2966
2967                 file = get_loaded_file(package, comp->KeyPath);
2968                 if (!file)
2969                     continue;
2970
2971                 row = MSI_QueryGetRecord(package->db, query, file->Sequence);
2972                 sprintfW(source, fmt, MSI_RecordGetInteger(row, 1));
2973                 ptr2 = strrchrW(source, '\\') + 1;
2974                 msiobj_release(&row->hdr);
2975
2976                 lstrcpyW(base, package->PackagePath);
2977                 ptr = strrchrW(base, '\\');
2978                 *(ptr + 1) = '\0';
2979
2980                 sourcepath = resolve_file_source(package, file);
2981                 ptr = sourcepath + lstrlenW(base);
2982                 lstrcpyW(ptr2, ptr);
2983                 msi_free(sourcepath);
2984
2985                 msi_reg_set_val_str(hkey, squished_pc, source);
2986             }
2987             RegCloseKey(hkey);
2988         }
2989         else if (comp->ActionRequest == INSTALLSTATE_ABSENT)
2990         {
2991             if (package->Context == MSIINSTALLCONTEXT_MACHINE)
2992                 MSIREG_DeleteUserDataComponentKey(comp->ComponentId, szLocalSid);
2993             else
2994                 MSIREG_DeleteUserDataComponentKey(comp->ComponentId, NULL);
2995         }
2996         comp->Action = comp->ActionRequest;
2997
2998         /* UI stuff */
2999         uirow = MSI_CreateRecord(3);
3000         MSI_RecordSetStringW(uirow,1,package->ProductCode);
3001         MSI_RecordSetStringW(uirow,2,comp->ComponentId);
3002         MSI_RecordSetStringW(uirow,3,comp->FullKeypath);
3003         ui_actiondata(package,szProcessComponents,uirow);
3004         msiobj_release( &uirow->hdr );
3005     }
3006
3007     return ERROR_SUCCESS;
3008 }
3009
3010 typedef struct {
3011     CLSID       clsid;
3012     LPWSTR      source;
3013
3014     LPWSTR      path;
3015     ITypeLib    *ptLib;
3016 } typelib_struct;
3017
3018 static BOOL CALLBACK Typelib_EnumResNameProc( HMODULE hModule, LPCWSTR lpszType, 
3019                                        LPWSTR lpszName, LONG_PTR lParam)
3020 {
3021     TLIBATTR *attr;
3022     typelib_struct *tl_struct = (typelib_struct*) lParam;
3023     static const WCHAR fmt[] = {'%','s','\\','%','i',0};
3024     int sz; 
3025     HRESULT res;
3026
3027     if (!IS_INTRESOURCE(lpszName))
3028     {
3029         ERR("Not Int Resource Name %s\n",debugstr_w(lpszName));
3030         return TRUE;
3031     }
3032
3033     sz = strlenW(tl_struct->source)+4;
3034     sz *= sizeof(WCHAR);
3035
3036     if ((INT_PTR)lpszName == 1)
3037         tl_struct->path = strdupW(tl_struct->source);
3038     else
3039     {
3040         tl_struct->path = msi_alloc(sz);
3041         sprintfW(tl_struct->path,fmt,tl_struct->source, lpszName);
3042     }
3043
3044     TRACE("trying %s\n", debugstr_w(tl_struct->path));
3045     res = LoadTypeLib(tl_struct->path,&tl_struct->ptLib);
3046     if (FAILED(res))
3047     {
3048         msi_free(tl_struct->path);
3049         tl_struct->path = NULL;
3050
3051         return TRUE;
3052     }
3053
3054     ITypeLib_GetLibAttr(tl_struct->ptLib, &attr);
3055     if (IsEqualGUID(&(tl_struct->clsid),&(attr->guid)))
3056     {
3057         ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
3058         return FALSE;
3059     }
3060
3061     msi_free(tl_struct->path);
3062     tl_struct->path = NULL;
3063
3064     ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
3065     ITypeLib_Release(tl_struct->ptLib);
3066
3067     return TRUE;
3068 }
3069
3070 static UINT ITERATE_RegisterTypeLibraries(MSIRECORD *row, LPVOID param)
3071 {
3072     MSIPACKAGE* package = param;
3073     LPCWSTR component;
3074     MSICOMPONENT *comp;
3075     MSIFILE *file;
3076     typelib_struct tl_struct;
3077     ITypeLib *tlib;
3078     HMODULE module;
3079     HRESULT hr;
3080
3081     component = MSI_RecordGetString(row,3);
3082     comp = get_loaded_component(package,component);
3083     if (!comp)
3084         return ERROR_SUCCESS;
3085
3086     if (comp->ActionRequest != INSTALLSTATE_LOCAL)
3087     {
3088         TRACE("Component not scheduled for installation: %s\n", debugstr_w(component));
3089         comp->Action = comp->Installed;
3090         return ERROR_SUCCESS;
3091     }
3092     comp->Action = INSTALLSTATE_LOCAL;
3093
3094     file = get_loaded_file( package, comp->KeyPath ); 
3095     if (!file)
3096         return ERROR_SUCCESS;
3097
3098     ui_actiondata( package, szRegisterTypeLibraries, row );
3099
3100     module = LoadLibraryExW( file->TargetPath, NULL, LOAD_LIBRARY_AS_DATAFILE );
3101     if (module)
3102     {
3103         LPCWSTR guid;
3104         guid = MSI_RecordGetString(row,1);
3105         CLSIDFromString((LPWSTR)guid, &tl_struct.clsid);
3106         tl_struct.source = strdupW( file->TargetPath );
3107         tl_struct.path = NULL;
3108
3109         EnumResourceNamesW(module, szTYPELIB, Typelib_EnumResNameProc,
3110                         (LONG_PTR)&tl_struct);
3111
3112         if (tl_struct.path)
3113         {
3114             LPWSTR help = NULL;
3115             LPCWSTR helpid;
3116             HRESULT res;
3117
3118             helpid = MSI_RecordGetString(row,6);
3119
3120             if (helpid)
3121                 help = resolve_folder(package,helpid,FALSE,FALSE,TRUE,NULL);
3122             res = RegisterTypeLib(tl_struct.ptLib,tl_struct.path,help);
3123             msi_free(help);
3124
3125             if (FAILED(res))
3126                 ERR("Failed to register type library %s\n",
3127                         debugstr_w(tl_struct.path));
3128             else
3129                 TRACE("Registered %s\n", debugstr_w(tl_struct.path));
3130
3131             ITypeLib_Release(tl_struct.ptLib);
3132             msi_free(tl_struct.path);
3133         }
3134         else
3135             ERR("Failed to load type library %s\n",
3136                     debugstr_w(tl_struct.source));
3137
3138         FreeLibrary(module);
3139         msi_free(tl_struct.source);
3140     }
3141     else
3142     {
3143         hr = LoadTypeLibEx(file->TargetPath, REGKIND_REGISTER, &tlib);
3144         if (FAILED(hr))
3145         {
3146             ERR("Failed to load type library: %08x\n", hr);
3147             return ERROR_INSTALL_FAILURE;
3148         }
3149
3150         ITypeLib_Release(tlib);
3151     }
3152
3153     return ERROR_SUCCESS;
3154 }
3155
3156 static UINT ACTION_RegisterTypeLibraries(MSIPACKAGE *package)
3157 {
3158     /* 
3159      * OK this is a bit confusing.. I am given a _Component key and I believe
3160      * that the file that is being registered as a type library is the "key file
3161      * of that component" which I interpret to mean "The file in the KeyPath of
3162      * that component".
3163      */
3164     UINT rc;
3165     MSIQUERY * view;
3166     static const WCHAR Query[] =
3167         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3168          '`','T','y','p','e','L','i','b','`',0};
3169
3170     rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3171     if (rc != ERROR_SUCCESS)
3172         return ERROR_SUCCESS;
3173
3174     rc = MSI_IterateRecords(view, NULL, ITERATE_RegisterTypeLibraries, package);
3175     msiobj_release(&view->hdr);
3176     return rc;
3177 }
3178
3179 static UINT ITERATE_UnregisterTypeLibraries( MSIRECORD *row, LPVOID param )
3180 {
3181     MSIPACKAGE *package = param;
3182     LPCWSTR component, guid;
3183     MSICOMPONENT *comp;
3184     GUID libid;
3185     UINT version;
3186     LCID language;
3187     SYSKIND syskind;
3188     HRESULT hr;
3189
3190     component = MSI_RecordGetString( row, 3 );
3191     comp = get_loaded_component( package, component );
3192     if (!comp)
3193         return ERROR_SUCCESS;
3194
3195     if (comp->ActionRequest != INSTALLSTATE_ABSENT)
3196     {
3197         TRACE("Component not scheduled for removal %s\n", debugstr_w(component));
3198         comp->Action = comp->Installed;
3199         return ERROR_SUCCESS;
3200     }
3201     comp->Action = INSTALLSTATE_ABSENT;
3202
3203     ui_actiondata( package, szUnregisterTypeLibraries, row );
3204
3205     guid = MSI_RecordGetString( row, 1 );
3206     CLSIDFromString( (LPWSTR)guid, &libid );
3207     version = MSI_RecordGetInteger( row, 4 );
3208     language = MSI_RecordGetInteger( row, 2 );
3209
3210 #ifdef _WIN64
3211     syskind = SYS_WIN64;
3212 #else
3213     syskind = SYS_WIN32;
3214 #endif
3215
3216     hr = UnRegisterTypeLib( &libid, (version >> 8) & 0xffff, version & 0xff, language, syskind );
3217     if (FAILED(hr))
3218     {
3219         WARN("Failed to unregister typelib: %08x\n", hr);
3220     }
3221
3222     return ERROR_SUCCESS;
3223 }
3224
3225 static UINT ACTION_UnregisterTypeLibraries( MSIPACKAGE *package )
3226 {
3227     UINT rc;
3228     MSIQUERY *view;
3229     static const WCHAR query[] =
3230         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3231          '`','T','y','p','e','L','i','b','`',0};
3232
3233     rc = MSI_DatabaseOpenViewW( package->db, query, &view );
3234     if (rc != ERROR_SUCCESS)
3235         return ERROR_SUCCESS;
3236
3237     rc = MSI_IterateRecords( view, NULL, ITERATE_UnregisterTypeLibraries, package );
3238     msiobj_release( &view->hdr );
3239     return rc;
3240 }
3241
3242 static WCHAR *get_link_file( MSIPACKAGE *package, MSIRECORD *row )
3243 {
3244     static const WCHAR szlnk[] = {'.','l','n','k',0};
3245     LPCWSTR directory, extension;
3246     LPWSTR link_folder, link_file, filename;
3247
3248     directory = MSI_RecordGetString( row, 2 );
3249     link_folder = resolve_folder( package, directory, FALSE, FALSE, TRUE, NULL );
3250
3251     /* may be needed because of a bug somewhere else */
3252     create_full_pathW( link_folder );
3253
3254     filename = msi_dup_record_field( row, 3 );
3255     reduce_to_longfilename( filename );
3256
3257     extension = strchrW( filename, '.' );
3258     if (!extension || strcmpiW( extension, szlnk ))
3259     {
3260         int len = strlenW( filename );
3261         filename = msi_realloc( filename, len * sizeof(WCHAR) + sizeof(szlnk) );
3262         memcpy( filename + len, szlnk, sizeof(szlnk) );
3263     }
3264     link_file = build_directory_name( 2, link_folder, filename );
3265     msi_free( link_folder );
3266     msi_free( filename );
3267
3268     return link_file;
3269 }
3270
3271 static UINT ITERATE_CreateShortcuts(MSIRECORD *row, LPVOID param)
3272 {
3273     MSIPACKAGE *package = param;
3274     LPWSTR link_file, deformated, path;
3275     LPCWSTR component, target;
3276     MSICOMPONENT *comp;
3277     IShellLinkW *sl = NULL;
3278     IPersistFile *pf = NULL;
3279     HRESULT res;
3280
3281     component = MSI_RecordGetString(row, 4);
3282     comp = get_loaded_component(package, component);
3283     if (!comp)
3284         return ERROR_SUCCESS;
3285
3286     if (comp->ActionRequest != INSTALLSTATE_LOCAL)
3287     {
3288         TRACE("Component not scheduled for installation %s\n", debugstr_w(component));
3289         comp->Action = comp->Installed;
3290         return ERROR_SUCCESS;
3291     }
3292     comp->Action = INSTALLSTATE_LOCAL;
3293
3294     ui_actiondata(package,szCreateShortcuts,row);
3295
3296     res = CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
3297                     &IID_IShellLinkW, (LPVOID *) &sl );
3298
3299     if (FAILED( res ))
3300     {
3301         ERR("CLSID_ShellLink not available\n");
3302         goto err;
3303     }
3304
3305     res = IShellLinkW_QueryInterface( sl, &IID_IPersistFile,(LPVOID*) &pf );
3306     if (FAILED( res ))
3307     {
3308         ERR("QueryInterface(IID_IPersistFile) failed\n");
3309         goto err;
3310     }
3311
3312     target = MSI_RecordGetString(row, 5);
3313     if (strchrW(target, '['))
3314     {
3315         deformat_string(package, target, &deformated);
3316         IShellLinkW_SetPath(sl,deformated);
3317         msi_free(deformated);
3318     }
3319     else
3320     {
3321         FIXME("poorly handled shortcut format, advertised shortcut\n");
3322         IShellLinkW_SetPath(sl,comp->FullKeypath);
3323     }
3324
3325     if (!MSI_RecordIsNull(row,6))
3326     {
3327         LPCWSTR arguments = MSI_RecordGetString(row, 6);
3328         deformat_string(package, arguments, &deformated);
3329         IShellLinkW_SetArguments(sl,deformated);
3330         msi_free(deformated);
3331     }
3332
3333     if (!MSI_RecordIsNull(row,7))
3334     {
3335         LPCWSTR description = MSI_RecordGetString(row, 7);
3336         IShellLinkW_SetDescription(sl, description);
3337     }
3338
3339     if (!MSI_RecordIsNull(row,8))
3340         IShellLinkW_SetHotkey(sl,MSI_RecordGetInteger(row,8));
3341
3342     if (!MSI_RecordIsNull(row,9))
3343     {
3344         INT index; 
3345         LPCWSTR icon = MSI_RecordGetString(row, 9);
3346
3347         path = build_icon_path(package, icon);
3348         index = MSI_RecordGetInteger(row,10);
3349
3350         /* no value means 0 */
3351         if (index == MSI_NULL_INTEGER)
3352             index = 0;
3353
3354         IShellLinkW_SetIconLocation(sl, path, index);
3355         msi_free(path);
3356     }
3357
3358     if (!MSI_RecordIsNull(row,11))
3359         IShellLinkW_SetShowCmd(sl,MSI_RecordGetInteger(row,11));
3360
3361     if (!MSI_RecordIsNull(row,12))
3362     {
3363         LPCWSTR wkdir = MSI_RecordGetString(row, 12);
3364         path = resolve_folder(package, wkdir, FALSE, FALSE, TRUE, NULL);
3365         if (path)
3366             IShellLinkW_SetWorkingDirectory(sl, path);
3367         msi_free(path);
3368     }
3369
3370     link_file = get_link_file(package, row);
3371
3372     TRACE("Writing shortcut to %s\n", debugstr_w(link_file));
3373     IPersistFile_Save(pf, link_file, FALSE);
3374
3375     msi_free(link_file);
3376
3377 err:
3378     if (pf)
3379         IPersistFile_Release( pf );
3380     if (sl)
3381         IShellLinkW_Release( sl );
3382
3383     return ERROR_SUCCESS;
3384 }
3385
3386 static UINT ACTION_CreateShortcuts(MSIPACKAGE *package)
3387 {
3388     UINT rc;
3389     HRESULT res;
3390     MSIQUERY * view;
3391     static const WCHAR Query[] =
3392         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3393          '`','S','h','o','r','t','c','u','t','`',0};
3394
3395     rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3396     if (rc != ERROR_SUCCESS)
3397         return ERROR_SUCCESS;
3398
3399     res = CoInitialize( NULL );
3400
3401     rc = MSI_IterateRecords(view, NULL, ITERATE_CreateShortcuts, package);
3402     msiobj_release(&view->hdr);
3403
3404     if (SUCCEEDED(res))
3405         CoUninitialize();
3406
3407     return rc;
3408 }
3409
3410 static UINT ITERATE_RemoveShortcuts( MSIRECORD *row, LPVOID param )
3411 {
3412     MSIPACKAGE *package = param;
3413     LPWSTR link_file;
3414     LPCWSTR component;
3415     MSICOMPONENT *comp;
3416
3417     component = MSI_RecordGetString( row, 4 );
3418     comp = get_loaded_component( package, component );
3419     if (!comp)
3420         return ERROR_SUCCESS;
3421
3422     if (comp->ActionRequest != INSTALLSTATE_ABSENT)
3423     {
3424         TRACE("Component not scheduled for removal %s\n", debugstr_w(component));
3425         comp->Action = comp->Installed;
3426         return ERROR_SUCCESS;
3427     }
3428     comp->Action = INSTALLSTATE_ABSENT;
3429
3430     ui_actiondata( package, szRemoveShortcuts, row );
3431
3432     link_file = get_link_file( package, row );
3433
3434     TRACE("Removing shortcut file %s\n", debugstr_w( link_file ));
3435     if (!DeleteFileW( link_file ))
3436     {
3437         WARN("Failed to remove shortcut file %u\n", GetLastError());
3438     }
3439     msi_free( link_file );
3440
3441     return ERROR_SUCCESS;
3442 }
3443
3444 static UINT ACTION_RemoveShortcuts( MSIPACKAGE *package )
3445 {
3446     UINT rc;
3447     MSIQUERY *view;
3448     static const WCHAR query[] =
3449         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3450          '`','S','h','o','r','t','c','u','t','`',0};
3451
3452     rc = MSI_DatabaseOpenViewW( package->db, query, &view );
3453     if (rc != ERROR_SUCCESS)
3454         return ERROR_SUCCESS;
3455
3456     rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveShortcuts, package );
3457     msiobj_release( &view->hdr );
3458
3459     return rc;
3460 }
3461
3462 static UINT ITERATE_PublishIcon(MSIRECORD *row, LPVOID param)
3463 {
3464     MSIPACKAGE* package = param;
3465     HANDLE the_file;
3466     LPWSTR FilePath;
3467     LPCWSTR FileName;
3468     CHAR buffer[1024];
3469     DWORD sz;
3470     UINT rc;
3471
3472     FileName = MSI_RecordGetString(row,1);
3473     if (!FileName)
3474     {
3475         ERR("Unable to get FileName\n");
3476         return ERROR_SUCCESS;
3477     }
3478
3479     FilePath = build_icon_path(package,FileName);
3480
3481     TRACE("Creating icon file at %s\n",debugstr_w(FilePath));
3482
3483     the_file = CreateFileW(FilePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
3484                         FILE_ATTRIBUTE_NORMAL, NULL);
3485
3486     if (the_file == INVALID_HANDLE_VALUE)
3487     {
3488         ERR("Unable to create file %s\n",debugstr_w(FilePath));
3489         msi_free(FilePath);
3490         return ERROR_SUCCESS;
3491     }
3492
3493     do 
3494     {
3495         DWORD write;
3496         sz = 1024;
3497         rc = MSI_RecordReadStream(row,2,buffer,&sz);
3498         if (rc != ERROR_SUCCESS)
3499         {
3500             ERR("Failed to get stream\n");
3501             CloseHandle(the_file);  
3502             DeleteFileW(FilePath);
3503             break;
3504         }
3505         WriteFile(the_file,buffer,sz,&write,NULL);
3506     } while (sz == 1024);
3507
3508     msi_free(FilePath);
3509     CloseHandle(the_file);
3510
3511     return ERROR_SUCCESS;
3512 }
3513
3514 static UINT msi_publish_icons(MSIPACKAGE *package)
3515 {
3516     UINT r;
3517     MSIQUERY *view;
3518
3519     static const WCHAR query[]= {
3520         'S','E','L','E','C','T',' ','*',' ',
3521         'F','R','O','M',' ','`','I','c','o','n','`',0};
3522
3523     r = MSI_DatabaseOpenViewW(package->db, query, &view);
3524     if (r == ERROR_SUCCESS)
3525     {
3526         MSI_IterateRecords(view, NULL, ITERATE_PublishIcon, package);
3527         msiobj_release(&view->hdr);
3528     }
3529
3530     return ERROR_SUCCESS;
3531 }
3532
3533 static UINT msi_publish_sourcelist(MSIPACKAGE *package, HKEY hkey)
3534 {
3535     UINT r;
3536     HKEY source;
3537     LPWSTR buffer;
3538     MSIMEDIADISK *disk;
3539     MSISOURCELISTINFO *info;
3540
3541     r = RegCreateKeyW(hkey, szSourceList, &source);
3542     if (r != ERROR_SUCCESS)
3543         return r;
3544
3545     RegCloseKey(source);
3546
3547     buffer = strrchrW(package->PackagePath, '\\') + 1;
3548     r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3549                               package->Context, MSICODE_PRODUCT,
3550                               INSTALLPROPERTY_PACKAGENAMEW, buffer);
3551     if (r != ERROR_SUCCESS)
3552         return r;
3553
3554     r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3555                               package->Context, MSICODE_PRODUCT,
3556                               INSTALLPROPERTY_MEDIAPACKAGEPATHW, szEmpty);
3557     if (r != ERROR_SUCCESS)
3558         return r;
3559
3560     r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3561                               package->Context, MSICODE_PRODUCT,
3562                               INSTALLPROPERTY_DISKPROMPTW, szEmpty);
3563     if (r != ERROR_SUCCESS)
3564         return r;
3565
3566     LIST_FOR_EACH_ENTRY(info, &package->sourcelist_info, MSISOURCELISTINFO, entry)
3567     {
3568         if (!lstrcmpW(info->property, INSTALLPROPERTY_LASTUSEDSOURCEW))
3569             msi_set_last_used_source(package->ProductCode, NULL, info->context,
3570                                      info->options, info->value);
3571         else
3572             MsiSourceListSetInfoW(package->ProductCode, NULL,
3573                                   info->context, info->options,
3574                                   info->property, info->value);
3575     }
3576
3577     LIST_FOR_EACH_ENTRY(disk, &package->sourcelist_media, MSIMEDIADISK, entry)
3578     {
3579         MsiSourceListAddMediaDiskW(package->ProductCode, NULL,
3580                                    disk->context, disk->options,
3581                                    disk->disk_id, disk->volume_label, disk->disk_prompt);
3582     }
3583
3584     return ERROR_SUCCESS;
3585 }
3586
3587 static UINT msi_publish_product_properties(MSIPACKAGE *package, HKEY hkey)
3588 {
3589     MSIHANDLE hdb, suminfo;
3590     WCHAR guids[MAX_PATH];
3591     WCHAR packcode[SQUISH_GUID_SIZE];
3592     LPWSTR buffer;
3593     LPWSTR ptr;
3594     DWORD langid;
3595     DWORD size;
3596     UINT r;
3597
3598     static const WCHAR szProductLanguage[] =
3599         {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
3600     static const WCHAR szARPProductIcon[] =
3601         {'A','R','P','P','R','O','D','U','C','T','I','C','O','N',0};
3602     static const WCHAR szProductVersion[] =
3603         {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
3604     static const WCHAR szAssignment[] =
3605         {'A','s','s','i','g','n','m','e','n','t',0};
3606     static const WCHAR szAdvertiseFlags[] =
3607         {'A','d','v','e','r','t','i','s','e','F','l','a','g','s',0};
3608     static const WCHAR szClients[] =
3609         {'C','l','i','e','n','t','s',0};
3610     static const WCHAR szColon[] = {':',0};
3611
3612     buffer = msi_dup_property(package, INSTALLPROPERTY_PRODUCTNAMEW);
3613     msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTNAMEW, buffer);
3614     msi_free(buffer);
3615
3616     langid = msi_get_property_int(package, szProductLanguage, 0);
3617     msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
3618
3619     /* FIXME */
3620     msi_reg_set_val_dword(hkey, INSTALLPROPERTY_AUTHORIZED_LUA_APPW, 0);
3621
3622     buffer = msi_dup_property(package, szARPProductIcon);
3623     if (buffer)
3624     {
3625         LPWSTR path = build_icon_path(package,buffer);
3626         msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTICONW, path);
3627         msi_free(path);
3628         msi_free(buffer);
3629     }
3630
3631     buffer = msi_dup_property(package, szProductVersion);
3632     if (buffer)
3633     {
3634         DWORD verdword = msi_version_str_to_dword(buffer);
3635         msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
3636         msi_free(buffer);
3637     }
3638
3639     msi_reg_set_val_dword(hkey, szAssignment, 0);
3640     msi_reg_set_val_dword(hkey, szAdvertiseFlags, 0x184);
3641     msi_reg_set_val_dword(hkey, INSTALLPROPERTY_INSTANCETYPEW, 0);
3642     msi_reg_set_val_str(hkey, szClients, szColon);
3643
3644     hdb = alloc_msihandle(&package->db->hdr);
3645     if (!hdb)
3646         return ERROR_NOT_ENOUGH_MEMORY;
3647
3648     r = MsiGetSummaryInformationW(hdb, NULL, 0, &suminfo);
3649     MsiCloseHandle(hdb);
3650     if (r != ERROR_SUCCESS)
3651         goto done;
3652
3653     size = MAX_PATH;
3654     r = MsiSummaryInfoGetPropertyW(suminfo, PID_REVNUMBER, NULL, NULL,
3655                                    NULL, guids, &size);
3656     if (r != ERROR_SUCCESS)
3657         goto done;
3658
3659     ptr = strchrW(guids, ';');
3660     if (ptr) *ptr = 0;
3661     squash_guid(guids, packcode);
3662     msi_reg_set_val_str(hkey, INSTALLPROPERTY_PACKAGECODEW, packcode);
3663
3664 done:
3665     MsiCloseHandle(suminfo);
3666     return ERROR_SUCCESS;
3667 }
3668
3669 static UINT msi_publish_upgrade_code(MSIPACKAGE *package)
3670 {
3671     UINT r;
3672     HKEY hkey;
3673     LPWSTR upgrade;
3674     WCHAR squashed_pc[SQUISH_GUID_SIZE];
3675
3676     static const WCHAR szUpgradeCode[] =
3677         {'U','p','g','r','a','d','e','C','o','d','e',0};
3678
3679     upgrade = msi_dup_property(package, szUpgradeCode);
3680     if (!upgrade)
3681         return ERROR_SUCCESS;
3682
3683     if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3684     {
3685         r = MSIREG_OpenClassesUpgradeCodesKey(upgrade, &hkey, TRUE);
3686         if (r != ERROR_SUCCESS)
3687             goto done;
3688     }
3689     else
3690     {
3691         r = MSIREG_OpenUserUpgradeCodesKey(upgrade, &hkey, TRUE);
3692         if (r != ERROR_SUCCESS)
3693             goto done;
3694     }
3695
3696     squash_guid(package->ProductCode, squashed_pc);
3697     msi_reg_set_val_str(hkey, squashed_pc, NULL);
3698
3699     RegCloseKey(hkey);
3700
3701 done:
3702     msi_free(upgrade);
3703     return r;
3704 }
3705
3706 static BOOL msi_check_publish(MSIPACKAGE *package)
3707 {
3708     MSIFEATURE *feature;
3709
3710     LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3711     {
3712         if (feature->ActionRequest == INSTALLSTATE_LOCAL)
3713             return TRUE;
3714     }
3715
3716     return FALSE;
3717 }
3718
3719 static BOOL msi_check_unpublish(MSIPACKAGE *package)
3720 {
3721     MSIFEATURE *feature;
3722
3723     LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3724     {
3725         if (feature->ActionRequest != INSTALLSTATE_ABSENT)
3726             return FALSE;
3727     }
3728
3729     return TRUE;
3730 }
3731
3732 static UINT msi_publish_patch(MSIPACKAGE *package, HKEY prodkey, HKEY hudkey)
3733 {
3734     WCHAR patch_squashed[GUID_SIZE];
3735     HKEY patches;
3736     LONG res;
3737     UINT r = ERROR_FUNCTION_FAILED;
3738
3739     res = RegCreateKeyExW(prodkey, szPatches, 0, NULL, 0, KEY_ALL_ACCESS, NULL,
3740                           &patches, NULL);
3741     if (res != ERROR_SUCCESS)
3742         return ERROR_FUNCTION_FAILED;
3743
3744     squash_guid(package->patch->patchcode, patch_squashed);
3745
3746     res = RegSetValueExW(patches, szPatches, 0, REG_MULTI_SZ,
3747                          (const BYTE *)patch_squashed,
3748                          (lstrlenW(patch_squashed) + 1) * sizeof(WCHAR));
3749     if (res != ERROR_SUCCESS)
3750         goto done;
3751
3752     res = RegSetValueExW(patches, patch_squashed, 0, REG_SZ,
3753                          (const BYTE *)package->patch->transforms,
3754                          (lstrlenW(package->patch->transforms) + 1) * sizeof(WCHAR));
3755     if (res == ERROR_SUCCESS)
3756         r = ERROR_SUCCESS;
3757
3758 done:
3759     RegCloseKey(patches);
3760     return r;
3761 }
3762
3763 /*
3764  * 99% of the work done here is only done for 
3765  * advertised installs. However this is where the
3766  * Icon table is processed and written out
3767  * so that is what I am going to do here.
3768  */
3769 static UINT ACTION_PublishProduct(MSIPACKAGE *package)
3770 {
3771     UINT rc;
3772     HKEY hukey = NULL, hudkey = NULL;
3773     MSIRECORD *uirow;
3774
3775     /* FIXME: also need to publish if the product is in advertise mode */
3776     if (!msi_check_publish(package))
3777         return ERROR_SUCCESS;
3778
3779     rc = MSIREG_OpenProductKey(package->ProductCode, NULL, package->Context,
3780                                &hukey, TRUE);
3781     if (rc != ERROR_SUCCESS)
3782         goto end;
3783
3784     rc = MSIREG_OpenUserDataProductKey(package->ProductCode, package->Context,
3785                                        NULL, &hudkey, TRUE);
3786     if (rc != ERROR_SUCCESS)
3787         goto end;
3788
3789     rc = msi_publish_upgrade_code(package);
3790     if (rc != ERROR_SUCCESS)
3791         goto end;
3792
3793     if (package->patch)
3794     {
3795         rc = msi_publish_patch(package, hukey, hudkey);
3796         if (rc != ERROR_SUCCESS)
3797             goto end;
3798     }
3799
3800     rc = msi_publish_product_properties(package, hukey);
3801     if (rc != ERROR_SUCCESS)
3802         goto end;
3803
3804     rc = msi_publish_sourcelist(package, hukey);
3805     if (rc != ERROR_SUCCESS)
3806         goto end;
3807
3808     rc = msi_publish_icons(package);
3809
3810 end:
3811     uirow = MSI_CreateRecord( 1 );
3812     MSI_RecordSetStringW( uirow, 1, package->ProductCode );
3813     ui_actiondata( package, szPublishProduct, uirow );
3814     msiobj_release( &uirow->hdr );
3815
3816     RegCloseKey(hukey);
3817     RegCloseKey(hudkey);
3818
3819     return rc;
3820 }
3821
3822 static WCHAR *get_ini_file_name( MSIPACKAGE *package, MSIRECORD *row )
3823 {
3824     WCHAR *filename, *ptr, *folder, *ret;
3825     const WCHAR *dirprop;
3826
3827     filename = msi_dup_record_field( row, 2 );
3828     if (filename && (ptr = strchrW( filename, '|' )))
3829         ptr++;
3830     else
3831         ptr = filename;
3832
3833     dirprop = MSI_RecordGetString( row, 3 );
3834     if (dirprop)
3835     {
3836         folder = resolve_folder( package, dirprop, FALSE, FALSE, TRUE, NULL );
3837         if (!folder)
3838             folder = msi_dup_property( package, dirprop );
3839     }
3840     else
3841         folder = msi_dup_property( package, szWindowsFolder );
3842
3843     if (!folder)
3844     {
3845         ERR("Unable to resolve folder %s\n", debugstr_w(dirprop));
3846         msi_free( filename );
3847         return NULL;
3848     }
3849
3850     ret = build_directory_name( 2, folder, ptr );
3851
3852     msi_free( filename );
3853     msi_free( folder );
3854     return ret;
3855 }
3856
3857 static UINT ITERATE_WriteIniValues(MSIRECORD *row, LPVOID param)
3858 {
3859     MSIPACKAGE *package = param;
3860     LPCWSTR component, section, key, value, identifier;
3861     LPWSTR deformated_section, deformated_key, deformated_value, fullname;
3862     MSIRECORD * uirow;
3863     INT action;
3864     MSICOMPONENT *comp;
3865
3866     component = MSI_RecordGetString(row, 8);
3867     comp = get_loaded_component(package,component);
3868     if (!comp)
3869         return ERROR_SUCCESS;
3870
3871     if (comp->ActionRequest != INSTALLSTATE_LOCAL)
3872     {
3873         TRACE("Component not scheduled for installation %s\n", debugstr_w(component));
3874         comp->Action = comp->Installed;
3875         return ERROR_SUCCESS;
3876     }
3877     comp->Action = INSTALLSTATE_LOCAL;
3878
3879     identifier = MSI_RecordGetString(row,1); 
3880     section = MSI_RecordGetString(row,4);
3881     key = MSI_RecordGetString(row,5);
3882     value = MSI_RecordGetString(row,6);
3883     action = MSI_RecordGetInteger(row,7);
3884
3885     deformat_string(package,section,&deformated_section);
3886     deformat_string(package,key,&deformated_key);
3887     deformat_string(package,value,&deformated_value);
3888
3889     fullname = get_ini_file_name(package, row);
3890
3891     if (action == 0)
3892     {
3893         TRACE("Adding value %s to section %s in %s\n",
3894                 debugstr_w(deformated_key), debugstr_w(deformated_section),
3895                 debugstr_w(fullname));
3896         WritePrivateProfileStringW(deformated_section, deformated_key,
3897                                    deformated_value, fullname);
3898     }
3899     else if (action == 1)
3900     {
3901         WCHAR returned[10];
3902         GetPrivateProfileStringW(deformated_section, deformated_key, NULL,
3903                                  returned, 10, fullname);
3904         if (returned[0] == 0)
3905         {
3906             TRACE("Adding value %s to section %s in %s\n",
3907                     debugstr_w(deformated_key), debugstr_w(deformated_section),
3908                     debugstr_w(fullname));
3909
3910             WritePrivateProfileStringW(deformated_section, deformated_key,
3911                                        deformated_value, fullname);
3912         }
3913     }
3914     else if (action == 3)
3915         FIXME("Append to existing section not yet implemented\n");
3916
3917     uirow = MSI_CreateRecord(4);
3918     MSI_RecordSetStringW(uirow,1,identifier);
3919     MSI_RecordSetStringW(uirow,2,deformated_section);
3920     MSI_RecordSetStringW(uirow,3,deformated_key);
3921     MSI_RecordSetStringW(uirow,4,deformated_value);
3922     ui_actiondata(package,szWriteIniValues,uirow);
3923     msiobj_release( &uirow->hdr );
3924
3925     msi_free(fullname);
3926     msi_free(deformated_key);
3927     msi_free(deformated_value);
3928     msi_free(deformated_section);
3929     return ERROR_SUCCESS;
3930 }
3931
3932 static UINT ACTION_WriteIniValues(MSIPACKAGE *package)
3933 {
3934     UINT rc;
3935     MSIQUERY * view;
3936     static const WCHAR ExecSeqQuery[] = 
3937         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3938          '`','I','n','i','F','i','l','e','`',0};
3939
3940     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
3941     if (rc != ERROR_SUCCESS)
3942     {
3943         TRACE("no IniFile table\n");
3944         return ERROR_SUCCESS;
3945     }
3946
3947     rc = MSI_IterateRecords(view, NULL, ITERATE_WriteIniValues, package);
3948     msiobj_release(&view->hdr);
3949     return rc;
3950 }
3951
3952 static UINT ITERATE_RemoveIniValuesOnUninstall( MSIRECORD *row, LPVOID param )
3953 {
3954     MSIPACKAGE *package = param;
3955     LPCWSTR component, section, key, value, identifier;
3956     LPWSTR deformated_section, deformated_key, deformated_value, filename;
3957     MSICOMPONENT *comp;
3958     MSIRECORD *uirow;
3959     INT action;
3960
3961     component = MSI_RecordGetString( row, 8 );
3962     comp = get_loaded_component( package, component );
3963     if (!comp)
3964         return ERROR_SUCCESS;
3965
3966     if (comp->ActionRequest != INSTALLSTATE_ABSENT)
3967     {
3968         TRACE("Component not scheduled for removal %s\n", debugstr_w(component));
3969         comp->Action = comp->Installed;
3970         return ERROR_SUCCESS;
3971     }
3972     comp->Action = INSTALLSTATE_ABSENT;
3973
3974     identifier = MSI_RecordGetString( row, 1 );
3975     section = MSI_RecordGetString( row, 4 );
3976     key = MSI_RecordGetString( row, 5 );
3977     value = MSI_RecordGetString( row, 6 );
3978     action = MSI_RecordGetInteger( row, 7 );
3979
3980     deformat_string( package, section, &deformated_section );
3981     deformat_string( package, key, &deformated_key );
3982     deformat_string( package, value, &deformated_value );
3983
3984     if (action == msidbIniFileActionAddLine || action == msidbIniFileActionCreateLine)
3985     {
3986         filename = get_ini_file_name( package, row );
3987
3988         TRACE("Removing key %s from section %s in %s\n",
3989                debugstr_w(deformated_key), debugstr_w(deformated_section), debugstr_w(filename));
3990
3991         if (!WritePrivateProfileStringW( deformated_section, deformated_key, NULL, filename ))
3992         {
3993             WARN("Unable to remove key %u\n", GetLastError());
3994         }
3995         msi_free( filename );
3996     }
3997     else
3998         FIXME("Unsupported action %d\n", action);
3999
4000
4001     uirow = MSI_CreateRecord( 4 );
4002     MSI_RecordSetStringW( uirow, 1, identifier );
4003     MSI_RecordSetStringW( uirow, 2, deformated_section );
4004     MSI_RecordSetStringW( uirow, 3, deformated_key );
4005     MSI_RecordSetStringW( uirow, 4, deformated_value );
4006     ui_actiondata( package, szRemoveIniValues, uirow );
4007     msiobj_release( &uirow->hdr );
4008
4009     msi_free( deformated_key );
4010     msi_free( deformated_value );
4011     msi_free( deformated_section );
4012     return ERROR_SUCCESS;
4013 }
4014
4015 static UINT ITERATE_RemoveIniValuesOnInstall( MSIRECORD *row, LPVOID param )
4016 {
4017     MSIPACKAGE *package = param;
4018     LPCWSTR component, section, key, value, identifier;
4019     LPWSTR deformated_section, deformated_key, deformated_value, filename;
4020     MSICOMPONENT *comp;
4021     MSIRECORD *uirow;
4022     INT action;
4023
4024     component = MSI_RecordGetString( row, 8 );
4025     comp = get_loaded_component( package, component );
4026     if (!comp)
4027         return ERROR_SUCCESS;
4028
4029     if (comp->ActionRequest != INSTALLSTATE_LOCAL)
4030     {
4031         TRACE("Component not scheduled for installation %s\n", debugstr_w(component));
4032         comp->Action = comp->Installed;
4033         return ERROR_SUCCESS;
4034     }
4035     comp->Action = INSTALLSTATE_LOCAL;
4036
4037     identifier = MSI_RecordGetString( row, 1 );
4038     section = MSI_RecordGetString( row, 4 );
4039     key = MSI_RecordGetString( row, 5 );
4040     value = MSI_RecordGetString( row, 6 );
4041     action = MSI_RecordGetInteger( row, 7 );
4042
4043     deformat_string( package, section, &deformated_section );
4044     deformat_string( package, key, &deformated_key );
4045     deformat_string( package, value, &deformated_value );
4046
4047     if (action == msidbIniFileActionRemoveLine)
4048     {
4049         filename = get_ini_file_name( package, row );
4050
4051         TRACE("Removing key %s from section %s in %s\n",
4052                debugstr_w(deformated_key), debugstr_w(deformated_section), debugstr_w(filename));
4053
4054         if (!WritePrivateProfileStringW( deformated_section, deformated_key, NULL, filename ))
4055         {
4056             WARN("Unable to remove key %u\n", GetLastError());
4057         }
4058         msi_free( filename );
4059     }
4060     else
4061         FIXME("Unsupported action %d\n", action);
4062
4063     uirow = MSI_CreateRecord( 4 );
4064     MSI_RecordSetStringW( uirow, 1, identifier );
4065     MSI_RecordSetStringW( uirow, 2, deformated_section );
4066     MSI_RecordSetStringW( uirow, 3, deformated_key );
4067     MSI_RecordSetStringW( uirow, 4, deformated_value );
4068     ui_actiondata( package, szRemoveIniValues, uirow );
4069     msiobj_release( &uirow->hdr );
4070
4071     msi_free( deformated_key );
4072     msi_free( deformated_value );
4073     msi_free( deformated_section );
4074     return ERROR_SUCCESS;
4075 }
4076
4077 static UINT ACTION_RemoveIniValues( MSIPACKAGE *package )
4078 {
4079     UINT rc;
4080     MSIQUERY *view;
4081     static const WCHAR query[] =
4082         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4083          '`','I','n','i','F','i','l','e','`',0};
4084     static const WCHAR remove_query[] =
4085         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4086          '`','R','e','m','o','v','e','I','n','i','F','i','l','e','`',0};
4087
4088     rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4089     if (rc == ERROR_SUCCESS)
4090     {
4091         rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveIniValuesOnUninstall, package );
4092         msiobj_release( &view->hdr );
4093         if (rc != ERROR_SUCCESS)
4094             return rc;
4095     }
4096
4097     rc = MSI_DatabaseOpenViewW( package->db, remove_query, &view );
4098     if (rc == ERROR_SUCCESS)
4099     {
4100         rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveIniValuesOnInstall, package );
4101         msiobj_release( &view->hdr );
4102         if (rc != ERROR_SUCCESS)
4103             return rc;
4104     }
4105
4106     return ERROR_SUCCESS;
4107 }
4108
4109 static UINT ITERATE_SelfRegModules(MSIRECORD *row, LPVOID param)
4110 {
4111     MSIPACKAGE *package = param;
4112     LPCWSTR filename;
4113     LPWSTR FullName;
4114     MSIFILE *file;
4115     DWORD len;
4116     static const WCHAR ExeStr[] =
4117         {'r','e','g','s','v','r','3','2','.','e','x','e',' ','\"',0};
4118     static const WCHAR close[] =  {'\"',0};
4119     STARTUPINFOW si;
4120     PROCESS_INFORMATION info;
4121     BOOL brc;
4122     MSIRECORD *uirow;
4123     LPWSTR uipath, p;
4124
4125     memset(&si,0,sizeof(STARTUPINFOW));
4126
4127     filename = MSI_RecordGetString(row,1);
4128     file = get_loaded_file( package, filename );
4129
4130     if (!file)
4131     {
4132         ERR("Unable to find file id %s\n",debugstr_w(filename));
4133         return ERROR_SUCCESS;
4134     }
4135
4136     len = strlenW(ExeStr) + strlenW( file->TargetPath ) + 2;
4137
4138     FullName = msi_alloc(len*sizeof(WCHAR));
4139     strcpyW(FullName,ExeStr);
4140     strcatW( FullName, file->TargetPath );
4141     strcatW(FullName,close);
4142
4143     TRACE("Registering %s\n",debugstr_w(FullName));
4144     brc = CreateProcessW(NULL, FullName, NULL, NULL, FALSE, 0, NULL, c_colon,
4145                     &si, &info);
4146
4147     if (brc)
4148     {
4149         CloseHandle(info.hThread);
4150         msi_dialog_check_messages(info.hProcess);
4151         CloseHandle(info.hProcess);
4152     }
4153
4154     uirow = MSI_CreateRecord( 2 );
4155     MSI_RecordSetStringW( uirow, 1, filename );
4156     uipath = strdupW( file->TargetPath );
4157     if ((p = strrchrW( uipath,'\\' ))) *p = 0;
4158     MSI_RecordSetStringW( uirow, 2, uipath );
4159     ui_actiondata( package, szSelfRegModules, uirow );
4160     msiobj_release( &uirow->hdr );
4161
4162     msi_free( FullName );
4163     msi_free( uipath );
4164     return ERROR_SUCCESS;
4165 }
4166
4167 static UINT ACTION_SelfRegModules(MSIPACKAGE *package)
4168 {
4169     UINT rc;
4170     MSIQUERY * view;
4171     static const WCHAR ExecSeqQuery[] = 
4172         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4173          '`','S','e','l','f','R','e','g','`',0};
4174
4175     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4176     if (rc != ERROR_SUCCESS)
4177     {
4178         TRACE("no SelfReg table\n");
4179         return ERROR_SUCCESS;
4180     }
4181
4182     MSI_IterateRecords(view, NULL, ITERATE_SelfRegModules, package);
4183     msiobj_release(&view->hdr);
4184
4185     return ERROR_SUCCESS;
4186 }
4187
4188 static UINT ITERATE_SelfUnregModules( MSIRECORD *row, LPVOID param )
4189 {
4190     static const WCHAR regsvr32[] =
4191         {'r','e','g','s','v','r','3','2','.','e','x','e',' ','/','u',' ','\"',0};
4192     static const WCHAR close[] =  {'\"',0};
4193     MSIPACKAGE *package = param;
4194     LPCWSTR filename;
4195     LPWSTR cmdline;
4196     MSIFILE *file;
4197     DWORD len;
4198     STARTUPINFOW si;
4199     PROCESS_INFORMATION pi;
4200     BOOL ret;
4201     MSIRECORD *uirow;
4202     LPWSTR uipath, p;
4203
4204     memset( &si, 0, sizeof(STARTUPINFOW) );
4205
4206     filename = MSI_RecordGetString( row, 1 );
4207     file = get_loaded_file( package, filename );
4208
4209     if (!file)
4210     {
4211         ERR("Unable to find file id %s\n", debugstr_w(filename));
4212         return ERROR_SUCCESS;
4213     }
4214
4215     len = strlenW( regsvr32 ) + strlenW( file->TargetPath ) + 2;
4216
4217     cmdline = msi_alloc( len * sizeof(WCHAR) );
4218     strcpyW( cmdline, regsvr32 );
4219     strcatW( cmdline, file->TargetPath );
4220     strcatW( cmdline, close );
4221
4222     TRACE("Unregistering %s\n", debugstr_w(cmdline));
4223
4224     ret = CreateProcessW( NULL, cmdline, NULL, NULL, FALSE, 0, NULL, c_colon, &si, &pi );
4225     if (ret)
4226     {
4227         CloseHandle( pi.hThread );
4228         msi_dialog_check_messages( pi.hProcess );
4229         CloseHandle( pi.hProcess );
4230     }
4231
4232     uirow = MSI_CreateRecord( 2 );
4233     MSI_RecordSetStringW( uirow, 1, filename );
4234     uipath = strdupW( file->TargetPath );
4235     if ((p = strrchrW( uipath,'\\' ))) *p = 0;
4236     MSI_RecordSetStringW( uirow, 2, uipath );
4237     ui_actiondata( package, szSelfUnregModules, uirow );
4238     msiobj_release( &uirow->hdr );
4239
4240     msi_free( cmdline );
4241     msi_free( uipath );
4242     return ERROR_SUCCESS;
4243 }
4244
4245 static UINT ACTION_SelfUnregModules( MSIPACKAGE *package )
4246 {
4247     UINT rc;
4248     MSIQUERY *view;
4249     static const WCHAR query[] =
4250         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4251          '`','S','e','l','f','R','e','g','`',0};
4252
4253     rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4254     if (rc != ERROR_SUCCESS)
4255     {
4256         TRACE("no SelfReg table\n");
4257         return ERROR_SUCCESS;
4258     }
4259
4260     MSI_IterateRecords( view, NULL, ITERATE_SelfUnregModules, package );
4261     msiobj_release( &view->hdr );
4262
4263     return ERROR_SUCCESS;
4264 }
4265
4266 static UINT ACTION_PublishFeatures(MSIPACKAGE *package)
4267 {
4268     MSIFEATURE *feature;
4269     UINT rc;
4270     HKEY hkey = NULL, userdata = NULL;
4271
4272     if (!msi_check_publish(package))
4273         return ERROR_SUCCESS;
4274
4275     rc = MSIREG_OpenFeaturesKey(package->ProductCode, package->Context,
4276                                 &hkey, TRUE);
4277     if (rc != ERROR_SUCCESS)
4278         goto end;
4279
4280     rc = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, package->Context,
4281                                         &userdata, TRUE);
4282     if (rc != ERROR_SUCCESS)
4283         goto end;
4284
4285     /* here the guids are base 85 encoded */
4286     LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
4287     {
4288         ComponentList *cl;
4289         LPWSTR data = NULL;
4290         GUID clsid;
4291         INT size;
4292         BOOL absent = FALSE;
4293         MSIRECORD *uirow;
4294
4295         if (feature->ActionRequest != INSTALLSTATE_LOCAL &&
4296             feature->ActionRequest != INSTALLSTATE_SOURCE &&
4297             feature->ActionRequest != INSTALLSTATE_ADVERTISED) absent = TRUE;
4298
4299         size = 1;
4300         LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
4301         {
4302             size += 21;
4303         }
4304         if (feature->Feature_Parent)
4305             size += strlenW( feature->Feature_Parent )+2;
4306
4307         data = msi_alloc(size * sizeof(WCHAR));
4308
4309         data[0] = 0;
4310         LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
4311         {
4312             MSICOMPONENT* component = cl->component;
4313             WCHAR buf[21];
4314
4315             buf[0] = 0;
4316             if (component->ComponentId)
4317             {
4318                 TRACE("From %s\n",debugstr_w(component->ComponentId));
4319                 CLSIDFromString(component->ComponentId, &clsid);
4320                 encode_base85_guid(&clsid,buf);
4321                 TRACE("to %s\n",debugstr_w(buf));
4322                 strcatW(data,buf);
4323             }
4324         }
4325
4326         if (feature->Feature_Parent)
4327         {
4328             static const WCHAR sep[] = {'\2',0};
4329             strcatW(data,sep);
4330             strcatW(data,feature->Feature_Parent);
4331         }
4332
4333         msi_reg_set_val_str( userdata, feature->Feature, data );
4334         msi_free(data);
4335
4336         size = 0;
4337         if (feature->Feature_Parent)
4338             size = strlenW(feature->Feature_Parent)*sizeof(WCHAR);
4339         if (!absent)
4340         {
4341             size += sizeof(WCHAR);
4342             RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
4343                            (LPBYTE)(feature->Feature_Parent ? feature->Feature_Parent : szEmpty),size);
4344         }
4345         else
4346         {
4347             size += 2*sizeof(WCHAR);
4348             data = msi_alloc(size);
4349             data[0] = 0x6;
4350             data[1] = 0;
4351             if (feature->Feature_Parent)
4352                 strcpyW( &data[1], feature->Feature_Parent );
4353             RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
4354                        (LPBYTE)data,size);
4355             msi_free(data);
4356         }
4357
4358         /* the UI chunk */
4359         uirow = MSI_CreateRecord( 1 );
4360         MSI_RecordSetStringW( uirow, 1, feature->Feature );
4361         ui_actiondata( package, szPublishFeatures, uirow);
4362         msiobj_release( &uirow->hdr );
4363         /* FIXME: call ui_progress? */
4364     }
4365
4366 end:
4367     RegCloseKey(hkey);
4368     RegCloseKey(userdata);
4369     return rc;
4370 }
4371
4372 static UINT msi_unpublish_feature(MSIPACKAGE *package, MSIFEATURE *feature)
4373 {
4374     UINT r;
4375     HKEY hkey;
4376
4377     TRACE("unpublishing feature %s\n", debugstr_w(feature->Feature));
4378
4379     r = MSIREG_OpenFeaturesKey(package->ProductCode, package->Context,
4380                                &hkey, FALSE);
4381     if (r == ERROR_SUCCESS)
4382     {
4383         RegDeleteValueW(hkey, feature->Feature);
4384         RegCloseKey(hkey);
4385     }
4386
4387     r = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, package->Context,
4388                                        &hkey, FALSE);
4389     if (r == ERROR_SUCCESS)
4390     {
4391         RegDeleteValueW(hkey, feature->Feature);
4392         RegCloseKey(hkey);
4393     }
4394
4395     return ERROR_SUCCESS;
4396 }
4397
4398 static UINT ACTION_UnpublishFeatures(MSIPACKAGE *package)
4399 {
4400     MSIFEATURE *feature;
4401
4402     if (!msi_check_unpublish(package))
4403         return ERROR_SUCCESS;
4404
4405     LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4406     {
4407         msi_unpublish_feature(package, feature);
4408     }
4409
4410     return ERROR_SUCCESS;
4411 }
4412
4413 static UINT msi_publish_install_properties(MSIPACKAGE *package, HKEY hkey)
4414 {
4415     LPWSTR prop, val, key;
4416     SYSTEMTIME systime;
4417     DWORD size, langid;
4418     WCHAR date[9];
4419     LPWSTR buffer;
4420
4421     static const WCHAR date_fmt[] = {'%','i','%','0','2','i','%','0','2','i',0};
4422     static const WCHAR szWindowsInstaller[] =
4423         {'W','i','n','d','o','w','s','I','n','s','t','a','l','l','e','r',0};
4424     static const WCHAR modpath_fmt[] =
4425         {'M','s','i','E','x','e','c','.','e','x','e',' ',
4426          '/','I','[','P','r','o','d','u','c','t','C','o','d','e',']',0};
4427     static const WCHAR szModifyPath[] =
4428         {'M','o','d','i','f','y','P','a','t','h',0};
4429     static const WCHAR szUninstallString[] =
4430         {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0};
4431     static const WCHAR szEstimatedSize[] =
4432         {'E','s','t','i','m','a','t','e','d','S','i','z','e',0};
4433     static const WCHAR szProductLanguage[] =
4434         {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
4435     static const WCHAR szProductVersion[] =
4436         {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
4437     static const WCHAR szProductName[] =
4438         {'P','r','o','d','u','c','t','N','a','m','e',0};
4439     static const WCHAR szDisplayName[] =
4440         {'D','i','s','p','l','a','y','N','a','m','e',0};
4441     static const WCHAR szDisplayVersion[] =
4442         {'D','i','s','p','l','a','y','V','e','r','s','i','o','n',0};
4443     static const WCHAR szManufacturer[] =
4444         {'M','a','n','u','f','a','c','t','u','r','e','r',0};
4445
4446     static const LPCSTR propval[] = {
4447         "ARPAUTHORIZEDCDFPREFIX", "AuthorizedCDFPrefix",
4448         "ARPCONTACT",             "Contact",
4449         "ARPCOMMENTS",            "Comments",
4450         "ProductName",            "DisplayName",
4451         "ProductVersion",         "DisplayVersion",
4452         "ARPHELPLINK",            "HelpLink",
4453         "ARPHELPTELEPHONE",       "HelpTelephone",
4454         "ARPINSTALLLOCATION",     "InstallLocation",
4455         "SourceDir",              "InstallSource",
4456         "Manufacturer",           "Publisher",
4457         "ARPREADME",              "Readme",
4458         "ARPSIZE",                "Size",
4459         "ARPURLINFOABOUT",        "URLInfoAbout",
4460         "ARPURLUPDATEINFO",       "URLUpdateInfo",
4461         NULL,
4462     };
4463     const LPCSTR *p = propval;
4464
4465     while (*p)
4466     {
4467         prop = strdupAtoW(*p++);
4468         key = strdupAtoW(*p++);
4469         val = msi_dup_property(package, prop);
4470         msi_reg_set_val_str(hkey, key, val);
4471         msi_free(val);
4472         msi_free(key);
4473         msi_free(prop);
4474     }
4475
4476     msi_reg_set_val_dword(hkey, szWindowsInstaller, 1);
4477
4478     size = deformat_string(package, modpath_fmt, &buffer);
4479     RegSetValueExW(hkey, szModifyPath, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
4480     RegSetValueExW(hkey, szUninstallString, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
4481     msi_free(buffer);
4482
4483     /* FIXME: Write real Estimated Size when we have it */
4484     msi_reg_set_val_dword(hkey, szEstimatedSize, 0);
4485
4486     buffer = msi_dup_property(package, szProductName);
4487     msi_reg_set_val_str(hkey, szDisplayName, buffer);
4488     msi_free(buffer);
4489
4490     buffer = msi_dup_property(package, cszSourceDir);
4491     msi_reg_set_val_str(hkey, INSTALLPROPERTY_INSTALLSOURCEW, buffer);
4492     msi_free(buffer);
4493
4494     buffer = msi_dup_property(package, szManufacturer);
4495     msi_reg_set_val_str(hkey, INSTALLPROPERTY_PUBLISHERW, buffer);
4496     msi_free(buffer);
4497
4498     GetLocalTime(&systime);
4499     sprintfW(date, date_fmt, systime.wYear, systime.wMonth, systime.wDay);
4500     msi_reg_set_val_str(hkey, INSTALLPROPERTY_INSTALLDATEW, date);
4501
4502     langid = msi_get_property_int(package, szProductLanguage, 0);
4503     msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
4504
4505     buffer = msi_dup_property(package, szProductVersion);
4506     msi_reg_set_val_str(hkey, szDisplayVersion, buffer);
4507     if (buffer)
4508     {
4509         DWORD verdword = msi_version_str_to_dword(buffer);
4510
4511         msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
4512         msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMAJORW, verdword >> 24);
4513         msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMINORW, (verdword >> 16) & 0xFF);
4514         msi_free(buffer);
4515     }
4516
4517     return ERROR_SUCCESS;
4518 }
4519
4520 static UINT ACTION_RegisterProduct(MSIPACKAGE *package)
4521 {
4522     WCHAR squashed_pc[SQUISH_GUID_SIZE];
4523     MSIRECORD *uirow;
4524     LPWSTR upgrade_code;
4525     HKEY hkey, props;
4526     HKEY upgrade;
4527     UINT rc;
4528
4529     static const WCHAR szUpgradeCode[] = {
4530         'U','p','g','r','a','d','e','C','o','d','e',0};
4531
4532     /* FIXME: also need to publish if the product is in advertise mode */
4533     if (!msi_check_publish(package))
4534         return ERROR_SUCCESS;
4535
4536     rc = MSIREG_OpenUninstallKey(package->ProductCode, &hkey, TRUE);
4537     if (rc != ERROR_SUCCESS)
4538         return rc;
4539
4540     rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context,
4541                                  NULL, &props, TRUE);
4542     if (rc != ERROR_SUCCESS)
4543         goto done;
4544
4545     msi_reg_set_val_str( props, INSTALLPROPERTY_LOCALPACKAGEW, package->db->localfile );
4546     msi_free( package->db->localfile );
4547     package->db->localfile = NULL;
4548
4549     rc = msi_publish_install_properties(package, hkey);
4550     if (rc != ERROR_SUCCESS)
4551         goto done;
4552
4553     rc = msi_publish_install_properties(package, props);
4554     if (rc != ERROR_SUCCESS)
4555         goto done;
4556
4557     upgrade_code = msi_dup_property(package, szUpgradeCode);
4558     if (upgrade_code)
4559     {
4560         MSIREG_OpenUpgradeCodesKey(upgrade_code, &upgrade, TRUE);
4561         squash_guid(package->ProductCode, squashed_pc);
4562         msi_reg_set_val_str(upgrade, squashed_pc, NULL);
4563         RegCloseKey(upgrade);
4564         msi_free(upgrade_code);
4565     }
4566
4567 done:
4568     uirow = MSI_CreateRecord( 1 );
4569     MSI_RecordSetStringW( uirow, 1, package->ProductCode );
4570     ui_actiondata( package, szRegisterProduct, uirow );
4571     msiobj_release( &uirow->hdr );
4572
4573     RegCloseKey(hkey);
4574     return ERROR_SUCCESS;
4575 }
4576
4577 static UINT ACTION_InstallExecute(MSIPACKAGE *package)
4578 {
4579     return execute_script(package,INSTALL_SCRIPT);
4580 }
4581
4582 static UINT msi_unpublish_product(MSIPACKAGE *package)
4583 {
4584     LPWSTR upgrade;
4585     LPWSTR remove = NULL;
4586     LPWSTR *features = NULL;
4587     BOOL full_uninstall = TRUE;
4588     MSIFEATURE *feature;
4589
4590     static const WCHAR szUpgradeCode[] =
4591         {'U','p','g','r','a','d','e','C','o','d','e',0};
4592
4593     remove = msi_dup_property(package, szRemove);
4594     if (!remove)
4595         return ERROR_SUCCESS;
4596
4597     features = msi_split_string(remove, ',');
4598     if (!features)
4599     {
4600         msi_free(remove);
4601         ERR("REMOVE feature list is empty!\n");
4602         return ERROR_FUNCTION_FAILED;
4603     }
4604
4605     if (!lstrcmpW(features[0], szAll))
4606         full_uninstall = TRUE;
4607     else
4608     {
4609         LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4610         {
4611             if (feature->Action != INSTALLSTATE_ABSENT)
4612                 full_uninstall = FALSE;
4613         }
4614     }
4615
4616     if (!full_uninstall)
4617         goto done;
4618
4619     MSIREG_DeleteProductKey(package->ProductCode);
4620     MSIREG_DeleteUserDataProductKey(package->ProductCode);
4621     MSIREG_DeleteUninstallKey(package->ProductCode);
4622
4623     if (package->Context == MSIINSTALLCONTEXT_MACHINE)
4624     {
4625         MSIREG_DeleteLocalClassesProductKey(package->ProductCode);
4626         MSIREG_DeleteLocalClassesFeaturesKey(package->ProductCode);
4627     }
4628     else
4629     {
4630         MSIREG_DeleteUserProductKey(package->ProductCode);
4631         MSIREG_DeleteUserFeaturesKey(package->ProductCode);
4632     }
4633
4634     upgrade = msi_dup_property(package, szUpgradeCode);
4635     if (upgrade)
4636     {
4637         MSIREG_DeleteUserUpgradeCodesKey(upgrade);
4638         msi_free(upgrade);
4639     }
4640
4641 done:
4642     msi_free(remove);
4643     msi_free(features);
4644     return ERROR_SUCCESS;
4645 }
4646
4647 static UINT ACTION_InstallFinalize(MSIPACKAGE *package)
4648 {
4649     UINT rc;
4650
4651     rc = msi_unpublish_product(package);
4652     if (rc != ERROR_SUCCESS)
4653         return rc;
4654
4655     /* turn off scheduling */
4656     package->script->CurrentlyScripting= FALSE;
4657
4658     /* first do the same as an InstallExecute */
4659     rc = ACTION_InstallExecute(package);
4660     if (rc != ERROR_SUCCESS)
4661         return rc;
4662
4663     /* then handle Commit Actions */
4664     rc = execute_script(package,COMMIT_SCRIPT);
4665
4666     return rc;
4667 }
4668
4669 UINT ACTION_ForceReboot(MSIPACKAGE *package)
4670 {
4671     static const WCHAR RunOnce[] = {
4672     'S','o','f','t','w','a','r','e','\\',
4673     'M','i','c','r','o','s','o','f','t','\\',
4674     'W','i','n','d','o','w','s','\\',
4675     'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
4676     'R','u','n','O','n','c','e',0};
4677     static const WCHAR InstallRunOnce[] = {
4678     'S','o','f','t','w','a','r','e','\\',
4679     'M','i','c','r','o','s','o','f','t','\\',
4680     'W','i','n','d','o','w','s','\\',
4681     'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
4682     'I','n','s','t','a','l','l','e','r','\\',
4683     'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
4684
4685     static const WCHAR msiexec_fmt[] = {
4686     '%','s',
4687     '\\','M','s','i','E','x','e','c','.','e','x','e',' ','/','@',' ',
4688     '\"','%','s','\"',0};
4689     static const WCHAR install_fmt[] = {
4690     '/','I',' ','\"','%','s','\"',' ',
4691     'A','F','T','E','R','R','E','B','O','O','T','=','1',' ',
4692     'R','U','N','O','N','C','E','E','N','T','R','Y','=','\"','%','s','\"',0};
4693     WCHAR buffer[256], sysdir[MAX_PATH];
4694     HKEY hkey;
4695     WCHAR squished_pc[100];
4696
4697     squash_guid(package->ProductCode,squished_pc);
4698
4699     GetSystemDirectoryW(sysdir, sizeof(sysdir)/sizeof(sysdir[0]));
4700     RegCreateKeyW(HKEY_LOCAL_MACHINE,RunOnce,&hkey);
4701     snprintfW(buffer,sizeof(buffer)/sizeof(buffer[0]),msiexec_fmt,sysdir,
4702      squished_pc);
4703
4704     msi_reg_set_val_str( hkey, squished_pc, buffer );
4705     RegCloseKey(hkey);
4706
4707     TRACE("Reboot command %s\n",debugstr_w(buffer));
4708
4709     RegCreateKeyW(HKEY_LOCAL_MACHINE,InstallRunOnce,&hkey);
4710     sprintfW(buffer,install_fmt,package->ProductCode,squished_pc);
4711
4712     msi_reg_set_val_str( hkey, squished_pc, buffer );
4713     RegCloseKey(hkey);
4714
4715     return ERROR_INSTALL_SUSPEND;
4716 }
4717
4718 static UINT ACTION_ResolveSource(MSIPACKAGE* package)
4719 {
4720     DWORD attrib;
4721     UINT rc;
4722
4723     /*
4724      * We are currently doing what should be done here in the top level Install
4725      * however for Administrative and uninstalls this step will be needed
4726      */
4727     if (!package->PackagePath)
4728         return ERROR_SUCCESS;
4729
4730     msi_set_sourcedir_props(package, TRUE);
4731
4732     attrib = GetFileAttributesW(package->db->path);
4733     if (attrib == INVALID_FILE_ATTRIBUTES)
4734     {
4735         LPWSTR prompt;
4736         LPWSTR msg;
4737         DWORD size = 0;
4738
4739         rc = MsiSourceListGetInfoW(package->ProductCode, NULL, 
4740                 package->Context, MSICODE_PRODUCT,
4741                 INSTALLPROPERTY_DISKPROMPTW,NULL,&size);
4742         if (rc == ERROR_MORE_DATA)
4743         {
4744             prompt = msi_alloc(size * sizeof(WCHAR));
4745             MsiSourceListGetInfoW(package->ProductCode, NULL, 
4746                     package->Context, MSICODE_PRODUCT,
4747                     INSTALLPROPERTY_DISKPROMPTW,prompt,&size);
4748         }
4749         else
4750             prompt = strdupW(package->db->path);
4751
4752         msg = generate_error_string(package,1302,1,prompt);
4753         while(attrib == INVALID_FILE_ATTRIBUTES)
4754         {
4755             rc = MessageBoxW(NULL,msg,NULL,MB_OKCANCEL);
4756             if (rc == IDCANCEL)
4757             {
4758                 rc = ERROR_INSTALL_USEREXIT;
4759                 break;
4760             }
4761             attrib = GetFileAttributesW(package->db->path);
4762         }
4763         msi_free(prompt);
4764         rc = ERROR_SUCCESS;
4765     }
4766     else
4767         return ERROR_SUCCESS;
4768
4769     return rc;
4770 }
4771
4772 static UINT ACTION_RegisterUser(MSIPACKAGE *package)
4773 {
4774     HKEY hkey = 0;
4775     LPWSTR buffer, productid = NULL;
4776     UINT i, rc = ERROR_SUCCESS;
4777     MSIRECORD *uirow;
4778
4779     static const WCHAR szPropKeys[][80] = 
4780     {
4781         {'P','r','o','d','u','c','t','I','D',0},
4782         {'U','S','E','R','N','A','M','E',0},
4783         {'C','O','M','P','A','N','Y','N','A','M','E',0},
4784         {0},
4785     };
4786
4787     static const WCHAR szRegKeys[][80] = 
4788     {
4789         {'P','r','o','d','u','c','t','I','D',0},
4790         {'R','e','g','O','w','n','e','r',0},
4791         {'R','e','g','C','o','m','p','a','n','y',0},
4792         {0},
4793     };
4794
4795     if (msi_check_unpublish(package))
4796     {
4797         MSIREG_DeleteUserDataProductKey(package->ProductCode);
4798         goto end;
4799     }
4800
4801     productid = msi_dup_property( package, INSTALLPROPERTY_PRODUCTIDW );
4802     if (!productid)
4803         goto end;
4804
4805     rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context,
4806                                  NULL, &hkey, TRUE);
4807     if (rc != ERROR_SUCCESS)
4808         goto end;
4809
4810     for( i = 0; szPropKeys[i][0]; i++ )
4811     {
4812         buffer = msi_dup_property( package, szPropKeys[i] );
4813         msi_reg_set_val_str( hkey, szRegKeys[i], buffer );
4814         msi_free( buffer );
4815     }
4816
4817 end:
4818     uirow = MSI_CreateRecord( 1 );
4819     MSI_RecordSetStringW( uirow, 1, productid );
4820     ui_actiondata( package, szRegisterUser, uirow );
4821     msiobj_release( &uirow->hdr );
4822
4823     msi_free(productid);
4824     RegCloseKey(hkey);
4825     return rc;
4826 }
4827
4828
4829 static UINT ACTION_ExecuteAction(MSIPACKAGE *package)
4830 {
4831     UINT rc;
4832
4833     package->script->InWhatSequence |= SEQUENCE_EXEC;
4834     rc = ACTION_ProcessExecSequence(package,FALSE);
4835     return rc;
4836 }
4837
4838
4839 static UINT ITERATE_PublishComponent(MSIRECORD *rec, LPVOID param)
4840 {
4841     MSIPACKAGE *package = param;
4842     LPCWSTR compgroupid, component, feature, qualifier, text;
4843     LPWSTR advertise = NULL, output = NULL;
4844     HKEY hkey = NULL;
4845     UINT rc;
4846     MSICOMPONENT *comp;
4847     MSIFEATURE *feat;
4848     DWORD sz;
4849     MSIRECORD *uirow;
4850
4851     feature = MSI_RecordGetString(rec, 5);
4852     feat = get_loaded_feature(package, feature);
4853     if (!feat)
4854         return ERROR_SUCCESS;
4855
4856     if (feat->ActionRequest != INSTALLSTATE_LOCAL &&
4857         feat->ActionRequest != INSTALLSTATE_SOURCE &&
4858         feat->ActionRequest != INSTALLSTATE_ADVERTISED)
4859     {
4860         TRACE("Feature %s not scheduled for installation\n", debugstr_w(feature));
4861         feat->Action = feat->Installed;
4862         return ERROR_SUCCESS;
4863     }
4864
4865     component = MSI_RecordGetString(rec, 3);
4866     comp = get_loaded_component(package, component);
4867     if (!comp)
4868         return ERROR_SUCCESS;
4869
4870     compgroupid = MSI_RecordGetString(rec,1);
4871     qualifier = MSI_RecordGetString(rec,2);
4872
4873     rc = MSIREG_OpenUserComponentsKey(compgroupid, &hkey, TRUE);
4874     if (rc != ERROR_SUCCESS)
4875         goto end;
4876     
4877     text = MSI_RecordGetString(rec,4);
4878     advertise = create_component_advertise_string(package, comp, feature);
4879
4880     sz = strlenW(advertise);
4881
4882     if (text)
4883         sz += lstrlenW(text);
4884
4885     sz+=3;
4886     sz *= sizeof(WCHAR);
4887            
4888     output = msi_alloc_zero(sz);
4889     strcpyW(output,advertise);
4890     msi_free(advertise);
4891
4892     if (text)
4893         strcatW(output,text);
4894
4895     msi_reg_set_val_multi_str( hkey, qualifier, output );
4896     
4897 end:
4898     RegCloseKey(hkey);
4899     msi_free(output);
4900
4901     /* the UI chunk */
4902     uirow = MSI_CreateRecord( 2 );
4903     MSI_RecordSetStringW( uirow, 1, compgroupid );
4904     MSI_RecordSetStringW( uirow, 2, qualifier);
4905     ui_actiondata( package, szPublishComponents, uirow);
4906     msiobj_release( &uirow->hdr );
4907     /* FIXME: call ui_progress? */
4908
4909     return rc;
4910 }
4911
4912 /*
4913  * At present I am ignorning the advertised components part of this and only
4914  * focusing on the qualified component sets
4915  */
4916 static UINT ACTION_PublishComponents(MSIPACKAGE *package)
4917 {
4918     UINT rc;
4919     MSIQUERY * view;
4920     static const WCHAR ExecSeqQuery[] =
4921         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4922          '`','P','u','b','l','i','s','h',
4923          'C','o','m','p','o','n','e','n','t','`',0};
4924     
4925     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4926     if (rc != ERROR_SUCCESS)
4927         return ERROR_SUCCESS;
4928
4929     rc = MSI_IterateRecords(view, NULL, ITERATE_PublishComponent, package);
4930     msiobj_release(&view->hdr);
4931
4932     return rc;
4933 }
4934
4935 static UINT ITERATE_UnpublishComponent( MSIRECORD *rec, LPVOID param )
4936 {
4937     static const WCHAR szInstallerComponents[] = {
4938         'S','o','f','t','w','a','r','e','\\',
4939         'M','i','c','r','o','s','o','f','t','\\',
4940         'I','n','s','t','a','l','l','e','r','\\',
4941         'C','o','m','p','o','n','e','n','t','s','\\',0};
4942
4943     MSIPACKAGE *package = param;
4944     LPCWSTR compgroupid, component, feature, qualifier;
4945     MSICOMPONENT *comp;
4946     MSIFEATURE *feat;
4947     MSIRECORD *uirow;
4948     WCHAR squashed[GUID_SIZE], keypath[MAX_PATH];
4949     LONG res;
4950
4951     feature = MSI_RecordGetString( rec, 5 );
4952     feat = get_loaded_feature( package, feature );
4953     if (!feat)
4954         return ERROR_SUCCESS;
4955
4956     if (feat->ActionRequest != INSTALLSTATE_ABSENT)
4957     {
4958         TRACE("Feature %s not scheduled for removal\n", debugstr_w(feature));
4959         feat->Action = feat->Installed;
4960         return ERROR_SUCCESS;
4961     }
4962
4963     component = MSI_RecordGetString( rec, 3 );
4964     comp = get_loaded_component( package, component );
4965     if (!comp)
4966         return ERROR_SUCCESS;
4967
4968     compgroupid = MSI_RecordGetString( rec, 1 );
4969     qualifier = MSI_RecordGetString( rec, 2 );
4970
4971     squash_guid( compgroupid, squashed );
4972     strcpyW( keypath, szInstallerComponents );
4973     strcatW( keypath, squashed );
4974
4975     res = RegDeleteKeyW( HKEY_CURRENT_USER, keypath );
4976     if (res != ERROR_SUCCESS)
4977     {
4978         WARN("Unable to delete component key %d\n", res);
4979     }
4980
4981     uirow = MSI_CreateRecord( 2 );
4982     MSI_RecordSetStringW( uirow, 1, compgroupid );
4983     MSI_RecordSetStringW( uirow, 2, qualifier );
4984     ui_actiondata( package, szUnpublishComponents, uirow );
4985     msiobj_release( &uirow->hdr );
4986
4987     return ERROR_SUCCESS;
4988 }
4989
4990 static UINT ACTION_UnpublishComponents( MSIPACKAGE *package )
4991 {
4992     UINT rc;
4993     MSIQUERY *view;
4994     static const WCHAR query[] =
4995         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4996          '`','P','u','b','l','i','s','h',
4997          'C','o','m','p','o','n','e','n','t','`',0};
4998
4999     rc = MSI_DatabaseOpenViewW( package->db, query, &view );
5000     if (rc != ERROR_SUCCESS)
5001         return ERROR_SUCCESS;
5002
5003     rc = MSI_IterateRecords( view, NULL, ITERATE_UnpublishComponent, package );
5004     msiobj_release( &view->hdr );
5005
5006     return rc;
5007 }
5008
5009 static UINT ITERATE_InstallService(MSIRECORD *rec, LPVOID param)
5010 {
5011     MSIPACKAGE *package = param;
5012     MSIRECORD *row;
5013     MSIFILE *file;
5014     SC_HANDLE hscm, service = NULL;
5015     LPCWSTR comp, depends, pass;
5016     LPWSTR name = NULL, disp = NULL;
5017     LPCWSTR load_order, serv_name, key;
5018     DWORD serv_type, start_type;
5019     DWORD err_control;
5020
5021     static const WCHAR query[] =
5022         {'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
5023          '`','C','o','m','p','o','n','e','n','t','`',' ',
5024          'W','H','E','R','E',' ',
5025          '`','C','o','m','p','o','n','e','n','t','`',' ',
5026          '=','\'','%','s','\'',0};
5027
5028     hscm = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASEW, GENERIC_WRITE);
5029     if (!hscm)
5030     {
5031         ERR("Failed to open the SC Manager!\n");
5032         goto done;
5033     }
5034
5035     start_type = MSI_RecordGetInteger(rec, 5);
5036     if (start_type == SERVICE_BOOT_START || start_type == SERVICE_SYSTEM_START)
5037         goto done;
5038
5039     depends = MSI_RecordGetString(rec, 8);
5040     if (depends && *depends)
5041         FIXME("Dependency list unhandled!\n");
5042
5043     deformat_string(package, MSI_RecordGetString(rec, 2), &name);
5044     deformat_string(package, MSI_RecordGetString(rec, 3), &disp);
5045     serv_type = MSI_RecordGetInteger(rec, 4);
5046     err_control = MSI_RecordGetInteger(rec, 6);
5047     load_order = MSI_RecordGetString(rec, 7);
5048     serv_name = MSI_RecordGetString(rec, 9);
5049     pass = MSI_RecordGetString(rec, 10);
5050     comp = MSI_RecordGetString(rec, 12);
5051
5052     /* fetch the service path */
5053     row = MSI_QueryGetRecord(package->db, query, comp);
5054     if (!row)
5055     {
5056         ERR("Control query failed!\n");
5057         goto done;
5058     }
5059
5060     key = MSI_RecordGetString(row, 6);
5061
5062     file = get_loaded_file(package, key);
5063     msiobj_release(&row->hdr);
5064     if (!file)
5065     {
5066         ERR("Failed to load the service file\n");
5067         goto done;
5068     }
5069
5070     service = CreateServiceW(hscm, name, disp, GENERIC_ALL, serv_type,
5071                              start_type, err_control, file->TargetPath,
5072                              load_order, NULL, NULL, serv_name, pass);
5073     if (!service)
5074     {
5075         if (GetLastError() != ERROR_SERVICE_EXISTS)
5076             ERR("Failed to create service %s: %d\n", debugstr_w(name), GetLastError());
5077     }
5078
5079 done:
5080     CloseServiceHandle(service);
5081     CloseServiceHandle(hscm);
5082     msi_free(name);
5083     msi_free(disp);
5084
5085     return ERROR_SUCCESS;
5086 }
5087
5088 static UINT ACTION_InstallServices( MSIPACKAGE *package )
5089 {
5090     UINT rc;
5091     MSIQUERY * view;
5092     static const WCHAR ExecSeqQuery[] =
5093         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5094          'S','e','r','v','i','c','e','I','n','s','t','a','l','l',0};
5095     
5096     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
5097     if (rc != ERROR_SUCCESS)
5098         return ERROR_SUCCESS;
5099
5100     rc = MSI_IterateRecords(view, NULL, ITERATE_InstallService, package);
5101     msiobj_release(&view->hdr);
5102
5103     return rc;
5104 }
5105
5106 /* converts arg1[~]arg2[~]arg3 to a list of ptrs to the strings */
5107 static LPCWSTR *msi_service_args_to_vector(LPWSTR args, DWORD *numargs)
5108 {
5109     LPCWSTR *vector, *temp_vector;
5110     LPWSTR p, q;
5111     DWORD sep_len;
5112
5113     static const WCHAR separator[] = {'[','~',']',0};
5114
5115     *numargs = 0;
5116     sep_len = sizeof(separator) / sizeof(WCHAR) - 1;
5117
5118     if (!args)
5119         return NULL;
5120
5121     vector = msi_alloc(sizeof(LPWSTR));
5122     if (!vector)
5123         return NULL;
5124
5125     p = args;
5126     do
5127     {
5128         (*numargs)++;
5129         vector[*numargs - 1] = p;
5130
5131         if ((q = strstrW(p, separator)))
5132         {
5133             *q = '\0';
5134
5135             temp_vector = msi_realloc(vector, (*numargs + 1) * sizeof(LPWSTR));
5136             if (!temp_vector)
5137             {
5138                 msi_free(vector);
5139                 return NULL;
5140             }
5141             vector = temp_vector;
5142
5143             p = q + sep_len;
5144         }
5145     } while (q);
5146
5147     return vector;
5148 }
5149
5150 static UINT ITERATE_StartService(MSIRECORD *rec, LPVOID param)
5151 {
5152     MSIPACKAGE *package = param;
5153     MSICOMPONENT *comp;
5154     SC_HANDLE scm = NULL, service = NULL;
5155     LPCWSTR component, *vector = NULL;
5156     LPWSTR name, args;
5157     DWORD event, numargs;
5158     UINT r = ERROR_FUNCTION_FAILED;
5159
5160     component = MSI_RecordGetString(rec, 6);
5161     comp = get_loaded_component(package, component);
5162     if (!comp)
5163         return ERROR_SUCCESS;
5164
5165     if (comp->ActionRequest != INSTALLSTATE_LOCAL)
5166     {
5167         TRACE("Component not scheduled for installation: %s\n", debugstr_w(component));
5168         comp->Action = comp->Installed;
5169         return ERROR_SUCCESS;
5170     }
5171     comp->Action = INSTALLSTATE_LOCAL;
5172
5173     deformat_string(package, MSI_RecordGetString(rec, 2), &name);
5174     deformat_string(package, MSI_RecordGetString(rec, 4), &args);
5175     event = MSI_RecordGetInteger(rec, 3);
5176
5177     if (!(event & msidbServiceControlEventStart))
5178     {
5179         r = ERROR_SUCCESS;
5180         goto done;
5181     }
5182
5183     scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
5184     if (!scm)
5185     {
5186         ERR("Failed to open the service control manager\n");
5187         goto done;
5188     }
5189
5190     service = OpenServiceW(scm, name, SERVICE_START);
5191     if (!service)
5192     {
5193         ERR("Failed to open service %s (%u)\n", debugstr_w(name), GetLastError());
5194         goto done;
5195     }
5196
5197     vector = msi_service_args_to_vector(args, &numargs);
5198
5199     if (!StartServiceW(service, numargs, vector) &&
5200         GetLastError() != ERROR_SERVICE_ALREADY_RUNNING)
5201     {
5202         ERR("Failed to start service %s (%u)\n", debugstr_w(name), GetLastError());
5203         goto done;
5204     }
5205
5206     r = ERROR_SUCCESS;
5207
5208 done:
5209     CloseServiceHandle(service);
5210     CloseServiceHandle(scm);
5211
5212     msi_free(name);
5213     msi_free(args);
5214     msi_free(vector);
5215     return r;
5216 }
5217
5218 static UINT ACTION_StartServices( MSIPACKAGE *package )
5219 {
5220     UINT rc;
5221     MSIQUERY *view;
5222
5223     static const WCHAR query[] = {
5224         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5225         'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
5226
5227     rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5228     if (rc != ERROR_SUCCESS)
5229         return ERROR_SUCCESS;
5230
5231     rc = MSI_IterateRecords(view, NULL, ITERATE_StartService, package);
5232     msiobj_release(&view->hdr);
5233
5234     return rc;
5235 }
5236
5237 static BOOL stop_service_dependents(SC_HANDLE scm, SC_HANDLE service)
5238 {
5239     DWORD i, needed, count;
5240     ENUM_SERVICE_STATUSW *dependencies;
5241     SERVICE_STATUS ss;
5242     SC_HANDLE depserv;
5243
5244     if (EnumDependentServicesW(service, SERVICE_ACTIVE, NULL,
5245                                0, &needed, &count))
5246         return TRUE;
5247
5248     if (GetLastError() != ERROR_MORE_DATA)
5249         return FALSE;
5250
5251     dependencies = msi_alloc(needed);
5252     if (!dependencies)
5253         return FALSE;
5254
5255     if (!EnumDependentServicesW(service, SERVICE_ACTIVE, dependencies,
5256                                 needed, &needed, &count))
5257         goto error;
5258
5259     for (i = 0; i < count; i++)
5260     {
5261         depserv = OpenServiceW(scm, dependencies[i].lpServiceName,
5262                                SERVICE_STOP | SERVICE_QUERY_STATUS);
5263         if (!depserv)
5264             goto error;
5265
5266         if (!ControlService(depserv, SERVICE_CONTROL_STOP, &ss))
5267             goto error;
5268     }
5269
5270     return TRUE;
5271
5272 error:
5273     msi_free(dependencies);
5274     return FALSE;
5275 }
5276
5277 static UINT stop_service( LPCWSTR name )
5278 {
5279     SC_HANDLE scm = NULL, service = NULL;
5280     SERVICE_STATUS status;
5281     SERVICE_STATUS_PROCESS ssp;
5282     DWORD needed;
5283
5284     scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
5285     if (!scm)
5286     {
5287         WARN("Failed to open the SCM: %d\n", GetLastError());
5288         goto done;
5289     }
5290
5291     service = OpenServiceW(scm, name,
5292                            SERVICE_STOP |
5293                            SERVICE_QUERY_STATUS |
5294                            SERVICE_ENUMERATE_DEPENDENTS);
5295     if (!service)
5296     {
5297         WARN("Failed to open service (%s): %d\n", debugstr_w(name), GetLastError());
5298         goto done;
5299     }
5300
5301     if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp,
5302                               sizeof(SERVICE_STATUS_PROCESS), &needed))
5303     {
5304         WARN("Failed to query service status (%s): %d\n", debugstr_w(name), GetLastError());
5305         goto done;
5306     }
5307
5308     if (ssp.dwCurrentState == SERVICE_STOPPED)
5309         goto done;
5310
5311     stop_service_dependents(scm, service);
5312
5313     if (!ControlService(service, SERVICE_CONTROL_STOP, &status))
5314         WARN("Failed to stop service (%s): %d\n", debugstr_w(name), GetLastError());
5315
5316 done:
5317     CloseServiceHandle(service);
5318     CloseServiceHandle(scm);
5319
5320     return ERROR_SUCCESS;
5321 }
5322
5323 static UINT ITERATE_StopService( MSIRECORD *rec, LPVOID param )
5324 {
5325     MSIPACKAGE *package = param;
5326     MSICOMPONENT *comp;
5327     LPCWSTR component;
5328     LPWSTR name;
5329     DWORD event;
5330
5331     event = MSI_RecordGetInteger( rec, 3 );
5332     if (!(event & msidbServiceControlEventStop))
5333         return ERROR_SUCCESS;
5334
5335     component = MSI_RecordGetString( rec, 6 );
5336     comp = get_loaded_component( package, component );
5337     if (!comp)
5338         return ERROR_SUCCESS;
5339
5340     if (comp->ActionRequest != INSTALLSTATE_ABSENT)
5341     {
5342         TRACE("Component not scheduled for removal: %s\n", debugstr_w(component));
5343         comp->Action = comp->Installed;
5344         return ERROR_SUCCESS;
5345     }
5346     comp->Action = INSTALLSTATE_ABSENT;
5347
5348     deformat_string( package, MSI_RecordGetString( rec, 2 ), &name );
5349     stop_service( name );
5350     msi_free( name );
5351
5352     return ERROR_SUCCESS;
5353 }
5354
5355 static UINT ACTION_StopServices( MSIPACKAGE *package )
5356 {
5357     UINT rc;
5358     MSIQUERY *view;
5359
5360     static const WCHAR query[] = {
5361         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5362         'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
5363
5364     rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5365     if (rc != ERROR_SUCCESS)
5366         return ERROR_SUCCESS;
5367
5368     rc = MSI_IterateRecords(view, NULL, ITERATE_StopService, package);
5369     msiobj_release(&view->hdr);
5370
5371     return rc;
5372 }
5373
5374 static UINT ITERATE_DeleteService( MSIRECORD *rec, LPVOID param )
5375 {
5376     MSIPACKAGE *package = param;
5377     MSICOMPONENT *comp;
5378     MSIRECORD *uirow;
5379     LPCWSTR component;
5380     LPWSTR name = NULL, display_name = NULL;
5381     DWORD event, len;
5382     SC_HANDLE scm = NULL, service = NULL;
5383
5384     event = MSI_RecordGetInteger( rec, 3 );
5385     if (!(event & msidbServiceControlEventDelete))
5386         return ERROR_SUCCESS;
5387
5388     component = MSI_RecordGetString(rec, 6);
5389     comp = get_loaded_component(package, component);
5390     if (!comp)
5391         return ERROR_SUCCESS;
5392
5393     if (comp->ActionRequest != INSTALLSTATE_ABSENT)
5394     {
5395         TRACE("Component not scheduled for removal: %s\n", debugstr_w(component));
5396         comp->Action = comp->Installed;
5397         return ERROR_SUCCESS;
5398     }
5399     comp->Action = INSTALLSTATE_ABSENT;
5400
5401     deformat_string( package, MSI_RecordGetString(rec, 2), &name );
5402     stop_service( name );
5403
5404     scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_ALL_ACCESS );
5405     if (!scm)
5406     {
5407         WARN("Failed to open the SCM: %d\n", GetLastError());
5408         goto done;
5409     }
5410
5411     len = 0;
5412     if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
5413         GetLastError() == ERROR_INSUFFICIENT_BUFFER)
5414     {
5415         if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
5416             GetServiceDisplayNameW( scm, name, display_name, &len );
5417     }
5418
5419     service = OpenServiceW( scm, name, DELETE );
5420     if (!service)
5421     {
5422         WARN("Failed to open service (%s): %u\n", debugstr_w(name), GetLastError());
5423         goto done;
5424     }
5425
5426     if (!DeleteService( service ))
5427         WARN("Failed to delete service (%s): %u\n", debugstr_w(name), GetLastError());
5428
5429 done:
5430     uirow = MSI_CreateRecord( 2 );
5431     MSI_RecordSetStringW( uirow, 1, display_name );
5432     MSI_RecordSetStringW( uirow, 2, name );
5433     ui_actiondata( package, szDeleteServices, uirow );
5434     msiobj_release( &uirow->hdr );
5435
5436     CloseServiceHandle( service );
5437     CloseServiceHandle( scm );
5438     msi_free( name );
5439     msi_free( display_name );
5440
5441     return ERROR_SUCCESS;
5442 }
5443
5444 static UINT ACTION_DeleteServices( MSIPACKAGE *package )
5445 {
5446     UINT rc;
5447     MSIQUERY *view;
5448
5449     static const WCHAR query[] = {
5450         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5451         'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
5452
5453     rc = MSI_DatabaseOpenViewW( package->db, query, &view );
5454     if (rc != ERROR_SUCCESS)
5455         return ERROR_SUCCESS;
5456
5457     rc = MSI_IterateRecords( view, NULL, ITERATE_DeleteService, package );
5458     msiobj_release( &view->hdr );
5459
5460     return rc;
5461 }
5462
5463 static MSIFILE *msi_find_file( MSIPACKAGE *package, LPCWSTR filename )
5464 {
5465     MSIFILE *file;
5466
5467     LIST_FOR_EACH_ENTRY(file, &package->files, MSIFILE, entry)
5468     {
5469         if (!lstrcmpW(file->File, filename))
5470             return file;
5471     }
5472
5473     return NULL;
5474 }
5475
5476 static UINT ITERATE_InstallODBCDriver( MSIRECORD *rec, LPVOID param )
5477 {
5478     MSIPACKAGE *package = param;
5479     LPWSTR driver, driver_path, ptr;
5480     WCHAR outpath[MAX_PATH];
5481     MSIFILE *driver_file, *setup_file;
5482     MSIRECORD *uirow;
5483     LPCWSTR desc;
5484     DWORD len, usage;
5485     UINT r = ERROR_SUCCESS;
5486
5487     static const WCHAR driver_fmt[] = {
5488         'D','r','i','v','e','r','=','%','s',0};
5489     static const WCHAR setup_fmt[] = {
5490         'S','e','t','u','p','=','%','s',0};
5491     static const WCHAR usage_fmt[] = {
5492         'F','i','l','e','U','s','a','g','e','=','1',0};
5493
5494     desc = MSI_RecordGetString(rec, 3);
5495
5496     driver_file = msi_find_file(package, MSI_RecordGetString(rec, 4));
5497     setup_file = msi_find_file(package, MSI_RecordGetString(rec, 5));
5498
5499     if (!driver_file)
5500     {
5501         ERR("ODBC Driver entry not found!\n");
5502         return ERROR_FUNCTION_FAILED;
5503     }
5504
5505     len = lstrlenW(desc) + lstrlenW(driver_fmt) + lstrlenW(driver_file->FileName);
5506     if (setup_file)
5507         len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
5508     len += lstrlenW(usage_fmt) + 2; /* \0\0 */
5509
5510     driver = msi_alloc(len * sizeof(WCHAR));
5511     if (!driver)
5512         return ERROR_OUTOFMEMORY;
5513
5514     ptr = driver;
5515     lstrcpyW(ptr, desc);
5516     ptr += lstrlenW(ptr) + 1;
5517
5518     len = sprintfW(ptr, driver_fmt, driver_file->FileName);
5519     ptr += len + 1;
5520
5521     if (setup_file)
5522     {
5523         len = sprintfW(ptr, setup_fmt, setup_file->FileName);
5524         ptr += len + 1;
5525     }
5526
5527     lstrcpyW(ptr, usage_fmt);
5528     ptr += lstrlenW(ptr) + 1;
5529     *ptr = '\0';
5530
5531     driver_path = strdupW(driver_file->TargetPath);
5532     ptr = strrchrW(driver_path, '\\');
5533     if (ptr) *ptr = '\0';
5534
5535     if (!SQLInstallDriverExW(driver, driver_path, outpath, MAX_PATH,
5536                              NULL, ODBC_INSTALL_COMPLETE, &usage))
5537     {
5538         ERR("Failed to install SQL driver!\n");
5539         r = ERROR_FUNCTION_FAILED;
5540     }
5541
5542     uirow = MSI_CreateRecord( 5 );
5543     MSI_RecordSetStringW( uirow, 1, desc );
5544     MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
5545     MSI_RecordSetStringW( uirow, 3, driver_path );
5546     ui_actiondata( package, szInstallODBC, uirow );
5547     msiobj_release( &uirow->hdr );
5548
5549     msi_free(driver);
5550     msi_free(driver_path);
5551
5552     return r;
5553 }
5554
5555 static UINT ITERATE_InstallODBCTranslator( MSIRECORD *rec, LPVOID param )
5556 {
5557     MSIPACKAGE *package = param;
5558     LPWSTR translator, translator_path, ptr;
5559     WCHAR outpath[MAX_PATH];
5560     MSIFILE *translator_file, *setup_file;
5561     MSIRECORD *uirow;
5562     LPCWSTR desc;
5563     DWORD len, usage;
5564     UINT r = ERROR_SUCCESS;
5565
5566     static const WCHAR translator_fmt[] = {
5567         'T','r','a','n','s','l','a','t','o','r','=','%','s',0};
5568     static const WCHAR setup_fmt[] = {
5569         'S','e','t','u','p','=','%','s',0};
5570
5571     desc = MSI_RecordGetString(rec, 3);
5572
5573     translator_file = msi_find_file(package, MSI_RecordGetString(rec, 4));
5574     setup_file = msi_find_file(package, MSI_RecordGetString(rec, 5));
5575
5576     if (!translator_file)
5577     {
5578         ERR("ODBC Translator entry not found!\n");
5579         return ERROR_FUNCTION_FAILED;
5580     }
5581
5582     len = lstrlenW(desc) + lstrlenW(translator_fmt) + lstrlenW(translator_file->FileName) + 2; /* \0\0 */
5583     if (setup_file)
5584         len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
5585
5586     translator = msi_alloc(len * sizeof(WCHAR));
5587     if (!translator)
5588         return ERROR_OUTOFMEMORY;
5589
5590     ptr = translator;
5591     lstrcpyW(ptr, desc);
5592     ptr += lstrlenW(ptr) + 1;
5593
5594     len = sprintfW(ptr, translator_fmt, translator_file->FileName);
5595     ptr += len + 1;
5596
5597     if (setup_file)
5598     {
5599         len = sprintfW(ptr, setup_fmt, setup_file->FileName);
5600         ptr += len + 1;
5601     }
5602     *ptr = '\0';
5603
5604     translator_path = strdupW(translator_file->TargetPath);
5605     ptr = strrchrW(translator_path, '\\');
5606     if (ptr) *ptr = '\0';
5607
5608     if (!SQLInstallTranslatorExW(translator, translator_path, outpath, MAX_PATH,
5609                                  NULL, ODBC_INSTALL_COMPLETE, &usage))
5610     {
5611         ERR("Failed to install SQL translator!\n");
5612         r = ERROR_FUNCTION_FAILED;
5613     }
5614
5615     uirow = MSI_CreateRecord( 5 );
5616     MSI_RecordSetStringW( uirow, 1, desc );
5617     MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
5618     MSI_RecordSetStringW( uirow, 3, translator_path );
5619     ui_actiondata( package, szInstallODBC, uirow );
5620     msiobj_release( &uirow->hdr );
5621
5622     msi_free(translator);
5623     msi_free(translator_path);
5624
5625     return r;
5626 }
5627
5628 static UINT ITERATE_InstallODBCDataSource( MSIRECORD *rec, LPVOID param )
5629 {
5630     MSIPACKAGE *package = param;
5631     LPWSTR attrs;
5632     LPCWSTR desc, driver;
5633     WORD request = ODBC_ADD_SYS_DSN;
5634     INT registration;
5635     DWORD len;
5636     UINT r = ERROR_SUCCESS;
5637     MSIRECORD *uirow;
5638
5639     static const WCHAR attrs_fmt[] = {
5640         'D','S','N','=','%','s',0 };
5641
5642     desc = MSI_RecordGetString(rec, 3);
5643     driver = MSI_RecordGetString(rec, 4);
5644     registration = MSI_RecordGetInteger(rec, 5);
5645
5646     if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_ADD_SYS_DSN;
5647     else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_ADD_DSN;
5648
5649     len = lstrlenW(attrs_fmt) + lstrlenW(desc) + 2; /* \0\0 */
5650     attrs = msi_alloc(len * sizeof(WCHAR));
5651     if (!attrs)
5652         return ERROR_OUTOFMEMORY;
5653
5654     len = sprintfW(attrs, attrs_fmt, desc);
5655     attrs[len + 1] = 0;
5656
5657     if (!SQLConfigDataSourceW(NULL, request, driver, attrs))
5658     {
5659         ERR("Failed to install SQL data source!\n");
5660         r = ERROR_FUNCTION_FAILED;
5661     }
5662
5663     uirow = MSI_CreateRecord( 5 );
5664     MSI_RecordSetStringW( uirow, 1, desc );
5665     MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
5666     MSI_RecordSetInteger( uirow, 3, request );
5667     ui_actiondata( package, szInstallODBC, uirow );
5668     msiobj_release( &uirow->hdr );
5669
5670     msi_free(attrs);
5671
5672     return r;
5673 }
5674
5675 static UINT ACTION_InstallODBC( MSIPACKAGE *package )
5676 {
5677     UINT rc;
5678     MSIQUERY *view;
5679
5680     static const WCHAR driver_query[] = {
5681         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5682         'O','D','B','C','D','r','i','v','e','r',0 };
5683
5684     static const WCHAR translator_query[] = {
5685         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5686         'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0 };
5687
5688     static const WCHAR source_query[] = {
5689         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5690         'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0 };
5691
5692     rc = MSI_DatabaseOpenViewW(package->db, driver_query, &view);
5693     if (rc != ERROR_SUCCESS)
5694         return ERROR_SUCCESS;
5695
5696     rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDriver, package);
5697     msiobj_release(&view->hdr);
5698
5699     rc = MSI_DatabaseOpenViewW(package->db, translator_query, &view);
5700     if (rc != ERROR_SUCCESS)
5701         return ERROR_SUCCESS;
5702
5703     rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCTranslator, package);
5704     msiobj_release(&view->hdr);
5705
5706     rc = MSI_DatabaseOpenViewW(package->db, source_query, &view);
5707     if (rc != ERROR_SUCCESS)
5708         return ERROR_SUCCESS;
5709
5710     rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDataSource, package);
5711     msiobj_release(&view->hdr);
5712
5713     return rc;
5714 }
5715
5716 static UINT ITERATE_RemoveODBCDriver( MSIRECORD *rec, LPVOID param )
5717 {
5718     MSIPACKAGE *package = param;
5719     MSIRECORD *uirow;
5720     DWORD usage;
5721     LPCWSTR desc;
5722
5723     desc = MSI_RecordGetString( rec, 3 );
5724     if (!SQLRemoveDriverW( desc, FALSE, &usage ))
5725     {
5726         WARN("Failed to remove ODBC driver\n");
5727     }
5728     else if (!usage)
5729     {
5730         FIXME("Usage count reached 0\n");
5731     }
5732
5733     uirow = MSI_CreateRecord( 2 );
5734     MSI_RecordSetStringW( uirow, 1, desc );
5735     MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
5736     ui_actiondata( package, szRemoveODBC, uirow );
5737     msiobj_release( &uirow->hdr );
5738
5739     return ERROR_SUCCESS;
5740 }
5741
5742 static UINT ITERATE_RemoveODBCTranslator( MSIRECORD *rec, LPVOID param )
5743 {
5744     MSIPACKAGE *package = param;
5745     MSIRECORD *uirow;
5746     DWORD usage;
5747     LPCWSTR desc;
5748
5749     desc = MSI_RecordGetString( rec, 3 );
5750     if (!SQLRemoveTranslatorW( desc, &usage ))
5751     {
5752         WARN("Failed to remove ODBC translator\n");
5753     }
5754     else if (!usage)
5755     {
5756         FIXME("Usage count reached 0\n");
5757     }
5758
5759     uirow = MSI_CreateRecord( 2 );
5760     MSI_RecordSetStringW( uirow, 1, desc );
5761     MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
5762     ui_actiondata( package, szRemoveODBC, uirow );
5763     msiobj_release( &uirow->hdr );
5764
5765     return ERROR_SUCCESS;
5766 }
5767
5768 static UINT ITERATE_RemoveODBCDataSource( MSIRECORD *rec, LPVOID param )
5769 {
5770     MSIPACKAGE *package = param;
5771     MSIRECORD *uirow;
5772     LPWSTR attrs;
5773     LPCWSTR desc, driver;
5774     WORD request = ODBC_REMOVE_SYS_DSN;
5775     INT registration;
5776     DWORD len;
5777
5778     static const WCHAR attrs_fmt[] = {
5779         'D','S','N','=','%','s',0 };
5780
5781     desc = MSI_RecordGetString( rec, 3 );
5782     driver = MSI_RecordGetString( rec, 4 );
5783     registration = MSI_RecordGetInteger( rec, 5 );
5784
5785     if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_REMOVE_SYS_DSN;
5786     else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_REMOVE_DSN;
5787
5788     len = strlenW( attrs_fmt ) + strlenW( desc ) + 2; /* \0\0 */
5789     attrs = msi_alloc( len * sizeof(WCHAR) );
5790     if (!attrs)
5791         return ERROR_OUTOFMEMORY;
5792
5793     FIXME("Use ODBCSourceAttribute table\n");
5794
5795     len = sprintfW( attrs, attrs_fmt, desc );
5796     attrs[len + 1] = 0;
5797
5798     if (!SQLConfigDataSourceW( NULL, request, driver, attrs ))
5799     {
5800         WARN("Failed to remove ODBC data source\n");
5801     }
5802     msi_free( attrs );
5803
5804     uirow = MSI_CreateRecord( 3 );
5805     MSI_RecordSetStringW( uirow, 1, desc );
5806     MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
5807     MSI_RecordSetInteger( uirow, 3, request );
5808     ui_actiondata( package, szRemoveODBC, uirow );
5809     msiobj_release( &uirow->hdr );
5810
5811     return ERROR_SUCCESS;
5812 }
5813
5814 static UINT ACTION_RemoveODBC( MSIPACKAGE *package )
5815 {
5816     UINT rc;
5817     MSIQUERY *view;
5818
5819     static const WCHAR driver_query[] = {
5820         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5821         'O','D','B','C','D','r','i','v','e','r',0 };
5822
5823     static const WCHAR translator_query[] = {
5824         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5825         'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0 };
5826
5827     static const WCHAR source_query[] = {
5828         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5829         'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0 };
5830
5831     rc = MSI_DatabaseOpenViewW( package->db, driver_query, &view );
5832     if (rc != ERROR_SUCCESS)
5833         return ERROR_SUCCESS;
5834
5835     rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDriver, package );
5836     msiobj_release( &view->hdr );
5837
5838     rc = MSI_DatabaseOpenViewW( package->db, translator_query, &view );
5839     if (rc != ERROR_SUCCESS)
5840         return ERROR_SUCCESS;
5841
5842     rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCTranslator, package );
5843     msiobj_release( &view->hdr );
5844
5845     rc = MSI_DatabaseOpenViewW( package->db, source_query, &view );
5846     if (rc != ERROR_SUCCESS)
5847         return ERROR_SUCCESS;
5848
5849     rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDataSource, package );
5850     msiobj_release( &view->hdr );
5851
5852     return rc;
5853 }
5854
5855 #define ENV_ACT_SETALWAYS   0x1
5856 #define ENV_ACT_SETABSENT   0x2
5857 #define ENV_ACT_REMOVE      0x4
5858 #define ENV_ACT_REMOVEMATCH 0x8
5859
5860 #define ENV_MOD_MACHINE     0x20000000
5861 #define ENV_MOD_APPEND      0x40000000
5862 #define ENV_MOD_PREFIX      0x80000000
5863 #define ENV_MOD_MASK        0xC0000000
5864
5865 #define check_flag_combo(x, y) ((x) & ~(y)) == (y)
5866
5867 static UINT env_parse_flags( LPCWSTR *name, LPCWSTR *value, DWORD *flags )
5868 {
5869     LPCWSTR cptr = *name;
5870
5871     static const WCHAR prefix[] = {'[','~',']',0};
5872     static const int prefix_len = 3;
5873
5874     *flags = 0;
5875     while (*cptr)
5876     {
5877         if (*cptr == '=')
5878             *flags |= ENV_ACT_SETALWAYS;
5879         else if (*cptr == '+')
5880             *flags |= ENV_ACT_SETABSENT;
5881         else if (*cptr == '-')
5882             *flags |= ENV_ACT_REMOVE;
5883         else if (*cptr == '!')
5884             *flags |= ENV_ACT_REMOVEMATCH;
5885         else if (*cptr == '*')
5886             *flags |= ENV_MOD_MACHINE;
5887         else
5888             break;
5889
5890         cptr++;
5891         (*name)++;
5892     }
5893
5894     if (!*cptr)
5895     {
5896         ERR("Missing environment variable\n");
5897         return ERROR_FUNCTION_FAILED;
5898     }
5899
5900     if (*value)
5901     {
5902         LPCWSTR ptr = *value;
5903         if (!strncmpW(ptr, prefix, prefix_len))
5904         {
5905             if (ptr[prefix_len] == szSemiColon[0])
5906             {
5907                 *flags |= ENV_MOD_APPEND;
5908                 *value += lstrlenW(prefix);
5909             }
5910             else
5911             {
5912                 *value = NULL;
5913             }
5914         }
5915         else if (lstrlenW(*value) >= prefix_len)
5916         {
5917             ptr += lstrlenW(ptr) - prefix_len;
5918             if (!lstrcmpW(ptr, prefix))
5919             {
5920                 if ((ptr-1) > *value && *(ptr-1) == szSemiColon[0])
5921                 {
5922                     *flags |= ENV_MOD_PREFIX;
5923                     /* the "[~]" will be removed by deformat_string */;
5924                 }
5925                 else
5926                 {
5927                     *value = NULL;
5928                 }
5929             }
5930         }
5931     }
5932
5933     if (check_flag_combo(*flags, ENV_ACT_SETALWAYS | ENV_ACT_SETABSENT) ||
5934         check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETABSENT) ||
5935         check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETALWAYS) ||
5936         check_flag_combo(*flags, ENV_ACT_SETABSENT | ENV_MOD_MASK))
5937     {
5938         ERR("Invalid flags: %08x\n", *flags);
5939         return ERROR_FUNCTION_FAILED;
5940     }
5941
5942     if (!*flags)
5943         *flags = ENV_ACT_SETALWAYS | ENV_ACT_REMOVE;
5944
5945     return ERROR_SUCCESS;
5946 }
5947
5948 static UINT open_env_key( DWORD flags, HKEY *key )
5949 {
5950     static const WCHAR user_env[] =
5951         {'E','n','v','i','r','o','n','m','e','n','t',0};
5952     static const WCHAR machine_env[] =
5953         {'S','y','s','t','e','m','\\',
5954          'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
5955          'C','o','n','t','r','o','l','\\',
5956          'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r','\\',
5957          'E','n','v','i','r','o','n','m','e','n','t',0};
5958     const WCHAR *env;
5959     HKEY root;
5960     LONG res;
5961
5962     if (flags & ENV_MOD_MACHINE)
5963     {
5964         env = machine_env;
5965         root = HKEY_LOCAL_MACHINE;
5966     }
5967     else
5968     {
5969         env = user_env;
5970         root = HKEY_CURRENT_USER;
5971     }
5972
5973     res = RegOpenKeyExW( root, env, 0, KEY_ALL_ACCESS, key );
5974     if (res != ERROR_SUCCESS)
5975     {
5976         WARN("Failed to open key %s (%d)\n", debugstr_w(env), res);
5977         return ERROR_FUNCTION_FAILED;
5978     }
5979
5980     return ERROR_SUCCESS;
5981 }
5982
5983 static UINT ITERATE_WriteEnvironmentString( MSIRECORD *rec, LPVOID param )
5984 {
5985     MSIPACKAGE *package = param;
5986     LPCWSTR name, value, component;
5987     LPWSTR data = NULL, newval = NULL, deformatted = NULL, ptr;
5988     DWORD flags, type, size;
5989     UINT res;
5990     HKEY env = NULL;
5991     MSICOMPONENT *comp;
5992     MSIRECORD *uirow;
5993     int action = 0;
5994
5995     component = MSI_RecordGetString(rec, 4);
5996     comp = get_loaded_component(package, component);
5997     if (!comp)
5998         return ERROR_SUCCESS;
5999
6000     if (comp->ActionRequest != INSTALLSTATE_LOCAL)
6001     {
6002         TRACE("Component not scheduled for installation: %s\n", debugstr_w(component));
6003         comp->Action = comp->Installed;
6004         return ERROR_SUCCESS;
6005     }
6006     comp->Action = INSTALLSTATE_LOCAL;
6007
6008     name = MSI_RecordGetString(rec, 2);
6009     value = MSI_RecordGetString(rec, 3);
6010
6011     TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
6012
6013     res = env_parse_flags(&name, &value, &flags);
6014     if (res != ERROR_SUCCESS || !value)
6015        goto done;
6016
6017     if (value && !deformat_string(package, value, &deformatted))
6018     {
6019         res = ERROR_OUTOFMEMORY;
6020         goto done;
6021     }
6022
6023     value = deformatted;
6024
6025     res = open_env_key( flags, &env );
6026     if (res != ERROR_SUCCESS)
6027         goto done;
6028
6029     if (flags & ENV_MOD_MACHINE)
6030         action |= 0x20000000;
6031
6032     size = 0;
6033     type = REG_SZ;
6034     res = RegQueryValueExW(env, name, NULL, &type, NULL, &size);
6035     if ((res != ERROR_SUCCESS && res != ERROR_FILE_NOT_FOUND) ||
6036         (res == ERROR_SUCCESS && type != REG_SZ && type != REG_EXPAND_SZ))
6037         goto done;
6038
6039     if ((res == ERROR_FILE_NOT_FOUND || !(flags & ENV_MOD_MASK)))
6040     {
6041         action = 0x2;
6042
6043         /* Nothing to do. */
6044         if (!value)
6045         {
6046             res = ERROR_SUCCESS;
6047             goto done;
6048         }
6049
6050         /* If we are appending but the string was empty, strip ; */
6051         if ((flags & ENV_MOD_APPEND) && (value[0] == szSemiColon[0])) value++;
6052
6053         size = (lstrlenW(value) + 1) * sizeof(WCHAR);
6054         newval = strdupW(value);
6055         if (!newval)
6056         {
6057             res = ERROR_OUTOFMEMORY;
6058             goto done;
6059         }
6060     }
6061     else
6062     {
6063         action = 0x1;
6064
6065         /* Contrary to MSDN, +-variable to [~];path works */
6066         if (flags & ENV_ACT_SETABSENT && !(flags & ENV_MOD_MASK))
6067         {
6068             res = ERROR_SUCCESS;
6069             goto done;
6070         }
6071
6072         data = msi_alloc(size);
6073         if (!data)
6074         {
6075             RegCloseKey(env);
6076             return ERROR_OUTOFMEMORY;
6077         }
6078
6079         res = RegQueryValueExW(env, name, NULL, &type, (LPVOID)data, &size);
6080         if (res != ERROR_SUCCESS)
6081             goto done;
6082
6083         if (flags & ENV_ACT_REMOVEMATCH && (!value || !lstrcmpW(data, value)))
6084         {
6085             action = 0x4;
6086             res = RegDeleteValueW(env, name);
6087             if (res != ERROR_SUCCESS)
6088                 WARN("Failed to remove value %s (%d)\n", debugstr_w(name), res);
6089             goto done;
6090         }
6091
6092         size = (lstrlenW(data) + 1) * sizeof(WCHAR);
6093         if (flags & ENV_MOD_MASK)
6094         {
6095             DWORD mod_size;
6096             int multiplier = 0;
6097             if (flags & ENV_MOD_APPEND) multiplier++;
6098             if (flags & ENV_MOD_PREFIX) multiplier++;
6099             mod_size = lstrlenW(value) * multiplier;
6100             size += mod_size * sizeof(WCHAR);
6101         }
6102
6103         newval = msi_alloc(size);
6104         ptr = newval;
6105         if (!newval)
6106         {
6107             res = ERROR_OUTOFMEMORY;
6108             goto done;
6109         }
6110
6111         if (flags & ENV_MOD_PREFIX)
6112         {
6113             lstrcpyW(newval, value);
6114             ptr = newval + lstrlenW(value);
6115             action |= 0x80000000;
6116         }
6117
6118         lstrcpyW(ptr, data);
6119
6120         if (flags & ENV_MOD_APPEND)
6121         {
6122             lstrcatW(newval, value);
6123             action |= 0x40000000;
6124         }
6125     }
6126     TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(newval));
6127     res = RegSetValueExW(env, name, 0, type, (LPVOID)newval, size);
6128     if (res)
6129     {
6130         WARN("Failed to set %s to %s (%d)\n",  debugstr_w(name), debugstr_w(newval), res);
6131     }
6132
6133 done:
6134     uirow = MSI_CreateRecord( 3 );
6135     MSI_RecordSetStringW( uirow, 1, name );
6136     MSI_RecordSetStringW( uirow, 2, newval );
6137     MSI_RecordSetInteger( uirow, 3, action );
6138     ui_actiondata( package, szWriteEnvironmentStrings, uirow );
6139     msiobj_release( &uirow->hdr );
6140
6141     if (env) RegCloseKey(env);
6142     msi_free(deformatted);
6143     msi_free(data);
6144     msi_free(newval);
6145     return res;
6146 }
6147
6148 static UINT ACTION_WriteEnvironmentStrings( MSIPACKAGE *package )
6149 {
6150     UINT rc;
6151     MSIQUERY * view;
6152     static const WCHAR ExecSeqQuery[] =
6153         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6154          '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
6155     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
6156     if (rc != ERROR_SUCCESS)
6157         return ERROR_SUCCESS;
6158
6159     rc = MSI_IterateRecords(view, NULL, ITERATE_WriteEnvironmentString, package);
6160     msiobj_release(&view->hdr);
6161
6162     return rc;
6163 }
6164
6165 static UINT ITERATE_RemoveEnvironmentString( MSIRECORD *rec, LPVOID param )
6166 {
6167     MSIPACKAGE *package = param;
6168     LPCWSTR name, value, component;
6169     LPWSTR deformatted = NULL;
6170     DWORD flags;
6171     HKEY env;
6172     MSICOMPONENT *comp;
6173     MSIRECORD *uirow;
6174     int action = 0;
6175     LONG res;
6176     UINT r;
6177
6178     component = MSI_RecordGetString( rec, 4 );
6179     comp = get_loaded_component( package, component );
6180     if (!comp)
6181         return ERROR_SUCCESS;
6182
6183     if (comp->ActionRequest != INSTALLSTATE_ABSENT)
6184     {
6185         TRACE("Component not scheduled for removal: %s\n", debugstr_w(component));
6186         comp->Action = comp->Installed;
6187         return ERROR_SUCCESS;
6188     }
6189     comp->Action = INSTALLSTATE_ABSENT;
6190
6191     name = MSI_RecordGetString( rec, 2 );
6192     value = MSI_RecordGetString( rec, 3 );
6193
6194     TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
6195
6196     r = env_parse_flags( &name, &value, &flags );
6197     if (r != ERROR_SUCCESS)
6198        return r;
6199
6200     if (!(flags & ENV_ACT_REMOVE))
6201     {
6202         TRACE("Environment variable %s not marked for removal\n", debugstr_w(name));
6203         return ERROR_SUCCESS;
6204     }
6205
6206     if (value && !deformat_string( package, value, &deformatted ))
6207         return ERROR_OUTOFMEMORY;
6208
6209     value = deformatted;
6210
6211     r = open_env_key( flags, &env );
6212     if (r != ERROR_SUCCESS)
6213     {
6214         r = ERROR_SUCCESS;
6215         goto done;
6216     }
6217
6218     if (flags & ENV_MOD_MACHINE)
6219         action |= 0x20000000;
6220
6221     TRACE("Removing %s\n", debugstr_w(name));
6222
6223     res = RegDeleteValueW( env, name );
6224     if (res != ERROR_SUCCESS)
6225     {
6226         WARN("Failed to delete value %s (%d)\n", debugstr_w(name), res);
6227         r = ERROR_SUCCESS;
6228     }
6229
6230 done:
6231     uirow = MSI_CreateRecord( 3 );
6232     MSI_RecordSetStringW( uirow, 1, name );
6233     MSI_RecordSetStringW( uirow, 2, value );
6234     MSI_RecordSetInteger( uirow, 3, action );
6235     ui_actiondata( package, szRemoveEnvironmentStrings, uirow );
6236     msiobj_release( &uirow->hdr );
6237
6238     if (env) RegCloseKey( env );
6239     msi_free( deformatted );
6240     return r;
6241 }
6242
6243 static UINT ACTION_RemoveEnvironmentStrings( MSIPACKAGE *package )
6244 {
6245     UINT rc;
6246     MSIQUERY *view;
6247     static const WCHAR query[] =
6248         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6249          '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
6250
6251     rc = MSI_DatabaseOpenViewW( package->db, query, &view );
6252     if (rc != ERROR_SUCCESS)
6253         return ERROR_SUCCESS;
6254
6255     rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveEnvironmentString, package );
6256     msiobj_release( &view->hdr );
6257
6258     return rc;
6259 }
6260
6261 typedef struct tagMSIASSEMBLY
6262 {
6263     struct list entry;
6264     MSICOMPONENT *component;
6265     MSIFEATURE *feature;
6266     MSIFILE *file;
6267     LPWSTR manifest;
6268     LPWSTR application;
6269     LPWSTR display_name;
6270     DWORD attributes;
6271     BOOL installed;
6272 } MSIASSEMBLY;
6273
6274 static HRESULT (WINAPI *pCreateAssemblyCache)(IAssemblyCache **ppAsmCache,
6275                                               DWORD dwReserved);
6276 static HRESULT (WINAPI *pLoadLibraryShim)(LPCWSTR szDllName, LPCWSTR szVersion,
6277                                           LPVOID pvReserved, HMODULE *phModDll);
6278
6279 static BOOL init_functionpointers(void)
6280 {
6281     HRESULT hr;
6282     HMODULE hfusion;
6283     HMODULE hmscoree;
6284
6285     static const WCHAR szFusion[] = {'f','u','s','i','o','n','.','d','l','l',0};
6286
6287     hmscoree = LoadLibraryA("mscoree.dll");
6288     if (!hmscoree)
6289     {
6290         WARN("mscoree.dll not available\n");
6291         return FALSE;
6292     }
6293
6294     pLoadLibraryShim = (void *)GetProcAddress(hmscoree, "LoadLibraryShim");
6295     if (!pLoadLibraryShim)
6296     {
6297         WARN("LoadLibraryShim not available\n");
6298         FreeLibrary(hmscoree);
6299         return FALSE;
6300     }
6301
6302     hr = pLoadLibraryShim(szFusion, NULL, NULL, &hfusion);
6303     if (FAILED(hr))
6304     {
6305         WARN("fusion.dll not available\n");
6306         FreeLibrary(hmscoree);
6307         return FALSE;
6308     }
6309
6310     pCreateAssemblyCache = (void *)GetProcAddress(hfusion, "CreateAssemblyCache");
6311
6312     FreeLibrary(hmscoree);
6313     return TRUE;
6314 }
6315
6316 static UINT install_assembly(MSIPACKAGE *package, MSIASSEMBLY *assembly,
6317                              LPWSTR path)
6318 {
6319     IAssemblyCache *cache;
6320     MSIRECORD *uirow;
6321     HRESULT hr;
6322     UINT r = ERROR_FUNCTION_FAILED;
6323
6324     TRACE("installing assembly: %s\n", debugstr_w(path));
6325
6326     uirow = MSI_CreateRecord( 2 );
6327     MSI_RecordSetStringW( uirow, 2, assembly->display_name );
6328     ui_actiondata( package, szMsiPublishAssemblies, uirow );
6329     msiobj_release( &uirow->hdr );
6330
6331     if (assembly->feature)
6332         msi_feature_set_state(package, assembly->feature, INSTALLSTATE_LOCAL);
6333
6334     if (assembly->manifest)
6335         FIXME("Manifest unhandled\n");
6336
6337     if (assembly->application)
6338     {
6339         FIXME("Assembly should be privately installed\n");
6340         return ERROR_SUCCESS;
6341     }
6342
6343     if (assembly->attributes == msidbAssemblyAttributesWin32)
6344     {
6345         FIXME("Win32 assemblies not handled\n");
6346         return ERROR_SUCCESS;
6347     }
6348
6349     hr = pCreateAssemblyCache(&cache, 0);
6350     if (FAILED(hr))
6351         goto done;
6352
6353     hr = IAssemblyCache_InstallAssembly(cache, 0, path, NULL);
6354     if (FAILED(hr))
6355         ERR("Failed to install assembly: %s %08x\n", debugstr_w(path), hr);
6356
6357     r = ERROR_SUCCESS;
6358
6359 done:
6360     IAssemblyCache_Release(cache);
6361     return r;
6362 }
6363
6364 typedef struct tagASSEMBLY_LIST
6365 {
6366     MSIPACKAGE *package;
6367     IAssemblyCache *cache;
6368     struct list *assemblies;
6369 } ASSEMBLY_LIST;
6370
6371 typedef struct tagASSEMBLY_NAME
6372 {
6373     LPWSTR name;
6374     LPWSTR version;
6375     LPWSTR culture;
6376     LPWSTR pubkeytoken;
6377 } ASSEMBLY_NAME;
6378
6379 static UINT parse_assembly_name(MSIRECORD *rec, LPVOID param)
6380 {
6381     ASSEMBLY_NAME *asmname = param;
6382     LPCWSTR name = MSI_RecordGetString(rec, 2);
6383     LPWSTR val = msi_dup_record_field(rec, 3);
6384
6385     static const WCHAR Name[] = {'N','a','m','e',0};
6386     static const WCHAR Version[] = {'V','e','r','s','i','o','n',0};
6387     static const WCHAR Culture[] = {'C','u','l','t','u','r','e',0};
6388     static const WCHAR PublicKeyToken[] = {
6389         'P','u','b','l','i','c','K','e','y','T','o','k','e','n',0};
6390
6391     if (!strcmpiW(name, Name))
6392         asmname->name = val;
6393     else if (!strcmpiW(name, Version))
6394         asmname->version = val;
6395     else if (!strcmpiW(name, Culture))
6396         asmname->culture = val;
6397     else if (!strcmpiW(name, PublicKeyToken))
6398         asmname->pubkeytoken = val;
6399     else
6400         msi_free(val);
6401
6402     return ERROR_SUCCESS;
6403 }
6404
6405 static void append_str(LPWSTR *str, DWORD *size, LPCWSTR append)
6406 {
6407     if (!*str)
6408     {
6409         *size = lstrlenW(append) + 1;
6410         *str = msi_alloc((*size) * sizeof(WCHAR));
6411         lstrcpyW(*str, append);
6412         return;
6413     }
6414
6415     (*size) += lstrlenW(append);
6416     *str = msi_realloc(*str, (*size) * sizeof(WCHAR));
6417     lstrcatW(*str, append);
6418 }
6419
6420 static WCHAR *get_assembly_display_name( MSIDATABASE *db, MSICOMPONENT *comp )
6421 {
6422     static const WCHAR separator[] = {',',' ',0};
6423     static const WCHAR Version[] = {'V','e','r','s','i','o','n','=',0};
6424     static const WCHAR Culture[] = {'C','u','l','t','u','r','e','=',0};
6425     static const WCHAR PublicKeyToken[] = {'P','u','b','l','i','c','K','e','y','T','o','k','e','n','=',0};
6426     static const WCHAR query[] = {
6427         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6428         '`','M','s','i','A','s','s','e','m','b','l','y','N','a','m','e','`',' ',
6429         'W','H','E','R','E',' ','`','C','o','m','p','o','n','e','n','t','_','`',
6430         '=','\'','%','s','\'',0};
6431     ASSEMBLY_NAME name;
6432     MSIQUERY *view;
6433     LPWSTR display_name;
6434     DWORD size;
6435     UINT r;
6436
6437     display_name = NULL;
6438     memset( &name, 0, sizeof(ASSEMBLY_NAME) );
6439
6440     r = MSI_OpenQuery( db, &view, query, comp->Component );
6441     if (r != ERROR_SUCCESS)
6442         return NULL;
6443
6444     MSI_IterateRecords( view, NULL, parse_assembly_name, &name );
6445     msiobj_release( &view->hdr );
6446
6447     if (!name.name)
6448     {
6449         ERR("No assembly name specified!\n");
6450         return NULL;
6451     }
6452
6453     append_str( &display_name, &size, name.name );
6454
6455     if (name.version)
6456     {
6457         append_str( &display_name, &size, separator );
6458         append_str( &display_name, &size, Version );
6459         append_str( &display_name, &size, name.version );
6460     }
6461     if (name.culture)
6462     {
6463         append_str( &display_name, &size, separator );
6464         append_str( &display_name, &size, Culture );
6465         append_str( &display_name, &size, name.culture );
6466     }
6467     if (name.pubkeytoken)
6468     {
6469         append_str( &display_name, &size, separator );
6470         append_str( &display_name, &size, PublicKeyToken );
6471         append_str( &display_name, &size, name.pubkeytoken );
6472     }
6473
6474     msi_free( name.name );
6475     msi_free( name.version );
6476     msi_free( name.culture );
6477     msi_free( name.pubkeytoken );
6478
6479     return display_name;
6480 }
6481
6482 static BOOL check_assembly_installed( MSIDATABASE *db, IAssemblyCache *cache, MSICOMPONENT *comp )
6483 {
6484     ASSEMBLY_INFO asminfo;
6485     LPWSTR disp;
6486     BOOL found = FALSE;
6487     HRESULT hr;
6488
6489     disp = get_assembly_display_name( db, comp );
6490     if (!disp)
6491         return FALSE;
6492
6493     memset( &asminfo, 0, sizeof(ASSEMBLY_INFO) );
6494     asminfo.cbAssemblyInfo = sizeof(ASSEMBLY_INFO);
6495
6496     hr = IAssemblyCache_QueryAssemblyInfo( cache, QUERYASMINFO_FLAG_VALIDATE, disp, &asminfo );
6497     if (SUCCEEDED(hr))
6498         found = (asminfo.dwAssemblyFlags == ASSEMBLYINFO_FLAG_INSTALLED);
6499
6500     msi_free( disp );
6501     return found;
6502 }
6503
6504 static UINT load_assembly(MSIRECORD *rec, LPVOID param)
6505 {
6506     ASSEMBLY_LIST *list = param;
6507     MSIASSEMBLY *assembly;
6508     LPCWSTR component;
6509
6510     assembly = msi_alloc_zero(sizeof(MSIASSEMBLY));
6511     if (!assembly)
6512         return ERROR_OUTOFMEMORY;
6513
6514     component = MSI_RecordGetString(rec, 1);
6515     assembly->component = get_loaded_component(list->package, component);
6516     if (!assembly->component)
6517         return ERROR_SUCCESS;
6518
6519     if (assembly->component->ActionRequest != INSTALLSTATE_LOCAL &&
6520         assembly->component->ActionRequest != INSTALLSTATE_SOURCE)
6521     {
6522         TRACE("Component not scheduled for installation: %s\n", debugstr_w(component));
6523         assembly->component->Action = assembly->component->Installed;
6524         return ERROR_SUCCESS;
6525     }
6526     assembly->component->Action = assembly->component->ActionRequest;
6527
6528     assembly->feature = find_feature_by_name(list->package, MSI_RecordGetString(rec, 2));
6529     assembly->file = msi_find_file(list->package, assembly->component->KeyPath);
6530
6531     if (!assembly->file)
6532     {
6533         ERR("File %s not found\n", debugstr_w(assembly->component->KeyPath));
6534         return ERROR_FUNCTION_FAILED;
6535     }
6536
6537     assembly->manifest = strdupW(MSI_RecordGetString(rec, 3));
6538     assembly->application = strdupW(MSI_RecordGetString(rec, 4));
6539     assembly->attributes = MSI_RecordGetInteger(rec, 5);
6540
6541     if (assembly->application)
6542     {
6543         WCHAR version[24];
6544         DWORD size = sizeof(version)/sizeof(WCHAR);
6545
6546         /* FIXME: we should probably check the manifest file here */
6547
6548         if (!MsiGetFileVersionW(assembly->file->TargetPath, version, &size, NULL, NULL) &&
6549             (!assembly->file->Version || strcmpW(version, assembly->file->Version) >= 0))
6550         {
6551             assembly->installed = TRUE;
6552         }
6553     }
6554     else
6555         assembly->installed = check_assembly_installed(list->package->db,
6556                                                        list->cache,
6557                                                        assembly->component);
6558
6559     list_add_head(list->assemblies, &assembly->entry);
6560     return ERROR_SUCCESS;
6561 }
6562
6563 static UINT load_assemblies(MSIPACKAGE *package, struct list *assemblies)
6564 {
6565     IAssemblyCache *cache = NULL;
6566     ASSEMBLY_LIST list;
6567     MSIQUERY *view;
6568     HRESULT hr;
6569     UINT r;
6570
6571     static const WCHAR query[] =
6572         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6573          '`','M','s','i','A','s','s','e','m','b','l','y','`',0};
6574
6575     r = MSI_DatabaseOpenViewW(package->db, query, &view);
6576     if (r != ERROR_SUCCESS)
6577         return ERROR_SUCCESS;
6578
6579     hr = pCreateAssemblyCache(&cache, 0);
6580     if (FAILED(hr))
6581         return ERROR_FUNCTION_FAILED;
6582
6583     list.package = package;
6584     list.cache = cache;
6585     list.assemblies = assemblies;
6586
6587     r = MSI_IterateRecords(view, NULL, load_assembly, &list);
6588     msiobj_release(&view->hdr);
6589
6590     IAssemblyCache_Release(cache);
6591
6592     return r;
6593 }
6594
6595 static void free_assemblies(struct list *assemblies)
6596 {
6597     struct list *item, *cursor;
6598
6599     LIST_FOR_EACH_SAFE(item, cursor, assemblies)
6600     {
6601         MSIASSEMBLY *assembly = LIST_ENTRY(item, MSIASSEMBLY, entry);
6602
6603         list_remove(&assembly->entry);
6604         msi_free(assembly->application);
6605         msi_free(assembly->manifest);
6606         msi_free(assembly->display_name);
6607         msi_free(assembly);
6608     }
6609 }
6610
6611 static BOOL find_assembly(struct list *assemblies, LPCWSTR file, MSIASSEMBLY **out)
6612 {
6613     MSIASSEMBLY *assembly;
6614
6615     LIST_FOR_EACH_ENTRY(assembly, assemblies, MSIASSEMBLY, entry)
6616     {
6617         if (!lstrcmpW(assembly->file->File, file))
6618         {
6619             *out = assembly;
6620             return TRUE;
6621         }
6622     }
6623
6624     return FALSE;
6625 }
6626
6627 static BOOL installassembly_cb(MSIPACKAGE *package, LPCWSTR file, DWORD action,
6628                                LPWSTR *path, DWORD *attrs, PVOID user)
6629 {
6630     MSIASSEMBLY *assembly;
6631     WCHAR temppath[MAX_PATH];
6632     struct list *assemblies = user;
6633     UINT r;
6634
6635     if (!find_assembly(assemblies, file, &assembly))
6636         return FALSE;
6637
6638     GetTempPathW(MAX_PATH, temppath);
6639     PathAddBackslashW(temppath);
6640     lstrcatW(temppath, assembly->file->FileName);
6641
6642     if (action == MSICABEXTRACT_BEGINEXTRACT)
6643     {
6644         if (assembly->installed)
6645             return FALSE;
6646
6647         *path = strdupW(temppath);
6648         *attrs = assembly->file->Attributes;
6649     }
6650     else if (action == MSICABEXTRACT_FILEEXTRACTED)
6651     {
6652         assembly->installed = TRUE;
6653
6654         r = install_assembly(package, assembly, temppath);
6655         if (r != ERROR_SUCCESS)
6656             ERR("Failed to install assembly\n");
6657     }
6658
6659     return TRUE;
6660 }
6661
6662 static UINT ACTION_MsiPublishAssemblies( MSIPACKAGE *package )
6663 {
6664     UINT r;
6665     struct list assemblies = LIST_INIT(assemblies);
6666     MSIASSEMBLY *assembly;
6667     MSIMEDIAINFO *mi;
6668
6669     if (!init_functionpointers() || !pCreateAssemblyCache)
6670         return ERROR_FUNCTION_FAILED;
6671
6672     r = load_assemblies(package, &assemblies);
6673     if (r != ERROR_SUCCESS)
6674         goto done;
6675
6676     if (list_empty(&assemblies))
6677         goto done;
6678
6679     mi = msi_alloc_zero(sizeof(MSIMEDIAINFO));
6680     if (!mi)
6681     {
6682         r = ERROR_OUTOFMEMORY;
6683         goto done;
6684     }
6685
6686     LIST_FOR_EACH_ENTRY(assembly, &assemblies, MSIASSEMBLY, entry)
6687     {
6688         if (assembly->installed && !mi->is_continuous)
6689             continue;
6690
6691         if (assembly->file->Sequence > mi->last_sequence || mi->is_continuous ||
6692             (assembly->file->IsCompressed && !mi->is_extracted))
6693         {
6694             MSICABDATA data;
6695
6696             r = ready_media(package, assembly->file, mi);
6697             if (r != ERROR_SUCCESS)
6698             {
6699                 ERR("Failed to ready media\n");
6700                 break;
6701             }
6702
6703             data.mi = mi;
6704             data.package = package;
6705             data.cb = installassembly_cb;
6706             data.user = &assemblies;
6707
6708             if (assembly->file->IsCompressed &&
6709                 !msi_cabextract(package, mi, &data))
6710             {
6711                 ERR("Failed to extract cabinet: %s\n", debugstr_w(mi->cabinet));
6712                 r = ERROR_FUNCTION_FAILED;
6713                 break;
6714             }
6715         }
6716
6717         if (!assembly->file->IsCompressed)
6718         {
6719             LPWSTR source = resolve_file_source(package, assembly->file);
6720
6721             r = install_assembly(package, assembly, source);
6722             if (r != ERROR_SUCCESS)
6723                 ERR("Failed to install assembly\n");
6724
6725             msi_free(source);
6726         }
6727
6728         /* FIXME: write Installer assembly reg values */
6729     }
6730
6731 done:
6732     free_assemblies(&assemblies);
6733     return r;
6734 }
6735
6736 static UINT ACTION_ValidateProductID( MSIPACKAGE *package )
6737 {
6738     LPWSTR key, template, id;
6739     UINT r = ERROR_SUCCESS;
6740
6741     id = msi_dup_property( package, szProductID );
6742     if (id)
6743     {
6744         msi_free( id );
6745         return ERROR_SUCCESS;
6746     }
6747     template = msi_dup_property( package, szPIDTemplate );
6748     key = msi_dup_property( package, szPIDKEY );
6749
6750     if (key && template)
6751     {
6752         FIXME( "partial stub: template %s key %s\n", debugstr_w(template), debugstr_w(key) );
6753         r = MSI_SetPropertyW( package, szProductID, key );
6754     }
6755     msi_free( template );
6756     msi_free( key );
6757     return r;
6758 }
6759
6760 static UINT ACTION_ScheduleReboot( MSIPACKAGE *package )
6761 {
6762     TRACE("\n");
6763     package->need_reboot = 1;
6764     return ERROR_SUCCESS;
6765 }
6766
6767 static UINT ACTION_AllocateRegistrySpace( MSIPACKAGE *package )
6768 {
6769     static const WCHAR szAvailableFreeReg[] =
6770         {'A','V','A','I','L','A','B','L','E','F','R','E','E','R','E','G',0};
6771     MSIRECORD *uirow;
6772     int space = msi_get_property_int( package, szAvailableFreeReg, 0 );
6773
6774     TRACE("%p %d kilobytes\n", package, space);
6775
6776     uirow = MSI_CreateRecord( 1 );
6777     MSI_RecordSetInteger( uirow, 1, space );
6778     ui_actiondata( package, szAllocateRegistrySpace, uirow );
6779     msiobj_release( &uirow->hdr );
6780
6781     return ERROR_SUCCESS;
6782 }
6783
6784 static UINT ACTION_DisableRollback( MSIPACKAGE *package )
6785 {
6786     FIXME("%p\n", package);
6787     return ERROR_SUCCESS;
6788 }
6789
6790 static UINT ACTION_InstallAdminPackage( MSIPACKAGE *package )
6791 {
6792     FIXME("%p\n", package);
6793     return ERROR_SUCCESS;
6794 }
6795
6796 static UINT msi_unimplemented_action_stub( MSIPACKAGE *package,
6797                                            LPCSTR action, LPCWSTR table )
6798 {
6799     static const WCHAR query[] = {
6800         'S','E','L','E','C','T',' ','*',' ',
6801         'F','R','O','M',' ','`','%','s','`',0 };
6802     MSIQUERY *view = NULL;
6803     DWORD count = 0;
6804     UINT r;
6805     
6806     r = MSI_OpenQuery( package->db, &view, query, table );
6807     if (r == ERROR_SUCCESS)
6808     {
6809         r = MSI_IterateRecords(view, &count, NULL, package);
6810         msiobj_release(&view->hdr);
6811     }
6812
6813     if (count)
6814         FIXME("%s -> %u ignored %s table values\n",
6815               action, count, debugstr_w(table));
6816
6817     return ERROR_SUCCESS;
6818 }
6819
6820 static UINT ACTION_PatchFiles( MSIPACKAGE *package )
6821 {
6822     static const WCHAR table[] = { 'P','a','t','c','h',0 };
6823     return msi_unimplemented_action_stub( package, "PatchFiles", table );
6824 }
6825
6826 static UINT ACTION_BindImage( MSIPACKAGE *package )
6827 {
6828     static const WCHAR table[] = { 'B','i','n','d','I','m','a','g','e',0 };
6829     return msi_unimplemented_action_stub( package, "BindImage", table );
6830 }
6831
6832 static UINT ACTION_IsolateComponents( MSIPACKAGE *package )
6833 {
6834     static const WCHAR table[] = {
6835         'I','s','o','l','a','t','e','d','C','o','m','p','o','n','e','n','t',0 };
6836     return msi_unimplemented_action_stub( package, "IsolateComponents", table );
6837 }
6838
6839 static UINT ACTION_MigrateFeatureStates( MSIPACKAGE *package )
6840 {
6841     static const WCHAR table[] = { 'U','p','g','r','a','d','e',0 };
6842     return msi_unimplemented_action_stub( package, "MigrateFeatureStates", table );
6843 }
6844
6845 static UINT ACTION_MsiUnpublishAssemblies( MSIPACKAGE *package )
6846 {
6847     static const WCHAR table[] = {
6848         'M','s','i','A','s','s','e','m','b','l','y',0 };
6849     return msi_unimplemented_action_stub( package, "MsiUnpublishAssemblies", table );
6850 }
6851
6852 static UINT ACTION_RMCCPSearch( MSIPACKAGE *package )
6853 {
6854     static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
6855     return msi_unimplemented_action_stub( package, "RMCCPSearch", table );
6856 }
6857
6858 static UINT ACTION_RegisterComPlus( MSIPACKAGE *package )
6859 {
6860     static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
6861     return msi_unimplemented_action_stub( package, "RegisterComPlus", table );
6862 }
6863
6864 static UINT ACTION_UnregisterComPlus( MSIPACKAGE *package )
6865 {
6866     static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
6867     return msi_unimplemented_action_stub( package, "UnregisterComPlus", table );
6868 }
6869
6870 static UINT ACTION_InstallSFPCatalogFile( MSIPACKAGE *package )
6871 {
6872     static const WCHAR table[] = { 'S','F','P','C','a','t','a','l','o','g',0 };
6873     return msi_unimplemented_action_stub( package, "InstallSFPCatalogFile", table );
6874 }
6875
6876 static UINT ACTION_RemoveExistingProducts( MSIPACKAGE *package )
6877 {
6878     static const WCHAR table[] = { 'U','p','g','r','a','d','e',0 };
6879     return msi_unimplemented_action_stub( package, "RemoveExistingProducts", table );
6880 }
6881
6882 static UINT ACTION_SetODBCFolders( MSIPACKAGE *package )
6883 {
6884     static const WCHAR table[] = { 'D','i','r','e','c','t','o','r','y',0 };
6885     return msi_unimplemented_action_stub( package, "SetODBCFolders", table );
6886 }
6887
6888 static UINT ACTION_UnregisterClassInfo( MSIPACKAGE *package )
6889 {
6890     static const WCHAR table[] = { 'A','p','p','I','d',0 };
6891     return msi_unimplemented_action_stub( package, "UnregisterClassInfo", table );
6892 }
6893
6894 static UINT ACTION_UnregisterExtensionInfo( MSIPACKAGE *package )
6895 {
6896     static const WCHAR table[] = { 'E','x','t','e','n','s','i','o','n',0 };
6897     return msi_unimplemented_action_stub( package, "UnregisterExtensionInfo", table );
6898 }
6899
6900 static UINT ACTION_UnregisterMIMEInfo( MSIPACKAGE *package )
6901 {
6902     static const WCHAR table[] = { 'M','I','M','E',0 };
6903     return msi_unimplemented_action_stub( package, "UnregisterMIMEInfo", table );
6904 }
6905
6906 static UINT ACTION_UnregisterProgIdInfo( MSIPACKAGE *package )
6907 {
6908     static const WCHAR table[] = { 'P','r','o','g','I','d',0 };
6909     return msi_unimplemented_action_stub( package, "UnregisterProgIdInfo", table );
6910 }
6911
6912 typedef UINT (*STANDARDACTIONHANDLER)(MSIPACKAGE*);
6913
6914 static const struct
6915 {
6916     const WCHAR *action;
6917     UINT (*handler)(MSIPACKAGE *);
6918 }
6919 StandardActions[] =
6920 {
6921     { szAllocateRegistrySpace, ACTION_AllocateRegistrySpace },
6922     { szAppSearch, ACTION_AppSearch },
6923     { szBindImage, ACTION_BindImage },
6924     { szCCPSearch, ACTION_CCPSearch },
6925     { szCostFinalize, ACTION_CostFinalize },
6926     { szCostInitialize, ACTION_CostInitialize },
6927     { szCreateFolders, ACTION_CreateFolders },
6928     { szCreateShortcuts, ACTION_CreateShortcuts },
6929     { szDeleteServices, ACTION_DeleteServices },
6930     { szDisableRollback, ACTION_DisableRollback },
6931     { szDuplicateFiles, ACTION_DuplicateFiles },
6932     { szExecuteAction, ACTION_ExecuteAction },
6933     { szFileCost, ACTION_FileCost },
6934     { szFindRelatedProducts, ACTION_FindRelatedProducts },
6935     { szForceReboot, ACTION_ForceReboot },
6936     { szInstallAdminPackage, ACTION_InstallAdminPackage },
6937     { szInstallExecute, ACTION_InstallExecute },
6938     { szInstallExecuteAgain, ACTION_InstallExecute },
6939     { szInstallFiles, ACTION_InstallFiles},
6940     { szInstallFinalize, ACTION_InstallFinalize },
6941     { szInstallInitialize, ACTION_InstallInitialize },
6942     { szInstallSFPCatalogFile, ACTION_InstallSFPCatalogFile },
6943     { szInstallValidate, ACTION_InstallValidate },
6944     { szIsolateComponents, ACTION_IsolateComponents },
6945     { szLaunchConditions, ACTION_LaunchConditions },
6946     { szMigrateFeatureStates, ACTION_MigrateFeatureStates },
6947     { szMoveFiles, ACTION_MoveFiles },
6948     { szMsiPublishAssemblies, ACTION_MsiPublishAssemblies },
6949     { szMsiUnpublishAssemblies, ACTION_MsiUnpublishAssemblies },
6950     { szInstallODBC, ACTION_InstallODBC },
6951     { szInstallServices, ACTION_InstallServices },
6952     { szPatchFiles, ACTION_PatchFiles },
6953     { szProcessComponents, ACTION_ProcessComponents },
6954     { szPublishComponents, ACTION_PublishComponents },
6955     { szPublishFeatures, ACTION_PublishFeatures },
6956     { szPublishProduct, ACTION_PublishProduct },
6957     { szRegisterClassInfo, ACTION_RegisterClassInfo },
6958     { szRegisterComPlus, ACTION_RegisterComPlus},
6959     { szRegisterExtensionInfo, ACTION_RegisterExtensionInfo },
6960     { szRegisterFonts, ACTION_RegisterFonts },
6961     { szRegisterMIMEInfo, ACTION_RegisterMIMEInfo },
6962     { szRegisterProduct, ACTION_RegisterProduct },
6963     { szRegisterProgIdInfo, ACTION_RegisterProgIdInfo },
6964     { szRegisterTypeLibraries, ACTION_RegisterTypeLibraries },
6965     { szRegisterUser, ACTION_RegisterUser },
6966     { szRemoveDuplicateFiles, ACTION_RemoveDuplicateFiles },
6967     { szRemoveEnvironmentStrings, ACTION_RemoveEnvironmentStrings },
6968     { szRemoveExistingProducts, ACTION_RemoveExistingProducts },
6969     { szRemoveFiles, ACTION_RemoveFiles },
6970     { szRemoveFolders, ACTION_RemoveFolders },
6971     { szRemoveIniValues, ACTION_RemoveIniValues },
6972     { szRemoveODBC, ACTION_RemoveODBC },
6973     { szRemoveRegistryValues, ACTION_RemoveRegistryValues },
6974     { szRemoveShortcuts, ACTION_RemoveShortcuts },
6975     { szResolveSource, ACTION_ResolveSource },
6976     { szRMCCPSearch, ACTION_RMCCPSearch },
6977     { szScheduleReboot, ACTION_ScheduleReboot },
6978     { szSelfRegModules, ACTION_SelfRegModules },
6979     { szSelfUnregModules, ACTION_SelfUnregModules },
6980     { szSetODBCFolders, ACTION_SetODBCFolders },
6981     { szStartServices, ACTION_StartServices },
6982     { szStopServices, ACTION_StopServices },
6983     { szUnpublishComponents, ACTION_UnpublishComponents },
6984     { szUnpublishFeatures, ACTION_UnpublishFeatures },
6985     { szUnregisterClassInfo, ACTION_UnregisterClassInfo },
6986     { szUnregisterComPlus, ACTION_UnregisterComPlus },
6987     { szUnregisterExtensionInfo, ACTION_UnregisterExtensionInfo },
6988     { szUnregisterFonts, ACTION_UnregisterFonts },
6989     { szUnregisterMIMEInfo, ACTION_UnregisterMIMEInfo },
6990     { szUnregisterProgIdInfo, ACTION_UnregisterProgIdInfo },
6991     { szUnregisterTypeLibraries, ACTION_UnregisterTypeLibraries },
6992     { szValidateProductID, ACTION_ValidateProductID },
6993     { szWriteEnvironmentStrings, ACTION_WriteEnvironmentStrings },
6994     { szWriteIniValues, ACTION_WriteIniValues },
6995     { szWriteRegistryValues, ACTION_WriteRegistryValues },
6996     { NULL, NULL },
6997 };
6998
6999 static BOOL ACTION_HandleStandardAction(MSIPACKAGE *package, LPCWSTR action,
7000                                         UINT* rc, BOOL force )
7001 {
7002     BOOL ret = FALSE;
7003     BOOL run = force;
7004     int i;
7005
7006     if (!run && !package->script->CurrentlyScripting)
7007         run = TRUE;
7008
7009     if (!run)
7010     {
7011         if (strcmpW(action,szInstallFinalize) == 0 ||
7012             strcmpW(action,szInstallExecute) == 0 ||
7013             strcmpW(action,szInstallExecuteAgain) == 0)
7014                 run = TRUE;
7015     }
7016
7017     i = 0;
7018     while (StandardActions[i].action != NULL)
7019     {
7020         if (strcmpW(StandardActions[i].action, action)==0)
7021         {
7022             if (!run)
7023             {
7024                 ui_actioninfo(package, action, TRUE, 0);
7025                 *rc = schedule_action(package,INSTALL_SCRIPT,action);
7026                 ui_actioninfo(package, action, FALSE, *rc);
7027             }
7028             else
7029             {
7030                 ui_actionstart(package, action);
7031                 if (StandardActions[i].handler)
7032                 {
7033                     *rc = StandardActions[i].handler(package);
7034                 }
7035                 else
7036                 {
7037                     FIXME("unhandled standard action %s\n",debugstr_w(action));
7038                     *rc = ERROR_SUCCESS;
7039                 }
7040             }
7041             ret = TRUE;
7042             break;
7043         }
7044         i++;
7045     }
7046     return ret;
7047 }
7048
7049 UINT ACTION_PerformAction(MSIPACKAGE *package, const WCHAR *action, UINT script, BOOL force)
7050 {
7051     UINT rc = ERROR_SUCCESS;
7052     BOOL handled;
7053
7054     TRACE("Performing action (%s)\n", debugstr_w(action));
7055
7056     handled = ACTION_HandleStandardAction(package, action, &rc, force);
7057
7058     if (!handled)
7059         handled = ACTION_HandleCustomAction(package, action, &rc, script, force);
7060
7061     if (!handled)
7062     {
7063         WARN("unhandled msi action %s\n", debugstr_w(action));
7064         rc = ERROR_FUNCTION_NOT_CALLED;
7065     }
7066
7067     return rc;
7068 }
7069
7070 UINT ACTION_PerformUIAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
7071 {
7072     UINT rc = ERROR_SUCCESS;
7073     BOOL handled = FALSE;
7074
7075     TRACE("Performing action (%s)\n", debugstr_w(action));
7076
7077     handled = ACTION_HandleStandardAction(package, action, &rc,TRUE);
7078
7079     if (!handled)
7080         handled = ACTION_HandleCustomAction(package, action, &rc, script, FALSE);
7081
7082     if( !handled && ACTION_DialogBox(package, action) == ERROR_SUCCESS )
7083         handled = TRUE;
7084
7085     if (!handled)
7086     {
7087         WARN("unhandled msi action %s\n", debugstr_w(action));
7088         rc = ERROR_FUNCTION_NOT_CALLED;
7089     }
7090
7091     return rc;
7092 }
7093
7094 static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq)
7095 {
7096     UINT rc = ERROR_SUCCESS;
7097     MSIRECORD *row;
7098
7099     static const WCHAR ExecSeqQuery[] =
7100         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7101          '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
7102          'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
7103          '`','S','e','q','u','e','n','c','e','`',' ', '=',' ','%','i',0};
7104     static const WCHAR UISeqQuery[] =
7105         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7106      '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e',
7107      '`', ' ', 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',
7108          ' ', '=',' ','%','i',0};
7109
7110     if (needs_ui_sequence(package))
7111         row = MSI_QueryGetRecord(package->db, UISeqQuery, seq);
7112     else
7113         row = MSI_QueryGetRecord(package->db, ExecSeqQuery, seq);
7114
7115     if (row)
7116     {
7117         LPCWSTR action, cond;
7118
7119         TRACE("Running the actions\n");
7120
7121         /* check conditions */
7122         cond = MSI_RecordGetString(row, 2);
7123
7124         /* this is a hack to skip errors in the condition code */
7125         if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
7126         {
7127             msiobj_release(&row->hdr);
7128             return ERROR_SUCCESS;
7129         }
7130
7131         action = MSI_RecordGetString(row, 1);
7132         if (!action)
7133         {
7134             ERR("failed to fetch action\n");
7135             msiobj_release(&row->hdr);
7136             return ERROR_FUNCTION_FAILED;
7137         }
7138
7139         if (needs_ui_sequence(package))
7140             rc = ACTION_PerformUIAction(package, action, -1);
7141         else
7142             rc = ACTION_PerformAction(package, action, -1, FALSE);
7143
7144         msiobj_release(&row->hdr);
7145     }
7146
7147     return rc;
7148 }
7149
7150 /****************************************************
7151  * TOP level entry points
7152  *****************************************************/
7153
7154 UINT MSI_InstallPackage( MSIPACKAGE *package, LPCWSTR szPackagePath,
7155                          LPCWSTR szCommandLine )
7156 {
7157     UINT rc;
7158     BOOL ui_exists;
7159
7160     static const WCHAR szAction[] = {'A','C','T','I','O','N',0};
7161     static const WCHAR szInstall[] = {'I','N','S','T','A','L','L',0};
7162
7163     MSI_SetPropertyW(package, szAction, szInstall);
7164
7165     package->script->InWhatSequence = SEQUENCE_INSTALL;
7166
7167     if (szPackagePath)
7168     {
7169         LPWSTR p, dir;
7170         LPCWSTR file;
7171
7172         dir = strdupW(szPackagePath);
7173         p = strrchrW(dir, '\\');
7174         if (p)
7175         {
7176             *(++p) = 0;
7177             file = szPackagePath + (p - dir);
7178         }
7179         else
7180         {
7181             msi_free(dir);
7182             dir = msi_alloc(MAX_PATH * sizeof(WCHAR));
7183             GetCurrentDirectoryW(MAX_PATH, dir);
7184             lstrcatW(dir, szBackSlash);
7185             file = szPackagePath;
7186         }
7187
7188         msi_free( package->PackagePath );
7189         package->PackagePath = msi_alloc((lstrlenW(dir) + lstrlenW(file) + 1) * sizeof(WCHAR));
7190         if (!package->PackagePath)
7191         {
7192             msi_free(dir);
7193             return ERROR_OUTOFMEMORY;
7194         }
7195
7196         lstrcpyW(package->PackagePath, dir);
7197         lstrcatW(package->PackagePath, file);
7198         msi_free(dir);
7199
7200         msi_set_sourcedir_props(package, FALSE);
7201     }
7202
7203     msi_parse_command_line( package, szCommandLine, FALSE );
7204
7205     msi_apply_transforms( package );
7206     msi_apply_patches( package );
7207
7208     if (!szCommandLine && msi_get_property_int( package, szInstalled, 0 ))
7209     {
7210         TRACE("setting reinstall property\n");
7211         MSI_SetPropertyW( package, szReinstall, szAll );
7212     }
7213
7214     /* properties may have been added by a transform */
7215     msi_clone_properties( package );
7216     msi_set_context( package );
7217
7218     if (needs_ui_sequence( package))
7219     {
7220         package->script->InWhatSequence |= SEQUENCE_UI;
7221         rc = ACTION_ProcessUISequence(package);
7222         ui_exists = ui_sequence_exists(package);
7223         if (rc == ERROR_SUCCESS || !ui_exists)
7224         {
7225             package->script->InWhatSequence |= SEQUENCE_EXEC;
7226             rc = ACTION_ProcessExecSequence(package, ui_exists);
7227         }
7228     }
7229     else
7230         rc = ACTION_ProcessExecSequence(package, FALSE);
7231
7232     package->script->CurrentlyScripting = FALSE;
7233
7234     /* process the ending type action */
7235     if (rc == ERROR_SUCCESS)
7236         ACTION_PerformActionSequence(package, -1);
7237     else if (rc == ERROR_INSTALL_USEREXIT)
7238         ACTION_PerformActionSequence(package, -2);
7239     else if (rc == ERROR_INSTALL_SUSPEND)
7240         ACTION_PerformActionSequence(package, -4);
7241     else  /* failed */
7242         ACTION_PerformActionSequence(package, -3);
7243
7244     /* finish up running custom actions */
7245     ACTION_FinishCustomActions(package);
7246
7247     if (rc == ERROR_SUCCESS && package->need_reboot)
7248         return ERROR_SUCCESS_REBOOT_REQUIRED;
7249
7250     return rc;
7251 }