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