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