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