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