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