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