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