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