msi: Fail if a registry key cannot be created.
[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 static LPSTR parse_value(MSIPACKAGE *package, LPCWSTR value, DWORD *type, DWORD *size)
2394 {
2395     LPSTR data = NULL;
2396
2397     if (!value)
2398     {
2399         data = (LPSTR)strdupW(szEmpty);
2400         *size = sizeof(szEmpty);
2401         *type = REG_SZ;
2402         return data;
2403     }
2404     if (value[0]=='#' && value[1]!='#' && value[1]!='%')
2405     {
2406         if (value[1]=='x')
2407         {
2408             LPWSTR ptr;
2409             CHAR byte[5];
2410             LPWSTR deformated = NULL;
2411             int count;
2412
2413             deformat_string(package, &value[2], &deformated);
2414
2415             /* binary value type */
2416             ptr = deformated;
2417             *type = REG_BINARY;
2418             if (strlenW(ptr)%2)
2419                 *size = (strlenW(ptr)/2)+1;
2420             else
2421                 *size = strlenW(ptr)/2;
2422
2423             data = msi_alloc(*size);
2424
2425             byte[0] = '0'; 
2426             byte[1] = 'x'; 
2427             byte[4] = 0; 
2428             count = 0;
2429             /* if uneven pad with a zero in front */
2430             if (strlenW(ptr)%2)
2431             {
2432                 byte[2]= '0';
2433                 byte[3]= *ptr;
2434                 ptr++;
2435                 data[count] = (BYTE)strtol(byte,NULL,0);
2436                 count ++;
2437                 TRACE("Uneven byte count\n");
2438             }
2439             while (*ptr)
2440             {
2441                 byte[2]= *ptr;
2442                 ptr++;
2443                 byte[3]= *ptr;
2444                 ptr++;
2445                 data[count] = (BYTE)strtol(byte,NULL,0);
2446                 count ++;
2447             }
2448             msi_free(deformated);
2449
2450             TRACE("Data %i bytes(%i)\n",*size,count);
2451         }
2452         else
2453         {
2454             LPWSTR deformated;
2455             LPWSTR p;
2456             DWORD d = 0;
2457             deformat_string(package, &value[1], &deformated);
2458
2459             *type=REG_DWORD; 
2460             *size = sizeof(DWORD);
2461             data = msi_alloc(*size);
2462             p = deformated;
2463             if (*p == '-')
2464                 p++;
2465             while (*p)
2466             {
2467                 if ( (*p < '0') || (*p > '9') )
2468                     break;
2469                 d *= 10;
2470                 d += (*p - '0');
2471                 p++;
2472             }
2473             if (deformated[0] == '-')
2474                 d = -d;
2475             *(LPDWORD)data = d;
2476             TRACE("DWORD %i\n",*(LPDWORD)data);
2477
2478             msi_free(deformated);
2479         }
2480     }
2481     else
2482     {
2483         static const WCHAR szMulti[] = {'[','~',']',0};
2484         LPCWSTR ptr;
2485         *type=REG_SZ;
2486
2487         if (value[0]=='#')
2488         {
2489             if (value[1]=='%')
2490             {
2491                 ptr = &value[2];
2492                 *type=REG_EXPAND_SZ;
2493             }
2494             else
2495                 ptr = &value[1];
2496          }
2497          else
2498             ptr=value;
2499
2500         if (strstrW(value, szMulti))
2501             *type = REG_MULTI_SZ;
2502
2503         /* remove initial delimiter */
2504         if (!strncmpW(value, szMulti, 3))
2505             ptr = value + 3;
2506
2507         *size = deformat_string(package, ptr,(LPWSTR*)&data);
2508
2509         /* add double NULL terminator */
2510         if (*type == REG_MULTI_SZ)
2511         {
2512             *size += 2 * sizeof(WCHAR); /* two NULL terminators */
2513             data = msi_realloc_zero(data, *size);
2514         }
2515     }
2516     return data;
2517 }
2518
2519 static const WCHAR *get_root_key( MSIPACKAGE *package, INT root, HKEY *root_key )
2520 {
2521     const WCHAR *ret;
2522
2523     switch (root)
2524     {
2525     case -1:
2526         if (msi_get_property_int( package->db, szAllUsers, 0 ))
2527         {
2528             *root_key = HKEY_LOCAL_MACHINE;
2529             ret = szHLM;
2530         }
2531         else
2532         {
2533             *root_key = HKEY_CURRENT_USER;
2534             ret = szHCU;
2535         }
2536         break;
2537     case 0:
2538         *root_key = HKEY_CLASSES_ROOT;
2539         ret = szHCR;
2540         break;
2541     case 1:
2542         *root_key = HKEY_CURRENT_USER;
2543         ret = szHCU;
2544         break;
2545     case 2:
2546         *root_key = HKEY_LOCAL_MACHINE;
2547         ret = szHLM;
2548         break;
2549     case 3:
2550         *root_key = HKEY_USERS;
2551         ret = szHU;
2552         break;
2553     default:
2554         ERR("Unknown root %i\n", root);
2555         return NULL;
2556     }
2557
2558     return ret;
2559 }
2560
2561 static WCHAR *get_keypath( MSICOMPONENT *comp, HKEY root, const WCHAR *path )
2562 {
2563     static const WCHAR prefixW[] = {'S','O','F','T','W','A','R','E','\\'};
2564     static const UINT len = sizeof(prefixW) / sizeof(prefixW[0]);
2565
2566     if ((is_64bit || is_wow64) &&
2567         !(comp->Attributes & msidbComponentAttributes64bit) &&
2568         root == HKEY_LOCAL_MACHINE && !strncmpiW( path, prefixW, len ))
2569     {
2570         UINT size;
2571         WCHAR *path_32node;
2572
2573         size = (strlenW( path ) + strlenW( szWow6432Node ) + 2) * sizeof(WCHAR);
2574         if (!(path_32node = msi_alloc( size ))) return NULL;
2575
2576         memcpy( path_32node, path, len * sizeof(WCHAR) );
2577         strcpyW( path_32node + len, szWow6432Node );
2578         strcatW( path_32node, szBackSlash );
2579         strcatW( path_32node, path + len );
2580         return path_32node;
2581     }
2582     return strdupW( path );
2583 }
2584
2585 static BOOL is_special_entry( const WCHAR *name )
2586 {
2587      return (name && (name[0] == '*' || name[0] == '+') && !name[1]);
2588 }
2589
2590 static UINT ITERATE_WriteRegistryValues(MSIRECORD *row, LPVOID param)
2591 {
2592     MSIPACKAGE *package = param;
2593     LPSTR value;
2594     HKEY  root_key, hkey;
2595     DWORD type,size;
2596     LPWSTR deformated, uikey, keypath;
2597     LPCWSTR szRoot, component, name, key;
2598     MSICOMPONENT *comp;
2599     MSIRECORD * uirow;
2600     INT   root;
2601     BOOL check_first = FALSE;
2602     UINT rc;
2603
2604     msi_ui_progress( package, 2, REG_PROGRESS_VALUE, 0, 0 );
2605
2606     component = MSI_RecordGetString(row, 6);
2607     comp = msi_get_loaded_component(package,component);
2608     if (!comp)
2609         return ERROR_SUCCESS;
2610
2611     comp->Action = msi_get_component_action( package, comp );
2612     if (comp->Action != INSTALLSTATE_LOCAL)
2613     {
2614         TRACE("component not scheduled for installation %s\n", debugstr_w(component));
2615         return ERROR_SUCCESS;
2616     }
2617
2618     name = MSI_RecordGetString(row, 4);
2619     if( MSI_RecordIsNull(row,5) && name )
2620     {
2621         /* null values can have special meanings */
2622         if (name[0]=='-' && name[1] == 0)
2623                 return ERROR_SUCCESS;
2624         if ((name[0] == '+' || name[0] == '*') && !name[1])
2625             check_first = TRUE;
2626     }
2627
2628     root = MSI_RecordGetInteger(row,2);
2629     key = MSI_RecordGetString(row, 3);
2630
2631     szRoot = get_root_key( package, root, &root_key );
2632     if (!szRoot)
2633         return ERROR_SUCCESS;
2634
2635     deformat_string(package, key , &deformated);
2636     size = strlenW(deformated) + strlenW(szRoot) + 1;
2637     uikey = msi_alloc(size*sizeof(WCHAR));
2638     strcpyW(uikey,szRoot);
2639     strcatW(uikey,deformated);
2640
2641     keypath = get_keypath( comp, root_key, deformated );
2642     msi_free( deformated );
2643     if (RegCreateKeyExW( root_key, keypath, 0, NULL, 0, KEY_ALL_ACCESS|KEY_WOW64_64KEY, NULL, &hkey, NULL ))
2644     {
2645         ERR("Could not create key %s\n", debugstr_w(keypath));
2646         msi_free(uikey);
2647         msi_free(keypath);
2648         return ERROR_FUNCTION_FAILED;
2649     }
2650     value = parse_value(package, MSI_RecordGetString(row, 5), &type, &size);
2651     deformat_string(package, name, &deformated);
2652
2653     if (!is_special_entry( name ))
2654     {
2655         if (!check_first)
2656         {
2657             TRACE("Setting value %s of %s\n", debugstr_w(deformated),
2658                   debugstr_w(uikey));
2659             RegSetValueExW(hkey, deformated, 0, type, (LPBYTE)value, size);
2660         }
2661         else
2662         {
2663             DWORD sz = 0;
2664             rc = RegQueryValueExW(hkey, deformated, NULL, NULL, NULL, &sz);
2665             if (rc == ERROR_SUCCESS || rc == ERROR_MORE_DATA)
2666             {
2667                 TRACE("value %s of %s checked already exists\n", debugstr_w(deformated),
2668                       debugstr_w(uikey));
2669             }
2670             else
2671             {
2672                 TRACE("Checked and setting value %s of %s\n", debugstr_w(deformated),
2673                       debugstr_w(uikey));
2674                 if (deformated || size)
2675                     RegSetValueExW(hkey, deformated, 0, type, (LPBYTE)value, size);
2676             }
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);
2686     msi_ui_actiondata( package, szWriteRegistryValues, uirow );
2687     msiobj_release( &uirow->hdr );
2688
2689     msi_free(value);
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 = RegOpenKeyExW( root, keypath, 0, KEY_ALL_ACCESS|KEY_WOW64_64KEY, &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             RegDeleteKeyExW( root, keypath, KEY_WOW64_64KEY, 0 );
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     HKEY hkey;
2742     LONG res = RegOpenKeyExW( root, keypath, 0, KEY_ALL_ACCESS|KEY_WOW64_64KEY, &hkey );
2743     if (res)
2744     {
2745         TRACE("failed to open key %s (%d)\n", debugstr_w(keypath), res);
2746         return;
2747     }
2748     res = RegDeleteTreeW( hkey, NULL );
2749     if (res) TRACE("failed to delete subtree of %s (%d)\n", debugstr_w(keypath), res);
2750     res = RegDeleteKeyExW( root, keypath, KEY_WOW64_64KEY, 0 );
2751     if (res) TRACE("failed to delete key %s (%d)\n", debugstr_w(keypath), res);
2752     RegCloseKey( hkey );
2753 }
2754
2755 static UINT ITERATE_RemoveRegistryValuesOnUninstall( MSIRECORD *row, LPVOID param )
2756 {
2757     MSIPACKAGE *package = param;
2758     LPCWSTR component, name, key_str, root_key_str;
2759     LPWSTR deformated_key, deformated_name, ui_key_str, keypath;
2760     MSICOMPONENT *comp;
2761     MSIRECORD *uirow;
2762     BOOL delete_key = FALSE;
2763     HKEY hkey_root;
2764     UINT size;
2765     INT root;
2766
2767     msi_ui_progress( package, 2, REG_PROGRESS_VALUE, 0, 0 );
2768
2769     component = MSI_RecordGetString( row, 6 );
2770     comp = msi_get_loaded_component( package, component );
2771     if (!comp)
2772         return ERROR_SUCCESS;
2773
2774     comp->Action = msi_get_component_action( package, comp );
2775     if (comp->Action != INSTALLSTATE_ABSENT)
2776     {
2777         TRACE("component not scheduled for removal %s\n", debugstr_w(component));
2778         return ERROR_SUCCESS;
2779     }
2780
2781     name = MSI_RecordGetString( row, 4 );
2782     if (MSI_RecordIsNull( row, 5 ) && name )
2783     {
2784         if (name[0] == '+' && !name[1])
2785             return ERROR_SUCCESS;
2786         if ((name[0] == '-' || name[0] == '*') && !name[1])
2787         {
2788             delete_key = TRUE;
2789             name = NULL;
2790         }
2791     }
2792
2793     root = MSI_RecordGetInteger( row, 2 );
2794     key_str = MSI_RecordGetString( row, 3 );
2795
2796     root_key_str = get_root_key( package, root, &hkey_root );
2797     if (!root_key_str)
2798         return ERROR_SUCCESS;
2799
2800     deformat_string( package, key_str, &deformated_key );
2801     size = strlenW( deformated_key ) + strlenW( root_key_str ) + 1;
2802     ui_key_str = msi_alloc( size * sizeof(WCHAR) );
2803     strcpyW( ui_key_str, root_key_str );
2804     strcatW( ui_key_str, deformated_key );
2805
2806     deformat_string( package, name, &deformated_name );
2807
2808     keypath = get_keypath( comp, hkey_root, deformated_key );
2809     msi_free( deformated_key );
2810     if (delete_key) delete_reg_key( hkey_root, keypath );
2811     else delete_reg_value( hkey_root, keypath, deformated_name );
2812     msi_free( keypath );
2813
2814     uirow = MSI_CreateRecord( 2 );
2815     MSI_RecordSetStringW( uirow, 1, ui_key_str );
2816     MSI_RecordSetStringW( uirow, 2, deformated_name );
2817     msi_ui_actiondata( package, szRemoveRegistryValues, uirow );
2818     msiobj_release( &uirow->hdr );
2819
2820     msi_free( ui_key_str );
2821     msi_free( deformated_name );
2822     return ERROR_SUCCESS;
2823 }
2824
2825 static UINT ITERATE_RemoveRegistryValuesOnInstall( MSIRECORD *row, LPVOID param )
2826 {
2827     MSIPACKAGE *package = param;
2828     LPCWSTR component, name, key_str, root_key_str;
2829     LPWSTR deformated_key, deformated_name, ui_key_str, keypath;
2830     MSICOMPONENT *comp;
2831     MSIRECORD *uirow;
2832     BOOL delete_key = FALSE;
2833     HKEY hkey_root;
2834     UINT size;
2835     INT root;
2836
2837     component = MSI_RecordGetString( row, 5 );
2838     comp = msi_get_loaded_component( package, component );
2839     if (!comp)
2840         return ERROR_SUCCESS;
2841
2842     comp->Action = msi_get_component_action( package, comp );
2843     if (comp->Action != INSTALLSTATE_LOCAL)
2844     {
2845         TRACE("component not scheduled for installation %s\n", debugstr_w(component));
2846         return ERROR_SUCCESS;
2847     }
2848
2849     if ((name = MSI_RecordGetString( row, 4 )))
2850     {
2851         if (name[0] == '-' && !name[1])
2852         {
2853             delete_key = TRUE;
2854             name = NULL;
2855         }
2856     }
2857
2858     root = MSI_RecordGetInteger( row, 2 );
2859     key_str = MSI_RecordGetString( row, 3 );
2860
2861     root_key_str = get_root_key( package, root, &hkey_root );
2862     if (!root_key_str)
2863         return ERROR_SUCCESS;
2864
2865     deformat_string( package, key_str, &deformated_key );
2866     size = strlenW( deformated_key ) + strlenW( root_key_str ) + 1;
2867     ui_key_str = msi_alloc( size * sizeof(WCHAR) );
2868     strcpyW( ui_key_str, root_key_str );
2869     strcatW( ui_key_str, deformated_key );
2870
2871     deformat_string( package, name, &deformated_name );
2872
2873     keypath = get_keypath( comp, hkey_root, deformated_key );
2874     msi_free( deformated_key );
2875     if (delete_key) delete_reg_key( hkey_root, keypath );
2876     else delete_reg_value( hkey_root, keypath, deformated_name );
2877     msi_free( keypath );
2878
2879     uirow = MSI_CreateRecord( 2 );
2880     MSI_RecordSetStringW( uirow, 1, ui_key_str );
2881     MSI_RecordSetStringW( uirow, 2, deformated_name );
2882     msi_ui_actiondata( package, szRemoveRegistryValues, uirow );
2883     msiobj_release( &uirow->hdr );
2884
2885     msi_free( ui_key_str );
2886     msi_free( deformated_name );
2887     return ERROR_SUCCESS;
2888 }
2889
2890 static UINT ACTION_RemoveRegistryValues( MSIPACKAGE *package )
2891 {
2892     static const WCHAR registry_query[] = {
2893         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2894         '`','R','e','g','i','s','t','r','y','`',0};
2895     static const WCHAR remove_registry_query[] = {
2896         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2897         '`','R','e','m','o','v','e','R','e','g','i','s','t','r','y','`',0};
2898     MSIQUERY *view;
2899     UINT rc;
2900
2901     rc = MSI_DatabaseOpenViewW( package->db, registry_query, &view );
2902     if (rc == ERROR_SUCCESS)
2903     {
2904         rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveRegistryValuesOnUninstall, package );
2905         msiobj_release( &view->hdr );
2906         if (rc != ERROR_SUCCESS)
2907             return rc;
2908     }
2909     rc = MSI_DatabaseOpenViewW( package->db, remove_registry_query, &view );
2910     if (rc == ERROR_SUCCESS)
2911     {
2912         rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveRegistryValuesOnInstall, package );
2913         msiobj_release( &view->hdr );
2914         if (rc != ERROR_SUCCESS)
2915             return rc;
2916     }
2917     return ERROR_SUCCESS;
2918 }
2919
2920 static UINT ACTION_InstallInitialize(MSIPACKAGE *package)
2921 {
2922     package->script->CurrentlyScripting = TRUE;
2923
2924     return ERROR_SUCCESS;
2925 }
2926
2927
2928 static UINT ACTION_InstallValidate(MSIPACKAGE *package)
2929 {
2930     static const WCHAR query[]= {
2931         'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
2932         '`','R','e','g','i','s','t','r','y','`',0};
2933     MSICOMPONENT *comp;
2934     DWORD total = 0, count = 0;
2935     MSIQUERY *view;
2936     MSIFEATURE *feature;
2937     MSIFILE *file;
2938     UINT rc;
2939
2940     TRACE("InstallValidate\n");
2941
2942     rc = MSI_DatabaseOpenViewW( package->db, query, &view );
2943     if (rc == ERROR_SUCCESS)
2944     {
2945         rc = MSI_IterateRecords( view, &count, NULL, package );
2946         msiobj_release( &view->hdr );
2947         if (rc != ERROR_SUCCESS)
2948             return rc;
2949         total += count * REG_PROGRESS_VALUE;
2950     }
2951     LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2952         total += COMPONENT_PROGRESS_VALUE;
2953
2954     LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2955         total += file->FileSize;
2956
2957     msi_ui_progress( package, 0, total, 0, 0 );
2958
2959     LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2960     {
2961         TRACE("Feature: %s Installed %d Request %d Action %d\n",
2962               debugstr_w(feature->Feature), feature->Installed,
2963               feature->ActionRequest, feature->Action);
2964     }
2965     return ERROR_SUCCESS;
2966 }
2967
2968 static UINT ITERATE_LaunchConditions(MSIRECORD *row, LPVOID param)
2969 {
2970     MSIPACKAGE* package = param;
2971     LPCWSTR cond = NULL; 
2972     LPCWSTR message = NULL;
2973     UINT r;
2974
2975     static const WCHAR title[]=
2976         {'I','n','s','t','a','l','l',' ','F','a', 'i','l','e','d',0};
2977
2978     cond = MSI_RecordGetString(row,1);
2979
2980     r = MSI_EvaluateConditionW(package,cond);
2981     if (r == MSICONDITION_FALSE)
2982     {
2983         if ((package->ui_level & INSTALLUILEVEL_MASK) != INSTALLUILEVEL_NONE)
2984         {
2985             LPWSTR deformated;
2986             message = MSI_RecordGetString(row,2);
2987             deformat_string(package,message,&deformated);
2988             MessageBoxW(NULL,deformated,title,MB_OK);
2989             msi_free(deformated);
2990         }
2991
2992         return ERROR_INSTALL_FAILURE;
2993     }
2994
2995     return ERROR_SUCCESS;
2996 }
2997
2998 static UINT ACTION_LaunchConditions(MSIPACKAGE *package)
2999 {
3000     static const WCHAR query[] = {
3001         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3002         '`','L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','`',0};
3003     MSIQUERY *view;
3004     UINT rc;
3005
3006     TRACE("Checking launch conditions\n");
3007
3008     rc = MSI_DatabaseOpenViewW(package->db, query, &view);
3009     if (rc != ERROR_SUCCESS)
3010         return ERROR_SUCCESS;
3011
3012     rc = MSI_IterateRecords(view, NULL, ITERATE_LaunchConditions, package);
3013     msiobj_release(&view->hdr);
3014     return rc;
3015 }
3016
3017 static LPWSTR resolve_keypath( MSIPACKAGE* package, MSICOMPONENT *cmp )
3018 {
3019
3020     if (!cmp->KeyPath)
3021         return strdupW( msi_get_target_folder( package, cmp->Directory ) );
3022
3023     if (cmp->Attributes & msidbComponentAttributesRegistryKeyPath)
3024     {
3025         static const WCHAR query[] = {
3026             'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3027             '`','R','e','g','i','s','t','r','y','`',' ','W','H','E','R','E',' ',
3028             '`','R','e','g','i','s','t','r','y','`',' ','=',' ' ,'\'','%','s','\'',0};
3029         static const WCHAR fmt[] = {'%','0','2','i',':','\\','%','s','\\',0};
3030         static const WCHAR fmt2[]= {'%','0','2','i',':','\\','%','s','\\','%','s',0};
3031         MSIRECORD *row;
3032         UINT root, len;
3033         LPWSTR deformated, buffer, deformated_name;
3034         LPCWSTR key, name;
3035
3036         row = MSI_QueryGetRecord(package->db, query, cmp->KeyPath);
3037         if (!row)
3038             return NULL;
3039
3040         root = MSI_RecordGetInteger(row,2);
3041         key = MSI_RecordGetString(row, 3);
3042         name = MSI_RecordGetString(row, 4);
3043         deformat_string(package, key , &deformated);
3044         deformat_string(package, name, &deformated_name);
3045
3046         len = strlenW(deformated) + 6;
3047         if (deformated_name)
3048             len+=strlenW(deformated_name);
3049
3050         buffer = msi_alloc( len *sizeof(WCHAR));
3051
3052         if (deformated_name)
3053             sprintfW(buffer,fmt2,root,deformated,deformated_name);
3054         else
3055             sprintfW(buffer,fmt,root,deformated);
3056
3057         msi_free(deformated);
3058         msi_free(deformated_name);
3059         msiobj_release(&row->hdr);
3060
3061         return buffer;
3062     }
3063     else if (cmp->Attributes & msidbComponentAttributesODBCDataSource)
3064     {
3065         FIXME("UNIMPLEMENTED keypath as ODBC Source\n");
3066         return NULL;
3067     }
3068     else
3069     {
3070         MSIFILE *file = msi_get_loaded_file( package, cmp->KeyPath );
3071
3072         if (file)
3073             return strdupW( file->TargetPath );
3074     }
3075     return NULL;
3076 }
3077
3078 static HKEY openSharedDLLsKey(void)
3079 {
3080     HKEY hkey=0;
3081     static const WCHAR path[] =
3082         {'S','o','f','t','w','a','r','e','\\',
3083          'M','i','c','r','o','s','o','f','t','\\',
3084          'W','i','n','d','o','w','s','\\',
3085          'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
3086          'S','h','a','r','e','d','D','L','L','s',0};
3087
3088     RegCreateKeyW(HKEY_LOCAL_MACHINE,path,&hkey);
3089     return hkey;
3090 }
3091
3092 static UINT ACTION_GetSharedDLLsCount(LPCWSTR dll)
3093 {
3094     HKEY hkey;
3095     DWORD count=0;
3096     DWORD type;
3097     DWORD sz = sizeof(count);
3098     DWORD rc;
3099     
3100     hkey = openSharedDLLsKey();
3101     rc = RegQueryValueExW(hkey, dll, NULL, &type, (LPBYTE)&count, &sz);
3102     if (rc != ERROR_SUCCESS)
3103         count = 0;
3104     RegCloseKey(hkey);
3105     return count;
3106 }
3107
3108 static UINT ACTION_WriteSharedDLLsCount(LPCWSTR path, UINT count)
3109 {
3110     HKEY hkey;
3111
3112     hkey = openSharedDLLsKey();
3113     if (count > 0)
3114         msi_reg_set_val_dword( hkey, path, count );
3115     else
3116         RegDeleteValueW(hkey,path);
3117     RegCloseKey(hkey);
3118     return count;
3119 }
3120
3121 static void ACTION_RefCountComponent( MSIPACKAGE* package, MSICOMPONENT *comp )
3122 {
3123     MSIFEATURE *feature;
3124     INT count = 0;
3125     BOOL write = FALSE;
3126
3127     /* only refcount DLLs */
3128     if (comp->KeyPath == NULL || 
3129         comp->assembly ||
3130         comp->Attributes & msidbComponentAttributesRegistryKeyPath || 
3131         comp->Attributes & msidbComponentAttributesODBCDataSource)
3132         write = FALSE;
3133     else
3134     {
3135         count = ACTION_GetSharedDLLsCount( comp->FullKeypath);
3136         write = (count > 0);
3137
3138         if (comp->Attributes & msidbComponentAttributesSharedDllRefCount)
3139             write = TRUE;
3140     }
3141
3142     /* increment counts */
3143     LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3144     {
3145         ComponentList *cl;
3146
3147         if (msi_get_feature_action( package, feature ) != INSTALLSTATE_LOCAL)
3148             continue;
3149
3150         LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3151         {
3152             if ( cl->component == comp )
3153                 count++;
3154         }
3155     }
3156
3157     /* decrement counts */
3158     LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3159     {
3160         ComponentList *cl;
3161
3162         if (msi_get_feature_action( package, feature ) != INSTALLSTATE_ABSENT)
3163             continue;
3164
3165         LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3166         {
3167             if ( cl->component == comp )
3168                 count--;
3169         }
3170     }
3171
3172     /* ref count all the files in the component */
3173     if (write)
3174     {
3175         MSIFILE *file;
3176
3177         LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
3178         {
3179             if (file->Component == comp)
3180                 ACTION_WriteSharedDLLsCount( file->TargetPath, count );
3181         }
3182     }
3183     
3184     /* add a count for permanent */
3185     if (comp->Attributes & msidbComponentAttributesPermanent)
3186         count ++;
3187     
3188     comp->RefCount = count;
3189
3190     if (write)
3191         ACTION_WriteSharedDLLsCount( comp->FullKeypath, comp->RefCount );
3192 }
3193
3194 static WCHAR *build_full_keypath( MSIPACKAGE *package, MSICOMPONENT *comp )
3195 {
3196     if (comp->assembly)
3197     {
3198         const WCHAR prefixW[] = {'<','\\',0};
3199         DWORD len = strlenW( prefixW ) + strlenW( comp->assembly->display_name );
3200         WCHAR *keypath = msi_alloc( (len + 1) * sizeof(WCHAR) );
3201
3202         if (keypath)
3203         {
3204             strcpyW( keypath, prefixW );
3205             strcatW( keypath, comp->assembly->display_name );
3206         }
3207         return keypath;
3208     }
3209     return resolve_keypath( package, comp );
3210 }
3211
3212 static UINT ACTION_ProcessComponents(MSIPACKAGE *package)
3213 {
3214     WCHAR squished_pc[GUID_SIZE], squished_cc[GUID_SIZE];
3215     UINT rc;
3216     MSICOMPONENT *comp;
3217     HKEY hkey;
3218
3219     TRACE("\n");
3220
3221     squash_guid(package->ProductCode,squished_pc);
3222     msi_set_sourcedir_props(package, FALSE);
3223
3224     LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
3225     {
3226         MSIRECORD *uirow;
3227         INSTALLSTATE action;
3228
3229         msi_ui_progress( package, 2, COMPONENT_PROGRESS_VALUE, 0, 0 );
3230         if (!comp->ComponentId)
3231             continue;
3232
3233         squash_guid( comp->ComponentId, squished_cc );
3234         msi_free( comp->FullKeypath );
3235         comp->FullKeypath = build_full_keypath( package, comp );
3236
3237         ACTION_RefCountComponent( package, comp );
3238
3239         if (package->need_rollback) action = comp->Installed;
3240         else action = comp->ActionRequest;
3241
3242         TRACE("Component %s (%s), Keypath=%s, RefCount=%u Action=%u\n",
3243                             debugstr_w(comp->Component), debugstr_w(squished_cc),
3244                             debugstr_w(comp->FullKeypath), comp->RefCount, action);
3245
3246         if (action == INSTALLSTATE_LOCAL || action == INSTALLSTATE_SOURCE)
3247         {
3248             if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3249                 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, szLocalSid, &hkey, TRUE);
3250             else
3251                 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, NULL, &hkey, TRUE);
3252
3253             if (rc != ERROR_SUCCESS)
3254                 continue;
3255
3256             if (comp->Attributes & msidbComponentAttributesPermanent)
3257             {
3258                 static const WCHAR szPermKey[] =
3259                     { '0','0','0','0','0','0','0','0','0','0','0','0',
3260                       '0','0','0','0','0','0','0','0','0','0','0','0',
3261                       '0','0','0','0','0','0','0','0',0 };
3262
3263                 msi_reg_set_val_str(hkey, szPermKey, comp->FullKeypath);
3264             }
3265             if (action == INSTALLSTATE_LOCAL)
3266                 msi_reg_set_val_str(hkey, squished_pc, comp->FullKeypath);
3267             else
3268             {
3269                 MSIFILE *file;
3270                 MSIRECORD *row;
3271                 LPWSTR ptr, ptr2;
3272                 WCHAR source[MAX_PATH];
3273                 WCHAR base[MAX_PATH];
3274                 LPWSTR sourcepath;
3275
3276                 static const WCHAR fmt[] = {'%','0','2','d','\\',0};
3277                 static const WCHAR query[] = {
3278                     'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
3279                     '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
3280                     '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ',
3281                     '>','=',' ','%','i',' ','O','R','D','E','R',' ','B','Y',' ',
3282                     '`','D','i','s','k','I','d','`',0};
3283
3284                 if (!comp->KeyPath || !(file = msi_get_loaded_file(package, comp->KeyPath)))
3285                     continue;
3286
3287                 row = MSI_QueryGetRecord(package->db, query, file->Sequence);
3288                 sprintfW(source, fmt, MSI_RecordGetInteger(row, 1));
3289                 ptr2 = strrchrW(source, '\\') + 1;
3290                 msiobj_release(&row->hdr);
3291
3292                 lstrcpyW(base, package->PackagePath);
3293                 ptr = strrchrW(base, '\\');
3294                 *(ptr + 1) = '\0';
3295
3296                 sourcepath = msi_resolve_file_source(package, file);
3297                 ptr = sourcepath + lstrlenW(base);
3298                 lstrcpyW(ptr2, ptr);
3299                 msi_free(sourcepath);
3300
3301                 msi_reg_set_val_str(hkey, squished_pc, source);
3302             }
3303             RegCloseKey(hkey);
3304         }
3305         else if (action == INSTALLSTATE_ABSENT)
3306         {
3307             if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3308                 MSIREG_DeleteUserDataComponentKey(comp->ComponentId, szLocalSid);
3309             else
3310                 MSIREG_DeleteUserDataComponentKey(comp->ComponentId, NULL);
3311         }
3312
3313         /* UI stuff */
3314         uirow = MSI_CreateRecord(3);
3315         MSI_RecordSetStringW(uirow,1,package->ProductCode);
3316         MSI_RecordSetStringW(uirow,2,comp->ComponentId);
3317         MSI_RecordSetStringW(uirow,3,comp->FullKeypath);
3318         msi_ui_actiondata( package, szProcessComponents, uirow );
3319         msiobj_release( &uirow->hdr );
3320     }
3321     return ERROR_SUCCESS;
3322 }
3323
3324 typedef struct {
3325     CLSID       clsid;
3326     LPWSTR      source;
3327
3328     LPWSTR      path;
3329     ITypeLib    *ptLib;
3330 } typelib_struct;
3331
3332 static BOOL CALLBACK Typelib_EnumResNameProc( HMODULE hModule, LPCWSTR lpszType, 
3333                                        LPWSTR lpszName, LONG_PTR lParam)
3334 {
3335     TLIBATTR *attr;
3336     typelib_struct *tl_struct = (typelib_struct*) lParam;
3337     static const WCHAR fmt[] = {'%','s','\\','%','i',0};
3338     int sz; 
3339     HRESULT res;
3340
3341     if (!IS_INTRESOURCE(lpszName))
3342     {
3343         ERR("Not Int Resource Name %s\n",debugstr_w(lpszName));
3344         return TRUE;
3345     }
3346
3347     sz = strlenW(tl_struct->source)+4;
3348     sz *= sizeof(WCHAR);
3349
3350     if ((INT_PTR)lpszName == 1)
3351         tl_struct->path = strdupW(tl_struct->source);
3352     else
3353     {
3354         tl_struct->path = msi_alloc(sz);
3355         sprintfW(tl_struct->path,fmt,tl_struct->source, lpszName);
3356     }
3357
3358     TRACE("trying %s\n", debugstr_w(tl_struct->path));
3359     res = LoadTypeLib(tl_struct->path,&tl_struct->ptLib);
3360     if (FAILED(res))
3361     {
3362         msi_free(tl_struct->path);
3363         tl_struct->path = NULL;
3364
3365         return TRUE;
3366     }
3367
3368     ITypeLib_GetLibAttr(tl_struct->ptLib, &attr);
3369     if (IsEqualGUID(&(tl_struct->clsid),&(attr->guid)))
3370     {
3371         ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
3372         return FALSE;
3373     }
3374
3375     msi_free(tl_struct->path);
3376     tl_struct->path = NULL;
3377
3378     ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
3379     ITypeLib_Release(tl_struct->ptLib);
3380
3381     return TRUE;
3382 }
3383
3384 static UINT ITERATE_RegisterTypeLibraries(MSIRECORD *row, LPVOID param)
3385 {
3386     MSIPACKAGE* package = param;
3387     LPCWSTR component;
3388     MSICOMPONENT *comp;
3389     MSIFILE *file;
3390     typelib_struct tl_struct;
3391     ITypeLib *tlib;
3392     HMODULE module;
3393     HRESULT hr;
3394
3395     component = MSI_RecordGetString(row,3);
3396     comp = msi_get_loaded_component(package,component);
3397     if (!comp)
3398         return ERROR_SUCCESS;
3399
3400     comp->Action = msi_get_component_action( package, comp );
3401     if (comp->Action != INSTALLSTATE_LOCAL)
3402     {
3403         TRACE("component not scheduled for installation %s\n", debugstr_w(component));
3404         return ERROR_SUCCESS;
3405     }
3406
3407     if (!comp->KeyPath || !(file = msi_get_loaded_file( package, comp->KeyPath )))
3408     {
3409         TRACE("component has no key path\n");
3410         return ERROR_SUCCESS;
3411     }
3412     msi_ui_actiondata( package, szRegisterTypeLibraries, row );
3413
3414     module = LoadLibraryExW( file->TargetPath, NULL, LOAD_LIBRARY_AS_DATAFILE );
3415     if (module)
3416     {
3417         LPCWSTR guid;
3418         guid = MSI_RecordGetString(row,1);
3419         CLSIDFromString( guid, &tl_struct.clsid);
3420         tl_struct.source = strdupW( file->TargetPath );
3421         tl_struct.path = NULL;
3422
3423         EnumResourceNamesW(module, szTYPELIB, Typelib_EnumResNameProc,
3424                         (LONG_PTR)&tl_struct);
3425
3426         if (tl_struct.path)
3427         {
3428             LPCWSTR helpid, help_path = NULL;
3429             HRESULT res;
3430
3431             helpid = MSI_RecordGetString(row,6);
3432
3433             if (helpid) help_path = msi_get_target_folder( package, helpid );
3434             res = RegisterTypeLib( tl_struct.ptLib, tl_struct.path, (OLECHAR *)help_path );
3435
3436             if (FAILED(res))
3437                 ERR("Failed to register type library %s\n", debugstr_w(tl_struct.path));
3438             else
3439                 TRACE("Registered %s\n", debugstr_w(tl_struct.path));
3440
3441             ITypeLib_Release(tl_struct.ptLib);
3442             msi_free(tl_struct.path);
3443         }
3444         else ERR("Failed to load type library %s\n", debugstr_w(tl_struct.source));
3445
3446         FreeLibrary(module);
3447         msi_free(tl_struct.source);
3448     }
3449     else
3450     {
3451         hr = LoadTypeLibEx(file->TargetPath, REGKIND_REGISTER, &tlib);
3452         if (FAILED(hr))
3453         {
3454             ERR("Failed to load type library: %08x\n", hr);
3455             return ERROR_INSTALL_FAILURE;
3456         }
3457
3458         ITypeLib_Release(tlib);
3459     }
3460
3461     return ERROR_SUCCESS;
3462 }
3463
3464 static UINT ACTION_RegisterTypeLibraries(MSIPACKAGE *package)
3465 {
3466     static const WCHAR query[] = {
3467         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3468         '`','T','y','p','e','L','i','b','`',0};
3469     MSIQUERY *view;
3470     UINT rc;
3471
3472     rc = MSI_DatabaseOpenViewW(package->db, query, &view);
3473     if (rc != ERROR_SUCCESS)
3474         return ERROR_SUCCESS;
3475
3476     rc = MSI_IterateRecords(view, NULL, ITERATE_RegisterTypeLibraries, package);
3477     msiobj_release(&view->hdr);
3478     return rc;
3479 }
3480
3481 static UINT ITERATE_UnregisterTypeLibraries( MSIRECORD *row, LPVOID param )
3482 {
3483     MSIPACKAGE *package = param;
3484     LPCWSTR component, guid;
3485     MSICOMPONENT *comp;
3486     GUID libid;
3487     UINT version;
3488     LCID language;
3489     SYSKIND syskind;
3490     HRESULT hr;
3491
3492     component = MSI_RecordGetString( row, 3 );
3493     comp = msi_get_loaded_component( package, component );
3494     if (!comp)
3495         return ERROR_SUCCESS;
3496
3497     comp->Action = msi_get_component_action( package, comp );
3498     if (comp->Action != INSTALLSTATE_ABSENT)
3499     {
3500         TRACE("component not scheduled for removal %s\n", debugstr_w(component));
3501         return ERROR_SUCCESS;
3502     }
3503     msi_ui_actiondata( package, szUnregisterTypeLibraries, row );
3504
3505     guid = MSI_RecordGetString( row, 1 );
3506     CLSIDFromString( guid, &libid );
3507     version = MSI_RecordGetInteger( row, 4 );
3508     language = MSI_RecordGetInteger( row, 2 );
3509
3510 #ifdef _WIN64
3511     syskind = SYS_WIN64;
3512 #else
3513     syskind = SYS_WIN32;
3514 #endif
3515
3516     hr = UnRegisterTypeLib( &libid, (version >> 8) & 0xffff, version & 0xff, language, syskind );
3517     if (FAILED(hr))
3518     {
3519         WARN("Failed to unregister typelib: %08x\n", hr);
3520     }
3521
3522     return ERROR_SUCCESS;
3523 }
3524
3525 static UINT ACTION_UnregisterTypeLibraries( MSIPACKAGE *package )
3526 {
3527     static const WCHAR query[] = {
3528         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3529         '`','T','y','p','e','L','i','b','`',0};
3530     MSIQUERY *view;
3531     UINT rc;
3532
3533     rc = MSI_DatabaseOpenViewW( package->db, query, &view );
3534     if (rc != ERROR_SUCCESS)
3535         return ERROR_SUCCESS;
3536
3537     rc = MSI_IterateRecords( view, NULL, ITERATE_UnregisterTypeLibraries, package );
3538     msiobj_release( &view->hdr );
3539     return rc;
3540 }
3541
3542 static WCHAR *get_link_file( MSIPACKAGE *package, MSIRECORD *row )
3543 {
3544     static const WCHAR szlnk[] = {'.','l','n','k',0};
3545     LPCWSTR directory, extension, link_folder;
3546     LPWSTR link_file, filename;
3547
3548     directory = MSI_RecordGetString( row, 2 );
3549     link_folder = msi_get_target_folder( package, directory );
3550     if (!link_folder)
3551     {
3552         ERR("unable to resolve folder %s\n", debugstr_w(directory));
3553         return NULL;
3554     }
3555     /* may be needed because of a bug somewhere else */
3556     msi_create_full_path( link_folder );
3557
3558     filename = msi_dup_record_field( row, 3 );
3559     msi_reduce_to_long_filename( filename );
3560
3561     extension = strchrW( filename, '.' );
3562     if (!extension || strcmpiW( extension, szlnk ))
3563     {
3564         int len = strlenW( filename );
3565         filename = msi_realloc( filename, len * sizeof(WCHAR) + sizeof(szlnk) );
3566         memcpy( filename + len, szlnk, sizeof(szlnk) );
3567     }
3568     link_file = msi_build_directory_name( 2, link_folder, filename );
3569     msi_free( filename );
3570
3571     return link_file;
3572 }
3573
3574 WCHAR *msi_build_icon_path( MSIPACKAGE *package, const WCHAR *icon_name )
3575 {
3576     static const WCHAR szMicrosoft[] = {'M','i','c','r','o','s','o','f','t','\\',0};
3577     static const WCHAR szInstaller[] = {'I','n','s','t','a','l','l','e','r','\\',0};
3578     WCHAR *folder, *dest, *path;
3579
3580     if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3581         folder = msi_dup_property( package->db, szWindowsFolder );
3582     else
3583     {
3584         WCHAR *appdata = msi_dup_property( package->db, szAppDataFolder );
3585         folder = msi_build_directory_name( 2, appdata, szMicrosoft );
3586         msi_free( appdata );
3587     }
3588     dest = msi_build_directory_name( 3, folder, szInstaller, package->ProductCode );
3589     msi_create_full_path( dest );
3590     path = msi_build_directory_name( 2, dest, icon_name );
3591     msi_free( folder );
3592     msi_free( dest );
3593     return path;
3594 }
3595
3596 static UINT ITERATE_CreateShortcuts(MSIRECORD *row, LPVOID param)
3597 {
3598     MSIPACKAGE *package = param;
3599     LPWSTR link_file, deformated, path;
3600     LPCWSTR component, target;
3601     MSICOMPONENT *comp;
3602     IShellLinkW *sl = NULL;
3603     IPersistFile *pf = NULL;
3604     HRESULT res;
3605
3606     component = MSI_RecordGetString(row, 4);
3607     comp = msi_get_loaded_component(package, component);
3608     if (!comp)
3609         return ERROR_SUCCESS;
3610
3611     comp->Action = msi_get_component_action( package, comp );
3612     if (comp->Action != INSTALLSTATE_LOCAL)
3613     {
3614         TRACE("component not scheduled for installation %s\n", debugstr_w(component));
3615         return ERROR_SUCCESS;
3616     }
3617     msi_ui_actiondata( package, szCreateShortcuts, row );
3618
3619     res = CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
3620                     &IID_IShellLinkW, (LPVOID *) &sl );
3621
3622     if (FAILED( res ))
3623     {
3624         ERR("CLSID_ShellLink not available\n");
3625         goto err;
3626     }
3627
3628     res = IShellLinkW_QueryInterface( sl, &IID_IPersistFile,(LPVOID*) &pf );
3629     if (FAILED( res ))
3630     {
3631         ERR("QueryInterface(IID_IPersistFile) failed\n");
3632         goto err;
3633     }
3634
3635     target = MSI_RecordGetString(row, 5);
3636     if (strchrW(target, '['))
3637     {
3638         deformat_string( package, target, &path );
3639         TRACE("target path is %s\n", debugstr_w(path));
3640         IShellLinkW_SetPath( sl, path );
3641         msi_free( path );
3642     }
3643     else
3644     {
3645         FIXME("poorly handled shortcut format, advertised shortcut\n");
3646         IShellLinkW_SetPath(sl,comp->FullKeypath);
3647     }
3648
3649     if (!MSI_RecordIsNull(row,6))
3650     {
3651         LPCWSTR arguments = MSI_RecordGetString(row, 6);
3652         deformat_string(package, arguments, &deformated);
3653         IShellLinkW_SetArguments(sl,deformated);
3654         msi_free(deformated);
3655     }
3656
3657     if (!MSI_RecordIsNull(row,7))
3658     {
3659         LPCWSTR description = MSI_RecordGetString(row, 7);
3660         IShellLinkW_SetDescription(sl, description);
3661     }
3662
3663     if (!MSI_RecordIsNull(row,8))
3664         IShellLinkW_SetHotkey(sl,MSI_RecordGetInteger(row,8));
3665
3666     if (!MSI_RecordIsNull(row,9))
3667     {
3668         INT index; 
3669         LPCWSTR icon = MSI_RecordGetString(row, 9);
3670
3671         path = msi_build_icon_path(package, icon);
3672         index = MSI_RecordGetInteger(row,10);
3673
3674         /* no value means 0 */
3675         if (index == MSI_NULL_INTEGER)
3676             index = 0;
3677
3678         IShellLinkW_SetIconLocation(sl, path, index);
3679         msi_free(path);
3680     }
3681
3682     if (!MSI_RecordIsNull(row,11))
3683         IShellLinkW_SetShowCmd(sl,MSI_RecordGetInteger(row,11));
3684
3685     if (!MSI_RecordIsNull(row,12))
3686     {
3687         LPCWSTR full_path, wkdir = MSI_RecordGetString( row, 12 );
3688         full_path = msi_get_target_folder( package, wkdir );
3689         if (full_path) IShellLinkW_SetWorkingDirectory( sl, full_path );
3690     }
3691     link_file = get_link_file(package, row);
3692
3693     TRACE("Writing shortcut to %s\n", debugstr_w(link_file));
3694     IPersistFile_Save(pf, link_file, FALSE);
3695     msi_free(link_file);
3696
3697 err:
3698     if (pf)
3699         IPersistFile_Release( pf );
3700     if (sl)
3701         IShellLinkW_Release( sl );
3702
3703     return ERROR_SUCCESS;
3704 }
3705
3706 static UINT ACTION_CreateShortcuts(MSIPACKAGE *package)
3707 {
3708     static const WCHAR query[] = {
3709         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3710         '`','S','h','o','r','t','c','u','t','`',0};
3711     MSIQUERY *view;
3712     HRESULT res;
3713     UINT rc;
3714
3715     rc = MSI_DatabaseOpenViewW(package->db, query, &view);
3716     if (rc != ERROR_SUCCESS)
3717         return ERROR_SUCCESS;
3718
3719     res = CoInitialize( NULL );
3720
3721     rc = MSI_IterateRecords(view, NULL, ITERATE_CreateShortcuts, package);
3722     msiobj_release(&view->hdr);
3723
3724     if (SUCCEEDED(res)) CoUninitialize();
3725     return rc;
3726 }
3727
3728 static UINT ITERATE_RemoveShortcuts( MSIRECORD *row, LPVOID param )
3729 {
3730     MSIPACKAGE *package = param;
3731     LPWSTR link_file;
3732     LPCWSTR component;
3733     MSICOMPONENT *comp;
3734
3735     component = MSI_RecordGetString( row, 4 );
3736     comp = msi_get_loaded_component( package, component );
3737     if (!comp)
3738         return ERROR_SUCCESS;
3739
3740     comp->Action = msi_get_component_action( package, comp );
3741     if (comp->Action != INSTALLSTATE_ABSENT)
3742     {
3743         TRACE("component not scheduled for removal %s\n", debugstr_w(component));
3744         return ERROR_SUCCESS;
3745     }
3746     msi_ui_actiondata( package, szRemoveShortcuts, row );
3747
3748     link_file = get_link_file( package, row );
3749
3750     TRACE("Removing shortcut file %s\n", debugstr_w( link_file ));
3751     if (!DeleteFileW( link_file ))
3752     {
3753         WARN("Failed to remove shortcut file %u\n", GetLastError());
3754     }
3755     msi_free( link_file );
3756
3757     return ERROR_SUCCESS;
3758 }
3759
3760 static UINT ACTION_RemoveShortcuts( MSIPACKAGE *package )
3761 {
3762     static const WCHAR query[] = {
3763         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3764         '`','S','h','o','r','t','c','u','t','`',0};
3765     MSIQUERY *view;
3766     UINT rc;
3767
3768     rc = MSI_DatabaseOpenViewW( package->db, query, &view );
3769     if (rc != ERROR_SUCCESS)
3770         return ERROR_SUCCESS;
3771
3772     rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveShortcuts, package );
3773     msiobj_release( &view->hdr );
3774     return rc;
3775 }
3776
3777 static UINT ITERATE_PublishIcon(MSIRECORD *row, LPVOID param)
3778 {
3779     MSIPACKAGE* package = param;
3780     HANDLE the_file;
3781     LPWSTR FilePath;
3782     LPCWSTR FileName;
3783     CHAR buffer[1024];
3784     DWORD sz;
3785     UINT rc;
3786
3787     FileName = MSI_RecordGetString(row,1);
3788     if (!FileName)
3789     {
3790         ERR("Unable to get FileName\n");
3791         return ERROR_SUCCESS;
3792     }
3793
3794     FilePath = msi_build_icon_path(package, FileName);
3795
3796     TRACE("Creating icon file at %s\n",debugstr_w(FilePath));
3797
3798     the_file = CreateFileW(FilePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
3799                         FILE_ATTRIBUTE_NORMAL, NULL);
3800
3801     if (the_file == INVALID_HANDLE_VALUE)
3802     {
3803         ERR("Unable to create file %s\n",debugstr_w(FilePath));
3804         msi_free(FilePath);
3805         return ERROR_SUCCESS;
3806     }
3807
3808     do 
3809     {
3810         DWORD write;
3811         sz = 1024;
3812         rc = MSI_RecordReadStream(row,2,buffer,&sz);
3813         if (rc != ERROR_SUCCESS)
3814         {
3815             ERR("Failed to get stream\n");
3816             CloseHandle(the_file);  
3817             DeleteFileW(FilePath);
3818             break;
3819         }
3820         WriteFile(the_file,buffer,sz,&write,NULL);
3821     } while (sz == 1024);
3822
3823     msi_free(FilePath);
3824     CloseHandle(the_file);
3825
3826     return ERROR_SUCCESS;
3827 }
3828
3829 static UINT msi_publish_icons(MSIPACKAGE *package)
3830 {
3831     static const WCHAR query[]= {
3832         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3833         '`','I','c','o','n','`',0};
3834     MSIQUERY *view;
3835     UINT r;
3836
3837     r = MSI_DatabaseOpenViewW(package->db, query, &view);
3838     if (r == ERROR_SUCCESS)
3839     {
3840         r = MSI_IterateRecords(view, NULL, ITERATE_PublishIcon, package);
3841         msiobj_release(&view->hdr);
3842         if (r != ERROR_SUCCESS)
3843             return r;
3844     }
3845     return ERROR_SUCCESS;
3846 }
3847
3848 static UINT msi_publish_sourcelist(MSIPACKAGE *package, HKEY hkey)
3849 {
3850     UINT r;
3851     HKEY source;
3852     LPWSTR buffer;
3853     MSIMEDIADISK *disk;
3854     MSISOURCELISTINFO *info;
3855
3856     r = RegCreateKeyW(hkey, szSourceList, &source);
3857     if (r != ERROR_SUCCESS)
3858         return r;
3859
3860     RegCloseKey(source);
3861
3862     buffer = strrchrW(package->PackagePath, '\\') + 1;
3863     r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3864                               package->Context, MSICODE_PRODUCT,
3865                               INSTALLPROPERTY_PACKAGENAMEW, buffer);
3866     if (r != ERROR_SUCCESS)
3867         return r;
3868
3869     r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3870                               package->Context, MSICODE_PRODUCT,
3871                               INSTALLPROPERTY_MEDIAPACKAGEPATHW, szEmpty);
3872     if (r != ERROR_SUCCESS)
3873         return r;
3874
3875     r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3876                               package->Context, MSICODE_PRODUCT,
3877                               INSTALLPROPERTY_DISKPROMPTW, szEmpty);
3878     if (r != ERROR_SUCCESS)
3879         return r;
3880
3881     LIST_FOR_EACH_ENTRY(info, &package->sourcelist_info, MSISOURCELISTINFO, entry)
3882     {
3883         if (!strcmpW( info->property, INSTALLPROPERTY_LASTUSEDSOURCEW ))
3884             msi_set_last_used_source(package->ProductCode, NULL, info->context,
3885                                      info->options, info->value);
3886         else
3887             MsiSourceListSetInfoW(package->ProductCode, NULL,
3888                                   info->context, info->options,
3889                                   info->property, info->value);
3890     }
3891
3892     LIST_FOR_EACH_ENTRY(disk, &package->sourcelist_media, MSIMEDIADISK, entry)
3893     {
3894         MsiSourceListAddMediaDiskW(package->ProductCode, NULL,
3895                                    disk->context, disk->options,
3896                                    disk->disk_id, disk->volume_label, disk->disk_prompt);
3897     }
3898
3899     return ERROR_SUCCESS;
3900 }
3901
3902 static UINT msi_publish_product_properties(MSIPACKAGE *package, HKEY hkey)
3903 {
3904     MSIHANDLE hdb, suminfo;
3905     WCHAR guids[MAX_PATH];
3906     WCHAR packcode[SQUISH_GUID_SIZE];
3907     LPWSTR buffer;
3908     LPWSTR ptr;
3909     DWORD langid;
3910     DWORD size;
3911     UINT r;
3912
3913     static const WCHAR szARPProductIcon[] =
3914         {'A','R','P','P','R','O','D','U','C','T','I','C','O','N',0};
3915     static const WCHAR szAssignment[] =
3916         {'A','s','s','i','g','n','m','e','n','t',0};
3917     static const WCHAR szAdvertiseFlags[] =
3918         {'A','d','v','e','r','t','i','s','e','F','l','a','g','s',0};
3919     static const WCHAR szClients[] =
3920         {'C','l','i','e','n','t','s',0};
3921     static const WCHAR szColon[] = {':',0};
3922
3923     buffer = msi_dup_property(package->db, INSTALLPROPERTY_PRODUCTNAMEW);
3924     msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTNAMEW, buffer);
3925     msi_free(buffer);
3926
3927     langid = msi_get_property_int(package->db, szProductLanguage, 0);
3928     msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
3929
3930     /* FIXME */
3931     msi_reg_set_val_dword(hkey, INSTALLPROPERTY_AUTHORIZED_LUA_APPW, 0);
3932
3933     buffer = msi_dup_property(package->db, szARPProductIcon);
3934     if (buffer)
3935     {
3936         LPWSTR path = msi_build_icon_path(package, buffer);
3937         msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTICONW, path);
3938         msi_free(path);
3939         msi_free(buffer);
3940     }
3941
3942     buffer = msi_dup_property(package->db, szProductVersion);
3943     if (buffer)
3944     {
3945         DWORD verdword = msi_version_str_to_dword(buffer);
3946         msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
3947         msi_free(buffer);
3948     }
3949
3950     msi_reg_set_val_dword(hkey, szAssignment, 0);
3951     msi_reg_set_val_dword(hkey, szAdvertiseFlags, 0x184);
3952     msi_reg_set_val_dword(hkey, INSTALLPROPERTY_INSTANCETYPEW, 0);
3953     msi_reg_set_val_str(hkey, szClients, szColon);
3954
3955     hdb = alloc_msihandle(&package->db->hdr);
3956     if (!hdb)
3957         return ERROR_NOT_ENOUGH_MEMORY;
3958
3959     r = MsiGetSummaryInformationW(hdb, NULL, 0, &suminfo);
3960     MsiCloseHandle(hdb);
3961     if (r != ERROR_SUCCESS)
3962         goto done;
3963
3964     size = MAX_PATH;
3965     r = MsiSummaryInfoGetPropertyW(suminfo, PID_REVNUMBER, NULL, NULL,
3966                                    NULL, guids, &size);
3967     if (r != ERROR_SUCCESS)
3968         goto done;
3969
3970     ptr = strchrW(guids, ';');
3971     if (ptr) *ptr = 0;
3972     squash_guid(guids, packcode);
3973     msi_reg_set_val_str(hkey, INSTALLPROPERTY_PACKAGECODEW, packcode);
3974
3975 done:
3976     MsiCloseHandle(suminfo);
3977     return ERROR_SUCCESS;
3978 }
3979
3980 static UINT msi_publish_upgrade_code(MSIPACKAGE *package)
3981 {
3982     UINT r;
3983     HKEY hkey;
3984     LPWSTR upgrade;
3985     WCHAR squashed_pc[SQUISH_GUID_SIZE];
3986
3987     upgrade = msi_dup_property(package->db, szUpgradeCode);
3988     if (!upgrade)
3989         return ERROR_SUCCESS;
3990
3991     if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3992         r = MSIREG_OpenClassesUpgradeCodesKey(upgrade, &hkey, TRUE);
3993     else
3994         r = MSIREG_OpenUserUpgradeCodesKey(upgrade, &hkey, TRUE);
3995
3996     if (r != ERROR_SUCCESS)
3997     {
3998         WARN("failed to open upgrade code key\n");
3999         msi_free(upgrade);
4000         return ERROR_SUCCESS;
4001     }
4002     squash_guid(package->ProductCode, squashed_pc);
4003     msi_reg_set_val_str(hkey, squashed_pc, NULL);
4004     RegCloseKey(hkey);
4005     msi_free(upgrade);
4006     return ERROR_SUCCESS;
4007 }
4008
4009 static BOOL msi_check_publish(MSIPACKAGE *package)
4010 {
4011     MSIFEATURE *feature;
4012
4013     LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4014     {
4015         feature->Action = msi_get_feature_action( package, feature );
4016         if (feature->Action == INSTALLSTATE_LOCAL)
4017             return TRUE;
4018     }
4019
4020     return FALSE;
4021 }
4022
4023 static BOOL msi_check_unpublish(MSIPACKAGE *package)
4024 {
4025     MSIFEATURE *feature;
4026
4027     LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4028     {
4029         feature->Action = msi_get_feature_action( package, feature );
4030         if (feature->Action != INSTALLSTATE_ABSENT)
4031             return FALSE;
4032     }
4033
4034     return TRUE;
4035 }
4036
4037 static UINT msi_publish_patches( MSIPACKAGE *package )
4038 {
4039     static const WCHAR szAllPatches[] = {'A','l','l','P','a','t','c','h','e','s',0};
4040     WCHAR patch_squashed[GUID_SIZE];
4041     HKEY patches_key = NULL, product_patches_key = NULL, product_key;
4042     LONG res;
4043     MSIPATCHINFO *patch;
4044     UINT r;
4045     WCHAR *p, *all_patches = NULL;
4046     DWORD len = 0;
4047
4048     r = MSIREG_OpenProductKey( package->ProductCode, NULL, package->Context, &product_key, TRUE );
4049     if (r != ERROR_SUCCESS)
4050         return ERROR_FUNCTION_FAILED;
4051
4052     res = RegCreateKeyExW( product_key, szPatches, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &patches_key, NULL );
4053     if (res != ERROR_SUCCESS)
4054     {
4055         r = ERROR_FUNCTION_FAILED;
4056         goto done;
4057     }
4058
4059     r = MSIREG_OpenUserDataProductPatchesKey( package->ProductCode, package->Context, &product_patches_key, TRUE );
4060     if (r != ERROR_SUCCESS)
4061         goto done;
4062
4063     LIST_FOR_EACH_ENTRY( patch, &package->patches, MSIPATCHINFO, entry )
4064     {
4065         squash_guid( patch->patchcode, patch_squashed );
4066         len += strlenW( patch_squashed ) + 1;
4067     }
4068
4069     p = all_patches = msi_alloc( (len + 1) * sizeof(WCHAR) );
4070     if (!all_patches)
4071         goto done;
4072
4073     LIST_FOR_EACH_ENTRY( patch, &package->patches, MSIPATCHINFO, entry )
4074     {
4075         HKEY patch_key;
4076
4077         squash_guid( patch->patchcode, p );
4078         p += strlenW( p ) + 1;
4079
4080         res = RegSetValueExW( patches_key, patch_squashed, 0, REG_SZ,
4081                               (const BYTE *)patch->transforms,
4082                               (strlenW(patch->transforms) + 1) * sizeof(WCHAR) );
4083         if (res != ERROR_SUCCESS)
4084             goto done;
4085
4086         r = MSIREG_OpenUserDataPatchKey( patch->patchcode, package->Context, &patch_key, TRUE );
4087         if (r != ERROR_SUCCESS)
4088             goto done;
4089
4090         res = RegSetValueExW( patch_key, szLocalPackage, 0, REG_SZ, (const BYTE *)patch->localfile,
4091                               (strlenW( patch->localfile ) + 1) * sizeof(WCHAR) );
4092         RegCloseKey( patch_key );
4093         if (res != ERROR_SUCCESS)
4094             goto done;
4095
4096         if (patch->filename && !CopyFileW( patch->filename, patch->localfile, FALSE ))
4097         {
4098             res = GetLastError();
4099             ERR("Unable to copy patch package %d\n", res);
4100             goto done;
4101         }
4102         res = RegCreateKeyExW( product_patches_key, patch_squashed, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &patch_key, NULL );
4103         if (res != ERROR_SUCCESS)
4104             goto done;
4105
4106         res = RegSetValueExW( patch_key, szState, 0, REG_DWORD, (const BYTE *)&patch->state, sizeof(patch->state) );
4107         RegCloseKey( patch_key );
4108         if (res != ERROR_SUCCESS)
4109             goto done;
4110     }
4111
4112     all_patches[len] = 0;
4113     res = RegSetValueExW( patches_key, szPatches, 0, REG_MULTI_SZ,
4114                           (const BYTE *)all_patches, (len + 1) * sizeof(WCHAR) );
4115     if (res != ERROR_SUCCESS)
4116         goto done;
4117
4118     res = RegSetValueExW( product_patches_key, szAllPatches, 0, REG_MULTI_SZ,
4119                           (const BYTE *)all_patches, (len + 1) * sizeof(WCHAR) );
4120     if (res != ERROR_SUCCESS)
4121         r = ERROR_FUNCTION_FAILED;
4122
4123 done:
4124     RegCloseKey( product_patches_key );
4125     RegCloseKey( patches_key );
4126     RegCloseKey( product_key );
4127     msi_free( all_patches );
4128     return r;
4129 }
4130
4131 static UINT ACTION_PublishProduct(MSIPACKAGE *package)
4132 {
4133     UINT rc;
4134     HKEY hukey = NULL, hudkey = NULL;
4135     MSIRECORD *uirow;
4136
4137     if (!list_empty(&package->patches))
4138     {
4139         rc = msi_publish_patches(package);
4140         if (rc != ERROR_SUCCESS)
4141             goto end;
4142     }
4143
4144     /* FIXME: also need to publish if the product is in advertise mode */
4145     if (!msi_check_publish(package))
4146         return ERROR_SUCCESS;
4147
4148     rc = MSIREG_OpenProductKey(package->ProductCode, NULL, package->Context,
4149                                &hukey, TRUE);
4150     if (rc != ERROR_SUCCESS)
4151         goto end;
4152
4153     rc = MSIREG_OpenUserDataProductKey(package->ProductCode, package->Context,
4154                                        NULL, &hudkey, TRUE);
4155     if (rc != ERROR_SUCCESS)
4156         goto end;
4157
4158     rc = msi_publish_upgrade_code(package);
4159     if (rc != ERROR_SUCCESS)
4160         goto end;
4161
4162     rc = msi_publish_product_properties(package, hukey);
4163     if (rc != ERROR_SUCCESS)
4164         goto end;
4165
4166     rc = msi_publish_sourcelist(package, hukey);
4167     if (rc != ERROR_SUCCESS)
4168         goto end;
4169
4170     rc = msi_publish_icons(package);
4171
4172 end:
4173     uirow = MSI_CreateRecord( 1 );
4174     MSI_RecordSetStringW( uirow, 1, package->ProductCode );
4175     msi_ui_actiondata( package, szPublishProduct, uirow );
4176     msiobj_release( &uirow->hdr );
4177
4178     RegCloseKey(hukey);
4179     RegCloseKey(hudkey);
4180     return rc;
4181 }
4182
4183 static WCHAR *get_ini_file_name( MSIPACKAGE *package, MSIRECORD *row )
4184 {
4185     WCHAR *filename, *ptr, *folder, *ret;
4186     const WCHAR *dirprop;
4187
4188     filename = msi_dup_record_field( row, 2 );
4189     if (filename && (ptr = strchrW( filename, '|' )))
4190         ptr++;
4191     else
4192         ptr = filename;
4193
4194     dirprop = MSI_RecordGetString( row, 3 );
4195     if (dirprop)
4196     {
4197         folder = strdupW( msi_get_target_folder( package, dirprop ) );
4198         if (!folder) folder = msi_dup_property( package->db, dirprop );
4199     }
4200     else
4201         folder = msi_dup_property( package->db, szWindowsFolder );
4202
4203     if (!folder)
4204     {
4205         ERR("Unable to resolve folder %s\n", debugstr_w(dirprop));
4206         msi_free( filename );
4207         return NULL;
4208     }
4209
4210     ret = msi_build_directory_name( 2, folder, ptr );
4211
4212     msi_free( filename );
4213     msi_free( folder );
4214     return ret;
4215 }
4216
4217 static UINT ITERATE_WriteIniValues(MSIRECORD *row, LPVOID param)
4218 {
4219     MSIPACKAGE *package = param;
4220     LPCWSTR component, section, key, value, identifier;
4221     LPWSTR deformated_section, deformated_key, deformated_value, fullname;
4222     MSIRECORD * uirow;
4223     INT action;
4224     MSICOMPONENT *comp;
4225
4226     component = MSI_RecordGetString(row, 8);
4227     comp = msi_get_loaded_component(package,component);
4228     if (!comp)
4229         return ERROR_SUCCESS;
4230
4231     comp->Action = msi_get_component_action( package, comp );
4232     if (comp->Action != INSTALLSTATE_LOCAL)
4233     {
4234         TRACE("component not scheduled for installation %s\n", debugstr_w(component));
4235         return ERROR_SUCCESS;
4236     }
4237
4238     identifier = MSI_RecordGetString(row,1); 
4239     section = MSI_RecordGetString(row,4);
4240     key = MSI_RecordGetString(row,5);
4241     value = MSI_RecordGetString(row,6);
4242     action = MSI_RecordGetInteger(row,7);
4243
4244     deformat_string(package,section,&deformated_section);
4245     deformat_string(package,key,&deformated_key);
4246     deformat_string(package,value,&deformated_value);
4247
4248     fullname = get_ini_file_name(package, row);
4249
4250     if (action == 0)
4251     {
4252         TRACE("Adding value %s to section %s in %s\n",
4253                 debugstr_w(deformated_key), debugstr_w(deformated_section),
4254                 debugstr_w(fullname));
4255         WritePrivateProfileStringW(deformated_section, deformated_key,
4256                                    deformated_value, fullname);
4257     }
4258     else if (action == 1)
4259     {
4260         WCHAR returned[10];
4261         GetPrivateProfileStringW(deformated_section, deformated_key, NULL,
4262                                  returned, 10, fullname);
4263         if (returned[0] == 0)
4264         {
4265             TRACE("Adding value %s to section %s in %s\n",
4266                     debugstr_w(deformated_key), debugstr_w(deformated_section),
4267                     debugstr_w(fullname));
4268
4269             WritePrivateProfileStringW(deformated_section, deformated_key,
4270                                        deformated_value, fullname);
4271         }
4272     }
4273     else if (action == 3)
4274         FIXME("Append to existing section not yet implemented\n");
4275
4276     uirow = MSI_CreateRecord(4);
4277     MSI_RecordSetStringW(uirow,1,identifier);
4278     MSI_RecordSetStringW(uirow,2,deformated_section);
4279     MSI_RecordSetStringW(uirow,3,deformated_key);
4280     MSI_RecordSetStringW(uirow,4,deformated_value);
4281     msi_ui_actiondata( package, szWriteIniValues, uirow );
4282     msiobj_release( &uirow->hdr );
4283
4284     msi_free(fullname);
4285     msi_free(deformated_key);
4286     msi_free(deformated_value);
4287     msi_free(deformated_section);
4288     return ERROR_SUCCESS;
4289 }
4290
4291 static UINT ACTION_WriteIniValues(MSIPACKAGE *package)
4292 {
4293     static const WCHAR query[] = {
4294         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4295         '`','I','n','i','F','i','l','e','`',0};
4296     MSIQUERY *view;
4297     UINT rc;
4298
4299     rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4300     if (rc != ERROR_SUCCESS)
4301         return ERROR_SUCCESS;
4302
4303     rc = MSI_IterateRecords(view, NULL, ITERATE_WriteIniValues, package);
4304     msiobj_release(&view->hdr);
4305     return rc;
4306 }
4307
4308 static UINT ITERATE_RemoveIniValuesOnUninstall( MSIRECORD *row, LPVOID param )
4309 {
4310     MSIPACKAGE *package = param;
4311     LPCWSTR component, section, key, value, identifier;
4312     LPWSTR deformated_section, deformated_key, deformated_value, filename;
4313     MSICOMPONENT *comp;
4314     MSIRECORD *uirow;
4315     INT action;
4316
4317     component = MSI_RecordGetString( row, 8 );
4318     comp = msi_get_loaded_component( package, component );
4319     if (!comp)
4320         return ERROR_SUCCESS;
4321
4322     comp->Action = msi_get_component_action( package, comp );
4323     if (comp->Action != INSTALLSTATE_ABSENT)
4324     {
4325         TRACE("component not scheduled for removal %s\n", debugstr_w(component));
4326         return ERROR_SUCCESS;
4327     }
4328
4329     identifier = MSI_RecordGetString( row, 1 );
4330     section = MSI_RecordGetString( row, 4 );
4331     key = MSI_RecordGetString( row, 5 );
4332     value = MSI_RecordGetString( row, 6 );
4333     action = MSI_RecordGetInteger( row, 7 );
4334
4335     deformat_string( package, section, &deformated_section );
4336     deformat_string( package, key, &deformated_key );
4337     deformat_string( package, value, &deformated_value );
4338
4339     if (action == msidbIniFileActionAddLine || action == msidbIniFileActionCreateLine)
4340     {
4341         filename = get_ini_file_name( package, row );
4342
4343         TRACE("Removing key %s from section %s in %s\n",
4344                debugstr_w(deformated_key), debugstr_w(deformated_section), debugstr_w(filename));
4345
4346         if (!WritePrivateProfileStringW( deformated_section, deformated_key, NULL, filename ))
4347         {
4348             WARN("Unable to remove key %u\n", GetLastError());
4349         }
4350         msi_free( filename );
4351     }
4352     else
4353         FIXME("Unsupported action %d\n", action);
4354
4355
4356     uirow = MSI_CreateRecord( 4 );
4357     MSI_RecordSetStringW( uirow, 1, identifier );
4358     MSI_RecordSetStringW( uirow, 2, deformated_section );
4359     MSI_RecordSetStringW( uirow, 3, deformated_key );
4360     MSI_RecordSetStringW( uirow, 4, deformated_value );
4361     msi_ui_actiondata( package, szRemoveIniValues, uirow );
4362     msiobj_release( &uirow->hdr );
4363
4364     msi_free( deformated_key );
4365     msi_free( deformated_value );
4366     msi_free( deformated_section );
4367     return ERROR_SUCCESS;
4368 }
4369
4370 static UINT ITERATE_RemoveIniValuesOnInstall( MSIRECORD *row, LPVOID param )
4371 {
4372     MSIPACKAGE *package = param;
4373     LPCWSTR component, section, key, value, identifier;
4374     LPWSTR deformated_section, deformated_key, deformated_value, filename;
4375     MSICOMPONENT *comp;
4376     MSIRECORD *uirow;
4377     INT action;
4378
4379     component = MSI_RecordGetString( row, 8 );
4380     comp = msi_get_loaded_component( package, component );
4381     if (!comp)
4382         return ERROR_SUCCESS;
4383
4384     comp->Action = msi_get_component_action( package, comp );
4385     if (comp->Action != INSTALLSTATE_LOCAL)
4386     {
4387         TRACE("component not scheduled for installation %s\n", debugstr_w(component));
4388         return ERROR_SUCCESS;
4389     }
4390
4391     identifier = MSI_RecordGetString( row, 1 );
4392     section = MSI_RecordGetString( row, 4 );
4393     key = MSI_RecordGetString( row, 5 );
4394     value = MSI_RecordGetString( row, 6 );
4395     action = MSI_RecordGetInteger( row, 7 );
4396
4397     deformat_string( package, section, &deformated_section );
4398     deformat_string( package, key, &deformated_key );
4399     deformat_string( package, value, &deformated_value );
4400
4401     if (action == msidbIniFileActionRemoveLine)
4402     {
4403         filename = get_ini_file_name( package, row );
4404
4405         TRACE("Removing key %s from section %s in %s\n",
4406                debugstr_w(deformated_key), debugstr_w(deformated_section), debugstr_w(filename));
4407
4408         if (!WritePrivateProfileStringW( deformated_section, deformated_key, NULL, filename ))
4409         {
4410             WARN("Unable to remove key %u\n", GetLastError());
4411         }
4412         msi_free( filename );
4413     }
4414     else
4415         FIXME("Unsupported action %d\n", action);
4416
4417     uirow = MSI_CreateRecord( 4 );
4418     MSI_RecordSetStringW( uirow, 1, identifier );
4419     MSI_RecordSetStringW( uirow, 2, deformated_section );
4420     MSI_RecordSetStringW( uirow, 3, deformated_key );
4421     MSI_RecordSetStringW( uirow, 4, deformated_value );
4422     msi_ui_actiondata( package, szRemoveIniValues, uirow );
4423     msiobj_release( &uirow->hdr );
4424
4425     msi_free( deformated_key );
4426     msi_free( deformated_value );
4427     msi_free( deformated_section );
4428     return ERROR_SUCCESS;
4429 }
4430
4431 static UINT ACTION_RemoveIniValues( MSIPACKAGE *package )
4432 {
4433     static const WCHAR query[] = {
4434         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4435         '`','I','n','i','F','i','l','e','`',0};
4436     static const WCHAR remove_query[] = {
4437         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4438         '`','R','e','m','o','v','e','I','n','i','F','i','l','e','`',0};
4439     MSIQUERY *view;
4440     UINT rc;
4441
4442     rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4443     if (rc == ERROR_SUCCESS)
4444     {
4445         rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveIniValuesOnUninstall, package );
4446         msiobj_release( &view->hdr );
4447         if (rc != ERROR_SUCCESS)
4448             return rc;
4449     }
4450     rc = MSI_DatabaseOpenViewW( package->db, remove_query, &view );
4451     if (rc == ERROR_SUCCESS)
4452     {
4453         rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveIniValuesOnInstall, package );
4454         msiobj_release( &view->hdr );
4455         if (rc != ERROR_SUCCESS)
4456             return rc;
4457     }
4458     return ERROR_SUCCESS;
4459 }
4460
4461 static void register_dll( const WCHAR *dll, BOOL unregister )
4462 {
4463     HMODULE hmod;
4464
4465     hmod = LoadLibraryExW( dll, 0, LOAD_WITH_ALTERED_SEARCH_PATH );
4466     if (hmod)
4467     {
4468         HRESULT (WINAPI *func_ptr)( void );
4469         const char *func = unregister ? "DllUnregisterServer" : "DllRegisterServer";
4470
4471         func_ptr = (void *)GetProcAddress( hmod, func );
4472         if (func_ptr)
4473         {
4474             HRESULT hr = func_ptr();
4475             if (FAILED( hr ))
4476                 WARN("failed to register dll 0x%08x\n", hr);
4477         }
4478         else
4479             WARN("entry point %s not found\n", func);
4480         FreeLibrary( hmod );
4481         return;
4482     }
4483     WARN("failed to load library %u\n", GetLastError());
4484 }
4485
4486 static UINT ITERATE_SelfRegModules(MSIRECORD *row, LPVOID param)
4487 {
4488     MSIPACKAGE *package = param;
4489     LPCWSTR filename;
4490     MSIFILE *file;
4491     MSIRECORD *uirow;
4492
4493     filename = MSI_RecordGetString( row, 1 );
4494     file = msi_get_loaded_file( package, filename );
4495     if (!file)
4496     {
4497         WARN("unable to find file %s\n", debugstr_w(filename));
4498         return ERROR_SUCCESS;
4499     }
4500     file->Component->Action = msi_get_component_action( package, file->Component );
4501     if (file->Component->Action != INSTALLSTATE_LOCAL)
4502     {
4503         TRACE("component not scheduled for installation %s\n", debugstr_w(file->Component->Component));
4504         return ERROR_SUCCESS;
4505     }
4506
4507     TRACE("Registering %s\n", debugstr_w( file->TargetPath ));
4508     register_dll( file->TargetPath, FALSE );
4509
4510     uirow = MSI_CreateRecord( 2 );
4511     MSI_RecordSetStringW( uirow, 1, file->File );
4512     MSI_RecordSetStringW( uirow, 2, file->Component->Directory );
4513     msi_ui_actiondata( package, szSelfRegModules, uirow );
4514     msiobj_release( &uirow->hdr );
4515
4516     return ERROR_SUCCESS;
4517 }
4518
4519 static UINT ACTION_SelfRegModules(MSIPACKAGE *package)
4520 {
4521     static const WCHAR query[] = {
4522         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4523         '`','S','e','l','f','R','e','g','`',0};
4524     MSIQUERY *view;
4525     UINT rc;
4526
4527     rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4528     if (rc != ERROR_SUCCESS)
4529         return ERROR_SUCCESS;
4530
4531     rc = MSI_IterateRecords(view, NULL, ITERATE_SelfRegModules, package);
4532     msiobj_release(&view->hdr);
4533     return rc;
4534 }
4535
4536 static UINT ITERATE_SelfUnregModules( MSIRECORD *row, LPVOID param )
4537 {
4538     MSIPACKAGE *package = param;
4539     LPCWSTR filename;
4540     MSIFILE *file;
4541     MSIRECORD *uirow;
4542
4543     filename = MSI_RecordGetString( row, 1 );
4544     file = msi_get_loaded_file( package, filename );
4545     if (!file)
4546     {
4547         WARN("unable to find file %s\n", debugstr_w(filename));
4548         return ERROR_SUCCESS;
4549     }
4550     file->Component->Action = msi_get_component_action( package, file->Component );
4551     if (file->Component->Action != INSTALLSTATE_ABSENT)
4552     {
4553         TRACE("component not scheduled for removal %s\n", debugstr_w(file->Component->Component));
4554         return ERROR_SUCCESS;
4555     }
4556
4557     TRACE("Unregistering %s\n", debugstr_w( file->TargetPath ));
4558     register_dll( file->TargetPath, TRUE );
4559
4560     uirow = MSI_CreateRecord( 2 );
4561     MSI_RecordSetStringW( uirow, 1, file->File );
4562     MSI_RecordSetStringW( uirow, 2, file->Component->Directory );
4563     msi_ui_actiondata( package, szSelfUnregModules, uirow );
4564     msiobj_release( &uirow->hdr );
4565
4566     return ERROR_SUCCESS;
4567 }
4568
4569 static UINT ACTION_SelfUnregModules( MSIPACKAGE *package )
4570 {
4571     static const WCHAR query[] = {
4572         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4573         '`','S','e','l','f','R','e','g','`',0};
4574     MSIQUERY *view;
4575     UINT rc;
4576
4577     rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4578     if (rc != ERROR_SUCCESS)
4579         return ERROR_SUCCESS;
4580
4581     rc = MSI_IterateRecords( view, NULL, ITERATE_SelfUnregModules, package );
4582     msiobj_release( &view->hdr );
4583     return rc;
4584 }
4585
4586 static UINT ACTION_PublishFeatures(MSIPACKAGE *package)
4587 {
4588     MSIFEATURE *feature;
4589     UINT rc;
4590     HKEY hkey = NULL, userdata = NULL;
4591
4592     if (!msi_check_publish(package))
4593         return ERROR_SUCCESS;
4594
4595     rc = MSIREG_OpenFeaturesKey(package->ProductCode, package->Context,
4596                                 &hkey, TRUE);
4597     if (rc != ERROR_SUCCESS)
4598         goto end;
4599
4600     rc = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, package->Context,
4601                                         &userdata, TRUE);
4602     if (rc != ERROR_SUCCESS)
4603         goto end;
4604
4605     /* here the guids are base 85 encoded */
4606     LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
4607     {
4608         ComponentList *cl;
4609         LPWSTR data = NULL;
4610         GUID clsid;
4611         INT size;
4612         BOOL absent = FALSE;
4613         MSIRECORD *uirow;
4614
4615         if (feature->Action != INSTALLSTATE_LOCAL &&
4616             feature->Action != INSTALLSTATE_SOURCE &&
4617             feature->Action != INSTALLSTATE_ADVERTISED) absent = TRUE;
4618
4619         size = 1;
4620         LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
4621         {
4622             size += 21;
4623         }
4624         if (feature->Feature_Parent)
4625             size += strlenW( feature->Feature_Parent )+2;
4626
4627         data = msi_alloc(size * sizeof(WCHAR));
4628
4629         data[0] = 0;
4630         LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
4631         {
4632             MSICOMPONENT* component = cl->component;
4633             WCHAR buf[21];
4634
4635             buf[0] = 0;
4636             if (component->ComponentId)
4637             {
4638                 TRACE("From %s\n",debugstr_w(component->ComponentId));
4639                 CLSIDFromString(component->ComponentId, &clsid);
4640                 encode_base85_guid(&clsid,buf);
4641                 TRACE("to %s\n",debugstr_w(buf));
4642                 strcatW(data,buf);
4643             }
4644         }
4645
4646         if (feature->Feature_Parent)
4647         {
4648             static const WCHAR sep[] = {'\2',0};
4649             strcatW(data,sep);
4650             strcatW(data,feature->Feature_Parent);
4651         }
4652
4653         msi_reg_set_val_str( userdata, feature->Feature, data );
4654         msi_free(data);
4655
4656         size = 0;
4657         if (feature->Feature_Parent)
4658             size = strlenW(feature->Feature_Parent)*sizeof(WCHAR);
4659         if (!absent)
4660         {
4661             size += sizeof(WCHAR);
4662             RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
4663                            (const BYTE*)(feature->Feature_Parent ? feature->Feature_Parent : szEmpty),size);
4664         }
4665         else
4666         {
4667             size += 2*sizeof(WCHAR);
4668             data = msi_alloc(size);
4669             data[0] = 0x6;
4670             data[1] = 0;
4671             if (feature->Feature_Parent)
4672                 strcpyW( &data[1], feature->Feature_Parent );
4673             RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
4674                        (LPBYTE)data,size);
4675             msi_free(data);
4676         }
4677
4678         /* the UI chunk */
4679         uirow = MSI_CreateRecord( 1 );
4680         MSI_RecordSetStringW( uirow, 1, feature->Feature );
4681         msi_ui_actiondata( package, szPublishFeatures, uirow );
4682         msiobj_release( &uirow->hdr );
4683         /* FIXME: call msi_ui_progress? */
4684     }
4685
4686 end:
4687     RegCloseKey(hkey);
4688     RegCloseKey(userdata);
4689     return rc;
4690 }
4691
4692 static UINT msi_unpublish_feature(MSIPACKAGE *package, MSIFEATURE *feature)
4693 {
4694     UINT r;
4695     HKEY hkey;
4696     MSIRECORD *uirow;
4697
4698     TRACE("unpublishing feature %s\n", debugstr_w(feature->Feature));
4699
4700     r = MSIREG_OpenFeaturesKey(package->ProductCode, package->Context,
4701                                &hkey, FALSE);
4702     if (r == ERROR_SUCCESS)
4703     {
4704         RegDeleteValueW(hkey, feature->Feature);
4705         RegCloseKey(hkey);
4706     }
4707
4708     r = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, package->Context,
4709                                        &hkey, FALSE);
4710     if (r == ERROR_SUCCESS)
4711     {
4712         RegDeleteValueW(hkey, feature->Feature);
4713         RegCloseKey(hkey);
4714     }
4715
4716     uirow = MSI_CreateRecord( 1 );
4717     MSI_RecordSetStringW( uirow, 1, feature->Feature );
4718     msi_ui_actiondata( package, szUnpublishFeatures, uirow );
4719     msiobj_release( &uirow->hdr );
4720
4721     return ERROR_SUCCESS;
4722 }
4723
4724 static UINT ACTION_UnpublishFeatures(MSIPACKAGE *package)
4725 {
4726     MSIFEATURE *feature;
4727
4728     if (!msi_check_unpublish(package))
4729         return ERROR_SUCCESS;
4730
4731     LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4732     {
4733         msi_unpublish_feature(package, feature);
4734     }
4735
4736     return ERROR_SUCCESS;
4737 }
4738
4739 static UINT msi_publish_install_properties(MSIPACKAGE *package, HKEY hkey)
4740 {
4741     SYSTEMTIME systime;
4742     DWORD size, langid;
4743     WCHAR date[9], *val, *buffer;
4744     const WCHAR *prop, *key;
4745
4746     static const WCHAR date_fmt[] = {'%','i','%','0','2','i','%','0','2','i',0};
4747     static const WCHAR modpath_fmt[] =
4748         {'M','s','i','E','x','e','c','.','e','x','e',' ',
4749          '/','I','[','P','r','o','d','u','c','t','C','o','d','e',']',0};
4750     static const WCHAR szModifyPath[] =
4751         {'M','o','d','i','f','y','P','a','t','h',0};
4752     static const WCHAR szUninstallString[] =
4753         {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0};
4754     static const WCHAR szEstimatedSize[] =
4755         {'E','s','t','i','m','a','t','e','d','S','i','z','e',0};
4756     static const WCHAR szDisplayVersion[] =
4757         {'D','i','s','p','l','a','y','V','e','r','s','i','o','n',0};
4758     static const WCHAR szInstallSource[] =
4759         {'I','n','s','t','a','l','l','S','o','u','r','c','e',0};
4760     static const WCHAR szARPAUTHORIZEDCDFPREFIX[] =
4761         {'A','R','P','A','U','T','H','O','R','I','Z','E','D','C','D','F','P','R','E','F','I','X',0};
4762     static const WCHAR szAuthorizedCDFPrefix[] =
4763         {'A','u','t','h','o','r','i','z','e','d','C','D','F','P','r','e','f','i','x',0};
4764     static const WCHAR szARPCONTACT[] =
4765         {'A','R','P','C','O','N','T','A','C','T',0};
4766     static const WCHAR szContact[] =
4767         {'C','o','n','t','a','c','t',0};
4768     static const WCHAR szARPCOMMENTS[] =
4769         {'A','R','P','C','O','M','M','E','N','T','S',0};
4770     static const WCHAR szComments[] =
4771         {'C','o','m','m','e','n','t','s',0};
4772     static const WCHAR szProductName[] =
4773         {'P','r','o','d','u','c','t','N','a','m','e',0};
4774     static const WCHAR szDisplayName[] =
4775         {'D','i','s','p','l','a','y','N','a','m','e',0};
4776     static const WCHAR szARPHELPLINK[] =
4777         {'A','R','P','H','E','L','P','L','I','N','K',0};
4778     static const WCHAR szHelpLink[] =
4779         {'H','e','l','p','L','i','n','k',0};
4780     static const WCHAR szARPHELPTELEPHONE[] =
4781         {'A','R','P','H','E','L','P','T','E','L','E','P','H','O','N','E',0};
4782     static const WCHAR szHelpTelephone[] =
4783         {'H','e','l','p','T','e','l','e','p','h','o','n','e',0};
4784     static const WCHAR szARPINSTALLLOCATION[] =
4785         {'A','R','P','I','N','S','T','A','L','L','L','O','C','A','T','I','O','N',0};
4786     static const WCHAR szInstallLocation[] =
4787         {'I','n','s','t','a','l','l','L','o','c','a','t','i','o','n',0};
4788     static const WCHAR szManufacturer[] =
4789         {'M','a','n','u','f','a','c','t','u','r','e','r',0};
4790     static const WCHAR szPublisher[] =
4791         {'P','u','b','l','i','s','h','e','r',0};
4792     static const WCHAR szARPREADME[] =
4793         {'A','R','P','R','E','A','D','M','E',0};
4794     static const WCHAR szReadme[] =
4795         {'R','e','a','d','M','e',0};
4796     static const WCHAR szARPSIZE[] =
4797         {'A','R','P','S','I','Z','E',0};
4798     static const WCHAR szSize[] =
4799         {'S','i','z','e',0};
4800     static const WCHAR szARPURLINFOABOUT[] =
4801         {'A','R','P','U','R','L','I','N','F','O','A','B','O','U','T',0};
4802     static const WCHAR szURLInfoAbout[] =
4803         {'U','R','L','I','n','f','o','A','b','o','u','t',0};
4804     static const WCHAR szARPURLUPDATEINFO[] =
4805         {'A','R','P','U','R','L','U','P','D','A','T','E','I','N','F','O',0};
4806     static const WCHAR szURLUpdateInfo[] =
4807         {'U','R','L','U','p','d','a','t','e','I','n','f','o',0};
4808     static const WCHAR szARPSYSTEMCOMPONENT[] =
4809         {'A','R','P','S','Y','S','T','E','M','C','O','M','P','O','N','E','N','T',0};
4810     static const WCHAR szSystemComponent[] =
4811         {'S','y','s','t','e','m','C','o','m','p','o','n','e','n','t',0};
4812
4813     static const WCHAR *propval[] = {
4814         szARPAUTHORIZEDCDFPREFIX, szAuthorizedCDFPrefix,
4815         szARPCONTACT,             szContact,
4816         szARPCOMMENTS,            szComments,
4817         szProductName,            szDisplayName,
4818         szARPHELPLINK,            szHelpLink,
4819         szARPHELPTELEPHONE,       szHelpTelephone,
4820         szARPINSTALLLOCATION,     szInstallLocation,
4821         szSourceDir,              szInstallSource,
4822         szManufacturer,           szPublisher,
4823         szARPREADME,              szReadme,
4824         szARPSIZE,                szSize,
4825         szARPURLINFOABOUT,        szURLInfoAbout,
4826         szARPURLUPDATEINFO,       szURLUpdateInfo,
4827         NULL
4828     };
4829     const WCHAR **p = propval;
4830
4831     while (*p)
4832     {
4833         prop = *p++;
4834         key = *p++;
4835         val = msi_dup_property(package->db, prop);
4836         msi_reg_set_val_str(hkey, key, val);
4837         msi_free(val);
4838     }
4839
4840     msi_reg_set_val_dword(hkey, szWindowsInstaller, 1);
4841     if (msi_get_property_int( package->db, szARPSYSTEMCOMPONENT, 0 ))
4842     {
4843         msi_reg_set_val_dword( hkey, szSystemComponent, 1 );
4844     }
4845     size = deformat_string(package, modpath_fmt, &buffer);
4846     RegSetValueExW(hkey, szModifyPath, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
4847     RegSetValueExW(hkey, szUninstallString, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
4848     msi_free(buffer);
4849
4850     /* FIXME: Write real Estimated Size when we have it */
4851     msi_reg_set_val_dword(hkey, szEstimatedSize, 0);
4852
4853     GetLocalTime(&systime);
4854     sprintfW(date, date_fmt, systime.wYear, systime.wMonth, systime.wDay);
4855     msi_reg_set_val_str(hkey, INSTALLPROPERTY_INSTALLDATEW, date);
4856
4857     langid = msi_get_property_int(package->db, szProductLanguage, 0);
4858     msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
4859
4860     buffer = msi_dup_property(package->db, szProductVersion);
4861     msi_reg_set_val_str(hkey, szDisplayVersion, buffer);
4862     if (buffer)
4863     {
4864         DWORD verdword = msi_version_str_to_dword(buffer);
4865
4866         msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
4867         msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMAJORW, verdword >> 24);
4868         msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMINORW, (verdword >> 16) & 0xFF);
4869         msi_free(buffer);
4870     }
4871
4872     return ERROR_SUCCESS;
4873 }
4874
4875 static UINT ACTION_RegisterProduct(MSIPACKAGE *package)
4876 {
4877     WCHAR squashed_pc[SQUISH_GUID_SIZE];
4878     MSIRECORD *uirow;
4879     LPWSTR upgrade_code;
4880     HKEY hkey, props, upgrade_key;
4881     UINT rc;
4882
4883     /* FIXME: also need to publish if the product is in advertise mode */
4884     if (!msi_check_publish(package))
4885         return ERROR_SUCCESS;
4886
4887     rc = MSIREG_OpenUninstallKey(package->ProductCode, package->platform, &hkey, TRUE);
4888     if (rc != ERROR_SUCCESS)
4889         return rc;
4890
4891     rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context, NULL, &props, TRUE);
4892     if (rc != ERROR_SUCCESS)
4893         goto done;
4894
4895     rc = msi_publish_install_properties(package, hkey);
4896     if (rc != ERROR_SUCCESS)
4897         goto done;
4898
4899     rc = msi_publish_install_properties(package, props);
4900     if (rc != ERROR_SUCCESS)
4901         goto done;
4902
4903     upgrade_code = msi_dup_property(package->db, szUpgradeCode);
4904     if (upgrade_code)
4905     {
4906         rc = MSIREG_OpenUpgradeCodesKey( upgrade_code, &upgrade_key, TRUE );
4907         if (rc == ERROR_SUCCESS)
4908         {
4909             squash_guid( package->ProductCode, squashed_pc );
4910             msi_reg_set_val_str( upgrade_key, squashed_pc, NULL );
4911             RegCloseKey( upgrade_key );
4912         }
4913         msi_free( upgrade_code );
4914     }
4915     msi_reg_set_val_str( props, INSTALLPROPERTY_LOCALPACKAGEW, package->localfile );
4916     package->delete_on_close = FALSE;
4917
4918 done:
4919     uirow = MSI_CreateRecord( 1 );
4920     MSI_RecordSetStringW( uirow, 1, package->ProductCode );
4921     msi_ui_actiondata( package, szRegisterProduct, uirow );
4922     msiobj_release( &uirow->hdr );
4923
4924     RegCloseKey(hkey);
4925     return ERROR_SUCCESS;
4926 }
4927
4928 static UINT ACTION_InstallExecute(MSIPACKAGE *package)
4929 {
4930     return execute_script(package, SCRIPT_INSTALL);
4931 }
4932
4933 static UINT ITERATE_UnpublishIcon( MSIRECORD *row, LPVOID param )
4934 {
4935     MSIPACKAGE *package = param;
4936     const WCHAR *icon = MSI_RecordGetString( row, 1 );
4937     WCHAR *p, *icon_path;
4938
4939     if (!icon) return ERROR_SUCCESS;
4940     if ((icon_path = msi_build_icon_path( package, icon )))
4941     {
4942         TRACE("removing icon file %s\n", debugstr_w(icon_path));
4943         DeleteFileW( icon_path );
4944         if ((p = strrchrW( icon_path, '\\' )))
4945         {
4946             *p = 0;
4947             RemoveDirectoryW( icon_path );
4948         }
4949         msi_free( icon_path );
4950     }
4951     return ERROR_SUCCESS;
4952 }
4953
4954 static UINT msi_unpublish_icons( MSIPACKAGE *package )
4955 {
4956     static const WCHAR query[]= {
4957         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','I','c','o','n','`',0};
4958     MSIQUERY *view;
4959     UINT r;
4960
4961     r = MSI_DatabaseOpenViewW( package->db, query, &view );
4962     if (r == ERROR_SUCCESS)
4963     {
4964         r = MSI_IterateRecords( view, NULL, ITERATE_UnpublishIcon, package );
4965         msiobj_release( &view->hdr );
4966         if (r != ERROR_SUCCESS)
4967             return r;
4968     }
4969     return ERROR_SUCCESS;
4970 }
4971
4972 static UINT msi_unpublish_product( MSIPACKAGE *package, const WCHAR *remove )
4973 {
4974     static const WCHAR szUpgradeCode[] = {'U','p','g','r','a','d','e','C','o','d','e',0};
4975     WCHAR *upgrade, **features;
4976     BOOL full_uninstall = TRUE;
4977     MSIFEATURE *feature;
4978     MSIPATCHINFO *patch;
4979     UINT i;
4980
4981     LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
4982     {
4983         if (feature->Action == INSTALLSTATE_LOCAL) full_uninstall = FALSE;
4984     }
4985     features = msi_split_string( remove, ',' );
4986     for (i = 0; features && features[i]; i++)
4987     {
4988         if (!strcmpW( features[i], szAll )) full_uninstall = TRUE;
4989     }
4990     msi_free(features);
4991
4992     if (!full_uninstall)
4993         return ERROR_SUCCESS;
4994
4995     MSIREG_DeleteProductKey(package->ProductCode);
4996     MSIREG_DeleteUserDataProductKey(package->ProductCode);
4997     MSIREG_DeleteUninstallKey(package->ProductCode, package->platform);
4998
4999     MSIREG_DeleteLocalClassesProductKey(package->ProductCode);
5000     MSIREG_DeleteLocalClassesFeaturesKey(package->ProductCode);
5001     MSIREG_DeleteUserProductKey(package->ProductCode);
5002     MSIREG_DeleteUserFeaturesKey(package->ProductCode);
5003
5004     upgrade = msi_dup_property(package->db, szUpgradeCode);
5005     if (upgrade)
5006     {
5007         MSIREG_DeleteUserUpgradeCodesKey(upgrade);
5008         MSIREG_DeleteClassesUpgradeCodesKey(upgrade);
5009         msi_free(upgrade);
5010     }
5011
5012     LIST_FOR_EACH_ENTRY(patch, &package->patches, MSIPATCHINFO, entry)
5013     {
5014         MSIREG_DeleteUserDataPatchKey(patch->patchcode, package->Context);
5015         if (!strcmpW( package->ProductCode, patch->products ))
5016         {
5017             TRACE("removing local patch package %s\n", debugstr_w(patch->localfile));
5018             patch->delete_on_close = TRUE;
5019         }
5020         /* FIXME: remove local patch package if this is the last product */
5021     }
5022     TRACE("removing local package %s\n", debugstr_w(package->localfile));
5023     package->delete_on_close = TRUE;
5024
5025     msi_unpublish_icons( package );
5026     return ERROR_SUCCESS;
5027 }
5028
5029 static UINT ACTION_InstallFinalize(MSIPACKAGE *package)
5030 {
5031     UINT rc;
5032     WCHAR *remove;
5033
5034     /* turn off scheduling */
5035     package->script->CurrentlyScripting= FALSE;
5036
5037     /* first do the same as an InstallExecute */
5038     rc = ACTION_InstallExecute(package);
5039     if (rc != ERROR_SUCCESS)
5040         return rc;
5041
5042     /* then handle commit actions */
5043     rc = execute_script(package, SCRIPT_COMMIT);
5044     if (rc != ERROR_SUCCESS)
5045         return rc;
5046
5047     remove = msi_dup_property(package->db, szRemove);
5048     rc = msi_unpublish_product(package, remove);
5049     msi_free(remove);
5050     return rc;
5051 }
5052
5053 UINT ACTION_ForceReboot(MSIPACKAGE *package)
5054 {
5055     static const WCHAR RunOnce[] = {
5056     'S','o','f','t','w','a','r','e','\\',
5057     'M','i','c','r','o','s','o','f','t','\\',
5058     'W','i','n','d','o','w','s','\\',
5059     'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
5060     'R','u','n','O','n','c','e',0};
5061     static const WCHAR InstallRunOnce[] = {
5062     'S','o','f','t','w','a','r','e','\\',
5063     'M','i','c','r','o','s','o','f','t','\\',
5064     'W','i','n','d','o','w','s','\\',
5065     'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
5066     'I','n','s','t','a','l','l','e','r','\\',
5067     'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
5068
5069     static const WCHAR msiexec_fmt[] = {
5070     '%','s',
5071     '\\','M','s','i','E','x','e','c','.','e','x','e',' ','/','@',' ',
5072     '\"','%','s','\"',0};
5073     static const WCHAR install_fmt[] = {
5074     '/','I',' ','\"','%','s','\"',' ',
5075     'A','F','T','E','R','R','E','B','O','O','T','=','1',' ',
5076     'R','U','N','O','N','C','E','E','N','T','R','Y','=','\"','%','s','\"',0};
5077     WCHAR buffer[256], sysdir[MAX_PATH];
5078     HKEY hkey;
5079     WCHAR squished_pc[100];
5080
5081     squash_guid(package->ProductCode,squished_pc);
5082
5083     GetSystemDirectoryW(sysdir, sizeof(sysdir)/sizeof(sysdir[0]));
5084     RegCreateKeyW(HKEY_LOCAL_MACHINE,RunOnce,&hkey);
5085     snprintfW(buffer,sizeof(buffer)/sizeof(buffer[0]),msiexec_fmt,sysdir,
5086      squished_pc);
5087
5088     msi_reg_set_val_str( hkey, squished_pc, buffer );
5089     RegCloseKey(hkey);
5090
5091     TRACE("Reboot command %s\n",debugstr_w(buffer));
5092
5093     RegCreateKeyW(HKEY_LOCAL_MACHINE,InstallRunOnce,&hkey);
5094     sprintfW(buffer,install_fmt,package->ProductCode,squished_pc);
5095
5096     msi_reg_set_val_str( hkey, squished_pc, buffer );
5097     RegCloseKey(hkey);
5098
5099     return ERROR_INSTALL_SUSPEND;
5100 }
5101
5102 WCHAR *msi_build_error_string( MSIPACKAGE *package, UINT error, DWORD count, ... )
5103 {
5104     static const WCHAR query[] =
5105         {'S','E','L','E','C','T',' ','`','M','e','s','s','a','g','e','`',' ',
5106          'F','R','O','M',' ','`','E','r','r','o','r','`',' ','W','H','E','R','E',' ',
5107          '`','E','r','r','o','r','`',' ','=',' ','%','i',0};
5108     MSIRECORD *rec, *row;
5109     DWORD i, size = 0;
5110     va_list va;
5111     const WCHAR *str;
5112     WCHAR *data;
5113
5114     if (!(row = MSI_QueryGetRecord( package->db, query, error ))) return 0;
5115
5116     rec = MSI_CreateRecord( count + 2 );
5117     str = MSI_RecordGetString( row, 1 );
5118     MSI_RecordSetStringW( rec, 0, str );
5119     msiobj_release( &row->hdr );
5120     MSI_RecordSetInteger( rec, 1, error );
5121
5122     va_start( va, count );
5123     for (i = 0; i < count; i++)
5124     {
5125         str = va_arg( va, const WCHAR *);
5126         MSI_RecordSetStringW( rec, i + 2, str );
5127     }
5128     va_end( va );
5129
5130     MSI_FormatRecordW( package, rec, NULL, &size );
5131     size++;
5132     data = msi_alloc( size * sizeof(WCHAR) );
5133     if (size > 1) MSI_FormatRecordW( package, rec, data, &size );
5134     else data[0] = 0;
5135     msiobj_release( &rec->hdr );
5136     return data;
5137 }
5138
5139 static UINT ACTION_ResolveSource(MSIPACKAGE* package)
5140 {
5141     DWORD attrib;
5142     UINT rc;
5143
5144     /*
5145      * We are currently doing what should be done here in the top level Install
5146      * however for Administrative and uninstalls this step will be needed
5147      */
5148     if (!package->PackagePath)
5149         return ERROR_SUCCESS;
5150
5151     msi_set_sourcedir_props(package, TRUE);
5152
5153     attrib = GetFileAttributesW(package->db->path);
5154     if (attrib == INVALID_FILE_ATTRIBUTES)
5155     {
5156         LPWSTR prompt, msg;
5157         DWORD size = 0;
5158
5159         rc = MsiSourceListGetInfoW(package->ProductCode, NULL, 
5160                 package->Context, MSICODE_PRODUCT,
5161                 INSTALLPROPERTY_DISKPROMPTW,NULL,&size);
5162         if (rc == ERROR_MORE_DATA)
5163         {
5164             prompt = msi_alloc(size * sizeof(WCHAR));
5165             MsiSourceListGetInfoW(package->ProductCode, NULL, 
5166                     package->Context, MSICODE_PRODUCT,
5167                     INSTALLPROPERTY_DISKPROMPTW,prompt,&size);
5168         }
5169         else
5170             prompt = strdupW(package->db->path);
5171
5172         msg = msi_build_error_string(package, 1302, 1, prompt);
5173         msi_free(prompt);
5174         while(attrib == INVALID_FILE_ATTRIBUTES)
5175         {
5176             rc = MessageBoxW(NULL, msg, NULL, MB_OKCANCEL);
5177             if (rc == IDCANCEL)
5178             {
5179                 msi_free(msg);
5180                 return ERROR_INSTALL_USEREXIT;
5181             }
5182             attrib = GetFileAttributesW(package->db->path);
5183         }
5184         msi_free(msg);
5185         rc = ERROR_SUCCESS;
5186     }
5187     else
5188         return ERROR_SUCCESS;
5189
5190     return rc;
5191 }
5192
5193 static UINT ACTION_RegisterUser(MSIPACKAGE *package)
5194 {
5195     HKEY hkey = 0;
5196     LPWSTR buffer, productid = NULL;
5197     UINT i, rc = ERROR_SUCCESS;
5198     MSIRECORD *uirow;
5199
5200     static const WCHAR szPropKeys[][80] = 
5201     {
5202         {'P','r','o','d','u','c','t','I','D',0},
5203         {'U','S','E','R','N','A','M','E',0},
5204         {'C','O','M','P','A','N','Y','N','A','M','E',0},
5205         {0},
5206     };
5207
5208     static const WCHAR szRegKeys[][80] = 
5209     {
5210         {'P','r','o','d','u','c','t','I','D',0},
5211         {'R','e','g','O','w','n','e','r',0},
5212         {'R','e','g','C','o','m','p','a','n','y',0},
5213         {0},
5214     };
5215
5216     if (msi_check_unpublish(package))
5217     {
5218         MSIREG_DeleteUserDataProductKey(package->ProductCode);
5219         goto end;
5220     }
5221
5222     productid = msi_dup_property( package->db, INSTALLPROPERTY_PRODUCTIDW );
5223     if (!productid)
5224         goto end;
5225
5226     rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context,
5227                                  NULL, &hkey, TRUE);
5228     if (rc != ERROR_SUCCESS)
5229         goto end;
5230
5231     for( i = 0; szPropKeys[i][0]; i++ )
5232     {
5233         buffer = msi_dup_property( package->db, szPropKeys[i] );
5234         msi_reg_set_val_str( hkey, szRegKeys[i], buffer );
5235         msi_free( buffer );
5236     }
5237
5238 end:
5239     uirow = MSI_CreateRecord( 1 );
5240     MSI_RecordSetStringW( uirow, 1, productid );
5241     msi_ui_actiondata( package, szRegisterUser, uirow );
5242     msiobj_release( &uirow->hdr );
5243
5244     msi_free(productid);
5245     RegCloseKey(hkey);
5246     return rc;
5247 }
5248
5249
5250 static UINT ACTION_ExecuteAction(MSIPACKAGE *package)
5251 {
5252     UINT rc;
5253
5254     package->script->InWhatSequence |= SEQUENCE_EXEC;
5255     rc = ACTION_ProcessExecSequence(package,FALSE);
5256     return rc;
5257 }
5258
5259 WCHAR *msi_create_component_advertise_string( MSIPACKAGE *package, MSICOMPONENT *component, const WCHAR *feature )
5260 {
5261     static const WCHAR fmt[] = {'%','s','%','s','%','c','%','s',0};
5262     WCHAR productid_85[21], component_85[21], *ret;
5263     GUID clsid;
5264     DWORD sz;
5265
5266     /* > is used if there is a component GUID and < if not.  */
5267
5268     productid_85[0] = 0;
5269     component_85[0] = 0;
5270     CLSIDFromString( package->ProductCode, &clsid );
5271
5272     encode_base85_guid( &clsid, productid_85 );
5273     if (component)
5274     {
5275         CLSIDFromString( component->ComponentId, &clsid );
5276         encode_base85_guid( &clsid, component_85 );
5277     }
5278
5279     TRACE("product=%s feature=%s component=%s\n", debugstr_w(productid_85), debugstr_w(feature),
5280           debugstr_w(component_85));
5281
5282     sz = 20 + strlenW( feature ) + 20 + 3;
5283     ret = msi_alloc_zero( sz * sizeof(WCHAR) );
5284     if (ret) sprintfW( ret, fmt, productid_85, feature, component ? '>' : '<', component_85 );
5285     return ret;
5286 }
5287
5288 static UINT ITERATE_PublishComponent(MSIRECORD *rec, LPVOID param)
5289 {
5290     MSIPACKAGE *package = param;
5291     LPCWSTR compgroupid, component, feature, qualifier, text;
5292     LPWSTR advertise = NULL, output = NULL, existing = NULL, p, q;
5293     HKEY hkey = NULL;
5294     UINT rc;
5295     MSICOMPONENT *comp;
5296     MSIFEATURE *feat;
5297     DWORD sz;
5298     MSIRECORD *uirow;
5299     int len;
5300
5301     feature = MSI_RecordGetString(rec, 5);
5302     feat = msi_get_loaded_feature(package, feature);
5303     if (!feat)
5304         return ERROR_SUCCESS;
5305
5306     feat->Action = msi_get_feature_action( package, feat );
5307     if (feat->Action != INSTALLSTATE_LOCAL &&
5308         feat->Action != INSTALLSTATE_SOURCE &&
5309         feat->Action != INSTALLSTATE_ADVERTISED)
5310     {
5311         TRACE("feature not scheduled for installation %s\n", debugstr_w(feature));
5312         return ERROR_SUCCESS;
5313     }
5314
5315     component = MSI_RecordGetString(rec, 3);
5316     comp = msi_get_loaded_component(package, component);
5317     if (!comp)
5318         return ERROR_SUCCESS;
5319
5320     compgroupid = MSI_RecordGetString(rec,1);
5321     qualifier = MSI_RecordGetString(rec,2);
5322
5323     rc = MSIREG_OpenUserComponentsKey(compgroupid, &hkey, TRUE);
5324     if (rc != ERROR_SUCCESS)
5325         goto end;
5326
5327     advertise = msi_create_component_advertise_string( package, comp, feature );
5328     text = MSI_RecordGetString( rec, 4 );
5329     if (text)
5330     {
5331         p = msi_alloc( (strlenW( advertise ) + strlenW( text ) + 1) * sizeof(WCHAR) );
5332         strcpyW( p, advertise );
5333         strcatW( p, text );
5334         msi_free( advertise );
5335         advertise = p;
5336     }
5337     existing = msi_reg_get_val_str( hkey, qualifier );
5338
5339     sz = strlenW( advertise ) + 1;
5340     if (existing)
5341     {
5342         for (p = existing; *p; p += len)
5343         {
5344             len = strlenW( p ) + 1;
5345             if (strcmpW( advertise, p )) sz += len;
5346         }
5347     }
5348     if (!(output = msi_alloc( (sz + 1) * sizeof(WCHAR) )))
5349     {
5350         rc = ERROR_OUTOFMEMORY;
5351         goto end;
5352     }
5353     q = output;
5354     if (existing)
5355     {
5356         for (p = existing; *p; p += len)
5357         {
5358             len = strlenW( p ) + 1;
5359             if (strcmpW( advertise, p ))
5360             {
5361                 memcpy( q, p, len * sizeof(WCHAR) );
5362                 q += len;
5363             }
5364         }
5365     }
5366     strcpyW( q, advertise );
5367     q[strlenW( q ) + 1] = 0;
5368
5369     msi_reg_set_val_multi_str( hkey, qualifier, output );
5370     
5371 end:
5372     RegCloseKey(hkey);
5373     msi_free( output );
5374     msi_free( advertise );
5375     msi_free( existing );
5376
5377     /* the UI chunk */
5378     uirow = MSI_CreateRecord( 2 );
5379     MSI_RecordSetStringW( uirow, 1, compgroupid );
5380     MSI_RecordSetStringW( uirow, 2, qualifier);
5381     msi_ui_actiondata( package, szPublishComponents, uirow );
5382     msiobj_release( &uirow->hdr );
5383     /* FIXME: call ui_progress? */
5384
5385     return rc;
5386 }
5387
5388 /*
5389  * At present I am ignorning the advertised components part of this and only
5390  * focusing on the qualified component sets
5391  */
5392 static UINT ACTION_PublishComponents(MSIPACKAGE *package)
5393 {
5394     static const WCHAR query[] = {
5395         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5396         '`','P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','`',0};
5397     MSIQUERY *view;
5398     UINT rc;
5399     
5400     rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5401     if (rc != ERROR_SUCCESS)
5402         return ERROR_SUCCESS;
5403
5404     rc = MSI_IterateRecords(view, NULL, ITERATE_PublishComponent, package);
5405     msiobj_release(&view->hdr);
5406     return rc;
5407 }
5408
5409 static UINT ITERATE_UnpublishComponent( MSIRECORD *rec, LPVOID param )
5410 {
5411     static const WCHAR szInstallerComponents[] = {
5412         'S','o','f','t','w','a','r','e','\\',
5413         'M','i','c','r','o','s','o','f','t','\\',
5414         'I','n','s','t','a','l','l','e','r','\\',
5415         'C','o','m','p','o','n','e','n','t','s','\\',0};
5416
5417     MSIPACKAGE *package = param;
5418     LPCWSTR compgroupid, component, feature, qualifier;
5419     MSICOMPONENT *comp;
5420     MSIFEATURE *feat;
5421     MSIRECORD *uirow;
5422     WCHAR squashed[GUID_SIZE], keypath[MAX_PATH];
5423     LONG res;
5424
5425     feature = MSI_RecordGetString( rec, 5 );
5426     feat = msi_get_loaded_feature( package, feature );
5427     if (!feat)
5428         return ERROR_SUCCESS;
5429
5430     feat->Action = msi_get_feature_action( package, feat );
5431     if (feat->Action != INSTALLSTATE_ABSENT)
5432     {
5433         TRACE("feature not scheduled for removal %s\n", debugstr_w(feature));
5434         return ERROR_SUCCESS;
5435     }
5436
5437     component = MSI_RecordGetString( rec, 3 );
5438     comp = msi_get_loaded_component( package, component );
5439     if (!comp)
5440         return ERROR_SUCCESS;
5441
5442     compgroupid = MSI_RecordGetString( rec, 1 );
5443     qualifier = MSI_RecordGetString( rec, 2 );
5444
5445     squash_guid( compgroupid, squashed );
5446     strcpyW( keypath, szInstallerComponents );
5447     strcatW( keypath, squashed );
5448
5449     res = RegDeleteKeyW( HKEY_CURRENT_USER, keypath );
5450     if (res != ERROR_SUCCESS)
5451     {
5452         WARN("Unable to delete component key %d\n", res);
5453     }
5454
5455     uirow = MSI_CreateRecord( 2 );
5456     MSI_RecordSetStringW( uirow, 1, compgroupid );
5457     MSI_RecordSetStringW( uirow, 2, qualifier );
5458     msi_ui_actiondata( package, szUnpublishComponents, uirow );
5459     msiobj_release( &uirow->hdr );
5460
5461     return ERROR_SUCCESS;
5462 }
5463
5464 static UINT ACTION_UnpublishComponents( MSIPACKAGE *package )
5465 {
5466     static const WCHAR query[] = {
5467         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5468         '`','P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','`',0};
5469     MSIQUERY *view;
5470     UINT rc;
5471
5472     rc = MSI_DatabaseOpenViewW( package->db, query, &view );
5473     if (rc != ERROR_SUCCESS)
5474         return ERROR_SUCCESS;
5475
5476     rc = MSI_IterateRecords( view, NULL, ITERATE_UnpublishComponent, package );
5477     msiobj_release( &view->hdr );
5478     return rc;
5479 }
5480
5481 static UINT ITERATE_InstallService(MSIRECORD *rec, LPVOID param)
5482 {
5483     static const WCHAR query[] =
5484         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5485          '`','C','o','m','p','o','n','e','n','t','`',' ','W','H','E','R','E',' ',
5486          '`','C','o','m','p','o','n','e','n','t','`',' ','=','\'','%','s','\'',0};
5487     MSIPACKAGE *package = param;
5488     MSICOMPONENT *component;
5489     MSIRECORD *row;
5490     MSIFILE *file;
5491     SC_HANDLE hscm = NULL, service = NULL;
5492     LPCWSTR comp, key;
5493     LPWSTR name = NULL, disp = NULL, load_order = NULL, serv_name = NULL;
5494     LPWSTR depends = NULL, pass = NULL, args = NULL, image_path = NULL;
5495     DWORD serv_type, start_type, err_control;
5496     SERVICE_DESCRIPTIONW sd = {NULL};
5497
5498     comp = MSI_RecordGetString( rec, 12 );
5499     component = msi_get_loaded_component( package, comp );
5500     if (!component)
5501     {
5502         WARN("service component not found\n");
5503         goto done;
5504     }
5505     component->Action = msi_get_component_action( package, component );
5506     if (component->Action != INSTALLSTATE_LOCAL)
5507     {
5508         TRACE("component not scheduled for installation %s\n", debugstr_w(comp));
5509         goto done;
5510     }
5511     hscm = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASEW, GENERIC_WRITE);
5512     if (!hscm)
5513     {
5514         ERR("Failed to open the SC Manager!\n");
5515         goto done;
5516     }
5517
5518     start_type = MSI_RecordGetInteger(rec, 5);
5519     if (start_type == SERVICE_BOOT_START || start_type == SERVICE_SYSTEM_START)
5520         goto done;
5521
5522     deformat_string(package, MSI_RecordGetString(rec, 2), &name);
5523     deformat_string(package, MSI_RecordGetString(rec, 3), &disp);
5524     serv_type = MSI_RecordGetInteger(rec, 4);
5525     err_control = MSI_RecordGetInteger(rec, 6);
5526     deformat_string(package, MSI_RecordGetString(rec, 7), &load_order);
5527     deformat_string(package, MSI_RecordGetString(rec, 8), &depends);
5528     deformat_string(package, MSI_RecordGetString(rec, 9), &serv_name);
5529     deformat_string(package, MSI_RecordGetString(rec, 10), &pass);
5530     deformat_string(package, MSI_RecordGetString(rec, 11), &args);
5531     deformat_string(package, MSI_RecordGetString(rec, 13), &sd.lpDescription);
5532
5533     /* fetch the service path */
5534     row = MSI_QueryGetRecord(package->db, query, comp);
5535     if (!row)
5536     {
5537         ERR("Query failed\n");
5538         goto done;
5539     }
5540     key = MSI_RecordGetString(row, 6);
5541     file = msi_get_loaded_file(package, key);
5542     msiobj_release(&row->hdr);
5543     if (!file)
5544     {
5545         ERR("Failed to load the service file\n");
5546         goto done;
5547     }
5548
5549     if (!args || !args[0]) image_path = file->TargetPath;
5550     else
5551     {
5552         int len = strlenW(file->TargetPath) + strlenW(args) + 2;
5553         if (!(image_path = msi_alloc(len * sizeof(WCHAR))))
5554             return ERROR_OUTOFMEMORY;
5555
5556         strcpyW(image_path, file->TargetPath);
5557         strcatW(image_path, szSpace);
5558         strcatW(image_path, args);
5559     }
5560     service = CreateServiceW(hscm, name, disp, GENERIC_ALL, serv_type,
5561                              start_type, err_control, image_path, load_order,
5562                              NULL, depends, serv_name, pass);
5563
5564     if (!service)
5565     {
5566         if (GetLastError() != ERROR_SERVICE_EXISTS)
5567             ERR("Failed to create service %s: %d\n", debugstr_w(name), GetLastError());
5568     }
5569     else if (sd.lpDescription)
5570     {
5571         if (!ChangeServiceConfig2W(service, SERVICE_CONFIG_DESCRIPTION, &sd))
5572             WARN("failed to set service description %u\n", GetLastError());
5573     }
5574
5575     if (image_path != file->TargetPath) msi_free(image_path);
5576 done:
5577     CloseServiceHandle(service);
5578     CloseServiceHandle(hscm);
5579     msi_free(name);
5580     msi_free(disp);
5581     msi_free(sd.lpDescription);
5582     msi_free(load_order);
5583     msi_free(serv_name);
5584     msi_free(pass);
5585     msi_free(depends);
5586     msi_free(args);
5587
5588     return ERROR_SUCCESS;
5589 }
5590
5591 static UINT ACTION_InstallServices( MSIPACKAGE *package )
5592 {
5593     static const WCHAR query[] = {
5594         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5595         'S','e','r','v','i','c','e','I','n','s','t','a','l','l',0};
5596     MSIQUERY *view;
5597     UINT rc;
5598     
5599     rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5600     if (rc != ERROR_SUCCESS)
5601         return ERROR_SUCCESS;
5602
5603     rc = MSI_IterateRecords(view, NULL, ITERATE_InstallService, package);
5604     msiobj_release(&view->hdr);
5605     return rc;
5606 }
5607
5608 /* converts arg1[~]arg2[~]arg3 to a list of ptrs to the strings */
5609 static LPCWSTR *msi_service_args_to_vector(LPWSTR args, DWORD *numargs)
5610 {
5611     LPCWSTR *vector, *temp_vector;
5612     LPWSTR p, q;
5613     DWORD sep_len;
5614
5615     static const WCHAR separator[] = {'[','~',']',0};
5616
5617     *numargs = 0;
5618     sep_len = sizeof(separator) / sizeof(WCHAR) - 1;
5619
5620     if (!args)
5621         return NULL;
5622
5623     vector = msi_alloc(sizeof(LPWSTR));
5624     if (!vector)
5625         return NULL;
5626
5627     p = args;
5628     do
5629     {
5630         (*numargs)++;
5631         vector[*numargs - 1] = p;
5632
5633         if ((q = strstrW(p, separator)))
5634         {
5635             *q = '\0';
5636
5637             temp_vector = msi_realloc(vector, (*numargs + 1) * sizeof(LPWSTR));
5638             if (!temp_vector)
5639             {
5640                 msi_free(vector);
5641                 return NULL;
5642             }
5643             vector = temp_vector;
5644
5645             p = q + sep_len;
5646         }
5647     } while (q);
5648
5649     return vector;
5650 }
5651
5652 static UINT ITERATE_StartService(MSIRECORD *rec, LPVOID param)
5653 {
5654     MSIPACKAGE *package = param;
5655     MSICOMPONENT *comp;
5656     MSIRECORD *uirow;
5657     SC_HANDLE scm = NULL, service = NULL;
5658     LPCWSTR component, *vector = NULL;
5659     LPWSTR name, args, display_name = NULL;
5660     DWORD event, numargs, len, wait, dummy;
5661     UINT r = ERROR_FUNCTION_FAILED;
5662     SERVICE_STATUS_PROCESS status;
5663     ULONGLONG start_time;
5664
5665     component = MSI_RecordGetString(rec, 6);
5666     comp = msi_get_loaded_component(package, component);
5667     if (!comp)
5668         return ERROR_SUCCESS;
5669
5670     comp->Action = msi_get_component_action( package, comp );
5671     if (comp->Action != INSTALLSTATE_LOCAL)
5672     {
5673         TRACE("component not scheduled for installation %s\n", debugstr_w(component));
5674         return ERROR_SUCCESS;
5675     }
5676
5677     deformat_string(package, MSI_RecordGetString(rec, 2), &name);
5678     deformat_string(package, MSI_RecordGetString(rec, 4), &args);
5679     event = MSI_RecordGetInteger(rec, 3);
5680     wait = MSI_RecordGetInteger(rec, 5);
5681
5682     if (!(event & msidbServiceControlEventStart))
5683     {
5684         r = ERROR_SUCCESS;
5685         goto done;
5686     }
5687
5688     scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
5689     if (!scm)
5690     {
5691         ERR("Failed to open the service control manager\n");
5692         goto done;
5693     }
5694
5695     len = 0;
5696     if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
5697         GetLastError() == ERROR_INSUFFICIENT_BUFFER)
5698     {
5699         if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
5700             GetServiceDisplayNameW( scm, name, display_name, &len );
5701     }
5702
5703     service = OpenServiceW(scm, name, SERVICE_START|SERVICE_QUERY_STATUS);
5704     if (!service)
5705     {
5706         ERR("Failed to open service %s (%u)\n", debugstr_w(name), GetLastError());
5707         goto done;
5708     }
5709
5710     vector = msi_service_args_to_vector(args, &numargs);
5711
5712     if (!StartServiceW(service, numargs, vector) &&
5713         GetLastError() != ERROR_SERVICE_ALREADY_RUNNING)
5714     {
5715         ERR("Failed to start service %s (%u)\n", debugstr_w(name), GetLastError());
5716         goto done;
5717     }
5718
5719     r = ERROR_SUCCESS;
5720     if (wait)
5721     {
5722         /* wait for at most 30 seconds for the service to be up and running */
5723         if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO,
5724             (BYTE *)&status, sizeof(SERVICE_STATUS_PROCESS), &dummy))
5725         {
5726             TRACE("failed to query service status (%u)\n", GetLastError());
5727             goto done;
5728         }
5729         start_time = GetTickCount64();
5730         while (status.dwCurrentState == SERVICE_START_PENDING)
5731         {
5732             if (GetTickCount64() - start_time > 30000) break;
5733             Sleep(1000);
5734             if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO,
5735                 (BYTE *)&status, sizeof(SERVICE_STATUS_PROCESS), &dummy))
5736             {
5737                 TRACE("failed to query service status (%u)\n", GetLastError());
5738                 goto done;
5739             }
5740         }
5741         if (status.dwCurrentState != SERVICE_RUNNING)
5742         {
5743             WARN("service failed to start %u\n", status.dwCurrentState);
5744             r = ERROR_FUNCTION_FAILED;
5745         }
5746     }
5747
5748 done:
5749     uirow = MSI_CreateRecord( 2 );
5750     MSI_RecordSetStringW( uirow, 1, display_name );
5751     MSI_RecordSetStringW( uirow, 2, name );
5752     msi_ui_actiondata( package, szStartServices, uirow );
5753     msiobj_release( &uirow->hdr );
5754
5755     CloseServiceHandle(service);
5756     CloseServiceHandle(scm);
5757
5758     msi_free(name);
5759     msi_free(args);
5760     msi_free(vector);
5761     msi_free(display_name);
5762     return r;
5763 }
5764
5765 static UINT ACTION_StartServices( MSIPACKAGE *package )
5766 {
5767     static const WCHAR query[] = {
5768         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5769         'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
5770     MSIQUERY *view;
5771     UINT rc;
5772
5773     rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5774     if (rc != ERROR_SUCCESS)
5775         return ERROR_SUCCESS;
5776
5777     rc = MSI_IterateRecords(view, NULL, ITERATE_StartService, package);
5778     msiobj_release(&view->hdr);
5779     return rc;
5780 }
5781
5782 static BOOL stop_service_dependents(SC_HANDLE scm, SC_HANDLE service)
5783 {
5784     DWORD i, needed, count;
5785     ENUM_SERVICE_STATUSW *dependencies;
5786     SERVICE_STATUS ss;
5787     SC_HANDLE depserv;
5788
5789     if (EnumDependentServicesW(service, SERVICE_ACTIVE, NULL,
5790                                0, &needed, &count))
5791         return TRUE;
5792
5793     if (GetLastError() != ERROR_MORE_DATA)
5794         return FALSE;
5795
5796     dependencies = msi_alloc(needed);
5797     if (!dependencies)
5798         return FALSE;
5799
5800     if (!EnumDependentServicesW(service, SERVICE_ACTIVE, dependencies,
5801                                 needed, &needed, &count))
5802         goto error;
5803
5804     for (i = 0; i < count; i++)
5805     {
5806         depserv = OpenServiceW(scm, dependencies[i].lpServiceName,
5807                                SERVICE_STOP | SERVICE_QUERY_STATUS);
5808         if (!depserv)
5809             goto error;
5810
5811         if (!ControlService(depserv, SERVICE_CONTROL_STOP, &ss))
5812             goto error;
5813     }
5814
5815     return TRUE;
5816
5817 error:
5818     msi_free(dependencies);
5819     return FALSE;
5820 }
5821
5822 static UINT stop_service( LPCWSTR name )
5823 {
5824     SC_HANDLE scm = NULL, service = NULL;
5825     SERVICE_STATUS status;
5826     SERVICE_STATUS_PROCESS ssp;
5827     DWORD needed;
5828
5829     scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
5830     if (!scm)
5831     {
5832         WARN("Failed to open the SCM: %d\n", GetLastError());
5833         goto done;
5834     }
5835
5836     service = OpenServiceW(scm, name,
5837                            SERVICE_STOP |
5838                            SERVICE_QUERY_STATUS |
5839                            SERVICE_ENUMERATE_DEPENDENTS);
5840     if (!service)
5841     {
5842         WARN("Failed to open service (%s): %d\n", debugstr_w(name), GetLastError());
5843         goto done;
5844     }
5845
5846     if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp,
5847                               sizeof(SERVICE_STATUS_PROCESS), &needed))
5848     {
5849         WARN("Failed to query service status (%s): %d\n", debugstr_w(name), GetLastError());
5850         goto done;
5851     }
5852
5853     if (ssp.dwCurrentState == SERVICE_STOPPED)
5854         goto done;
5855
5856     stop_service_dependents(scm, service);
5857
5858     if (!ControlService(service, SERVICE_CONTROL_STOP, &status))
5859         WARN("Failed to stop service (%s): %d\n", debugstr_w(name), GetLastError());
5860
5861 done:
5862     CloseServiceHandle(service);
5863     CloseServiceHandle(scm);
5864
5865     return ERROR_SUCCESS;
5866 }
5867
5868 static UINT ITERATE_StopService( MSIRECORD *rec, LPVOID param )
5869 {
5870     MSIPACKAGE *package = param;
5871     MSICOMPONENT *comp;
5872     MSIRECORD *uirow;
5873     LPCWSTR component;
5874     LPWSTR name = NULL, display_name = NULL;
5875     DWORD event, len;
5876     SC_HANDLE scm;
5877
5878     event = MSI_RecordGetInteger( rec, 3 );
5879     if (!(event & msidbServiceControlEventStop))
5880         return ERROR_SUCCESS;
5881
5882     component = MSI_RecordGetString( rec, 6 );
5883     comp = msi_get_loaded_component( package, component );
5884     if (!comp)
5885         return ERROR_SUCCESS;
5886
5887     comp->Action = msi_get_component_action( package, comp );
5888     if (comp->Action != INSTALLSTATE_ABSENT)
5889     {
5890         TRACE("component not scheduled for removal %s\n", debugstr_w(component));
5891         return ERROR_SUCCESS;
5892     }
5893
5894     scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_CONNECT );
5895     if (!scm)
5896     {
5897         ERR("Failed to open the service control manager\n");
5898         goto done;
5899     }
5900
5901     len = 0;
5902     if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
5903         GetLastError() == ERROR_INSUFFICIENT_BUFFER)
5904     {
5905         if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
5906             GetServiceDisplayNameW( scm, name, display_name, &len );
5907     }
5908     CloseServiceHandle( scm );
5909
5910     deformat_string( package, MSI_RecordGetString( rec, 2 ), &name );
5911     stop_service( name );
5912
5913 done:
5914     uirow = MSI_CreateRecord( 2 );
5915     MSI_RecordSetStringW( uirow, 1, display_name );
5916     MSI_RecordSetStringW( uirow, 2, name );
5917     msi_ui_actiondata( package, szStopServices, uirow );
5918     msiobj_release( &uirow->hdr );
5919
5920     msi_free( name );
5921     msi_free( display_name );
5922     return ERROR_SUCCESS;
5923 }
5924
5925 static UINT ACTION_StopServices( MSIPACKAGE *package )
5926 {
5927     static const WCHAR query[] = {
5928         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5929         'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
5930     MSIQUERY *view;
5931     UINT rc;
5932
5933     rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5934     if (rc != ERROR_SUCCESS)
5935         return ERROR_SUCCESS;
5936
5937     rc = MSI_IterateRecords(view, NULL, ITERATE_StopService, package);
5938     msiobj_release(&view->hdr);
5939     return rc;
5940 }
5941
5942 static UINT ITERATE_DeleteService( MSIRECORD *rec, LPVOID param )
5943 {
5944     MSIPACKAGE *package = param;
5945     MSICOMPONENT *comp;
5946     MSIRECORD *uirow;
5947     LPWSTR name = NULL, display_name = NULL;
5948     DWORD event, len;
5949     SC_HANDLE scm = NULL, service = NULL;
5950
5951     comp = msi_get_loaded_component( package, MSI_RecordGetString(rec, 6) );
5952     if (!comp)
5953         return ERROR_SUCCESS;
5954
5955     event = MSI_RecordGetInteger( rec, 3 );
5956     deformat_string( package, MSI_RecordGetString(rec, 2), &name );
5957
5958     comp->Action = msi_get_component_action( package, comp );
5959     if (!(comp->Action == INSTALLSTATE_LOCAL && (event & msidbServiceControlEventDelete)) &&
5960         !(comp->Action == INSTALLSTATE_ABSENT && (event & msidbServiceControlEventUninstallDelete)))
5961     {
5962         TRACE("service %s not scheduled for removal\n", debugstr_w(name));
5963         msi_free( name );
5964         return ERROR_SUCCESS;
5965     }
5966     stop_service( name );
5967
5968     scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_ALL_ACCESS );
5969     if (!scm)
5970     {
5971         WARN("Failed to open the SCM: %d\n", GetLastError());
5972         goto done;
5973     }
5974
5975     len = 0;
5976     if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
5977         GetLastError() == ERROR_INSUFFICIENT_BUFFER)
5978     {
5979         if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
5980             GetServiceDisplayNameW( scm, name, display_name, &len );
5981     }
5982
5983     service = OpenServiceW( scm, name, DELETE );
5984     if (!service)
5985     {
5986         WARN("Failed to open service (%s): %u\n", debugstr_w(name), GetLastError());
5987         goto done;
5988     }
5989
5990     if (!DeleteService( service ))
5991         WARN("Failed to delete service (%s): %u\n", debugstr_w(name), GetLastError());
5992
5993 done:
5994     uirow = MSI_CreateRecord( 2 );
5995     MSI_RecordSetStringW( uirow, 1, display_name );
5996     MSI_RecordSetStringW( uirow, 2, name );
5997     msi_ui_actiondata( package, szDeleteServices, uirow );
5998     msiobj_release( &uirow->hdr );
5999
6000     CloseServiceHandle( service );
6001     CloseServiceHandle( scm );
6002     msi_free( name );
6003     msi_free( display_name );
6004
6005     return ERROR_SUCCESS;
6006 }
6007
6008 static UINT ACTION_DeleteServices( MSIPACKAGE *package )
6009 {
6010     static const WCHAR query[] = {
6011         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6012         'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
6013     MSIQUERY *view;
6014     UINT rc;
6015
6016     rc = MSI_DatabaseOpenViewW( package->db, query, &view );
6017     if (rc != ERROR_SUCCESS)
6018         return ERROR_SUCCESS;
6019
6020     rc = MSI_IterateRecords( view, NULL, ITERATE_DeleteService, package );
6021     msiobj_release( &view->hdr );
6022     return rc;
6023 }
6024
6025 static UINT ITERATE_InstallODBCDriver( MSIRECORD *rec, LPVOID param )
6026 {
6027     MSIPACKAGE *package = param;
6028     LPWSTR driver, driver_path, ptr;
6029     WCHAR outpath[MAX_PATH];
6030     MSIFILE *driver_file = NULL, *setup_file = NULL;
6031     MSICOMPONENT *comp;
6032     MSIRECORD *uirow;
6033     LPCWSTR desc, file_key, component;
6034     DWORD len, usage;
6035     UINT r = ERROR_SUCCESS;
6036
6037     static const WCHAR driver_fmt[] = {
6038         'D','r','i','v','e','r','=','%','s',0};
6039     static const WCHAR setup_fmt[] = {
6040         'S','e','t','u','p','=','%','s',0};
6041     static const WCHAR usage_fmt[] = {
6042         'F','i','l','e','U','s','a','g','e','=','1',0};
6043
6044     component = MSI_RecordGetString( rec, 2 );
6045     comp = msi_get_loaded_component( package, component );
6046     if (!comp)
6047         return ERROR_SUCCESS;
6048
6049     comp->Action = msi_get_component_action( package, comp );
6050     if (comp->Action != INSTALLSTATE_LOCAL)
6051     {
6052         TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6053         return ERROR_SUCCESS;
6054     }
6055     desc = MSI_RecordGetString(rec, 3);
6056
6057     file_key = MSI_RecordGetString( rec, 4 );
6058     if (file_key) driver_file = msi_get_loaded_file( package, file_key );
6059
6060     file_key = MSI_RecordGetString( rec, 5 );
6061     if (file_key) setup_file = msi_get_loaded_file( package, file_key );
6062
6063     if (!driver_file)
6064     {
6065         ERR("ODBC Driver entry not found!\n");
6066         return ERROR_FUNCTION_FAILED;
6067     }
6068
6069     len = lstrlenW(desc) + lstrlenW(driver_fmt) + lstrlenW(driver_file->FileName);
6070     if (setup_file)
6071         len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
6072     len += lstrlenW(usage_fmt) + 2; /* \0\0 */
6073
6074     driver = msi_alloc(len * sizeof(WCHAR));
6075     if (!driver)
6076         return ERROR_OUTOFMEMORY;
6077
6078     ptr = driver;
6079     lstrcpyW(ptr, desc);
6080     ptr += lstrlenW(ptr) + 1;
6081
6082     len = sprintfW(ptr, driver_fmt, driver_file->FileName);
6083     ptr += len + 1;
6084
6085     if (setup_file)
6086     {
6087         len = sprintfW(ptr, setup_fmt, setup_file->FileName);
6088         ptr += len + 1;
6089     }
6090
6091     lstrcpyW(ptr, usage_fmt);
6092     ptr += lstrlenW(ptr) + 1;
6093     *ptr = '\0';
6094
6095     if (!driver_file->TargetPath)
6096     {
6097         const WCHAR *dir = msi_get_target_folder( package, driver_file->Component->Directory );
6098         driver_file->TargetPath = msi_build_directory_name( 2, dir, driver_file->FileName );
6099     }
6100     driver_path = strdupW(driver_file->TargetPath);
6101     ptr = strrchrW(driver_path, '\\');
6102     if (ptr) *ptr = '\0';
6103
6104     if (!SQLInstallDriverExW(driver, driver_path, outpath, MAX_PATH,
6105                              NULL, ODBC_INSTALL_COMPLETE, &usage))
6106     {
6107         ERR("Failed to install SQL driver!\n");
6108         r = ERROR_FUNCTION_FAILED;
6109     }
6110
6111     uirow = MSI_CreateRecord( 5 );
6112     MSI_RecordSetStringW( uirow, 1, desc );
6113     MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6114     MSI_RecordSetStringW( uirow, 3, driver_file->Component->Directory );
6115     msi_ui_actiondata( package, szInstallODBC, uirow );
6116     msiobj_release( &uirow->hdr );
6117
6118     msi_free(driver);
6119     msi_free(driver_path);
6120
6121     return r;
6122 }
6123
6124 static UINT ITERATE_InstallODBCTranslator( MSIRECORD *rec, LPVOID param )
6125 {
6126     MSIPACKAGE *package = param;
6127     LPWSTR translator, translator_path, ptr;
6128     WCHAR outpath[MAX_PATH];
6129     MSIFILE *translator_file = NULL, *setup_file = NULL;
6130     MSICOMPONENT *comp;
6131     MSIRECORD *uirow;
6132     LPCWSTR desc, file_key, component;
6133     DWORD len, usage;
6134     UINT r = ERROR_SUCCESS;
6135
6136     static const WCHAR translator_fmt[] = {
6137         'T','r','a','n','s','l','a','t','o','r','=','%','s',0};
6138     static const WCHAR setup_fmt[] = {
6139         'S','e','t','u','p','=','%','s',0};
6140
6141     component = MSI_RecordGetString( rec, 2 );
6142     comp = msi_get_loaded_component( package, component );
6143     if (!comp)
6144         return ERROR_SUCCESS;
6145
6146     comp->Action = msi_get_component_action( package, comp );
6147     if (comp->Action != INSTALLSTATE_LOCAL)
6148     {
6149         TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6150         return ERROR_SUCCESS;
6151     }
6152     desc = MSI_RecordGetString(rec, 3);
6153
6154     file_key = MSI_RecordGetString( rec, 4 );
6155     if (file_key) translator_file = msi_get_loaded_file( package, file_key );
6156
6157     file_key = MSI_RecordGetString( rec, 5 );
6158     if (file_key) setup_file = msi_get_loaded_file( package, file_key );
6159
6160     if (!translator_file)
6161     {
6162         ERR("ODBC Translator entry not found!\n");
6163         return ERROR_FUNCTION_FAILED;
6164     }
6165
6166     len = lstrlenW(desc) + lstrlenW(translator_fmt) + lstrlenW(translator_file->FileName) + 2; /* \0\0 */
6167     if (setup_file)
6168         len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
6169
6170     translator = msi_alloc(len * sizeof(WCHAR));
6171     if (!translator)
6172         return ERROR_OUTOFMEMORY;
6173
6174     ptr = translator;
6175     lstrcpyW(ptr, desc);
6176     ptr += lstrlenW(ptr) + 1;
6177
6178     len = sprintfW(ptr, translator_fmt, translator_file->FileName);
6179     ptr += len + 1;
6180
6181     if (setup_file)
6182     {
6183         len = sprintfW(ptr, setup_fmt, setup_file->FileName);
6184         ptr += len + 1;
6185     }
6186     *ptr = '\0';
6187
6188     translator_path = strdupW(translator_file->TargetPath);
6189     ptr = strrchrW(translator_path, '\\');
6190     if (ptr) *ptr = '\0';
6191
6192     if (!SQLInstallTranslatorExW(translator, translator_path, outpath, MAX_PATH,
6193                                  NULL, ODBC_INSTALL_COMPLETE, &usage))
6194     {
6195         ERR("Failed to install SQL translator!\n");
6196         r = ERROR_FUNCTION_FAILED;
6197     }
6198
6199     uirow = MSI_CreateRecord( 5 );
6200     MSI_RecordSetStringW( uirow, 1, desc );
6201     MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6202     MSI_RecordSetStringW( uirow, 3, translator_file->Component->Directory );
6203     msi_ui_actiondata( package, szInstallODBC, uirow );
6204     msiobj_release( &uirow->hdr );
6205
6206     msi_free(translator);
6207     msi_free(translator_path);
6208
6209     return r;
6210 }
6211
6212 static UINT ITERATE_InstallODBCDataSource( MSIRECORD *rec, LPVOID param )
6213 {
6214     MSIPACKAGE *package = param;
6215     MSICOMPONENT *comp;
6216     LPWSTR attrs;
6217     LPCWSTR desc, driver, component;
6218     WORD request = ODBC_ADD_SYS_DSN;
6219     INT registration;
6220     DWORD len;
6221     UINT r = ERROR_SUCCESS;
6222     MSIRECORD *uirow;
6223
6224     static const WCHAR attrs_fmt[] = {
6225         'D','S','N','=','%','s',0 };
6226
6227     component = MSI_RecordGetString( rec, 2 );
6228     comp = msi_get_loaded_component( package, component );
6229     if (!comp)
6230         return ERROR_SUCCESS;
6231
6232     comp->Action = msi_get_component_action( package, comp );
6233     if (comp->Action != INSTALLSTATE_LOCAL)
6234     {
6235         TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6236         return ERROR_SUCCESS;
6237     }
6238
6239     desc = MSI_RecordGetString(rec, 3);
6240     driver = MSI_RecordGetString(rec, 4);
6241     registration = MSI_RecordGetInteger(rec, 5);
6242
6243     if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_ADD_SYS_DSN;
6244     else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_ADD_DSN;
6245
6246     len = lstrlenW(attrs_fmt) + lstrlenW(desc) + 2; /* \0\0 */
6247     attrs = msi_alloc(len * sizeof(WCHAR));
6248     if (!attrs)
6249         return ERROR_OUTOFMEMORY;
6250
6251     len = sprintfW(attrs, attrs_fmt, desc);
6252     attrs[len + 1] = 0;
6253
6254     if (!SQLConfigDataSourceW(NULL, request, driver, attrs))
6255     {
6256         ERR("Failed to install SQL data source!\n");
6257         r = ERROR_FUNCTION_FAILED;
6258     }
6259
6260     uirow = MSI_CreateRecord( 5 );
6261     MSI_RecordSetStringW( uirow, 1, desc );
6262     MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6263     MSI_RecordSetInteger( uirow, 3, request );
6264     msi_ui_actiondata( package, szInstallODBC, uirow );
6265     msiobj_release( &uirow->hdr );
6266
6267     msi_free(attrs);
6268
6269     return r;
6270 }
6271
6272 static UINT ACTION_InstallODBC( MSIPACKAGE *package )
6273 {
6274     static const WCHAR driver_query[] = {
6275         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6276         'O','D','B','C','D','r','i','v','e','r',0};
6277     static const WCHAR translator_query[] = {
6278         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6279         'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
6280     static const WCHAR source_query[] = {
6281         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6282         'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0};
6283     MSIQUERY *view;
6284     UINT rc;
6285
6286     rc = MSI_DatabaseOpenViewW(package->db, driver_query, &view);
6287     if (rc == ERROR_SUCCESS)
6288     {
6289         rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDriver, package);
6290         msiobj_release(&view->hdr);
6291         if (rc != ERROR_SUCCESS)
6292             return rc;
6293     }
6294     rc = MSI_DatabaseOpenViewW(package->db, translator_query, &view);
6295     if (rc == ERROR_SUCCESS)
6296     {
6297         rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCTranslator, package);
6298         msiobj_release(&view->hdr);
6299         if (rc != ERROR_SUCCESS)
6300             return rc;
6301     }
6302     rc = MSI_DatabaseOpenViewW(package->db, source_query, &view);
6303     if (rc == ERROR_SUCCESS)
6304     {
6305         rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDataSource, package);
6306         msiobj_release(&view->hdr);
6307         if (rc != ERROR_SUCCESS)
6308             return rc;
6309     }
6310     return ERROR_SUCCESS;
6311 }
6312
6313 static UINT ITERATE_RemoveODBCDriver( MSIRECORD *rec, LPVOID param )
6314 {
6315     MSIPACKAGE *package = param;
6316     MSICOMPONENT *comp;
6317     MSIRECORD *uirow;
6318     DWORD usage;
6319     LPCWSTR desc, component;
6320
6321     component = MSI_RecordGetString( rec, 2 );
6322     comp = msi_get_loaded_component( package, component );
6323     if (!comp)
6324         return ERROR_SUCCESS;
6325
6326     comp->Action = msi_get_component_action( package, comp );
6327     if (comp->Action != INSTALLSTATE_ABSENT)
6328     {
6329         TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6330         return ERROR_SUCCESS;
6331     }
6332
6333     desc = MSI_RecordGetString( rec, 3 );
6334     if (!SQLRemoveDriverW( desc, FALSE, &usage ))
6335     {
6336         WARN("Failed to remove ODBC driver\n");
6337     }
6338     else if (!usage)
6339     {
6340         FIXME("Usage count reached 0\n");
6341     }
6342
6343     uirow = MSI_CreateRecord( 2 );
6344     MSI_RecordSetStringW( uirow, 1, desc );
6345     MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6346     msi_ui_actiondata( package, szRemoveODBC, uirow );
6347     msiobj_release( &uirow->hdr );
6348
6349     return ERROR_SUCCESS;
6350 }
6351
6352 static UINT ITERATE_RemoveODBCTranslator( MSIRECORD *rec, LPVOID param )
6353 {
6354     MSIPACKAGE *package = param;
6355     MSICOMPONENT *comp;
6356     MSIRECORD *uirow;
6357     DWORD usage;
6358     LPCWSTR desc, component;
6359
6360     component = MSI_RecordGetString( rec, 2 );
6361     comp = msi_get_loaded_component( package, component );
6362     if (!comp)
6363         return ERROR_SUCCESS;
6364
6365     comp->Action = msi_get_component_action( package, comp );
6366     if (comp->Action != INSTALLSTATE_ABSENT)
6367     {
6368         TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6369         return ERROR_SUCCESS;
6370     }
6371
6372     desc = MSI_RecordGetString( rec, 3 );
6373     if (!SQLRemoveTranslatorW( desc, &usage ))
6374     {
6375         WARN("Failed to remove ODBC translator\n");
6376     }
6377     else if (!usage)
6378     {
6379         FIXME("Usage count reached 0\n");
6380     }
6381
6382     uirow = MSI_CreateRecord( 2 );
6383     MSI_RecordSetStringW( uirow, 1, desc );
6384     MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6385     msi_ui_actiondata( package, szRemoveODBC, uirow );
6386     msiobj_release( &uirow->hdr );
6387
6388     return ERROR_SUCCESS;
6389 }
6390
6391 static UINT ITERATE_RemoveODBCDataSource( MSIRECORD *rec, LPVOID param )
6392 {
6393     MSIPACKAGE *package = param;
6394     MSICOMPONENT *comp;
6395     MSIRECORD *uirow;
6396     LPWSTR attrs;
6397     LPCWSTR desc, driver, component;
6398     WORD request = ODBC_REMOVE_SYS_DSN;
6399     INT registration;
6400     DWORD len;
6401
6402     static const WCHAR attrs_fmt[] = {
6403         'D','S','N','=','%','s',0 };
6404
6405     component = MSI_RecordGetString( rec, 2 );
6406     comp = msi_get_loaded_component( package, component );
6407     if (!comp)
6408         return ERROR_SUCCESS;
6409
6410     comp->Action = msi_get_component_action( package, comp );
6411     if (comp->Action != INSTALLSTATE_ABSENT)
6412     {
6413         TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6414         return ERROR_SUCCESS;
6415     }
6416
6417     desc = MSI_RecordGetString( rec, 3 );
6418     driver = MSI_RecordGetString( rec, 4 );
6419     registration = MSI_RecordGetInteger( rec, 5 );
6420
6421     if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_REMOVE_SYS_DSN;
6422     else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_REMOVE_DSN;
6423
6424     len = strlenW( attrs_fmt ) + strlenW( desc ) + 2; /* \0\0 */
6425     attrs = msi_alloc( len * sizeof(WCHAR) );
6426     if (!attrs)
6427         return ERROR_OUTOFMEMORY;
6428
6429     FIXME("Use ODBCSourceAttribute table\n");
6430
6431     len = sprintfW( attrs, attrs_fmt, desc );
6432     attrs[len + 1] = 0;
6433
6434     if (!SQLConfigDataSourceW( NULL, request, driver, attrs ))
6435     {
6436         WARN("Failed to remove ODBC data source\n");
6437     }
6438     msi_free( attrs );
6439
6440     uirow = MSI_CreateRecord( 3 );
6441     MSI_RecordSetStringW( uirow, 1, desc );
6442     MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6443     MSI_RecordSetInteger( uirow, 3, request );
6444     msi_ui_actiondata( package, szRemoveODBC, uirow );
6445     msiobj_release( &uirow->hdr );
6446
6447     return ERROR_SUCCESS;
6448 }
6449
6450 static UINT ACTION_RemoveODBC( MSIPACKAGE *package )
6451 {
6452     static const WCHAR driver_query[] = {
6453         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6454         'O','D','B','C','D','r','i','v','e','r',0};
6455     static const WCHAR translator_query[] = {
6456         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6457         'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
6458     static const WCHAR source_query[] = {
6459         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6460         'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0};
6461     MSIQUERY *view;
6462     UINT rc;
6463
6464     rc = MSI_DatabaseOpenViewW( package->db, driver_query, &view );
6465     if (rc == ERROR_SUCCESS)
6466     {
6467         rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDriver, package );
6468         msiobj_release( &view->hdr );
6469         if (rc != ERROR_SUCCESS)
6470             return rc;
6471     }
6472     rc = MSI_DatabaseOpenViewW( package->db, translator_query, &view );
6473     if (rc == ERROR_SUCCESS)
6474     {
6475         rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCTranslator, package );
6476         msiobj_release( &view->hdr );
6477         if (rc != ERROR_SUCCESS)
6478             return rc;
6479     }
6480     rc = MSI_DatabaseOpenViewW( package->db, source_query, &view );
6481     if (rc == ERROR_SUCCESS)
6482     {
6483         rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDataSource, package );
6484         msiobj_release( &view->hdr );
6485         if (rc != ERROR_SUCCESS)
6486             return rc;
6487     }
6488     return ERROR_SUCCESS;
6489 }
6490
6491 #define ENV_ACT_SETALWAYS   0x1
6492 #define ENV_ACT_SETABSENT   0x2
6493 #define ENV_ACT_REMOVE      0x4
6494 #define ENV_ACT_REMOVEMATCH 0x8
6495
6496 #define ENV_MOD_MACHINE     0x20000000
6497 #define ENV_MOD_APPEND      0x40000000
6498 #define ENV_MOD_PREFIX      0x80000000
6499 #define ENV_MOD_MASK        0xC0000000
6500
6501 #define check_flag_combo(x, y) ((x) & ~(y)) == (y)
6502
6503 static UINT env_parse_flags( LPCWSTR *name, LPCWSTR *value, DWORD *flags )
6504 {
6505     LPCWSTR cptr = *name;
6506
6507     static const WCHAR prefix[] = {'[','~',']',0};
6508     static const int prefix_len = 3;
6509
6510     *flags = 0;
6511     while (*cptr)
6512     {
6513         if (*cptr == '=')
6514             *flags |= ENV_ACT_SETALWAYS;
6515         else if (*cptr == '+')
6516             *flags |= ENV_ACT_SETABSENT;
6517         else if (*cptr == '-')
6518             *flags |= ENV_ACT_REMOVE;
6519         else if (*cptr == '!')
6520             *flags |= ENV_ACT_REMOVEMATCH;
6521         else if (*cptr == '*')
6522             *flags |= ENV_MOD_MACHINE;
6523         else
6524             break;
6525
6526         cptr++;
6527         (*name)++;
6528     }
6529
6530     if (!*cptr)
6531     {
6532         ERR("Missing environment variable\n");
6533         return ERROR_FUNCTION_FAILED;
6534     }
6535
6536     if (*value)
6537     {
6538         LPCWSTR ptr = *value;
6539         if (!strncmpW(ptr, prefix, prefix_len))
6540         {
6541             if (ptr[prefix_len] == szSemiColon[0])
6542             {
6543                 *flags |= ENV_MOD_APPEND;
6544                 *value += lstrlenW(prefix);
6545             }
6546             else
6547             {
6548                 *value = NULL;
6549             }
6550         }
6551         else if (lstrlenW(*value) >= prefix_len)
6552         {
6553             ptr += lstrlenW(ptr) - prefix_len;
6554             if (!strcmpW( ptr, prefix ))
6555             {
6556                 if ((ptr-1) > *value && *(ptr-1) == szSemiColon[0])
6557                 {
6558                     *flags |= ENV_MOD_PREFIX;
6559                     /* the "[~]" will be removed by deformat_string */;
6560                 }
6561                 else
6562                 {
6563                     *value = NULL;
6564                 }
6565             }
6566         }
6567     }
6568
6569     if (check_flag_combo(*flags, ENV_ACT_SETALWAYS | ENV_ACT_SETABSENT) ||
6570         check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETABSENT) ||
6571         check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETALWAYS) ||
6572         check_flag_combo(*flags, ENV_ACT_SETABSENT | ENV_MOD_MASK))
6573     {
6574         ERR("Invalid flags: %08x\n", *flags);
6575         return ERROR_FUNCTION_FAILED;
6576     }
6577
6578     if (!*flags)
6579         *flags = ENV_ACT_SETALWAYS | ENV_ACT_REMOVE;
6580
6581     return ERROR_SUCCESS;
6582 }
6583
6584 static UINT open_env_key( DWORD flags, HKEY *key )
6585 {
6586     static const WCHAR user_env[] =
6587         {'E','n','v','i','r','o','n','m','e','n','t',0};
6588     static const WCHAR machine_env[] =
6589         {'S','y','s','t','e','m','\\',
6590          'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
6591          'C','o','n','t','r','o','l','\\',
6592          'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r','\\',
6593          'E','n','v','i','r','o','n','m','e','n','t',0};
6594     const WCHAR *env;
6595     HKEY root;
6596     LONG res;
6597
6598     if (flags & ENV_MOD_MACHINE)
6599     {
6600         env = machine_env;
6601         root = HKEY_LOCAL_MACHINE;
6602     }
6603     else
6604     {
6605         env = user_env;
6606         root = HKEY_CURRENT_USER;
6607     }
6608
6609     res = RegOpenKeyExW( root, env, 0, KEY_ALL_ACCESS, key );
6610     if (res != ERROR_SUCCESS)
6611     {
6612         WARN("Failed to open key %s (%d)\n", debugstr_w(env), res);
6613         return ERROR_FUNCTION_FAILED;
6614     }
6615
6616     return ERROR_SUCCESS;
6617 }
6618
6619 static UINT ITERATE_WriteEnvironmentString( MSIRECORD *rec, LPVOID param )
6620 {
6621     MSIPACKAGE *package = param;
6622     LPCWSTR name, value, component;
6623     LPWSTR data = NULL, newval = NULL, deformatted = NULL, ptr;
6624     DWORD flags, type, size;
6625     UINT res;
6626     HKEY env = NULL;
6627     MSICOMPONENT *comp;
6628     MSIRECORD *uirow;
6629     int action = 0;
6630
6631     component = MSI_RecordGetString(rec, 4);
6632     comp = msi_get_loaded_component(package, component);
6633     if (!comp)
6634         return ERROR_SUCCESS;
6635
6636     comp->Action = msi_get_component_action( package, comp );
6637     if (comp->Action != INSTALLSTATE_LOCAL)
6638     {
6639         TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6640         return ERROR_SUCCESS;
6641     }
6642     name = MSI_RecordGetString(rec, 2);
6643     value = MSI_RecordGetString(rec, 3);
6644
6645     TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
6646
6647     res = env_parse_flags(&name, &value, &flags);
6648     if (res != ERROR_SUCCESS || !value)
6649        goto done;
6650
6651     if (value && !deformat_string(package, value, &deformatted))
6652     {
6653         res = ERROR_OUTOFMEMORY;
6654         goto done;
6655     }
6656
6657     value = deformatted;
6658
6659     res = open_env_key( flags, &env );
6660     if (res != ERROR_SUCCESS)
6661         goto done;
6662
6663     if (flags & ENV_MOD_MACHINE)
6664         action |= 0x20000000;
6665
6666     size = 0;
6667     type = REG_SZ;
6668     res = RegQueryValueExW(env, name, NULL, &type, NULL, &size);
6669     if ((res != ERROR_SUCCESS && res != ERROR_FILE_NOT_FOUND) ||
6670         (res == ERROR_SUCCESS && type != REG_SZ && type != REG_EXPAND_SZ))
6671         goto done;
6672
6673     if ((res == ERROR_FILE_NOT_FOUND || !(flags & ENV_MOD_MASK)))
6674     {
6675         action = 0x2;
6676
6677         /* Nothing to do. */
6678         if (!value)
6679         {
6680             res = ERROR_SUCCESS;
6681             goto done;
6682         }
6683
6684         /* If we are appending but the string was empty, strip ; */
6685         if ((flags & ENV_MOD_APPEND) && (value[0] == szSemiColon[0])) value++;
6686
6687         size = (lstrlenW(value) + 1) * sizeof(WCHAR);
6688         newval = strdupW(value);
6689         if (!newval)
6690         {
6691             res = ERROR_OUTOFMEMORY;
6692             goto done;
6693         }
6694     }
6695     else
6696     {
6697         action = 0x1;
6698
6699         /* Contrary to MSDN, +-variable to [~];path works */
6700         if (flags & ENV_ACT_SETABSENT && !(flags & ENV_MOD_MASK))
6701         {
6702             res = ERROR_SUCCESS;
6703             goto done;
6704         }
6705
6706         data = msi_alloc(size);
6707         if (!data)
6708         {
6709             RegCloseKey(env);
6710             return ERROR_OUTOFMEMORY;
6711         }
6712
6713         res = RegQueryValueExW(env, name, NULL, &type, (LPVOID)data, &size);
6714         if (res != ERROR_SUCCESS)
6715             goto done;
6716
6717         if (flags & ENV_ACT_REMOVEMATCH && (!value || !strcmpW( data, value )))
6718         {
6719             action = 0x4;
6720             res = RegDeleteValueW(env, name);
6721             if (res != ERROR_SUCCESS)
6722                 WARN("Failed to remove value %s (%d)\n", debugstr_w(name), res);
6723             goto done;
6724         }
6725
6726         size = (lstrlenW(data) + 1) * sizeof(WCHAR);
6727         if (flags & ENV_MOD_MASK)
6728         {
6729             DWORD mod_size;
6730             int multiplier = 0;
6731             if (flags & ENV_MOD_APPEND) multiplier++;
6732             if (flags & ENV_MOD_PREFIX) multiplier++;
6733             mod_size = lstrlenW(value) * multiplier;
6734             size += mod_size * sizeof(WCHAR);
6735         }
6736
6737         newval = msi_alloc(size);
6738         ptr = newval;
6739         if (!newval)
6740         {
6741             res = ERROR_OUTOFMEMORY;
6742             goto done;
6743         }
6744
6745         if (flags & ENV_MOD_PREFIX)
6746         {
6747             lstrcpyW(newval, value);
6748             ptr = newval + lstrlenW(value);
6749             action |= 0x80000000;
6750         }
6751
6752         lstrcpyW(ptr, data);
6753
6754         if (flags & ENV_MOD_APPEND)
6755         {
6756             lstrcatW(newval, value);
6757             action |= 0x40000000;
6758         }
6759     }
6760     TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(newval));
6761     res = RegSetValueExW(env, name, 0, type, (LPVOID)newval, size);
6762     if (res)
6763     {
6764         WARN("Failed to set %s to %s (%d)\n",  debugstr_w(name), debugstr_w(newval), res);
6765     }
6766
6767 done:
6768     uirow = MSI_CreateRecord( 3 );
6769     MSI_RecordSetStringW( uirow, 1, name );
6770     MSI_RecordSetStringW( uirow, 2, newval );
6771     MSI_RecordSetInteger( uirow, 3, action );
6772     msi_ui_actiondata( package, szWriteEnvironmentStrings, uirow );
6773     msiobj_release( &uirow->hdr );
6774
6775     if (env) RegCloseKey(env);
6776     msi_free(deformatted);
6777     msi_free(data);
6778     msi_free(newval);
6779     return res;
6780 }
6781
6782 static UINT ACTION_WriteEnvironmentStrings( MSIPACKAGE *package )
6783 {
6784     static const WCHAR query[] = {
6785         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6786         '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
6787     MSIQUERY *view;
6788     UINT rc;
6789
6790     rc = MSI_DatabaseOpenViewW(package->db, query, &view);
6791     if (rc != ERROR_SUCCESS)
6792         return ERROR_SUCCESS;
6793
6794     rc = MSI_IterateRecords(view, NULL, ITERATE_WriteEnvironmentString, package);
6795     msiobj_release(&view->hdr);
6796     return rc;
6797 }
6798
6799 static UINT ITERATE_RemoveEnvironmentString( MSIRECORD *rec, LPVOID param )
6800 {
6801     MSIPACKAGE *package = param;
6802     LPCWSTR name, value, component;
6803     LPWSTR deformatted = NULL;
6804     DWORD flags;
6805     HKEY env;
6806     MSICOMPONENT *comp;
6807     MSIRECORD *uirow;
6808     int action = 0;
6809     LONG res;
6810     UINT r;
6811
6812     component = MSI_RecordGetString( rec, 4 );
6813     comp = msi_get_loaded_component( package, component );
6814     if (!comp)
6815         return ERROR_SUCCESS;
6816
6817     comp->Action = msi_get_component_action( package, comp );
6818     if (comp->Action != INSTALLSTATE_ABSENT)
6819     {
6820         TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6821         return ERROR_SUCCESS;
6822     }
6823     name = MSI_RecordGetString( rec, 2 );
6824     value = MSI_RecordGetString( rec, 3 );
6825
6826     TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
6827
6828     r = env_parse_flags( &name, &value, &flags );
6829     if (r != ERROR_SUCCESS)
6830        return r;
6831
6832     if (!(flags & ENV_ACT_REMOVE))
6833     {
6834         TRACE("Environment variable %s not marked for removal\n", debugstr_w(name));
6835         return ERROR_SUCCESS;
6836     }
6837
6838     if (value && !deformat_string( package, value, &deformatted ))
6839         return ERROR_OUTOFMEMORY;
6840
6841     value = deformatted;
6842
6843     r = open_env_key( flags, &env );
6844     if (r != ERROR_SUCCESS)
6845     {
6846         r = ERROR_SUCCESS;
6847         goto done;
6848     }
6849
6850     if (flags & ENV_MOD_MACHINE)
6851         action |= 0x20000000;
6852
6853     TRACE("Removing %s\n", debugstr_w(name));
6854
6855     res = RegDeleteValueW( env, name );
6856     if (res != ERROR_SUCCESS)
6857     {
6858         WARN("Failed to delete value %s (%d)\n", debugstr_w(name), res);
6859         r = ERROR_SUCCESS;
6860     }
6861
6862 done:
6863     uirow = MSI_CreateRecord( 3 );
6864     MSI_RecordSetStringW( uirow, 1, name );
6865     MSI_RecordSetStringW( uirow, 2, value );
6866     MSI_RecordSetInteger( uirow, 3, action );
6867     msi_ui_actiondata( package, szRemoveEnvironmentStrings, uirow );
6868     msiobj_release( &uirow->hdr );
6869
6870     if (env) RegCloseKey( env );
6871     msi_free( deformatted );
6872     return r;
6873 }
6874
6875 static UINT ACTION_RemoveEnvironmentStrings( MSIPACKAGE *package )
6876 {
6877     static const WCHAR query[] = {
6878         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6879         '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
6880     MSIQUERY *view;
6881     UINT rc;
6882
6883     rc = MSI_DatabaseOpenViewW( package->db, query, &view );
6884     if (rc != ERROR_SUCCESS)
6885         return ERROR_SUCCESS;
6886
6887     rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveEnvironmentString, package );
6888     msiobj_release( &view->hdr );
6889     return rc;
6890 }
6891
6892 UINT msi_validate_product_id( MSIPACKAGE *package )
6893 {
6894     LPWSTR key, template, id;
6895     UINT r = ERROR_SUCCESS;
6896
6897     id = msi_dup_property( package->db, szProductID );
6898     if (id)
6899     {
6900         msi_free( id );
6901         return ERROR_SUCCESS;
6902     }
6903     template = msi_dup_property( package->db, szPIDTemplate );
6904     key = msi_dup_property( package->db, szPIDKEY );
6905     if (key && template)
6906     {
6907         FIXME( "partial stub: template %s key %s\n", debugstr_w(template), debugstr_w(key) );
6908         r = msi_set_property( package->db, szProductID, key );
6909     }
6910     msi_free( template );
6911     msi_free( key );
6912     return r;
6913 }
6914
6915 static UINT ACTION_ValidateProductID( MSIPACKAGE *package )
6916 {
6917     return msi_validate_product_id( package );
6918 }
6919
6920 static UINT ACTION_ScheduleReboot( MSIPACKAGE *package )
6921 {
6922     TRACE("\n");
6923     package->need_reboot_at_end = 1;
6924     return ERROR_SUCCESS;
6925 }
6926
6927 static UINT ACTION_AllocateRegistrySpace( MSIPACKAGE *package )
6928 {
6929     static const WCHAR szAvailableFreeReg[] =
6930         {'A','V','A','I','L','A','B','L','E','F','R','E','E','R','E','G',0};
6931     MSIRECORD *uirow;
6932     int space = msi_get_property_int( package->db, szAvailableFreeReg, 0 );
6933
6934     TRACE("%p %d kilobytes\n", package, space);
6935
6936     uirow = MSI_CreateRecord( 1 );
6937     MSI_RecordSetInteger( uirow, 1, space );
6938     msi_ui_actiondata( package, szAllocateRegistrySpace, uirow );
6939     msiobj_release( &uirow->hdr );
6940
6941     return ERROR_SUCCESS;
6942 }
6943
6944 static UINT ACTION_DisableRollback( MSIPACKAGE *package )
6945 {
6946     TRACE("%p\n", package);
6947
6948     msi_set_property( package->db, szRollbackDisabled, szOne );
6949     return ERROR_SUCCESS;
6950 }
6951
6952 static UINT ACTION_InstallAdminPackage( MSIPACKAGE *package )
6953 {
6954     FIXME("%p\n", package);
6955     return ERROR_SUCCESS;
6956 }
6957
6958 static UINT ACTION_SetODBCFolders( MSIPACKAGE *package )
6959 {
6960     static const WCHAR driver_query[] = {
6961         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6962         'O','D','B','C','D','r','i','v','e','r',0};
6963     static const WCHAR translator_query[] = {
6964         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6965         'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
6966     MSIQUERY *view;
6967     UINT r, count;
6968
6969     r = MSI_DatabaseOpenViewW( package->db, driver_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 ODBCDriver table\n", count);
6978     }
6979     r = MSI_DatabaseOpenViewW( package->db, translator_query, &view );
6980     if (r == ERROR_SUCCESS)
6981     {
6982         count = 0;
6983         r = MSI_IterateRecords( view, &count, NULL, package );
6984         msiobj_release( &view->hdr );
6985         if (r != ERROR_SUCCESS)
6986             return r;
6987         if (count) FIXME("ignored %u rows in ODBCTranslator table\n", count);
6988     }
6989     return ERROR_SUCCESS;
6990 }
6991
6992 static UINT ITERATE_RemoveExistingProducts( MSIRECORD *rec, LPVOID param )
6993 {
6994     MSIPACKAGE *package = param;
6995     const WCHAR *property = MSI_RecordGetString( rec, 1 );
6996     WCHAR *value;
6997
6998     if ((value = msi_dup_property( package->db, property )))
6999     {
7000         FIXME("remove %s\n", debugstr_w(value));
7001         msi_free( value );
7002     }
7003     return ERROR_SUCCESS;
7004 }
7005
7006 static UINT ACTION_RemoveExistingProducts( MSIPACKAGE *package )
7007 {
7008     static const WCHAR query[] = {
7009         'S','E','L','E','C','T',' ','A','c','t','i','o','n','P','r','o','p','e','r','t','y',' ',
7010         'F','R','O','M',' ','U','p','g','r','a','d','e',0};
7011     MSIQUERY *view;
7012     UINT r;
7013
7014     r = MSI_DatabaseOpenViewW( package->db, query, &view );
7015     if (r == ERROR_SUCCESS)
7016     {
7017         r = MSI_IterateRecords( view, NULL, ITERATE_RemoveExistingProducts, package );
7018         msiobj_release( &view->hdr );
7019         if (r != ERROR_SUCCESS)
7020             return r;
7021     }
7022     return ERROR_SUCCESS;
7023 }
7024
7025 static UINT ITERATE_MigrateFeatureStates( MSIRECORD *rec, LPVOID param )
7026 {
7027     MSIPACKAGE *package = param;
7028     int attributes = MSI_RecordGetInteger( rec, 5 );
7029
7030     if (attributes & msidbUpgradeAttributesMigrateFeatures)
7031     {
7032         const WCHAR *upgrade_code = MSI_RecordGetString( rec, 1 );
7033         const WCHAR *version_min = MSI_RecordGetString( rec, 2 );
7034         const WCHAR *version_max = MSI_RecordGetString( rec, 3 );
7035         const WCHAR *language = MSI_RecordGetString( rec, 4 );
7036         HKEY hkey;
7037         UINT r;
7038
7039         if (package->Context == MSIINSTALLCONTEXT_MACHINE)
7040         {
7041             r = MSIREG_OpenClassesUpgradeCodesKey( upgrade_code, &hkey, FALSE );
7042             if (r != ERROR_SUCCESS)
7043                 return ERROR_SUCCESS;
7044         }
7045         else
7046         {
7047             r = MSIREG_OpenUserUpgradeCodesKey( upgrade_code, &hkey, FALSE );
7048             if (r != ERROR_SUCCESS)
7049                 return ERROR_SUCCESS;
7050         }
7051         RegCloseKey( hkey );
7052
7053         FIXME("migrate feature states from %s version min %s version max %s language %s\n",
7054               debugstr_w(upgrade_code), debugstr_w(version_min),
7055               debugstr_w(version_max), debugstr_w(language));
7056     }
7057     return ERROR_SUCCESS;
7058 }
7059
7060 static UINT ACTION_MigrateFeatureStates( MSIPACKAGE *package )
7061 {
7062     static const WCHAR query[] = {
7063         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7064         'U','p','g','r','a','d','e',0};
7065     MSIQUERY *view;
7066     UINT r;
7067
7068     if (msi_get_property_int( package->db, szInstalled, 0 ))
7069     {
7070         TRACE("product is installed, skipping action\n");
7071         return ERROR_SUCCESS;
7072     }
7073     if (msi_get_property_int( package->db, szPreselected, 0 ))
7074     {
7075         TRACE("Preselected property is set, not migrating feature states\n");
7076         return ERROR_SUCCESS;
7077     }
7078     r = MSI_DatabaseOpenViewW( package->db, query, &view );
7079     if (r == ERROR_SUCCESS)
7080     {
7081         r = MSI_IterateRecords( view, NULL, ITERATE_MigrateFeatureStates, package );
7082         msiobj_release( &view->hdr );
7083         if (r != ERROR_SUCCESS)
7084             return r;
7085     }
7086     return ERROR_SUCCESS;
7087 }
7088
7089 static void bind_image( const char *filename, const char *path )
7090 {
7091     if (!BindImageEx( 0, filename, path, NULL, NULL ))
7092     {
7093         WARN("failed to bind image %u\n", GetLastError());
7094     }
7095 }
7096
7097 static UINT ITERATE_BindImage( MSIRECORD *rec, LPVOID param )
7098 {
7099     UINT i;
7100     MSIFILE *file;
7101     MSIPACKAGE *package = param;
7102     const WCHAR *key = MSI_RecordGetString( rec, 1 );
7103     const WCHAR *paths = MSI_RecordGetString( rec, 2 );
7104     char *filenameA, *pathA;
7105     WCHAR *pathW, **path_list;
7106
7107     if (!(file = msi_get_loaded_file( package, key )))
7108     {
7109         WARN("file %s not found\n", debugstr_w(key));
7110         return ERROR_SUCCESS;
7111     }
7112     if (!(filenameA = strdupWtoA( file->TargetPath ))) return ERROR_SUCCESS;
7113     path_list = msi_split_string( paths, ';' );
7114     if (!path_list) bind_image( filenameA, NULL );
7115     else
7116     {
7117         for (i = 0; path_list[i] && path_list[i][0]; i++)
7118         {
7119             deformat_string( package, path_list[i], &pathW );
7120             if ((pathA = strdupWtoA( pathW )))
7121             {
7122                 bind_image( filenameA, pathA );
7123                 msi_free( pathA );
7124             }
7125             msi_free( pathW );
7126         }
7127     }
7128     msi_free( path_list );
7129     msi_free( filenameA );
7130     return ERROR_SUCCESS;
7131 }
7132
7133 static UINT ACTION_BindImage( MSIPACKAGE *package )
7134 {
7135     static const WCHAR query[] = {
7136         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7137         'B','i','n','d','I','m','a','g','e',0};
7138     MSIQUERY *view;
7139     UINT r;
7140
7141     r = MSI_DatabaseOpenViewW( package->db, query, &view );
7142     if (r == ERROR_SUCCESS)
7143     {
7144         r = MSI_IterateRecords( view, NULL, ITERATE_BindImage, package );
7145         msiobj_release( &view->hdr );
7146         if (r != ERROR_SUCCESS)
7147             return r;
7148     }
7149     return ERROR_SUCCESS;
7150 }
7151
7152 static UINT msi_unimplemented_action_stub( MSIPACKAGE *package, LPCSTR action, LPCWSTR table )
7153 {
7154     static const WCHAR query[] = {
7155         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','%','s','`',0};
7156     MSIQUERY *view;
7157     DWORD count = 0;
7158     UINT r;
7159     
7160     r = MSI_OpenQuery( package->db, &view, query, table );
7161     if (r == ERROR_SUCCESS)
7162     {
7163         r = MSI_IterateRecords(view, &count, NULL, package);
7164         msiobj_release(&view->hdr);
7165         if (r != ERROR_SUCCESS)
7166             return r;
7167     }
7168     if (count) FIXME("%s: ignored %u rows from %s\n", action, count, debugstr_w(table));
7169     return ERROR_SUCCESS;
7170 }
7171
7172 static UINT ACTION_IsolateComponents( MSIPACKAGE *package )
7173 {
7174     static const WCHAR table[] = {
7175         'I','s','o','l','a','t','e','d','C','o','m','p','o','n','e','n','t',0 };
7176     return msi_unimplemented_action_stub( package, "IsolateComponents", table );
7177 }
7178
7179 static UINT ACTION_RMCCPSearch( MSIPACKAGE *package )
7180 {
7181     static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
7182     return msi_unimplemented_action_stub( package, "RMCCPSearch", table );
7183 }
7184
7185 static UINT ACTION_RegisterComPlus( MSIPACKAGE *package )
7186 {
7187     static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
7188     return msi_unimplemented_action_stub( package, "RegisterComPlus", table );
7189 }
7190
7191 static UINT ACTION_UnregisterComPlus( MSIPACKAGE *package )
7192 {
7193     static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
7194     return msi_unimplemented_action_stub( package, "UnregisterComPlus", table );
7195 }
7196
7197 static UINT ACTION_InstallSFPCatalogFile( MSIPACKAGE *package )
7198 {
7199     static const WCHAR table[] = { 'S','F','P','C','a','t','a','l','o','g',0 };
7200     return msi_unimplemented_action_stub( package, "InstallSFPCatalogFile", table );
7201 }
7202
7203 static const struct
7204 {
7205     const WCHAR *action;
7206     UINT (*handler)(MSIPACKAGE *);
7207     const WCHAR *action_rollback;
7208 }
7209 StandardActions[] =
7210 {
7211     { szAllocateRegistrySpace, ACTION_AllocateRegistrySpace, NULL },
7212     { szAppSearch, ACTION_AppSearch, NULL },
7213     { szBindImage, ACTION_BindImage, NULL },
7214     { szCCPSearch, ACTION_CCPSearch, NULL },
7215     { szCostFinalize, ACTION_CostFinalize, NULL },
7216     { szCostInitialize, ACTION_CostInitialize, NULL },
7217     { szCreateFolders, ACTION_CreateFolders, szRemoveFolders },
7218     { szCreateShortcuts, ACTION_CreateShortcuts, szRemoveShortcuts },
7219     { szDeleteServices, ACTION_DeleteServices, szInstallServices },
7220     { szDisableRollback, ACTION_DisableRollback, NULL },
7221     { szDuplicateFiles, ACTION_DuplicateFiles, szRemoveDuplicateFiles },
7222     { szExecuteAction, ACTION_ExecuteAction, NULL },
7223     { szFileCost, ACTION_FileCost, NULL },
7224     { szFindRelatedProducts, ACTION_FindRelatedProducts, NULL },
7225     { szForceReboot, ACTION_ForceReboot, NULL },
7226     { szInstallAdminPackage, ACTION_InstallAdminPackage, NULL },
7227     { szInstallExecute, ACTION_InstallExecute, NULL },
7228     { szInstallExecuteAgain, ACTION_InstallExecute, NULL },
7229     { szInstallFiles, ACTION_InstallFiles, szRemoveFiles },
7230     { szInstallFinalize, ACTION_InstallFinalize, NULL },
7231     { szInstallInitialize, ACTION_InstallInitialize, NULL },
7232     { szInstallODBC, ACTION_InstallODBC, szRemoveODBC },
7233     { szInstallServices, ACTION_InstallServices, szDeleteServices },
7234     { szInstallSFPCatalogFile, ACTION_InstallSFPCatalogFile, NULL },
7235     { szInstallValidate, ACTION_InstallValidate, NULL },
7236     { szIsolateComponents, ACTION_IsolateComponents, NULL },
7237     { szLaunchConditions, ACTION_LaunchConditions, NULL },
7238     { szMigrateFeatureStates, ACTION_MigrateFeatureStates, NULL },
7239     { szMoveFiles, ACTION_MoveFiles, NULL },
7240     { szMsiPublishAssemblies, ACTION_MsiPublishAssemblies, szMsiUnpublishAssemblies },
7241     { szMsiUnpublishAssemblies, ACTION_MsiUnpublishAssemblies, szMsiPublishAssemblies },
7242     { szPatchFiles, ACTION_PatchFiles, NULL },
7243     { szProcessComponents, ACTION_ProcessComponents, szProcessComponents },
7244     { szPublishComponents, ACTION_PublishComponents, szUnpublishComponents },
7245     { szPublishFeatures, ACTION_PublishFeatures, szUnpublishFeatures },
7246     { szPublishProduct, ACTION_PublishProduct, NULL },
7247     { szRegisterClassInfo, ACTION_RegisterClassInfo, szUnregisterClassInfo },
7248     { szRegisterComPlus, ACTION_RegisterComPlus, szUnregisterComPlus },
7249     { szRegisterExtensionInfo, ACTION_RegisterExtensionInfo, szUnregisterExtensionInfo },
7250     { szRegisterFonts, ACTION_RegisterFonts, szUnregisterFonts },
7251     { szRegisterMIMEInfo, ACTION_RegisterMIMEInfo, szUnregisterMIMEInfo },
7252     { szRegisterProduct, ACTION_RegisterProduct, NULL },
7253     { szRegisterProgIdInfo, ACTION_RegisterProgIdInfo, szUnregisterProgIdInfo },
7254     { szRegisterTypeLibraries, ACTION_RegisterTypeLibraries, szUnregisterTypeLibraries },
7255     { szRegisterUser, ACTION_RegisterUser, NULL },
7256     { szRemoveDuplicateFiles, ACTION_RemoveDuplicateFiles, szDuplicateFiles },
7257     { szRemoveEnvironmentStrings, ACTION_RemoveEnvironmentStrings, szWriteEnvironmentStrings },
7258     { szRemoveExistingProducts, ACTION_RemoveExistingProducts, NULL },
7259     { szRemoveFiles, ACTION_RemoveFiles, szInstallFiles },
7260     { szRemoveFolders, ACTION_RemoveFolders, szCreateFolders },
7261     { szRemoveIniValues, ACTION_RemoveIniValues, szWriteIniValues },
7262     { szRemoveODBC, ACTION_RemoveODBC, szInstallODBC },
7263     { szRemoveRegistryValues, ACTION_RemoveRegistryValues, szWriteRegistryValues },
7264     { szRemoveShortcuts, ACTION_RemoveShortcuts, szCreateShortcuts },
7265     { szResolveSource, ACTION_ResolveSource, NULL },
7266     { szRMCCPSearch, ACTION_RMCCPSearch, NULL },
7267     { szScheduleReboot, ACTION_ScheduleReboot, NULL },
7268     { szSelfRegModules, ACTION_SelfRegModules, szSelfUnregModules },
7269     { szSelfUnregModules, ACTION_SelfUnregModules, szSelfRegModules },
7270     { szSetODBCFolders, ACTION_SetODBCFolders, NULL },
7271     { szStartServices, ACTION_StartServices, szStopServices },
7272     { szStopServices, ACTION_StopServices, szStartServices },
7273     { szUnpublishComponents, ACTION_UnpublishComponents, szPublishComponents },
7274     { szUnpublishFeatures, ACTION_UnpublishFeatures, szPublishFeatures },
7275     { szUnregisterClassInfo, ACTION_UnregisterClassInfo, szRegisterClassInfo },
7276     { szUnregisterComPlus, ACTION_UnregisterComPlus, szRegisterComPlus },
7277     { szUnregisterExtensionInfo, ACTION_UnregisterExtensionInfo, szRegisterExtensionInfo },
7278     { szUnregisterFonts, ACTION_UnregisterFonts, szRegisterFonts },
7279     { szUnregisterMIMEInfo, ACTION_UnregisterMIMEInfo, szRegisterMIMEInfo },
7280     { szUnregisterProgIdInfo, ACTION_UnregisterProgIdInfo, szRegisterProgIdInfo },
7281     { szUnregisterTypeLibraries, ACTION_UnregisterTypeLibraries, szRegisterTypeLibraries },
7282     { szValidateProductID, ACTION_ValidateProductID, NULL },
7283     { szWriteEnvironmentStrings, ACTION_WriteEnvironmentStrings, szRemoveEnvironmentStrings },
7284     { szWriteIniValues, ACTION_WriteIniValues, szRemoveIniValues },
7285     { szWriteRegistryValues, ACTION_WriteRegistryValues, szRemoveRegistryValues },
7286     { NULL, NULL, NULL }
7287 };
7288
7289 static BOOL ACTION_HandleStandardAction( MSIPACKAGE *package, LPCWSTR action, UINT *rc )
7290 {
7291     BOOL ret = FALSE;
7292     UINT i;
7293
7294     i = 0;
7295     while (StandardActions[i].action != NULL)
7296     {
7297         if (!strcmpW( StandardActions[i].action, action ))
7298         {
7299             ui_actionstart( package, action );
7300             if (StandardActions[i].handler)
7301             {
7302                 ui_actioninfo( package, action, TRUE, 0 );
7303                 *rc = StandardActions[i].handler( package );
7304                 ui_actioninfo( package, action, FALSE, *rc );
7305
7306                 if (StandardActions[i].action_rollback && !package->need_rollback)
7307                 {
7308                     TRACE("scheduling rollback action\n");
7309                     msi_schedule_action( package, SCRIPT_ROLLBACK, StandardActions[i].action_rollback );
7310                 }
7311             }
7312             else
7313             {
7314                 FIXME("unhandled standard action %s\n", debugstr_w(action));
7315                 *rc = ERROR_SUCCESS;
7316             }
7317             ret = TRUE;
7318             break;
7319         }
7320         i++;
7321     }
7322     return ret;
7323 }
7324
7325 UINT ACTION_PerformAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
7326 {
7327     UINT rc = ERROR_SUCCESS;
7328     BOOL handled;
7329
7330     TRACE("Performing action (%s)\n", debugstr_w(action));
7331
7332     handled = ACTION_HandleStandardAction(package, action, &rc);
7333
7334     if (!handled)
7335         handled = ACTION_HandleCustomAction(package, action, &rc, script, TRUE);
7336
7337     if (!handled)
7338     {
7339         WARN("unhandled msi action %s\n", debugstr_w(action));
7340         rc = ERROR_FUNCTION_NOT_CALLED;
7341     }
7342
7343     return rc;
7344 }
7345
7346 UINT ACTION_PerformUIAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
7347 {
7348     UINT rc = ERROR_SUCCESS;
7349     BOOL handled = FALSE;
7350
7351     TRACE("Performing action (%s)\n", debugstr_w(action));
7352
7353     package->action_progress_increment = 0;
7354     handled = ACTION_HandleStandardAction(package, action, &rc);
7355
7356     if (!handled)
7357         handled = ACTION_HandleCustomAction(package, action, &rc, script, FALSE);
7358
7359     if( !handled && ACTION_DialogBox(package, action) == ERROR_SUCCESS )
7360         handled = TRUE;
7361
7362     if (!handled)
7363     {
7364         WARN("unhandled msi action %s\n", debugstr_w(action));
7365         rc = ERROR_FUNCTION_NOT_CALLED;
7366     }
7367
7368     return rc;
7369 }
7370
7371 static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq)
7372 {
7373     UINT rc = ERROR_SUCCESS;
7374     MSIRECORD *row;
7375
7376     static const WCHAR query[] =
7377         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7378          '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
7379          'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
7380          '`','S','e','q','u','e','n','c','e','`',' ', '=',' ','%','i',0};
7381     static const WCHAR ui_query[] =
7382         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7383      '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e',
7384      '`', ' ', 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',
7385          ' ', '=',' ','%','i',0};
7386
7387     if (needs_ui_sequence(package))
7388         row = MSI_QueryGetRecord(package->db, ui_query, seq);
7389     else
7390         row = MSI_QueryGetRecord(package->db, query, seq);
7391
7392     if (row)
7393     {
7394         LPCWSTR action, cond;
7395
7396         TRACE("Running the actions\n");
7397
7398         /* check conditions */
7399         cond = MSI_RecordGetString(row, 2);
7400
7401         /* this is a hack to skip errors in the condition code */
7402         if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
7403         {
7404             msiobj_release(&row->hdr);
7405             return ERROR_SUCCESS;
7406         }
7407
7408         action = MSI_RecordGetString(row, 1);
7409         if (!action)
7410         {
7411             ERR("failed to fetch action\n");
7412             msiobj_release(&row->hdr);
7413             return ERROR_FUNCTION_FAILED;
7414         }
7415
7416         if (needs_ui_sequence(package))
7417             rc = ACTION_PerformUIAction(package, action, SCRIPT_NONE);
7418         else
7419             rc = ACTION_PerformAction(package, action, SCRIPT_NONE);
7420
7421         msiobj_release(&row->hdr);
7422     }
7423
7424     return rc;
7425 }
7426
7427 /****************************************************
7428  * TOP level entry points
7429  *****************************************************/
7430
7431 UINT MSI_InstallPackage( MSIPACKAGE *package, LPCWSTR szPackagePath,
7432                          LPCWSTR szCommandLine )
7433 {
7434     static const WCHAR szDisableRollback[] = {'D','I','S','A','B','L','E','R','O','L','L','B','A','C','K',0};
7435     static const WCHAR szAction[] = {'A','C','T','I','O','N',0};
7436     static const WCHAR szInstall[] = {'I','N','S','T','A','L','L',0};
7437     WCHAR *reinstall = NULL;
7438     BOOL ui_exists;
7439     UINT rc;
7440
7441     msi_set_property( package->db, szAction, szInstall );
7442
7443     package->script->InWhatSequence = SEQUENCE_INSTALL;
7444
7445     if (szPackagePath)
7446     {
7447         LPWSTR p, dir;
7448         LPCWSTR file;
7449
7450         dir = strdupW(szPackagePath);
7451         p = strrchrW(dir, '\\');
7452         if (p)
7453         {
7454             *(++p) = 0;
7455             file = szPackagePath + (p - dir);
7456         }
7457         else
7458         {
7459             msi_free(dir);
7460             dir = msi_alloc(MAX_PATH * sizeof(WCHAR));
7461             GetCurrentDirectoryW(MAX_PATH, dir);
7462             lstrcatW(dir, szBackSlash);
7463             file = szPackagePath;
7464         }
7465
7466         msi_free( package->PackagePath );
7467         package->PackagePath = msi_alloc((lstrlenW(dir) + lstrlenW(file) + 1) * sizeof(WCHAR));
7468         if (!package->PackagePath)
7469         {
7470             msi_free(dir);
7471             return ERROR_OUTOFMEMORY;
7472         }
7473
7474         lstrcpyW(package->PackagePath, dir);
7475         lstrcatW(package->PackagePath, file);
7476         msi_free(dir);
7477
7478         msi_set_sourcedir_props(package, FALSE);
7479     }
7480
7481     rc = msi_parse_command_line( package, szCommandLine, FALSE );
7482     if (rc != ERROR_SUCCESS)
7483         return rc;
7484
7485     msi_apply_transforms( package );
7486     msi_apply_patches( package );
7487
7488     if (!szCommandLine && msi_get_property_int( package->db, szInstalled, 0 ))
7489     {
7490         TRACE("setting reinstall property\n");
7491         msi_set_property( package->db, szReinstall, szAll );
7492     }
7493
7494     /* properties may have been added by a transform */
7495     msi_clone_properties( package );
7496
7497     msi_parse_command_line( package, szCommandLine, FALSE );
7498     msi_adjust_privilege_properties( package );
7499     msi_set_context( package );
7500
7501     if (msi_get_property_int( package->db, szDisableRollback, 0 ))
7502     {
7503         TRACE("disabling rollback\n");
7504         msi_set_property( package->db, szRollbackDisabled, szOne );
7505     }
7506
7507     if (needs_ui_sequence( package))
7508     {
7509         package->script->InWhatSequence |= SEQUENCE_UI;
7510         rc = ACTION_ProcessUISequence(package);
7511         ui_exists = ui_sequence_exists(package);
7512         if (rc == ERROR_SUCCESS || !ui_exists)
7513         {
7514             package->script->InWhatSequence |= SEQUENCE_EXEC;
7515             rc = ACTION_ProcessExecSequence(package, ui_exists);
7516         }
7517     }
7518     else
7519         rc = ACTION_ProcessExecSequence(package, FALSE);
7520
7521     package->script->CurrentlyScripting = FALSE;
7522
7523     /* process the ending type action */
7524     if (rc == ERROR_SUCCESS)
7525         ACTION_PerformActionSequence(package, -1);
7526     else if (rc == ERROR_INSTALL_USEREXIT)
7527         ACTION_PerformActionSequence(package, -2);
7528     else if (rc == ERROR_INSTALL_SUSPEND)
7529         ACTION_PerformActionSequence(package, -4);
7530     else  /* failed */
7531     {
7532         ACTION_PerformActionSequence(package, -3);
7533         if (!msi_get_property_int( package->db, szRollbackDisabled, 0 ))
7534         {
7535             package->need_rollback = TRUE;
7536         }
7537     }
7538
7539     /* finish up running custom actions */
7540     ACTION_FinishCustomActions(package);
7541
7542     if (package->need_rollback && !(reinstall = msi_dup_property( package->db, szReinstall )))
7543     {
7544         WARN("installation failed, running rollback script\n");
7545         execute_script( package, SCRIPT_ROLLBACK );
7546     }
7547     msi_free( reinstall );
7548
7549     if (rc == ERROR_SUCCESS && package->need_reboot_at_end)
7550         return ERROR_SUCCESS_REBOOT_REQUIRED;
7551
7552     return rc;
7553 }