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