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