winex11: Get the current pixel format from the drawable structure in wglCreateContext.
[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[0] && 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 static WCHAR *get_install_location( MSIPACKAGE *package )
2285 {
2286     HKEY hkey;
2287     WCHAR *path;
2288
2289     if (!package->ProductCode) return NULL;
2290     if (MSIREG_OpenInstallProps( package->ProductCode, package->Context, NULL, &hkey, FALSE ))
2291         return NULL;
2292     path = msi_reg_get_val_str( hkey, szInstallLocation );
2293     RegCloseKey( hkey );
2294     return path;
2295 }
2296
2297 void msi_resolve_target_folder( MSIPACKAGE *package, const WCHAR *name, BOOL load_prop )
2298 {
2299     FolderList *fl;
2300     MSIFOLDER *folder, *parent, *child;
2301     WCHAR *path, *normalized_path;
2302
2303     TRACE("resolving %s\n", debugstr_w(name));
2304
2305     if (!(folder = msi_get_loaded_folder( package, name ))) return;
2306
2307     if (!strcmpW( folder->Directory, szTargetDir )) /* special resolving for target root dir */
2308     {
2309         if (!(path = get_install_location( package )) &&
2310             (!load_prop || !(path = msi_dup_property( package->db, szTargetDir ))))
2311         {
2312             path = msi_dup_property( package->db, szRootDrive );
2313         }
2314     }
2315     else if (!load_prop || !(path = msi_dup_property( package->db, folder->Directory )))
2316     {
2317         if (folder->Parent && strcmpW( folder->Directory, folder->Parent ))
2318         {
2319             parent = msi_get_loaded_folder( package, folder->Parent );
2320             path = msi_build_directory_name( 3, parent->ResolvedTarget, folder->TargetDefault, NULL );
2321         }
2322         else
2323             path = msi_build_directory_name( 2, folder->TargetDefault, NULL );
2324     }
2325     normalized_path = msi_normalize_path( path );
2326     msi_free( path );
2327     if (folder->ResolvedTarget && !strcmpiW( normalized_path, folder->ResolvedTarget ))
2328     {
2329         TRACE("%s already resolved to %s\n", debugstr_w(name), debugstr_w(folder->ResolvedTarget));
2330         msi_free( normalized_path );
2331         return;
2332     }
2333     msi_set_property( package->db, folder->Directory, normalized_path );
2334     msi_free( folder->ResolvedTarget );
2335     folder->ResolvedTarget = normalized_path;
2336
2337     LIST_FOR_EACH_ENTRY( fl, &folder->children, FolderList, entry )
2338     {
2339         child = fl->folder;
2340         msi_resolve_target_folder( package, child->Directory, load_prop );
2341     }
2342     TRACE("%s resolves to %s\n", debugstr_w(name), debugstr_w(folder->ResolvedTarget));
2343 }
2344
2345 static UINT ACTION_CostFinalize(MSIPACKAGE *package)
2346 {
2347     static const WCHAR query[] = {
2348         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2349         '`','C','o','n','d','i','t','i','o','n','`',0};
2350     static const WCHAR szOutOfDiskSpace[] = {
2351         'O','u','t','O','f','D','i','s','k','S','p','a','c','e',0};
2352     MSICOMPONENT *comp;
2353     MSIQUERY *view;
2354     LPWSTR level;
2355     UINT rc;
2356
2357     TRACE("Building directory properties\n");
2358     msi_resolve_target_folder( package, szTargetDir, TRUE );
2359
2360     TRACE("Evaluating component conditions\n");
2361     LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2362     {
2363         if (MSI_EvaluateConditionW( package, comp->Condition ) == MSICONDITION_FALSE)
2364         {
2365             TRACE("Disabling component %s\n", debugstr_w(comp->Component));
2366             comp->Enabled = FALSE;
2367         }
2368         else
2369             comp->Enabled = TRUE;
2370     }
2371
2372     /* read components states from the registry */
2373     ACTION_GetComponentInstallStates(package);
2374     ACTION_GetFeatureInstallStates(package);
2375
2376     if (!process_overrides( package, msi_get_property_int( package->db, szInstallLevel, 1 ) ))
2377     {
2378         TRACE("Evaluating feature conditions\n");
2379
2380         rc = MSI_DatabaseOpenViewW( package->db, query, &view );
2381         if (rc == ERROR_SUCCESS)
2382         {
2383             rc = MSI_IterateRecords( view, NULL, ITERATE_CostFinalizeConditions, package );
2384             msiobj_release( &view->hdr );
2385             if (rc != ERROR_SUCCESS)
2386                 return rc;
2387         }
2388     }
2389
2390     TRACE("Calculating file cost\n");
2391     calculate_file_cost( package );
2392
2393     msi_set_property( package->db, szCostingComplete, szOne );
2394     /* set default run level if not set */
2395     level = msi_dup_property( package->db, szInstallLevel );
2396     if (!level)
2397         msi_set_property( package->db, szInstallLevel, szOne );
2398     msi_free(level);
2399
2400     /* FIXME: check volume disk space */
2401     msi_set_property( package->db, szOutOfDiskSpace, szZero );
2402
2403     return MSI_SetFeatureStates(package);
2404 }
2405
2406 static LPSTR parse_value(MSIPACKAGE *package, LPCWSTR value, DWORD *type, DWORD *size)
2407 {
2408     LPSTR data = NULL;
2409
2410     if (!value)
2411     {
2412         data = (LPSTR)strdupW(szEmpty);
2413         *size = sizeof(szEmpty);
2414         *type = REG_SZ;
2415         return data;
2416     }
2417     if (value[0]=='#' && value[1]!='#' && value[1]!='%')
2418     {
2419         if (value[1]=='x')
2420         {
2421             LPWSTR ptr;
2422             CHAR byte[5];
2423             LPWSTR deformated = NULL;
2424             int count;
2425
2426             deformat_string(package, &value[2], &deformated);
2427
2428             /* binary value type */
2429             ptr = deformated;
2430             *type = REG_BINARY;
2431             if (strlenW(ptr)%2)
2432                 *size = (strlenW(ptr)/2)+1;
2433             else
2434                 *size = strlenW(ptr)/2;
2435
2436             data = msi_alloc(*size);
2437
2438             byte[0] = '0'; 
2439             byte[1] = 'x'; 
2440             byte[4] = 0; 
2441             count = 0;
2442             /* if uneven pad with a zero in front */
2443             if (strlenW(ptr)%2)
2444             {
2445                 byte[2]= '0';
2446                 byte[3]= *ptr;
2447                 ptr++;
2448                 data[count] = (BYTE)strtol(byte,NULL,0);
2449                 count ++;
2450                 TRACE("Uneven byte count\n");
2451             }
2452             while (*ptr)
2453             {
2454                 byte[2]= *ptr;
2455                 ptr++;
2456                 byte[3]= *ptr;
2457                 ptr++;
2458                 data[count] = (BYTE)strtol(byte,NULL,0);
2459                 count ++;
2460             }
2461             msi_free(deformated);
2462
2463             TRACE("Data %i bytes(%i)\n",*size,count);
2464         }
2465         else
2466         {
2467             LPWSTR deformated;
2468             LPWSTR p;
2469             DWORD d = 0;
2470             deformat_string(package, &value[1], &deformated);
2471
2472             *type=REG_DWORD; 
2473             *size = sizeof(DWORD);
2474             data = msi_alloc(*size);
2475             p = deformated;
2476             if (*p == '-')
2477                 p++;
2478             while (*p)
2479             {
2480                 if ( (*p < '0') || (*p > '9') )
2481                     break;
2482                 d *= 10;
2483                 d += (*p - '0');
2484                 p++;
2485             }
2486             if (deformated[0] == '-')
2487                 d = -d;
2488             *(LPDWORD)data = d;
2489             TRACE("DWORD %i\n",*(LPDWORD)data);
2490
2491             msi_free(deformated);
2492         }
2493     }
2494     else
2495     {
2496         static const WCHAR szMulti[] = {'[','~',']',0};
2497         LPCWSTR ptr;
2498         *type=REG_SZ;
2499
2500         if (value[0]=='#')
2501         {
2502             if (value[1]=='%')
2503             {
2504                 ptr = &value[2];
2505                 *type=REG_EXPAND_SZ;
2506             }
2507             else
2508                 ptr = &value[1];
2509          }
2510          else
2511             ptr=value;
2512
2513         if (strstrW(value, szMulti))
2514             *type = REG_MULTI_SZ;
2515
2516         /* remove initial delimiter */
2517         if (!strncmpW(value, szMulti, 3))
2518             ptr = value + 3;
2519
2520         *size = deformat_string(package, ptr,(LPWSTR*)&data);
2521
2522         /* add double NULL terminator */
2523         if (*type == REG_MULTI_SZ)
2524         {
2525             *size += 2 * sizeof(WCHAR); /* two NULL terminators */
2526             data = msi_realloc_zero(data, *size);
2527         }
2528     }
2529     return data;
2530 }
2531
2532 static const WCHAR *get_root_key( MSIPACKAGE *package, INT root, HKEY *root_key )
2533 {
2534     const WCHAR *ret;
2535
2536     switch (root)
2537     {
2538     case -1:
2539         if (msi_get_property_int( package->db, szAllUsers, 0 ))
2540         {
2541             *root_key = HKEY_LOCAL_MACHINE;
2542             ret = szHLM;
2543         }
2544         else
2545         {
2546             *root_key = HKEY_CURRENT_USER;
2547             ret = szHCU;
2548         }
2549         break;
2550     case 0:
2551         *root_key = HKEY_CLASSES_ROOT;
2552         ret = szHCR;
2553         break;
2554     case 1:
2555         *root_key = HKEY_CURRENT_USER;
2556         ret = szHCU;
2557         break;
2558     case 2:
2559         *root_key = HKEY_LOCAL_MACHINE;
2560         ret = szHLM;
2561         break;
2562     case 3:
2563         *root_key = HKEY_USERS;
2564         ret = szHU;
2565         break;
2566     default:
2567         ERR("Unknown root %i\n", root);
2568         return NULL;
2569     }
2570
2571     return ret;
2572 }
2573
2574 static WCHAR *get_keypath( MSICOMPONENT *comp, HKEY root, const WCHAR *path )
2575 {
2576     static const WCHAR prefixW[] = {'S','O','F','T','W','A','R','E','\\'};
2577     static const UINT len = sizeof(prefixW) / sizeof(prefixW[0]);
2578
2579     if ((is_64bit || is_wow64) &&
2580         !(comp->Attributes & msidbComponentAttributes64bit) &&
2581         root == HKEY_LOCAL_MACHINE && !strncmpiW( path, prefixW, len ))
2582     {
2583         UINT size;
2584         WCHAR *path_32node;
2585
2586         size = (strlenW( path ) + strlenW( szWow6432Node ) + 2) * sizeof(WCHAR);
2587         if (!(path_32node = msi_alloc( size ))) return NULL;
2588
2589         memcpy( path_32node, path, len * sizeof(WCHAR) );
2590         strcpyW( path_32node + len, szWow6432Node );
2591         strcatW( path_32node, szBackSlash );
2592         strcatW( path_32node, path + len );
2593         return path_32node;
2594     }
2595     return strdupW( path );
2596 }
2597
2598 static HKEY open_key( HKEY root, const WCHAR *path, BOOL create )
2599 {
2600     REGSAM access = KEY_ALL_ACCESS;
2601     WCHAR *subkey, *p, *q;
2602     HKEY hkey, ret = NULL;
2603     LONG res;
2604
2605     if (is_wow64) access |= KEY_WOW64_64KEY;
2606
2607     if (!(subkey = strdupW( path ))) return NULL;
2608     p = subkey;
2609     if ((q = strchrW( p, '\\' ))) *q = 0;
2610     if (create)
2611         res = RegCreateKeyExW( root, subkey, 0, NULL, 0, access, NULL, &hkey, NULL );
2612     else
2613         res = RegOpenKeyExW( root, subkey, 0, access, &hkey );
2614     if (res)
2615     {
2616         TRACE("failed to open key %s (%d)\n", debugstr_w(subkey), res);
2617         msi_free( subkey );
2618         return NULL;
2619     }
2620     if (q && q[1])
2621     {
2622         ret = open_key( hkey, q + 1, create );
2623         RegCloseKey( hkey );
2624     }
2625     else ret = hkey;
2626     msi_free( subkey );
2627     return ret;
2628 }
2629
2630 static BOOL is_special_entry( const WCHAR *name )
2631 {
2632      return (name && (name[0] == '*' || name[0] == '+') && !name[1]);
2633 }
2634
2635 static UINT ITERATE_WriteRegistryValues(MSIRECORD *row, LPVOID param)
2636 {
2637     MSIPACKAGE *package = param;
2638     LPSTR value;
2639     HKEY  root_key, hkey;
2640     DWORD type,size;
2641     LPWSTR deformated, uikey, keypath;
2642     LPCWSTR szRoot, component, name, key;
2643     MSICOMPONENT *comp;
2644     MSIRECORD * uirow;
2645     INT   root;
2646     BOOL check_first = FALSE;
2647     UINT rc;
2648
2649     msi_ui_progress( package, 2, REG_PROGRESS_VALUE, 0, 0 );
2650
2651     component = MSI_RecordGetString(row, 6);
2652     comp = msi_get_loaded_component(package,component);
2653     if (!comp)
2654         return ERROR_SUCCESS;
2655
2656     comp->Action = msi_get_component_action( package, comp );
2657     if (comp->Action != INSTALLSTATE_LOCAL)
2658     {
2659         TRACE("component not scheduled for installation %s\n", debugstr_w(component));
2660         return ERROR_SUCCESS;
2661     }
2662
2663     name = MSI_RecordGetString(row, 4);
2664     if( MSI_RecordIsNull(row,5) && name )
2665     {
2666         /* null values can have special meanings */
2667         if (name[0]=='-' && name[1] == 0)
2668                 return ERROR_SUCCESS;
2669         if ((name[0] == '+' || name[0] == '*') && !name[1])
2670             check_first = TRUE;
2671     }
2672
2673     root = MSI_RecordGetInteger(row,2);
2674     key = MSI_RecordGetString(row, 3);
2675
2676     szRoot = get_root_key( package, root, &root_key );
2677     if (!szRoot)
2678         return ERROR_SUCCESS;
2679
2680     deformat_string(package, key , &deformated);
2681     size = strlenW(deformated) + strlenW(szRoot) + 1;
2682     uikey = msi_alloc(size*sizeof(WCHAR));
2683     strcpyW(uikey,szRoot);
2684     strcatW(uikey,deformated);
2685
2686     keypath = get_keypath( comp, root_key, deformated );
2687     msi_free( deformated );
2688     if (!(hkey = open_key( root_key, keypath, TRUE )))
2689     {
2690         ERR("Could not create key %s\n", debugstr_w(keypath));
2691         msi_free(uikey);
2692         msi_free(keypath);
2693         return ERROR_FUNCTION_FAILED;
2694     }
2695     value = parse_value(package, MSI_RecordGetString(row, 5), &type, &size);
2696     deformat_string(package, name, &deformated);
2697
2698     if (!is_special_entry( name ))
2699     {
2700         if (!check_first)
2701         {
2702             TRACE("Setting value %s of %s\n", debugstr_w(deformated),
2703                   debugstr_w(uikey));
2704             RegSetValueExW(hkey, deformated, 0, type, (LPBYTE)value, size);
2705         }
2706         else
2707         {
2708             DWORD sz = 0;
2709             rc = RegQueryValueExW(hkey, deformated, NULL, NULL, NULL, &sz);
2710             if (rc == ERROR_SUCCESS || rc == ERROR_MORE_DATA)
2711             {
2712                 TRACE("value %s of %s checked already exists\n", debugstr_w(deformated),
2713                       debugstr_w(uikey));
2714             }
2715             else
2716             {
2717                 TRACE("Checked and setting value %s of %s\n", debugstr_w(deformated),
2718                       debugstr_w(uikey));
2719                 if (deformated || size)
2720                     RegSetValueExW(hkey, deformated, 0, type, (LPBYTE)value, size);
2721             }
2722         }
2723     }
2724     RegCloseKey(hkey);
2725
2726     uirow = MSI_CreateRecord(3);
2727     MSI_RecordSetStringW(uirow,2,deformated);
2728     MSI_RecordSetStringW(uirow,1,uikey);
2729     if (type == REG_SZ || type == REG_EXPAND_SZ)
2730         MSI_RecordSetStringW(uirow, 3, (LPWSTR)value);
2731     msi_ui_actiondata( package, szWriteRegistryValues, uirow );
2732     msiobj_release( &uirow->hdr );
2733
2734     msi_free(value);
2735     msi_free(deformated);
2736     msi_free(uikey);
2737     msi_free(keypath);
2738
2739     return ERROR_SUCCESS;
2740 }
2741
2742 static UINT ACTION_WriteRegistryValues(MSIPACKAGE *package)
2743 {
2744     static const WCHAR query[] = {
2745         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2746         '`','R','e','g','i','s','t','r','y','`',0};
2747     MSIQUERY *view;
2748     UINT rc;
2749
2750     rc = MSI_DatabaseOpenViewW(package->db, query, &view);
2751     if (rc != ERROR_SUCCESS)
2752         return ERROR_SUCCESS;
2753
2754     rc = MSI_IterateRecords(view, NULL, ITERATE_WriteRegistryValues, package);
2755     msiobj_release(&view->hdr);
2756     return rc;
2757 }
2758
2759 static void delete_key( HKEY root, const WCHAR *path )
2760 {
2761     REGSAM access = 0;
2762     WCHAR *subkey, *p;
2763     HKEY hkey;
2764     LONG res;
2765
2766     if (is_wow64) access |= KEY_WOW64_64KEY;
2767
2768     if (!(subkey = strdupW( path ))) return;
2769     for (;;)
2770     {
2771         if ((p = strrchrW( subkey, '\\' ))) *p = 0;
2772         hkey = open_key( root, subkey, FALSE );
2773         if (!hkey) break;
2774         if (p && p[1])
2775             res = RegDeleteKeyExW( hkey, p + 1, access, 0 );
2776         else
2777             res = RegDeleteKeyExW( root, subkey, access, 0 );
2778         if (res)
2779         {
2780             TRACE("failed to delete key %s (%d)\n", debugstr_w(subkey), res);
2781             break;
2782         }
2783         if (p && p[1]) RegCloseKey( hkey );
2784         else break;
2785     }
2786     msi_free( subkey );
2787 }
2788
2789 static void delete_value( HKEY root, const WCHAR *path, const WCHAR *value )
2790 {
2791     LONG res;
2792     HKEY hkey;
2793     DWORD num_subkeys, num_values;
2794
2795     if ((hkey = open_key( root, path, FALSE )))
2796     {
2797         if ((res = RegDeleteValueW( hkey, value )))
2798             TRACE("failed to delete value %s (%d)\n", debugstr_w(value), res);
2799
2800         res = RegQueryInfoKeyW( hkey, NULL, NULL, NULL, &num_subkeys, NULL, NULL, &num_values,
2801                                 NULL, NULL, NULL, NULL );
2802         RegCloseKey( hkey );
2803         if (!res && !num_subkeys && !num_values)
2804         {
2805             TRACE("removing empty key %s\n", debugstr_w(path));
2806             delete_key( root, path );
2807         }
2808     }
2809 }
2810
2811 static void delete_tree( HKEY root, const WCHAR *path )
2812 {
2813     LONG res;
2814     HKEY hkey;
2815
2816     if (!(hkey = open_key( root, path, FALSE ))) return;
2817     res = RegDeleteTreeW( hkey, NULL );
2818     if (res) TRACE("failed to delete subtree of %s (%d)\n", debugstr_w(path), res);
2819     delete_key( root, path );
2820     RegCloseKey( hkey );
2821 }
2822
2823 static UINT ITERATE_RemoveRegistryValuesOnUninstall( MSIRECORD *row, LPVOID param )
2824 {
2825     MSIPACKAGE *package = param;
2826     LPCWSTR component, name, key_str, root_key_str;
2827     LPWSTR deformated_key, deformated_name, ui_key_str, keypath;
2828     MSICOMPONENT *comp;
2829     MSIRECORD *uirow;
2830     BOOL delete_key = FALSE;
2831     HKEY hkey_root;
2832     UINT size;
2833     INT root;
2834
2835     msi_ui_progress( package, 2, REG_PROGRESS_VALUE, 0, 0 );
2836
2837     component = MSI_RecordGetString( row, 6 );
2838     comp = msi_get_loaded_component( package, component );
2839     if (!comp)
2840         return ERROR_SUCCESS;
2841
2842     comp->Action = msi_get_component_action( package, comp );
2843     if (comp->Action != INSTALLSTATE_ABSENT)
2844     {
2845         TRACE("component not scheduled for removal %s\n", debugstr_w(component));
2846         return ERROR_SUCCESS;
2847     }
2848
2849     name = MSI_RecordGetString( row, 4 );
2850     if (MSI_RecordIsNull( row, 5 ) && name )
2851     {
2852         if (name[0] == '+' && !name[1])
2853             return ERROR_SUCCESS;
2854         if ((name[0] == '-' || name[0] == '*') && !name[1])
2855         {
2856             delete_key = TRUE;
2857             name = NULL;
2858         }
2859     }
2860
2861     root = MSI_RecordGetInteger( row, 2 );
2862     key_str = MSI_RecordGetString( row, 3 );
2863
2864     root_key_str = get_root_key( package, root, &hkey_root );
2865     if (!root_key_str)
2866         return ERROR_SUCCESS;
2867
2868     deformat_string( package, key_str, &deformated_key );
2869     size = strlenW( deformated_key ) + strlenW( root_key_str ) + 1;
2870     ui_key_str = msi_alloc( size * sizeof(WCHAR) );
2871     strcpyW( ui_key_str, root_key_str );
2872     strcatW( ui_key_str, deformated_key );
2873
2874     deformat_string( package, name, &deformated_name );
2875
2876     keypath = get_keypath( comp, hkey_root, deformated_key );
2877     msi_free( deformated_key );
2878     if (delete_key) delete_tree( hkey_root, keypath );
2879     else delete_value( hkey_root, keypath, deformated_name );
2880     msi_free( keypath );
2881
2882     uirow = MSI_CreateRecord( 2 );
2883     MSI_RecordSetStringW( uirow, 1, ui_key_str );
2884     MSI_RecordSetStringW( uirow, 2, deformated_name );
2885     msi_ui_actiondata( package, szRemoveRegistryValues, uirow );
2886     msiobj_release( &uirow->hdr );
2887
2888     msi_free( ui_key_str );
2889     msi_free( deformated_name );
2890     return ERROR_SUCCESS;
2891 }
2892
2893 static UINT ITERATE_RemoveRegistryValuesOnInstall( MSIRECORD *row, LPVOID param )
2894 {
2895     MSIPACKAGE *package = param;
2896     LPCWSTR component, name, key_str, root_key_str;
2897     LPWSTR deformated_key, deformated_name, ui_key_str, keypath;
2898     MSICOMPONENT *comp;
2899     MSIRECORD *uirow;
2900     BOOL delete_key = FALSE;
2901     HKEY hkey_root;
2902     UINT size;
2903     INT root;
2904
2905     component = MSI_RecordGetString( row, 5 );
2906     comp = msi_get_loaded_component( package, component );
2907     if (!comp)
2908         return ERROR_SUCCESS;
2909
2910     comp->Action = msi_get_component_action( package, comp );
2911     if (comp->Action != INSTALLSTATE_LOCAL)
2912     {
2913         TRACE("component not scheduled for installation %s\n", debugstr_w(component));
2914         return ERROR_SUCCESS;
2915     }
2916
2917     if ((name = MSI_RecordGetString( row, 4 )))
2918     {
2919         if (name[0] == '-' && !name[1])
2920         {
2921             delete_key = TRUE;
2922             name = NULL;
2923         }
2924     }
2925
2926     root = MSI_RecordGetInteger( row, 2 );
2927     key_str = MSI_RecordGetString( row, 3 );
2928
2929     root_key_str = get_root_key( package, root, &hkey_root );
2930     if (!root_key_str)
2931         return ERROR_SUCCESS;
2932
2933     deformat_string( package, key_str, &deformated_key );
2934     size = strlenW( deformated_key ) + strlenW( root_key_str ) + 1;
2935     ui_key_str = msi_alloc( size * sizeof(WCHAR) );
2936     strcpyW( ui_key_str, root_key_str );
2937     strcatW( ui_key_str, deformated_key );
2938
2939     deformat_string( package, name, &deformated_name );
2940
2941     keypath = get_keypath( comp, hkey_root, deformated_key );
2942     msi_free( deformated_key );
2943     if (delete_key) delete_tree( hkey_root, keypath );
2944     else delete_value( hkey_root, keypath, deformated_name );
2945     msi_free( keypath );
2946
2947     uirow = MSI_CreateRecord( 2 );
2948     MSI_RecordSetStringW( uirow, 1, ui_key_str );
2949     MSI_RecordSetStringW( uirow, 2, deformated_name );
2950     msi_ui_actiondata( package, szRemoveRegistryValues, uirow );
2951     msiobj_release( &uirow->hdr );
2952
2953     msi_free( ui_key_str );
2954     msi_free( deformated_name );
2955     return ERROR_SUCCESS;
2956 }
2957
2958 static UINT ACTION_RemoveRegistryValues( MSIPACKAGE *package )
2959 {
2960     static const WCHAR registry_query[] = {
2961         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2962         '`','R','e','g','i','s','t','r','y','`',0};
2963     static const WCHAR remove_registry_query[] = {
2964         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2965         '`','R','e','m','o','v','e','R','e','g','i','s','t','r','y','`',0};
2966     MSIQUERY *view;
2967     UINT rc;
2968
2969     rc = MSI_DatabaseOpenViewW( package->db, registry_query, &view );
2970     if (rc == ERROR_SUCCESS)
2971     {
2972         rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveRegistryValuesOnUninstall, package );
2973         msiobj_release( &view->hdr );
2974         if (rc != ERROR_SUCCESS)
2975             return rc;
2976     }
2977     rc = MSI_DatabaseOpenViewW( package->db, remove_registry_query, &view );
2978     if (rc == ERROR_SUCCESS)
2979     {
2980         rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveRegistryValuesOnInstall, package );
2981         msiobj_release( &view->hdr );
2982         if (rc != ERROR_SUCCESS)
2983             return rc;
2984     }
2985     return ERROR_SUCCESS;
2986 }
2987
2988 static UINT ACTION_InstallInitialize(MSIPACKAGE *package)
2989 {
2990     package->script->CurrentlyScripting = TRUE;
2991
2992     return ERROR_SUCCESS;
2993 }
2994
2995
2996 static UINT ACTION_InstallValidate(MSIPACKAGE *package)
2997 {
2998     static const WCHAR query[]= {
2999         'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
3000         '`','R','e','g','i','s','t','r','y','`',0};
3001     MSICOMPONENT *comp;
3002     DWORD total = 0, count = 0;
3003     MSIQUERY *view;
3004     MSIFEATURE *feature;
3005     MSIFILE *file;
3006     UINT rc;
3007
3008     TRACE("InstallValidate\n");
3009
3010     rc = MSI_DatabaseOpenViewW( package->db, query, &view );
3011     if (rc == ERROR_SUCCESS)
3012     {
3013         rc = MSI_IterateRecords( view, &count, NULL, package );
3014         msiobj_release( &view->hdr );
3015         if (rc != ERROR_SUCCESS)
3016             return rc;
3017         total += count * REG_PROGRESS_VALUE;
3018     }
3019     LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
3020         total += COMPONENT_PROGRESS_VALUE;
3021
3022     LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
3023         total += file->FileSize;
3024
3025     msi_ui_progress( package, 0, total, 0, 0 );
3026
3027     LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3028     {
3029         TRACE("Feature: %s Installed %d Request %d Action %d\n",
3030               debugstr_w(feature->Feature), feature->Installed,
3031               feature->ActionRequest, feature->Action);
3032     }
3033     return ERROR_SUCCESS;
3034 }
3035
3036 static UINT ITERATE_LaunchConditions(MSIRECORD *row, LPVOID param)
3037 {
3038     MSIPACKAGE* package = param;
3039     LPCWSTR cond = NULL; 
3040     LPCWSTR message = NULL;
3041     UINT r;
3042
3043     static const WCHAR title[]=
3044         {'I','n','s','t','a','l','l',' ','F','a', 'i','l','e','d',0};
3045
3046     cond = MSI_RecordGetString(row,1);
3047
3048     r = MSI_EvaluateConditionW(package,cond);
3049     if (r == MSICONDITION_FALSE)
3050     {
3051         if ((package->ui_level & INSTALLUILEVEL_MASK) != INSTALLUILEVEL_NONE)
3052         {
3053             LPWSTR deformated;
3054             message = MSI_RecordGetString(row,2);
3055             deformat_string(package,message,&deformated);
3056             MessageBoxW(NULL,deformated,title,MB_OK);
3057             msi_free(deformated);
3058         }
3059
3060         return ERROR_INSTALL_FAILURE;
3061     }
3062
3063     return ERROR_SUCCESS;
3064 }
3065
3066 static UINT ACTION_LaunchConditions(MSIPACKAGE *package)
3067 {
3068     static const WCHAR query[] = {
3069         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3070         '`','L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','`',0};
3071     MSIQUERY *view;
3072     UINT rc;
3073
3074     TRACE("Checking launch conditions\n");
3075
3076     rc = MSI_DatabaseOpenViewW(package->db, query, &view);
3077     if (rc != ERROR_SUCCESS)
3078         return ERROR_SUCCESS;
3079
3080     rc = MSI_IterateRecords(view, NULL, ITERATE_LaunchConditions, package);
3081     msiobj_release(&view->hdr);
3082     return rc;
3083 }
3084
3085 static LPWSTR resolve_keypath( MSIPACKAGE* package, MSICOMPONENT *cmp )
3086 {
3087
3088     if (!cmp->KeyPath)
3089         return strdupW( msi_get_target_folder( package, cmp->Directory ) );
3090
3091     if (cmp->Attributes & msidbComponentAttributesRegistryKeyPath)
3092     {
3093         static const WCHAR query[] = {
3094             'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3095             '`','R','e','g','i','s','t','r','y','`',' ','W','H','E','R','E',' ',
3096             '`','R','e','g','i','s','t','r','y','`',' ','=',' ' ,'\'','%','s','\'',0};
3097         static const WCHAR fmt[] = {'%','0','2','i',':','\\','%','s','\\',0};
3098         static const WCHAR fmt2[]= {'%','0','2','i',':','\\','%','s','\\','%','s',0};
3099         MSIRECORD *row;
3100         UINT root, len;
3101         LPWSTR deformated, buffer, deformated_name;
3102         LPCWSTR key, name;
3103
3104         row = MSI_QueryGetRecord(package->db, query, cmp->KeyPath);
3105         if (!row)
3106             return NULL;
3107
3108         root = MSI_RecordGetInteger(row,2);
3109         key = MSI_RecordGetString(row, 3);
3110         name = MSI_RecordGetString(row, 4);
3111         deformat_string(package, key , &deformated);
3112         deformat_string(package, name, &deformated_name);
3113
3114         len = strlenW(deformated) + 6;
3115         if (deformated_name)
3116             len+=strlenW(deformated_name);
3117
3118         buffer = msi_alloc( len *sizeof(WCHAR));
3119
3120         if (deformated_name)
3121             sprintfW(buffer,fmt2,root,deformated,deformated_name);
3122         else
3123             sprintfW(buffer,fmt,root,deformated);
3124
3125         msi_free(deformated);
3126         msi_free(deformated_name);
3127         msiobj_release(&row->hdr);
3128
3129         return buffer;
3130     }
3131     else if (cmp->Attributes & msidbComponentAttributesODBCDataSource)
3132     {
3133         FIXME("UNIMPLEMENTED keypath as ODBC Source\n");
3134         return NULL;
3135     }
3136     else
3137     {
3138         MSIFILE *file = msi_get_loaded_file( package, cmp->KeyPath );
3139
3140         if (file)
3141             return strdupW( file->TargetPath );
3142     }
3143     return NULL;
3144 }
3145
3146 static HKEY openSharedDLLsKey(void)
3147 {
3148     HKEY hkey=0;
3149     static const WCHAR path[] =
3150         {'S','o','f','t','w','a','r','e','\\',
3151          'M','i','c','r','o','s','o','f','t','\\',
3152          'W','i','n','d','o','w','s','\\',
3153          'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
3154          'S','h','a','r','e','d','D','L','L','s',0};
3155
3156     RegCreateKeyW(HKEY_LOCAL_MACHINE,path,&hkey);
3157     return hkey;
3158 }
3159
3160 static UINT ACTION_GetSharedDLLsCount(LPCWSTR dll)
3161 {
3162     HKEY hkey;
3163     DWORD count=0;
3164     DWORD type;
3165     DWORD sz = sizeof(count);
3166     DWORD rc;
3167     
3168     hkey = openSharedDLLsKey();
3169     rc = RegQueryValueExW(hkey, dll, NULL, &type, (LPBYTE)&count, &sz);
3170     if (rc != ERROR_SUCCESS)
3171         count = 0;
3172     RegCloseKey(hkey);
3173     return count;
3174 }
3175
3176 static UINT ACTION_WriteSharedDLLsCount(LPCWSTR path, UINT count)
3177 {
3178     HKEY hkey;
3179
3180     hkey = openSharedDLLsKey();
3181     if (count > 0)
3182         msi_reg_set_val_dword( hkey, path, count );
3183     else
3184         RegDeleteValueW(hkey,path);
3185     RegCloseKey(hkey);
3186     return count;
3187 }
3188
3189 static void ACTION_RefCountComponent( MSIPACKAGE* package, MSICOMPONENT *comp )
3190 {
3191     MSIFEATURE *feature;
3192     INT count = 0;
3193     BOOL write = FALSE;
3194
3195     /* only refcount DLLs */
3196     if (comp->KeyPath == NULL || 
3197         comp->assembly ||
3198         comp->Attributes & msidbComponentAttributesRegistryKeyPath || 
3199         comp->Attributes & msidbComponentAttributesODBCDataSource)
3200         write = FALSE;
3201     else
3202     {
3203         count = ACTION_GetSharedDLLsCount( comp->FullKeypath);
3204         write = (count > 0);
3205
3206         if (comp->Attributes & msidbComponentAttributesSharedDllRefCount)
3207             write = TRUE;
3208     }
3209
3210     /* increment counts */
3211     LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3212     {
3213         ComponentList *cl;
3214
3215         if (msi_get_feature_action( package, feature ) != INSTALLSTATE_LOCAL)
3216             continue;
3217
3218         LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3219         {
3220             if ( cl->component == comp )
3221                 count++;
3222         }
3223     }
3224
3225     /* decrement counts */
3226     LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3227     {
3228         ComponentList *cl;
3229
3230         if (msi_get_feature_action( package, feature ) != INSTALLSTATE_ABSENT)
3231             continue;
3232
3233         LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3234         {
3235             if ( cl->component == comp )
3236                 count--;
3237         }
3238     }
3239
3240     /* ref count all the files in the component */
3241     if (write)
3242     {
3243         MSIFILE *file;
3244
3245         LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
3246         {
3247             if (file->Component == comp)
3248                 ACTION_WriteSharedDLLsCount( file->TargetPath, count );
3249         }
3250     }
3251     
3252     /* add a count for permanent */
3253     if (comp->Attributes & msidbComponentAttributesPermanent)
3254         count ++;
3255     
3256     comp->RefCount = count;
3257
3258     if (write)
3259         ACTION_WriteSharedDLLsCount( comp->FullKeypath, comp->RefCount );
3260 }
3261
3262 static WCHAR *build_full_keypath( MSIPACKAGE *package, MSICOMPONENT *comp )
3263 {
3264     if (comp->assembly)
3265     {
3266         const WCHAR prefixW[] = {'<','\\',0};
3267         DWORD len = strlenW( prefixW ) + strlenW( comp->assembly->display_name );
3268         WCHAR *keypath = msi_alloc( (len + 1) * sizeof(WCHAR) );
3269
3270         if (keypath)
3271         {
3272             strcpyW( keypath, prefixW );
3273             strcatW( keypath, comp->assembly->display_name );
3274         }
3275         return keypath;
3276     }
3277     return resolve_keypath( package, comp );
3278 }
3279
3280 static UINT ACTION_ProcessComponents(MSIPACKAGE *package)
3281 {
3282     WCHAR squished_pc[GUID_SIZE], squished_cc[GUID_SIZE];
3283     UINT rc;
3284     MSICOMPONENT *comp;
3285     HKEY hkey;
3286
3287     TRACE("\n");
3288
3289     squash_guid(package->ProductCode,squished_pc);
3290     msi_set_sourcedir_props(package, FALSE);
3291
3292     LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
3293     {
3294         MSIRECORD *uirow;
3295         INSTALLSTATE action;
3296
3297         msi_ui_progress( package, 2, COMPONENT_PROGRESS_VALUE, 0, 0 );
3298         if (!comp->ComponentId)
3299             continue;
3300
3301         squash_guid( comp->ComponentId, squished_cc );
3302         msi_free( comp->FullKeypath );
3303         comp->FullKeypath = build_full_keypath( package, comp );
3304
3305         ACTION_RefCountComponent( package, comp );
3306
3307         if (package->need_rollback) action = comp->Installed;
3308         else action = comp->ActionRequest;
3309
3310         TRACE("Component %s (%s), Keypath=%s, RefCount=%u Action=%u\n",
3311                             debugstr_w(comp->Component), debugstr_w(squished_cc),
3312                             debugstr_w(comp->FullKeypath), comp->RefCount, action);
3313
3314         if (action == INSTALLSTATE_LOCAL || action == INSTALLSTATE_SOURCE)
3315         {
3316             if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3317                 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, szLocalSid, &hkey, TRUE);
3318             else
3319                 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, NULL, &hkey, TRUE);
3320
3321             if (rc != ERROR_SUCCESS)
3322                 continue;
3323
3324             if (comp->Attributes & msidbComponentAttributesPermanent)
3325             {
3326                 static const WCHAR szPermKey[] =
3327                     { '0','0','0','0','0','0','0','0','0','0','0','0',
3328                       '0','0','0','0','0','0','0','0','0','0','0','0',
3329                       '0','0','0','0','0','0','0','0',0 };
3330
3331                 msi_reg_set_val_str(hkey, szPermKey, comp->FullKeypath);
3332             }
3333             if (action == INSTALLSTATE_LOCAL)
3334                 msi_reg_set_val_str(hkey, squished_pc, comp->FullKeypath);
3335             else
3336             {
3337                 MSIFILE *file;
3338                 MSIRECORD *row;
3339                 LPWSTR ptr, ptr2;
3340                 WCHAR source[MAX_PATH];
3341                 WCHAR base[MAX_PATH];
3342                 LPWSTR sourcepath;
3343
3344                 static const WCHAR fmt[] = {'%','0','2','d','\\',0};
3345                 static const WCHAR query[] = {
3346                     'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
3347                     '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
3348                     '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ',
3349                     '>','=',' ','%','i',' ','O','R','D','E','R',' ','B','Y',' ',
3350                     '`','D','i','s','k','I','d','`',0};
3351
3352                 if (!comp->KeyPath || !(file = msi_get_loaded_file(package, comp->KeyPath)))
3353                     continue;
3354
3355                 row = MSI_QueryGetRecord(package->db, query, file->Sequence);
3356                 sprintfW(source, fmt, MSI_RecordGetInteger(row, 1));
3357                 ptr2 = strrchrW(source, '\\') + 1;
3358                 msiobj_release(&row->hdr);
3359
3360                 lstrcpyW(base, package->PackagePath);
3361                 ptr = strrchrW(base, '\\');
3362                 *(ptr + 1) = '\0';
3363
3364                 sourcepath = msi_resolve_file_source(package, file);
3365                 ptr = sourcepath + lstrlenW(base);
3366                 lstrcpyW(ptr2, ptr);
3367                 msi_free(sourcepath);
3368
3369                 msi_reg_set_val_str(hkey, squished_pc, source);
3370             }
3371             RegCloseKey(hkey);
3372         }
3373         else if (action == INSTALLSTATE_ABSENT)
3374         {
3375             if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3376                 MSIREG_DeleteUserDataComponentKey(comp->ComponentId, szLocalSid);
3377             else
3378                 MSIREG_DeleteUserDataComponentKey(comp->ComponentId, NULL);
3379         }
3380
3381         /* UI stuff */
3382         uirow = MSI_CreateRecord(3);
3383         MSI_RecordSetStringW(uirow,1,package->ProductCode);
3384         MSI_RecordSetStringW(uirow,2,comp->ComponentId);
3385         MSI_RecordSetStringW(uirow,3,comp->FullKeypath);
3386         msi_ui_actiondata( package, szProcessComponents, uirow );
3387         msiobj_release( &uirow->hdr );
3388     }
3389     return ERROR_SUCCESS;
3390 }
3391
3392 typedef struct {
3393     CLSID       clsid;
3394     LPWSTR      source;
3395
3396     LPWSTR      path;
3397     ITypeLib    *ptLib;
3398 } typelib_struct;
3399
3400 static BOOL CALLBACK Typelib_EnumResNameProc( HMODULE hModule, LPCWSTR lpszType, 
3401                                        LPWSTR lpszName, LONG_PTR lParam)
3402 {
3403     TLIBATTR *attr;
3404     typelib_struct *tl_struct = (typelib_struct*) lParam;
3405     static const WCHAR fmt[] = {'%','s','\\','%','i',0};
3406     int sz; 
3407     HRESULT res;
3408
3409     if (!IS_INTRESOURCE(lpszName))
3410     {
3411         ERR("Not Int Resource Name %s\n",debugstr_w(lpszName));
3412         return TRUE;
3413     }
3414
3415     sz = strlenW(tl_struct->source)+4;
3416     sz *= sizeof(WCHAR);
3417
3418     if ((INT_PTR)lpszName == 1)
3419         tl_struct->path = strdupW(tl_struct->source);
3420     else
3421     {
3422         tl_struct->path = msi_alloc(sz);
3423         sprintfW(tl_struct->path,fmt,tl_struct->source, lpszName);
3424     }
3425
3426     TRACE("trying %s\n", debugstr_w(tl_struct->path));
3427     res = LoadTypeLib(tl_struct->path,&tl_struct->ptLib);
3428     if (FAILED(res))
3429     {
3430         msi_free(tl_struct->path);
3431         tl_struct->path = NULL;
3432
3433         return TRUE;
3434     }
3435
3436     ITypeLib_GetLibAttr(tl_struct->ptLib, &attr);
3437     if (IsEqualGUID(&(tl_struct->clsid),&(attr->guid)))
3438     {
3439         ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
3440         return FALSE;
3441     }
3442
3443     msi_free(tl_struct->path);
3444     tl_struct->path = NULL;
3445
3446     ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
3447     ITypeLib_Release(tl_struct->ptLib);
3448
3449     return TRUE;
3450 }
3451
3452 static UINT ITERATE_RegisterTypeLibraries(MSIRECORD *row, LPVOID param)
3453 {
3454     MSIPACKAGE* package = param;
3455     LPCWSTR component;
3456     MSICOMPONENT *comp;
3457     MSIFILE *file;
3458     typelib_struct tl_struct;
3459     ITypeLib *tlib;
3460     HMODULE module;
3461     HRESULT hr;
3462
3463     component = MSI_RecordGetString(row,3);
3464     comp = msi_get_loaded_component(package,component);
3465     if (!comp)
3466         return ERROR_SUCCESS;
3467
3468     comp->Action = msi_get_component_action( package, comp );
3469     if (comp->Action != INSTALLSTATE_LOCAL)
3470     {
3471         TRACE("component not scheduled for installation %s\n", debugstr_w(component));
3472         return ERROR_SUCCESS;
3473     }
3474
3475     if (!comp->KeyPath || !(file = msi_get_loaded_file( package, comp->KeyPath )))
3476     {
3477         TRACE("component has no key path\n");
3478         return ERROR_SUCCESS;
3479     }
3480     msi_ui_actiondata( package, szRegisterTypeLibraries, row );
3481
3482     module = LoadLibraryExW( file->TargetPath, NULL, LOAD_LIBRARY_AS_DATAFILE );
3483     if (module)
3484     {
3485         LPCWSTR guid;
3486         guid = MSI_RecordGetString(row,1);
3487         CLSIDFromString( guid, &tl_struct.clsid);
3488         tl_struct.source = strdupW( file->TargetPath );
3489         tl_struct.path = NULL;
3490
3491         EnumResourceNamesW(module, szTYPELIB, Typelib_EnumResNameProc,
3492                         (LONG_PTR)&tl_struct);
3493
3494         if (tl_struct.path)
3495         {
3496             LPCWSTR helpid, help_path = NULL;
3497             HRESULT res;
3498
3499             helpid = MSI_RecordGetString(row,6);
3500
3501             if (helpid) help_path = msi_get_target_folder( package, helpid );
3502             res = RegisterTypeLib( tl_struct.ptLib, tl_struct.path, (OLECHAR *)help_path );
3503
3504             if (FAILED(res))
3505                 ERR("Failed to register type library %s\n", debugstr_w(tl_struct.path));
3506             else
3507                 TRACE("Registered %s\n", debugstr_w(tl_struct.path));
3508
3509             ITypeLib_Release(tl_struct.ptLib);
3510             msi_free(tl_struct.path);
3511         }
3512         else ERR("Failed to load type library %s\n", debugstr_w(tl_struct.source));
3513
3514         FreeLibrary(module);
3515         msi_free(tl_struct.source);
3516     }
3517     else
3518     {
3519         hr = LoadTypeLibEx(file->TargetPath, REGKIND_REGISTER, &tlib);
3520         if (FAILED(hr))
3521         {
3522             ERR("Failed to load type library: %08x\n", hr);
3523             return ERROR_INSTALL_FAILURE;
3524         }
3525
3526         ITypeLib_Release(tlib);
3527     }
3528
3529     return ERROR_SUCCESS;
3530 }
3531
3532 static UINT ACTION_RegisterTypeLibraries(MSIPACKAGE *package)
3533 {
3534     static const WCHAR query[] = {
3535         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3536         '`','T','y','p','e','L','i','b','`',0};
3537     MSIQUERY *view;
3538     UINT rc;
3539
3540     rc = MSI_DatabaseOpenViewW(package->db, query, &view);
3541     if (rc != ERROR_SUCCESS)
3542         return ERROR_SUCCESS;
3543
3544     rc = MSI_IterateRecords(view, NULL, ITERATE_RegisterTypeLibraries, package);
3545     msiobj_release(&view->hdr);
3546     return rc;
3547 }
3548
3549 static UINT ITERATE_UnregisterTypeLibraries( MSIRECORD *row, LPVOID param )
3550 {
3551     MSIPACKAGE *package = param;
3552     LPCWSTR component, guid;
3553     MSICOMPONENT *comp;
3554     GUID libid;
3555     UINT version;
3556     LCID language;
3557     SYSKIND syskind;
3558     HRESULT hr;
3559
3560     component = MSI_RecordGetString( row, 3 );
3561     comp = msi_get_loaded_component( package, component );
3562     if (!comp)
3563         return ERROR_SUCCESS;
3564
3565     comp->Action = msi_get_component_action( package, comp );
3566     if (comp->Action != INSTALLSTATE_ABSENT)
3567     {
3568         TRACE("component not scheduled for removal %s\n", debugstr_w(component));
3569         return ERROR_SUCCESS;
3570     }
3571     msi_ui_actiondata( package, szUnregisterTypeLibraries, row );
3572
3573     guid = MSI_RecordGetString( row, 1 );
3574     CLSIDFromString( guid, &libid );
3575     version = MSI_RecordGetInteger( row, 4 );
3576     language = MSI_RecordGetInteger( row, 2 );
3577
3578 #ifdef _WIN64
3579     syskind = SYS_WIN64;
3580 #else
3581     syskind = SYS_WIN32;
3582 #endif
3583
3584     hr = UnRegisterTypeLib( &libid, (version >> 8) & 0xffff, version & 0xff, language, syskind );
3585     if (FAILED(hr))
3586     {
3587         WARN("Failed to unregister typelib: %08x\n", hr);
3588     }
3589
3590     return ERROR_SUCCESS;
3591 }
3592
3593 static UINT ACTION_UnregisterTypeLibraries( MSIPACKAGE *package )
3594 {
3595     static const WCHAR query[] = {
3596         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3597         '`','T','y','p','e','L','i','b','`',0};
3598     MSIQUERY *view;
3599     UINT rc;
3600
3601     rc = MSI_DatabaseOpenViewW( package->db, query, &view );
3602     if (rc != ERROR_SUCCESS)
3603         return ERROR_SUCCESS;
3604
3605     rc = MSI_IterateRecords( view, NULL, ITERATE_UnregisterTypeLibraries, package );
3606     msiobj_release( &view->hdr );
3607     return rc;
3608 }
3609
3610 static WCHAR *get_link_file( MSIPACKAGE *package, MSIRECORD *row )
3611 {
3612     static const WCHAR szlnk[] = {'.','l','n','k',0};
3613     LPCWSTR directory, extension, link_folder;
3614     LPWSTR link_file, filename;
3615
3616     directory = MSI_RecordGetString( row, 2 );
3617     link_folder = msi_get_target_folder( package, directory );
3618     if (!link_folder)
3619     {
3620         ERR("unable to resolve folder %s\n", debugstr_w(directory));
3621         return NULL;
3622     }
3623     /* may be needed because of a bug somewhere else */
3624     msi_create_full_path( link_folder );
3625
3626     filename = msi_dup_record_field( row, 3 );
3627     msi_reduce_to_long_filename( filename );
3628
3629     extension = strchrW( filename, '.' );
3630     if (!extension || strcmpiW( extension, szlnk ))
3631     {
3632         int len = strlenW( filename );
3633         filename = msi_realloc( filename, len * sizeof(WCHAR) + sizeof(szlnk) );
3634         memcpy( filename + len, szlnk, sizeof(szlnk) );
3635     }
3636     link_file = msi_build_directory_name( 2, link_folder, filename );
3637     msi_free( filename );
3638
3639     return link_file;
3640 }
3641
3642 WCHAR *msi_build_icon_path( MSIPACKAGE *package, const WCHAR *icon_name )
3643 {
3644     static const WCHAR szMicrosoft[] = {'M','i','c','r','o','s','o','f','t','\\',0};
3645     static const WCHAR szInstaller[] = {'I','n','s','t','a','l','l','e','r','\\',0};
3646     WCHAR *folder, *dest, *path;
3647
3648     if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3649         folder = msi_dup_property( package->db, szWindowsFolder );
3650     else
3651     {
3652         WCHAR *appdata = msi_dup_property( package->db, szAppDataFolder );
3653         folder = msi_build_directory_name( 2, appdata, szMicrosoft );
3654         msi_free( appdata );
3655     }
3656     dest = msi_build_directory_name( 3, folder, szInstaller, package->ProductCode );
3657     msi_create_full_path( dest );
3658     path = msi_build_directory_name( 2, dest, icon_name );
3659     msi_free( folder );
3660     msi_free( dest );
3661     return path;
3662 }
3663
3664 static UINT ITERATE_CreateShortcuts(MSIRECORD *row, LPVOID param)
3665 {
3666     MSIPACKAGE *package = param;
3667     LPWSTR link_file, deformated, path;
3668     LPCWSTR component, target;
3669     MSICOMPONENT *comp;
3670     IShellLinkW *sl = NULL;
3671     IPersistFile *pf = NULL;
3672     HRESULT res;
3673
3674     component = MSI_RecordGetString(row, 4);
3675     comp = msi_get_loaded_component(package, component);
3676     if (!comp)
3677         return ERROR_SUCCESS;
3678
3679     comp->Action = msi_get_component_action( package, comp );
3680     if (comp->Action != INSTALLSTATE_LOCAL)
3681     {
3682         TRACE("component not scheduled for installation %s\n", debugstr_w(component));
3683         return ERROR_SUCCESS;
3684     }
3685     msi_ui_actiondata( package, szCreateShortcuts, row );
3686
3687     res = CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
3688                     &IID_IShellLinkW, (LPVOID *) &sl );
3689
3690     if (FAILED( res ))
3691     {
3692         ERR("CLSID_ShellLink not available\n");
3693         goto err;
3694     }
3695
3696     res = IShellLinkW_QueryInterface( sl, &IID_IPersistFile,(LPVOID*) &pf );
3697     if (FAILED( res ))
3698     {
3699         ERR("QueryInterface(IID_IPersistFile) failed\n");
3700         goto err;
3701     }
3702
3703     target = MSI_RecordGetString(row, 5);
3704     if (strchrW(target, '['))
3705     {
3706         deformat_string( package, target, &path );
3707         TRACE("target path is %s\n", debugstr_w(path));
3708         IShellLinkW_SetPath( sl, path );
3709         msi_free( path );
3710     }
3711     else
3712     {
3713         FIXME("poorly handled shortcut format, advertised shortcut\n");
3714         IShellLinkW_SetPath(sl,comp->FullKeypath);
3715     }
3716
3717     if (!MSI_RecordIsNull(row,6))
3718     {
3719         LPCWSTR arguments = MSI_RecordGetString(row, 6);
3720         deformat_string(package, arguments, &deformated);
3721         IShellLinkW_SetArguments(sl,deformated);
3722         msi_free(deformated);
3723     }
3724
3725     if (!MSI_RecordIsNull(row,7))
3726     {
3727         LPCWSTR description = MSI_RecordGetString(row, 7);
3728         IShellLinkW_SetDescription(sl, description);
3729     }
3730
3731     if (!MSI_RecordIsNull(row,8))
3732         IShellLinkW_SetHotkey(sl,MSI_RecordGetInteger(row,8));
3733
3734     if (!MSI_RecordIsNull(row,9))
3735     {
3736         INT index; 
3737         LPCWSTR icon = MSI_RecordGetString(row, 9);
3738
3739         path = msi_build_icon_path(package, icon);
3740         index = MSI_RecordGetInteger(row,10);
3741
3742         /* no value means 0 */
3743         if (index == MSI_NULL_INTEGER)
3744             index = 0;
3745
3746         IShellLinkW_SetIconLocation(sl, path, index);
3747         msi_free(path);
3748     }
3749
3750     if (!MSI_RecordIsNull(row,11))
3751         IShellLinkW_SetShowCmd(sl,MSI_RecordGetInteger(row,11));
3752
3753     if (!MSI_RecordIsNull(row,12))
3754     {
3755         LPCWSTR full_path, wkdir = MSI_RecordGetString( row, 12 );
3756         full_path = msi_get_target_folder( package, wkdir );
3757         if (full_path) IShellLinkW_SetWorkingDirectory( sl, full_path );
3758     }
3759     link_file = get_link_file(package, row);
3760
3761     TRACE("Writing shortcut to %s\n", debugstr_w(link_file));
3762     IPersistFile_Save(pf, link_file, FALSE);
3763     msi_free(link_file);
3764
3765 err:
3766     if (pf)
3767         IPersistFile_Release( pf );
3768     if (sl)
3769         IShellLinkW_Release( sl );
3770
3771     return ERROR_SUCCESS;
3772 }
3773
3774 static UINT ACTION_CreateShortcuts(MSIPACKAGE *package)
3775 {
3776     static const WCHAR query[] = {
3777         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3778         '`','S','h','o','r','t','c','u','t','`',0};
3779     MSIQUERY *view;
3780     HRESULT res;
3781     UINT rc;
3782
3783     rc = MSI_DatabaseOpenViewW(package->db, query, &view);
3784     if (rc != ERROR_SUCCESS)
3785         return ERROR_SUCCESS;
3786
3787     res = CoInitialize( NULL );
3788
3789     rc = MSI_IterateRecords(view, NULL, ITERATE_CreateShortcuts, package);
3790     msiobj_release(&view->hdr);
3791
3792     if (SUCCEEDED(res)) CoUninitialize();
3793     return rc;
3794 }
3795
3796 static UINT ITERATE_RemoveShortcuts( MSIRECORD *row, LPVOID param )
3797 {
3798     MSIPACKAGE *package = param;
3799     LPWSTR link_file;
3800     LPCWSTR component;
3801     MSICOMPONENT *comp;
3802
3803     component = MSI_RecordGetString( row, 4 );
3804     comp = msi_get_loaded_component( package, component );
3805     if (!comp)
3806         return ERROR_SUCCESS;
3807
3808     comp->Action = msi_get_component_action( package, comp );
3809     if (comp->Action != INSTALLSTATE_ABSENT)
3810     {
3811         TRACE("component not scheduled for removal %s\n", debugstr_w(component));
3812         return ERROR_SUCCESS;
3813     }
3814     msi_ui_actiondata( package, szRemoveShortcuts, row );
3815
3816     link_file = get_link_file( package, row );
3817
3818     TRACE("Removing shortcut file %s\n", debugstr_w( link_file ));
3819     if (!DeleteFileW( link_file ))
3820     {
3821         WARN("Failed to remove shortcut file %u\n", GetLastError());
3822     }
3823     msi_free( link_file );
3824
3825     return ERROR_SUCCESS;
3826 }
3827
3828 static UINT ACTION_RemoveShortcuts( MSIPACKAGE *package )
3829 {
3830     static const WCHAR query[] = {
3831         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3832         '`','S','h','o','r','t','c','u','t','`',0};
3833     MSIQUERY *view;
3834     UINT rc;
3835
3836     rc = MSI_DatabaseOpenViewW( package->db, query, &view );
3837     if (rc != ERROR_SUCCESS)
3838         return ERROR_SUCCESS;
3839
3840     rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveShortcuts, package );
3841     msiobj_release( &view->hdr );
3842     return rc;
3843 }
3844
3845 static UINT ITERATE_PublishIcon(MSIRECORD *row, LPVOID param)
3846 {
3847     MSIPACKAGE* package = param;
3848     HANDLE the_file;
3849     LPWSTR FilePath;
3850     LPCWSTR FileName;
3851     CHAR buffer[1024];
3852     DWORD sz;
3853     UINT rc;
3854
3855     FileName = MSI_RecordGetString(row,1);
3856     if (!FileName)
3857     {
3858         ERR("Unable to get FileName\n");
3859         return ERROR_SUCCESS;
3860     }
3861
3862     FilePath = msi_build_icon_path(package, FileName);
3863
3864     TRACE("Creating icon file at %s\n",debugstr_w(FilePath));
3865
3866     the_file = CreateFileW(FilePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
3867                         FILE_ATTRIBUTE_NORMAL, NULL);
3868
3869     if (the_file == INVALID_HANDLE_VALUE)
3870     {
3871         ERR("Unable to create file %s\n",debugstr_w(FilePath));
3872         msi_free(FilePath);
3873         return ERROR_SUCCESS;
3874     }
3875
3876     do 
3877     {
3878         DWORD write;
3879         sz = 1024;
3880         rc = MSI_RecordReadStream(row,2,buffer,&sz);
3881         if (rc != ERROR_SUCCESS)
3882         {
3883             ERR("Failed to get stream\n");
3884             CloseHandle(the_file);  
3885             DeleteFileW(FilePath);
3886             break;
3887         }
3888         WriteFile(the_file,buffer,sz,&write,NULL);
3889     } while (sz == 1024);
3890
3891     msi_free(FilePath);
3892     CloseHandle(the_file);
3893
3894     return ERROR_SUCCESS;
3895 }
3896
3897 static UINT msi_publish_icons(MSIPACKAGE *package)
3898 {
3899     static const WCHAR query[]= {
3900         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3901         '`','I','c','o','n','`',0};
3902     MSIQUERY *view;
3903     UINT r;
3904
3905     r = MSI_DatabaseOpenViewW(package->db, query, &view);
3906     if (r == ERROR_SUCCESS)
3907     {
3908         r = MSI_IterateRecords(view, NULL, ITERATE_PublishIcon, package);
3909         msiobj_release(&view->hdr);
3910         if (r != ERROR_SUCCESS)
3911             return r;
3912     }
3913     return ERROR_SUCCESS;
3914 }
3915
3916 static UINT msi_publish_sourcelist(MSIPACKAGE *package, HKEY hkey)
3917 {
3918     UINT r;
3919     HKEY source;
3920     LPWSTR buffer;
3921     MSIMEDIADISK *disk;
3922     MSISOURCELISTINFO *info;
3923
3924     r = RegCreateKeyW(hkey, szSourceList, &source);
3925     if (r != ERROR_SUCCESS)
3926         return r;
3927
3928     RegCloseKey(source);
3929
3930     buffer = strrchrW(package->PackagePath, '\\') + 1;
3931     r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3932                               package->Context, MSICODE_PRODUCT,
3933                               INSTALLPROPERTY_PACKAGENAMEW, buffer);
3934     if (r != ERROR_SUCCESS)
3935         return r;
3936
3937     r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3938                               package->Context, MSICODE_PRODUCT,
3939                               INSTALLPROPERTY_MEDIAPACKAGEPATHW, szEmpty);
3940     if (r != ERROR_SUCCESS)
3941         return r;
3942
3943     r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3944                               package->Context, MSICODE_PRODUCT,
3945                               INSTALLPROPERTY_DISKPROMPTW, szEmpty);
3946     if (r != ERROR_SUCCESS)
3947         return r;
3948
3949     LIST_FOR_EACH_ENTRY(info, &package->sourcelist_info, MSISOURCELISTINFO, entry)
3950     {
3951         if (!strcmpW( info->property, INSTALLPROPERTY_LASTUSEDSOURCEW ))
3952             msi_set_last_used_source(package->ProductCode, NULL, info->context,
3953                                      info->options, info->value);
3954         else
3955             MsiSourceListSetInfoW(package->ProductCode, NULL,
3956                                   info->context, info->options,
3957                                   info->property, info->value);
3958     }
3959
3960     LIST_FOR_EACH_ENTRY(disk, &package->sourcelist_media, MSIMEDIADISK, entry)
3961     {
3962         MsiSourceListAddMediaDiskW(package->ProductCode, NULL,
3963                                    disk->context, disk->options,
3964                                    disk->disk_id, disk->volume_label, disk->disk_prompt);
3965     }
3966
3967     return ERROR_SUCCESS;
3968 }
3969
3970 static UINT msi_publish_product_properties(MSIPACKAGE *package, HKEY hkey)
3971 {
3972     MSIHANDLE hdb, suminfo;
3973     WCHAR guids[MAX_PATH];
3974     WCHAR packcode[SQUISH_GUID_SIZE];
3975     LPWSTR buffer;
3976     LPWSTR ptr;
3977     DWORD langid;
3978     DWORD size;
3979     UINT r;
3980
3981     static const WCHAR szARPProductIcon[] =
3982         {'A','R','P','P','R','O','D','U','C','T','I','C','O','N',0};
3983     static const WCHAR szAssignment[] =
3984         {'A','s','s','i','g','n','m','e','n','t',0};
3985     static const WCHAR szAdvertiseFlags[] =
3986         {'A','d','v','e','r','t','i','s','e','F','l','a','g','s',0};
3987     static const WCHAR szClients[] =
3988         {'C','l','i','e','n','t','s',0};
3989     static const WCHAR szColon[] = {':',0};
3990
3991     buffer = msi_dup_property(package->db, INSTALLPROPERTY_PRODUCTNAMEW);
3992     msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTNAMEW, buffer);
3993     msi_free(buffer);
3994
3995     langid = msi_get_property_int(package->db, szProductLanguage, 0);
3996     msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
3997
3998     /* FIXME */
3999     msi_reg_set_val_dword(hkey, INSTALLPROPERTY_AUTHORIZED_LUA_APPW, 0);
4000
4001     buffer = msi_dup_property(package->db, szARPProductIcon);
4002     if (buffer)
4003     {
4004         LPWSTR path = msi_build_icon_path(package, buffer);
4005         msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTICONW, path);
4006         msi_free(path);
4007         msi_free(buffer);
4008     }
4009
4010     buffer = msi_dup_property(package->db, szProductVersion);
4011     if (buffer)
4012     {
4013         DWORD verdword = msi_version_str_to_dword(buffer);
4014         msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
4015         msi_free(buffer);
4016     }
4017
4018     msi_reg_set_val_dword(hkey, szAssignment, 0);
4019     msi_reg_set_val_dword(hkey, szAdvertiseFlags, 0x184);
4020     msi_reg_set_val_dword(hkey, INSTALLPROPERTY_INSTANCETYPEW, 0);
4021     msi_reg_set_val_str(hkey, szClients, szColon);
4022
4023     hdb = alloc_msihandle(&package->db->hdr);
4024     if (!hdb)
4025         return ERROR_NOT_ENOUGH_MEMORY;
4026
4027     r = MsiGetSummaryInformationW(hdb, NULL, 0, &suminfo);
4028     MsiCloseHandle(hdb);
4029     if (r != ERROR_SUCCESS)
4030         goto done;
4031
4032     size = MAX_PATH;
4033     r = MsiSummaryInfoGetPropertyW(suminfo, PID_REVNUMBER, NULL, NULL,
4034                                    NULL, guids, &size);
4035     if (r != ERROR_SUCCESS)
4036         goto done;
4037
4038     ptr = strchrW(guids, ';');
4039     if (ptr) *ptr = 0;
4040     squash_guid(guids, packcode);
4041     msi_reg_set_val_str(hkey, INSTALLPROPERTY_PACKAGECODEW, packcode);
4042
4043 done:
4044     MsiCloseHandle(suminfo);
4045     return ERROR_SUCCESS;
4046 }
4047
4048 static UINT msi_publish_upgrade_code(MSIPACKAGE *package)
4049 {
4050     UINT r;
4051     HKEY hkey;
4052     LPWSTR upgrade;
4053     WCHAR squashed_pc[SQUISH_GUID_SIZE];
4054
4055     upgrade = msi_dup_property(package->db, szUpgradeCode);
4056     if (!upgrade)
4057         return ERROR_SUCCESS;
4058
4059     if (package->Context == MSIINSTALLCONTEXT_MACHINE)
4060         r = MSIREG_OpenClassesUpgradeCodesKey(upgrade, &hkey, TRUE);
4061     else
4062         r = MSIREG_OpenUserUpgradeCodesKey(upgrade, &hkey, TRUE);
4063
4064     if (r != ERROR_SUCCESS)
4065     {
4066         WARN("failed to open upgrade code key\n");
4067         msi_free(upgrade);
4068         return ERROR_SUCCESS;
4069     }
4070     squash_guid(package->ProductCode, squashed_pc);
4071     msi_reg_set_val_str(hkey, squashed_pc, NULL);
4072     RegCloseKey(hkey);
4073     msi_free(upgrade);
4074     return ERROR_SUCCESS;
4075 }
4076
4077 static BOOL msi_check_publish(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_LOCAL)
4085             return TRUE;
4086     }
4087
4088     return FALSE;
4089 }
4090
4091 static BOOL msi_check_unpublish(MSIPACKAGE *package)
4092 {
4093     MSIFEATURE *feature;
4094
4095     LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4096     {
4097         feature->Action = msi_get_feature_action( package, feature );
4098         if (feature->Action != INSTALLSTATE_ABSENT)
4099             return FALSE;
4100     }
4101
4102     return TRUE;
4103 }
4104
4105 static UINT msi_publish_patches( MSIPACKAGE *package )
4106 {
4107     static const WCHAR szAllPatches[] = {'A','l','l','P','a','t','c','h','e','s',0};
4108     WCHAR patch_squashed[GUID_SIZE];
4109     HKEY patches_key = NULL, product_patches_key = NULL, product_key;
4110     LONG res;
4111     MSIPATCHINFO *patch;
4112     UINT r;
4113     WCHAR *p, *all_patches = NULL;
4114     DWORD len = 0;
4115
4116     r = MSIREG_OpenProductKey( package->ProductCode, NULL, package->Context, &product_key, TRUE );
4117     if (r != ERROR_SUCCESS)
4118         return ERROR_FUNCTION_FAILED;
4119
4120     res = RegCreateKeyExW( product_key, szPatches, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &patches_key, NULL );
4121     if (res != ERROR_SUCCESS)
4122     {
4123         r = ERROR_FUNCTION_FAILED;
4124         goto done;
4125     }
4126
4127     r = MSIREG_OpenUserDataProductPatchesKey( package->ProductCode, package->Context, &product_patches_key, TRUE );
4128     if (r != ERROR_SUCCESS)
4129         goto done;
4130
4131     LIST_FOR_EACH_ENTRY( patch, &package->patches, MSIPATCHINFO, entry )
4132     {
4133         squash_guid( patch->patchcode, patch_squashed );
4134         len += strlenW( patch_squashed ) + 1;
4135     }
4136
4137     p = all_patches = msi_alloc( (len + 1) * sizeof(WCHAR) );
4138     if (!all_patches)
4139         goto done;
4140
4141     LIST_FOR_EACH_ENTRY( patch, &package->patches, MSIPATCHINFO, entry )
4142     {
4143         HKEY patch_key;
4144
4145         squash_guid( patch->patchcode, p );
4146         p += strlenW( p ) + 1;
4147
4148         res = RegSetValueExW( patches_key, patch_squashed, 0, REG_SZ,
4149                               (const BYTE *)patch->transforms,
4150                               (strlenW(patch->transforms) + 1) * sizeof(WCHAR) );
4151         if (res != ERROR_SUCCESS)
4152             goto done;
4153
4154         r = MSIREG_OpenUserDataPatchKey( patch->patchcode, package->Context, &patch_key, TRUE );
4155         if (r != ERROR_SUCCESS)
4156             goto done;
4157
4158         res = RegSetValueExW( patch_key, szLocalPackage, 0, REG_SZ, (const BYTE *)patch->localfile,
4159                               (strlenW( patch->localfile ) + 1) * sizeof(WCHAR) );
4160         RegCloseKey( patch_key );
4161         if (res != ERROR_SUCCESS)
4162             goto done;
4163
4164         if (patch->filename && !CopyFileW( patch->filename, patch->localfile, FALSE ))
4165         {
4166             res = GetLastError();
4167             ERR("Unable to copy patch package %d\n", res);
4168             goto done;
4169         }
4170         res = RegCreateKeyExW( product_patches_key, patch_squashed, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &patch_key, NULL );
4171         if (res != ERROR_SUCCESS)
4172             goto done;
4173
4174         res = RegSetValueExW( patch_key, szState, 0, REG_DWORD, (const BYTE *)&patch->state, sizeof(patch->state) );
4175         RegCloseKey( patch_key );
4176         if (res != ERROR_SUCCESS)
4177             goto done;
4178     }
4179
4180     all_patches[len] = 0;
4181     res = RegSetValueExW( patches_key, szPatches, 0, REG_MULTI_SZ,
4182                           (const BYTE *)all_patches, (len + 1) * sizeof(WCHAR) );
4183     if (res != ERROR_SUCCESS)
4184         goto done;
4185
4186     res = RegSetValueExW( product_patches_key, szAllPatches, 0, REG_MULTI_SZ,
4187                           (const BYTE *)all_patches, (len + 1) * sizeof(WCHAR) );
4188     if (res != ERROR_SUCCESS)
4189         r = ERROR_FUNCTION_FAILED;
4190
4191 done:
4192     RegCloseKey( product_patches_key );
4193     RegCloseKey( patches_key );
4194     RegCloseKey( product_key );
4195     msi_free( all_patches );
4196     return r;
4197 }
4198
4199 static UINT ACTION_PublishProduct(MSIPACKAGE *package)
4200 {
4201     UINT rc;
4202     HKEY hukey = NULL, hudkey = NULL;
4203     MSIRECORD *uirow;
4204
4205     if (!list_empty(&package->patches))
4206     {
4207         rc = msi_publish_patches(package);
4208         if (rc != ERROR_SUCCESS)
4209             goto end;
4210     }
4211
4212     /* FIXME: also need to publish if the product is in advertise mode */
4213     if (!msi_check_publish(package))
4214         return ERROR_SUCCESS;
4215
4216     rc = MSIREG_OpenProductKey(package->ProductCode, NULL, package->Context,
4217                                &hukey, TRUE);
4218     if (rc != ERROR_SUCCESS)
4219         goto end;
4220
4221     rc = MSIREG_OpenUserDataProductKey(package->ProductCode, package->Context,
4222                                        NULL, &hudkey, TRUE);
4223     if (rc != ERROR_SUCCESS)
4224         goto end;
4225
4226     rc = msi_publish_upgrade_code(package);
4227     if (rc != ERROR_SUCCESS)
4228         goto end;
4229
4230     rc = msi_publish_product_properties(package, hukey);
4231     if (rc != ERROR_SUCCESS)
4232         goto end;
4233
4234     rc = msi_publish_sourcelist(package, hukey);
4235     if (rc != ERROR_SUCCESS)
4236         goto end;
4237
4238     rc = msi_publish_icons(package);
4239
4240 end:
4241     uirow = MSI_CreateRecord( 1 );
4242     MSI_RecordSetStringW( uirow, 1, package->ProductCode );
4243     msi_ui_actiondata( package, szPublishProduct, uirow );
4244     msiobj_release( &uirow->hdr );
4245
4246     RegCloseKey(hukey);
4247     RegCloseKey(hudkey);
4248     return rc;
4249 }
4250
4251 static WCHAR *get_ini_file_name( MSIPACKAGE *package, MSIRECORD *row )
4252 {
4253     WCHAR *filename, *ptr, *folder, *ret;
4254     const WCHAR *dirprop;
4255
4256     filename = msi_dup_record_field( row, 2 );
4257     if (filename && (ptr = strchrW( filename, '|' )))
4258         ptr++;
4259     else
4260         ptr = filename;
4261
4262     dirprop = MSI_RecordGetString( row, 3 );
4263     if (dirprop)
4264     {
4265         folder = strdupW( msi_get_target_folder( package, dirprop ) );
4266         if (!folder) folder = msi_dup_property( package->db, dirprop );
4267     }
4268     else
4269         folder = msi_dup_property( package->db, szWindowsFolder );
4270
4271     if (!folder)
4272     {
4273         ERR("Unable to resolve folder %s\n", debugstr_w(dirprop));
4274         msi_free( filename );
4275         return NULL;
4276     }
4277
4278     ret = msi_build_directory_name( 2, folder, ptr );
4279
4280     msi_free( filename );
4281     msi_free( folder );
4282     return ret;
4283 }
4284
4285 static UINT ITERATE_WriteIniValues(MSIRECORD *row, LPVOID param)
4286 {
4287     MSIPACKAGE *package = param;
4288     LPCWSTR component, section, key, value, identifier;
4289     LPWSTR deformated_section, deformated_key, deformated_value, fullname;
4290     MSIRECORD * uirow;
4291     INT action;
4292     MSICOMPONENT *comp;
4293
4294     component = MSI_RecordGetString(row, 8);
4295     comp = msi_get_loaded_component(package,component);
4296     if (!comp)
4297         return ERROR_SUCCESS;
4298
4299     comp->Action = msi_get_component_action( package, comp );
4300     if (comp->Action != INSTALLSTATE_LOCAL)
4301     {
4302         TRACE("component not scheduled for installation %s\n", debugstr_w(component));
4303         return ERROR_SUCCESS;
4304     }
4305
4306     identifier = MSI_RecordGetString(row,1); 
4307     section = MSI_RecordGetString(row,4);
4308     key = MSI_RecordGetString(row,5);
4309     value = MSI_RecordGetString(row,6);
4310     action = MSI_RecordGetInteger(row,7);
4311
4312     deformat_string(package,section,&deformated_section);
4313     deformat_string(package,key,&deformated_key);
4314     deformat_string(package,value,&deformated_value);
4315
4316     fullname = get_ini_file_name(package, row);
4317
4318     if (action == 0)
4319     {
4320         TRACE("Adding value %s to section %s in %s\n",
4321                 debugstr_w(deformated_key), debugstr_w(deformated_section),
4322                 debugstr_w(fullname));
4323         WritePrivateProfileStringW(deformated_section, deformated_key,
4324                                    deformated_value, fullname);
4325     }
4326     else if (action == 1)
4327     {
4328         WCHAR returned[10];
4329         GetPrivateProfileStringW(deformated_section, deformated_key, NULL,
4330                                  returned, 10, fullname);
4331         if (returned[0] == 0)
4332         {
4333             TRACE("Adding value %s to section %s in %s\n",
4334                     debugstr_w(deformated_key), debugstr_w(deformated_section),
4335                     debugstr_w(fullname));
4336
4337             WritePrivateProfileStringW(deformated_section, deformated_key,
4338                                        deformated_value, fullname);
4339         }
4340     }
4341     else if (action == 3)
4342         FIXME("Append to existing section not yet implemented\n");
4343
4344     uirow = MSI_CreateRecord(4);
4345     MSI_RecordSetStringW(uirow,1,identifier);
4346     MSI_RecordSetStringW(uirow,2,deformated_section);
4347     MSI_RecordSetStringW(uirow,3,deformated_key);
4348     MSI_RecordSetStringW(uirow,4,deformated_value);
4349     msi_ui_actiondata( package, szWriteIniValues, uirow );
4350     msiobj_release( &uirow->hdr );
4351
4352     msi_free(fullname);
4353     msi_free(deformated_key);
4354     msi_free(deformated_value);
4355     msi_free(deformated_section);
4356     return ERROR_SUCCESS;
4357 }
4358
4359 static UINT ACTION_WriteIniValues(MSIPACKAGE *package)
4360 {
4361     static const WCHAR query[] = {
4362         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4363         '`','I','n','i','F','i','l','e','`',0};
4364     MSIQUERY *view;
4365     UINT rc;
4366
4367     rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4368     if (rc != ERROR_SUCCESS)
4369         return ERROR_SUCCESS;
4370
4371     rc = MSI_IterateRecords(view, NULL, ITERATE_WriteIniValues, package);
4372     msiobj_release(&view->hdr);
4373     return rc;
4374 }
4375
4376 static UINT ITERATE_RemoveIniValuesOnUninstall( MSIRECORD *row, LPVOID param )
4377 {
4378     MSIPACKAGE *package = param;
4379     LPCWSTR component, section, key, value, identifier;
4380     LPWSTR deformated_section, deformated_key, deformated_value, filename;
4381     MSICOMPONENT *comp;
4382     MSIRECORD *uirow;
4383     INT action;
4384
4385     component = MSI_RecordGetString( row, 8 );
4386     comp = msi_get_loaded_component( package, component );
4387     if (!comp)
4388         return ERROR_SUCCESS;
4389
4390     comp->Action = msi_get_component_action( package, comp );
4391     if (comp->Action != INSTALLSTATE_ABSENT)
4392     {
4393         TRACE("component not scheduled for removal %s\n", debugstr_w(component));
4394         return ERROR_SUCCESS;
4395     }
4396
4397     identifier = MSI_RecordGetString( row, 1 );
4398     section = MSI_RecordGetString( row, 4 );
4399     key = MSI_RecordGetString( row, 5 );
4400     value = MSI_RecordGetString( row, 6 );
4401     action = MSI_RecordGetInteger( row, 7 );
4402
4403     deformat_string( package, section, &deformated_section );
4404     deformat_string( package, key, &deformated_key );
4405     deformat_string( package, value, &deformated_value );
4406
4407     if (action == msidbIniFileActionAddLine || action == msidbIniFileActionCreateLine)
4408     {
4409         filename = get_ini_file_name( package, row );
4410
4411         TRACE("Removing key %s from section %s in %s\n",
4412                debugstr_w(deformated_key), debugstr_w(deformated_section), debugstr_w(filename));
4413
4414         if (!WritePrivateProfileStringW( deformated_section, deformated_key, NULL, filename ))
4415         {
4416             WARN("Unable to remove key %u\n", GetLastError());
4417         }
4418         msi_free( filename );
4419     }
4420     else
4421         FIXME("Unsupported action %d\n", action);
4422
4423
4424     uirow = MSI_CreateRecord( 4 );
4425     MSI_RecordSetStringW( uirow, 1, identifier );
4426     MSI_RecordSetStringW( uirow, 2, deformated_section );
4427     MSI_RecordSetStringW( uirow, 3, deformated_key );
4428     MSI_RecordSetStringW( uirow, 4, deformated_value );
4429     msi_ui_actiondata( package, szRemoveIniValues, uirow );
4430     msiobj_release( &uirow->hdr );
4431
4432     msi_free( deformated_key );
4433     msi_free( deformated_value );
4434     msi_free( deformated_section );
4435     return ERROR_SUCCESS;
4436 }
4437
4438 static UINT ITERATE_RemoveIniValuesOnInstall( MSIRECORD *row, LPVOID param )
4439 {
4440     MSIPACKAGE *package = param;
4441     LPCWSTR component, section, key, value, identifier;
4442     LPWSTR deformated_section, deformated_key, deformated_value, filename;
4443     MSICOMPONENT *comp;
4444     MSIRECORD *uirow;
4445     INT action;
4446
4447     component = MSI_RecordGetString( row, 8 );
4448     comp = msi_get_loaded_component( package, component );
4449     if (!comp)
4450         return ERROR_SUCCESS;
4451
4452     comp->Action = msi_get_component_action( package, comp );
4453     if (comp->Action != INSTALLSTATE_LOCAL)
4454     {
4455         TRACE("component not scheduled for installation %s\n", debugstr_w(component));
4456         return ERROR_SUCCESS;
4457     }
4458
4459     identifier = MSI_RecordGetString( row, 1 );
4460     section = MSI_RecordGetString( row, 4 );
4461     key = MSI_RecordGetString( row, 5 );
4462     value = MSI_RecordGetString( row, 6 );
4463     action = MSI_RecordGetInteger( row, 7 );
4464
4465     deformat_string( package, section, &deformated_section );
4466     deformat_string( package, key, &deformated_key );
4467     deformat_string( package, value, &deformated_value );
4468
4469     if (action == msidbIniFileActionRemoveLine)
4470     {
4471         filename = get_ini_file_name( package, row );
4472
4473         TRACE("Removing key %s from section %s in %s\n",
4474                debugstr_w(deformated_key), debugstr_w(deformated_section), debugstr_w(filename));
4475
4476         if (!WritePrivateProfileStringW( deformated_section, deformated_key, NULL, filename ))
4477         {
4478             WARN("Unable to remove key %u\n", GetLastError());
4479         }
4480         msi_free( filename );
4481     }
4482     else
4483         FIXME("Unsupported action %d\n", action);
4484
4485     uirow = MSI_CreateRecord( 4 );
4486     MSI_RecordSetStringW( uirow, 1, identifier );
4487     MSI_RecordSetStringW( uirow, 2, deformated_section );
4488     MSI_RecordSetStringW( uirow, 3, deformated_key );
4489     MSI_RecordSetStringW( uirow, 4, deformated_value );
4490     msi_ui_actiondata( package, szRemoveIniValues, uirow );
4491     msiobj_release( &uirow->hdr );
4492
4493     msi_free( deformated_key );
4494     msi_free( deformated_value );
4495     msi_free( deformated_section );
4496     return ERROR_SUCCESS;
4497 }
4498
4499 static UINT ACTION_RemoveIniValues( MSIPACKAGE *package )
4500 {
4501     static const WCHAR query[] = {
4502         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4503         '`','I','n','i','F','i','l','e','`',0};
4504     static const WCHAR remove_query[] = {
4505         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4506         '`','R','e','m','o','v','e','I','n','i','F','i','l','e','`',0};
4507     MSIQUERY *view;
4508     UINT rc;
4509
4510     rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4511     if (rc == ERROR_SUCCESS)
4512     {
4513         rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveIniValuesOnUninstall, package );
4514         msiobj_release( &view->hdr );
4515         if (rc != ERROR_SUCCESS)
4516             return rc;
4517     }
4518     rc = MSI_DatabaseOpenViewW( package->db, remove_query, &view );
4519     if (rc == ERROR_SUCCESS)
4520     {
4521         rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveIniValuesOnInstall, package );
4522         msiobj_release( &view->hdr );
4523         if (rc != ERROR_SUCCESS)
4524             return rc;
4525     }
4526     return ERROR_SUCCESS;
4527 }
4528
4529 static void register_dll( const WCHAR *dll, BOOL unregister )
4530 {
4531     HMODULE hmod;
4532
4533     hmod = LoadLibraryExW( dll, 0, LOAD_WITH_ALTERED_SEARCH_PATH );
4534     if (hmod)
4535     {
4536         HRESULT (WINAPI *func_ptr)( void );
4537         const char *func = unregister ? "DllUnregisterServer" : "DllRegisterServer";
4538
4539         func_ptr = (void *)GetProcAddress( hmod, func );
4540         if (func_ptr)
4541         {
4542             HRESULT hr = func_ptr();
4543             if (FAILED( hr ))
4544                 WARN("failed to register dll 0x%08x\n", hr);
4545         }
4546         else
4547             WARN("entry point %s not found\n", func);
4548         FreeLibrary( hmod );
4549         return;
4550     }
4551     WARN("failed to load library %u\n", GetLastError());
4552 }
4553
4554 static UINT ITERATE_SelfRegModules(MSIRECORD *row, LPVOID param)
4555 {
4556     MSIPACKAGE *package = param;
4557     LPCWSTR filename;
4558     MSIFILE *file;
4559     MSIRECORD *uirow;
4560
4561     filename = MSI_RecordGetString( row, 1 );
4562     file = msi_get_loaded_file( package, filename );
4563     if (!file)
4564     {
4565         WARN("unable to find file %s\n", debugstr_w(filename));
4566         return ERROR_SUCCESS;
4567     }
4568     file->Component->Action = msi_get_component_action( package, file->Component );
4569     if (file->Component->Action != INSTALLSTATE_LOCAL)
4570     {
4571         TRACE("component not scheduled for installation %s\n", debugstr_w(file->Component->Component));
4572         return ERROR_SUCCESS;
4573     }
4574
4575     TRACE("Registering %s\n", debugstr_w( file->TargetPath ));
4576     register_dll( file->TargetPath, FALSE );
4577
4578     uirow = MSI_CreateRecord( 2 );
4579     MSI_RecordSetStringW( uirow, 1, file->File );
4580     MSI_RecordSetStringW( uirow, 2, file->Component->Directory );
4581     msi_ui_actiondata( package, szSelfRegModules, uirow );
4582     msiobj_release( &uirow->hdr );
4583
4584     return ERROR_SUCCESS;
4585 }
4586
4587 static UINT ACTION_SelfRegModules(MSIPACKAGE *package)
4588 {
4589     static const WCHAR query[] = {
4590         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4591         '`','S','e','l','f','R','e','g','`',0};
4592     MSIQUERY *view;
4593     UINT rc;
4594
4595     rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4596     if (rc != ERROR_SUCCESS)
4597         return ERROR_SUCCESS;
4598
4599     rc = MSI_IterateRecords(view, NULL, ITERATE_SelfRegModules, package);
4600     msiobj_release(&view->hdr);
4601     return rc;
4602 }
4603
4604 static UINT ITERATE_SelfUnregModules( MSIRECORD *row, LPVOID param )
4605 {
4606     MSIPACKAGE *package = param;
4607     LPCWSTR filename;
4608     MSIFILE *file;
4609     MSIRECORD *uirow;
4610
4611     filename = MSI_RecordGetString( row, 1 );
4612     file = msi_get_loaded_file( package, filename );
4613     if (!file)
4614     {
4615         WARN("unable to find file %s\n", debugstr_w(filename));
4616         return ERROR_SUCCESS;
4617     }
4618     file->Component->Action = msi_get_component_action( package, file->Component );
4619     if (file->Component->Action != INSTALLSTATE_ABSENT)
4620     {
4621         TRACE("component not scheduled for removal %s\n", debugstr_w(file->Component->Component));
4622         return ERROR_SUCCESS;
4623     }
4624
4625     TRACE("Unregistering %s\n", debugstr_w( file->TargetPath ));
4626     register_dll( file->TargetPath, TRUE );
4627
4628     uirow = MSI_CreateRecord( 2 );
4629     MSI_RecordSetStringW( uirow, 1, file->File );
4630     MSI_RecordSetStringW( uirow, 2, file->Component->Directory );
4631     msi_ui_actiondata( package, szSelfUnregModules, uirow );
4632     msiobj_release( &uirow->hdr );
4633
4634     return ERROR_SUCCESS;
4635 }
4636
4637 static UINT ACTION_SelfUnregModules( MSIPACKAGE *package )
4638 {
4639     static const WCHAR query[] = {
4640         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4641         '`','S','e','l','f','R','e','g','`',0};
4642     MSIQUERY *view;
4643     UINT rc;
4644
4645     rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4646     if (rc != ERROR_SUCCESS)
4647         return ERROR_SUCCESS;
4648
4649     rc = MSI_IterateRecords( view, NULL, ITERATE_SelfUnregModules, package );
4650     msiobj_release( &view->hdr );
4651     return rc;
4652 }
4653
4654 static UINT ACTION_PublishFeatures(MSIPACKAGE *package)
4655 {
4656     MSIFEATURE *feature;
4657     UINT rc;
4658     HKEY hkey = NULL, userdata = NULL;
4659
4660     if (!msi_check_publish(package))
4661         return ERROR_SUCCESS;
4662
4663     rc = MSIREG_OpenFeaturesKey(package->ProductCode, package->Context,
4664                                 &hkey, TRUE);
4665     if (rc != ERROR_SUCCESS)
4666         goto end;
4667
4668     rc = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, package->Context,
4669                                         &userdata, TRUE);
4670     if (rc != ERROR_SUCCESS)
4671         goto end;
4672
4673     /* here the guids are base 85 encoded */
4674     LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
4675     {
4676         ComponentList *cl;
4677         LPWSTR data = NULL;
4678         GUID clsid;
4679         INT size;
4680         BOOL absent = FALSE;
4681         MSIRECORD *uirow;
4682
4683         if (feature->Action != INSTALLSTATE_LOCAL &&
4684             feature->Action != INSTALLSTATE_SOURCE &&
4685             feature->Action != INSTALLSTATE_ADVERTISED) absent = TRUE;
4686
4687         size = 1;
4688         LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
4689         {
4690             size += 21;
4691         }
4692         if (feature->Feature_Parent)
4693             size += strlenW( feature->Feature_Parent )+2;
4694
4695         data = msi_alloc(size * sizeof(WCHAR));
4696
4697         data[0] = 0;
4698         LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
4699         {
4700             MSICOMPONENT* component = cl->component;
4701             WCHAR buf[21];
4702
4703             buf[0] = 0;
4704             if (component->ComponentId)
4705             {
4706                 TRACE("From %s\n",debugstr_w(component->ComponentId));
4707                 CLSIDFromString(component->ComponentId, &clsid);
4708                 encode_base85_guid(&clsid,buf);
4709                 TRACE("to %s\n",debugstr_w(buf));
4710                 strcatW(data,buf);
4711             }
4712         }
4713
4714         if (feature->Feature_Parent)
4715         {
4716             static const WCHAR sep[] = {'\2',0};
4717             strcatW(data,sep);
4718             strcatW(data,feature->Feature_Parent);
4719         }
4720
4721         msi_reg_set_val_str( userdata, feature->Feature, data );
4722         msi_free(data);
4723
4724         size = 0;
4725         if (feature->Feature_Parent)
4726             size = strlenW(feature->Feature_Parent)*sizeof(WCHAR);
4727         if (!absent)
4728         {
4729             size += sizeof(WCHAR);
4730             RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
4731                            (const BYTE*)(feature->Feature_Parent ? feature->Feature_Parent : szEmpty),size);
4732         }
4733         else
4734         {
4735             size += 2*sizeof(WCHAR);
4736             data = msi_alloc(size);
4737             data[0] = 0x6;
4738             data[1] = 0;
4739             if (feature->Feature_Parent)
4740                 strcpyW( &data[1], feature->Feature_Parent );
4741             RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
4742                        (LPBYTE)data,size);
4743             msi_free(data);
4744         }
4745
4746         /* the UI chunk */
4747         uirow = MSI_CreateRecord( 1 );
4748         MSI_RecordSetStringW( uirow, 1, feature->Feature );
4749         msi_ui_actiondata( package, szPublishFeatures, uirow );
4750         msiobj_release( &uirow->hdr );
4751         /* FIXME: call msi_ui_progress? */
4752     }
4753
4754 end:
4755     RegCloseKey(hkey);
4756     RegCloseKey(userdata);
4757     return rc;
4758 }
4759
4760 static UINT msi_unpublish_feature(MSIPACKAGE *package, MSIFEATURE *feature)
4761 {
4762     UINT r;
4763     HKEY hkey;
4764     MSIRECORD *uirow;
4765
4766     TRACE("unpublishing feature %s\n", debugstr_w(feature->Feature));
4767
4768     r = MSIREG_OpenFeaturesKey(package->ProductCode, package->Context,
4769                                &hkey, FALSE);
4770     if (r == ERROR_SUCCESS)
4771     {
4772         RegDeleteValueW(hkey, feature->Feature);
4773         RegCloseKey(hkey);
4774     }
4775
4776     r = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, package->Context,
4777                                        &hkey, FALSE);
4778     if (r == ERROR_SUCCESS)
4779     {
4780         RegDeleteValueW(hkey, feature->Feature);
4781         RegCloseKey(hkey);
4782     }
4783
4784     uirow = MSI_CreateRecord( 1 );
4785     MSI_RecordSetStringW( uirow, 1, feature->Feature );
4786     msi_ui_actiondata( package, szUnpublishFeatures, uirow );
4787     msiobj_release( &uirow->hdr );
4788
4789     return ERROR_SUCCESS;
4790 }
4791
4792 static UINT ACTION_UnpublishFeatures(MSIPACKAGE *package)
4793 {
4794     MSIFEATURE *feature;
4795
4796     if (!msi_check_unpublish(package))
4797         return ERROR_SUCCESS;
4798
4799     LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4800     {
4801         msi_unpublish_feature(package, feature);
4802     }
4803
4804     return ERROR_SUCCESS;
4805 }
4806
4807 static UINT msi_publish_install_properties(MSIPACKAGE *package, HKEY hkey)
4808 {
4809     SYSTEMTIME systime;
4810     DWORD size, langid;
4811     WCHAR date[9], *val, *buffer;
4812     const WCHAR *prop, *key;
4813
4814     static const WCHAR date_fmt[] = {'%','i','%','0','2','i','%','0','2','i',0};
4815     static const WCHAR modpath_fmt[] =
4816         {'M','s','i','E','x','e','c','.','e','x','e',' ',
4817          '/','I','[','P','r','o','d','u','c','t','C','o','d','e',']',0};
4818     static const WCHAR szModifyPath[] =
4819         {'M','o','d','i','f','y','P','a','t','h',0};
4820     static const WCHAR szUninstallString[] =
4821         {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0};
4822     static const WCHAR szEstimatedSize[] =
4823         {'E','s','t','i','m','a','t','e','d','S','i','z','e',0};
4824     static const WCHAR szDisplayVersion[] =
4825         {'D','i','s','p','l','a','y','V','e','r','s','i','o','n',0};
4826     static const WCHAR szInstallSource[] =
4827         {'I','n','s','t','a','l','l','S','o','u','r','c','e',0};
4828     static const WCHAR szARPAUTHORIZEDCDFPREFIX[] =
4829         {'A','R','P','A','U','T','H','O','R','I','Z','E','D','C','D','F','P','R','E','F','I','X',0};
4830     static const WCHAR szAuthorizedCDFPrefix[] =
4831         {'A','u','t','h','o','r','i','z','e','d','C','D','F','P','r','e','f','i','x',0};
4832     static const WCHAR szARPCONTACT[] =
4833         {'A','R','P','C','O','N','T','A','C','T',0};
4834     static const WCHAR szContact[] =
4835         {'C','o','n','t','a','c','t',0};
4836     static const WCHAR szARPCOMMENTS[] =
4837         {'A','R','P','C','O','M','M','E','N','T','S',0};
4838     static const WCHAR szComments[] =
4839         {'C','o','m','m','e','n','t','s',0};
4840     static const WCHAR szProductName[] =
4841         {'P','r','o','d','u','c','t','N','a','m','e',0};
4842     static const WCHAR szDisplayName[] =
4843         {'D','i','s','p','l','a','y','N','a','m','e',0};
4844     static const WCHAR szARPHELPLINK[] =
4845         {'A','R','P','H','E','L','P','L','I','N','K',0};
4846     static const WCHAR szHelpLink[] =
4847         {'H','e','l','p','L','i','n','k',0};
4848     static const WCHAR szARPHELPTELEPHONE[] =
4849         {'A','R','P','H','E','L','P','T','E','L','E','P','H','O','N','E',0};
4850     static const WCHAR szHelpTelephone[] =
4851         {'H','e','l','p','T','e','l','e','p','h','o','n','e',0};
4852     static const WCHAR szARPINSTALLLOCATION[] =
4853         {'A','R','P','I','N','S','T','A','L','L','L','O','C','A','T','I','O','N',0};
4854     static const WCHAR szManufacturer[] =
4855         {'M','a','n','u','f','a','c','t','u','r','e','r',0};
4856     static const WCHAR szPublisher[] =
4857         {'P','u','b','l','i','s','h','e','r',0};
4858     static const WCHAR szARPREADME[] =
4859         {'A','R','P','R','E','A','D','M','E',0};
4860     static const WCHAR szReadme[] =
4861         {'R','e','a','d','M','e',0};
4862     static const WCHAR szARPSIZE[] =
4863         {'A','R','P','S','I','Z','E',0};
4864     static const WCHAR szSize[] =
4865         {'S','i','z','e',0};
4866     static const WCHAR szARPURLINFOABOUT[] =
4867         {'A','R','P','U','R','L','I','N','F','O','A','B','O','U','T',0};
4868     static const WCHAR szURLInfoAbout[] =
4869         {'U','R','L','I','n','f','o','A','b','o','u','t',0};
4870     static const WCHAR szARPURLUPDATEINFO[] =
4871         {'A','R','P','U','R','L','U','P','D','A','T','E','I','N','F','O',0};
4872     static const WCHAR szURLUpdateInfo[] =
4873         {'U','R','L','U','p','d','a','t','e','I','n','f','o',0};
4874     static const WCHAR szARPSYSTEMCOMPONENT[] =
4875         {'A','R','P','S','Y','S','T','E','M','C','O','M','P','O','N','E','N','T',0};
4876     static const WCHAR szSystemComponent[] =
4877         {'S','y','s','t','e','m','C','o','m','p','o','n','e','n','t',0};
4878
4879     static const WCHAR *propval[] = {
4880         szARPAUTHORIZEDCDFPREFIX, szAuthorizedCDFPrefix,
4881         szARPCONTACT,             szContact,
4882         szARPCOMMENTS,            szComments,
4883         szProductName,            szDisplayName,
4884         szARPHELPLINK,            szHelpLink,
4885         szARPHELPTELEPHONE,       szHelpTelephone,
4886         szARPINSTALLLOCATION,     szInstallLocation,
4887         szSourceDir,              szInstallSource,
4888         szManufacturer,           szPublisher,
4889         szARPREADME,              szReadme,
4890         szARPSIZE,                szSize,
4891         szARPURLINFOABOUT,        szURLInfoAbout,
4892         szARPURLUPDATEINFO,       szURLUpdateInfo,
4893         NULL
4894     };
4895     const WCHAR **p = propval;
4896
4897     while (*p)
4898     {
4899         prop = *p++;
4900         key = *p++;
4901         val = msi_dup_property(package->db, prop);
4902         msi_reg_set_val_str(hkey, key, val);
4903         msi_free(val);
4904     }
4905
4906     msi_reg_set_val_dword(hkey, szWindowsInstaller, 1);
4907     if (msi_get_property_int( package->db, szARPSYSTEMCOMPONENT, 0 ))
4908     {
4909         msi_reg_set_val_dword( hkey, szSystemComponent, 1 );
4910     }
4911     size = deformat_string(package, modpath_fmt, &buffer);
4912     RegSetValueExW(hkey, szModifyPath, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
4913     RegSetValueExW(hkey, szUninstallString, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
4914     msi_free(buffer);
4915
4916     /* FIXME: Write real Estimated Size when we have it */
4917     msi_reg_set_val_dword(hkey, szEstimatedSize, 0);
4918
4919     GetLocalTime(&systime);
4920     sprintfW(date, date_fmt, systime.wYear, systime.wMonth, systime.wDay);
4921     msi_reg_set_val_str(hkey, INSTALLPROPERTY_INSTALLDATEW, date);
4922
4923     langid = msi_get_property_int(package->db, szProductLanguage, 0);
4924     msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
4925
4926     buffer = msi_dup_property(package->db, szProductVersion);
4927     msi_reg_set_val_str(hkey, szDisplayVersion, buffer);
4928     if (buffer)
4929     {
4930         DWORD verdword = msi_version_str_to_dword(buffer);
4931
4932         msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
4933         msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMAJORW, verdword >> 24);
4934         msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMINORW, (verdword >> 16) & 0xFF);
4935         msi_free(buffer);
4936     }
4937
4938     return ERROR_SUCCESS;
4939 }
4940
4941 static UINT ACTION_RegisterProduct(MSIPACKAGE *package)
4942 {
4943     WCHAR squashed_pc[SQUISH_GUID_SIZE];
4944     MSIRECORD *uirow;
4945     LPWSTR upgrade_code;
4946     HKEY hkey, props, upgrade_key;
4947     UINT rc;
4948
4949     /* FIXME: also need to publish if the product is in advertise mode */
4950     if (!msi_check_publish(package))
4951         return ERROR_SUCCESS;
4952
4953     rc = MSIREG_OpenUninstallKey(package->ProductCode, package->platform, &hkey, TRUE);
4954     if (rc != ERROR_SUCCESS)
4955         return rc;
4956
4957     rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context, NULL, &props, TRUE);
4958     if (rc != ERROR_SUCCESS)
4959         goto done;
4960
4961     rc = msi_publish_install_properties(package, hkey);
4962     if (rc != ERROR_SUCCESS)
4963         goto done;
4964
4965     rc = msi_publish_install_properties(package, props);
4966     if (rc != ERROR_SUCCESS)
4967         goto done;
4968
4969     upgrade_code = msi_dup_property(package->db, szUpgradeCode);
4970     if (upgrade_code)
4971     {
4972         rc = MSIREG_OpenUpgradeCodesKey( upgrade_code, &upgrade_key, TRUE );
4973         if (rc == ERROR_SUCCESS)
4974         {
4975             squash_guid( package->ProductCode, squashed_pc );
4976             msi_reg_set_val_str( upgrade_key, squashed_pc, NULL );
4977             RegCloseKey( upgrade_key );
4978         }
4979         msi_free( upgrade_code );
4980     }
4981     msi_reg_set_val_str( props, INSTALLPROPERTY_LOCALPACKAGEW, package->localfile );
4982     package->delete_on_close = FALSE;
4983
4984 done:
4985     uirow = MSI_CreateRecord( 1 );
4986     MSI_RecordSetStringW( uirow, 1, package->ProductCode );
4987     msi_ui_actiondata( package, szRegisterProduct, uirow );
4988     msiobj_release( &uirow->hdr );
4989
4990     RegCloseKey(hkey);
4991     return ERROR_SUCCESS;
4992 }
4993
4994 static UINT ACTION_InstallExecute(MSIPACKAGE *package)
4995 {
4996     return execute_script(package, SCRIPT_INSTALL);
4997 }
4998
4999 static UINT ITERATE_UnpublishIcon( MSIRECORD *row, LPVOID param )
5000 {
5001     MSIPACKAGE *package = param;
5002     const WCHAR *icon = MSI_RecordGetString( row, 1 );
5003     WCHAR *p, *icon_path;
5004
5005     if (!icon) return ERROR_SUCCESS;
5006     if ((icon_path = msi_build_icon_path( package, icon )))
5007     {
5008         TRACE("removing icon file %s\n", debugstr_w(icon_path));
5009         DeleteFileW( icon_path );
5010         if ((p = strrchrW( icon_path, '\\' )))
5011         {
5012             *p = 0;
5013             RemoveDirectoryW( icon_path );
5014         }
5015         msi_free( icon_path );
5016     }
5017     return ERROR_SUCCESS;
5018 }
5019
5020 static UINT msi_unpublish_icons( MSIPACKAGE *package )
5021 {
5022     static const WCHAR query[]= {
5023         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','I','c','o','n','`',0};
5024     MSIQUERY *view;
5025     UINT r;
5026
5027     r = MSI_DatabaseOpenViewW( package->db, query, &view );
5028     if (r == ERROR_SUCCESS)
5029     {
5030         r = MSI_IterateRecords( view, NULL, ITERATE_UnpublishIcon, package );
5031         msiobj_release( &view->hdr );
5032         if (r != ERROR_SUCCESS)
5033             return r;
5034     }
5035     return ERROR_SUCCESS;
5036 }
5037
5038 static UINT msi_unpublish_product( MSIPACKAGE *package, const WCHAR *remove )
5039 {
5040     static const WCHAR szUpgradeCode[] = {'U','p','g','r','a','d','e','C','o','d','e',0};
5041     WCHAR *upgrade, **features;
5042     BOOL full_uninstall = TRUE;
5043     MSIFEATURE *feature;
5044     MSIPATCHINFO *patch;
5045     UINT i;
5046
5047     LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
5048     {
5049         if (feature->Action == INSTALLSTATE_LOCAL) full_uninstall = FALSE;
5050     }
5051     features = msi_split_string( remove, ',' );
5052     for (i = 0; features && features[i]; i++)
5053     {
5054         if (!strcmpW( features[i], szAll )) full_uninstall = TRUE;
5055     }
5056     msi_free(features);
5057
5058     if (!full_uninstall)
5059         return ERROR_SUCCESS;
5060
5061     MSIREG_DeleteProductKey(package->ProductCode);
5062     MSIREG_DeleteUserDataProductKey(package->ProductCode);
5063     MSIREG_DeleteUninstallKey(package->ProductCode, package->platform);
5064
5065     MSIREG_DeleteLocalClassesProductKey(package->ProductCode);
5066     MSIREG_DeleteLocalClassesFeaturesKey(package->ProductCode);
5067     MSIREG_DeleteUserProductKey(package->ProductCode);
5068     MSIREG_DeleteUserFeaturesKey(package->ProductCode);
5069
5070     upgrade = msi_dup_property(package->db, szUpgradeCode);
5071     if (upgrade)
5072     {
5073         MSIREG_DeleteUserUpgradeCodesKey(upgrade);
5074         MSIREG_DeleteClassesUpgradeCodesKey(upgrade);
5075         msi_free(upgrade);
5076     }
5077
5078     LIST_FOR_EACH_ENTRY(patch, &package->patches, MSIPATCHINFO, entry)
5079     {
5080         MSIREG_DeleteUserDataPatchKey(patch->patchcode, package->Context);
5081         if (!strcmpW( package->ProductCode, patch->products ))
5082         {
5083             TRACE("removing local patch package %s\n", debugstr_w(patch->localfile));
5084             patch->delete_on_close = TRUE;
5085         }
5086         /* FIXME: remove local patch package if this is the last product */
5087     }
5088     TRACE("removing local package %s\n", debugstr_w(package->localfile));
5089     package->delete_on_close = TRUE;
5090
5091     msi_unpublish_icons( package );
5092     return ERROR_SUCCESS;
5093 }
5094
5095 static UINT ACTION_InstallFinalize(MSIPACKAGE *package)
5096 {
5097     UINT rc;
5098     WCHAR *remove;
5099
5100     /* turn off scheduling */
5101     package->script->CurrentlyScripting= FALSE;
5102
5103     /* first do the same as an InstallExecute */
5104     rc = ACTION_InstallExecute(package);
5105     if (rc != ERROR_SUCCESS)
5106         return rc;
5107
5108     /* then handle commit actions */
5109     rc = execute_script(package, SCRIPT_COMMIT);
5110     if (rc != ERROR_SUCCESS)
5111         return rc;
5112
5113     remove = msi_dup_property(package->db, szRemove);
5114     rc = msi_unpublish_product(package, remove);
5115     msi_free(remove);
5116     return rc;
5117 }
5118
5119 UINT ACTION_ForceReboot(MSIPACKAGE *package)
5120 {
5121     static const WCHAR RunOnce[] = {
5122     'S','o','f','t','w','a','r','e','\\',
5123     'M','i','c','r','o','s','o','f','t','\\',
5124     'W','i','n','d','o','w','s','\\',
5125     'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
5126     'R','u','n','O','n','c','e',0};
5127     static const WCHAR InstallRunOnce[] = {
5128     'S','o','f','t','w','a','r','e','\\',
5129     'M','i','c','r','o','s','o','f','t','\\',
5130     'W','i','n','d','o','w','s','\\',
5131     'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
5132     'I','n','s','t','a','l','l','e','r','\\',
5133     'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
5134
5135     static const WCHAR msiexec_fmt[] = {
5136     '%','s',
5137     '\\','M','s','i','E','x','e','c','.','e','x','e',' ','/','@',' ',
5138     '\"','%','s','\"',0};
5139     static const WCHAR install_fmt[] = {
5140     '/','I',' ','\"','%','s','\"',' ',
5141     'A','F','T','E','R','R','E','B','O','O','T','=','1',' ',
5142     'R','U','N','O','N','C','E','E','N','T','R','Y','=','\"','%','s','\"',0};
5143     WCHAR buffer[256], sysdir[MAX_PATH];
5144     HKEY hkey;
5145     WCHAR squished_pc[100];
5146
5147     squash_guid(package->ProductCode,squished_pc);
5148
5149     GetSystemDirectoryW(sysdir, sizeof(sysdir)/sizeof(sysdir[0]));
5150     RegCreateKeyW(HKEY_LOCAL_MACHINE,RunOnce,&hkey);
5151     snprintfW(buffer,sizeof(buffer)/sizeof(buffer[0]),msiexec_fmt,sysdir,
5152      squished_pc);
5153
5154     msi_reg_set_val_str( hkey, squished_pc, buffer );
5155     RegCloseKey(hkey);
5156
5157     TRACE("Reboot command %s\n",debugstr_w(buffer));
5158
5159     RegCreateKeyW(HKEY_LOCAL_MACHINE,InstallRunOnce,&hkey);
5160     sprintfW(buffer,install_fmt,package->ProductCode,squished_pc);
5161
5162     msi_reg_set_val_str( hkey, squished_pc, buffer );
5163     RegCloseKey(hkey);
5164
5165     return ERROR_INSTALL_SUSPEND;
5166 }
5167
5168 WCHAR *msi_build_error_string( MSIPACKAGE *package, UINT error, DWORD count, ... )
5169 {
5170     static const WCHAR query[] =
5171         {'S','E','L','E','C','T',' ','`','M','e','s','s','a','g','e','`',' ',
5172          'F','R','O','M',' ','`','E','r','r','o','r','`',' ','W','H','E','R','E',' ',
5173          '`','E','r','r','o','r','`',' ','=',' ','%','i',0};
5174     MSIRECORD *rec, *row;
5175     DWORD i, size = 0;
5176     va_list va;
5177     const WCHAR *str;
5178     WCHAR *data;
5179
5180     if (!(row = MSI_QueryGetRecord( package->db, query, error ))) return 0;
5181
5182     rec = MSI_CreateRecord( count + 2 );
5183     str = MSI_RecordGetString( row, 1 );
5184     MSI_RecordSetStringW( rec, 0, str );
5185     msiobj_release( &row->hdr );
5186     MSI_RecordSetInteger( rec, 1, error );
5187
5188     va_start( va, count );
5189     for (i = 0; i < count; i++)
5190     {
5191         str = va_arg( va, const WCHAR *);
5192         MSI_RecordSetStringW( rec, i + 2, str );
5193     }
5194     va_end( va );
5195
5196     MSI_FormatRecordW( package, rec, NULL, &size );
5197     size++;
5198     data = msi_alloc( size * sizeof(WCHAR) );
5199     if (size > 1) MSI_FormatRecordW( package, rec, data, &size );
5200     else data[0] = 0;
5201     msiobj_release( &rec->hdr );
5202     return data;
5203 }
5204
5205 static UINT ACTION_ResolveSource(MSIPACKAGE* package)
5206 {
5207     DWORD attrib;
5208     UINT rc;
5209
5210     /*
5211      * We are currently doing what should be done here in the top level Install
5212      * however for Administrative and uninstalls this step will be needed
5213      */
5214     if (!package->PackagePath)
5215         return ERROR_SUCCESS;
5216
5217     msi_set_sourcedir_props(package, TRUE);
5218
5219     attrib = GetFileAttributesW(package->db->path);
5220     if (attrib == INVALID_FILE_ATTRIBUTES)
5221     {
5222         LPWSTR prompt, msg;
5223         DWORD size = 0;
5224
5225         rc = MsiSourceListGetInfoW(package->ProductCode, NULL, 
5226                 package->Context, MSICODE_PRODUCT,
5227                 INSTALLPROPERTY_DISKPROMPTW,NULL,&size);
5228         if (rc == ERROR_MORE_DATA)
5229         {
5230             prompt = msi_alloc(size * sizeof(WCHAR));
5231             MsiSourceListGetInfoW(package->ProductCode, NULL, 
5232                     package->Context, MSICODE_PRODUCT,
5233                     INSTALLPROPERTY_DISKPROMPTW,prompt,&size);
5234         }
5235         else
5236             prompt = strdupW(package->db->path);
5237
5238         msg = msi_build_error_string(package, 1302, 1, prompt);
5239         msi_free(prompt);
5240         while(attrib == INVALID_FILE_ATTRIBUTES)
5241         {
5242             rc = MessageBoxW(NULL, msg, NULL, MB_OKCANCEL);
5243             if (rc == IDCANCEL)
5244             {
5245                 msi_free(msg);
5246                 return ERROR_INSTALL_USEREXIT;
5247             }
5248             attrib = GetFileAttributesW(package->db->path);
5249         }
5250         msi_free(msg);
5251         rc = ERROR_SUCCESS;
5252     }
5253     else
5254         return ERROR_SUCCESS;
5255
5256     return rc;
5257 }
5258
5259 static UINT ACTION_RegisterUser(MSIPACKAGE *package)
5260 {
5261     HKEY hkey = 0;
5262     LPWSTR buffer, productid = NULL;
5263     UINT i, rc = ERROR_SUCCESS;
5264     MSIRECORD *uirow;
5265
5266     static const WCHAR szPropKeys[][80] = 
5267     {
5268         {'P','r','o','d','u','c','t','I','D',0},
5269         {'U','S','E','R','N','A','M','E',0},
5270         {'C','O','M','P','A','N','Y','N','A','M','E',0},
5271         {0},
5272     };
5273
5274     static const WCHAR szRegKeys[][80] = 
5275     {
5276         {'P','r','o','d','u','c','t','I','D',0},
5277         {'R','e','g','O','w','n','e','r',0},
5278         {'R','e','g','C','o','m','p','a','n','y',0},
5279         {0},
5280     };
5281
5282     if (msi_check_unpublish(package))
5283     {
5284         MSIREG_DeleteUserDataProductKey(package->ProductCode);
5285         goto end;
5286     }
5287
5288     productid = msi_dup_property( package->db, INSTALLPROPERTY_PRODUCTIDW );
5289     if (!productid)
5290         goto end;
5291
5292     rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context,
5293                                  NULL, &hkey, TRUE);
5294     if (rc != ERROR_SUCCESS)
5295         goto end;
5296
5297     for( i = 0; szPropKeys[i][0]; i++ )
5298     {
5299         buffer = msi_dup_property( package->db, szPropKeys[i] );
5300         msi_reg_set_val_str( hkey, szRegKeys[i], buffer );
5301         msi_free( buffer );
5302     }
5303
5304 end:
5305     uirow = MSI_CreateRecord( 1 );
5306     MSI_RecordSetStringW( uirow, 1, productid );
5307     msi_ui_actiondata( package, szRegisterUser, uirow );
5308     msiobj_release( &uirow->hdr );
5309
5310     msi_free(productid);
5311     RegCloseKey(hkey);
5312     return rc;
5313 }
5314
5315
5316 static UINT ACTION_ExecuteAction(MSIPACKAGE *package)
5317 {
5318     UINT rc;
5319
5320     package->script->InWhatSequence |= SEQUENCE_EXEC;
5321     rc = ACTION_ProcessExecSequence(package,FALSE);
5322     return rc;
5323 }
5324
5325 WCHAR *msi_create_component_advertise_string( MSIPACKAGE *package, MSICOMPONENT *component, const WCHAR *feature )
5326 {
5327     static const WCHAR fmt[] = {'%','s','%','s','%','c','%','s',0};
5328     WCHAR productid_85[21], component_85[21], *ret;
5329     GUID clsid;
5330     DWORD sz;
5331
5332     /* > is used if there is a component GUID and < if not.  */
5333
5334     productid_85[0] = 0;
5335     component_85[0] = 0;
5336     CLSIDFromString( package->ProductCode, &clsid );
5337
5338     encode_base85_guid( &clsid, productid_85 );
5339     if (component)
5340     {
5341         CLSIDFromString( component->ComponentId, &clsid );
5342         encode_base85_guid( &clsid, component_85 );
5343     }
5344
5345     TRACE("product=%s feature=%s component=%s\n", debugstr_w(productid_85), debugstr_w(feature),
5346           debugstr_w(component_85));
5347
5348     sz = 20 + strlenW( feature ) + 20 + 3;
5349     ret = msi_alloc_zero( sz * sizeof(WCHAR) );
5350     if (ret) sprintfW( ret, fmt, productid_85, feature, component ? '>' : '<', component_85 );
5351     return ret;
5352 }
5353
5354 static UINT ITERATE_PublishComponent(MSIRECORD *rec, LPVOID param)
5355 {
5356     MSIPACKAGE *package = param;
5357     LPCWSTR compgroupid, component, feature, qualifier, text;
5358     LPWSTR advertise = NULL, output = NULL, existing = NULL, p, q;
5359     HKEY hkey = NULL;
5360     UINT rc;
5361     MSICOMPONENT *comp;
5362     MSIFEATURE *feat;
5363     DWORD sz;
5364     MSIRECORD *uirow;
5365     int len;
5366
5367     feature = MSI_RecordGetString(rec, 5);
5368     feat = msi_get_loaded_feature(package, feature);
5369     if (!feat)
5370         return ERROR_SUCCESS;
5371
5372     feat->Action = msi_get_feature_action( package, feat );
5373     if (feat->Action != INSTALLSTATE_LOCAL &&
5374         feat->Action != INSTALLSTATE_SOURCE &&
5375         feat->Action != INSTALLSTATE_ADVERTISED)
5376     {
5377         TRACE("feature not scheduled for installation %s\n", debugstr_w(feature));
5378         return ERROR_SUCCESS;
5379     }
5380
5381     component = MSI_RecordGetString(rec, 3);
5382     comp = msi_get_loaded_component(package, component);
5383     if (!comp)
5384         return ERROR_SUCCESS;
5385
5386     compgroupid = MSI_RecordGetString(rec,1);
5387     qualifier = MSI_RecordGetString(rec,2);
5388
5389     rc = MSIREG_OpenUserComponentsKey(compgroupid, &hkey, TRUE);
5390     if (rc != ERROR_SUCCESS)
5391         goto end;
5392
5393     advertise = msi_create_component_advertise_string( package, comp, feature );
5394     text = MSI_RecordGetString( rec, 4 );
5395     if (text)
5396     {
5397         p = msi_alloc( (strlenW( advertise ) + strlenW( text ) + 1) * sizeof(WCHAR) );
5398         strcpyW( p, advertise );
5399         strcatW( p, text );
5400         msi_free( advertise );
5401         advertise = p;
5402     }
5403     existing = msi_reg_get_val_str( hkey, qualifier );
5404
5405     sz = strlenW( advertise ) + 1;
5406     if (existing)
5407     {
5408         for (p = existing; *p; p += len)
5409         {
5410             len = strlenW( p ) + 1;
5411             if (strcmpW( advertise, p )) sz += len;
5412         }
5413     }
5414     if (!(output = msi_alloc( (sz + 1) * sizeof(WCHAR) )))
5415     {
5416         rc = ERROR_OUTOFMEMORY;
5417         goto end;
5418     }
5419     q = output;
5420     if (existing)
5421     {
5422         for (p = existing; *p; p += len)
5423         {
5424             len = strlenW( p ) + 1;
5425             if (strcmpW( advertise, p ))
5426             {
5427                 memcpy( q, p, len * sizeof(WCHAR) );
5428                 q += len;
5429             }
5430         }
5431     }
5432     strcpyW( q, advertise );
5433     q[strlenW( q ) + 1] = 0;
5434
5435     msi_reg_set_val_multi_str( hkey, qualifier, output );
5436     
5437 end:
5438     RegCloseKey(hkey);
5439     msi_free( output );
5440     msi_free( advertise );
5441     msi_free( existing );
5442
5443     /* the UI chunk */
5444     uirow = MSI_CreateRecord( 2 );
5445     MSI_RecordSetStringW( uirow, 1, compgroupid );
5446     MSI_RecordSetStringW( uirow, 2, qualifier);
5447     msi_ui_actiondata( package, szPublishComponents, uirow );
5448     msiobj_release( &uirow->hdr );
5449     /* FIXME: call ui_progress? */
5450
5451     return rc;
5452 }
5453
5454 /*
5455  * At present I am ignorning the advertised components part of this and only
5456  * focusing on the qualified component sets
5457  */
5458 static UINT ACTION_PublishComponents(MSIPACKAGE *package)
5459 {
5460     static const WCHAR query[] = {
5461         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5462         '`','P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','`',0};
5463     MSIQUERY *view;
5464     UINT rc;
5465     
5466     rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5467     if (rc != ERROR_SUCCESS)
5468         return ERROR_SUCCESS;
5469
5470     rc = MSI_IterateRecords(view, NULL, ITERATE_PublishComponent, package);
5471     msiobj_release(&view->hdr);
5472     return rc;
5473 }
5474
5475 static UINT ITERATE_UnpublishComponent( MSIRECORD *rec, LPVOID param )
5476 {
5477     static const WCHAR szInstallerComponents[] = {
5478         'S','o','f','t','w','a','r','e','\\',
5479         'M','i','c','r','o','s','o','f','t','\\',
5480         'I','n','s','t','a','l','l','e','r','\\',
5481         'C','o','m','p','o','n','e','n','t','s','\\',0};
5482
5483     MSIPACKAGE *package = param;
5484     LPCWSTR compgroupid, component, feature, qualifier;
5485     MSICOMPONENT *comp;
5486     MSIFEATURE *feat;
5487     MSIRECORD *uirow;
5488     WCHAR squashed[GUID_SIZE], keypath[MAX_PATH];
5489     LONG res;
5490
5491     feature = MSI_RecordGetString( rec, 5 );
5492     feat = msi_get_loaded_feature( package, feature );
5493     if (!feat)
5494         return ERROR_SUCCESS;
5495
5496     feat->Action = msi_get_feature_action( package, feat );
5497     if (feat->Action != INSTALLSTATE_ABSENT)
5498     {
5499         TRACE("feature not scheduled for removal %s\n", debugstr_w(feature));
5500         return ERROR_SUCCESS;
5501     }
5502
5503     component = MSI_RecordGetString( rec, 3 );
5504     comp = msi_get_loaded_component( package, component );
5505     if (!comp)
5506         return ERROR_SUCCESS;
5507
5508     compgroupid = MSI_RecordGetString( rec, 1 );
5509     qualifier = MSI_RecordGetString( rec, 2 );
5510
5511     squash_guid( compgroupid, squashed );
5512     strcpyW( keypath, szInstallerComponents );
5513     strcatW( keypath, squashed );
5514
5515     res = RegDeleteKeyW( HKEY_CURRENT_USER, keypath );
5516     if (res != ERROR_SUCCESS)
5517     {
5518         WARN("Unable to delete component key %d\n", res);
5519     }
5520
5521     uirow = MSI_CreateRecord( 2 );
5522     MSI_RecordSetStringW( uirow, 1, compgroupid );
5523     MSI_RecordSetStringW( uirow, 2, qualifier );
5524     msi_ui_actiondata( package, szUnpublishComponents, uirow );
5525     msiobj_release( &uirow->hdr );
5526
5527     return ERROR_SUCCESS;
5528 }
5529
5530 static UINT ACTION_UnpublishComponents( MSIPACKAGE *package )
5531 {
5532     static const WCHAR query[] = {
5533         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5534         '`','P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','`',0};
5535     MSIQUERY *view;
5536     UINT rc;
5537
5538     rc = MSI_DatabaseOpenViewW( package->db, query, &view );
5539     if (rc != ERROR_SUCCESS)
5540         return ERROR_SUCCESS;
5541
5542     rc = MSI_IterateRecords( view, NULL, ITERATE_UnpublishComponent, package );
5543     msiobj_release( &view->hdr );
5544     return rc;
5545 }
5546
5547 static UINT ITERATE_InstallService(MSIRECORD *rec, LPVOID param)
5548 {
5549     static const WCHAR query[] =
5550         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5551          '`','C','o','m','p','o','n','e','n','t','`',' ','W','H','E','R','E',' ',
5552          '`','C','o','m','p','o','n','e','n','t','`',' ','=','\'','%','s','\'',0};
5553     MSIPACKAGE *package = param;
5554     MSICOMPONENT *component;
5555     MSIRECORD *row;
5556     MSIFILE *file;
5557     SC_HANDLE hscm = NULL, service = NULL;
5558     LPCWSTR comp, key;
5559     LPWSTR name = NULL, disp = NULL, load_order = NULL, serv_name = NULL;
5560     LPWSTR depends = NULL, pass = NULL, args = NULL, image_path = NULL;
5561     DWORD serv_type, start_type, err_control;
5562     SERVICE_DESCRIPTIONW sd = {NULL};
5563
5564     comp = MSI_RecordGetString( rec, 12 );
5565     component = msi_get_loaded_component( package, comp );
5566     if (!component)
5567     {
5568         WARN("service component not found\n");
5569         goto done;
5570     }
5571     component->Action = msi_get_component_action( package, component );
5572     if (component->Action != INSTALLSTATE_LOCAL)
5573     {
5574         TRACE("component not scheduled for installation %s\n", debugstr_w(comp));
5575         goto done;
5576     }
5577     hscm = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASEW, GENERIC_WRITE);
5578     if (!hscm)
5579     {
5580         ERR("Failed to open the SC Manager!\n");
5581         goto done;
5582     }
5583
5584     start_type = MSI_RecordGetInteger(rec, 5);
5585     if (start_type == SERVICE_BOOT_START || start_type == SERVICE_SYSTEM_START)
5586         goto done;
5587
5588     deformat_string(package, MSI_RecordGetString(rec, 2), &name);
5589     deformat_string(package, MSI_RecordGetString(rec, 3), &disp);
5590     serv_type = MSI_RecordGetInteger(rec, 4);
5591     err_control = MSI_RecordGetInteger(rec, 6);
5592     deformat_string(package, MSI_RecordGetString(rec, 7), &load_order);
5593     deformat_string(package, MSI_RecordGetString(rec, 8), &depends);
5594     deformat_string(package, MSI_RecordGetString(rec, 9), &serv_name);
5595     deformat_string(package, MSI_RecordGetString(rec, 10), &pass);
5596     deformat_string(package, MSI_RecordGetString(rec, 11), &args);
5597     deformat_string(package, MSI_RecordGetString(rec, 13), &sd.lpDescription);
5598
5599     /* fetch the service path */
5600     row = MSI_QueryGetRecord(package->db, query, comp);
5601     if (!row)
5602     {
5603         ERR("Query failed\n");
5604         goto done;
5605     }
5606     key = MSI_RecordGetString(row, 6);
5607     file = msi_get_loaded_file(package, key);
5608     msiobj_release(&row->hdr);
5609     if (!file)
5610     {
5611         ERR("Failed to load the service file\n");
5612         goto done;
5613     }
5614
5615     if (!args || !args[0]) image_path = file->TargetPath;
5616     else
5617     {
5618         int len = strlenW(file->TargetPath) + strlenW(args) + 2;
5619         if (!(image_path = msi_alloc(len * sizeof(WCHAR))))
5620             return ERROR_OUTOFMEMORY;
5621
5622         strcpyW(image_path, file->TargetPath);
5623         strcatW(image_path, szSpace);
5624         strcatW(image_path, args);
5625     }
5626     service = CreateServiceW(hscm, name, disp, GENERIC_ALL, serv_type,
5627                              start_type, err_control, image_path, load_order,
5628                              NULL, depends, serv_name, pass);
5629
5630     if (!service)
5631     {
5632         if (GetLastError() != ERROR_SERVICE_EXISTS)
5633             ERR("Failed to create service %s: %d\n", debugstr_w(name), GetLastError());
5634     }
5635     else if (sd.lpDescription)
5636     {
5637         if (!ChangeServiceConfig2W(service, SERVICE_CONFIG_DESCRIPTION, &sd))
5638             WARN("failed to set service description %u\n", GetLastError());
5639     }
5640
5641     if (image_path != file->TargetPath) msi_free(image_path);
5642 done:
5643     CloseServiceHandle(service);
5644     CloseServiceHandle(hscm);
5645     msi_free(name);
5646     msi_free(disp);
5647     msi_free(sd.lpDescription);
5648     msi_free(load_order);
5649     msi_free(serv_name);
5650     msi_free(pass);
5651     msi_free(depends);
5652     msi_free(args);
5653
5654     return ERROR_SUCCESS;
5655 }
5656
5657 static UINT ACTION_InstallServices( MSIPACKAGE *package )
5658 {
5659     static const WCHAR query[] = {
5660         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5661         'S','e','r','v','i','c','e','I','n','s','t','a','l','l',0};
5662     MSIQUERY *view;
5663     UINT rc;
5664     
5665     rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5666     if (rc != ERROR_SUCCESS)
5667         return ERROR_SUCCESS;
5668
5669     rc = MSI_IterateRecords(view, NULL, ITERATE_InstallService, package);
5670     msiobj_release(&view->hdr);
5671     return rc;
5672 }
5673
5674 /* converts arg1[~]arg2[~]arg3 to a list of ptrs to the strings */
5675 static LPCWSTR *msi_service_args_to_vector(LPWSTR args, DWORD *numargs)
5676 {
5677     LPCWSTR *vector, *temp_vector;
5678     LPWSTR p, q;
5679     DWORD sep_len;
5680
5681     static const WCHAR separator[] = {'[','~',']',0};
5682
5683     *numargs = 0;
5684     sep_len = sizeof(separator) / sizeof(WCHAR) - 1;
5685
5686     if (!args)
5687         return NULL;
5688
5689     vector = msi_alloc(sizeof(LPWSTR));
5690     if (!vector)
5691         return NULL;
5692
5693     p = args;
5694     do
5695     {
5696         (*numargs)++;
5697         vector[*numargs - 1] = p;
5698
5699         if ((q = strstrW(p, separator)))
5700         {
5701             *q = '\0';
5702
5703             temp_vector = msi_realloc(vector, (*numargs + 1) * sizeof(LPWSTR));
5704             if (!temp_vector)
5705             {
5706                 msi_free(vector);
5707                 return NULL;
5708             }
5709             vector = temp_vector;
5710
5711             p = q + sep_len;
5712         }
5713     } while (q);
5714
5715     return vector;
5716 }
5717
5718 static UINT ITERATE_StartService(MSIRECORD *rec, LPVOID param)
5719 {
5720     MSIPACKAGE *package = param;
5721     MSICOMPONENT *comp;
5722     MSIRECORD *uirow;
5723     SC_HANDLE scm = NULL, service = NULL;
5724     LPCWSTR component, *vector = NULL;
5725     LPWSTR name, args, display_name = NULL;
5726     DWORD event, numargs, len, wait, dummy;
5727     UINT r = ERROR_FUNCTION_FAILED;
5728     SERVICE_STATUS_PROCESS status;
5729     ULONGLONG start_time;
5730
5731     component = MSI_RecordGetString(rec, 6);
5732     comp = msi_get_loaded_component(package, component);
5733     if (!comp)
5734         return ERROR_SUCCESS;
5735
5736     comp->Action = msi_get_component_action( package, comp );
5737     if (comp->Action != INSTALLSTATE_LOCAL)
5738     {
5739         TRACE("component not scheduled for installation %s\n", debugstr_w(component));
5740         return ERROR_SUCCESS;
5741     }
5742
5743     deformat_string(package, MSI_RecordGetString(rec, 2), &name);
5744     deformat_string(package, MSI_RecordGetString(rec, 4), &args);
5745     event = MSI_RecordGetInteger(rec, 3);
5746     wait = MSI_RecordGetInteger(rec, 5);
5747
5748     if (!(event & msidbServiceControlEventStart))
5749     {
5750         r = ERROR_SUCCESS;
5751         goto done;
5752     }
5753
5754     scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
5755     if (!scm)
5756     {
5757         ERR("Failed to open the service control manager\n");
5758         goto done;
5759     }
5760
5761     len = 0;
5762     if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
5763         GetLastError() == ERROR_INSUFFICIENT_BUFFER)
5764     {
5765         if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
5766             GetServiceDisplayNameW( scm, name, display_name, &len );
5767     }
5768
5769     service = OpenServiceW(scm, name, SERVICE_START|SERVICE_QUERY_STATUS);
5770     if (!service)
5771     {
5772         ERR("Failed to open service %s (%u)\n", debugstr_w(name), GetLastError());
5773         goto done;
5774     }
5775
5776     vector = msi_service_args_to_vector(args, &numargs);
5777
5778     if (!StartServiceW(service, numargs, vector) &&
5779         GetLastError() != ERROR_SERVICE_ALREADY_RUNNING)
5780     {
5781         ERR("Failed to start service %s (%u)\n", debugstr_w(name), GetLastError());
5782         goto done;
5783     }
5784
5785     r = ERROR_SUCCESS;
5786     if (wait)
5787     {
5788         /* wait for at most 30 seconds for the service to be up and running */
5789         if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO,
5790             (BYTE *)&status, sizeof(SERVICE_STATUS_PROCESS), &dummy))
5791         {
5792             TRACE("failed to query service status (%u)\n", GetLastError());
5793             goto done;
5794         }
5795         start_time = GetTickCount64();
5796         while (status.dwCurrentState == SERVICE_START_PENDING)
5797         {
5798             if (GetTickCount64() - start_time > 30000) break;
5799             Sleep(1000);
5800             if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO,
5801                 (BYTE *)&status, sizeof(SERVICE_STATUS_PROCESS), &dummy))
5802             {
5803                 TRACE("failed to query service status (%u)\n", GetLastError());
5804                 goto done;
5805             }
5806         }
5807         if (status.dwCurrentState != SERVICE_RUNNING)
5808         {
5809             WARN("service failed to start %u\n", status.dwCurrentState);
5810             r = ERROR_FUNCTION_FAILED;
5811         }
5812     }
5813
5814 done:
5815     uirow = MSI_CreateRecord( 2 );
5816     MSI_RecordSetStringW( uirow, 1, display_name );
5817     MSI_RecordSetStringW( uirow, 2, name );
5818     msi_ui_actiondata( package, szStartServices, uirow );
5819     msiobj_release( &uirow->hdr );
5820
5821     CloseServiceHandle(service);
5822     CloseServiceHandle(scm);
5823
5824     msi_free(name);
5825     msi_free(args);
5826     msi_free(vector);
5827     msi_free(display_name);
5828     return r;
5829 }
5830
5831 static UINT ACTION_StartServices( MSIPACKAGE *package )
5832 {
5833     static const WCHAR query[] = {
5834         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5835         'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
5836     MSIQUERY *view;
5837     UINT rc;
5838
5839     rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5840     if (rc != ERROR_SUCCESS)
5841         return ERROR_SUCCESS;
5842
5843     rc = MSI_IterateRecords(view, NULL, ITERATE_StartService, package);
5844     msiobj_release(&view->hdr);
5845     return rc;
5846 }
5847
5848 static BOOL stop_service_dependents(SC_HANDLE scm, SC_HANDLE service)
5849 {
5850     DWORD i, needed, count;
5851     ENUM_SERVICE_STATUSW *dependencies;
5852     SERVICE_STATUS ss;
5853     SC_HANDLE depserv;
5854
5855     if (EnumDependentServicesW(service, SERVICE_ACTIVE, NULL,
5856                                0, &needed, &count))
5857         return TRUE;
5858
5859     if (GetLastError() != ERROR_MORE_DATA)
5860         return FALSE;
5861
5862     dependencies = msi_alloc(needed);
5863     if (!dependencies)
5864         return FALSE;
5865
5866     if (!EnumDependentServicesW(service, SERVICE_ACTIVE, dependencies,
5867                                 needed, &needed, &count))
5868         goto error;
5869
5870     for (i = 0; i < count; i++)
5871     {
5872         depserv = OpenServiceW(scm, dependencies[i].lpServiceName,
5873                                SERVICE_STOP | SERVICE_QUERY_STATUS);
5874         if (!depserv)
5875             goto error;
5876
5877         if (!ControlService(depserv, SERVICE_CONTROL_STOP, &ss))
5878             goto error;
5879     }
5880
5881     return TRUE;
5882
5883 error:
5884     msi_free(dependencies);
5885     return FALSE;
5886 }
5887
5888 static UINT stop_service( LPCWSTR name )
5889 {
5890     SC_HANDLE scm = NULL, service = NULL;
5891     SERVICE_STATUS status;
5892     SERVICE_STATUS_PROCESS ssp;
5893     DWORD needed;
5894
5895     scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
5896     if (!scm)
5897     {
5898         WARN("Failed to open the SCM: %d\n", GetLastError());
5899         goto done;
5900     }
5901
5902     service = OpenServiceW(scm, name,
5903                            SERVICE_STOP |
5904                            SERVICE_QUERY_STATUS |
5905                            SERVICE_ENUMERATE_DEPENDENTS);
5906     if (!service)
5907     {
5908         WARN("Failed to open service (%s): %d\n", debugstr_w(name), GetLastError());
5909         goto done;
5910     }
5911
5912     if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp,
5913                               sizeof(SERVICE_STATUS_PROCESS), &needed))
5914     {
5915         WARN("Failed to query service status (%s): %d\n", debugstr_w(name), GetLastError());
5916         goto done;
5917     }
5918
5919     if (ssp.dwCurrentState == SERVICE_STOPPED)
5920         goto done;
5921
5922     stop_service_dependents(scm, service);
5923
5924     if (!ControlService(service, SERVICE_CONTROL_STOP, &status))
5925         WARN("Failed to stop service (%s): %d\n", debugstr_w(name), GetLastError());
5926
5927 done:
5928     CloseServiceHandle(service);
5929     CloseServiceHandle(scm);
5930
5931     return ERROR_SUCCESS;
5932 }
5933
5934 static UINT ITERATE_StopService( MSIRECORD *rec, LPVOID param )
5935 {
5936     MSIPACKAGE *package = param;
5937     MSICOMPONENT *comp;
5938     MSIRECORD *uirow;
5939     LPCWSTR component;
5940     LPWSTR name = NULL, display_name = NULL;
5941     DWORD event, len;
5942     SC_HANDLE scm;
5943
5944     event = MSI_RecordGetInteger( rec, 3 );
5945     if (!(event & msidbServiceControlEventStop))
5946         return ERROR_SUCCESS;
5947
5948     component = MSI_RecordGetString( rec, 6 );
5949     comp = msi_get_loaded_component( package, component );
5950     if (!comp)
5951         return ERROR_SUCCESS;
5952
5953     comp->Action = msi_get_component_action( package, comp );
5954     if (comp->Action != INSTALLSTATE_ABSENT)
5955     {
5956         TRACE("component not scheduled for removal %s\n", debugstr_w(component));
5957         return ERROR_SUCCESS;
5958     }
5959
5960     scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_CONNECT );
5961     if (!scm)
5962     {
5963         ERR("Failed to open the service control manager\n");
5964         goto done;
5965     }
5966
5967     len = 0;
5968     if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
5969         GetLastError() == ERROR_INSUFFICIENT_BUFFER)
5970     {
5971         if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
5972             GetServiceDisplayNameW( scm, name, display_name, &len );
5973     }
5974     CloseServiceHandle( scm );
5975
5976     deformat_string( package, MSI_RecordGetString( rec, 2 ), &name );
5977     stop_service( name );
5978
5979 done:
5980     uirow = MSI_CreateRecord( 2 );
5981     MSI_RecordSetStringW( uirow, 1, display_name );
5982     MSI_RecordSetStringW( uirow, 2, name );
5983     msi_ui_actiondata( package, szStopServices, uirow );
5984     msiobj_release( &uirow->hdr );
5985
5986     msi_free( name );
5987     msi_free( display_name );
5988     return ERROR_SUCCESS;
5989 }
5990
5991 static UINT ACTION_StopServices( MSIPACKAGE *package )
5992 {
5993     static const WCHAR query[] = {
5994         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5995         'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
5996     MSIQUERY *view;
5997     UINT rc;
5998
5999     rc = MSI_DatabaseOpenViewW(package->db, query, &view);
6000     if (rc != ERROR_SUCCESS)
6001         return ERROR_SUCCESS;
6002
6003     rc = MSI_IterateRecords(view, NULL, ITERATE_StopService, package);
6004     msiobj_release(&view->hdr);
6005     return rc;
6006 }
6007
6008 static UINT ITERATE_DeleteService( MSIRECORD *rec, LPVOID param )
6009 {
6010     MSIPACKAGE *package = param;
6011     MSICOMPONENT *comp;
6012     MSIRECORD *uirow;
6013     LPWSTR name = NULL, display_name = NULL;
6014     DWORD event, len;
6015     SC_HANDLE scm = NULL, service = NULL;
6016
6017     comp = msi_get_loaded_component( package, MSI_RecordGetString(rec, 6) );
6018     if (!comp)
6019         return ERROR_SUCCESS;
6020
6021     event = MSI_RecordGetInteger( rec, 3 );
6022     deformat_string( package, MSI_RecordGetString(rec, 2), &name );
6023
6024     comp->Action = msi_get_component_action( package, comp );
6025     if (!(comp->Action == INSTALLSTATE_LOCAL && (event & msidbServiceControlEventDelete)) &&
6026         !(comp->Action == INSTALLSTATE_ABSENT && (event & msidbServiceControlEventUninstallDelete)))
6027     {
6028         TRACE("service %s not scheduled for removal\n", debugstr_w(name));
6029         msi_free( name );
6030         return ERROR_SUCCESS;
6031     }
6032     stop_service( name );
6033
6034     scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_ALL_ACCESS );
6035     if (!scm)
6036     {
6037         WARN("Failed to open the SCM: %d\n", GetLastError());
6038         goto done;
6039     }
6040
6041     len = 0;
6042     if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
6043         GetLastError() == ERROR_INSUFFICIENT_BUFFER)
6044     {
6045         if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
6046             GetServiceDisplayNameW( scm, name, display_name, &len );
6047     }
6048
6049     service = OpenServiceW( scm, name, DELETE );
6050     if (!service)
6051     {
6052         WARN("Failed to open service (%s): %u\n", debugstr_w(name), GetLastError());
6053         goto done;
6054     }
6055
6056     if (!DeleteService( service ))
6057         WARN("Failed to delete service (%s): %u\n", debugstr_w(name), GetLastError());
6058
6059 done:
6060     uirow = MSI_CreateRecord( 2 );
6061     MSI_RecordSetStringW( uirow, 1, display_name );
6062     MSI_RecordSetStringW( uirow, 2, name );
6063     msi_ui_actiondata( package, szDeleteServices, uirow );
6064     msiobj_release( &uirow->hdr );
6065
6066     CloseServiceHandle( service );
6067     CloseServiceHandle( scm );
6068     msi_free( name );
6069     msi_free( display_name );
6070
6071     return ERROR_SUCCESS;
6072 }
6073
6074 static UINT ACTION_DeleteServices( MSIPACKAGE *package )
6075 {
6076     static const WCHAR query[] = {
6077         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6078         'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
6079     MSIQUERY *view;
6080     UINT rc;
6081
6082     rc = MSI_DatabaseOpenViewW( package->db, query, &view );
6083     if (rc != ERROR_SUCCESS)
6084         return ERROR_SUCCESS;
6085
6086     rc = MSI_IterateRecords( view, NULL, ITERATE_DeleteService, package );
6087     msiobj_release( &view->hdr );
6088     return rc;
6089 }
6090
6091 static UINT ITERATE_InstallODBCDriver( MSIRECORD *rec, LPVOID param )
6092 {
6093     MSIPACKAGE *package = param;
6094     LPWSTR driver, driver_path, ptr;
6095     WCHAR outpath[MAX_PATH];
6096     MSIFILE *driver_file = NULL, *setup_file = NULL;
6097     MSICOMPONENT *comp;
6098     MSIRECORD *uirow;
6099     LPCWSTR desc, file_key, component;
6100     DWORD len, usage;
6101     UINT r = ERROR_SUCCESS;
6102
6103     static const WCHAR driver_fmt[] = {
6104         'D','r','i','v','e','r','=','%','s',0};
6105     static const WCHAR setup_fmt[] = {
6106         'S','e','t','u','p','=','%','s',0};
6107     static const WCHAR usage_fmt[] = {
6108         'F','i','l','e','U','s','a','g','e','=','1',0};
6109
6110     component = MSI_RecordGetString( rec, 2 );
6111     comp = msi_get_loaded_component( package, component );
6112     if (!comp)
6113         return ERROR_SUCCESS;
6114
6115     comp->Action = msi_get_component_action( package, comp );
6116     if (comp->Action != INSTALLSTATE_LOCAL)
6117     {
6118         TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6119         return ERROR_SUCCESS;
6120     }
6121     desc = MSI_RecordGetString(rec, 3);
6122
6123     file_key = MSI_RecordGetString( rec, 4 );
6124     if (file_key) driver_file = msi_get_loaded_file( package, file_key );
6125
6126     file_key = MSI_RecordGetString( rec, 5 );
6127     if (file_key) setup_file = msi_get_loaded_file( package, file_key );
6128
6129     if (!driver_file)
6130     {
6131         ERR("ODBC Driver entry not found!\n");
6132         return ERROR_FUNCTION_FAILED;
6133     }
6134
6135     len = lstrlenW(desc) + lstrlenW(driver_fmt) + lstrlenW(driver_file->FileName);
6136     if (setup_file)
6137         len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
6138     len += lstrlenW(usage_fmt) + 2; /* \0\0 */
6139
6140     driver = msi_alloc(len * sizeof(WCHAR));
6141     if (!driver)
6142         return ERROR_OUTOFMEMORY;
6143
6144     ptr = driver;
6145     lstrcpyW(ptr, desc);
6146     ptr += lstrlenW(ptr) + 1;
6147
6148     len = sprintfW(ptr, driver_fmt, driver_file->FileName);
6149     ptr += len + 1;
6150
6151     if (setup_file)
6152     {
6153         len = sprintfW(ptr, setup_fmt, setup_file->FileName);
6154         ptr += len + 1;
6155     }
6156
6157     lstrcpyW(ptr, usage_fmt);
6158     ptr += lstrlenW(ptr) + 1;
6159     *ptr = '\0';
6160
6161     if (!driver_file->TargetPath)
6162     {
6163         const WCHAR *dir = msi_get_target_folder( package, driver_file->Component->Directory );
6164         driver_file->TargetPath = msi_build_directory_name( 2, dir, driver_file->FileName );
6165     }
6166     driver_path = strdupW(driver_file->TargetPath);
6167     ptr = strrchrW(driver_path, '\\');
6168     if (ptr) *ptr = '\0';
6169
6170     if (!SQLInstallDriverExW(driver, driver_path, outpath, MAX_PATH,
6171                              NULL, ODBC_INSTALL_COMPLETE, &usage))
6172     {
6173         ERR("Failed to install SQL driver!\n");
6174         r = ERROR_FUNCTION_FAILED;
6175     }
6176
6177     uirow = MSI_CreateRecord( 5 );
6178     MSI_RecordSetStringW( uirow, 1, desc );
6179     MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6180     MSI_RecordSetStringW( uirow, 3, driver_file->Component->Directory );
6181     msi_ui_actiondata( package, szInstallODBC, uirow );
6182     msiobj_release( &uirow->hdr );
6183
6184     msi_free(driver);
6185     msi_free(driver_path);
6186
6187     return r;
6188 }
6189
6190 static UINT ITERATE_InstallODBCTranslator( MSIRECORD *rec, LPVOID param )
6191 {
6192     MSIPACKAGE *package = param;
6193     LPWSTR translator, translator_path, ptr;
6194     WCHAR outpath[MAX_PATH];
6195     MSIFILE *translator_file = NULL, *setup_file = NULL;
6196     MSICOMPONENT *comp;
6197     MSIRECORD *uirow;
6198     LPCWSTR desc, file_key, component;
6199     DWORD len, usage;
6200     UINT r = ERROR_SUCCESS;
6201
6202     static const WCHAR translator_fmt[] = {
6203         'T','r','a','n','s','l','a','t','o','r','=','%','s',0};
6204     static const WCHAR setup_fmt[] = {
6205         'S','e','t','u','p','=','%','s',0};
6206
6207     component = MSI_RecordGetString( rec, 2 );
6208     comp = msi_get_loaded_component( package, component );
6209     if (!comp)
6210         return ERROR_SUCCESS;
6211
6212     comp->Action = msi_get_component_action( package, comp );
6213     if (comp->Action != INSTALLSTATE_LOCAL)
6214     {
6215         TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6216         return ERROR_SUCCESS;
6217     }
6218     desc = MSI_RecordGetString(rec, 3);
6219
6220     file_key = MSI_RecordGetString( rec, 4 );
6221     if (file_key) translator_file = msi_get_loaded_file( package, file_key );
6222
6223     file_key = MSI_RecordGetString( rec, 5 );
6224     if (file_key) setup_file = msi_get_loaded_file( package, file_key );
6225
6226     if (!translator_file)
6227     {
6228         ERR("ODBC Translator entry not found!\n");
6229         return ERROR_FUNCTION_FAILED;
6230     }
6231
6232     len = lstrlenW(desc) + lstrlenW(translator_fmt) + lstrlenW(translator_file->FileName) + 2; /* \0\0 */
6233     if (setup_file)
6234         len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
6235
6236     translator = msi_alloc(len * sizeof(WCHAR));
6237     if (!translator)
6238         return ERROR_OUTOFMEMORY;
6239
6240     ptr = translator;
6241     lstrcpyW(ptr, desc);
6242     ptr += lstrlenW(ptr) + 1;
6243
6244     len = sprintfW(ptr, translator_fmt, translator_file->FileName);
6245     ptr += len + 1;
6246
6247     if (setup_file)
6248     {
6249         len = sprintfW(ptr, setup_fmt, setup_file->FileName);
6250         ptr += len + 1;
6251     }
6252     *ptr = '\0';
6253
6254     translator_path = strdupW(translator_file->TargetPath);
6255     ptr = strrchrW(translator_path, '\\');
6256     if (ptr) *ptr = '\0';
6257
6258     if (!SQLInstallTranslatorExW(translator, translator_path, outpath, MAX_PATH,
6259                                  NULL, ODBC_INSTALL_COMPLETE, &usage))
6260     {
6261         ERR("Failed to install SQL translator!\n");
6262         r = ERROR_FUNCTION_FAILED;
6263     }
6264
6265     uirow = MSI_CreateRecord( 5 );
6266     MSI_RecordSetStringW( uirow, 1, desc );
6267     MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6268     MSI_RecordSetStringW( uirow, 3, translator_file->Component->Directory );
6269     msi_ui_actiondata( package, szInstallODBC, uirow );
6270     msiobj_release( &uirow->hdr );
6271
6272     msi_free(translator);
6273     msi_free(translator_path);
6274
6275     return r;
6276 }
6277
6278 static UINT ITERATE_InstallODBCDataSource( MSIRECORD *rec, LPVOID param )
6279 {
6280     MSIPACKAGE *package = param;
6281     MSICOMPONENT *comp;
6282     LPWSTR attrs;
6283     LPCWSTR desc, driver, component;
6284     WORD request = ODBC_ADD_SYS_DSN;
6285     INT registration;
6286     DWORD len;
6287     UINT r = ERROR_SUCCESS;
6288     MSIRECORD *uirow;
6289
6290     static const WCHAR attrs_fmt[] = {
6291         'D','S','N','=','%','s',0 };
6292
6293     component = MSI_RecordGetString( rec, 2 );
6294     comp = msi_get_loaded_component( package, component );
6295     if (!comp)
6296         return ERROR_SUCCESS;
6297
6298     comp->Action = msi_get_component_action( package, comp );
6299     if (comp->Action != INSTALLSTATE_LOCAL)
6300     {
6301         TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6302         return ERROR_SUCCESS;
6303     }
6304
6305     desc = MSI_RecordGetString(rec, 3);
6306     driver = MSI_RecordGetString(rec, 4);
6307     registration = MSI_RecordGetInteger(rec, 5);
6308
6309     if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_ADD_SYS_DSN;
6310     else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_ADD_DSN;
6311
6312     len = lstrlenW(attrs_fmt) + lstrlenW(desc) + 2; /* \0\0 */
6313     attrs = msi_alloc(len * sizeof(WCHAR));
6314     if (!attrs)
6315         return ERROR_OUTOFMEMORY;
6316
6317     len = sprintfW(attrs, attrs_fmt, desc);
6318     attrs[len + 1] = 0;
6319
6320     if (!SQLConfigDataSourceW(NULL, request, driver, attrs))
6321     {
6322         ERR("Failed to install SQL data source!\n");
6323         r = ERROR_FUNCTION_FAILED;
6324     }
6325
6326     uirow = MSI_CreateRecord( 5 );
6327     MSI_RecordSetStringW( uirow, 1, desc );
6328     MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6329     MSI_RecordSetInteger( uirow, 3, request );
6330     msi_ui_actiondata( package, szInstallODBC, uirow );
6331     msiobj_release( &uirow->hdr );
6332
6333     msi_free(attrs);
6334
6335     return r;
6336 }
6337
6338 static UINT ACTION_InstallODBC( MSIPACKAGE *package )
6339 {
6340     static const WCHAR driver_query[] = {
6341         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6342         'O','D','B','C','D','r','i','v','e','r',0};
6343     static const WCHAR translator_query[] = {
6344         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6345         'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
6346     static const WCHAR source_query[] = {
6347         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6348         'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0};
6349     MSIQUERY *view;
6350     UINT rc;
6351
6352     rc = MSI_DatabaseOpenViewW(package->db, driver_query, &view);
6353     if (rc == ERROR_SUCCESS)
6354     {
6355         rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDriver, package);
6356         msiobj_release(&view->hdr);
6357         if (rc != ERROR_SUCCESS)
6358             return rc;
6359     }
6360     rc = MSI_DatabaseOpenViewW(package->db, translator_query, &view);
6361     if (rc == ERROR_SUCCESS)
6362     {
6363         rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCTranslator, package);
6364         msiobj_release(&view->hdr);
6365         if (rc != ERROR_SUCCESS)
6366             return rc;
6367     }
6368     rc = MSI_DatabaseOpenViewW(package->db, source_query, &view);
6369     if (rc == ERROR_SUCCESS)
6370     {
6371         rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDataSource, package);
6372         msiobj_release(&view->hdr);
6373         if (rc != ERROR_SUCCESS)
6374             return rc;
6375     }
6376     return ERROR_SUCCESS;
6377 }
6378
6379 static UINT ITERATE_RemoveODBCDriver( MSIRECORD *rec, LPVOID param )
6380 {
6381     MSIPACKAGE *package = param;
6382     MSICOMPONENT *comp;
6383     MSIRECORD *uirow;
6384     DWORD usage;
6385     LPCWSTR desc, component;
6386
6387     component = MSI_RecordGetString( rec, 2 );
6388     comp = msi_get_loaded_component( package, component );
6389     if (!comp)
6390         return ERROR_SUCCESS;
6391
6392     comp->Action = msi_get_component_action( package, comp );
6393     if (comp->Action != INSTALLSTATE_ABSENT)
6394     {
6395         TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6396         return ERROR_SUCCESS;
6397     }
6398
6399     desc = MSI_RecordGetString( rec, 3 );
6400     if (!SQLRemoveDriverW( desc, FALSE, &usage ))
6401     {
6402         WARN("Failed to remove ODBC driver\n");
6403     }
6404     else if (!usage)
6405     {
6406         FIXME("Usage count reached 0\n");
6407     }
6408
6409     uirow = MSI_CreateRecord( 2 );
6410     MSI_RecordSetStringW( uirow, 1, desc );
6411     MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6412     msi_ui_actiondata( package, szRemoveODBC, uirow );
6413     msiobj_release( &uirow->hdr );
6414
6415     return ERROR_SUCCESS;
6416 }
6417
6418 static UINT ITERATE_RemoveODBCTranslator( MSIRECORD *rec, LPVOID param )
6419 {
6420     MSIPACKAGE *package = param;
6421     MSICOMPONENT *comp;
6422     MSIRECORD *uirow;
6423     DWORD usage;
6424     LPCWSTR desc, component;
6425
6426     component = MSI_RecordGetString( rec, 2 );
6427     comp = msi_get_loaded_component( package, component );
6428     if (!comp)
6429         return ERROR_SUCCESS;
6430
6431     comp->Action = msi_get_component_action( package, comp );
6432     if (comp->Action != INSTALLSTATE_ABSENT)
6433     {
6434         TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6435         return ERROR_SUCCESS;
6436     }
6437
6438     desc = MSI_RecordGetString( rec, 3 );
6439     if (!SQLRemoveTranslatorW( desc, &usage ))
6440     {
6441         WARN("Failed to remove ODBC translator\n");
6442     }
6443     else if (!usage)
6444     {
6445         FIXME("Usage count reached 0\n");
6446     }
6447
6448     uirow = MSI_CreateRecord( 2 );
6449     MSI_RecordSetStringW( uirow, 1, desc );
6450     MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6451     msi_ui_actiondata( package, szRemoveODBC, uirow );
6452     msiobj_release( &uirow->hdr );
6453
6454     return ERROR_SUCCESS;
6455 }
6456
6457 static UINT ITERATE_RemoveODBCDataSource( MSIRECORD *rec, LPVOID param )
6458 {
6459     MSIPACKAGE *package = param;
6460     MSICOMPONENT *comp;
6461     MSIRECORD *uirow;
6462     LPWSTR attrs;
6463     LPCWSTR desc, driver, component;
6464     WORD request = ODBC_REMOVE_SYS_DSN;
6465     INT registration;
6466     DWORD len;
6467
6468     static const WCHAR attrs_fmt[] = {
6469         'D','S','N','=','%','s',0 };
6470
6471     component = MSI_RecordGetString( rec, 2 );
6472     comp = msi_get_loaded_component( package, component );
6473     if (!comp)
6474         return ERROR_SUCCESS;
6475
6476     comp->Action = msi_get_component_action( package, comp );
6477     if (comp->Action != INSTALLSTATE_ABSENT)
6478     {
6479         TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6480         return ERROR_SUCCESS;
6481     }
6482
6483     desc = MSI_RecordGetString( rec, 3 );
6484     driver = MSI_RecordGetString( rec, 4 );
6485     registration = MSI_RecordGetInteger( rec, 5 );
6486
6487     if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_REMOVE_SYS_DSN;
6488     else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_REMOVE_DSN;
6489
6490     len = strlenW( attrs_fmt ) + strlenW( desc ) + 2; /* \0\0 */
6491     attrs = msi_alloc( len * sizeof(WCHAR) );
6492     if (!attrs)
6493         return ERROR_OUTOFMEMORY;
6494
6495     FIXME("Use ODBCSourceAttribute table\n");
6496
6497     len = sprintfW( attrs, attrs_fmt, desc );
6498     attrs[len + 1] = 0;
6499
6500     if (!SQLConfigDataSourceW( NULL, request, driver, attrs ))
6501     {
6502         WARN("Failed to remove ODBC data source\n");
6503     }
6504     msi_free( attrs );
6505
6506     uirow = MSI_CreateRecord( 3 );
6507     MSI_RecordSetStringW( uirow, 1, desc );
6508     MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6509     MSI_RecordSetInteger( uirow, 3, request );
6510     msi_ui_actiondata( package, szRemoveODBC, uirow );
6511     msiobj_release( &uirow->hdr );
6512
6513     return ERROR_SUCCESS;
6514 }
6515
6516 static UINT ACTION_RemoveODBC( MSIPACKAGE *package )
6517 {
6518     static const WCHAR driver_query[] = {
6519         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6520         'O','D','B','C','D','r','i','v','e','r',0};
6521     static const WCHAR translator_query[] = {
6522         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6523         'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
6524     static const WCHAR source_query[] = {
6525         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6526         'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0};
6527     MSIQUERY *view;
6528     UINT rc;
6529
6530     rc = MSI_DatabaseOpenViewW( package->db, driver_query, &view );
6531     if (rc == ERROR_SUCCESS)
6532     {
6533         rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDriver, package );
6534         msiobj_release( &view->hdr );
6535         if (rc != ERROR_SUCCESS)
6536             return rc;
6537     }
6538     rc = MSI_DatabaseOpenViewW( package->db, translator_query, &view );
6539     if (rc == ERROR_SUCCESS)
6540     {
6541         rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCTranslator, package );
6542         msiobj_release( &view->hdr );
6543         if (rc != ERROR_SUCCESS)
6544             return rc;
6545     }
6546     rc = MSI_DatabaseOpenViewW( package->db, source_query, &view );
6547     if (rc == ERROR_SUCCESS)
6548     {
6549         rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDataSource, package );
6550         msiobj_release( &view->hdr );
6551         if (rc != ERROR_SUCCESS)
6552             return rc;
6553     }
6554     return ERROR_SUCCESS;
6555 }
6556
6557 #define ENV_ACT_SETALWAYS   0x1
6558 #define ENV_ACT_SETABSENT   0x2
6559 #define ENV_ACT_REMOVE      0x4
6560 #define ENV_ACT_REMOVEMATCH 0x8
6561
6562 #define ENV_MOD_MACHINE     0x20000000
6563 #define ENV_MOD_APPEND      0x40000000
6564 #define ENV_MOD_PREFIX      0x80000000
6565 #define ENV_MOD_MASK        0xC0000000
6566
6567 #define check_flag_combo(x, y) ((x) & ~(y)) == (y)
6568
6569 static UINT env_parse_flags( LPCWSTR *name, LPCWSTR *value, DWORD *flags )
6570 {
6571     LPCWSTR cptr = *name;
6572
6573     static const WCHAR prefix[] = {'[','~',']',0};
6574     static const int prefix_len = 3;
6575
6576     *flags = 0;
6577     while (*cptr)
6578     {
6579         if (*cptr == '=')
6580             *flags |= ENV_ACT_SETALWAYS;
6581         else if (*cptr == '+')
6582             *flags |= ENV_ACT_SETABSENT;
6583         else if (*cptr == '-')
6584             *flags |= ENV_ACT_REMOVE;
6585         else if (*cptr == '!')
6586             *flags |= ENV_ACT_REMOVEMATCH;
6587         else if (*cptr == '*')
6588             *flags |= ENV_MOD_MACHINE;
6589         else
6590             break;
6591
6592         cptr++;
6593         (*name)++;
6594     }
6595
6596     if (!*cptr)
6597     {
6598         ERR("Missing environment variable\n");
6599         return ERROR_FUNCTION_FAILED;
6600     }
6601
6602     if (*value)
6603     {
6604         LPCWSTR ptr = *value;
6605         if (!strncmpW(ptr, prefix, prefix_len))
6606         {
6607             if (ptr[prefix_len] == szSemiColon[0])
6608             {
6609                 *flags |= ENV_MOD_APPEND;
6610                 *value += lstrlenW(prefix);
6611             }
6612             else
6613             {
6614                 *value = NULL;
6615             }
6616         }
6617         else if (lstrlenW(*value) >= prefix_len)
6618         {
6619             ptr += lstrlenW(ptr) - prefix_len;
6620             if (!strcmpW( ptr, prefix ))
6621             {
6622                 if ((ptr-1) > *value && *(ptr-1) == szSemiColon[0])
6623                 {
6624                     *flags |= ENV_MOD_PREFIX;
6625                     /* the "[~]" will be removed by deformat_string */;
6626                 }
6627                 else
6628                 {
6629                     *value = NULL;
6630                 }
6631             }
6632         }
6633     }
6634
6635     if (check_flag_combo(*flags, ENV_ACT_SETALWAYS | ENV_ACT_SETABSENT) ||
6636         check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETABSENT) ||
6637         check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETALWAYS) ||
6638         check_flag_combo(*flags, ENV_ACT_SETABSENT | ENV_MOD_MASK))
6639     {
6640         ERR("Invalid flags: %08x\n", *flags);
6641         return ERROR_FUNCTION_FAILED;
6642     }
6643
6644     if (!*flags)
6645         *flags = ENV_ACT_SETALWAYS | ENV_ACT_REMOVE;
6646
6647     return ERROR_SUCCESS;
6648 }
6649
6650 static UINT open_env_key( DWORD flags, HKEY *key )
6651 {
6652     static const WCHAR user_env[] =
6653         {'E','n','v','i','r','o','n','m','e','n','t',0};
6654     static const WCHAR machine_env[] =
6655         {'S','y','s','t','e','m','\\',
6656          'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
6657          'C','o','n','t','r','o','l','\\',
6658          'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r','\\',
6659          'E','n','v','i','r','o','n','m','e','n','t',0};
6660     const WCHAR *env;
6661     HKEY root;
6662     LONG res;
6663
6664     if (flags & ENV_MOD_MACHINE)
6665     {
6666         env = machine_env;
6667         root = HKEY_LOCAL_MACHINE;
6668     }
6669     else
6670     {
6671         env = user_env;
6672         root = HKEY_CURRENT_USER;
6673     }
6674
6675     res = RegOpenKeyExW( root, env, 0, KEY_ALL_ACCESS, key );
6676     if (res != ERROR_SUCCESS)
6677     {
6678         WARN("Failed to open key %s (%d)\n", debugstr_w(env), res);
6679         return ERROR_FUNCTION_FAILED;
6680     }
6681
6682     return ERROR_SUCCESS;
6683 }
6684
6685 static UINT ITERATE_WriteEnvironmentString( MSIRECORD *rec, LPVOID param )
6686 {
6687     MSIPACKAGE *package = param;
6688     LPCWSTR name, value, component;
6689     LPWSTR data = NULL, newval = NULL, deformatted = NULL, ptr;
6690     DWORD flags, type, size;
6691     UINT res;
6692     HKEY env = NULL;
6693     MSICOMPONENT *comp;
6694     MSIRECORD *uirow;
6695     int action = 0;
6696
6697     component = MSI_RecordGetString(rec, 4);
6698     comp = msi_get_loaded_component(package, component);
6699     if (!comp)
6700         return ERROR_SUCCESS;
6701
6702     comp->Action = msi_get_component_action( package, comp );
6703     if (comp->Action != INSTALLSTATE_LOCAL)
6704     {
6705         TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6706         return ERROR_SUCCESS;
6707     }
6708     name = MSI_RecordGetString(rec, 2);
6709     value = MSI_RecordGetString(rec, 3);
6710
6711     TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
6712
6713     res = env_parse_flags(&name, &value, &flags);
6714     if (res != ERROR_SUCCESS || !value)
6715        goto done;
6716
6717     if (value && !deformat_string(package, value, &deformatted))
6718     {
6719         res = ERROR_OUTOFMEMORY;
6720         goto done;
6721     }
6722
6723     value = deformatted;
6724
6725     res = open_env_key( flags, &env );
6726     if (res != ERROR_SUCCESS)
6727         goto done;
6728
6729     if (flags & ENV_MOD_MACHINE)
6730         action |= 0x20000000;
6731
6732     size = 0;
6733     type = REG_SZ;
6734     res = RegQueryValueExW(env, name, NULL, &type, NULL, &size);
6735     if ((res != ERROR_SUCCESS && res != ERROR_FILE_NOT_FOUND) ||
6736         (res == ERROR_SUCCESS && type != REG_SZ && type != REG_EXPAND_SZ))
6737         goto done;
6738
6739     if ((res == ERROR_FILE_NOT_FOUND || !(flags & ENV_MOD_MASK)))
6740     {
6741         action = 0x2;
6742
6743         /* Nothing to do. */
6744         if (!value)
6745         {
6746             res = ERROR_SUCCESS;
6747             goto done;
6748         }
6749
6750         /* If we are appending but the string was empty, strip ; */
6751         if ((flags & ENV_MOD_APPEND) && (value[0] == szSemiColon[0])) value++;
6752
6753         size = (lstrlenW(value) + 1) * sizeof(WCHAR);
6754         newval = strdupW(value);
6755         if (!newval)
6756         {
6757             res = ERROR_OUTOFMEMORY;
6758             goto done;
6759         }
6760     }
6761     else
6762     {
6763         action = 0x1;
6764
6765         /* Contrary to MSDN, +-variable to [~];path works */
6766         if (flags & ENV_ACT_SETABSENT && !(flags & ENV_MOD_MASK))
6767         {
6768             res = ERROR_SUCCESS;
6769             goto done;
6770         }
6771
6772         data = msi_alloc(size);
6773         if (!data)
6774         {
6775             RegCloseKey(env);
6776             return ERROR_OUTOFMEMORY;
6777         }
6778
6779         res = RegQueryValueExW(env, name, NULL, &type, (LPVOID)data, &size);
6780         if (res != ERROR_SUCCESS)
6781             goto done;
6782
6783         if (flags & ENV_ACT_REMOVEMATCH && (!value || !strcmpW( data, value )))
6784         {
6785             action = 0x4;
6786             res = RegDeleteValueW(env, name);
6787             if (res != ERROR_SUCCESS)
6788                 WARN("Failed to remove value %s (%d)\n", debugstr_w(name), res);
6789             goto done;
6790         }
6791
6792         size = (lstrlenW(data) + 1) * sizeof(WCHAR);
6793         if (flags & ENV_MOD_MASK)
6794         {
6795             DWORD mod_size;
6796             int multiplier = 0;
6797             if (flags & ENV_MOD_APPEND) multiplier++;
6798             if (flags & ENV_MOD_PREFIX) multiplier++;
6799             mod_size = lstrlenW(value) * multiplier;
6800             size += mod_size * sizeof(WCHAR);
6801         }
6802
6803         newval = msi_alloc(size);
6804         ptr = newval;
6805         if (!newval)
6806         {
6807             res = ERROR_OUTOFMEMORY;
6808             goto done;
6809         }
6810
6811         if (flags & ENV_MOD_PREFIX)
6812         {
6813             lstrcpyW(newval, value);
6814             ptr = newval + lstrlenW(value);
6815             action |= 0x80000000;
6816         }
6817
6818         lstrcpyW(ptr, data);
6819
6820         if (flags & ENV_MOD_APPEND)
6821         {
6822             lstrcatW(newval, value);
6823             action |= 0x40000000;
6824         }
6825     }
6826     TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(newval));
6827     res = RegSetValueExW(env, name, 0, type, (LPVOID)newval, size);
6828     if (res)
6829     {
6830         WARN("Failed to set %s to %s (%d)\n",  debugstr_w(name), debugstr_w(newval), res);
6831     }
6832
6833 done:
6834     uirow = MSI_CreateRecord( 3 );
6835     MSI_RecordSetStringW( uirow, 1, name );
6836     MSI_RecordSetStringW( uirow, 2, newval );
6837     MSI_RecordSetInteger( uirow, 3, action );
6838     msi_ui_actiondata( package, szWriteEnvironmentStrings, uirow );
6839     msiobj_release( &uirow->hdr );
6840
6841     if (env) RegCloseKey(env);
6842     msi_free(deformatted);
6843     msi_free(data);
6844     msi_free(newval);
6845     return res;
6846 }
6847
6848 static UINT ACTION_WriteEnvironmentStrings( MSIPACKAGE *package )
6849 {
6850     static const WCHAR query[] = {
6851         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6852         '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
6853     MSIQUERY *view;
6854     UINT rc;
6855
6856     rc = MSI_DatabaseOpenViewW(package->db, query, &view);
6857     if (rc != ERROR_SUCCESS)
6858         return ERROR_SUCCESS;
6859
6860     rc = MSI_IterateRecords(view, NULL, ITERATE_WriteEnvironmentString, package);
6861     msiobj_release(&view->hdr);
6862     return rc;
6863 }
6864
6865 static UINT ITERATE_RemoveEnvironmentString( MSIRECORD *rec, LPVOID param )
6866 {
6867     MSIPACKAGE *package = param;
6868     LPCWSTR name, value, component;
6869     LPWSTR deformatted = NULL;
6870     DWORD flags;
6871     HKEY env;
6872     MSICOMPONENT *comp;
6873     MSIRECORD *uirow;
6874     int action = 0;
6875     LONG res;
6876     UINT r;
6877
6878     component = MSI_RecordGetString( rec, 4 );
6879     comp = msi_get_loaded_component( package, component );
6880     if (!comp)
6881         return ERROR_SUCCESS;
6882
6883     comp->Action = msi_get_component_action( package, comp );
6884     if (comp->Action != INSTALLSTATE_ABSENT)
6885     {
6886         TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6887         return ERROR_SUCCESS;
6888     }
6889     name = MSI_RecordGetString( rec, 2 );
6890     value = MSI_RecordGetString( rec, 3 );
6891
6892     TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
6893
6894     r = env_parse_flags( &name, &value, &flags );
6895     if (r != ERROR_SUCCESS)
6896        return r;
6897
6898     if (!(flags & ENV_ACT_REMOVE))
6899     {
6900         TRACE("Environment variable %s not marked for removal\n", debugstr_w(name));
6901         return ERROR_SUCCESS;
6902     }
6903
6904     if (value && !deformat_string( package, value, &deformatted ))
6905         return ERROR_OUTOFMEMORY;
6906
6907     value = deformatted;
6908
6909     r = open_env_key( flags, &env );
6910     if (r != ERROR_SUCCESS)
6911     {
6912         r = ERROR_SUCCESS;
6913         goto done;
6914     }
6915
6916     if (flags & ENV_MOD_MACHINE)
6917         action |= 0x20000000;
6918
6919     TRACE("Removing %s\n", debugstr_w(name));
6920
6921     res = RegDeleteValueW( env, name );
6922     if (res != ERROR_SUCCESS)
6923     {
6924         WARN("Failed to delete value %s (%d)\n", debugstr_w(name), res);
6925         r = ERROR_SUCCESS;
6926     }
6927
6928 done:
6929     uirow = MSI_CreateRecord( 3 );
6930     MSI_RecordSetStringW( uirow, 1, name );
6931     MSI_RecordSetStringW( uirow, 2, value );
6932     MSI_RecordSetInteger( uirow, 3, action );
6933     msi_ui_actiondata( package, szRemoveEnvironmentStrings, uirow );
6934     msiobj_release( &uirow->hdr );
6935
6936     if (env) RegCloseKey( env );
6937     msi_free( deformatted );
6938     return r;
6939 }
6940
6941 static UINT ACTION_RemoveEnvironmentStrings( MSIPACKAGE *package )
6942 {
6943     static const WCHAR query[] = {
6944         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6945         '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
6946     MSIQUERY *view;
6947     UINT rc;
6948
6949     rc = MSI_DatabaseOpenViewW( package->db, query, &view );
6950     if (rc != ERROR_SUCCESS)
6951         return ERROR_SUCCESS;
6952
6953     rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveEnvironmentString, package );
6954     msiobj_release( &view->hdr );
6955     return rc;
6956 }
6957
6958 UINT msi_validate_product_id( MSIPACKAGE *package )
6959 {
6960     LPWSTR key, template, id;
6961     UINT r = ERROR_SUCCESS;
6962
6963     id = msi_dup_property( package->db, szProductID );
6964     if (id)
6965     {
6966         msi_free( id );
6967         return ERROR_SUCCESS;
6968     }
6969     template = msi_dup_property( package->db, szPIDTemplate );
6970     key = msi_dup_property( package->db, szPIDKEY );
6971     if (key && template)
6972     {
6973         FIXME( "partial stub: template %s key %s\n", debugstr_w(template), debugstr_w(key) );
6974         r = msi_set_property( package->db, szProductID, key );
6975     }
6976     msi_free( template );
6977     msi_free( key );
6978     return r;
6979 }
6980
6981 static UINT ACTION_ValidateProductID( MSIPACKAGE *package )
6982 {
6983     return msi_validate_product_id( package );
6984 }
6985
6986 static UINT ACTION_ScheduleReboot( MSIPACKAGE *package )
6987 {
6988     TRACE("\n");
6989     package->need_reboot_at_end = 1;
6990     return ERROR_SUCCESS;
6991 }
6992
6993 static UINT ACTION_AllocateRegistrySpace( MSIPACKAGE *package )
6994 {
6995     static const WCHAR szAvailableFreeReg[] =
6996         {'A','V','A','I','L','A','B','L','E','F','R','E','E','R','E','G',0};
6997     MSIRECORD *uirow;
6998     int space = msi_get_property_int( package->db, szAvailableFreeReg, 0 );
6999
7000     TRACE("%p %d kilobytes\n", package, space);
7001
7002     uirow = MSI_CreateRecord( 1 );
7003     MSI_RecordSetInteger( uirow, 1, space );
7004     msi_ui_actiondata( package, szAllocateRegistrySpace, uirow );
7005     msiobj_release( &uirow->hdr );
7006
7007     return ERROR_SUCCESS;
7008 }
7009
7010 static UINT ACTION_DisableRollback( MSIPACKAGE *package )
7011 {
7012     TRACE("%p\n", package);
7013
7014     msi_set_property( package->db, szRollbackDisabled, szOne );
7015     return ERROR_SUCCESS;
7016 }
7017
7018 static UINT ACTION_InstallAdminPackage( MSIPACKAGE *package )
7019 {
7020     FIXME("%p\n", package);
7021     return ERROR_SUCCESS;
7022 }
7023
7024 static UINT ACTION_SetODBCFolders( MSIPACKAGE *package )
7025 {
7026     static const WCHAR driver_query[] = {
7027         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7028         'O','D','B','C','D','r','i','v','e','r',0};
7029     static const WCHAR translator_query[] = {
7030         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7031         'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
7032     MSIQUERY *view;
7033     UINT r, count;
7034
7035     r = MSI_DatabaseOpenViewW( package->db, driver_query, &view );
7036     if (r == ERROR_SUCCESS)
7037     {
7038         count = 0;
7039         r = MSI_IterateRecords( view, &count, NULL, package );
7040         msiobj_release( &view->hdr );
7041         if (r != ERROR_SUCCESS)
7042             return r;
7043         if (count) FIXME("ignored %u rows in ODBCDriver table\n", count);
7044     }
7045     r = MSI_DatabaseOpenViewW( package->db, translator_query, &view );
7046     if (r == ERROR_SUCCESS)
7047     {
7048         count = 0;
7049         r = MSI_IterateRecords( view, &count, NULL, package );
7050         msiobj_release( &view->hdr );
7051         if (r != ERROR_SUCCESS)
7052             return r;
7053         if (count) FIXME("ignored %u rows in ODBCTranslator table\n", count);
7054     }
7055     return ERROR_SUCCESS;
7056 }
7057
7058 static UINT ITERATE_RemoveExistingProducts( MSIRECORD *rec, LPVOID param )
7059 {
7060     static const WCHAR fmtW[] =
7061         {'m','s','i','e','x','e','c',' ','/','i',' ','%','s',' ','R','E','M','O','V','E','=','%','s',0};
7062     MSIPACKAGE *package = param;
7063     const WCHAR *property = MSI_RecordGetString( rec, 7 );
7064     UINT len = sizeof(fmtW)/sizeof(fmtW[0]);
7065     WCHAR *product, *features, *cmd;
7066     STARTUPINFOW si;
7067     PROCESS_INFORMATION info;
7068     BOOL ret;
7069
7070     if (!(product = msi_dup_property( package->db, property ))) return ERROR_SUCCESS;
7071
7072     deformat_string( package, MSI_RecordGetString( rec, 6 ), &features );
7073
7074     len += strlenW( product );
7075     if (features)
7076         len += strlenW( features );
7077     else
7078         len += sizeof(szAll) / sizeof(szAll[0]);
7079
7080     if (!(cmd = msi_alloc( len * sizeof(WCHAR) )))
7081     {
7082         msi_free( product );
7083         msi_free( features );
7084         return ERROR_OUTOFMEMORY;
7085     }
7086     sprintfW( cmd, fmtW, product, features ? features : szAll );
7087     msi_free( product );
7088     msi_free( features );
7089
7090     memset( &si, 0, sizeof(STARTUPINFOW) );
7091     ret = CreateProcessW( NULL, cmd, NULL, NULL, FALSE, 0, NULL, NULL, &si, &info );
7092     msi_free( cmd );
7093     if (!ret) return GetLastError();
7094     CloseHandle( info.hThread );
7095
7096     WaitForSingleObject( info.hProcess, INFINITE );
7097     CloseHandle( info.hProcess );
7098     return ERROR_SUCCESS;
7099 }
7100
7101 static UINT ACTION_RemoveExistingProducts( MSIPACKAGE *package )
7102 {
7103     static const WCHAR query[] = {
7104         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','U','p','g','r','a','d','e',0};
7105     MSIQUERY *view;
7106     UINT r;
7107
7108     r = MSI_DatabaseOpenViewW( package->db, query, &view );
7109     if (r == ERROR_SUCCESS)
7110     {
7111         r = MSI_IterateRecords( view, NULL, ITERATE_RemoveExistingProducts, package );
7112         msiobj_release( &view->hdr );
7113         if (r != ERROR_SUCCESS)
7114             return r;
7115     }
7116     return ERROR_SUCCESS;
7117 }
7118
7119 static UINT ITERATE_MigrateFeatureStates( MSIRECORD *rec, LPVOID param )
7120 {
7121     MSIPACKAGE *package = param;
7122     int attributes = MSI_RecordGetInteger( rec, 5 );
7123
7124     if (attributes & msidbUpgradeAttributesMigrateFeatures)
7125     {
7126         const WCHAR *upgrade_code = MSI_RecordGetString( rec, 1 );
7127         const WCHAR *version_min = MSI_RecordGetString( rec, 2 );
7128         const WCHAR *version_max = MSI_RecordGetString( rec, 3 );
7129         const WCHAR *language = MSI_RecordGetString( rec, 4 );
7130         HKEY hkey;
7131         UINT r;
7132
7133         if (package->Context == MSIINSTALLCONTEXT_MACHINE)
7134         {
7135             r = MSIREG_OpenClassesUpgradeCodesKey( upgrade_code, &hkey, FALSE );
7136             if (r != ERROR_SUCCESS)
7137                 return ERROR_SUCCESS;
7138         }
7139         else
7140         {
7141             r = MSIREG_OpenUserUpgradeCodesKey( upgrade_code, &hkey, FALSE );
7142             if (r != ERROR_SUCCESS)
7143                 return ERROR_SUCCESS;
7144         }
7145         RegCloseKey( hkey );
7146
7147         FIXME("migrate feature states from %s version min %s version max %s language %s\n",
7148               debugstr_w(upgrade_code), debugstr_w(version_min),
7149               debugstr_w(version_max), debugstr_w(language));
7150     }
7151     return ERROR_SUCCESS;
7152 }
7153
7154 static UINT ACTION_MigrateFeatureStates( MSIPACKAGE *package )
7155 {
7156     static const WCHAR query[] = {
7157         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7158         'U','p','g','r','a','d','e',0};
7159     MSIQUERY *view;
7160     UINT r;
7161
7162     if (msi_get_property_int( package->db, szInstalled, 0 ))
7163     {
7164         TRACE("product is installed, skipping action\n");
7165         return ERROR_SUCCESS;
7166     }
7167     if (msi_get_property_int( package->db, szPreselected, 0 ))
7168     {
7169         TRACE("Preselected property is set, not migrating feature states\n");
7170         return ERROR_SUCCESS;
7171     }
7172     r = MSI_DatabaseOpenViewW( package->db, query, &view );
7173     if (r == ERROR_SUCCESS)
7174     {
7175         r = MSI_IterateRecords( view, NULL, ITERATE_MigrateFeatureStates, package );
7176         msiobj_release( &view->hdr );
7177         if (r != ERROR_SUCCESS)
7178             return r;
7179     }
7180     return ERROR_SUCCESS;
7181 }
7182
7183 static void bind_image( const char *filename, const char *path )
7184 {
7185     if (!BindImageEx( 0, filename, path, NULL, NULL ))
7186     {
7187         WARN("failed to bind image %u\n", GetLastError());
7188     }
7189 }
7190
7191 static UINT ITERATE_BindImage( MSIRECORD *rec, LPVOID param )
7192 {
7193     UINT i;
7194     MSIFILE *file;
7195     MSIPACKAGE *package = param;
7196     const WCHAR *key = MSI_RecordGetString( rec, 1 );
7197     const WCHAR *paths = MSI_RecordGetString( rec, 2 );
7198     char *filenameA, *pathA;
7199     WCHAR *pathW, **path_list;
7200
7201     if (!(file = msi_get_loaded_file( package, key )))
7202     {
7203         WARN("file %s not found\n", debugstr_w(key));
7204         return ERROR_SUCCESS;
7205     }
7206     if (!(filenameA = strdupWtoA( file->TargetPath ))) return ERROR_SUCCESS;
7207     path_list = msi_split_string( paths, ';' );
7208     if (!path_list) bind_image( filenameA, NULL );
7209     else
7210     {
7211         for (i = 0; path_list[i] && path_list[i][0]; i++)
7212         {
7213             deformat_string( package, path_list[i], &pathW );
7214             if ((pathA = strdupWtoA( pathW )))
7215             {
7216                 bind_image( filenameA, pathA );
7217                 msi_free( pathA );
7218             }
7219             msi_free( pathW );
7220         }
7221     }
7222     msi_free( path_list );
7223     msi_free( filenameA );
7224     return ERROR_SUCCESS;
7225 }
7226
7227 static UINT ACTION_BindImage( MSIPACKAGE *package )
7228 {
7229     static const WCHAR query[] = {
7230         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7231         'B','i','n','d','I','m','a','g','e',0};
7232     MSIQUERY *view;
7233     UINT r;
7234
7235     r = MSI_DatabaseOpenViewW( package->db, query, &view );
7236     if (r == ERROR_SUCCESS)
7237     {
7238         r = MSI_IterateRecords( view, NULL, ITERATE_BindImage, package );
7239         msiobj_release( &view->hdr );
7240         if (r != ERROR_SUCCESS)
7241             return r;
7242     }
7243     return ERROR_SUCCESS;
7244 }
7245
7246 static UINT msi_unimplemented_action_stub( MSIPACKAGE *package, LPCSTR action, LPCWSTR table )
7247 {
7248     static const WCHAR query[] = {
7249         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','%','s','`',0};
7250     MSIQUERY *view;
7251     DWORD count = 0;
7252     UINT r;
7253     
7254     r = MSI_OpenQuery( package->db, &view, query, table );
7255     if (r == ERROR_SUCCESS)
7256     {
7257         r = MSI_IterateRecords(view, &count, NULL, package);
7258         msiobj_release(&view->hdr);
7259         if (r != ERROR_SUCCESS)
7260             return r;
7261     }
7262     if (count) FIXME("%s: ignored %u rows from %s\n", action, count, debugstr_w(table));
7263     return ERROR_SUCCESS;
7264 }
7265
7266 static UINT ACTION_IsolateComponents( MSIPACKAGE *package )
7267 {
7268     static const WCHAR table[] = {
7269         'I','s','o','l','a','t','e','d','C','o','m','p','o','n','e','n','t',0 };
7270     return msi_unimplemented_action_stub( package, "IsolateComponents", table );
7271 }
7272
7273 static UINT ACTION_RMCCPSearch( MSIPACKAGE *package )
7274 {
7275     static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
7276     return msi_unimplemented_action_stub( package, "RMCCPSearch", table );
7277 }
7278
7279 static UINT ACTION_RegisterComPlus( MSIPACKAGE *package )
7280 {
7281     static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
7282     return msi_unimplemented_action_stub( package, "RegisterComPlus", table );
7283 }
7284
7285 static UINT ACTION_UnregisterComPlus( MSIPACKAGE *package )
7286 {
7287     static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
7288     return msi_unimplemented_action_stub( package, "UnregisterComPlus", table );
7289 }
7290
7291 static UINT ACTION_InstallSFPCatalogFile( MSIPACKAGE *package )
7292 {
7293     static const WCHAR table[] = { 'S','F','P','C','a','t','a','l','o','g',0 };
7294     return msi_unimplemented_action_stub( package, "InstallSFPCatalogFile", table );
7295 }
7296
7297 static const struct
7298 {
7299     const WCHAR *action;
7300     UINT (*handler)(MSIPACKAGE *);
7301     const WCHAR *action_rollback;
7302 }
7303 StandardActions[] =
7304 {
7305     { szAllocateRegistrySpace, ACTION_AllocateRegistrySpace, NULL },
7306     { szAppSearch, ACTION_AppSearch, NULL },
7307     { szBindImage, ACTION_BindImage, NULL },
7308     { szCCPSearch, ACTION_CCPSearch, NULL },
7309     { szCostFinalize, ACTION_CostFinalize, NULL },
7310     { szCostInitialize, ACTION_CostInitialize, NULL },
7311     { szCreateFolders, ACTION_CreateFolders, szRemoveFolders },
7312     { szCreateShortcuts, ACTION_CreateShortcuts, szRemoveShortcuts },
7313     { szDeleteServices, ACTION_DeleteServices, szInstallServices },
7314     { szDisableRollback, ACTION_DisableRollback, NULL },
7315     { szDuplicateFiles, ACTION_DuplicateFiles, szRemoveDuplicateFiles },
7316     { szExecuteAction, ACTION_ExecuteAction, NULL },
7317     { szFileCost, ACTION_FileCost, NULL },
7318     { szFindRelatedProducts, ACTION_FindRelatedProducts, NULL },
7319     { szForceReboot, ACTION_ForceReboot, NULL },
7320     { szInstallAdminPackage, ACTION_InstallAdminPackage, NULL },
7321     { szInstallExecute, ACTION_InstallExecute, NULL },
7322     { szInstallExecuteAgain, ACTION_InstallExecute, NULL },
7323     { szInstallFiles, ACTION_InstallFiles, szRemoveFiles },
7324     { szInstallFinalize, ACTION_InstallFinalize, NULL },
7325     { szInstallInitialize, ACTION_InstallInitialize, NULL },
7326     { szInstallODBC, ACTION_InstallODBC, szRemoveODBC },
7327     { szInstallServices, ACTION_InstallServices, szDeleteServices },
7328     { szInstallSFPCatalogFile, ACTION_InstallSFPCatalogFile, NULL },
7329     { szInstallValidate, ACTION_InstallValidate, NULL },
7330     { szIsolateComponents, ACTION_IsolateComponents, NULL },
7331     { szLaunchConditions, ACTION_LaunchConditions, NULL },
7332     { szMigrateFeatureStates, ACTION_MigrateFeatureStates, NULL },
7333     { szMoveFiles, ACTION_MoveFiles, NULL },
7334     { szMsiPublishAssemblies, ACTION_MsiPublishAssemblies, szMsiUnpublishAssemblies },
7335     { szMsiUnpublishAssemblies, ACTION_MsiUnpublishAssemblies, szMsiPublishAssemblies },
7336     { szPatchFiles, ACTION_PatchFiles, NULL },
7337     { szProcessComponents, ACTION_ProcessComponents, szProcessComponents },
7338     { szPublishComponents, ACTION_PublishComponents, szUnpublishComponents },
7339     { szPublishFeatures, ACTION_PublishFeatures, szUnpublishFeatures },
7340     { szPublishProduct, ACTION_PublishProduct, NULL },
7341     { szRegisterClassInfo, ACTION_RegisterClassInfo, szUnregisterClassInfo },
7342     { szRegisterComPlus, ACTION_RegisterComPlus, szUnregisterComPlus },
7343     { szRegisterExtensionInfo, ACTION_RegisterExtensionInfo, szUnregisterExtensionInfo },
7344     { szRegisterFonts, ACTION_RegisterFonts, szUnregisterFonts },
7345     { szRegisterMIMEInfo, ACTION_RegisterMIMEInfo, szUnregisterMIMEInfo },
7346     { szRegisterProduct, ACTION_RegisterProduct, NULL },
7347     { szRegisterProgIdInfo, ACTION_RegisterProgIdInfo, szUnregisterProgIdInfo },
7348     { szRegisterTypeLibraries, ACTION_RegisterTypeLibraries, szUnregisterTypeLibraries },
7349     { szRegisterUser, ACTION_RegisterUser, NULL },
7350     { szRemoveDuplicateFiles, ACTION_RemoveDuplicateFiles, szDuplicateFiles },
7351     { szRemoveEnvironmentStrings, ACTION_RemoveEnvironmentStrings, szWriteEnvironmentStrings },
7352     { szRemoveExistingProducts, ACTION_RemoveExistingProducts, NULL },
7353     { szRemoveFiles, ACTION_RemoveFiles, szInstallFiles },
7354     { szRemoveFolders, ACTION_RemoveFolders, szCreateFolders },
7355     { szRemoveIniValues, ACTION_RemoveIniValues, szWriteIniValues },
7356     { szRemoveODBC, ACTION_RemoveODBC, szInstallODBC },
7357     { szRemoveRegistryValues, ACTION_RemoveRegistryValues, szWriteRegistryValues },
7358     { szRemoveShortcuts, ACTION_RemoveShortcuts, szCreateShortcuts },
7359     { szResolveSource, ACTION_ResolveSource, NULL },
7360     { szRMCCPSearch, ACTION_RMCCPSearch, NULL },
7361     { szScheduleReboot, ACTION_ScheduleReboot, NULL },
7362     { szSelfRegModules, ACTION_SelfRegModules, szSelfUnregModules },
7363     { szSelfUnregModules, ACTION_SelfUnregModules, szSelfRegModules },
7364     { szSetODBCFolders, ACTION_SetODBCFolders, NULL },
7365     { szStartServices, ACTION_StartServices, szStopServices },
7366     { szStopServices, ACTION_StopServices, szStartServices },
7367     { szUnpublishComponents, ACTION_UnpublishComponents, szPublishComponents },
7368     { szUnpublishFeatures, ACTION_UnpublishFeatures, szPublishFeatures },
7369     { szUnregisterClassInfo, ACTION_UnregisterClassInfo, szRegisterClassInfo },
7370     { szUnregisterComPlus, ACTION_UnregisterComPlus, szRegisterComPlus },
7371     { szUnregisterExtensionInfo, ACTION_UnregisterExtensionInfo, szRegisterExtensionInfo },
7372     { szUnregisterFonts, ACTION_UnregisterFonts, szRegisterFonts },
7373     { szUnregisterMIMEInfo, ACTION_UnregisterMIMEInfo, szRegisterMIMEInfo },
7374     { szUnregisterProgIdInfo, ACTION_UnregisterProgIdInfo, szRegisterProgIdInfo },
7375     { szUnregisterTypeLibraries, ACTION_UnregisterTypeLibraries, szRegisterTypeLibraries },
7376     { szValidateProductID, ACTION_ValidateProductID, NULL },
7377     { szWriteEnvironmentStrings, ACTION_WriteEnvironmentStrings, szRemoveEnvironmentStrings },
7378     { szWriteIniValues, ACTION_WriteIniValues, szRemoveIniValues },
7379     { szWriteRegistryValues, ACTION_WriteRegistryValues, szRemoveRegistryValues },
7380     { NULL, NULL, NULL }
7381 };
7382
7383 static BOOL ACTION_HandleStandardAction( MSIPACKAGE *package, LPCWSTR action, UINT *rc )
7384 {
7385     BOOL ret = FALSE;
7386     UINT i;
7387
7388     i = 0;
7389     while (StandardActions[i].action != NULL)
7390     {
7391         if (!strcmpW( StandardActions[i].action, action ))
7392         {
7393             ui_actionstart( package, action );
7394             if (StandardActions[i].handler)
7395             {
7396                 ui_actioninfo( package, action, TRUE, 0 );
7397                 *rc = StandardActions[i].handler( package );
7398                 ui_actioninfo( package, action, FALSE, *rc );
7399
7400                 if (StandardActions[i].action_rollback && !package->need_rollback)
7401                 {
7402                     TRACE("scheduling rollback action\n");
7403                     msi_schedule_action( package, SCRIPT_ROLLBACK, StandardActions[i].action_rollback );
7404                 }
7405             }
7406             else
7407             {
7408                 FIXME("unhandled standard action %s\n", debugstr_w(action));
7409                 *rc = ERROR_SUCCESS;
7410             }
7411             ret = TRUE;
7412             break;
7413         }
7414         i++;
7415     }
7416     return ret;
7417 }
7418
7419 UINT ACTION_PerformAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
7420 {
7421     UINT rc = ERROR_SUCCESS;
7422     BOOL handled;
7423
7424     TRACE("Performing action (%s)\n", debugstr_w(action));
7425
7426     handled = ACTION_HandleStandardAction(package, action, &rc);
7427
7428     if (!handled)
7429         handled = ACTION_HandleCustomAction(package, action, &rc, script, TRUE);
7430
7431     if (!handled)
7432     {
7433         WARN("unhandled msi action %s\n", debugstr_w(action));
7434         rc = ERROR_FUNCTION_NOT_CALLED;
7435     }
7436
7437     return rc;
7438 }
7439
7440 UINT ACTION_PerformUIAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
7441 {
7442     UINT rc = ERROR_SUCCESS;
7443     BOOL handled = FALSE;
7444
7445     TRACE("Performing action (%s)\n", debugstr_w(action));
7446
7447     package->action_progress_increment = 0;
7448     handled = ACTION_HandleStandardAction(package, action, &rc);
7449
7450     if (!handled)
7451         handled = ACTION_HandleCustomAction(package, action, &rc, script, FALSE);
7452
7453     if( !handled && ACTION_DialogBox(package, action) == ERROR_SUCCESS )
7454         handled = TRUE;
7455
7456     if (!handled)
7457     {
7458         WARN("unhandled msi action %s\n", debugstr_w(action));
7459         rc = ERROR_FUNCTION_NOT_CALLED;
7460     }
7461
7462     return rc;
7463 }
7464
7465 static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq)
7466 {
7467     UINT rc = ERROR_SUCCESS;
7468     MSIRECORD *row;
7469
7470     static const WCHAR query[] =
7471         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7472          '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
7473          'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
7474          '`','S','e','q','u','e','n','c','e','`',' ', '=',' ','%','i',0};
7475     static const WCHAR ui_query[] =
7476         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7477      '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e',
7478      '`', ' ', 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',
7479          ' ', '=',' ','%','i',0};
7480
7481     if (needs_ui_sequence(package))
7482         row = MSI_QueryGetRecord(package->db, ui_query, seq);
7483     else
7484         row = MSI_QueryGetRecord(package->db, query, seq);
7485
7486     if (row)
7487     {
7488         LPCWSTR action, cond;
7489
7490         TRACE("Running the actions\n");
7491
7492         /* check conditions */
7493         cond = MSI_RecordGetString(row, 2);
7494
7495         /* this is a hack to skip errors in the condition code */
7496         if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
7497         {
7498             msiobj_release(&row->hdr);
7499             return ERROR_SUCCESS;
7500         }
7501
7502         action = MSI_RecordGetString(row, 1);
7503         if (!action)
7504         {
7505             ERR("failed to fetch action\n");
7506             msiobj_release(&row->hdr);
7507             return ERROR_FUNCTION_FAILED;
7508         }
7509
7510         if (needs_ui_sequence(package))
7511             rc = ACTION_PerformUIAction(package, action, SCRIPT_NONE);
7512         else
7513             rc = ACTION_PerformAction(package, action, SCRIPT_NONE);
7514
7515         msiobj_release(&row->hdr);
7516     }
7517
7518     return rc;
7519 }
7520
7521 /****************************************************
7522  * TOP level entry points
7523  *****************************************************/
7524
7525 UINT MSI_InstallPackage( MSIPACKAGE *package, LPCWSTR szPackagePath,
7526                          LPCWSTR szCommandLine )
7527 {
7528     static const WCHAR szDisableRollback[] = {'D','I','S','A','B','L','E','R','O','L','L','B','A','C','K',0};
7529     static const WCHAR szAction[] = {'A','C','T','I','O','N',0};
7530     static const WCHAR szInstall[] = {'I','N','S','T','A','L','L',0};
7531     WCHAR *reinstall = NULL;
7532     BOOL ui_exists;
7533     UINT rc;
7534
7535     msi_set_property( package->db, szAction, szInstall );
7536
7537     package->script->InWhatSequence = SEQUENCE_INSTALL;
7538
7539     if (szPackagePath)
7540     {
7541         LPWSTR p, dir;
7542         LPCWSTR file;
7543
7544         dir = strdupW(szPackagePath);
7545         p = strrchrW(dir, '\\');
7546         if (p)
7547         {
7548             *(++p) = 0;
7549             file = szPackagePath + (p - dir);
7550         }
7551         else
7552         {
7553             msi_free(dir);
7554             dir = msi_alloc(MAX_PATH * sizeof(WCHAR));
7555             GetCurrentDirectoryW(MAX_PATH, dir);
7556             lstrcatW(dir, szBackSlash);
7557             file = szPackagePath;
7558         }
7559
7560         msi_free( package->PackagePath );
7561         package->PackagePath = msi_alloc((lstrlenW(dir) + lstrlenW(file) + 1) * sizeof(WCHAR));
7562         if (!package->PackagePath)
7563         {
7564             msi_free(dir);
7565             return ERROR_OUTOFMEMORY;
7566         }
7567
7568         lstrcpyW(package->PackagePath, dir);
7569         lstrcatW(package->PackagePath, file);
7570         msi_free(dir);
7571
7572         msi_set_sourcedir_props(package, FALSE);
7573     }
7574
7575     rc = msi_parse_command_line( package, szCommandLine, FALSE );
7576     if (rc != ERROR_SUCCESS)
7577         return rc;
7578
7579     msi_apply_transforms( package );
7580     msi_apply_patches( package );
7581
7582     if (!szCommandLine && msi_get_property_int( package->db, szInstalled, 0 ))
7583     {
7584         TRACE("setting reinstall property\n");
7585         msi_set_property( package->db, szReinstall, szAll );
7586     }
7587
7588     /* properties may have been added by a transform */
7589     msi_clone_properties( package );
7590
7591     msi_parse_command_line( package, szCommandLine, FALSE );
7592     msi_adjust_privilege_properties( package );
7593     msi_set_context( package );
7594
7595     if (msi_get_property_int( package->db, szDisableRollback, 0 ))
7596     {
7597         TRACE("disabling rollback\n");
7598         msi_set_property( package->db, szRollbackDisabled, szOne );
7599     }
7600
7601     if (needs_ui_sequence( package))
7602     {
7603         package->script->InWhatSequence |= SEQUENCE_UI;
7604         rc = ACTION_ProcessUISequence(package);
7605         ui_exists = ui_sequence_exists(package);
7606         if (rc == ERROR_SUCCESS || !ui_exists)
7607         {
7608             package->script->InWhatSequence |= SEQUENCE_EXEC;
7609             rc = ACTION_ProcessExecSequence(package, ui_exists);
7610         }
7611     }
7612     else
7613         rc = ACTION_ProcessExecSequence(package, FALSE);
7614
7615     package->script->CurrentlyScripting = FALSE;
7616
7617     /* process the ending type action */
7618     if (rc == ERROR_SUCCESS)
7619         ACTION_PerformActionSequence(package, -1);
7620     else if (rc == ERROR_INSTALL_USEREXIT)
7621         ACTION_PerformActionSequence(package, -2);
7622     else if (rc == ERROR_INSTALL_SUSPEND)
7623         ACTION_PerformActionSequence(package, -4);
7624     else  /* failed */
7625     {
7626         ACTION_PerformActionSequence(package, -3);
7627         if (!msi_get_property_int( package->db, szRollbackDisabled, 0 ))
7628         {
7629             package->need_rollback = TRUE;
7630         }
7631     }
7632
7633     /* finish up running custom actions */
7634     ACTION_FinishCustomActions(package);
7635
7636     if (package->need_rollback && !(reinstall = msi_dup_property( package->db, szReinstall )))
7637     {
7638         WARN("installation failed, running rollback script\n");
7639         execute_script( package, SCRIPT_ROLLBACK );
7640     }
7641     msi_free( reinstall );
7642
7643     if (rc == ERROR_SUCCESS && package->need_reboot_at_end)
7644         return ERROR_SUCCESS_REBOOT_REQUIRED;
7645
7646     return rc;
7647 }