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