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