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