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