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