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