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