netstat: Initial implementation.
[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 && !package->full_reinstall)
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     UINT ret = ERROR_SUCCESS;
5756
5757     comp = MSI_RecordGetString( rec, 12 );
5758     component = msi_get_loaded_component( package, comp );
5759     if (!component)
5760     {
5761         WARN("service component not found\n");
5762         goto done;
5763     }
5764     component->Action = msi_get_component_action( package, component );
5765     if (component->Action != INSTALLSTATE_LOCAL)
5766     {
5767         TRACE("component not scheduled for installation %s\n", debugstr_w(comp));
5768         goto done;
5769     }
5770     hscm = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASEW, GENERIC_WRITE);
5771     if (!hscm)
5772     {
5773         ERR("Failed to open the SC Manager!\n");
5774         goto done;
5775     }
5776
5777     start_type = MSI_RecordGetInteger(rec, 5);
5778     if (start_type == SERVICE_BOOT_START || start_type == SERVICE_SYSTEM_START)
5779         goto done;
5780
5781     deformat_string(package, MSI_RecordGetString(rec, 2), &name);
5782     deformat_string(package, MSI_RecordGetString(rec, 3), &disp);
5783     serv_type = MSI_RecordGetInteger(rec, 4);
5784     err_control = MSI_RecordGetInteger(rec, 6);
5785     deformat_string(package, MSI_RecordGetString(rec, 7), &load_order);
5786     deformat_string(package, MSI_RecordGetString(rec, 8), &depends);
5787     deformat_string(package, MSI_RecordGetString(rec, 9), &serv_name);
5788     deformat_string(package, MSI_RecordGetString(rec, 10), &pass);
5789     deformat_string(package, MSI_RecordGetString(rec, 11), &args);
5790     deformat_string(package, MSI_RecordGetString(rec, 13), &sd.lpDescription);
5791
5792     /* fetch the service path */
5793     row = MSI_QueryGetRecord(package->db, query, comp);
5794     if (!row)
5795     {
5796         ERR("Query failed\n");
5797         goto done;
5798     }
5799     key = MSI_RecordGetString(row, 6);
5800     file = msi_get_loaded_file(package, key);
5801     msiobj_release(&row->hdr);
5802     if (!file)
5803     {
5804         ERR("Failed to load the service file\n");
5805         goto done;
5806     }
5807
5808     if (!args || !args[0]) image_path = file->TargetPath;
5809     else
5810     {
5811         int len = strlenW(file->TargetPath) + strlenW(args) + 2;
5812         if (!(image_path = msi_alloc(len * sizeof(WCHAR))))
5813         {
5814             ret = ERROR_OUTOFMEMORY;
5815             goto done;
5816         }
5817
5818         strcpyW(image_path, file->TargetPath);
5819         strcatW(image_path, szSpace);
5820         strcatW(image_path, args);
5821     }
5822     service = CreateServiceW(hscm, name, disp, GENERIC_ALL, serv_type,
5823                              start_type, err_control, image_path, load_order,
5824                              NULL, depends, serv_name, pass);
5825
5826     if (!service)
5827     {
5828         if (GetLastError() != ERROR_SERVICE_EXISTS)
5829             ERR("Failed to create service %s: %d\n", debugstr_w(name), GetLastError());
5830     }
5831     else if (sd.lpDescription)
5832     {
5833         if (!ChangeServiceConfig2W(service, SERVICE_CONFIG_DESCRIPTION, &sd))
5834             WARN("failed to set service description %u\n", GetLastError());
5835     }
5836
5837     if (image_path != file->TargetPath) msi_free(image_path);
5838 done:
5839     CloseServiceHandle(service);
5840     CloseServiceHandle(hscm);
5841     msi_free(name);
5842     msi_free(disp);
5843     msi_free(sd.lpDescription);
5844     msi_free(load_order);
5845     msi_free(serv_name);
5846     msi_free(pass);
5847     msi_free(depends);
5848     msi_free(args);
5849
5850     return ret;
5851 }
5852
5853 static UINT ACTION_InstallServices( MSIPACKAGE *package )
5854 {
5855     static const WCHAR query[] = {
5856         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5857         'S','e','r','v','i','c','e','I','n','s','t','a','l','l',0};
5858     MSIQUERY *view;
5859     UINT rc;
5860     
5861     rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5862     if (rc != ERROR_SUCCESS)
5863         return ERROR_SUCCESS;
5864
5865     rc = MSI_IterateRecords(view, NULL, ITERATE_InstallService, package);
5866     msiobj_release(&view->hdr);
5867     return rc;
5868 }
5869
5870 /* converts arg1[~]arg2[~]arg3 to a list of ptrs to the strings */
5871 static LPCWSTR *msi_service_args_to_vector(LPWSTR args, DWORD *numargs)
5872 {
5873     LPCWSTR *vector, *temp_vector;
5874     LPWSTR p, q;
5875     DWORD sep_len;
5876
5877     static const WCHAR separator[] = {'[','~',']',0};
5878
5879     *numargs = 0;
5880     sep_len = sizeof(separator) / sizeof(WCHAR) - 1;
5881
5882     if (!args)
5883         return NULL;
5884
5885     vector = msi_alloc(sizeof(LPWSTR));
5886     if (!vector)
5887         return NULL;
5888
5889     p = args;
5890     do
5891     {
5892         (*numargs)++;
5893         vector[*numargs - 1] = p;
5894
5895         if ((q = strstrW(p, separator)))
5896         {
5897             *q = '\0';
5898
5899             temp_vector = msi_realloc(vector, (*numargs + 1) * sizeof(LPWSTR));
5900             if (!temp_vector)
5901             {
5902                 msi_free(vector);
5903                 return NULL;
5904             }
5905             vector = temp_vector;
5906
5907             p = q + sep_len;
5908         }
5909     } while (q);
5910
5911     return vector;
5912 }
5913
5914 static UINT ITERATE_StartService(MSIRECORD *rec, LPVOID param)
5915 {
5916     MSIPACKAGE *package = param;
5917     MSICOMPONENT *comp;
5918     MSIRECORD *uirow;
5919     SC_HANDLE scm = NULL, service = NULL;
5920     LPCWSTR component, *vector = NULL;
5921     LPWSTR name, args, display_name = NULL;
5922     DWORD event, numargs, len, wait, dummy;
5923     UINT r = ERROR_FUNCTION_FAILED;
5924     SERVICE_STATUS_PROCESS status;
5925     ULONGLONG start_time;
5926
5927     component = MSI_RecordGetString(rec, 6);
5928     comp = msi_get_loaded_component(package, component);
5929     if (!comp)
5930         return ERROR_SUCCESS;
5931
5932     comp->Action = msi_get_component_action( package, comp );
5933     if (comp->Action != INSTALLSTATE_LOCAL)
5934     {
5935         TRACE("component not scheduled for installation %s\n", debugstr_w(component));
5936         return ERROR_SUCCESS;
5937     }
5938
5939     deformat_string(package, MSI_RecordGetString(rec, 2), &name);
5940     deformat_string(package, MSI_RecordGetString(rec, 4), &args);
5941     event = MSI_RecordGetInteger(rec, 3);
5942     wait = MSI_RecordGetInteger(rec, 5);
5943
5944     if (!(event & msidbServiceControlEventStart))
5945     {
5946         r = ERROR_SUCCESS;
5947         goto done;
5948     }
5949
5950     scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
5951     if (!scm)
5952     {
5953         ERR("Failed to open the service control manager\n");
5954         goto done;
5955     }
5956
5957     len = 0;
5958     if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
5959         GetLastError() == ERROR_INSUFFICIENT_BUFFER)
5960     {
5961         if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
5962             GetServiceDisplayNameW( scm, name, display_name, &len );
5963     }
5964
5965     service = OpenServiceW(scm, name, SERVICE_START|SERVICE_QUERY_STATUS);
5966     if (!service)
5967     {
5968         ERR("Failed to open service %s (%u)\n", debugstr_w(name), GetLastError());
5969         goto done;
5970     }
5971
5972     vector = msi_service_args_to_vector(args, &numargs);
5973
5974     if (!StartServiceW(service, numargs, vector) &&
5975         GetLastError() != ERROR_SERVICE_ALREADY_RUNNING)
5976     {
5977         ERR("Failed to start service %s (%u)\n", debugstr_w(name), GetLastError());
5978         goto done;
5979     }
5980
5981     r = ERROR_SUCCESS;
5982     if (wait)
5983     {
5984         /* wait for at most 30 seconds for the service to be up and running */
5985         if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO,
5986             (BYTE *)&status, sizeof(SERVICE_STATUS_PROCESS), &dummy))
5987         {
5988             TRACE("failed to query service status (%u)\n", GetLastError());
5989             goto done;
5990         }
5991         start_time = GetTickCount64();
5992         while (status.dwCurrentState == SERVICE_START_PENDING)
5993         {
5994             if (GetTickCount64() - start_time > 30000) break;
5995             Sleep(1000);
5996             if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO,
5997                 (BYTE *)&status, sizeof(SERVICE_STATUS_PROCESS), &dummy))
5998             {
5999                 TRACE("failed to query service status (%u)\n", GetLastError());
6000                 goto done;
6001             }
6002         }
6003         if (status.dwCurrentState != SERVICE_RUNNING)
6004         {
6005             WARN("service failed to start %u\n", status.dwCurrentState);
6006             r = ERROR_FUNCTION_FAILED;
6007         }
6008     }
6009
6010 done:
6011     uirow = MSI_CreateRecord( 2 );
6012     MSI_RecordSetStringW( uirow, 1, display_name );
6013     MSI_RecordSetStringW( uirow, 2, name );
6014     msi_ui_actiondata( package, szStartServices, uirow );
6015     msiobj_release( &uirow->hdr );
6016
6017     CloseServiceHandle(service);
6018     CloseServiceHandle(scm);
6019
6020     msi_free(name);
6021     msi_free(args);
6022     msi_free(vector);
6023     msi_free(display_name);
6024     return r;
6025 }
6026
6027 static UINT ACTION_StartServices( MSIPACKAGE *package )
6028 {
6029     static const WCHAR query[] = {
6030         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6031         'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
6032     MSIQUERY *view;
6033     UINT rc;
6034
6035     rc = MSI_DatabaseOpenViewW(package->db, query, &view);
6036     if (rc != ERROR_SUCCESS)
6037         return ERROR_SUCCESS;
6038
6039     rc = MSI_IterateRecords(view, NULL, ITERATE_StartService, package);
6040     msiobj_release(&view->hdr);
6041     return rc;
6042 }
6043
6044 static BOOL stop_service_dependents(SC_HANDLE scm, SC_HANDLE service)
6045 {
6046     DWORD i, needed, count;
6047     ENUM_SERVICE_STATUSW *dependencies;
6048     SERVICE_STATUS ss;
6049     SC_HANDLE depserv;
6050     BOOL stopped, ret = FALSE;
6051
6052     if (EnumDependentServicesW(service, SERVICE_ACTIVE, NULL,
6053                                0, &needed, &count))
6054         return TRUE;
6055
6056     if (GetLastError() != ERROR_MORE_DATA)
6057         return FALSE;
6058
6059     dependencies = msi_alloc(needed);
6060     if (!dependencies)
6061         return FALSE;
6062
6063     if (!EnumDependentServicesW(service, SERVICE_ACTIVE, dependencies,
6064                                 needed, &needed, &count))
6065         goto done;
6066
6067     for (i = 0; i < count; i++)
6068     {
6069         depserv = OpenServiceW(scm, dependencies[i].lpServiceName,
6070                                SERVICE_STOP | SERVICE_QUERY_STATUS);
6071         if (!depserv)
6072             goto done;
6073
6074         stopped = ControlService(depserv, SERVICE_CONTROL_STOP, &ss);
6075         CloseServiceHandle(depserv);
6076         if (!stopped)
6077             goto done;
6078     }
6079
6080     ret = TRUE;
6081
6082 done:
6083     msi_free(dependencies);
6084     return ret;
6085 }
6086
6087 static UINT stop_service( LPCWSTR name )
6088 {
6089     SC_HANDLE scm = NULL, service = NULL;
6090     SERVICE_STATUS status;
6091     SERVICE_STATUS_PROCESS ssp;
6092     DWORD needed;
6093
6094     scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
6095     if (!scm)
6096     {
6097         WARN("Failed to open the SCM: %d\n", GetLastError());
6098         goto done;
6099     }
6100
6101     service = OpenServiceW(scm, name,
6102                            SERVICE_STOP |
6103                            SERVICE_QUERY_STATUS |
6104                            SERVICE_ENUMERATE_DEPENDENTS);
6105     if (!service)
6106     {
6107         WARN("Failed to open service (%s): %d\n", debugstr_w(name), GetLastError());
6108         goto done;
6109     }
6110
6111     if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp,
6112                               sizeof(SERVICE_STATUS_PROCESS), &needed))
6113     {
6114         WARN("Failed to query service status (%s): %d\n", debugstr_w(name), GetLastError());
6115         goto done;
6116     }
6117
6118     if (ssp.dwCurrentState == SERVICE_STOPPED)
6119         goto done;
6120
6121     stop_service_dependents(scm, service);
6122
6123     if (!ControlService(service, SERVICE_CONTROL_STOP, &status))
6124         WARN("Failed to stop service (%s): %d\n", debugstr_w(name), GetLastError());
6125
6126 done:
6127     CloseServiceHandle(service);
6128     CloseServiceHandle(scm);
6129
6130     return ERROR_SUCCESS;
6131 }
6132
6133 static UINT ITERATE_StopService( MSIRECORD *rec, LPVOID param )
6134 {
6135     MSIPACKAGE *package = param;
6136     MSICOMPONENT *comp;
6137     MSIRECORD *uirow;
6138     LPCWSTR component;
6139     LPWSTR name = NULL, display_name = NULL;
6140     DWORD event, len;
6141     SC_HANDLE scm;
6142
6143     event = MSI_RecordGetInteger( rec, 3 );
6144     if (!(event & msidbServiceControlEventStop))
6145         return ERROR_SUCCESS;
6146
6147     component = MSI_RecordGetString( rec, 6 );
6148     comp = msi_get_loaded_component( package, component );
6149     if (!comp)
6150         return ERROR_SUCCESS;
6151
6152     comp->Action = msi_get_component_action( package, comp );
6153     if (comp->Action != INSTALLSTATE_ABSENT)
6154     {
6155         TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6156         return ERROR_SUCCESS;
6157     }
6158
6159     scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_CONNECT );
6160     if (!scm)
6161     {
6162         ERR("Failed to open the service control manager\n");
6163         goto done;
6164     }
6165
6166     len = 0;
6167     if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
6168         GetLastError() == ERROR_INSUFFICIENT_BUFFER)
6169     {
6170         if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
6171             GetServiceDisplayNameW( scm, name, display_name, &len );
6172     }
6173     CloseServiceHandle( scm );
6174
6175     deformat_string( package, MSI_RecordGetString( rec, 2 ), &name );
6176     stop_service( name );
6177
6178 done:
6179     uirow = MSI_CreateRecord( 2 );
6180     MSI_RecordSetStringW( uirow, 1, display_name );
6181     MSI_RecordSetStringW( uirow, 2, name );
6182     msi_ui_actiondata( package, szStopServices, uirow );
6183     msiobj_release( &uirow->hdr );
6184
6185     msi_free( name );
6186     msi_free( display_name );
6187     return ERROR_SUCCESS;
6188 }
6189
6190 static UINT ACTION_StopServices( MSIPACKAGE *package )
6191 {
6192     static const WCHAR query[] = {
6193         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6194         'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
6195     MSIQUERY *view;
6196     UINT rc;
6197
6198     rc = MSI_DatabaseOpenViewW(package->db, query, &view);
6199     if (rc != ERROR_SUCCESS)
6200         return ERROR_SUCCESS;
6201
6202     rc = MSI_IterateRecords(view, NULL, ITERATE_StopService, package);
6203     msiobj_release(&view->hdr);
6204     return rc;
6205 }
6206
6207 static UINT ITERATE_DeleteService( MSIRECORD *rec, LPVOID param )
6208 {
6209     MSIPACKAGE *package = param;
6210     MSICOMPONENT *comp;
6211     MSIRECORD *uirow;
6212     LPWSTR name = NULL, display_name = NULL;
6213     DWORD event, len;
6214     SC_HANDLE scm = NULL, service = NULL;
6215
6216     comp = msi_get_loaded_component( package, MSI_RecordGetString(rec, 6) );
6217     if (!comp)
6218         return ERROR_SUCCESS;
6219
6220     event = MSI_RecordGetInteger( rec, 3 );
6221     deformat_string( package, MSI_RecordGetString(rec, 2), &name );
6222
6223     comp->Action = msi_get_component_action( package, comp );
6224     if (!(comp->Action == INSTALLSTATE_LOCAL && (event & msidbServiceControlEventDelete)) &&
6225         !(comp->Action == INSTALLSTATE_ABSENT && (event & msidbServiceControlEventUninstallDelete)))
6226     {
6227         TRACE("service %s not scheduled for removal\n", debugstr_w(name));
6228         msi_free( name );
6229         return ERROR_SUCCESS;
6230     }
6231     stop_service( name );
6232
6233     scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_ALL_ACCESS );
6234     if (!scm)
6235     {
6236         WARN("Failed to open the SCM: %d\n", GetLastError());
6237         goto done;
6238     }
6239
6240     len = 0;
6241     if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
6242         GetLastError() == ERROR_INSUFFICIENT_BUFFER)
6243     {
6244         if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
6245             GetServiceDisplayNameW( scm, name, display_name, &len );
6246     }
6247
6248     service = OpenServiceW( scm, name, DELETE );
6249     if (!service)
6250     {
6251         WARN("Failed to open service (%s): %u\n", debugstr_w(name), GetLastError());
6252         goto done;
6253     }
6254
6255     if (!DeleteService( service ))
6256         WARN("Failed to delete service (%s): %u\n", debugstr_w(name), GetLastError());
6257
6258 done:
6259     uirow = MSI_CreateRecord( 2 );
6260     MSI_RecordSetStringW( uirow, 1, display_name );
6261     MSI_RecordSetStringW( uirow, 2, name );
6262     msi_ui_actiondata( package, szDeleteServices, uirow );
6263     msiobj_release( &uirow->hdr );
6264
6265     CloseServiceHandle( service );
6266     CloseServiceHandle( scm );
6267     msi_free( name );
6268     msi_free( display_name );
6269
6270     return ERROR_SUCCESS;
6271 }
6272
6273 static UINT ACTION_DeleteServices( MSIPACKAGE *package )
6274 {
6275     static const WCHAR query[] = {
6276         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6277         'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
6278     MSIQUERY *view;
6279     UINT rc;
6280
6281     rc = MSI_DatabaseOpenViewW( package->db, query, &view );
6282     if (rc != ERROR_SUCCESS)
6283         return ERROR_SUCCESS;
6284
6285     rc = MSI_IterateRecords( view, NULL, ITERATE_DeleteService, package );
6286     msiobj_release( &view->hdr );
6287     return rc;
6288 }
6289
6290 static UINT ITERATE_InstallODBCDriver( MSIRECORD *rec, LPVOID param )
6291 {
6292     MSIPACKAGE *package = param;
6293     LPWSTR driver, driver_path, ptr;
6294     WCHAR outpath[MAX_PATH];
6295     MSIFILE *driver_file = NULL, *setup_file = NULL;
6296     MSICOMPONENT *comp;
6297     MSIRECORD *uirow;
6298     LPCWSTR desc, file_key, component;
6299     DWORD len, usage;
6300     UINT r = ERROR_SUCCESS;
6301
6302     static const WCHAR driver_fmt[] = {
6303         'D','r','i','v','e','r','=','%','s',0};
6304     static const WCHAR setup_fmt[] = {
6305         'S','e','t','u','p','=','%','s',0};
6306     static const WCHAR usage_fmt[] = {
6307         'F','i','l','e','U','s','a','g','e','=','1',0};
6308
6309     component = MSI_RecordGetString( rec, 2 );
6310     comp = msi_get_loaded_component( package, component );
6311     if (!comp)
6312         return ERROR_SUCCESS;
6313
6314     comp->Action = msi_get_component_action( package, comp );
6315     if (comp->Action != INSTALLSTATE_LOCAL)
6316     {
6317         TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6318         return ERROR_SUCCESS;
6319     }
6320     desc = MSI_RecordGetString(rec, 3);
6321
6322     file_key = MSI_RecordGetString( rec, 4 );
6323     if (file_key) driver_file = msi_get_loaded_file( package, file_key );
6324
6325     file_key = MSI_RecordGetString( rec, 5 );
6326     if (file_key) setup_file = msi_get_loaded_file( package, file_key );
6327
6328     if (!driver_file)
6329     {
6330         ERR("ODBC Driver entry not found!\n");
6331         return ERROR_FUNCTION_FAILED;
6332     }
6333
6334     len = lstrlenW(desc) + lstrlenW(driver_fmt) + lstrlenW(driver_file->FileName);
6335     if (setup_file)
6336         len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
6337     len += lstrlenW(usage_fmt) + 2; /* \0\0 */
6338
6339     driver = msi_alloc(len * sizeof(WCHAR));
6340     if (!driver)
6341         return ERROR_OUTOFMEMORY;
6342
6343     ptr = driver;
6344     lstrcpyW(ptr, desc);
6345     ptr += lstrlenW(ptr) + 1;
6346
6347     len = sprintfW(ptr, driver_fmt, driver_file->FileName);
6348     ptr += len + 1;
6349
6350     if (setup_file)
6351     {
6352         len = sprintfW(ptr, setup_fmt, setup_file->FileName);
6353         ptr += len + 1;
6354     }
6355
6356     lstrcpyW(ptr, usage_fmt);
6357     ptr += lstrlenW(ptr) + 1;
6358     *ptr = '\0';
6359
6360     if (!driver_file->TargetPath)
6361     {
6362         const WCHAR *dir = msi_get_target_folder( package, driver_file->Component->Directory );
6363         driver_file->TargetPath = msi_build_directory_name( 2, dir, driver_file->FileName );
6364     }
6365     driver_path = strdupW(driver_file->TargetPath);
6366     ptr = strrchrW(driver_path, '\\');
6367     if (ptr) *ptr = '\0';
6368
6369     if (!SQLInstallDriverExW(driver, driver_path, outpath, MAX_PATH,
6370                              NULL, ODBC_INSTALL_COMPLETE, &usage))
6371     {
6372         ERR("Failed to install SQL driver!\n");
6373         r = ERROR_FUNCTION_FAILED;
6374     }
6375
6376     uirow = MSI_CreateRecord( 5 );
6377     MSI_RecordSetStringW( uirow, 1, desc );
6378     MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6379     MSI_RecordSetStringW( uirow, 3, driver_file->Component->Directory );
6380     msi_ui_actiondata( package, szInstallODBC, uirow );
6381     msiobj_release( &uirow->hdr );
6382
6383     msi_free(driver);
6384     msi_free(driver_path);
6385
6386     return r;
6387 }
6388
6389 static UINT ITERATE_InstallODBCTranslator( MSIRECORD *rec, LPVOID param )
6390 {
6391     MSIPACKAGE *package = param;
6392     LPWSTR translator, translator_path, ptr;
6393     WCHAR outpath[MAX_PATH];
6394     MSIFILE *translator_file = NULL, *setup_file = NULL;
6395     MSICOMPONENT *comp;
6396     MSIRECORD *uirow;
6397     LPCWSTR desc, file_key, component;
6398     DWORD len, usage;
6399     UINT r = ERROR_SUCCESS;
6400
6401     static const WCHAR translator_fmt[] = {
6402         'T','r','a','n','s','l','a','t','o','r','=','%','s',0};
6403     static const WCHAR setup_fmt[] = {
6404         'S','e','t','u','p','=','%','s',0};
6405
6406     component = MSI_RecordGetString( rec, 2 );
6407     comp = msi_get_loaded_component( package, component );
6408     if (!comp)
6409         return ERROR_SUCCESS;
6410
6411     comp->Action = msi_get_component_action( package, comp );
6412     if (comp->Action != INSTALLSTATE_LOCAL)
6413     {
6414         TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6415         return ERROR_SUCCESS;
6416     }
6417     desc = MSI_RecordGetString(rec, 3);
6418
6419     file_key = MSI_RecordGetString( rec, 4 );
6420     if (file_key) translator_file = msi_get_loaded_file( package, file_key );
6421
6422     file_key = MSI_RecordGetString( rec, 5 );
6423     if (file_key) setup_file = msi_get_loaded_file( package, file_key );
6424
6425     if (!translator_file)
6426     {
6427         ERR("ODBC Translator entry not found!\n");
6428         return ERROR_FUNCTION_FAILED;
6429     }
6430
6431     len = lstrlenW(desc) + lstrlenW(translator_fmt) + lstrlenW(translator_file->FileName) + 2; /* \0\0 */
6432     if (setup_file)
6433         len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
6434
6435     translator = msi_alloc(len * sizeof(WCHAR));
6436     if (!translator)
6437         return ERROR_OUTOFMEMORY;
6438
6439     ptr = translator;
6440     lstrcpyW(ptr, desc);
6441     ptr += lstrlenW(ptr) + 1;
6442
6443     len = sprintfW(ptr, translator_fmt, translator_file->FileName);
6444     ptr += len + 1;
6445
6446     if (setup_file)
6447     {
6448         len = sprintfW(ptr, setup_fmt, setup_file->FileName);
6449         ptr += len + 1;
6450     }
6451     *ptr = '\0';
6452
6453     translator_path = strdupW(translator_file->TargetPath);
6454     ptr = strrchrW(translator_path, '\\');
6455     if (ptr) *ptr = '\0';
6456
6457     if (!SQLInstallTranslatorExW(translator, translator_path, outpath, MAX_PATH,
6458                                  NULL, ODBC_INSTALL_COMPLETE, &usage))
6459     {
6460         ERR("Failed to install SQL translator!\n");
6461         r = ERROR_FUNCTION_FAILED;
6462     }
6463
6464     uirow = MSI_CreateRecord( 5 );
6465     MSI_RecordSetStringW( uirow, 1, desc );
6466     MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6467     MSI_RecordSetStringW( uirow, 3, translator_file->Component->Directory );
6468     msi_ui_actiondata( package, szInstallODBC, uirow );
6469     msiobj_release( &uirow->hdr );
6470
6471     msi_free(translator);
6472     msi_free(translator_path);
6473
6474     return r;
6475 }
6476
6477 static UINT ITERATE_InstallODBCDataSource( MSIRECORD *rec, LPVOID param )
6478 {
6479     MSIPACKAGE *package = param;
6480     MSICOMPONENT *comp;
6481     LPWSTR attrs;
6482     LPCWSTR desc, driver, component;
6483     WORD request = ODBC_ADD_SYS_DSN;
6484     INT registration;
6485     DWORD len;
6486     UINT r = ERROR_SUCCESS;
6487     MSIRECORD *uirow;
6488
6489     static const WCHAR attrs_fmt[] = {
6490         'D','S','N','=','%','s',0 };
6491
6492     component = MSI_RecordGetString( rec, 2 );
6493     comp = msi_get_loaded_component( package, component );
6494     if (!comp)
6495         return ERROR_SUCCESS;
6496
6497     comp->Action = msi_get_component_action( package, comp );
6498     if (comp->Action != INSTALLSTATE_LOCAL)
6499     {
6500         TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6501         return ERROR_SUCCESS;
6502     }
6503
6504     desc = MSI_RecordGetString(rec, 3);
6505     driver = MSI_RecordGetString(rec, 4);
6506     registration = MSI_RecordGetInteger(rec, 5);
6507
6508     if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_ADD_SYS_DSN;
6509     else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_ADD_DSN;
6510
6511     len = lstrlenW(attrs_fmt) + lstrlenW(desc) + 2; /* \0\0 */
6512     attrs = msi_alloc(len * sizeof(WCHAR));
6513     if (!attrs)
6514         return ERROR_OUTOFMEMORY;
6515
6516     len = sprintfW(attrs, attrs_fmt, desc);
6517     attrs[len + 1] = 0;
6518
6519     if (!SQLConfigDataSourceW(NULL, request, driver, attrs))
6520     {
6521         ERR("Failed to install SQL data source!\n");
6522         r = ERROR_FUNCTION_FAILED;
6523     }
6524
6525     uirow = MSI_CreateRecord( 5 );
6526     MSI_RecordSetStringW( uirow, 1, desc );
6527     MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6528     MSI_RecordSetInteger( uirow, 3, request );
6529     msi_ui_actiondata( package, szInstallODBC, uirow );
6530     msiobj_release( &uirow->hdr );
6531
6532     msi_free(attrs);
6533
6534     return r;
6535 }
6536
6537 static UINT ACTION_InstallODBC( MSIPACKAGE *package )
6538 {
6539     static const WCHAR driver_query[] = {
6540         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6541         'O','D','B','C','D','r','i','v','e','r',0};
6542     static const WCHAR translator_query[] = {
6543         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6544         'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
6545     static const WCHAR source_query[] = {
6546         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6547         'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0};
6548     MSIQUERY *view;
6549     UINT rc;
6550
6551     rc = MSI_DatabaseOpenViewW(package->db, driver_query, &view);
6552     if (rc == ERROR_SUCCESS)
6553     {
6554         rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDriver, package);
6555         msiobj_release(&view->hdr);
6556         if (rc != ERROR_SUCCESS)
6557             return rc;
6558     }
6559     rc = MSI_DatabaseOpenViewW(package->db, translator_query, &view);
6560     if (rc == ERROR_SUCCESS)
6561     {
6562         rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCTranslator, package);
6563         msiobj_release(&view->hdr);
6564         if (rc != ERROR_SUCCESS)
6565             return rc;
6566     }
6567     rc = MSI_DatabaseOpenViewW(package->db, source_query, &view);
6568     if (rc == ERROR_SUCCESS)
6569     {
6570         rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDataSource, package);
6571         msiobj_release(&view->hdr);
6572         if (rc != ERROR_SUCCESS)
6573             return rc;
6574     }
6575     return ERROR_SUCCESS;
6576 }
6577
6578 static UINT ITERATE_RemoveODBCDriver( MSIRECORD *rec, LPVOID param )
6579 {
6580     MSIPACKAGE *package = param;
6581     MSICOMPONENT *comp;
6582     MSIRECORD *uirow;
6583     DWORD usage;
6584     LPCWSTR desc, component;
6585
6586     component = MSI_RecordGetString( rec, 2 );
6587     comp = msi_get_loaded_component( package, component );
6588     if (!comp)
6589         return ERROR_SUCCESS;
6590
6591     comp->Action = msi_get_component_action( package, comp );
6592     if (comp->Action != INSTALLSTATE_ABSENT)
6593     {
6594         TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6595         return ERROR_SUCCESS;
6596     }
6597
6598     desc = MSI_RecordGetString( rec, 3 );
6599     if (!SQLRemoveDriverW( desc, FALSE, &usage ))
6600     {
6601         WARN("Failed to remove ODBC driver\n");
6602     }
6603     else if (!usage)
6604     {
6605         FIXME("Usage count reached 0\n");
6606     }
6607
6608     uirow = MSI_CreateRecord( 2 );
6609     MSI_RecordSetStringW( uirow, 1, desc );
6610     MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6611     msi_ui_actiondata( package, szRemoveODBC, uirow );
6612     msiobj_release( &uirow->hdr );
6613
6614     return ERROR_SUCCESS;
6615 }
6616
6617 static UINT ITERATE_RemoveODBCTranslator( MSIRECORD *rec, LPVOID param )
6618 {
6619     MSIPACKAGE *package = param;
6620     MSICOMPONENT *comp;
6621     MSIRECORD *uirow;
6622     DWORD usage;
6623     LPCWSTR desc, component;
6624
6625     component = MSI_RecordGetString( rec, 2 );
6626     comp = msi_get_loaded_component( package, component );
6627     if (!comp)
6628         return ERROR_SUCCESS;
6629
6630     comp->Action = msi_get_component_action( package, comp );
6631     if (comp->Action != INSTALLSTATE_ABSENT)
6632     {
6633         TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6634         return ERROR_SUCCESS;
6635     }
6636
6637     desc = MSI_RecordGetString( rec, 3 );
6638     if (!SQLRemoveTranslatorW( desc, &usage ))
6639     {
6640         WARN("Failed to remove ODBC translator\n");
6641     }
6642     else if (!usage)
6643     {
6644         FIXME("Usage count reached 0\n");
6645     }
6646
6647     uirow = MSI_CreateRecord( 2 );
6648     MSI_RecordSetStringW( uirow, 1, desc );
6649     MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6650     msi_ui_actiondata( package, szRemoveODBC, uirow );
6651     msiobj_release( &uirow->hdr );
6652
6653     return ERROR_SUCCESS;
6654 }
6655
6656 static UINT ITERATE_RemoveODBCDataSource( MSIRECORD *rec, LPVOID param )
6657 {
6658     MSIPACKAGE *package = param;
6659     MSICOMPONENT *comp;
6660     MSIRECORD *uirow;
6661     LPWSTR attrs;
6662     LPCWSTR desc, driver, component;
6663     WORD request = ODBC_REMOVE_SYS_DSN;
6664     INT registration;
6665     DWORD len;
6666
6667     static const WCHAR attrs_fmt[] = {
6668         'D','S','N','=','%','s',0 };
6669
6670     component = MSI_RecordGetString( rec, 2 );
6671     comp = msi_get_loaded_component( package, component );
6672     if (!comp)
6673         return ERROR_SUCCESS;
6674
6675     comp->Action = msi_get_component_action( package, comp );
6676     if (comp->Action != INSTALLSTATE_ABSENT)
6677     {
6678         TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6679         return ERROR_SUCCESS;
6680     }
6681
6682     desc = MSI_RecordGetString( rec, 3 );
6683     driver = MSI_RecordGetString( rec, 4 );
6684     registration = MSI_RecordGetInteger( rec, 5 );
6685
6686     if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_REMOVE_SYS_DSN;
6687     else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_REMOVE_DSN;
6688
6689     len = strlenW( attrs_fmt ) + strlenW( desc ) + 2; /* \0\0 */
6690     attrs = msi_alloc( len * sizeof(WCHAR) );
6691     if (!attrs)
6692         return ERROR_OUTOFMEMORY;
6693
6694     FIXME("Use ODBCSourceAttribute table\n");
6695
6696     len = sprintfW( attrs, attrs_fmt, desc );
6697     attrs[len + 1] = 0;
6698
6699     if (!SQLConfigDataSourceW( NULL, request, driver, attrs ))
6700     {
6701         WARN("Failed to remove ODBC data source\n");
6702     }
6703     msi_free( attrs );
6704
6705     uirow = MSI_CreateRecord( 3 );
6706     MSI_RecordSetStringW( uirow, 1, desc );
6707     MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6708     MSI_RecordSetInteger( uirow, 3, request );
6709     msi_ui_actiondata( package, szRemoveODBC, uirow );
6710     msiobj_release( &uirow->hdr );
6711
6712     return ERROR_SUCCESS;
6713 }
6714
6715 static UINT ACTION_RemoveODBC( MSIPACKAGE *package )
6716 {
6717     static const WCHAR driver_query[] = {
6718         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6719         'O','D','B','C','D','r','i','v','e','r',0};
6720     static const WCHAR translator_query[] = {
6721         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6722         'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
6723     static const WCHAR source_query[] = {
6724         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6725         'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0};
6726     MSIQUERY *view;
6727     UINT rc;
6728
6729     rc = MSI_DatabaseOpenViewW( package->db, driver_query, &view );
6730     if (rc == ERROR_SUCCESS)
6731     {
6732         rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDriver, package );
6733         msiobj_release( &view->hdr );
6734         if (rc != ERROR_SUCCESS)
6735             return rc;
6736     }
6737     rc = MSI_DatabaseOpenViewW( package->db, translator_query, &view );
6738     if (rc == ERROR_SUCCESS)
6739     {
6740         rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCTranslator, package );
6741         msiobj_release( &view->hdr );
6742         if (rc != ERROR_SUCCESS)
6743             return rc;
6744     }
6745     rc = MSI_DatabaseOpenViewW( package->db, source_query, &view );
6746     if (rc == ERROR_SUCCESS)
6747     {
6748         rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDataSource, package );
6749         msiobj_release( &view->hdr );
6750         if (rc != ERROR_SUCCESS)
6751             return rc;
6752     }
6753     return ERROR_SUCCESS;
6754 }
6755
6756 #define ENV_ACT_SETALWAYS   0x1
6757 #define ENV_ACT_SETABSENT   0x2
6758 #define ENV_ACT_REMOVE      0x4
6759 #define ENV_ACT_REMOVEMATCH 0x8
6760
6761 #define ENV_MOD_MACHINE     0x20000000
6762 #define ENV_MOD_APPEND      0x40000000
6763 #define ENV_MOD_PREFIX      0x80000000
6764 #define ENV_MOD_MASK        0xC0000000
6765
6766 #define check_flag_combo(x, y) ((x) & ~(y)) == (y)
6767
6768 static UINT env_parse_flags( LPCWSTR *name, LPCWSTR *value, DWORD *flags )
6769 {
6770     LPCWSTR cptr = *name;
6771
6772     static const WCHAR prefix[] = {'[','~',']',0};
6773     static const int prefix_len = 3;
6774
6775     *flags = 0;
6776     while (*cptr)
6777     {
6778         if (*cptr == '=')
6779             *flags |= ENV_ACT_SETALWAYS;
6780         else if (*cptr == '+')
6781             *flags |= ENV_ACT_SETABSENT;
6782         else if (*cptr == '-')
6783             *flags |= ENV_ACT_REMOVE;
6784         else if (*cptr == '!')
6785             *flags |= ENV_ACT_REMOVEMATCH;
6786         else if (*cptr == '*')
6787             *flags |= ENV_MOD_MACHINE;
6788         else
6789             break;
6790
6791         cptr++;
6792         (*name)++;
6793     }
6794
6795     if (!*cptr)
6796     {
6797         ERR("Missing environment variable\n");
6798         return ERROR_FUNCTION_FAILED;
6799     }
6800
6801     if (*value)
6802     {
6803         LPCWSTR ptr = *value;
6804         if (!strncmpW(ptr, prefix, prefix_len))
6805         {
6806             if (ptr[prefix_len] == szSemiColon[0])
6807             {
6808                 *flags |= ENV_MOD_APPEND;
6809                 *value += lstrlenW(prefix);
6810             }
6811             else
6812             {
6813                 *value = NULL;
6814             }
6815         }
6816         else if (lstrlenW(*value) >= prefix_len)
6817         {
6818             ptr += lstrlenW(ptr) - prefix_len;
6819             if (!strcmpW( ptr, prefix ))
6820             {
6821                 if ((ptr-1) > *value && *(ptr-1) == szSemiColon[0])
6822                 {
6823                     *flags |= ENV_MOD_PREFIX;
6824                     /* the "[~]" will be removed by deformat_string */;
6825                 }
6826                 else
6827                 {
6828                     *value = NULL;
6829                 }
6830             }
6831         }
6832     }
6833
6834     if (check_flag_combo(*flags, ENV_ACT_SETALWAYS | ENV_ACT_SETABSENT) ||
6835         check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETABSENT) ||
6836         check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETALWAYS) ||
6837         check_flag_combo(*flags, ENV_ACT_SETABSENT | ENV_MOD_MASK))
6838     {
6839         ERR("Invalid flags: %08x\n", *flags);
6840         return ERROR_FUNCTION_FAILED;
6841     }
6842
6843     if (!*flags)
6844         *flags = ENV_ACT_SETALWAYS | ENV_ACT_REMOVE;
6845
6846     return ERROR_SUCCESS;
6847 }
6848
6849 static UINT open_env_key( DWORD flags, HKEY *key )
6850 {
6851     static const WCHAR user_env[] =
6852         {'E','n','v','i','r','o','n','m','e','n','t',0};
6853     static const WCHAR machine_env[] =
6854         {'S','y','s','t','e','m','\\',
6855          'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
6856          'C','o','n','t','r','o','l','\\',
6857          'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r','\\',
6858          'E','n','v','i','r','o','n','m','e','n','t',0};
6859     const WCHAR *env;
6860     HKEY root;
6861     LONG res;
6862
6863     if (flags & ENV_MOD_MACHINE)
6864     {
6865         env = machine_env;
6866         root = HKEY_LOCAL_MACHINE;
6867     }
6868     else
6869     {
6870         env = user_env;
6871         root = HKEY_CURRENT_USER;
6872     }
6873
6874     res = RegOpenKeyExW( root, env, 0, KEY_ALL_ACCESS, key );
6875     if (res != ERROR_SUCCESS)
6876     {
6877         WARN("Failed to open key %s (%d)\n", debugstr_w(env), res);
6878         return ERROR_FUNCTION_FAILED;
6879     }
6880
6881     return ERROR_SUCCESS;
6882 }
6883
6884 static UINT ITERATE_WriteEnvironmentString( MSIRECORD *rec, LPVOID param )
6885 {
6886     MSIPACKAGE *package = param;
6887     LPCWSTR name, value, component;
6888     LPWSTR data = NULL, newval = NULL, deformatted = NULL, ptr;
6889     DWORD flags, type, size;
6890     UINT res;
6891     HKEY env = NULL;
6892     MSICOMPONENT *comp;
6893     MSIRECORD *uirow;
6894     int action = 0;
6895
6896     component = MSI_RecordGetString(rec, 4);
6897     comp = msi_get_loaded_component(package, component);
6898     if (!comp)
6899         return ERROR_SUCCESS;
6900
6901     comp->Action = msi_get_component_action( package, comp );
6902     if (comp->Action != INSTALLSTATE_LOCAL)
6903     {
6904         TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6905         return ERROR_SUCCESS;
6906     }
6907     name = MSI_RecordGetString(rec, 2);
6908     value = MSI_RecordGetString(rec, 3);
6909
6910     TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
6911
6912     res = env_parse_flags(&name, &value, &flags);
6913     if (res != ERROR_SUCCESS || !value)
6914        goto done;
6915
6916     if (value && !deformat_string(package, value, &deformatted))
6917     {
6918         res = ERROR_OUTOFMEMORY;
6919         goto done;
6920     }
6921
6922     value = deformatted;
6923
6924     res = open_env_key( flags, &env );
6925     if (res != ERROR_SUCCESS)
6926         goto done;
6927
6928     if (flags & ENV_MOD_MACHINE)
6929         action |= 0x20000000;
6930
6931     size = 0;
6932     type = REG_SZ;
6933     res = RegQueryValueExW(env, name, NULL, &type, NULL, &size);
6934     if ((res != ERROR_SUCCESS && res != ERROR_FILE_NOT_FOUND) ||
6935         (res == ERROR_SUCCESS && type != REG_SZ && type != REG_EXPAND_SZ))
6936         goto done;
6937
6938     if ((res == ERROR_FILE_NOT_FOUND || !(flags & ENV_MOD_MASK)))
6939     {
6940         action = 0x2;
6941
6942         /* Nothing to do. */
6943         if (!value)
6944         {
6945             res = ERROR_SUCCESS;
6946             goto done;
6947         }
6948
6949         /* If we are appending but the string was empty, strip ; */
6950         if ((flags & ENV_MOD_APPEND) && (value[0] == szSemiColon[0])) value++;
6951
6952         size = (lstrlenW(value) + 1) * sizeof(WCHAR);
6953         newval = strdupW(value);
6954         if (!newval)
6955         {
6956             res = ERROR_OUTOFMEMORY;
6957             goto done;
6958         }
6959     }
6960     else
6961     {
6962         action = 0x1;
6963
6964         /* Contrary to MSDN, +-variable to [~];path works */
6965         if (flags & ENV_ACT_SETABSENT && !(flags & ENV_MOD_MASK))
6966         {
6967             res = ERROR_SUCCESS;
6968             goto done;
6969         }
6970
6971         data = msi_alloc(size);
6972         if (!data)
6973         {
6974             RegCloseKey(env);
6975             return ERROR_OUTOFMEMORY;
6976         }
6977
6978         res = RegQueryValueExW(env, name, NULL, &type, (LPVOID)data, &size);
6979         if (res != ERROR_SUCCESS)
6980             goto done;
6981
6982         if (flags & ENV_ACT_REMOVEMATCH && (!value || !strcmpW( data, value )))
6983         {
6984             action = 0x4;
6985             res = RegDeleteValueW(env, name);
6986             if (res != ERROR_SUCCESS)
6987                 WARN("Failed to remove value %s (%d)\n", debugstr_w(name), res);
6988             goto done;
6989         }
6990
6991         size = (lstrlenW(data) + 1) * sizeof(WCHAR);
6992         if (flags & ENV_MOD_MASK)
6993         {
6994             DWORD mod_size;
6995             int multiplier = 0;
6996             if (flags & ENV_MOD_APPEND) multiplier++;
6997             if (flags & ENV_MOD_PREFIX) multiplier++;
6998             mod_size = lstrlenW(value) * multiplier;
6999             size += mod_size * sizeof(WCHAR);
7000         }
7001
7002         newval = msi_alloc(size);
7003         ptr = newval;
7004         if (!newval)
7005         {
7006             res = ERROR_OUTOFMEMORY;
7007             goto done;
7008         }
7009
7010         if (flags & ENV_MOD_PREFIX)
7011         {
7012             lstrcpyW(newval, value);
7013             ptr = newval + lstrlenW(value);
7014             action |= 0x80000000;
7015         }
7016
7017         lstrcpyW(ptr, data);
7018
7019         if (flags & ENV_MOD_APPEND)
7020         {
7021             lstrcatW(newval, value);
7022             action |= 0x40000000;
7023         }
7024     }
7025     TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(newval));
7026     res = RegSetValueExW(env, name, 0, type, (LPVOID)newval, size);
7027     if (res)
7028     {
7029         WARN("Failed to set %s to %s (%d)\n",  debugstr_w(name), debugstr_w(newval), res);
7030     }
7031
7032 done:
7033     uirow = MSI_CreateRecord( 3 );
7034     MSI_RecordSetStringW( uirow, 1, name );
7035     MSI_RecordSetStringW( uirow, 2, newval );
7036     MSI_RecordSetInteger( uirow, 3, action );
7037     msi_ui_actiondata( package, szWriteEnvironmentStrings, uirow );
7038     msiobj_release( &uirow->hdr );
7039
7040     if (env) RegCloseKey(env);
7041     msi_free(deformatted);
7042     msi_free(data);
7043     msi_free(newval);
7044     return res;
7045 }
7046
7047 static UINT ACTION_WriteEnvironmentStrings( MSIPACKAGE *package )
7048 {
7049     static const WCHAR query[] = {
7050         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7051         '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
7052     MSIQUERY *view;
7053     UINT rc;
7054
7055     rc = MSI_DatabaseOpenViewW(package->db, query, &view);
7056     if (rc != ERROR_SUCCESS)
7057         return ERROR_SUCCESS;
7058
7059     rc = MSI_IterateRecords(view, NULL, ITERATE_WriteEnvironmentString, package);
7060     msiobj_release(&view->hdr);
7061     return rc;
7062 }
7063
7064 static UINT ITERATE_RemoveEnvironmentString( MSIRECORD *rec, LPVOID param )
7065 {
7066     MSIPACKAGE *package = param;
7067     LPCWSTR name, value, component;
7068     LPWSTR deformatted = NULL;
7069     DWORD flags;
7070     HKEY env;
7071     MSICOMPONENT *comp;
7072     MSIRECORD *uirow;
7073     int action = 0;
7074     LONG res;
7075     UINT r;
7076
7077     component = MSI_RecordGetString( rec, 4 );
7078     comp = msi_get_loaded_component( package, component );
7079     if (!comp)
7080         return ERROR_SUCCESS;
7081
7082     comp->Action = msi_get_component_action( package, comp );
7083     if (comp->Action != INSTALLSTATE_ABSENT)
7084     {
7085         TRACE("component not scheduled for removal %s\n", debugstr_w(component));
7086         return ERROR_SUCCESS;
7087     }
7088     name = MSI_RecordGetString( rec, 2 );
7089     value = MSI_RecordGetString( rec, 3 );
7090
7091     TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
7092
7093     r = env_parse_flags( &name, &value, &flags );
7094     if (r != ERROR_SUCCESS)
7095        return r;
7096
7097     if (!(flags & ENV_ACT_REMOVE))
7098     {
7099         TRACE("Environment variable %s not marked for removal\n", debugstr_w(name));
7100         return ERROR_SUCCESS;
7101     }
7102
7103     if (value && !deformat_string( package, value, &deformatted ))
7104         return ERROR_OUTOFMEMORY;
7105
7106     value = deformatted;
7107
7108     r = open_env_key( flags, &env );
7109     if (r != ERROR_SUCCESS)
7110     {
7111         r = ERROR_SUCCESS;
7112         goto done;
7113     }
7114
7115     if (flags & ENV_MOD_MACHINE)
7116         action |= 0x20000000;
7117
7118     TRACE("Removing %s\n", debugstr_w(name));
7119
7120     res = RegDeleteValueW( env, name );
7121     if (res != ERROR_SUCCESS)
7122     {
7123         WARN("Failed to delete value %s (%d)\n", debugstr_w(name), res);
7124         r = ERROR_SUCCESS;
7125     }
7126
7127 done:
7128     uirow = MSI_CreateRecord( 3 );
7129     MSI_RecordSetStringW( uirow, 1, name );
7130     MSI_RecordSetStringW( uirow, 2, value );
7131     MSI_RecordSetInteger( uirow, 3, action );
7132     msi_ui_actiondata( package, szRemoveEnvironmentStrings, uirow );
7133     msiobj_release( &uirow->hdr );
7134
7135     if (env) RegCloseKey( env );
7136     msi_free( deformatted );
7137     return r;
7138 }
7139
7140 static UINT ACTION_RemoveEnvironmentStrings( MSIPACKAGE *package )
7141 {
7142     static const WCHAR query[] = {
7143         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7144         '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
7145     MSIQUERY *view;
7146     UINT rc;
7147
7148     rc = MSI_DatabaseOpenViewW( package->db, query, &view );
7149     if (rc != ERROR_SUCCESS)
7150         return ERROR_SUCCESS;
7151
7152     rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveEnvironmentString, package );
7153     msiobj_release( &view->hdr );
7154     return rc;
7155 }
7156
7157 UINT msi_validate_product_id( MSIPACKAGE *package )
7158 {
7159     LPWSTR key, template, id;
7160     UINT r = ERROR_SUCCESS;
7161
7162     id = msi_dup_property( package->db, szProductID );
7163     if (id)
7164     {
7165         msi_free( id );
7166         return ERROR_SUCCESS;
7167     }
7168     template = msi_dup_property( package->db, szPIDTemplate );
7169     key = msi_dup_property( package->db, szPIDKEY );
7170     if (key && template)
7171     {
7172         FIXME( "partial stub: template %s key %s\n", debugstr_w(template), debugstr_w(key) );
7173         r = msi_set_property( package->db, szProductID, key, -1 );
7174     }
7175     msi_free( template );
7176     msi_free( key );
7177     return r;
7178 }
7179
7180 static UINT ACTION_ValidateProductID( MSIPACKAGE *package )
7181 {
7182     return msi_validate_product_id( package );
7183 }
7184
7185 static UINT ACTION_ScheduleReboot( MSIPACKAGE *package )
7186 {
7187     TRACE("\n");
7188     package->need_reboot_at_end = 1;
7189     return ERROR_SUCCESS;
7190 }
7191
7192 static UINT ACTION_AllocateRegistrySpace( MSIPACKAGE *package )
7193 {
7194     static const WCHAR szAvailableFreeReg[] =
7195         {'A','V','A','I','L','A','B','L','E','F','R','E','E','R','E','G',0};
7196     MSIRECORD *uirow;
7197     int space = msi_get_property_int( package->db, szAvailableFreeReg, 0 );
7198
7199     TRACE("%p %d kilobytes\n", package, space);
7200
7201     uirow = MSI_CreateRecord( 1 );
7202     MSI_RecordSetInteger( uirow, 1, space );
7203     msi_ui_actiondata( package, szAllocateRegistrySpace, uirow );
7204     msiobj_release( &uirow->hdr );
7205
7206     return ERROR_SUCCESS;
7207 }
7208
7209 static UINT ACTION_DisableRollback( MSIPACKAGE *package )
7210 {
7211     TRACE("%p\n", package);
7212
7213     msi_set_property( package->db, szRollbackDisabled, szOne, -1 );
7214     return ERROR_SUCCESS;
7215 }
7216
7217 static UINT ACTION_InstallAdminPackage( MSIPACKAGE *package )
7218 {
7219     FIXME("%p\n", package);
7220     return ERROR_SUCCESS;
7221 }
7222
7223 static UINT ACTION_SetODBCFolders( MSIPACKAGE *package )
7224 {
7225     static const WCHAR driver_query[] = {
7226         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7227         'O','D','B','C','D','r','i','v','e','r',0};
7228     static const WCHAR translator_query[] = {
7229         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7230         'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
7231     MSIQUERY *view;
7232     UINT r, count;
7233
7234     r = MSI_DatabaseOpenViewW( package->db, driver_query, &view );
7235     if (r == ERROR_SUCCESS)
7236     {
7237         count = 0;
7238         r = MSI_IterateRecords( view, &count, NULL, package );
7239         msiobj_release( &view->hdr );
7240         if (r != ERROR_SUCCESS)
7241             return r;
7242         if (count) FIXME("ignored %u rows in ODBCDriver table\n", count);
7243     }
7244     r = MSI_DatabaseOpenViewW( package->db, translator_query, &view );
7245     if (r == ERROR_SUCCESS)
7246     {
7247         count = 0;
7248         r = MSI_IterateRecords( view, &count, NULL, package );
7249         msiobj_release( &view->hdr );
7250         if (r != ERROR_SUCCESS)
7251             return r;
7252         if (count) FIXME("ignored %u rows in ODBCTranslator table\n", count);
7253     }
7254     return ERROR_SUCCESS;
7255 }
7256
7257 static UINT ITERATE_RemoveExistingProducts( MSIRECORD *rec, LPVOID param )
7258 {
7259     static const WCHAR fmtW[] =
7260         {'m','s','i','e','x','e','c',' ','/','i',' ','%','s',' ','R','E','M','O','V','E','=','%','s',0};
7261     MSIPACKAGE *package = param;
7262     const WCHAR *property = MSI_RecordGetString( rec, 7 );
7263     UINT len = sizeof(fmtW)/sizeof(fmtW[0]);
7264     WCHAR *product, *features, *cmd;
7265     STARTUPINFOW si;
7266     PROCESS_INFORMATION info;
7267     BOOL ret;
7268
7269     if (!(product = msi_dup_property( package->db, property ))) return ERROR_SUCCESS;
7270
7271     deformat_string( package, MSI_RecordGetString( rec, 6 ), &features );
7272
7273     len += strlenW( product );
7274     if (features)
7275         len += strlenW( features );
7276     else
7277         len += sizeof(szAll) / sizeof(szAll[0]);
7278
7279     if (!(cmd = msi_alloc( len * sizeof(WCHAR) )))
7280     {
7281         msi_free( product );
7282         msi_free( features );
7283         return ERROR_OUTOFMEMORY;
7284     }
7285     sprintfW( cmd, fmtW, product, features ? features : szAll );
7286     msi_free( product );
7287     msi_free( features );
7288
7289     memset( &si, 0, sizeof(STARTUPINFOW) );
7290     ret = CreateProcessW( NULL, cmd, NULL, NULL, FALSE, 0, NULL, NULL, &si, &info );
7291     msi_free( cmd );
7292     if (!ret) return GetLastError();
7293     CloseHandle( info.hThread );
7294
7295     WaitForSingleObject( info.hProcess, INFINITE );
7296     CloseHandle( info.hProcess );
7297     return ERROR_SUCCESS;
7298 }
7299
7300 static UINT ACTION_RemoveExistingProducts( MSIPACKAGE *package )
7301 {
7302     static const WCHAR query[] = {
7303         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','U','p','g','r','a','d','e',0};
7304     MSIQUERY *view;
7305     UINT r;
7306
7307     r = MSI_DatabaseOpenViewW( package->db, query, &view );
7308     if (r == ERROR_SUCCESS)
7309     {
7310         r = MSI_IterateRecords( view, NULL, ITERATE_RemoveExistingProducts, package );
7311         msiobj_release( &view->hdr );
7312         if (r != ERROR_SUCCESS)
7313             return r;
7314     }
7315     return ERROR_SUCCESS;
7316 }
7317
7318 static UINT ITERATE_MigrateFeatureStates( MSIRECORD *rec, LPVOID param )
7319 {
7320     MSIPACKAGE *package = param;
7321     int attributes = MSI_RecordGetInteger( rec, 5 );
7322
7323     if (attributes & msidbUpgradeAttributesMigrateFeatures)
7324     {
7325         const WCHAR *upgrade_code = MSI_RecordGetString( rec, 1 );
7326         const WCHAR *version_min = MSI_RecordGetString( rec, 2 );
7327         const WCHAR *version_max = MSI_RecordGetString( rec, 3 );
7328         const WCHAR *language = MSI_RecordGetString( rec, 4 );
7329         HKEY hkey;
7330         UINT r;
7331
7332         if (package->Context == MSIINSTALLCONTEXT_MACHINE)
7333         {
7334             r = MSIREG_OpenClassesUpgradeCodesKey( upgrade_code, &hkey, FALSE );
7335             if (r != ERROR_SUCCESS)
7336                 return ERROR_SUCCESS;
7337         }
7338         else
7339         {
7340             r = MSIREG_OpenUserUpgradeCodesKey( upgrade_code, &hkey, FALSE );
7341             if (r != ERROR_SUCCESS)
7342                 return ERROR_SUCCESS;
7343         }
7344         RegCloseKey( hkey );
7345
7346         FIXME("migrate feature states from %s version min %s version max %s language %s\n",
7347               debugstr_w(upgrade_code), debugstr_w(version_min),
7348               debugstr_w(version_max), debugstr_w(language));
7349     }
7350     return ERROR_SUCCESS;
7351 }
7352
7353 static UINT ACTION_MigrateFeatureStates( MSIPACKAGE *package )
7354 {
7355     static const WCHAR query[] = {
7356         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7357         'U','p','g','r','a','d','e',0};
7358     MSIQUERY *view;
7359     UINT r;
7360
7361     if (msi_get_property_int( package->db, szInstalled, 0 ))
7362     {
7363         TRACE("product is installed, skipping action\n");
7364         return ERROR_SUCCESS;
7365     }
7366     if (msi_get_property_int( package->db, szPreselected, 0 ))
7367     {
7368         TRACE("Preselected property is set, not migrating feature states\n");
7369         return ERROR_SUCCESS;
7370     }
7371     r = MSI_DatabaseOpenViewW( package->db, query, &view );
7372     if (r == ERROR_SUCCESS)
7373     {
7374         r = MSI_IterateRecords( view, NULL, ITERATE_MigrateFeatureStates, package );
7375         msiobj_release( &view->hdr );
7376         if (r != ERROR_SUCCESS)
7377             return r;
7378     }
7379     return ERROR_SUCCESS;
7380 }
7381
7382 static void bind_image( const char *filename, const char *path )
7383 {
7384     if (!BindImageEx( 0, filename, path, NULL, NULL ))
7385     {
7386         WARN("failed to bind image %u\n", GetLastError());
7387     }
7388 }
7389
7390 static UINT ITERATE_BindImage( MSIRECORD *rec, LPVOID param )
7391 {
7392     UINT i;
7393     MSIFILE *file;
7394     MSIPACKAGE *package = param;
7395     const WCHAR *key = MSI_RecordGetString( rec, 1 );
7396     const WCHAR *paths = MSI_RecordGetString( rec, 2 );
7397     char *filenameA, *pathA;
7398     WCHAR *pathW, **path_list;
7399
7400     if (!(file = msi_get_loaded_file( package, key )))
7401     {
7402         WARN("file %s not found\n", debugstr_w(key));
7403         return ERROR_SUCCESS;
7404     }
7405     if (!(filenameA = strdupWtoA( file->TargetPath ))) return ERROR_SUCCESS;
7406     path_list = msi_split_string( paths, ';' );
7407     if (!path_list) bind_image( filenameA, NULL );
7408     else
7409     {
7410         for (i = 0; path_list[i] && path_list[i][0]; i++)
7411         {
7412             deformat_string( package, path_list[i], &pathW );
7413             if ((pathA = strdupWtoA( pathW )))
7414             {
7415                 bind_image( filenameA, pathA );
7416                 msi_free( pathA );
7417             }
7418             msi_free( pathW );
7419         }
7420     }
7421     msi_free( path_list );
7422     msi_free( filenameA );
7423     return ERROR_SUCCESS;
7424 }
7425
7426 static UINT ACTION_BindImage( MSIPACKAGE *package )
7427 {
7428     static const WCHAR query[] = {
7429         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7430         'B','i','n','d','I','m','a','g','e',0};
7431     MSIQUERY *view;
7432     UINT r;
7433
7434     r = MSI_DatabaseOpenViewW( package->db, query, &view );
7435     if (r == ERROR_SUCCESS)
7436     {
7437         r = MSI_IterateRecords( view, NULL, ITERATE_BindImage, package );
7438         msiobj_release( &view->hdr );
7439         if (r != ERROR_SUCCESS)
7440             return r;
7441     }
7442     return ERROR_SUCCESS;
7443 }
7444
7445 static UINT msi_unimplemented_action_stub( MSIPACKAGE *package, LPCSTR action, LPCWSTR table )
7446 {
7447     static const WCHAR query[] = {
7448         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','%','s','`',0};
7449     MSIQUERY *view;
7450     DWORD count = 0;
7451     UINT r;
7452     
7453     r = MSI_OpenQuery( package->db, &view, query, table );
7454     if (r == ERROR_SUCCESS)
7455     {
7456         r = MSI_IterateRecords(view, &count, NULL, package);
7457         msiobj_release(&view->hdr);
7458         if (r != ERROR_SUCCESS)
7459             return r;
7460     }
7461     if (count) FIXME("%s: ignored %u rows from %s\n", action, count, debugstr_w(table));
7462     return ERROR_SUCCESS;
7463 }
7464
7465 static UINT ACTION_IsolateComponents( MSIPACKAGE *package )
7466 {
7467     static const WCHAR table[] = {
7468         'I','s','o','l','a','t','e','d','C','o','m','p','o','n','e','n','t',0 };
7469     return msi_unimplemented_action_stub( package, "IsolateComponents", table );
7470 }
7471
7472 static UINT ACTION_RMCCPSearch( MSIPACKAGE *package )
7473 {
7474     static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
7475     return msi_unimplemented_action_stub( package, "RMCCPSearch", table );
7476 }
7477
7478 static UINT ACTION_RegisterComPlus( MSIPACKAGE *package )
7479 {
7480     static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
7481     return msi_unimplemented_action_stub( package, "RegisterComPlus", table );
7482 }
7483
7484 static UINT ACTION_UnregisterComPlus( MSIPACKAGE *package )
7485 {
7486     static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
7487     return msi_unimplemented_action_stub( package, "UnregisterComPlus", table );
7488 }
7489
7490 static UINT ACTION_InstallSFPCatalogFile( MSIPACKAGE *package )
7491 {
7492     static const WCHAR table[] = { 'S','F','P','C','a','t','a','l','o','g',0 };
7493     return msi_unimplemented_action_stub( package, "InstallSFPCatalogFile", table );
7494 }
7495
7496 static const struct
7497 {
7498     const WCHAR *action;
7499     UINT (*handler)(MSIPACKAGE *);
7500     const WCHAR *action_rollback;
7501 }
7502 StandardActions[] =
7503 {
7504     { szAllocateRegistrySpace, ACTION_AllocateRegistrySpace, NULL },
7505     { szAppSearch, ACTION_AppSearch, NULL },
7506     { szBindImage, ACTION_BindImage, NULL },
7507     { szCCPSearch, ACTION_CCPSearch, NULL },
7508     { szCostFinalize, ACTION_CostFinalize, NULL },
7509     { szCostInitialize, ACTION_CostInitialize, NULL },
7510     { szCreateFolders, ACTION_CreateFolders, szRemoveFolders },
7511     { szCreateShortcuts, ACTION_CreateShortcuts, szRemoveShortcuts },
7512     { szDeleteServices, ACTION_DeleteServices, szInstallServices },
7513     { szDisableRollback, ACTION_DisableRollback, NULL },
7514     { szDuplicateFiles, ACTION_DuplicateFiles, szRemoveDuplicateFiles },
7515     { szExecuteAction, ACTION_ExecuteAction, NULL },
7516     { szFileCost, ACTION_FileCost, NULL },
7517     { szFindRelatedProducts, ACTION_FindRelatedProducts, NULL },
7518     { szForceReboot, ACTION_ForceReboot, NULL },
7519     { szInstallAdminPackage, ACTION_InstallAdminPackage, NULL },
7520     { szInstallExecute, ACTION_InstallExecute, NULL },
7521     { szInstallExecuteAgain, ACTION_InstallExecute, NULL },
7522     { szInstallFiles, ACTION_InstallFiles, szRemoveFiles },
7523     { szInstallFinalize, ACTION_InstallFinalize, NULL },
7524     { szInstallInitialize, ACTION_InstallInitialize, NULL },
7525     { szInstallODBC, ACTION_InstallODBC, szRemoveODBC },
7526     { szInstallServices, ACTION_InstallServices, szDeleteServices },
7527     { szInstallSFPCatalogFile, ACTION_InstallSFPCatalogFile, NULL },
7528     { szInstallValidate, ACTION_InstallValidate, NULL },
7529     { szIsolateComponents, ACTION_IsolateComponents, NULL },
7530     { szLaunchConditions, ACTION_LaunchConditions, NULL },
7531     { szMigrateFeatureStates, ACTION_MigrateFeatureStates, NULL },
7532     { szMoveFiles, ACTION_MoveFiles, NULL },
7533     { szMsiPublishAssemblies, ACTION_MsiPublishAssemblies, szMsiUnpublishAssemblies },
7534     { szMsiUnpublishAssemblies, ACTION_MsiUnpublishAssemblies, szMsiPublishAssemblies },
7535     { szPatchFiles, ACTION_PatchFiles, NULL },
7536     { szProcessComponents, ACTION_ProcessComponents, szProcessComponents },
7537     { szPublishComponents, ACTION_PublishComponents, szUnpublishComponents },
7538     { szPublishFeatures, ACTION_PublishFeatures, szUnpublishFeatures },
7539     { szPublishProduct, ACTION_PublishProduct, NULL },
7540     { szRegisterClassInfo, ACTION_RegisterClassInfo, szUnregisterClassInfo },
7541     { szRegisterComPlus, ACTION_RegisterComPlus, szUnregisterComPlus },
7542     { szRegisterExtensionInfo, ACTION_RegisterExtensionInfo, szUnregisterExtensionInfo },
7543     { szRegisterFonts, ACTION_RegisterFonts, szUnregisterFonts },
7544     { szRegisterMIMEInfo, ACTION_RegisterMIMEInfo, szUnregisterMIMEInfo },
7545     { szRegisterProduct, ACTION_RegisterProduct, NULL },
7546     { szRegisterProgIdInfo, ACTION_RegisterProgIdInfo, szUnregisterProgIdInfo },
7547     { szRegisterTypeLibraries, ACTION_RegisterTypeLibraries, szUnregisterTypeLibraries },
7548     { szRegisterUser, ACTION_RegisterUser, NULL },
7549     { szRemoveDuplicateFiles, ACTION_RemoveDuplicateFiles, szDuplicateFiles },
7550     { szRemoveEnvironmentStrings, ACTION_RemoveEnvironmentStrings, szWriteEnvironmentStrings },
7551     { szRemoveExistingProducts, ACTION_RemoveExistingProducts, NULL },
7552     { szRemoveFiles, ACTION_RemoveFiles, szInstallFiles },
7553     { szRemoveFolders, ACTION_RemoveFolders, szCreateFolders },
7554     { szRemoveIniValues, ACTION_RemoveIniValues, szWriteIniValues },
7555     { szRemoveODBC, ACTION_RemoveODBC, szInstallODBC },
7556     { szRemoveRegistryValues, ACTION_RemoveRegistryValues, szWriteRegistryValues },
7557     { szRemoveShortcuts, ACTION_RemoveShortcuts, szCreateShortcuts },
7558     { szResolveSource, ACTION_ResolveSource, NULL },
7559     { szRMCCPSearch, ACTION_RMCCPSearch, NULL },
7560     { szScheduleReboot, ACTION_ScheduleReboot, NULL },
7561     { szSelfRegModules, ACTION_SelfRegModules, szSelfUnregModules },
7562     { szSelfUnregModules, ACTION_SelfUnregModules, szSelfRegModules },
7563     { szSetODBCFolders, ACTION_SetODBCFolders, NULL },
7564     { szStartServices, ACTION_StartServices, szStopServices },
7565     { szStopServices, ACTION_StopServices, szStartServices },
7566     { szUnpublishComponents, ACTION_UnpublishComponents, szPublishComponents },
7567     { szUnpublishFeatures, ACTION_UnpublishFeatures, szPublishFeatures },
7568     { szUnregisterClassInfo, ACTION_UnregisterClassInfo, szRegisterClassInfo },
7569     { szUnregisterComPlus, ACTION_UnregisterComPlus, szRegisterComPlus },
7570     { szUnregisterExtensionInfo, ACTION_UnregisterExtensionInfo, szRegisterExtensionInfo },
7571     { szUnregisterFonts, ACTION_UnregisterFonts, szRegisterFonts },
7572     { szUnregisterMIMEInfo, ACTION_UnregisterMIMEInfo, szRegisterMIMEInfo },
7573     { szUnregisterProgIdInfo, ACTION_UnregisterProgIdInfo, szRegisterProgIdInfo },
7574     { szUnregisterTypeLibraries, ACTION_UnregisterTypeLibraries, szRegisterTypeLibraries },
7575     { szValidateProductID, ACTION_ValidateProductID, NULL },
7576     { szWriteEnvironmentStrings, ACTION_WriteEnvironmentStrings, szRemoveEnvironmentStrings },
7577     { szWriteIniValues, ACTION_WriteIniValues, szRemoveIniValues },
7578     { szWriteRegistryValues, ACTION_WriteRegistryValues, szRemoveRegistryValues },
7579     { NULL, NULL, NULL }
7580 };
7581
7582 static BOOL ACTION_HandleStandardAction( MSIPACKAGE *package, LPCWSTR action, UINT *rc )
7583 {
7584     BOOL ret = FALSE;
7585     UINT i;
7586
7587     i = 0;
7588     while (StandardActions[i].action != NULL)
7589     {
7590         if (!strcmpW( StandardActions[i].action, action ))
7591         {
7592             ui_actionstart( package, action );
7593             if (StandardActions[i].handler)
7594             {
7595                 ui_actioninfo( package, action, TRUE, 0 );
7596                 *rc = StandardActions[i].handler( package );
7597                 ui_actioninfo( package, action, FALSE, *rc );
7598
7599                 if (StandardActions[i].action_rollback && !package->need_rollback)
7600                 {
7601                     TRACE("scheduling rollback action\n");
7602                     msi_schedule_action( package, SCRIPT_ROLLBACK, StandardActions[i].action_rollback );
7603                 }
7604             }
7605             else
7606             {
7607                 FIXME("unhandled standard action %s\n", debugstr_w(action));
7608                 *rc = ERROR_SUCCESS;
7609             }
7610             ret = TRUE;
7611             break;
7612         }
7613         i++;
7614     }
7615     return ret;
7616 }
7617
7618 UINT ACTION_PerformAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
7619 {
7620     UINT rc = ERROR_SUCCESS;
7621     BOOL handled;
7622
7623     TRACE("Performing action (%s)\n", debugstr_w(action));
7624
7625     handled = ACTION_HandleStandardAction(package, action, &rc);
7626
7627     if (!handled)
7628         handled = ACTION_HandleCustomAction(package, action, &rc, script, TRUE);
7629
7630     if (!handled)
7631     {
7632         WARN("unhandled msi action %s\n", debugstr_w(action));
7633         rc = ERROR_FUNCTION_NOT_CALLED;
7634     }
7635
7636     return rc;
7637 }
7638
7639 UINT ACTION_PerformUIAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
7640 {
7641     UINT rc = ERROR_SUCCESS;
7642     BOOL handled = FALSE;
7643
7644     TRACE("Performing action (%s)\n", debugstr_w(action));
7645
7646     package->action_progress_increment = 0;
7647     handled = ACTION_HandleStandardAction(package, action, &rc);
7648
7649     if (!handled)
7650         handled = ACTION_HandleCustomAction(package, action, &rc, script, FALSE);
7651
7652     if( !handled && ACTION_DialogBox(package, action) == ERROR_SUCCESS )
7653         handled = TRUE;
7654
7655     if (!handled)
7656     {
7657         WARN("unhandled msi action %s\n", debugstr_w(action));
7658         rc = ERROR_FUNCTION_NOT_CALLED;
7659     }
7660
7661     return rc;
7662 }
7663
7664 static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq)
7665 {
7666     UINT rc = ERROR_SUCCESS;
7667     MSIRECORD *row;
7668
7669     static const WCHAR query[] =
7670         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7671          '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
7672          'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
7673          '`','S','e','q','u','e','n','c','e','`',' ', '=',' ','%','i',0};
7674     static const WCHAR ui_query[] =
7675         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7676      '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e',
7677      '`', ' ', 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',
7678          ' ', '=',' ','%','i',0};
7679
7680     if (needs_ui_sequence(package))
7681         row = MSI_QueryGetRecord(package->db, ui_query, seq);
7682     else
7683         row = MSI_QueryGetRecord(package->db, query, seq);
7684
7685     if (row)
7686     {
7687         LPCWSTR action, cond;
7688
7689         TRACE("Running the actions\n");
7690
7691         /* check conditions */
7692         cond = MSI_RecordGetString(row, 2);
7693
7694         /* this is a hack to skip errors in the condition code */
7695         if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
7696         {
7697             msiobj_release(&row->hdr);
7698             return ERROR_SUCCESS;
7699         }
7700
7701         action = MSI_RecordGetString(row, 1);
7702         if (!action)
7703         {
7704             ERR("failed to fetch action\n");
7705             msiobj_release(&row->hdr);
7706             return ERROR_FUNCTION_FAILED;
7707         }
7708
7709         if (needs_ui_sequence(package))
7710             rc = ACTION_PerformUIAction(package, action, SCRIPT_NONE);
7711         else
7712             rc = ACTION_PerformAction(package, action, SCRIPT_NONE);
7713
7714         msiobj_release(&row->hdr);
7715     }
7716
7717     return rc;
7718 }
7719
7720 /****************************************************
7721  * TOP level entry points
7722  *****************************************************/
7723
7724 UINT MSI_InstallPackage( MSIPACKAGE *package, LPCWSTR szPackagePath,
7725                          LPCWSTR szCommandLine )
7726 {
7727     static const WCHAR szDisableRollback[] = {'D','I','S','A','B','L','E','R','O','L','L','B','A','C','K',0};
7728     static const WCHAR szAction[] = {'A','C','T','I','O','N',0};
7729     static const WCHAR szInstall[] = {'I','N','S','T','A','L','L',0};
7730     WCHAR *reinstall, *remove, *patch;
7731     BOOL ui_exists;
7732     UINT rc;
7733
7734     msi_set_property( package->db, szAction, szInstall, -1 );
7735
7736     package->script->InWhatSequence = SEQUENCE_INSTALL;
7737
7738     if (szPackagePath)
7739     {
7740         LPWSTR p, dir;
7741         LPCWSTR file;
7742
7743         dir = strdupW(szPackagePath);
7744         p = strrchrW(dir, '\\');
7745         if (p)
7746         {
7747             *(++p) = 0;
7748             file = szPackagePath + (p - dir);
7749         }
7750         else
7751         {
7752             msi_free(dir);
7753             dir = msi_alloc(MAX_PATH * sizeof(WCHAR));
7754             GetCurrentDirectoryW(MAX_PATH, dir);
7755             lstrcatW(dir, szBackSlash);
7756             file = szPackagePath;
7757         }
7758
7759         msi_free( package->PackagePath );
7760         package->PackagePath = msi_alloc((lstrlenW(dir) + lstrlenW(file) + 1) * sizeof(WCHAR));
7761         if (!package->PackagePath)
7762         {
7763             msi_free(dir);
7764             return ERROR_OUTOFMEMORY;
7765         }
7766
7767         lstrcpyW(package->PackagePath, dir);
7768         lstrcatW(package->PackagePath, file);
7769         msi_free(dir);
7770
7771         msi_set_sourcedir_props(package, FALSE);
7772     }
7773
7774     rc = msi_parse_command_line( package, szCommandLine, FALSE );
7775     if (rc != ERROR_SUCCESS)
7776         return rc;
7777
7778     msi_apply_transforms( package );
7779     msi_apply_patches( package );
7780
7781     patch = msi_dup_property( package->db, szPatch );
7782     remove = msi_dup_property( package->db, szRemove );
7783     reinstall = msi_dup_property( package->db, szReinstall );
7784     if (msi_get_property_int( package->db, szInstalled, 0 ) && !remove && !reinstall && !patch)
7785     {
7786         TRACE("setting REINSTALL property to ALL\n");
7787         msi_set_property( package->db, szReinstall, szAll, -1 );
7788         package->full_reinstall = 1;
7789     }
7790
7791     /* properties may have been added by a transform */
7792     msi_clone_properties( package );
7793
7794     msi_parse_command_line( package, szCommandLine, FALSE );
7795     msi_adjust_privilege_properties( package );
7796     msi_set_context( package );
7797
7798     if (msi_get_property_int( package->db, szDisableRollback, 0 ))
7799     {
7800         TRACE("disabling rollback\n");
7801         msi_set_property( package->db, szRollbackDisabled, szOne, -1 );
7802     }
7803
7804     if (needs_ui_sequence( package))
7805     {
7806         package->script->InWhatSequence |= SEQUENCE_UI;
7807         rc = ACTION_ProcessUISequence(package);
7808         ui_exists = ui_sequence_exists(package);
7809         if (rc == ERROR_SUCCESS || !ui_exists)
7810         {
7811             package->script->InWhatSequence |= SEQUENCE_EXEC;
7812             rc = ACTION_ProcessExecSequence(package, ui_exists);
7813         }
7814     }
7815     else
7816         rc = ACTION_ProcessExecSequence(package, FALSE);
7817
7818     package->script->CurrentlyScripting = FALSE;
7819
7820     /* process the ending type action */
7821     if (rc == ERROR_SUCCESS)
7822         ACTION_PerformActionSequence(package, -1);
7823     else if (rc == ERROR_INSTALL_USEREXIT)
7824         ACTION_PerformActionSequence(package, -2);
7825     else if (rc == ERROR_INSTALL_SUSPEND)
7826         ACTION_PerformActionSequence(package, -4);
7827     else  /* failed */
7828     {
7829         ACTION_PerformActionSequence(package, -3);
7830         if (!msi_get_property_int( package->db, szRollbackDisabled, 0 ))
7831         {
7832             package->need_rollback = TRUE;
7833         }
7834     }
7835
7836     /* finish up running custom actions */
7837     ACTION_FinishCustomActions(package);
7838
7839     if (package->need_rollback && !reinstall)
7840     {
7841         WARN("installation failed, running rollback script\n");
7842         execute_script( package, SCRIPT_ROLLBACK );
7843     }
7844     msi_free( reinstall );
7845     msi_free( remove );
7846     msi_free( patch );
7847
7848     if (rc == ERROR_SUCCESS && package->need_reboot_at_end)
7849         return ERROR_SUCCESS_REBOOT_REQUIRED;
7850
7851     return rc;
7852 }