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