user32: Fixed some minor glitches in oic_winlogo.ico.
[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     LPWSTR prop, val, key;
4541     SYSTEMTIME systime;
4542     DWORD size, langid;
4543     WCHAR date[9];
4544     LPWSTR buffer;
4545
4546     static const WCHAR date_fmt[] = {'%','i','%','0','2','i','%','0','2','i',0};
4547     static const WCHAR szWindowsInstaller[] =
4548         {'W','i','n','d','o','w','s','I','n','s','t','a','l','l','e','r',0};
4549     static const WCHAR modpath_fmt[] =
4550         {'M','s','i','E','x','e','c','.','e','x','e',' ',
4551          '/','I','[','P','r','o','d','u','c','t','C','o','d','e',']',0};
4552     static const WCHAR szModifyPath[] =
4553         {'M','o','d','i','f','y','P','a','t','h',0};
4554     static const WCHAR szUninstallString[] =
4555         {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0};
4556     static const WCHAR szEstimatedSize[] =
4557         {'E','s','t','i','m','a','t','e','d','S','i','z','e',0};
4558     static const WCHAR szProductLanguage[] =
4559         {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
4560     static const WCHAR szProductVersion[] =
4561         {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
4562     static const WCHAR szProductName[] =
4563         {'P','r','o','d','u','c','t','N','a','m','e',0};
4564     static const WCHAR szDisplayName[] =
4565         {'D','i','s','p','l','a','y','N','a','m','e',0};
4566     static const WCHAR szDisplayVersion[] =
4567         {'D','i','s','p','l','a','y','V','e','r','s','i','o','n',0};
4568     static const WCHAR szManufacturer[] =
4569         {'M','a','n','u','f','a','c','t','u','r','e','r',0};
4570
4571     static const LPCSTR propval[] = {
4572         "ARPAUTHORIZEDCDFPREFIX", "AuthorizedCDFPrefix",
4573         "ARPCONTACT",             "Contact",
4574         "ARPCOMMENTS",            "Comments",
4575         "ProductName",            "DisplayName",
4576         "ProductVersion",         "DisplayVersion",
4577         "ARPHELPLINK",            "HelpLink",
4578         "ARPHELPTELEPHONE",       "HelpTelephone",
4579         "ARPINSTALLLOCATION",     "InstallLocation",
4580         "SourceDir",              "InstallSource",
4581         "Manufacturer",           "Publisher",
4582         "ARPREADME",              "Readme",
4583         "ARPSIZE",                "Size",
4584         "ARPURLINFOABOUT",        "URLInfoAbout",
4585         "ARPURLUPDATEINFO",       "URLUpdateInfo",
4586         NULL,
4587     };
4588     const LPCSTR *p = propval;
4589
4590     while (*p)
4591     {
4592         prop = strdupAtoW(*p++);
4593         key = strdupAtoW(*p++);
4594         val = msi_dup_property(package->db, prop);
4595         msi_reg_set_val_str(hkey, key, val);
4596         msi_free(val);
4597         msi_free(key);
4598         msi_free(prop);
4599     }
4600
4601     msi_reg_set_val_dword(hkey, szWindowsInstaller, 1);
4602
4603     size = deformat_string(package, modpath_fmt, &buffer);
4604     RegSetValueExW(hkey, szModifyPath, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
4605     RegSetValueExW(hkey, szUninstallString, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
4606     msi_free(buffer);
4607
4608     /* FIXME: Write real Estimated Size when we have it */
4609     msi_reg_set_val_dword(hkey, szEstimatedSize, 0);
4610
4611     buffer = msi_dup_property(package->db, szProductName);
4612     msi_reg_set_val_str(hkey, szDisplayName, buffer);
4613     msi_free(buffer);
4614
4615     buffer = msi_dup_property(package->db, cszSourceDir);
4616     msi_reg_set_val_str(hkey, INSTALLPROPERTY_INSTALLSOURCEW, buffer);
4617     msi_free(buffer);
4618
4619     buffer = msi_dup_property(package->db, szManufacturer);
4620     msi_reg_set_val_str(hkey, INSTALLPROPERTY_PUBLISHERW, buffer);
4621     msi_free(buffer);
4622
4623     GetLocalTime(&systime);
4624     sprintfW(date, date_fmt, systime.wYear, systime.wMonth, systime.wDay);
4625     msi_reg_set_val_str(hkey, INSTALLPROPERTY_INSTALLDATEW, date);
4626
4627     langid = msi_get_property_int(package->db, szProductLanguage, 0);
4628     msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
4629
4630     buffer = msi_dup_property(package->db, szProductVersion);
4631     msi_reg_set_val_str(hkey, szDisplayVersion, buffer);
4632     if (buffer)
4633     {
4634         DWORD verdword = msi_version_str_to_dword(buffer);
4635
4636         msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
4637         msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMAJORW, verdword >> 24);
4638         msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMINORW, (verdword >> 16) & 0xFF);
4639         msi_free(buffer);
4640     }
4641
4642     return ERROR_SUCCESS;
4643 }
4644
4645 static UINT ACTION_RegisterProduct(MSIPACKAGE *package)
4646 {
4647     WCHAR squashed_pc[SQUISH_GUID_SIZE];
4648     MSIRECORD *uirow;
4649     LPWSTR upgrade_code;
4650     HKEY hkey, props;
4651     HKEY upgrade;
4652     UINT rc;
4653
4654     static const WCHAR szUpgradeCode[] = {
4655         'U','p','g','r','a','d','e','C','o','d','e',0};
4656
4657     /* FIXME: also need to publish if the product is in advertise mode */
4658     if (!msi_check_publish(package))
4659         return ERROR_SUCCESS;
4660
4661     rc = MSIREG_OpenUninstallKey(package->ProductCode, &hkey, TRUE);
4662     if (rc != ERROR_SUCCESS)
4663         return rc;
4664
4665     rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context,
4666                                  NULL, &props, TRUE);
4667     if (rc != ERROR_SUCCESS)
4668         goto done;
4669
4670     msi_reg_set_val_str( props, INSTALLPROPERTY_LOCALPACKAGEW, package->db->localfile );
4671     msi_free( package->db->localfile );
4672     package->db->localfile = NULL;
4673
4674     rc = msi_publish_install_properties(package, hkey);
4675     if (rc != ERROR_SUCCESS)
4676         goto done;
4677
4678     rc = msi_publish_install_properties(package, props);
4679     if (rc != ERROR_SUCCESS)
4680         goto done;
4681
4682     upgrade_code = msi_dup_property(package->db, szUpgradeCode);
4683     if (upgrade_code)
4684     {
4685         MSIREG_OpenUpgradeCodesKey(upgrade_code, &upgrade, TRUE);
4686         squash_guid(package->ProductCode, squashed_pc);
4687         msi_reg_set_val_str(upgrade, squashed_pc, NULL);
4688         RegCloseKey(upgrade);
4689         msi_free(upgrade_code);
4690     }
4691
4692 done:
4693     uirow = MSI_CreateRecord( 1 );
4694     MSI_RecordSetStringW( uirow, 1, package->ProductCode );
4695     ui_actiondata( package, szRegisterProduct, uirow );
4696     msiobj_release( &uirow->hdr );
4697
4698     RegCloseKey(hkey);
4699     return ERROR_SUCCESS;
4700 }
4701
4702 static UINT ACTION_InstallExecute(MSIPACKAGE *package)
4703 {
4704     return execute_script(package,INSTALL_SCRIPT);
4705 }
4706
4707 static UINT msi_unpublish_product(MSIPACKAGE *package)
4708 {
4709     LPWSTR upgrade;
4710     LPWSTR remove = NULL;
4711     LPWSTR *features = NULL;
4712     BOOL full_uninstall = TRUE;
4713     MSIFEATURE *feature;
4714     MSIPATCHINFO *patch;
4715
4716     static const WCHAR szUpgradeCode[] =
4717         {'U','p','g','r','a','d','e','C','o','d','e',0};
4718
4719     remove = msi_dup_property(package->db, szRemove);
4720     if (!remove)
4721         return ERROR_SUCCESS;
4722
4723     features = msi_split_string(remove, ',');
4724     if (!features)
4725     {
4726         msi_free(remove);
4727         ERR("REMOVE feature list is empty!\n");
4728         return ERROR_FUNCTION_FAILED;
4729     }
4730
4731     if (!lstrcmpW(features[0], szAll))
4732         full_uninstall = TRUE;
4733     else
4734     {
4735         LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4736         {
4737             if (feature->Action != INSTALLSTATE_ABSENT)
4738                 full_uninstall = FALSE;
4739         }
4740     }
4741
4742     if (!full_uninstall)
4743         goto done;
4744
4745     MSIREG_DeleteProductKey(package->ProductCode);
4746     MSIREG_DeleteUserDataProductKey(package->ProductCode);
4747     MSIREG_DeleteUninstallKey(package->ProductCode);
4748
4749     if (package->Context == MSIINSTALLCONTEXT_MACHINE)
4750     {
4751         MSIREG_DeleteLocalClassesProductKey(package->ProductCode);
4752         MSIREG_DeleteLocalClassesFeaturesKey(package->ProductCode);
4753     }
4754     else
4755     {
4756         MSIREG_DeleteUserProductKey(package->ProductCode);
4757         MSIREG_DeleteUserFeaturesKey(package->ProductCode);
4758     }
4759
4760     upgrade = msi_dup_property(package->db, szUpgradeCode);
4761     if (upgrade)
4762     {
4763         MSIREG_DeleteUserUpgradeCodesKey(upgrade);
4764         msi_free(upgrade);
4765     }
4766
4767     LIST_FOR_EACH_ENTRY(patch, &package->patches, MSIPATCHINFO, entry)
4768     {
4769         MSIREG_DeleteUserDataPatchKey(patch->patchcode, package->Context);
4770     }
4771
4772 done:
4773     msi_free(remove);
4774     msi_free(features);
4775     return ERROR_SUCCESS;
4776 }
4777
4778 static UINT ACTION_InstallFinalize(MSIPACKAGE *package)
4779 {
4780     UINT rc;
4781
4782     rc = msi_unpublish_product(package);
4783     if (rc != ERROR_SUCCESS)
4784         return rc;
4785
4786     /* turn off scheduling */
4787     package->script->CurrentlyScripting= FALSE;
4788
4789     /* first do the same as an InstallExecute */
4790     rc = ACTION_InstallExecute(package);
4791     if (rc != ERROR_SUCCESS)
4792         return rc;
4793
4794     /* then handle Commit Actions */
4795     rc = execute_script(package,COMMIT_SCRIPT);
4796
4797     return rc;
4798 }
4799
4800 UINT ACTION_ForceReboot(MSIPACKAGE *package)
4801 {
4802     static const WCHAR RunOnce[] = {
4803     'S','o','f','t','w','a','r','e','\\',
4804     'M','i','c','r','o','s','o','f','t','\\',
4805     'W','i','n','d','o','w','s','\\',
4806     'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
4807     'R','u','n','O','n','c','e',0};
4808     static const WCHAR InstallRunOnce[] = {
4809     'S','o','f','t','w','a','r','e','\\',
4810     'M','i','c','r','o','s','o','f','t','\\',
4811     'W','i','n','d','o','w','s','\\',
4812     'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
4813     'I','n','s','t','a','l','l','e','r','\\',
4814     'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
4815
4816     static const WCHAR msiexec_fmt[] = {
4817     '%','s',
4818     '\\','M','s','i','E','x','e','c','.','e','x','e',' ','/','@',' ',
4819     '\"','%','s','\"',0};
4820     static const WCHAR install_fmt[] = {
4821     '/','I',' ','\"','%','s','\"',' ',
4822     'A','F','T','E','R','R','E','B','O','O','T','=','1',' ',
4823     'R','U','N','O','N','C','E','E','N','T','R','Y','=','\"','%','s','\"',0};
4824     WCHAR buffer[256], sysdir[MAX_PATH];
4825     HKEY hkey;
4826     WCHAR squished_pc[100];
4827
4828     squash_guid(package->ProductCode,squished_pc);
4829
4830     GetSystemDirectoryW(sysdir, sizeof(sysdir)/sizeof(sysdir[0]));
4831     RegCreateKeyW(HKEY_LOCAL_MACHINE,RunOnce,&hkey);
4832     snprintfW(buffer,sizeof(buffer)/sizeof(buffer[0]),msiexec_fmt,sysdir,
4833      squished_pc);
4834
4835     msi_reg_set_val_str( hkey, squished_pc, buffer );
4836     RegCloseKey(hkey);
4837
4838     TRACE("Reboot command %s\n",debugstr_w(buffer));
4839
4840     RegCreateKeyW(HKEY_LOCAL_MACHINE,InstallRunOnce,&hkey);
4841     sprintfW(buffer,install_fmt,package->ProductCode,squished_pc);
4842
4843     msi_reg_set_val_str( hkey, squished_pc, buffer );
4844     RegCloseKey(hkey);
4845
4846     return ERROR_INSTALL_SUSPEND;
4847 }
4848
4849 static UINT ACTION_ResolveSource(MSIPACKAGE* package)
4850 {
4851     DWORD attrib;
4852     UINT rc;
4853
4854     /*
4855      * We are currently doing what should be done here in the top level Install
4856      * however for Administrative and uninstalls this step will be needed
4857      */
4858     if (!package->PackagePath)
4859         return ERROR_SUCCESS;
4860
4861     msi_set_sourcedir_props(package, TRUE);
4862
4863     attrib = GetFileAttributesW(package->db->path);
4864     if (attrib == INVALID_FILE_ATTRIBUTES)
4865     {
4866         LPWSTR prompt;
4867         LPWSTR msg;
4868         DWORD size = 0;
4869
4870         rc = MsiSourceListGetInfoW(package->ProductCode, NULL, 
4871                 package->Context, MSICODE_PRODUCT,
4872                 INSTALLPROPERTY_DISKPROMPTW,NULL,&size);
4873         if (rc == ERROR_MORE_DATA)
4874         {
4875             prompt = msi_alloc(size * sizeof(WCHAR));
4876             MsiSourceListGetInfoW(package->ProductCode, NULL, 
4877                     package->Context, MSICODE_PRODUCT,
4878                     INSTALLPROPERTY_DISKPROMPTW,prompt,&size);
4879         }
4880         else
4881             prompt = strdupW(package->db->path);
4882
4883         msg = generate_error_string(package,1302,1,prompt);
4884         while(attrib == INVALID_FILE_ATTRIBUTES)
4885         {
4886             rc = MessageBoxW(NULL,msg,NULL,MB_OKCANCEL);
4887             if (rc == IDCANCEL)
4888             {
4889                 rc = ERROR_INSTALL_USEREXIT;
4890                 break;
4891             }
4892             attrib = GetFileAttributesW(package->db->path);
4893         }
4894         msi_free(prompt);
4895         rc = ERROR_SUCCESS;
4896     }
4897     else
4898         return ERROR_SUCCESS;
4899
4900     return rc;
4901 }
4902
4903 static UINT ACTION_RegisterUser(MSIPACKAGE *package)
4904 {
4905     HKEY hkey = 0;
4906     LPWSTR buffer, productid = NULL;
4907     UINT i, rc = ERROR_SUCCESS;
4908     MSIRECORD *uirow;
4909
4910     static const WCHAR szPropKeys[][80] = 
4911     {
4912         {'P','r','o','d','u','c','t','I','D',0},
4913         {'U','S','E','R','N','A','M','E',0},
4914         {'C','O','M','P','A','N','Y','N','A','M','E',0},
4915         {0},
4916     };
4917
4918     static const WCHAR szRegKeys[][80] = 
4919     {
4920         {'P','r','o','d','u','c','t','I','D',0},
4921         {'R','e','g','O','w','n','e','r',0},
4922         {'R','e','g','C','o','m','p','a','n','y',0},
4923         {0},
4924     };
4925
4926     if (msi_check_unpublish(package))
4927     {
4928         MSIREG_DeleteUserDataProductKey(package->ProductCode);
4929         goto end;
4930     }
4931
4932     productid = msi_dup_property( package->db, INSTALLPROPERTY_PRODUCTIDW );
4933     if (!productid)
4934         goto end;
4935
4936     rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context,
4937                                  NULL, &hkey, TRUE);
4938     if (rc != ERROR_SUCCESS)
4939         goto end;
4940
4941     for( i = 0; szPropKeys[i][0]; i++ )
4942     {
4943         buffer = msi_dup_property( package->db, szPropKeys[i] );
4944         msi_reg_set_val_str( hkey, szRegKeys[i], buffer );
4945         msi_free( buffer );
4946     }
4947
4948 end:
4949     uirow = MSI_CreateRecord( 1 );
4950     MSI_RecordSetStringW( uirow, 1, productid );
4951     ui_actiondata( package, szRegisterUser, uirow );
4952     msiobj_release( &uirow->hdr );
4953
4954     msi_free(productid);
4955     RegCloseKey(hkey);
4956     return rc;
4957 }
4958
4959
4960 static UINT ACTION_ExecuteAction(MSIPACKAGE *package)
4961 {
4962     UINT rc;
4963
4964     package->script->InWhatSequence |= SEQUENCE_EXEC;
4965     rc = ACTION_ProcessExecSequence(package,FALSE);
4966     return rc;
4967 }
4968
4969
4970 static UINT ITERATE_PublishComponent(MSIRECORD *rec, LPVOID param)
4971 {
4972     MSIPACKAGE *package = param;
4973     LPCWSTR compgroupid, component, feature, qualifier, text;
4974     LPWSTR advertise = NULL, output = NULL;
4975     HKEY hkey = NULL;
4976     UINT rc;
4977     MSICOMPONENT *comp;
4978     MSIFEATURE *feat;
4979     DWORD sz;
4980     MSIRECORD *uirow;
4981
4982     feature = MSI_RecordGetString(rec, 5);
4983     feat = get_loaded_feature(package, feature);
4984     if (!feat)
4985         return ERROR_SUCCESS;
4986
4987     if (feat->ActionRequest != INSTALLSTATE_LOCAL &&
4988         feat->ActionRequest != INSTALLSTATE_SOURCE &&
4989         feat->ActionRequest != INSTALLSTATE_ADVERTISED)
4990     {
4991         TRACE("Feature %s not scheduled for installation\n", debugstr_w(feature));
4992         feat->Action = feat->Installed;
4993         return ERROR_SUCCESS;
4994     }
4995
4996     component = MSI_RecordGetString(rec, 3);
4997     comp = get_loaded_component(package, component);
4998     if (!comp)
4999         return ERROR_SUCCESS;
5000
5001     compgroupid = MSI_RecordGetString(rec,1);
5002     qualifier = MSI_RecordGetString(rec,2);
5003
5004     rc = MSIREG_OpenUserComponentsKey(compgroupid, &hkey, TRUE);
5005     if (rc != ERROR_SUCCESS)
5006         goto end;
5007     
5008     text = MSI_RecordGetString(rec,4);
5009     advertise = create_component_advertise_string(package, comp, feature);
5010
5011     sz = strlenW(advertise);
5012
5013     if (text)
5014         sz += lstrlenW(text);
5015
5016     sz+=3;
5017     sz *= sizeof(WCHAR);
5018            
5019     output = msi_alloc_zero(sz);
5020     strcpyW(output,advertise);
5021     msi_free(advertise);
5022
5023     if (text)
5024         strcatW(output,text);
5025
5026     msi_reg_set_val_multi_str( hkey, qualifier, output );
5027     
5028 end:
5029     RegCloseKey(hkey);
5030     msi_free(output);
5031
5032     /* the UI chunk */
5033     uirow = MSI_CreateRecord( 2 );
5034     MSI_RecordSetStringW( uirow, 1, compgroupid );
5035     MSI_RecordSetStringW( uirow, 2, qualifier);
5036     ui_actiondata( package, szPublishComponents, uirow);
5037     msiobj_release( &uirow->hdr );
5038     /* FIXME: call ui_progress? */
5039
5040     return rc;
5041 }
5042
5043 /*
5044  * At present I am ignorning the advertised components part of this and only
5045  * focusing on the qualified component sets
5046  */
5047 static UINT ACTION_PublishComponents(MSIPACKAGE *package)
5048 {
5049     UINT rc;
5050     MSIQUERY * view;
5051     static const WCHAR ExecSeqQuery[] =
5052         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5053          '`','P','u','b','l','i','s','h',
5054          'C','o','m','p','o','n','e','n','t','`',0};
5055     
5056     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
5057     if (rc != ERROR_SUCCESS)
5058         return ERROR_SUCCESS;
5059
5060     rc = MSI_IterateRecords(view, NULL, ITERATE_PublishComponent, package);
5061     msiobj_release(&view->hdr);
5062
5063     return rc;
5064 }
5065
5066 static UINT ITERATE_UnpublishComponent( MSIRECORD *rec, LPVOID param )
5067 {
5068     static const WCHAR szInstallerComponents[] = {
5069         'S','o','f','t','w','a','r','e','\\',
5070         'M','i','c','r','o','s','o','f','t','\\',
5071         'I','n','s','t','a','l','l','e','r','\\',
5072         'C','o','m','p','o','n','e','n','t','s','\\',0};
5073
5074     MSIPACKAGE *package = param;
5075     LPCWSTR compgroupid, component, feature, qualifier;
5076     MSICOMPONENT *comp;
5077     MSIFEATURE *feat;
5078     MSIRECORD *uirow;
5079     WCHAR squashed[GUID_SIZE], keypath[MAX_PATH];
5080     LONG res;
5081
5082     feature = MSI_RecordGetString( rec, 5 );
5083     feat = get_loaded_feature( package, feature );
5084     if (!feat)
5085         return ERROR_SUCCESS;
5086
5087     if (feat->ActionRequest != INSTALLSTATE_ABSENT)
5088     {
5089         TRACE("Feature %s not scheduled for removal\n", debugstr_w(feature));
5090         feat->Action = feat->Installed;
5091         return ERROR_SUCCESS;
5092     }
5093
5094     component = MSI_RecordGetString( rec, 3 );
5095     comp = get_loaded_component( package, component );
5096     if (!comp)
5097         return ERROR_SUCCESS;
5098
5099     compgroupid = MSI_RecordGetString( rec, 1 );
5100     qualifier = MSI_RecordGetString( rec, 2 );
5101
5102     squash_guid( compgroupid, squashed );
5103     strcpyW( keypath, szInstallerComponents );
5104     strcatW( keypath, squashed );
5105
5106     res = RegDeleteKeyW( HKEY_CURRENT_USER, keypath );
5107     if (res != ERROR_SUCCESS)
5108     {
5109         WARN("Unable to delete component key %d\n", res);
5110     }
5111
5112     uirow = MSI_CreateRecord( 2 );
5113     MSI_RecordSetStringW( uirow, 1, compgroupid );
5114     MSI_RecordSetStringW( uirow, 2, qualifier );
5115     ui_actiondata( package, szUnpublishComponents, uirow );
5116     msiobj_release( &uirow->hdr );
5117
5118     return ERROR_SUCCESS;
5119 }
5120
5121 static UINT ACTION_UnpublishComponents( MSIPACKAGE *package )
5122 {
5123     UINT rc;
5124     MSIQUERY *view;
5125     static const WCHAR query[] =
5126         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5127          '`','P','u','b','l','i','s','h',
5128          'C','o','m','p','o','n','e','n','t','`',0};
5129
5130     rc = MSI_DatabaseOpenViewW( package->db, query, &view );
5131     if (rc != ERROR_SUCCESS)
5132         return ERROR_SUCCESS;
5133
5134     rc = MSI_IterateRecords( view, NULL, ITERATE_UnpublishComponent, package );
5135     msiobj_release( &view->hdr );
5136
5137     return rc;
5138 }
5139
5140 static UINT ITERATE_InstallService(MSIRECORD *rec, LPVOID param)
5141 {
5142     MSIPACKAGE *package = param;
5143     MSIRECORD *row;
5144     MSIFILE *file;
5145     SC_HANDLE hscm, service = NULL;
5146     LPCWSTR comp, depends, pass;
5147     LPWSTR name = NULL, disp = NULL;
5148     LPCWSTR load_order, serv_name, key;
5149     DWORD serv_type, start_type;
5150     DWORD err_control;
5151
5152     static const WCHAR query[] =
5153         {'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
5154          '`','C','o','m','p','o','n','e','n','t','`',' ',
5155          'W','H','E','R','E',' ',
5156          '`','C','o','m','p','o','n','e','n','t','`',' ',
5157          '=','\'','%','s','\'',0};
5158
5159     hscm = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASEW, GENERIC_WRITE);
5160     if (!hscm)
5161     {
5162         ERR("Failed to open the SC Manager!\n");
5163         goto done;
5164     }
5165
5166     start_type = MSI_RecordGetInteger(rec, 5);
5167     if (start_type == SERVICE_BOOT_START || start_type == SERVICE_SYSTEM_START)
5168         goto done;
5169
5170     depends = MSI_RecordGetString(rec, 8);
5171     if (depends && *depends)
5172         FIXME("Dependency list unhandled!\n");
5173
5174     deformat_string(package, MSI_RecordGetString(rec, 2), &name);
5175     deformat_string(package, MSI_RecordGetString(rec, 3), &disp);
5176     serv_type = MSI_RecordGetInteger(rec, 4);
5177     err_control = MSI_RecordGetInteger(rec, 6);
5178     load_order = MSI_RecordGetString(rec, 7);
5179     serv_name = MSI_RecordGetString(rec, 9);
5180     pass = MSI_RecordGetString(rec, 10);
5181     comp = MSI_RecordGetString(rec, 12);
5182
5183     /* fetch the service path */
5184     row = MSI_QueryGetRecord(package->db, query, comp);
5185     if (!row)
5186     {
5187         ERR("Control query failed!\n");
5188         goto done;
5189     }
5190
5191     key = MSI_RecordGetString(row, 6);
5192
5193     file = get_loaded_file(package, key);
5194     msiobj_release(&row->hdr);
5195     if (!file)
5196     {
5197         ERR("Failed to load the service file\n");
5198         goto done;
5199     }
5200
5201     service = CreateServiceW(hscm, name, disp, GENERIC_ALL, serv_type,
5202                              start_type, err_control, file->TargetPath,
5203                              load_order, NULL, NULL, serv_name, pass);
5204     if (!service)
5205     {
5206         if (GetLastError() != ERROR_SERVICE_EXISTS)
5207             ERR("Failed to create service %s: %d\n", debugstr_w(name), GetLastError());
5208     }
5209
5210 done:
5211     CloseServiceHandle(service);
5212     CloseServiceHandle(hscm);
5213     msi_free(name);
5214     msi_free(disp);
5215
5216     return ERROR_SUCCESS;
5217 }
5218
5219 static UINT ACTION_InstallServices( MSIPACKAGE *package )
5220 {
5221     UINT rc;
5222     MSIQUERY * view;
5223     static const WCHAR ExecSeqQuery[] =
5224         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5225          'S','e','r','v','i','c','e','I','n','s','t','a','l','l',0};
5226     
5227     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
5228     if (rc != ERROR_SUCCESS)
5229         return ERROR_SUCCESS;
5230
5231     rc = MSI_IterateRecords(view, NULL, ITERATE_InstallService, package);
5232     msiobj_release(&view->hdr);
5233
5234     return rc;
5235 }
5236
5237 /* converts arg1[~]arg2[~]arg3 to a list of ptrs to the strings */
5238 static LPCWSTR *msi_service_args_to_vector(LPWSTR args, DWORD *numargs)
5239 {
5240     LPCWSTR *vector, *temp_vector;
5241     LPWSTR p, q;
5242     DWORD sep_len;
5243
5244     static const WCHAR separator[] = {'[','~',']',0};
5245
5246     *numargs = 0;
5247     sep_len = sizeof(separator) / sizeof(WCHAR) - 1;
5248
5249     if (!args)
5250         return NULL;
5251
5252     vector = msi_alloc(sizeof(LPWSTR));
5253     if (!vector)
5254         return NULL;
5255
5256     p = args;
5257     do
5258     {
5259         (*numargs)++;
5260         vector[*numargs - 1] = p;
5261
5262         if ((q = strstrW(p, separator)))
5263         {
5264             *q = '\0';
5265
5266             temp_vector = msi_realloc(vector, (*numargs + 1) * sizeof(LPWSTR));
5267             if (!temp_vector)
5268             {
5269                 msi_free(vector);
5270                 return NULL;
5271             }
5272             vector = temp_vector;
5273
5274             p = q + sep_len;
5275         }
5276     } while (q);
5277
5278     return vector;
5279 }
5280
5281 static UINT ITERATE_StartService(MSIRECORD *rec, LPVOID param)
5282 {
5283     MSIPACKAGE *package = param;
5284     MSICOMPONENT *comp;
5285     MSIRECORD *uirow;
5286     SC_HANDLE scm = NULL, service = NULL;
5287     LPCWSTR component, *vector = NULL;
5288     LPWSTR name, args, display_name = NULL;
5289     DWORD event, numargs, len;
5290     UINT r = ERROR_FUNCTION_FAILED;
5291
5292     component = MSI_RecordGetString(rec, 6);
5293     comp = get_loaded_component(package, component);
5294     if (!comp)
5295         return ERROR_SUCCESS;
5296
5297     if (comp->ActionRequest != INSTALLSTATE_LOCAL)
5298     {
5299         TRACE("Component not scheduled for installation: %s\n", debugstr_w(component));
5300         comp->Action = comp->Installed;
5301         return ERROR_SUCCESS;
5302     }
5303     comp->Action = INSTALLSTATE_LOCAL;
5304
5305     deformat_string(package, MSI_RecordGetString(rec, 2), &name);
5306     deformat_string(package, MSI_RecordGetString(rec, 4), &args);
5307     event = MSI_RecordGetInteger(rec, 3);
5308
5309     if (!(event & msidbServiceControlEventStart))
5310     {
5311         r = ERROR_SUCCESS;
5312         goto done;
5313     }
5314
5315     scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
5316     if (!scm)
5317     {
5318         ERR("Failed to open the service control manager\n");
5319         goto done;
5320     }
5321
5322     len = 0;
5323     if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
5324         GetLastError() == ERROR_INSUFFICIENT_BUFFER)
5325     {
5326         if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
5327             GetServiceDisplayNameW( scm, name, display_name, &len );
5328     }
5329
5330     service = OpenServiceW(scm, name, SERVICE_START);
5331     if (!service)
5332     {
5333         ERR("Failed to open service %s (%u)\n", debugstr_w(name), GetLastError());
5334         goto done;
5335     }
5336
5337     vector = msi_service_args_to_vector(args, &numargs);
5338
5339     if (!StartServiceW(service, numargs, vector) &&
5340         GetLastError() != ERROR_SERVICE_ALREADY_RUNNING)
5341     {
5342         ERR("Failed to start service %s (%u)\n", debugstr_w(name), GetLastError());
5343         goto done;
5344     }
5345
5346     r = ERROR_SUCCESS;
5347
5348 done:
5349     uirow = MSI_CreateRecord( 2 );
5350     MSI_RecordSetStringW( uirow, 1, display_name );
5351     MSI_RecordSetStringW( uirow, 2, name );
5352     ui_actiondata( package, szStartServices, uirow );
5353     msiobj_release( &uirow->hdr );
5354
5355     CloseServiceHandle(service);
5356     CloseServiceHandle(scm);
5357
5358     msi_free(name);
5359     msi_free(args);
5360     msi_free(vector);
5361     msi_free(display_name);
5362     return r;
5363 }
5364
5365 static UINT ACTION_StartServices( MSIPACKAGE *package )
5366 {
5367     UINT rc;
5368     MSIQUERY *view;
5369
5370     static const WCHAR query[] = {
5371         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5372         'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
5373
5374     rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5375     if (rc != ERROR_SUCCESS)
5376         return ERROR_SUCCESS;
5377
5378     rc = MSI_IterateRecords(view, NULL, ITERATE_StartService, package);
5379     msiobj_release(&view->hdr);
5380
5381     return rc;
5382 }
5383
5384 static BOOL stop_service_dependents(SC_HANDLE scm, SC_HANDLE service)
5385 {
5386     DWORD i, needed, count;
5387     ENUM_SERVICE_STATUSW *dependencies;
5388     SERVICE_STATUS ss;
5389     SC_HANDLE depserv;
5390
5391     if (EnumDependentServicesW(service, SERVICE_ACTIVE, NULL,
5392                                0, &needed, &count))
5393         return TRUE;
5394
5395     if (GetLastError() != ERROR_MORE_DATA)
5396         return FALSE;
5397
5398     dependencies = msi_alloc(needed);
5399     if (!dependencies)
5400         return FALSE;
5401
5402     if (!EnumDependentServicesW(service, SERVICE_ACTIVE, dependencies,
5403                                 needed, &needed, &count))
5404         goto error;
5405
5406     for (i = 0; i < count; i++)
5407     {
5408         depserv = OpenServiceW(scm, dependencies[i].lpServiceName,
5409                                SERVICE_STOP | SERVICE_QUERY_STATUS);
5410         if (!depserv)
5411             goto error;
5412
5413         if (!ControlService(depserv, SERVICE_CONTROL_STOP, &ss))
5414             goto error;
5415     }
5416
5417     return TRUE;
5418
5419 error:
5420     msi_free(dependencies);
5421     return FALSE;
5422 }
5423
5424 static UINT stop_service( LPCWSTR name )
5425 {
5426     SC_HANDLE scm = NULL, service = NULL;
5427     SERVICE_STATUS status;
5428     SERVICE_STATUS_PROCESS ssp;
5429     DWORD needed;
5430
5431     scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
5432     if (!scm)
5433     {
5434         WARN("Failed to open the SCM: %d\n", GetLastError());
5435         goto done;
5436     }
5437
5438     service = OpenServiceW(scm, name,
5439                            SERVICE_STOP |
5440                            SERVICE_QUERY_STATUS |
5441                            SERVICE_ENUMERATE_DEPENDENTS);
5442     if (!service)
5443     {
5444         WARN("Failed to open service (%s): %d\n", debugstr_w(name), GetLastError());
5445         goto done;
5446     }
5447
5448     if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp,
5449                               sizeof(SERVICE_STATUS_PROCESS), &needed))
5450     {
5451         WARN("Failed to query service status (%s): %d\n", debugstr_w(name), GetLastError());
5452         goto done;
5453     }
5454
5455     if (ssp.dwCurrentState == SERVICE_STOPPED)
5456         goto done;
5457
5458     stop_service_dependents(scm, service);
5459
5460     if (!ControlService(service, SERVICE_CONTROL_STOP, &status))
5461         WARN("Failed to stop service (%s): %d\n", debugstr_w(name), GetLastError());
5462
5463 done:
5464     CloseServiceHandle(service);
5465     CloseServiceHandle(scm);
5466
5467     return ERROR_SUCCESS;
5468 }
5469
5470 static UINT ITERATE_StopService( MSIRECORD *rec, LPVOID param )
5471 {
5472     MSIPACKAGE *package = param;
5473     MSICOMPONENT *comp;
5474     MSIRECORD *uirow;
5475     LPCWSTR component;
5476     LPWSTR name = NULL, display_name = NULL;
5477     DWORD event, len;
5478     SC_HANDLE scm;
5479
5480     event = MSI_RecordGetInteger( rec, 3 );
5481     if (!(event & msidbServiceControlEventStop))
5482         return ERROR_SUCCESS;
5483
5484     component = MSI_RecordGetString( rec, 6 );
5485     comp = get_loaded_component( package, component );
5486     if (!comp)
5487         return ERROR_SUCCESS;
5488
5489     if (comp->ActionRequest != INSTALLSTATE_ABSENT)
5490     {
5491         TRACE("Component not scheduled for removal: %s\n", debugstr_w(component));
5492         comp->Action = comp->Installed;
5493         return ERROR_SUCCESS;
5494     }
5495     comp->Action = INSTALLSTATE_ABSENT;
5496
5497     scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_CONNECT );
5498     if (!scm)
5499     {
5500         ERR("Failed to open the service control manager\n");
5501         goto done;
5502     }
5503
5504     len = 0;
5505     if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
5506         GetLastError() == ERROR_INSUFFICIENT_BUFFER)
5507     {
5508         if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
5509             GetServiceDisplayNameW( scm, name, display_name, &len );
5510     }
5511     CloseServiceHandle( scm );
5512
5513     deformat_string( package, MSI_RecordGetString( rec, 2 ), &name );
5514     stop_service( name );
5515
5516 done:
5517     uirow = MSI_CreateRecord( 2 );
5518     MSI_RecordSetStringW( uirow, 1, display_name );
5519     MSI_RecordSetStringW( uirow, 2, name );
5520     ui_actiondata( package, szStopServices, uirow );
5521     msiobj_release( &uirow->hdr );
5522
5523     msi_free( name );
5524     msi_free( display_name );
5525     return ERROR_SUCCESS;
5526 }
5527
5528 static UINT ACTION_StopServices( MSIPACKAGE *package )
5529 {
5530     UINT rc;
5531     MSIQUERY *view;
5532
5533     static const WCHAR query[] = {
5534         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5535         'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
5536
5537     rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5538     if (rc != ERROR_SUCCESS)
5539         return ERROR_SUCCESS;
5540
5541     rc = MSI_IterateRecords(view, NULL, ITERATE_StopService, package);
5542     msiobj_release(&view->hdr);
5543
5544     return rc;
5545 }
5546
5547 static UINT ITERATE_DeleteService( MSIRECORD *rec, LPVOID param )
5548 {
5549     MSIPACKAGE *package = param;
5550     MSICOMPONENT *comp;
5551     MSIRECORD *uirow;
5552     LPCWSTR component;
5553     LPWSTR name = NULL, display_name = NULL;
5554     DWORD event, len;
5555     SC_HANDLE scm = NULL, service = NULL;
5556
5557     event = MSI_RecordGetInteger( rec, 3 );
5558     if (!(event & msidbServiceControlEventDelete))
5559         return ERROR_SUCCESS;
5560
5561     component = MSI_RecordGetString(rec, 6);
5562     comp = get_loaded_component(package, component);
5563     if (!comp)
5564         return ERROR_SUCCESS;
5565
5566     if (comp->ActionRequest != INSTALLSTATE_ABSENT)
5567     {
5568         TRACE("Component not scheduled for removal: %s\n", debugstr_w(component));
5569         comp->Action = comp->Installed;
5570         return ERROR_SUCCESS;
5571     }
5572     comp->Action = INSTALLSTATE_ABSENT;
5573
5574     deformat_string( package, MSI_RecordGetString(rec, 2), &name );
5575     stop_service( name );
5576
5577     scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_ALL_ACCESS );
5578     if (!scm)
5579     {
5580         WARN("Failed to open the SCM: %d\n", GetLastError());
5581         goto done;
5582     }
5583
5584     len = 0;
5585     if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
5586         GetLastError() == ERROR_INSUFFICIENT_BUFFER)
5587     {
5588         if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
5589             GetServiceDisplayNameW( scm, name, display_name, &len );
5590     }
5591
5592     service = OpenServiceW( scm, name, DELETE );
5593     if (!service)
5594     {
5595         WARN("Failed to open service (%s): %u\n", debugstr_w(name), GetLastError());
5596         goto done;
5597     }
5598
5599     if (!DeleteService( service ))
5600         WARN("Failed to delete service (%s): %u\n", debugstr_w(name), GetLastError());
5601
5602 done:
5603     uirow = MSI_CreateRecord( 2 );
5604     MSI_RecordSetStringW( uirow, 1, display_name );
5605     MSI_RecordSetStringW( uirow, 2, name );
5606     ui_actiondata( package, szDeleteServices, uirow );
5607     msiobj_release( &uirow->hdr );
5608
5609     CloseServiceHandle( service );
5610     CloseServiceHandle( scm );
5611     msi_free( name );
5612     msi_free( display_name );
5613
5614     return ERROR_SUCCESS;
5615 }
5616
5617 static UINT ACTION_DeleteServices( MSIPACKAGE *package )
5618 {
5619     UINT rc;
5620     MSIQUERY *view;
5621
5622     static const WCHAR query[] = {
5623         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5624         'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
5625
5626     rc = MSI_DatabaseOpenViewW( package->db, query, &view );
5627     if (rc != ERROR_SUCCESS)
5628         return ERROR_SUCCESS;
5629
5630     rc = MSI_IterateRecords( view, NULL, ITERATE_DeleteService, package );
5631     msiobj_release( &view->hdr );
5632
5633     return rc;
5634 }
5635
5636 static MSIFILE *msi_find_file( MSIPACKAGE *package, LPCWSTR filename )
5637 {
5638     MSIFILE *file;
5639
5640     LIST_FOR_EACH_ENTRY(file, &package->files, MSIFILE, entry)
5641     {
5642         if (!lstrcmpW(file->File, filename))
5643             return file;
5644     }
5645
5646     return NULL;
5647 }
5648
5649 static UINT ITERATE_InstallODBCDriver( MSIRECORD *rec, LPVOID param )
5650 {
5651     MSIPACKAGE *package = param;
5652     LPWSTR driver, driver_path, ptr;
5653     WCHAR outpath[MAX_PATH];
5654     MSIFILE *driver_file, *setup_file;
5655     MSIRECORD *uirow;
5656     LPCWSTR desc;
5657     DWORD len, usage;
5658     UINT r = ERROR_SUCCESS;
5659
5660     static const WCHAR driver_fmt[] = {
5661         'D','r','i','v','e','r','=','%','s',0};
5662     static const WCHAR setup_fmt[] = {
5663         'S','e','t','u','p','=','%','s',0};
5664     static const WCHAR usage_fmt[] = {
5665         'F','i','l','e','U','s','a','g','e','=','1',0};
5666
5667     desc = MSI_RecordGetString(rec, 3);
5668
5669     driver_file = msi_find_file(package, MSI_RecordGetString(rec, 4));
5670     setup_file = msi_find_file(package, MSI_RecordGetString(rec, 5));
5671
5672     if (!driver_file)
5673     {
5674         ERR("ODBC Driver entry not found!\n");
5675         return ERROR_FUNCTION_FAILED;
5676     }
5677
5678     len = lstrlenW(desc) + lstrlenW(driver_fmt) + lstrlenW(driver_file->FileName);
5679     if (setup_file)
5680         len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
5681     len += lstrlenW(usage_fmt) + 2; /* \0\0 */
5682
5683     driver = msi_alloc(len * sizeof(WCHAR));
5684     if (!driver)
5685         return ERROR_OUTOFMEMORY;
5686
5687     ptr = driver;
5688     lstrcpyW(ptr, desc);
5689     ptr += lstrlenW(ptr) + 1;
5690
5691     len = sprintfW(ptr, driver_fmt, driver_file->FileName);
5692     ptr += len + 1;
5693
5694     if (setup_file)
5695     {
5696         len = sprintfW(ptr, setup_fmt, setup_file->FileName);
5697         ptr += len + 1;
5698     }
5699
5700     lstrcpyW(ptr, usage_fmt);
5701     ptr += lstrlenW(ptr) + 1;
5702     *ptr = '\0';
5703
5704     driver_path = strdupW(driver_file->TargetPath);
5705     ptr = strrchrW(driver_path, '\\');
5706     if (ptr) *ptr = '\0';
5707
5708     if (!SQLInstallDriverExW(driver, driver_path, outpath, MAX_PATH,
5709                              NULL, ODBC_INSTALL_COMPLETE, &usage))
5710     {
5711         ERR("Failed to install SQL driver!\n");
5712         r = ERROR_FUNCTION_FAILED;
5713     }
5714
5715     uirow = MSI_CreateRecord( 5 );
5716     MSI_RecordSetStringW( uirow, 1, desc );
5717     MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
5718     MSI_RecordSetStringW( uirow, 3, driver_path );
5719     ui_actiondata( package, szInstallODBC, uirow );
5720     msiobj_release( &uirow->hdr );
5721
5722     msi_free(driver);
5723     msi_free(driver_path);
5724
5725     return r;
5726 }
5727
5728 static UINT ITERATE_InstallODBCTranslator( MSIRECORD *rec, LPVOID param )
5729 {
5730     MSIPACKAGE *package = param;
5731     LPWSTR translator, translator_path, ptr;
5732     WCHAR outpath[MAX_PATH];
5733     MSIFILE *translator_file, *setup_file;
5734     MSIRECORD *uirow;
5735     LPCWSTR desc;
5736     DWORD len, usage;
5737     UINT r = ERROR_SUCCESS;
5738
5739     static const WCHAR translator_fmt[] = {
5740         'T','r','a','n','s','l','a','t','o','r','=','%','s',0};
5741     static const WCHAR setup_fmt[] = {
5742         'S','e','t','u','p','=','%','s',0};
5743
5744     desc = MSI_RecordGetString(rec, 3);
5745
5746     translator_file = msi_find_file(package, MSI_RecordGetString(rec, 4));
5747     setup_file = msi_find_file(package, MSI_RecordGetString(rec, 5));
5748
5749     if (!translator_file)
5750     {
5751         ERR("ODBC Translator entry not found!\n");
5752         return ERROR_FUNCTION_FAILED;
5753     }
5754
5755     len = lstrlenW(desc) + lstrlenW(translator_fmt) + lstrlenW(translator_file->FileName) + 2; /* \0\0 */
5756     if (setup_file)
5757         len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
5758
5759     translator = msi_alloc(len * sizeof(WCHAR));
5760     if (!translator)
5761         return ERROR_OUTOFMEMORY;
5762
5763     ptr = translator;
5764     lstrcpyW(ptr, desc);
5765     ptr += lstrlenW(ptr) + 1;
5766
5767     len = sprintfW(ptr, translator_fmt, translator_file->FileName);
5768     ptr += len + 1;
5769
5770     if (setup_file)
5771     {
5772         len = sprintfW(ptr, setup_fmt, setup_file->FileName);
5773         ptr += len + 1;
5774     }
5775     *ptr = '\0';
5776
5777     translator_path = strdupW(translator_file->TargetPath);
5778     ptr = strrchrW(translator_path, '\\');
5779     if (ptr) *ptr = '\0';
5780
5781     if (!SQLInstallTranslatorExW(translator, translator_path, outpath, MAX_PATH,
5782                                  NULL, ODBC_INSTALL_COMPLETE, &usage))
5783     {
5784         ERR("Failed to install SQL translator!\n");
5785         r = ERROR_FUNCTION_FAILED;
5786     }
5787
5788     uirow = MSI_CreateRecord( 5 );
5789     MSI_RecordSetStringW( uirow, 1, desc );
5790     MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
5791     MSI_RecordSetStringW( uirow, 3, translator_path );
5792     ui_actiondata( package, szInstallODBC, uirow );
5793     msiobj_release( &uirow->hdr );
5794
5795     msi_free(translator);
5796     msi_free(translator_path);
5797
5798     return r;
5799 }
5800
5801 static UINT ITERATE_InstallODBCDataSource( MSIRECORD *rec, LPVOID param )
5802 {
5803     MSIPACKAGE *package = param;
5804     LPWSTR attrs;
5805     LPCWSTR desc, driver;
5806     WORD request = ODBC_ADD_SYS_DSN;
5807     INT registration;
5808     DWORD len;
5809     UINT r = ERROR_SUCCESS;
5810     MSIRECORD *uirow;
5811
5812     static const WCHAR attrs_fmt[] = {
5813         'D','S','N','=','%','s',0 };
5814
5815     desc = MSI_RecordGetString(rec, 3);
5816     driver = MSI_RecordGetString(rec, 4);
5817     registration = MSI_RecordGetInteger(rec, 5);
5818
5819     if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_ADD_SYS_DSN;
5820     else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_ADD_DSN;
5821
5822     len = lstrlenW(attrs_fmt) + lstrlenW(desc) + 2; /* \0\0 */
5823     attrs = msi_alloc(len * sizeof(WCHAR));
5824     if (!attrs)
5825         return ERROR_OUTOFMEMORY;
5826
5827     len = sprintfW(attrs, attrs_fmt, desc);
5828     attrs[len + 1] = 0;
5829
5830     if (!SQLConfigDataSourceW(NULL, request, driver, attrs))
5831     {
5832         ERR("Failed to install SQL data source!\n");
5833         r = ERROR_FUNCTION_FAILED;
5834     }
5835
5836     uirow = MSI_CreateRecord( 5 );
5837     MSI_RecordSetStringW( uirow, 1, desc );
5838     MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
5839     MSI_RecordSetInteger( uirow, 3, request );
5840     ui_actiondata( package, szInstallODBC, uirow );
5841     msiobj_release( &uirow->hdr );
5842
5843     msi_free(attrs);
5844
5845     return r;
5846 }
5847
5848 static UINT ACTION_InstallODBC( MSIPACKAGE *package )
5849 {
5850     UINT rc;
5851     MSIQUERY *view;
5852
5853     static const WCHAR driver_query[] = {
5854         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5855         'O','D','B','C','D','r','i','v','e','r',0 };
5856
5857     static const WCHAR translator_query[] = {
5858         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5859         'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0 };
5860
5861     static const WCHAR source_query[] = {
5862         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5863         'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0 };
5864
5865     rc = MSI_DatabaseOpenViewW(package->db, driver_query, &view);
5866     if (rc != ERROR_SUCCESS)
5867         return ERROR_SUCCESS;
5868
5869     rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDriver, package);
5870     msiobj_release(&view->hdr);
5871
5872     rc = MSI_DatabaseOpenViewW(package->db, translator_query, &view);
5873     if (rc != ERROR_SUCCESS)
5874         return ERROR_SUCCESS;
5875
5876     rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCTranslator, package);
5877     msiobj_release(&view->hdr);
5878
5879     rc = MSI_DatabaseOpenViewW(package->db, source_query, &view);
5880     if (rc != ERROR_SUCCESS)
5881         return ERROR_SUCCESS;
5882
5883     rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDataSource, package);
5884     msiobj_release(&view->hdr);
5885
5886     return rc;
5887 }
5888
5889 static UINT ITERATE_RemoveODBCDriver( MSIRECORD *rec, LPVOID param )
5890 {
5891     MSIPACKAGE *package = param;
5892     MSIRECORD *uirow;
5893     DWORD usage;
5894     LPCWSTR desc;
5895
5896     desc = MSI_RecordGetString( rec, 3 );
5897     if (!SQLRemoveDriverW( desc, FALSE, &usage ))
5898     {
5899         WARN("Failed to remove ODBC driver\n");
5900     }
5901     else if (!usage)
5902     {
5903         FIXME("Usage count reached 0\n");
5904     }
5905
5906     uirow = MSI_CreateRecord( 2 );
5907     MSI_RecordSetStringW( uirow, 1, desc );
5908     MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
5909     ui_actiondata( package, szRemoveODBC, uirow );
5910     msiobj_release( &uirow->hdr );
5911
5912     return ERROR_SUCCESS;
5913 }
5914
5915 static UINT ITERATE_RemoveODBCTranslator( MSIRECORD *rec, LPVOID param )
5916 {
5917     MSIPACKAGE *package = param;
5918     MSIRECORD *uirow;
5919     DWORD usage;
5920     LPCWSTR desc;
5921
5922     desc = MSI_RecordGetString( rec, 3 );
5923     if (!SQLRemoveTranslatorW( desc, &usage ))
5924     {
5925         WARN("Failed to remove ODBC translator\n");
5926     }
5927     else if (!usage)
5928     {
5929         FIXME("Usage count reached 0\n");
5930     }
5931
5932     uirow = MSI_CreateRecord( 2 );
5933     MSI_RecordSetStringW( uirow, 1, desc );
5934     MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
5935     ui_actiondata( package, szRemoveODBC, uirow );
5936     msiobj_release( &uirow->hdr );
5937
5938     return ERROR_SUCCESS;
5939 }
5940
5941 static UINT ITERATE_RemoveODBCDataSource( MSIRECORD *rec, LPVOID param )
5942 {
5943     MSIPACKAGE *package = param;
5944     MSIRECORD *uirow;
5945     LPWSTR attrs;
5946     LPCWSTR desc, driver;
5947     WORD request = ODBC_REMOVE_SYS_DSN;
5948     INT registration;
5949     DWORD len;
5950
5951     static const WCHAR attrs_fmt[] = {
5952         'D','S','N','=','%','s',0 };
5953
5954     desc = MSI_RecordGetString( rec, 3 );
5955     driver = MSI_RecordGetString( rec, 4 );
5956     registration = MSI_RecordGetInteger( rec, 5 );
5957
5958     if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_REMOVE_SYS_DSN;
5959     else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_REMOVE_DSN;
5960
5961     len = strlenW( attrs_fmt ) + strlenW( desc ) + 2; /* \0\0 */
5962     attrs = msi_alloc( len * sizeof(WCHAR) );
5963     if (!attrs)
5964         return ERROR_OUTOFMEMORY;
5965
5966     FIXME("Use ODBCSourceAttribute table\n");
5967
5968     len = sprintfW( attrs, attrs_fmt, desc );
5969     attrs[len + 1] = 0;
5970
5971     if (!SQLConfigDataSourceW( NULL, request, driver, attrs ))
5972     {
5973         WARN("Failed to remove ODBC data source\n");
5974     }
5975     msi_free( attrs );
5976
5977     uirow = MSI_CreateRecord( 3 );
5978     MSI_RecordSetStringW( uirow, 1, desc );
5979     MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
5980     MSI_RecordSetInteger( uirow, 3, request );
5981     ui_actiondata( package, szRemoveODBC, uirow );
5982     msiobj_release( &uirow->hdr );
5983
5984     return ERROR_SUCCESS;
5985 }
5986
5987 static UINT ACTION_RemoveODBC( MSIPACKAGE *package )
5988 {
5989     UINT rc;
5990     MSIQUERY *view;
5991
5992     static const WCHAR driver_query[] = {
5993         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5994         'O','D','B','C','D','r','i','v','e','r',0 };
5995
5996     static const WCHAR translator_query[] = {
5997         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5998         'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0 };
5999
6000     static const WCHAR source_query[] = {
6001         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6002         'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0 };
6003
6004     rc = MSI_DatabaseOpenViewW( package->db, driver_query, &view );
6005     if (rc != ERROR_SUCCESS)
6006         return ERROR_SUCCESS;
6007
6008     rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDriver, package );
6009     msiobj_release( &view->hdr );
6010
6011     rc = MSI_DatabaseOpenViewW( package->db, translator_query, &view );
6012     if (rc != ERROR_SUCCESS)
6013         return ERROR_SUCCESS;
6014
6015     rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCTranslator, package );
6016     msiobj_release( &view->hdr );
6017
6018     rc = MSI_DatabaseOpenViewW( package->db, source_query, &view );
6019     if (rc != ERROR_SUCCESS)
6020         return ERROR_SUCCESS;
6021
6022     rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDataSource, package );
6023     msiobj_release( &view->hdr );
6024
6025     return rc;
6026 }
6027
6028 #define ENV_ACT_SETALWAYS   0x1
6029 #define ENV_ACT_SETABSENT   0x2
6030 #define ENV_ACT_REMOVE      0x4
6031 #define ENV_ACT_REMOVEMATCH 0x8
6032
6033 #define ENV_MOD_MACHINE     0x20000000
6034 #define ENV_MOD_APPEND      0x40000000
6035 #define ENV_MOD_PREFIX      0x80000000
6036 #define ENV_MOD_MASK        0xC0000000
6037
6038 #define check_flag_combo(x, y) ((x) & ~(y)) == (y)
6039
6040 static UINT env_parse_flags( LPCWSTR *name, LPCWSTR *value, DWORD *flags )
6041 {
6042     LPCWSTR cptr = *name;
6043
6044     static const WCHAR prefix[] = {'[','~',']',0};
6045     static const int prefix_len = 3;
6046
6047     *flags = 0;
6048     while (*cptr)
6049     {
6050         if (*cptr == '=')
6051             *flags |= ENV_ACT_SETALWAYS;
6052         else if (*cptr == '+')
6053             *flags |= ENV_ACT_SETABSENT;
6054         else if (*cptr == '-')
6055             *flags |= ENV_ACT_REMOVE;
6056         else if (*cptr == '!')
6057             *flags |= ENV_ACT_REMOVEMATCH;
6058         else if (*cptr == '*')
6059             *flags |= ENV_MOD_MACHINE;
6060         else
6061             break;
6062
6063         cptr++;
6064         (*name)++;
6065     }
6066
6067     if (!*cptr)
6068     {
6069         ERR("Missing environment variable\n");
6070         return ERROR_FUNCTION_FAILED;
6071     }
6072
6073     if (*value)
6074     {
6075         LPCWSTR ptr = *value;
6076         if (!strncmpW(ptr, prefix, prefix_len))
6077         {
6078             if (ptr[prefix_len] == szSemiColon[0])
6079             {
6080                 *flags |= ENV_MOD_APPEND;
6081                 *value += lstrlenW(prefix);
6082             }
6083             else
6084             {
6085                 *value = NULL;
6086             }
6087         }
6088         else if (lstrlenW(*value) >= prefix_len)
6089         {
6090             ptr += lstrlenW(ptr) - prefix_len;
6091             if (!lstrcmpW(ptr, prefix))
6092             {
6093                 if ((ptr-1) > *value && *(ptr-1) == szSemiColon[0])
6094                 {
6095                     *flags |= ENV_MOD_PREFIX;
6096                     /* the "[~]" will be removed by deformat_string */;
6097                 }
6098                 else
6099                 {
6100                     *value = NULL;
6101                 }
6102             }
6103         }
6104     }
6105
6106     if (check_flag_combo(*flags, ENV_ACT_SETALWAYS | ENV_ACT_SETABSENT) ||
6107         check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETABSENT) ||
6108         check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETALWAYS) ||
6109         check_flag_combo(*flags, ENV_ACT_SETABSENT | ENV_MOD_MASK))
6110     {
6111         ERR("Invalid flags: %08x\n", *flags);
6112         return ERROR_FUNCTION_FAILED;
6113     }
6114
6115     if (!*flags)
6116         *flags = ENV_ACT_SETALWAYS | ENV_ACT_REMOVE;
6117
6118     return ERROR_SUCCESS;
6119 }
6120
6121 static UINT open_env_key( DWORD flags, HKEY *key )
6122 {
6123     static const WCHAR user_env[] =
6124         {'E','n','v','i','r','o','n','m','e','n','t',0};
6125     static const WCHAR machine_env[] =
6126         {'S','y','s','t','e','m','\\',
6127          'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
6128          'C','o','n','t','r','o','l','\\',
6129          'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r','\\',
6130          'E','n','v','i','r','o','n','m','e','n','t',0};
6131     const WCHAR *env;
6132     HKEY root;
6133     LONG res;
6134
6135     if (flags & ENV_MOD_MACHINE)
6136     {
6137         env = machine_env;
6138         root = HKEY_LOCAL_MACHINE;
6139     }
6140     else
6141     {
6142         env = user_env;
6143         root = HKEY_CURRENT_USER;
6144     }
6145
6146     res = RegOpenKeyExW( root, env, 0, KEY_ALL_ACCESS, key );
6147     if (res != ERROR_SUCCESS)
6148     {
6149         WARN("Failed to open key %s (%d)\n", debugstr_w(env), res);
6150         return ERROR_FUNCTION_FAILED;
6151     }
6152
6153     return ERROR_SUCCESS;
6154 }
6155
6156 static UINT ITERATE_WriteEnvironmentString( MSIRECORD *rec, LPVOID param )
6157 {
6158     MSIPACKAGE *package = param;
6159     LPCWSTR name, value, component;
6160     LPWSTR data = NULL, newval = NULL, deformatted = NULL, ptr;
6161     DWORD flags, type, size;
6162     UINT res;
6163     HKEY env = NULL;
6164     MSICOMPONENT *comp;
6165     MSIRECORD *uirow;
6166     int action = 0;
6167
6168     component = MSI_RecordGetString(rec, 4);
6169     comp = get_loaded_component(package, component);
6170     if (!comp)
6171         return ERROR_SUCCESS;
6172
6173     if (comp->ActionRequest != INSTALLSTATE_LOCAL)
6174     {
6175         TRACE("Component not scheduled for installation: %s\n", debugstr_w(component));
6176         comp->Action = comp->Installed;
6177         return ERROR_SUCCESS;
6178     }
6179     comp->Action = INSTALLSTATE_LOCAL;
6180
6181     name = MSI_RecordGetString(rec, 2);
6182     value = MSI_RecordGetString(rec, 3);
6183
6184     TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
6185
6186     res = env_parse_flags(&name, &value, &flags);
6187     if (res != ERROR_SUCCESS || !value)
6188        goto done;
6189
6190     if (value && !deformat_string(package, value, &deformatted))
6191     {
6192         res = ERROR_OUTOFMEMORY;
6193         goto done;
6194     }
6195
6196     value = deformatted;
6197
6198     res = open_env_key( flags, &env );
6199     if (res != ERROR_SUCCESS)
6200         goto done;
6201
6202     if (flags & ENV_MOD_MACHINE)
6203         action |= 0x20000000;
6204
6205     size = 0;
6206     type = REG_SZ;
6207     res = RegQueryValueExW(env, name, NULL, &type, NULL, &size);
6208     if ((res != ERROR_SUCCESS && res != ERROR_FILE_NOT_FOUND) ||
6209         (res == ERROR_SUCCESS && type != REG_SZ && type != REG_EXPAND_SZ))
6210         goto done;
6211
6212     if ((res == ERROR_FILE_NOT_FOUND || !(flags & ENV_MOD_MASK)))
6213     {
6214         action = 0x2;
6215
6216         /* Nothing to do. */
6217         if (!value)
6218         {
6219             res = ERROR_SUCCESS;
6220             goto done;
6221         }
6222
6223         /* If we are appending but the string was empty, strip ; */
6224         if ((flags & ENV_MOD_APPEND) && (value[0] == szSemiColon[0])) value++;
6225
6226         size = (lstrlenW(value) + 1) * sizeof(WCHAR);
6227         newval = strdupW(value);
6228         if (!newval)
6229         {
6230             res = ERROR_OUTOFMEMORY;
6231             goto done;
6232         }
6233     }
6234     else
6235     {
6236         action = 0x1;
6237
6238         /* Contrary to MSDN, +-variable to [~];path works */
6239         if (flags & ENV_ACT_SETABSENT && !(flags & ENV_MOD_MASK))
6240         {
6241             res = ERROR_SUCCESS;
6242             goto done;
6243         }
6244
6245         data = msi_alloc(size);
6246         if (!data)
6247         {
6248             RegCloseKey(env);
6249             return ERROR_OUTOFMEMORY;
6250         }
6251
6252         res = RegQueryValueExW(env, name, NULL, &type, (LPVOID)data, &size);
6253         if (res != ERROR_SUCCESS)
6254             goto done;
6255
6256         if (flags & ENV_ACT_REMOVEMATCH && (!value || !lstrcmpW(data, value)))
6257         {
6258             action = 0x4;
6259             res = RegDeleteValueW(env, name);
6260             if (res != ERROR_SUCCESS)
6261                 WARN("Failed to remove value %s (%d)\n", debugstr_w(name), res);
6262             goto done;
6263         }
6264
6265         size = (lstrlenW(data) + 1) * sizeof(WCHAR);
6266         if (flags & ENV_MOD_MASK)
6267         {
6268             DWORD mod_size;
6269             int multiplier = 0;
6270             if (flags & ENV_MOD_APPEND) multiplier++;
6271             if (flags & ENV_MOD_PREFIX) multiplier++;
6272             mod_size = lstrlenW(value) * multiplier;
6273             size += mod_size * sizeof(WCHAR);
6274         }
6275
6276         newval = msi_alloc(size);
6277         ptr = newval;
6278         if (!newval)
6279         {
6280             res = ERROR_OUTOFMEMORY;
6281             goto done;
6282         }
6283
6284         if (flags & ENV_MOD_PREFIX)
6285         {
6286             lstrcpyW(newval, value);
6287             ptr = newval + lstrlenW(value);
6288             action |= 0x80000000;
6289         }
6290
6291         lstrcpyW(ptr, data);
6292
6293         if (flags & ENV_MOD_APPEND)
6294         {
6295             lstrcatW(newval, value);
6296             action |= 0x40000000;
6297         }
6298     }
6299     TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(newval));
6300     res = RegSetValueExW(env, name, 0, type, (LPVOID)newval, size);
6301     if (res)
6302     {
6303         WARN("Failed to set %s to %s (%d)\n",  debugstr_w(name), debugstr_w(newval), res);
6304     }
6305
6306 done:
6307     uirow = MSI_CreateRecord( 3 );
6308     MSI_RecordSetStringW( uirow, 1, name );
6309     MSI_RecordSetStringW( uirow, 2, newval );
6310     MSI_RecordSetInteger( uirow, 3, action );
6311     ui_actiondata( package, szWriteEnvironmentStrings, uirow );
6312     msiobj_release( &uirow->hdr );
6313
6314     if (env) RegCloseKey(env);
6315     msi_free(deformatted);
6316     msi_free(data);
6317     msi_free(newval);
6318     return res;
6319 }
6320
6321 static UINT ACTION_WriteEnvironmentStrings( MSIPACKAGE *package )
6322 {
6323     UINT rc;
6324     MSIQUERY * view;
6325     static const WCHAR ExecSeqQuery[] =
6326         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6327          '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
6328     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
6329     if (rc != ERROR_SUCCESS)
6330         return ERROR_SUCCESS;
6331
6332     rc = MSI_IterateRecords(view, NULL, ITERATE_WriteEnvironmentString, package);
6333     msiobj_release(&view->hdr);
6334
6335     return rc;
6336 }
6337
6338 static UINT ITERATE_RemoveEnvironmentString( MSIRECORD *rec, LPVOID param )
6339 {
6340     MSIPACKAGE *package = param;
6341     LPCWSTR name, value, component;
6342     LPWSTR deformatted = NULL;
6343     DWORD flags;
6344     HKEY env;
6345     MSICOMPONENT *comp;
6346     MSIRECORD *uirow;
6347     int action = 0;
6348     LONG res;
6349     UINT r;
6350
6351     component = MSI_RecordGetString( rec, 4 );
6352     comp = get_loaded_component( package, component );
6353     if (!comp)
6354         return ERROR_SUCCESS;
6355
6356     if (comp->ActionRequest != INSTALLSTATE_ABSENT)
6357     {
6358         TRACE("Component not scheduled for removal: %s\n", debugstr_w(component));
6359         comp->Action = comp->Installed;
6360         return ERROR_SUCCESS;
6361     }
6362     comp->Action = INSTALLSTATE_ABSENT;
6363
6364     name = MSI_RecordGetString( rec, 2 );
6365     value = MSI_RecordGetString( rec, 3 );
6366
6367     TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
6368
6369     r = env_parse_flags( &name, &value, &flags );
6370     if (r != ERROR_SUCCESS)
6371        return r;
6372
6373     if (!(flags & ENV_ACT_REMOVE))
6374     {
6375         TRACE("Environment variable %s not marked for removal\n", debugstr_w(name));
6376         return ERROR_SUCCESS;
6377     }
6378
6379     if (value && !deformat_string( package, value, &deformatted ))
6380         return ERROR_OUTOFMEMORY;
6381
6382     value = deformatted;
6383
6384     r = open_env_key( flags, &env );
6385     if (r != ERROR_SUCCESS)
6386     {
6387         r = ERROR_SUCCESS;
6388         goto done;
6389     }
6390
6391     if (flags & ENV_MOD_MACHINE)
6392         action |= 0x20000000;
6393
6394     TRACE("Removing %s\n", debugstr_w(name));
6395
6396     res = RegDeleteValueW( env, name );
6397     if (res != ERROR_SUCCESS)
6398     {
6399         WARN("Failed to delete value %s (%d)\n", debugstr_w(name), res);
6400         r = ERROR_SUCCESS;
6401     }
6402
6403 done:
6404     uirow = MSI_CreateRecord( 3 );
6405     MSI_RecordSetStringW( uirow, 1, name );
6406     MSI_RecordSetStringW( uirow, 2, value );
6407     MSI_RecordSetInteger( uirow, 3, action );
6408     ui_actiondata( package, szRemoveEnvironmentStrings, uirow );
6409     msiobj_release( &uirow->hdr );
6410
6411     if (env) RegCloseKey( env );
6412     msi_free( deformatted );
6413     return r;
6414 }
6415
6416 static UINT ACTION_RemoveEnvironmentStrings( MSIPACKAGE *package )
6417 {
6418     UINT rc;
6419     MSIQUERY *view;
6420     static const WCHAR query[] =
6421         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6422          '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
6423
6424     rc = MSI_DatabaseOpenViewW( package->db, query, &view );
6425     if (rc != ERROR_SUCCESS)
6426         return ERROR_SUCCESS;
6427
6428     rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveEnvironmentString, package );
6429     msiobj_release( &view->hdr );
6430
6431     return rc;
6432 }
6433
6434 typedef struct tagMSIASSEMBLY
6435 {
6436     struct list entry;
6437     MSICOMPONENT *component;
6438     MSIFEATURE *feature;
6439     MSIFILE *file;
6440     LPWSTR manifest;
6441     LPWSTR application;
6442     LPWSTR display_name;
6443     DWORD attributes;
6444     BOOL installed;
6445 } MSIASSEMBLY;
6446
6447 static HRESULT (WINAPI *pCreateAssemblyCache)(IAssemblyCache **ppAsmCache,
6448                                               DWORD dwReserved);
6449 static HRESULT (WINAPI *pLoadLibraryShim)(LPCWSTR szDllName, LPCWSTR szVersion,
6450                                           LPVOID pvReserved, HMODULE *phModDll);
6451
6452 static BOOL init_functionpointers(void)
6453 {
6454     HRESULT hr;
6455     HMODULE hfusion;
6456     HMODULE hmscoree;
6457
6458     static const WCHAR szFusion[] = {'f','u','s','i','o','n','.','d','l','l',0};
6459
6460     hmscoree = LoadLibraryA("mscoree.dll");
6461     if (!hmscoree)
6462     {
6463         WARN("mscoree.dll not available\n");
6464         return FALSE;
6465     }
6466
6467     pLoadLibraryShim = (void *)GetProcAddress(hmscoree, "LoadLibraryShim");
6468     if (!pLoadLibraryShim)
6469     {
6470         WARN("LoadLibraryShim not available\n");
6471         FreeLibrary(hmscoree);
6472         return FALSE;
6473     }
6474
6475     hr = pLoadLibraryShim(szFusion, NULL, NULL, &hfusion);
6476     if (FAILED(hr))
6477     {
6478         WARN("fusion.dll not available\n");
6479         FreeLibrary(hmscoree);
6480         return FALSE;
6481     }
6482
6483     pCreateAssemblyCache = (void *)GetProcAddress(hfusion, "CreateAssemblyCache");
6484
6485     FreeLibrary(hmscoree);
6486     return TRUE;
6487 }
6488
6489 static UINT install_assembly(MSIPACKAGE *package, MSIASSEMBLY *assembly,
6490                              LPWSTR path)
6491 {
6492     IAssemblyCache *cache;
6493     MSIRECORD *uirow;
6494     HRESULT hr;
6495     UINT r = ERROR_FUNCTION_FAILED;
6496
6497     TRACE("installing assembly: %s\n", debugstr_w(path));
6498
6499     uirow = MSI_CreateRecord( 2 );
6500     MSI_RecordSetStringW( uirow, 2, assembly->display_name );
6501     ui_actiondata( package, szMsiPublishAssemblies, uirow );
6502     msiobj_release( &uirow->hdr );
6503
6504     if (assembly->feature)
6505         msi_feature_set_state(package, assembly->feature, INSTALLSTATE_LOCAL);
6506
6507     if (assembly->manifest)
6508         FIXME("Manifest unhandled\n");
6509
6510     if (assembly->application)
6511     {
6512         FIXME("Assembly should be privately installed\n");
6513         return ERROR_SUCCESS;
6514     }
6515
6516     if (assembly->attributes == msidbAssemblyAttributesWin32)
6517     {
6518         FIXME("Win32 assemblies not handled\n");
6519         return ERROR_SUCCESS;
6520     }
6521
6522     hr = pCreateAssemblyCache(&cache, 0);
6523     if (FAILED(hr))
6524         goto done;
6525
6526     hr = IAssemblyCache_InstallAssembly(cache, 0, path, NULL);
6527     if (FAILED(hr))
6528         ERR("Failed to install assembly: %s %08x\n", debugstr_w(path), hr);
6529
6530     r = ERROR_SUCCESS;
6531
6532 done:
6533     IAssemblyCache_Release(cache);
6534     return r;
6535 }
6536
6537 typedef struct tagASSEMBLY_LIST
6538 {
6539     MSIPACKAGE *package;
6540     IAssemblyCache *cache;
6541     struct list *assemblies;
6542 } ASSEMBLY_LIST;
6543
6544 typedef struct tagASSEMBLY_NAME
6545 {
6546     LPWSTR name;
6547     LPWSTR version;
6548     LPWSTR culture;
6549     LPWSTR pubkeytoken;
6550 } ASSEMBLY_NAME;
6551
6552 static UINT parse_assembly_name(MSIRECORD *rec, LPVOID param)
6553 {
6554     ASSEMBLY_NAME *asmname = param;
6555     LPCWSTR name = MSI_RecordGetString(rec, 2);
6556     LPWSTR val = msi_dup_record_field(rec, 3);
6557
6558     static const WCHAR Name[] = {'N','a','m','e',0};
6559     static const WCHAR Version[] = {'V','e','r','s','i','o','n',0};
6560     static const WCHAR Culture[] = {'C','u','l','t','u','r','e',0};
6561     static const WCHAR PublicKeyToken[] = {
6562         'P','u','b','l','i','c','K','e','y','T','o','k','e','n',0};
6563
6564     if (!strcmpiW(name, Name))
6565         asmname->name = val;
6566     else if (!strcmpiW(name, Version))
6567         asmname->version = val;
6568     else if (!strcmpiW(name, Culture))
6569         asmname->culture = val;
6570     else if (!strcmpiW(name, PublicKeyToken))
6571         asmname->pubkeytoken = val;
6572     else
6573         msi_free(val);
6574
6575     return ERROR_SUCCESS;
6576 }
6577
6578 static void append_str(LPWSTR *str, DWORD *size, LPCWSTR append)
6579 {
6580     if (!*str)
6581     {
6582         *size = lstrlenW(append) + 1;
6583         *str = msi_alloc((*size) * sizeof(WCHAR));
6584         lstrcpyW(*str, append);
6585         return;
6586     }
6587
6588     (*size) += lstrlenW(append);
6589     *str = msi_realloc(*str, (*size) * sizeof(WCHAR));
6590     lstrcatW(*str, append);
6591 }
6592
6593 static WCHAR *get_assembly_display_name( MSIDATABASE *db, MSICOMPONENT *comp )
6594 {
6595     static const WCHAR separator[] = {',',' ',0};
6596     static const WCHAR Version[] = {'V','e','r','s','i','o','n','=',0};
6597     static const WCHAR Culture[] = {'C','u','l','t','u','r','e','=',0};
6598     static const WCHAR PublicKeyToken[] = {'P','u','b','l','i','c','K','e','y','T','o','k','e','n','=',0};
6599     static const WCHAR query[] = {
6600         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6601         '`','M','s','i','A','s','s','e','m','b','l','y','N','a','m','e','`',' ',
6602         'W','H','E','R','E',' ','`','C','o','m','p','o','n','e','n','t','_','`',
6603         '=','\'','%','s','\'',0};
6604     ASSEMBLY_NAME name;
6605     MSIQUERY *view;
6606     LPWSTR display_name;
6607     DWORD size;
6608     UINT r;
6609
6610     display_name = NULL;
6611     memset( &name, 0, sizeof(ASSEMBLY_NAME) );
6612
6613     r = MSI_OpenQuery( db, &view, query, comp->Component );
6614     if (r != ERROR_SUCCESS)
6615         return NULL;
6616
6617     MSI_IterateRecords( view, NULL, parse_assembly_name, &name );
6618     msiobj_release( &view->hdr );
6619
6620     if (!name.name)
6621     {
6622         ERR("No assembly name specified!\n");
6623         return NULL;
6624     }
6625
6626     append_str( &display_name, &size, name.name );
6627
6628     if (name.version)
6629     {
6630         append_str( &display_name, &size, separator );
6631         append_str( &display_name, &size, Version );
6632         append_str( &display_name, &size, name.version );
6633     }
6634     if (name.culture)
6635     {
6636         append_str( &display_name, &size, separator );
6637         append_str( &display_name, &size, Culture );
6638         append_str( &display_name, &size, name.culture );
6639     }
6640     if (name.pubkeytoken)
6641     {
6642         append_str( &display_name, &size, separator );
6643         append_str( &display_name, &size, PublicKeyToken );
6644         append_str( &display_name, &size, name.pubkeytoken );
6645     }
6646
6647     msi_free( name.name );
6648     msi_free( name.version );
6649     msi_free( name.culture );
6650     msi_free( name.pubkeytoken );
6651
6652     return display_name;
6653 }
6654
6655 static BOOL check_assembly_installed( MSIDATABASE *db, IAssemblyCache *cache, MSICOMPONENT *comp )
6656 {
6657     ASSEMBLY_INFO asminfo;
6658     LPWSTR disp;
6659     BOOL found = FALSE;
6660     HRESULT hr;
6661
6662     disp = get_assembly_display_name( db, comp );
6663     if (!disp)
6664         return FALSE;
6665
6666     memset( &asminfo, 0, sizeof(ASSEMBLY_INFO) );
6667     asminfo.cbAssemblyInfo = sizeof(ASSEMBLY_INFO);
6668
6669     hr = IAssemblyCache_QueryAssemblyInfo( cache, QUERYASMINFO_FLAG_VALIDATE, disp, &asminfo );
6670     if (SUCCEEDED(hr))
6671         found = (asminfo.dwAssemblyFlags == ASSEMBLYINFO_FLAG_INSTALLED);
6672
6673     msi_free( disp );
6674     return found;
6675 }
6676
6677 static UINT load_assembly(MSIRECORD *rec, LPVOID param)
6678 {
6679     ASSEMBLY_LIST *list = param;
6680     MSIASSEMBLY *assembly;
6681     LPCWSTR component;
6682
6683     assembly = msi_alloc_zero(sizeof(MSIASSEMBLY));
6684     if (!assembly)
6685         return ERROR_OUTOFMEMORY;
6686
6687     component = MSI_RecordGetString(rec, 1);
6688     assembly->component = get_loaded_component(list->package, component);
6689     if (!assembly->component)
6690         return ERROR_SUCCESS;
6691
6692     if (assembly->component->ActionRequest != INSTALLSTATE_LOCAL &&
6693         assembly->component->ActionRequest != INSTALLSTATE_SOURCE)
6694     {
6695         TRACE("Component not scheduled for installation: %s\n", debugstr_w(component));
6696         assembly->component->Action = assembly->component->Installed;
6697         return ERROR_SUCCESS;
6698     }
6699     assembly->component->Action = assembly->component->ActionRequest;
6700
6701     assembly->feature = find_feature_by_name(list->package, MSI_RecordGetString(rec, 2));
6702     assembly->file = msi_find_file(list->package, assembly->component->KeyPath);
6703
6704     if (!assembly->file)
6705     {
6706         ERR("File %s not found\n", debugstr_w(assembly->component->KeyPath));
6707         return ERROR_FUNCTION_FAILED;
6708     }
6709
6710     assembly->manifest = strdupW(MSI_RecordGetString(rec, 3));
6711     assembly->application = strdupW(MSI_RecordGetString(rec, 4));
6712     assembly->attributes = MSI_RecordGetInteger(rec, 5);
6713
6714     if (assembly->application)
6715     {
6716         WCHAR version[24];
6717         DWORD size = sizeof(version)/sizeof(WCHAR);
6718
6719         /* FIXME: we should probably check the manifest file here */
6720
6721         if (!MsiGetFileVersionW(assembly->file->TargetPath, version, &size, NULL, NULL) &&
6722             (!assembly->file->Version || strcmpW(version, assembly->file->Version) >= 0))
6723         {
6724             assembly->installed = TRUE;
6725         }
6726     }
6727     else
6728         assembly->installed = check_assembly_installed(list->package->db,
6729                                                        list->cache,
6730                                                        assembly->component);
6731
6732     list_add_head(list->assemblies, &assembly->entry);
6733     return ERROR_SUCCESS;
6734 }
6735
6736 static UINT load_assemblies(MSIPACKAGE *package, struct list *assemblies)
6737 {
6738     IAssemblyCache *cache = NULL;
6739     ASSEMBLY_LIST list;
6740     MSIQUERY *view;
6741     HRESULT hr;
6742     UINT r;
6743
6744     static const WCHAR query[] =
6745         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6746          '`','M','s','i','A','s','s','e','m','b','l','y','`',0};
6747
6748     r = MSI_DatabaseOpenViewW(package->db, query, &view);
6749     if (r != ERROR_SUCCESS)
6750         return ERROR_SUCCESS;
6751
6752     hr = pCreateAssemblyCache(&cache, 0);
6753     if (FAILED(hr))
6754         return ERROR_FUNCTION_FAILED;
6755
6756     list.package = package;
6757     list.cache = cache;
6758     list.assemblies = assemblies;
6759
6760     r = MSI_IterateRecords(view, NULL, load_assembly, &list);
6761     msiobj_release(&view->hdr);
6762
6763     IAssemblyCache_Release(cache);
6764
6765     return r;
6766 }
6767
6768 static void free_assemblies(struct list *assemblies)
6769 {
6770     struct list *item, *cursor;
6771
6772     LIST_FOR_EACH_SAFE(item, cursor, assemblies)
6773     {
6774         MSIASSEMBLY *assembly = LIST_ENTRY(item, MSIASSEMBLY, entry);
6775
6776         list_remove(&assembly->entry);
6777         msi_free(assembly->application);
6778         msi_free(assembly->manifest);
6779         msi_free(assembly->display_name);
6780         msi_free(assembly);
6781     }
6782 }
6783
6784 static BOOL find_assembly(struct list *assemblies, LPCWSTR file, MSIASSEMBLY **out)
6785 {
6786     MSIASSEMBLY *assembly;
6787
6788     LIST_FOR_EACH_ENTRY(assembly, assemblies, MSIASSEMBLY, entry)
6789     {
6790         if (!lstrcmpW(assembly->file->File, file))
6791         {
6792             *out = assembly;
6793             return TRUE;
6794         }
6795     }
6796
6797     return FALSE;
6798 }
6799
6800 static BOOL installassembly_cb(MSIPACKAGE *package, LPCWSTR file, DWORD action,
6801                                LPWSTR *path, DWORD *attrs, PVOID user)
6802 {
6803     MSIASSEMBLY *assembly;
6804     WCHAR temppath[MAX_PATH];
6805     struct list *assemblies = user;
6806     UINT r;
6807
6808     if (!find_assembly(assemblies, file, &assembly))
6809         return FALSE;
6810
6811     GetTempPathW(MAX_PATH, temppath);
6812     PathAddBackslashW(temppath);
6813     lstrcatW(temppath, assembly->file->FileName);
6814
6815     if (action == MSICABEXTRACT_BEGINEXTRACT)
6816     {
6817         if (assembly->installed)
6818             return FALSE;
6819
6820         *path = strdupW(temppath);
6821         *attrs = assembly->file->Attributes;
6822     }
6823     else if (action == MSICABEXTRACT_FILEEXTRACTED)
6824     {
6825         assembly->installed = TRUE;
6826
6827         r = install_assembly(package, assembly, temppath);
6828         if (r != ERROR_SUCCESS)
6829             ERR("Failed to install assembly\n");
6830     }
6831
6832     return TRUE;
6833 }
6834
6835 static UINT ACTION_MsiPublishAssemblies( MSIPACKAGE *package )
6836 {
6837     UINT r;
6838     struct list assemblies = LIST_INIT(assemblies);
6839     MSIASSEMBLY *assembly;
6840     MSIMEDIAINFO *mi;
6841
6842     if (!init_functionpointers() || !pCreateAssemblyCache)
6843         return ERROR_FUNCTION_FAILED;
6844
6845     r = load_assemblies(package, &assemblies);
6846     if (r != ERROR_SUCCESS)
6847         goto done;
6848
6849     if (list_empty(&assemblies))
6850         goto done;
6851
6852     mi = msi_alloc_zero(sizeof(MSIMEDIAINFO));
6853     if (!mi)
6854     {
6855         r = ERROR_OUTOFMEMORY;
6856         goto done;
6857     }
6858
6859     LIST_FOR_EACH_ENTRY(assembly, &assemblies, MSIASSEMBLY, entry)
6860     {
6861         if (assembly->installed && !mi->is_continuous)
6862             continue;
6863
6864         if (assembly->file->Sequence > mi->last_sequence || mi->is_continuous ||
6865             (assembly->file->IsCompressed && !mi->is_extracted))
6866         {
6867             MSICABDATA data;
6868
6869             r = ready_media(package, assembly->file, mi);
6870             if (r != ERROR_SUCCESS)
6871             {
6872                 ERR("Failed to ready media\n");
6873                 break;
6874             }
6875
6876             data.mi = mi;
6877             data.package = package;
6878             data.cb = installassembly_cb;
6879             data.user = &assemblies;
6880
6881             if (assembly->file->IsCompressed &&
6882                 !msi_cabextract(package, mi, &data))
6883             {
6884                 ERR("Failed to extract cabinet: %s\n", debugstr_w(mi->cabinet));
6885                 r = ERROR_FUNCTION_FAILED;
6886                 break;
6887             }
6888         }
6889
6890         if (!assembly->file->IsCompressed)
6891         {
6892             LPWSTR source = resolve_file_source(package, assembly->file);
6893
6894             r = install_assembly(package, assembly, source);
6895             if (r != ERROR_SUCCESS)
6896                 ERR("Failed to install assembly\n");
6897
6898             msi_free(source);
6899         }
6900
6901         /* FIXME: write Installer assembly reg values */
6902     }
6903
6904 done:
6905     free_assemblies(&assemblies);
6906     return r;
6907 }
6908
6909 static UINT ACTION_ValidateProductID( MSIPACKAGE *package )
6910 {
6911     LPWSTR key, template, id;
6912     UINT r = ERROR_SUCCESS;
6913
6914     id = msi_dup_property( package->db, szProductID );
6915     if (id)
6916     {
6917         msi_free( id );
6918         return ERROR_SUCCESS;
6919     }
6920     template = msi_dup_property( package->db, szPIDTemplate );
6921     key = msi_dup_property( package->db, szPIDKEY );
6922
6923     if (key && template)
6924     {
6925         FIXME( "partial stub: template %s key %s\n", debugstr_w(template), debugstr_w(key) );
6926         r = msi_set_property( package->db, szProductID, key );
6927     }
6928     msi_free( template );
6929     msi_free( key );
6930     return r;
6931 }
6932
6933 static UINT ACTION_ScheduleReboot( MSIPACKAGE *package )
6934 {
6935     TRACE("\n");
6936     package->need_reboot = 1;
6937     return ERROR_SUCCESS;
6938 }
6939
6940 static UINT ACTION_AllocateRegistrySpace( MSIPACKAGE *package )
6941 {
6942     static const WCHAR szAvailableFreeReg[] =
6943         {'A','V','A','I','L','A','B','L','E','F','R','E','E','R','E','G',0};
6944     MSIRECORD *uirow;
6945     int space = msi_get_property_int( package->db, szAvailableFreeReg, 0 );
6946
6947     TRACE("%p %d kilobytes\n", package, space);
6948
6949     uirow = MSI_CreateRecord( 1 );
6950     MSI_RecordSetInteger( uirow, 1, space );
6951     ui_actiondata( package, szAllocateRegistrySpace, uirow );
6952     msiobj_release( &uirow->hdr );
6953
6954     return ERROR_SUCCESS;
6955 }
6956
6957 static UINT ACTION_DisableRollback( MSIPACKAGE *package )
6958 {
6959     FIXME("%p\n", package);
6960     return ERROR_SUCCESS;
6961 }
6962
6963 static UINT ACTION_InstallAdminPackage( MSIPACKAGE *package )
6964 {
6965     FIXME("%p\n", package);
6966     return ERROR_SUCCESS;
6967 }
6968
6969 static UINT ACTION_SetODBCFolders( MSIPACKAGE *package )
6970 {
6971     UINT r, count;
6972     MSIQUERY *view;
6973
6974     static const WCHAR driver_query[] = {
6975         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6976         'O','D','B','C','D','r','i','v','e','r',0 };
6977
6978     static const WCHAR translator_query[] = {
6979         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6980         'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0 };
6981
6982     r = MSI_DatabaseOpenViewW( package->db, driver_query, &view );
6983     if (r == ERROR_SUCCESS)
6984     {
6985         count = 0;
6986         r = MSI_IterateRecords( view, &count, NULL, package );
6987         msiobj_release( &view->hdr );
6988         if (count) FIXME("ignored %u rows in ODBCDriver table\n", count);
6989     }
6990
6991     r = MSI_DatabaseOpenViewW( package->db, translator_query, &view );
6992     if (r == ERROR_SUCCESS)
6993     {
6994         count = 0;
6995         r = MSI_IterateRecords( view, &count, NULL, package );
6996         msiobj_release( &view->hdr );
6997         if (count) FIXME("ignored %u rows in ODBCTranslator table\n", count);
6998     }
6999
7000     return ERROR_SUCCESS;
7001 }
7002
7003 static UINT msi_unimplemented_action_stub( MSIPACKAGE *package,
7004                                            LPCSTR action, LPCWSTR table )
7005 {
7006     static const WCHAR query[] = {
7007         'S','E','L','E','C','T',' ','*',' ',
7008         'F','R','O','M',' ','`','%','s','`',0 };
7009     MSIQUERY *view = NULL;
7010     DWORD count = 0;
7011     UINT r;
7012     
7013     r = MSI_OpenQuery( package->db, &view, query, table );
7014     if (r == ERROR_SUCCESS)
7015     {
7016         r = MSI_IterateRecords(view, &count, NULL, package);
7017         msiobj_release(&view->hdr);
7018     }
7019
7020     if (count)
7021         FIXME("%s -> %u ignored %s table values\n",
7022               action, count, debugstr_w(table));
7023
7024     return ERROR_SUCCESS;
7025 }
7026
7027 static UINT ACTION_PatchFiles( MSIPACKAGE *package )
7028 {
7029     static const WCHAR table[] = { 'P','a','t','c','h',0 };
7030     return msi_unimplemented_action_stub( package, "PatchFiles", table );
7031 }
7032
7033 static UINT ACTION_BindImage( MSIPACKAGE *package )
7034 {
7035     static const WCHAR table[] = { 'B','i','n','d','I','m','a','g','e',0 };
7036     return msi_unimplemented_action_stub( package, "BindImage", table );
7037 }
7038
7039 static UINT ACTION_IsolateComponents( MSIPACKAGE *package )
7040 {
7041     static const WCHAR table[] = {
7042         'I','s','o','l','a','t','e','d','C','o','m','p','o','n','e','n','t',0 };
7043     return msi_unimplemented_action_stub( package, "IsolateComponents", table );
7044 }
7045
7046 static UINT ACTION_MigrateFeatureStates( MSIPACKAGE *package )
7047 {
7048     static const WCHAR table[] = { 'U','p','g','r','a','d','e',0 };
7049     return msi_unimplemented_action_stub( package, "MigrateFeatureStates", table );
7050 }
7051
7052 static UINT ACTION_MsiUnpublishAssemblies( MSIPACKAGE *package )
7053 {
7054     static const WCHAR table[] = {
7055         'M','s','i','A','s','s','e','m','b','l','y',0 };
7056     return msi_unimplemented_action_stub( package, "MsiUnpublishAssemblies", table );
7057 }
7058
7059 static UINT ACTION_RMCCPSearch( MSIPACKAGE *package )
7060 {
7061     static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
7062     return msi_unimplemented_action_stub( package, "RMCCPSearch", table );
7063 }
7064
7065 static UINT ACTION_RegisterComPlus( MSIPACKAGE *package )
7066 {
7067     static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
7068     return msi_unimplemented_action_stub( package, "RegisterComPlus", table );
7069 }
7070
7071 static UINT ACTION_UnregisterComPlus( MSIPACKAGE *package )
7072 {
7073     static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
7074     return msi_unimplemented_action_stub( package, "UnregisterComPlus", table );
7075 }
7076
7077 static UINT ACTION_InstallSFPCatalogFile( MSIPACKAGE *package )
7078 {
7079     static const WCHAR table[] = { 'S','F','P','C','a','t','a','l','o','g',0 };
7080     return msi_unimplemented_action_stub( package, "InstallSFPCatalogFile", table );
7081 }
7082
7083 static UINT ACTION_RemoveExistingProducts( MSIPACKAGE *package )
7084 {
7085     static const WCHAR table[] = { 'U','p','g','r','a','d','e',0 };
7086     return msi_unimplemented_action_stub( package, "RemoveExistingProducts", table );
7087 }
7088
7089 typedef UINT (*STANDARDACTIONHANDLER)(MSIPACKAGE*);
7090
7091 static const struct
7092 {
7093     const WCHAR *action;
7094     UINT (*handler)(MSIPACKAGE *);
7095 }
7096 StandardActions[] =
7097 {
7098     { szAllocateRegistrySpace, ACTION_AllocateRegistrySpace },
7099     { szAppSearch, ACTION_AppSearch },
7100     { szBindImage, ACTION_BindImage },
7101     { szCCPSearch, ACTION_CCPSearch },
7102     { szCostFinalize, ACTION_CostFinalize },
7103     { szCostInitialize, ACTION_CostInitialize },
7104     { szCreateFolders, ACTION_CreateFolders },
7105     { szCreateShortcuts, ACTION_CreateShortcuts },
7106     { szDeleteServices, ACTION_DeleteServices },
7107     { szDisableRollback, ACTION_DisableRollback },
7108     { szDuplicateFiles, ACTION_DuplicateFiles },
7109     { szExecuteAction, ACTION_ExecuteAction },
7110     { szFileCost, ACTION_FileCost },
7111     { szFindRelatedProducts, ACTION_FindRelatedProducts },
7112     { szForceReboot, ACTION_ForceReboot },
7113     { szInstallAdminPackage, ACTION_InstallAdminPackage },
7114     { szInstallExecute, ACTION_InstallExecute },
7115     { szInstallExecuteAgain, ACTION_InstallExecute },
7116     { szInstallFiles, ACTION_InstallFiles},
7117     { szInstallFinalize, ACTION_InstallFinalize },
7118     { szInstallInitialize, ACTION_InstallInitialize },
7119     { szInstallSFPCatalogFile, ACTION_InstallSFPCatalogFile },
7120     { szInstallValidate, ACTION_InstallValidate },
7121     { szIsolateComponents, ACTION_IsolateComponents },
7122     { szLaunchConditions, ACTION_LaunchConditions },
7123     { szMigrateFeatureStates, ACTION_MigrateFeatureStates },
7124     { szMoveFiles, ACTION_MoveFiles },
7125     { szMsiPublishAssemblies, ACTION_MsiPublishAssemblies },
7126     { szMsiUnpublishAssemblies, ACTION_MsiUnpublishAssemblies },
7127     { szInstallODBC, ACTION_InstallODBC },
7128     { szInstallServices, ACTION_InstallServices },
7129     { szPatchFiles, ACTION_PatchFiles },
7130     { szProcessComponents, ACTION_ProcessComponents },
7131     { szPublishComponents, ACTION_PublishComponents },
7132     { szPublishFeatures, ACTION_PublishFeatures },
7133     { szPublishProduct, ACTION_PublishProduct },
7134     { szRegisterClassInfo, ACTION_RegisterClassInfo },
7135     { szRegisterComPlus, ACTION_RegisterComPlus},
7136     { szRegisterExtensionInfo, ACTION_RegisterExtensionInfo },
7137     { szRegisterFonts, ACTION_RegisterFonts },
7138     { szRegisterMIMEInfo, ACTION_RegisterMIMEInfo },
7139     { szRegisterProduct, ACTION_RegisterProduct },
7140     { szRegisterProgIdInfo, ACTION_RegisterProgIdInfo },
7141     { szRegisterTypeLibraries, ACTION_RegisterTypeLibraries },
7142     { szRegisterUser, ACTION_RegisterUser },
7143     { szRemoveDuplicateFiles, ACTION_RemoveDuplicateFiles },
7144     { szRemoveEnvironmentStrings, ACTION_RemoveEnvironmentStrings },
7145     { szRemoveExistingProducts, ACTION_RemoveExistingProducts },
7146     { szRemoveFiles, ACTION_RemoveFiles },
7147     { szRemoveFolders, ACTION_RemoveFolders },
7148     { szRemoveIniValues, ACTION_RemoveIniValues },
7149     { szRemoveODBC, ACTION_RemoveODBC },
7150     { szRemoveRegistryValues, ACTION_RemoveRegistryValues },
7151     { szRemoveShortcuts, ACTION_RemoveShortcuts },
7152     { szResolveSource, ACTION_ResolveSource },
7153     { szRMCCPSearch, ACTION_RMCCPSearch },
7154     { szScheduleReboot, ACTION_ScheduleReboot },
7155     { szSelfRegModules, ACTION_SelfRegModules },
7156     { szSelfUnregModules, ACTION_SelfUnregModules },
7157     { szSetODBCFolders, ACTION_SetODBCFolders },
7158     { szStartServices, ACTION_StartServices },
7159     { szStopServices, ACTION_StopServices },
7160     { szUnpublishComponents, ACTION_UnpublishComponents },
7161     { szUnpublishFeatures, ACTION_UnpublishFeatures },
7162     { szUnregisterClassInfo, ACTION_UnregisterClassInfo },
7163     { szUnregisterComPlus, ACTION_UnregisterComPlus },
7164     { szUnregisterExtensionInfo, ACTION_UnregisterExtensionInfo },
7165     { szUnregisterFonts, ACTION_UnregisterFonts },
7166     { szUnregisterMIMEInfo, ACTION_UnregisterMIMEInfo },
7167     { szUnregisterProgIdInfo, ACTION_UnregisterProgIdInfo },
7168     { szUnregisterTypeLibraries, ACTION_UnregisterTypeLibraries },
7169     { szValidateProductID, ACTION_ValidateProductID },
7170     { szWriteEnvironmentStrings, ACTION_WriteEnvironmentStrings },
7171     { szWriteIniValues, ACTION_WriteIniValues },
7172     { szWriteRegistryValues, ACTION_WriteRegistryValues },
7173     { NULL, NULL },
7174 };
7175
7176 static BOOL ACTION_HandleStandardAction(MSIPACKAGE *package, LPCWSTR action,
7177                                         UINT* rc, BOOL force )
7178 {
7179     BOOL ret = FALSE;
7180     BOOL run = force;
7181     int i;
7182
7183     if (!run && !package->script->CurrentlyScripting)
7184         run = TRUE;
7185
7186     if (!run)
7187     {
7188         if (strcmpW(action,szInstallFinalize) == 0 ||
7189             strcmpW(action,szInstallExecute) == 0 ||
7190             strcmpW(action,szInstallExecuteAgain) == 0)
7191                 run = TRUE;
7192     }
7193
7194     i = 0;
7195     while (StandardActions[i].action != NULL)
7196     {
7197         if (strcmpW(StandardActions[i].action, action)==0)
7198         {
7199             if (!run)
7200             {
7201                 ui_actioninfo(package, action, TRUE, 0);
7202                 *rc = schedule_action(package,INSTALL_SCRIPT,action);
7203                 ui_actioninfo(package, action, FALSE, *rc);
7204             }
7205             else
7206             {
7207                 ui_actionstart(package, action);
7208                 if (StandardActions[i].handler)
7209                 {
7210                     *rc = StandardActions[i].handler(package);
7211                 }
7212                 else
7213                 {
7214                     FIXME("unhandled standard action %s\n",debugstr_w(action));
7215                     *rc = ERROR_SUCCESS;
7216                 }
7217             }
7218             ret = TRUE;
7219             break;
7220         }
7221         i++;
7222     }
7223     return ret;
7224 }
7225
7226 UINT ACTION_PerformAction(MSIPACKAGE *package, const WCHAR *action, UINT script, BOOL force)
7227 {
7228     UINT rc = ERROR_SUCCESS;
7229     BOOL handled;
7230
7231     TRACE("Performing action (%s)\n", debugstr_w(action));
7232
7233     handled = ACTION_HandleStandardAction(package, action, &rc, force);
7234
7235     if (!handled)
7236         handled = ACTION_HandleCustomAction(package, action, &rc, script, force);
7237
7238     if (!handled)
7239     {
7240         WARN("unhandled msi action %s\n", debugstr_w(action));
7241         rc = ERROR_FUNCTION_NOT_CALLED;
7242     }
7243
7244     return rc;
7245 }
7246
7247 UINT ACTION_PerformUIAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
7248 {
7249     UINT rc = ERROR_SUCCESS;
7250     BOOL handled = FALSE;
7251
7252     TRACE("Performing action (%s)\n", debugstr_w(action));
7253
7254     handled = ACTION_HandleStandardAction(package, action, &rc,TRUE);
7255
7256     if (!handled)
7257         handled = ACTION_HandleCustomAction(package, action, &rc, script, FALSE);
7258
7259     if( !handled && ACTION_DialogBox(package, action) == ERROR_SUCCESS )
7260         handled = TRUE;
7261
7262     if (!handled)
7263     {
7264         WARN("unhandled msi action %s\n", debugstr_w(action));
7265         rc = ERROR_FUNCTION_NOT_CALLED;
7266     }
7267
7268     return rc;
7269 }
7270
7271 static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq)
7272 {
7273     UINT rc = ERROR_SUCCESS;
7274     MSIRECORD *row;
7275
7276     static const WCHAR ExecSeqQuery[] =
7277         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7278          '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
7279          'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
7280          '`','S','e','q','u','e','n','c','e','`',' ', '=',' ','%','i',0};
7281     static const WCHAR UISeqQuery[] =
7282         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7283      '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e',
7284      '`', ' ', 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',
7285          ' ', '=',' ','%','i',0};
7286
7287     if (needs_ui_sequence(package))
7288         row = MSI_QueryGetRecord(package->db, UISeqQuery, seq);
7289     else
7290         row = MSI_QueryGetRecord(package->db, ExecSeqQuery, seq);
7291
7292     if (row)
7293     {
7294         LPCWSTR action, cond;
7295
7296         TRACE("Running the actions\n");
7297
7298         /* check conditions */
7299         cond = MSI_RecordGetString(row, 2);
7300
7301         /* this is a hack to skip errors in the condition code */
7302         if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
7303         {
7304             msiobj_release(&row->hdr);
7305             return ERROR_SUCCESS;
7306         }
7307
7308         action = MSI_RecordGetString(row, 1);
7309         if (!action)
7310         {
7311             ERR("failed to fetch action\n");
7312             msiobj_release(&row->hdr);
7313             return ERROR_FUNCTION_FAILED;
7314         }
7315
7316         if (needs_ui_sequence(package))
7317             rc = ACTION_PerformUIAction(package, action, -1);
7318         else
7319             rc = ACTION_PerformAction(package, action, -1, FALSE);
7320
7321         msiobj_release(&row->hdr);
7322     }
7323
7324     return rc;
7325 }
7326
7327 /****************************************************
7328  * TOP level entry points
7329  *****************************************************/
7330
7331 UINT MSI_InstallPackage( MSIPACKAGE *package, LPCWSTR szPackagePath,
7332                          LPCWSTR szCommandLine )
7333 {
7334     UINT rc;
7335     BOOL ui_exists;
7336
7337     static const WCHAR szAction[] = {'A','C','T','I','O','N',0};
7338     static const WCHAR szInstall[] = {'I','N','S','T','A','L','L',0};
7339
7340     msi_set_property( package->db, szAction, szInstall );
7341
7342     package->script->InWhatSequence = SEQUENCE_INSTALL;
7343
7344     if (szPackagePath)
7345     {
7346         LPWSTR p, dir;
7347         LPCWSTR file;
7348
7349         dir = strdupW(szPackagePath);
7350         p = strrchrW(dir, '\\');
7351         if (p)
7352         {
7353             *(++p) = 0;
7354             file = szPackagePath + (p - dir);
7355         }
7356         else
7357         {
7358             msi_free(dir);
7359             dir = msi_alloc(MAX_PATH * sizeof(WCHAR));
7360             GetCurrentDirectoryW(MAX_PATH, dir);
7361             lstrcatW(dir, szBackSlash);
7362             file = szPackagePath;
7363         }
7364
7365         msi_free( package->PackagePath );
7366         package->PackagePath = msi_alloc((lstrlenW(dir) + lstrlenW(file) + 1) * sizeof(WCHAR));
7367         if (!package->PackagePath)
7368         {
7369             msi_free(dir);
7370             return ERROR_OUTOFMEMORY;
7371         }
7372
7373         lstrcpyW(package->PackagePath, dir);
7374         lstrcatW(package->PackagePath, file);
7375         msi_free(dir);
7376
7377         msi_set_sourcedir_props(package, FALSE);
7378     }
7379
7380     msi_parse_command_line( package, szCommandLine, FALSE );
7381
7382     msi_apply_transforms( package );
7383     msi_apply_patches( package );
7384
7385     if (!szCommandLine && msi_get_property_int( package->db, szInstalled, 0 ))
7386     {
7387         TRACE("setting reinstall property\n");
7388         msi_set_property( package->db, szReinstall, szAll );
7389     }
7390
7391     /* properties may have been added by a transform */
7392     msi_clone_properties( package );
7393     msi_set_context( package );
7394
7395     if (needs_ui_sequence( package))
7396     {
7397         package->script->InWhatSequence |= SEQUENCE_UI;
7398         rc = ACTION_ProcessUISequence(package);
7399         ui_exists = ui_sequence_exists(package);
7400         if (rc == ERROR_SUCCESS || !ui_exists)
7401         {
7402             package->script->InWhatSequence |= SEQUENCE_EXEC;
7403             rc = ACTION_ProcessExecSequence(package, ui_exists);
7404         }
7405     }
7406     else
7407         rc = ACTION_ProcessExecSequence(package, FALSE);
7408
7409     package->script->CurrentlyScripting = FALSE;
7410
7411     /* process the ending type action */
7412     if (rc == ERROR_SUCCESS)
7413         ACTION_PerformActionSequence(package, -1);
7414     else if (rc == ERROR_INSTALL_USEREXIT)
7415         ACTION_PerformActionSequence(package, -2);
7416     else if (rc == ERROR_INSTALL_SUSPEND)
7417         ACTION_PerformActionSequence(package, -4);
7418     else  /* failed */
7419         ACTION_PerformActionSequence(package, -3);
7420
7421     /* finish up running custom actions */
7422     ACTION_FinishCustomActions(package);
7423
7424     if (rc == ERROR_SUCCESS && package->need_reboot)
7425         return ERROR_SUCCESS_REBOOT_REQUIRED;
7426
7427     return rc;
7428 }