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