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