cd1be04572266341d714a8705ae508cd3bcbfcab
[reactos.git] / dll / win32 / msi / action.c
1 /*
2 * Implementation of the Microsoft Installer (msi.dll)
3 *
4 * Copyright 2004,2005 Aric Stewart for CodeWeavers
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19 */
20
21 #include "msipriv.h"
22
23 #include <winsvc.h>
24 #include <odbcinst.h>
25 #include <imagehlp.h>
26
27 #define REG_PROGRESS_VALUE 13200
28 #define COMPONENT_PROGRESS_VALUE 24000
29
30 WINE_DEFAULT_DEBUG_CHANNEL(msi);
31
32 static const WCHAR szCreateFolders[] =
33 {'C','r','e','a','t','e','F','o','l','d','e','r','s',0};
34 static const WCHAR szCostFinalize[] =
35 {'C','o','s','t','F','i','n','a','l','i','z','e',0};
36 static const WCHAR szWriteRegistryValues[] =
37 {'W','r','i','t','e','R','e','g','i','s','t','r','y','V','a','l','u','e','s',0};
38 static const WCHAR szFileCost[] =
39 {'F','i','l','e','C','o','s','t',0};
40 static const WCHAR szInstallInitialize[] =
41 {'I','n','s','t','a','l','l','I','n','i','t','i','a','l','i','z','e',0};
42 static const WCHAR szInstallValidate[] =
43 {'I','n','s','t','a','l','l','V','a','l','i','d','a','t','e',0};
44 static const WCHAR szLaunchConditions[] =
45 {'L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','s',0};
46 static const WCHAR szProcessComponents[] =
47 {'P','r','o','c','e','s','s','C','o','m','p','o','n','e','n','t','s',0};
48 static const WCHAR szRegisterTypeLibraries[] =
49 {'R','e','g','i','s','t','e','r','T','y','p','e','L','i','b','r','a','r','i','e','s',0};
50 static const WCHAR szCreateShortcuts[] =
51 {'C','r','e','a','t','e','S','h','o','r','t','c','u','t','s',0};
52 static const WCHAR szPublishProduct[] =
53 {'P','u','b','l','i','s','h','P','r','o','d','u','c','t',0};
54 static const WCHAR szWriteIniValues[] =
55 {'W','r','i','t','e','I','n','i','V','a','l','u','e','s',0};
56 static const WCHAR szSelfRegModules[] =
57 {'S','e','l','f','R','e','g','M','o','d','u','l','e','s',0};
58 static const WCHAR szPublishFeatures[] =
59 {'P','u','b','l','i','s','h','F','e','a','t','u','r','e','s',0};
60 static const WCHAR szRegisterProduct[] =
61 {'R','e','g','i','s','t','e','r','P','r','o','d','u','c','t',0};
62 static const WCHAR szInstallExecute[] =
63 {'I','n','s','t','a','l','l','E','x','e','c','u','t','e',0};
64 static const WCHAR szInstallExecuteAgain[] =
65 {'I','n','s','t','a','l','l','E','x','e','c','u','t','e','A','g','a','i','n',0};
66 static const WCHAR szInstallFinalize[] =
67 {'I','n','s','t','a','l','l','F','i','n','a','l','i','z','e',0};
68 static const WCHAR szForceReboot[] =
69 {'F','o','r','c','e','R','e','b','o','o','t',0};
70 static const WCHAR szResolveSource[] =
71 {'R','e','s','o','l','v','e','S','o','u','r','c','e',0};
72 static const WCHAR szAllocateRegistrySpace[] =
73 {'A','l','l','o','c','a','t','e','R','e','g','i','s','t','r','y','S','p','a','c','e',0};
74 static const WCHAR szBindImage[] =
75 {'B','i','n','d','I','m','a','g','e',0};
76 static const WCHAR szDeleteServices[] =
77 {'D','e','l','e','t','e','S','e','r','v','i','c','e','s',0};
78 static const WCHAR szDisableRollback[] =
79 {'D','i','s','a','b','l','e','R','o','l','l','b','a','c','k',0};
80 static const WCHAR szExecuteAction[] =
81 {'E','x','e','c','u','t','e','A','c','t','i','o','n',0};
82 static const WCHAR szInstallAdminPackage[] =
83 {'I','n','s','t','a','l','l','A','d','m','i','n','P','a','c','k','a','g','e',0};
84 static const WCHAR szInstallSFPCatalogFile[] =
85 {'I','n','s','t','a','l','l','S','F','P','C','a','t','a','l','o','g','F','i','l','e',0};
86 static const WCHAR szIsolateComponents[] =
87 {'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t','s',0};
88 static const WCHAR szMigrateFeatureStates[] =
89 {'M','i','g','r','a','t','e','F','e','a','t','u','r','e','S','t','a','t','e','s',0};
90 static const WCHAR szMsiUnpublishAssemblies[] =
91 {'M','s','i','U','n','p','u','b','l','i','s','h','A','s','s','e','m','b','l','i','e','s',0};
92 static const WCHAR szInstallODBC[] =
93 {'I','n','s','t','a','l','l','O','D','B','C',0};
94 static const WCHAR szInstallServices[] =
95 {'I','n','s','t','a','l','l','S','e','r','v','i','c','e','s',0};
96 static const WCHAR szPublishComponents[] =
97 {'P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','s',0};
98 static const WCHAR szRegisterComPlus[] =
99 {'R','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0};
100 static const WCHAR szRegisterUser[] =
101 {'R','e','g','i','s','t','e','r','U','s','e','r',0};
102 static const WCHAR szRemoveEnvironmentStrings[] =
103 {'R','e','m','o','v','e','E','n','v','i','r','o','n','m','e','n','t','S','t','r','i','n','g','s',0};
104 static const WCHAR szRemoveExistingProducts[] =
105 {'R','e','m','o','v','e','E','x','i','s','t','i','n','g','P','r','o','d','u','c','t','s',0};
106 static const WCHAR szRemoveFolders[] =
107 {'R','e','m','o','v','e','F','o','l','d','e','r','s',0};
108 static const WCHAR szRemoveIniValues[] =
109 {'R','e','m','o','v','e','I','n','i','V','a','l','u','e','s',0};
110 static const WCHAR szRemoveODBC[] =
111 {'R','e','m','o','v','e','O','D','B','C',0};
112 static const WCHAR szRemoveRegistryValues[] =
113 {'R','e','m','o','v','e','R','e','g','i','s','t','r','y','V','a','l','u','e','s',0};
114 static const WCHAR szRemoveShortcuts[] =
115 {'R','e','m','o','v','e','S','h','o','r','t','c','u','t','s',0};
116 static const WCHAR szRMCCPSearch[] =
117 {'R','M','C','C','P','S','e','a','r','c','h',0};
118 static const WCHAR szScheduleReboot[] =
119 {'S','c','h','e','d','u','l','e','R','e','b','o','o','t',0};
120 static const WCHAR szSelfUnregModules[] =
121 {'S','e','l','f','U','n','r','e','g','M','o','d','u','l','e','s',0};
122 static const WCHAR szSetODBCFolders[] =
123 {'S','e','t','O','D','B','C','F','o','l','d','e','r','s',0};
124 static const WCHAR szStartServices[] =
125 {'S','t','a','r','t','S','e','r','v','i','c','e','s',0};
126 static const WCHAR szStopServices[] =
127 {'S','t','o','p','S','e','r','v','i','c','e','s',0};
128 static const WCHAR szUnpublishComponents[] =
129 {'U','n','p','u','b','l','i','s','h', 'C','o','m','p','o','n','e','n','t','s',0};
130 static const WCHAR szUnpublishFeatures[] =
131 {'U','n','p','u','b','l','i','s','h','F','e','a','t','u','r','e','s',0};
132 static const WCHAR szUnpublishProduct[] =
133 {'U','n','p','u','b','l','i','s','h','P','r','o','d','u','c','t',0};
134 static const WCHAR szUnregisterComPlus[] =
135 {'U','n','r','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0};
136 static const WCHAR szUnregisterTypeLibraries[] =
137 {'U','n','r','e','g','i','s','t','e','r','T','y','p','e','L','i','b','r','a','r','i','e','s',0};
138 static const WCHAR szValidateProductID[] =
139 {'V','a','l','i','d','a','t','e','P','r','o','d','u','c','t','I','D',0};
140 static const WCHAR szWriteEnvironmentStrings[] =
141 {'W','r','i','t','e','E','n','v','i','r','o','n','m','e','n','t','S','t','r','i','n','g','s',0};
142
143 static void ui_actionstart(MSIPACKAGE *package, LPCWSTR action)
144 {
145 static const WCHAR Query_t[] =
146 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
147 '`','A','c','t','i','o', 'n','T','e','x','t','`',' ',
148 'W','H','E','R','E', ' ','`','A','c','t','i','o','n','`',' ','=',
149 ' ','\'','%','s','\'',0};
150 MSIRECORD * row;
151
152 row = MSI_QueryGetRecord( package->db, Query_t, action );
153 if (!row)
154 return;
155 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONSTART, row);
156 msiobj_release(&row->hdr);
157 }
158
159 static void ui_actioninfo(MSIPACKAGE *package, LPCWSTR action, BOOL start,
160 UINT rc)
161 {
162 MSIRECORD * row;
163 static const WCHAR template_s[]=
164 {'A','c','t','i','o','n',' ','s','t','a','r','t',' ','%','s',':',' ',
165 '%','s', '.',0};
166 static const WCHAR template_e[]=
167 {'A','c','t','i','o','n',' ','e','n','d','e','d',' ','%','s',':',' ',
168 '%','s', '.',' ','R','e','t','u','r','n',' ','v','a','l','u','e',' ',
169 '%','i','.',0};
170 static const WCHAR format[] =
171 {'H','H','\'',':','\'','m','m','\'',':','\'','s','s',0};
172 WCHAR message[1024];
173 WCHAR timet[0x100];
174
175 GetTimeFormatW(LOCALE_USER_DEFAULT, 0, NULL, format, timet, 0x100);
176 if (start)
177 sprintfW(message,template_s,timet,action);
178 else
179 sprintfW(message,template_e,timet,action,rc);
180
181 row = MSI_CreateRecord(1);
182 MSI_RecordSetStringW(row,1,message);
183
184 MSI_ProcessMessage(package, INSTALLMESSAGE_INFO, row);
185 msiobj_release(&row->hdr);
186 }
187
188 enum parse_state
189 {
190 state_whitespace,
191 state_token,
192 state_quote
193 };
194
195 static int parse_prop( const WCHAR *str, WCHAR *value, int *quotes )
196 {
197 enum parse_state state = state_quote;
198 const WCHAR *p;
199 WCHAR *out = value;
200 BOOL ignore, in_quotes = FALSE;
201 int count = 0, len = 0;
202
203 for (p = str; *p; p++)
204 {
205 ignore = FALSE;
206 switch (state)
207 {
208 case state_whitespace:
209 switch (*p)
210 {
211 case ' ':
212 in_quotes = TRUE;
213 ignore = TRUE;
214 len++;
215 break;
216 case '"':
217 state = state_quote;
218 if (in_quotes && p[1] != '\"') count--;
219 else count++;
220 break;
221 default:
222 state = state_token;
223 in_quotes = TRUE;
224 len++;
225 break;
226 }
227 break;
228
229 case state_token:
230 switch (*p)
231 {
232 case '"':
233 state = state_quote;
234 if (in_quotes) count--;
235 else count++;
236 break;
237 case ' ':
238 state = state_whitespace;
239 if (!count) goto done;
240 in_quotes = TRUE;
241 len++;
242 break;
243 default:
244 if (count) in_quotes = TRUE;
245 len++;
246 break;
247 }
248 break;
249
250 case state_quote:
251 switch (*p)
252 {
253 case '"':
254 if (in_quotes && p[1] != '\"') count--;
255 else count++;
256 break;
257 case ' ':
258 state = state_whitespace;
259 if (!count || (count > 1 && !len)) goto done;
260 in_quotes = TRUE;
261 len++;
262 break;
263 default:
264 state = state_token;
265 if (count) in_quotes = TRUE;
266 len++;
267 break;
268 }
269 break;
270
271 default: break;
272 }
273 if (!ignore) *out++ = *p;
274 if (!count) in_quotes = FALSE;
275 }
276
277 done:
278 if (!len) *value = 0;
279 else *out = 0;
280
281 *quotes = count;
282 return p - str;
283 }
284
285 static void remove_quotes( WCHAR *str )
286 {
287 WCHAR *p = str;
288 int len = strlenW( str );
289
290 while ((p = strchrW( p, '"' )))
291 {
292 memmove( p, p + 1, (len - (p - str)) * sizeof(WCHAR) );
293 p++;
294 }
295 }
296
297 UINT msi_parse_command_line( MSIPACKAGE *package, LPCWSTR szCommandLine,
298 BOOL preserve_case )
299 {
300 LPCWSTR ptr, ptr2;
301 int num_quotes;
302 DWORD len;
303 WCHAR *prop, *val;
304 UINT r;
305
306 if (!szCommandLine)
307 return ERROR_SUCCESS;
308
309 ptr = szCommandLine;
310 while (*ptr)
311 {
312 while (*ptr == ' ') ptr++;
313 if (!*ptr) break;
314
315 ptr2 = strchrW( ptr, '=' );
316 if (!ptr2) return ERROR_INVALID_COMMAND_LINE;
317
318 len = ptr2 - ptr;
319 if (!len) return ERROR_INVALID_COMMAND_LINE;
320
321 while (ptr[len - 1] == ' ') len--;
322
323 prop = msi_alloc( (len + 1) * sizeof(WCHAR) );
324 memcpy( prop, ptr, len * sizeof(WCHAR) );
325 prop[len] = 0;
326 if (!preserve_case) struprW( prop );
327
328 ptr2++;
329 while (*ptr2 == ' ') ptr2++;
330
331 num_quotes = 0;
332 val = msi_alloc( (strlenW( ptr2 ) + 1) * sizeof(WCHAR) );
333 len = parse_prop( ptr2, val, &num_quotes );
334 if (num_quotes % 2)
335 {
336 WARN("unbalanced quotes\n");
337 msi_free( val );
338 msi_free( prop );
339 return ERROR_INVALID_COMMAND_LINE;
340 }
341 remove_quotes( val );
342 TRACE("Found commandline property %s = %s\n", debugstr_w(prop), debugstr_w(val));
343
344 r = msi_set_property( package->db, prop, val, -1 );
345 if (r == ERROR_SUCCESS && !strcmpW( prop, szSourceDir ))
346 msi_reset_folders( package, TRUE );
347
348 msi_free( val );
349 msi_free( prop );
350
351 ptr = ptr2 + len;
352 }
353
354 return ERROR_SUCCESS;
355 }
356
357 WCHAR **msi_split_string( const WCHAR *str, WCHAR sep )
358 {
359 LPCWSTR pc;
360 LPWSTR p, *ret = NULL;
361 UINT count = 0;
362
363 if (!str)
364 return ret;
365
366 /* count the number of substrings */
367 for ( pc = str, count = 0; pc; count++ )
368 {
369 pc = strchrW( pc, sep );
370 if (pc)
371 pc++;
372 }
373
374 /* allocate space for an array of substring pointers and the substrings */
375 ret = msi_alloc( (count+1) * sizeof (LPWSTR) +
376 (lstrlenW(str)+1) * sizeof(WCHAR) );
377 if (!ret)
378 return ret;
379
380 /* copy the string and set the pointers */
381 p = (LPWSTR) &ret[count+1];
382 lstrcpyW( p, str );
383 for( count = 0; (ret[count] = p); count++ )
384 {
385 p = strchrW( p, sep );
386 if (p)
387 *p++ = 0;
388 }
389
390 return ret;
391 }
392
393 static BOOL ui_sequence_exists( MSIPACKAGE *package )
394 {
395 static const WCHAR query [] = {
396 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
397 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e','`',' ',
398 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',' ','>',' ','0',0};
399 MSIQUERY *view;
400 DWORD count = 0;
401
402 if (!(MSI_DatabaseOpenViewW( package->db, query, &view )))
403 {
404 MSI_IterateRecords( view, &count, NULL, package );
405 msiobj_release( &view->hdr );
406 }
407 return count != 0;
408 }
409
410 UINT msi_set_sourcedir_props(MSIPACKAGE *package, BOOL replace)
411 {
412 WCHAR *source, *check, *p, *db;
413 DWORD len;
414
415 if (!(db = msi_dup_property( package->db, szOriginalDatabase )))
416 return ERROR_OUTOFMEMORY;
417
418 if (!(p = strrchrW( db, '\\' )) && !(p = strrchrW( db, '/' )))
419 {
420 msi_free(db);
421 return ERROR_SUCCESS;
422 }
423 len = p - db + 2;
424 source = msi_alloc( len * sizeof(WCHAR) );
425 lstrcpynW( source, db, len );
426 msi_free( db );
427
428 check = msi_dup_property( package->db, szSourceDir );
429 if (!check || replace)
430 {
431 UINT r = msi_set_property( package->db, szSourceDir, source, -1 );
432 if (r == ERROR_SUCCESS)
433 msi_reset_folders( package, TRUE );
434 }
435 msi_free( check );
436
437 check = msi_dup_property( package->db, szSOURCEDIR );
438 if (!check || replace)
439 msi_set_property( package->db, szSOURCEDIR, source, -1 );
440
441 msi_free( check );
442 msi_free( source );
443
444 return ERROR_SUCCESS;
445 }
446
447 static BOOL needs_ui_sequence(MSIPACKAGE *package)
448 {
449 return (package->ui_level & INSTALLUILEVEL_MASK) >= INSTALLUILEVEL_REDUCED;
450 }
451
452 UINT msi_set_context(MSIPACKAGE *package)
453 {
454 UINT r = msi_locate_product( package->ProductCode, &package->Context );
455 if (r != ERROR_SUCCESS)
456 {
457 int num = msi_get_property_int( package->db, szAllUsers, 0 );
458 if (num == 1 || num == 2)
459 package->Context = MSIINSTALLCONTEXT_MACHINE;
460 else
461 package->Context = MSIINSTALLCONTEXT_USERUNMANAGED;
462 }
463 return ERROR_SUCCESS;
464 }
465
466 static UINT ITERATE_Actions(MSIRECORD *row, LPVOID param)
467 {
468 UINT rc;
469 LPCWSTR cond, action;
470 MSIPACKAGE *package = param;
471
472 action = MSI_RecordGetString(row,1);
473 if (!action)
474 {
475 ERR("Error is retrieving action name\n");
476 return ERROR_FUNCTION_FAILED;
477 }
478
479 /* check conditions */
480 cond = MSI_RecordGetString(row,2);
481
482 /* this is a hack to skip errors in the condition code */
483 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
484 {
485 TRACE("Skipping action: %s (condition is false)\n", debugstr_w(action));
486 return ERROR_SUCCESS;
487 }
488
489 if (needs_ui_sequence(package))
490 rc = ACTION_PerformUIAction(package, action, SCRIPT_NONE);
491 else
492 rc = ACTION_PerformAction(package, action, SCRIPT_NONE);
493
494 msi_dialog_check_messages( NULL );
495
496 if (package->CurrentInstallState != ERROR_SUCCESS)
497 rc = package->CurrentInstallState;
498
499 if (rc == ERROR_FUNCTION_NOT_CALLED)
500 rc = ERROR_SUCCESS;
501
502 if (rc != ERROR_SUCCESS)
503 ERR("Execution halted, action %s returned %i\n", debugstr_w(action), rc);
504
505 if (package->need_reboot_now)
506 {
507 TRACE("action %s asked for immediate reboot, suspending installation\n",
508 debugstr_w(action));
509 rc = ACTION_ForceReboot( package );
510 }
511 return rc;
512 }
513
514 UINT MSI_Sequence( MSIPACKAGE *package, LPCWSTR table )
515 {
516 static const WCHAR query[] = {
517 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','%','s','`',
518 ' ','W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',' ',
519 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
520 '`','S','e','q','u','e','n','c','e','`',0};
521 MSIQUERY *view;
522 UINT r;
523
524 TRACE("%p %s\n", package, debugstr_w(table));
525
526 r = MSI_OpenQuery( package->db, &view, query, table );
527 if (r == ERROR_SUCCESS)
528 {
529 r = MSI_IterateRecords( view, NULL, ITERATE_Actions, package );
530 msiobj_release(&view->hdr);
531 }
532 return r;
533 }
534
535 static UINT ACTION_ProcessExecSequence(MSIPACKAGE *package, BOOL UIran)
536 {
537 static const WCHAR query[] = {
538 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
539 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
540 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
541 '`','S','e','q','u','e','n','c','e','`',' ', '>',' ','%','i',' ',
542 'O','R','D','E','R',' ', 'B','Y',' ','`','S','e','q','u','e','n','c','e','`',0};
543 static const WCHAR query_validate[] = {
544 'S','E','L','E','C','T',' ','`','S','e','q','u','e','n','c','e','`',
545 ' ', 'F','R','O','M',' ','`','I','n','s','t','a','l','l',
546 'E','x','e','c','u','t','e','S','e','q','u','e','n','c','e','`',' ',
547 'W','H','E','R','E',' ','`','A','c','t','i','o','n','`',' ','=',
548 ' ','\'', 'I','n','s','t','a','l','l','V','a','l','i','d','a','t','e','\'',0};
549 MSIQUERY *view;
550 INT seq = 0;
551 UINT rc;
552
553 if (package->script->ExecuteSequenceRun)
554 {
555 TRACE("Execute Sequence already Run\n");
556 return ERROR_SUCCESS;
557 }
558
559 package->script->ExecuteSequenceRun = TRUE;
560
561 /* get the sequence number */
562 if (UIran)
563 {
564 MSIRECORD *row = MSI_QueryGetRecord(package->db, query_validate);
565 if (!row) return ERROR_FUNCTION_FAILED;
566 seq = MSI_RecordGetInteger(row,1);
567 msiobj_release(&row->hdr);
568 }
569 rc = MSI_OpenQuery(package->db, &view, query, seq);
570 if (rc == ERROR_SUCCESS)
571 {
572 TRACE("Running the actions\n");
573
574 msi_set_property( package->db, szSourceDir, NULL, -1 );
575 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, package);
576 msiobj_release(&view->hdr);
577 }
578 return rc;
579 }
580
581 static UINT ACTION_ProcessUISequence(MSIPACKAGE *package)
582 {
583 static const WCHAR query[] = {
584 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
585 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e','`',' ',
586 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',' ','>',' ','0',' ',
587 'O','R','D','E','R',' ','B','Y',' ','`','S','e','q','u','e','n','c','e','`',0};
588 MSIQUERY *view;
589 UINT rc;
590
591 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
592 if (rc == ERROR_SUCCESS)
593 {
594 TRACE("Running the actions\n");
595 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, package);
596 msiobj_release(&view->hdr);
597 }
598 return rc;
599 }
600
601 /********************************************************
602 * ACTION helper functions and functions that perform the actions
603 *******************************************************/
604 static BOOL ACTION_HandleCustomAction( MSIPACKAGE *package, LPCWSTR action, UINT *rc, UINT script )
605 {
606 BOOL ret=FALSE;
607 UINT arc;
608
609 arc = ACTION_CustomAction( package, action, script );
610 if (arc != ERROR_CALL_NOT_IMPLEMENTED)
611 {
612 *rc = arc;
613 ret = TRUE;
614 }
615 return ret;
616 }
617
618 MSICOMPONENT *msi_get_loaded_component( MSIPACKAGE *package, const WCHAR *Component )
619 {
620 MSICOMPONENT *comp;
621
622 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
623 {
624 if (!strcmpW( Component, comp->Component )) return comp;
625 }
626 return NULL;
627 }
628
629 MSIFEATURE *msi_get_loaded_feature(MSIPACKAGE* package, const WCHAR *Feature )
630 {
631 MSIFEATURE *feature;
632
633 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
634 {
635 if (!strcmpW( Feature, feature->Feature )) return feature;
636 }
637 return NULL;
638 }
639
640 MSIFILE *msi_get_loaded_file( MSIPACKAGE *package, const WCHAR *key )
641 {
642 MSIFILE *file;
643
644 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
645 {
646 if (!strcmpW( key, file->File )) return file;
647 }
648 return NULL;
649 }
650
651 MSIFOLDER *msi_get_loaded_folder( MSIPACKAGE *package, const WCHAR *dir )
652 {
653 MSIFOLDER *folder;
654
655 LIST_FOR_EACH_ENTRY( folder, &package->folders, MSIFOLDER, entry )
656 {
657 if (!strcmpW( dir, folder->Directory )) return folder;
658 }
659 return NULL;
660 }
661
662 /*
663 * Recursively create all directories in the path.
664 * shamelessly stolen from setupapi/queue.c
665 */
666 BOOL msi_create_full_path( const WCHAR *path )
667 {
668 BOOL ret = TRUE;
669 WCHAR *new_path;
670 int len;
671
672 new_path = msi_alloc( (strlenW( path ) + 1) * sizeof(WCHAR) );
673 strcpyW( new_path, path );
674
675 while ((len = strlenW( new_path )) && new_path[len - 1] == '\\')
676 new_path[len - 1] = 0;
677
678 while (!CreateDirectoryW( new_path, NULL ))
679 {
680 WCHAR *slash;
681 DWORD last_error = GetLastError();
682 if (last_error == ERROR_ALREADY_EXISTS) break;
683 if (last_error != ERROR_PATH_NOT_FOUND)
684 {
685 ret = FALSE;
686 break;
687 }
688 if (!(slash = strrchrW( new_path, '\\' )))
689 {
690 ret = FALSE;
691 break;
692 }
693 len = slash - new_path;
694 new_path[len] = 0;
695 if (!msi_create_full_path( new_path ))
696 {
697 ret = FALSE;
698 break;
699 }
700 new_path[len] = '\\';
701 }
702 msi_free( new_path );
703 return ret;
704 }
705
706 void msi_ui_progress( MSIPACKAGE *package, int a, int b, int c, int d )
707 {
708 MSIRECORD *row;
709
710 row = MSI_CreateRecord( 4 );
711 MSI_RecordSetInteger( row, 1, a );
712 MSI_RecordSetInteger( row, 2, b );
713 MSI_RecordSetInteger( row, 3, c );
714 MSI_RecordSetInteger( row, 4, d );
715 MSI_ProcessMessage( package, INSTALLMESSAGE_PROGRESS, row );
716 msiobj_release( &row->hdr );
717
718 msi_dialog_check_messages( NULL );
719 }
720
721 void msi_ui_actiondata( MSIPACKAGE *package, const WCHAR *action, MSIRECORD *record )
722 {
723 static const WCHAR query[] =
724 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
725 '`','A','c','t','i','o', 'n','T','e','x','t','`',' ',
726 'W','H','E','R','E',' ', '`','A','c','t','i','o','n','`',' ','=',' ','\'','%','s','\'',0};
727 WCHAR message[1024];
728 MSIRECORD *row = 0;
729 DWORD size;
730
731 if (!package->LastAction || strcmpW( package->LastAction, action ))
732 {
733 if (!(row = MSI_QueryGetRecord( package->db, query, action ))) return;
734
735 if (MSI_RecordIsNull( row, 3 ))
736 {
737 msiobj_release( &row->hdr );
738 return;
739 }
740 /* update the cached action format */
741 msi_free( package->ActionFormat );
742 package->ActionFormat = msi_dup_record_field( row, 3 );
743 msi_free( package->LastAction );
744 package->LastAction = strdupW( action );
745 msiobj_release( &row->hdr );
746 }
747 size = 1024;
748 MSI_RecordSetStringW( record, 0, package->ActionFormat );
749 MSI_FormatRecordW( package, record, message, &size );
750 row = MSI_CreateRecord( 1 );
751 MSI_RecordSetStringW( row, 1, message );
752 MSI_ProcessMessage( package, INSTALLMESSAGE_ACTIONDATA, row );
753 msiobj_release( &row->hdr );
754 }
755
756 INSTALLSTATE msi_get_component_action( MSIPACKAGE *package, MSICOMPONENT *comp )
757 {
758 if (!comp->Enabled)
759 {
760 TRACE("component is disabled: %s\n", debugstr_w(comp->Component));
761 return INSTALLSTATE_UNKNOWN;
762 }
763 if (package->need_rollback) return comp->Installed;
764 if (comp->num_clients > 0 && comp->ActionRequest == INSTALLSTATE_ABSENT)
765 {
766 TRACE("%s has %u clients left\n", debugstr_w(comp->Component), comp->num_clients);
767 return INSTALLSTATE_UNKNOWN;
768 }
769 return comp->ActionRequest;
770 }
771
772 INSTALLSTATE msi_get_feature_action( MSIPACKAGE *package, MSIFEATURE *feature )
773 {
774 if (package->need_rollback) return feature->Installed;
775 return feature->ActionRequest;
776 }
777
778 static UINT ITERATE_CreateFolders(MSIRECORD *row, LPVOID param)
779 {
780 MSIPACKAGE *package = param;
781 LPCWSTR dir, component, full_path;
782 MSIRECORD *uirow;
783 MSIFOLDER *folder;
784 MSICOMPONENT *comp;
785
786 component = MSI_RecordGetString(row, 2);
787 if (!component)
788 return ERROR_SUCCESS;
789
790 comp = msi_get_loaded_component(package, component);
791 if (!comp)
792 return ERROR_SUCCESS;
793
794 comp->Action = msi_get_component_action( package, comp );
795 if (comp->Action != INSTALLSTATE_LOCAL)
796 {
797 TRACE("component not scheduled for installation: %s\n", debugstr_w(component));
798 return ERROR_SUCCESS;
799 }
800
801 dir = MSI_RecordGetString(row,1);
802 if (!dir)
803 {
804 ERR("Unable to get folder id\n");
805 return ERROR_SUCCESS;
806 }
807
808 uirow = MSI_CreateRecord(1);
809 MSI_RecordSetStringW(uirow, 1, dir);
810 msi_ui_actiondata(package, szCreateFolders, uirow);
811 msiobj_release(&uirow->hdr);
812
813 full_path = msi_get_target_folder( package, dir );
814 if (!full_path)
815 {
816 ERR("Unable to retrieve folder %s\n", debugstr_w(dir));
817 return ERROR_SUCCESS;
818 }
819 TRACE("folder is %s\n", debugstr_w(full_path));
820
821 folder = msi_get_loaded_folder( package, dir );
822 if (folder->State == FOLDER_STATE_UNINITIALIZED) msi_create_full_path( full_path );
823 folder->State = FOLDER_STATE_CREATED;
824 return ERROR_SUCCESS;
825 }
826
827 static UINT ACTION_CreateFolders(MSIPACKAGE *package)
828 {
829 static const WCHAR query[] = {
830 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
831 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0};
832 MSIQUERY *view;
833 UINT rc;
834
835 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
836 if (rc != ERROR_SUCCESS)
837 return ERROR_SUCCESS;
838
839 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateFolders, package);
840 msiobj_release(&view->hdr);
841 return rc;
842 }
843
844 static void remove_persistent_folder( MSIFOLDER *folder )
845 {
846 FolderList *fl;
847
848 LIST_FOR_EACH_ENTRY( fl, &folder->children, FolderList, entry )
849 {
850 remove_persistent_folder( fl->folder );
851 }
852 if (folder->persistent && folder->State != FOLDER_STATE_REMOVED)
853 {
854 if (RemoveDirectoryW( folder->ResolvedTarget )) folder->State = FOLDER_STATE_REMOVED;
855 }
856 }
857
858 static UINT ITERATE_RemoveFolders( MSIRECORD *row, LPVOID param )
859 {
860 MSIPACKAGE *package = param;
861 LPCWSTR dir, component, full_path;
862 MSIRECORD *uirow;
863 MSIFOLDER *folder;
864 MSICOMPONENT *comp;
865
866 component = MSI_RecordGetString(row, 2);
867 if (!component)
868 return ERROR_SUCCESS;
869
870 comp = msi_get_loaded_component(package, component);
871 if (!comp)
872 return ERROR_SUCCESS;
873
874 comp->Action = msi_get_component_action( package, comp );
875 if (comp->Action != INSTALLSTATE_ABSENT)
876 {
877 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
878 return ERROR_SUCCESS;
879 }
880
881 dir = MSI_RecordGetString( row, 1 );
882 if (!dir)
883 {
884 ERR("Unable to get folder id\n");
885 return ERROR_SUCCESS;
886 }
887
888 full_path = msi_get_target_folder( package, dir );
889 if (!full_path)
890 {
891 ERR("Unable to resolve folder %s\n", debugstr_w(dir));
892 return ERROR_SUCCESS;
893 }
894 TRACE("folder is %s\n", debugstr_w(full_path));
895
896 uirow = MSI_CreateRecord( 1 );
897 MSI_RecordSetStringW( uirow, 1, dir );
898 msi_ui_actiondata( package, szRemoveFolders, uirow );
899 msiobj_release( &uirow->hdr );
900
901 folder = msi_get_loaded_folder( package, dir );
902 remove_persistent_folder( folder );
903 return ERROR_SUCCESS;
904 }
905
906 static UINT ACTION_RemoveFolders( MSIPACKAGE *package )
907 {
908 static const WCHAR query[] = {
909 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
910 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0};
911 MSIQUERY *view;
912 UINT rc;
913
914 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
915 if (rc != ERROR_SUCCESS)
916 return ERROR_SUCCESS;
917
918 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveFolders, package );
919 msiobj_release( &view->hdr );
920 return rc;
921 }
922
923 static UINT load_component( MSIRECORD *row, LPVOID param )
924 {
925 MSIPACKAGE *package = param;
926 MSICOMPONENT *comp;
927
928 comp = msi_alloc_zero( sizeof(MSICOMPONENT) );
929 if (!comp)
930 return ERROR_FUNCTION_FAILED;
931
932 list_add_tail( &package->components, &comp->entry );
933
934 /* fill in the data */
935 comp->Component = msi_dup_record_field( row, 1 );
936
937 TRACE("Loading Component %s\n", debugstr_w(comp->Component));
938
939 comp->ComponentId = msi_dup_record_field( row, 2 );
940 comp->Directory = msi_dup_record_field( row, 3 );
941 comp->Attributes = MSI_RecordGetInteger(row,4);
942 comp->Condition = msi_dup_record_field( row, 5 );
943 comp->KeyPath = msi_dup_record_field( row, 6 );
944
945 comp->Installed = INSTALLSTATE_UNKNOWN;
946 comp->Action = INSTALLSTATE_UNKNOWN;
947 comp->ActionRequest = INSTALLSTATE_UNKNOWN;
948
949 comp->assembly = msi_load_assembly( package, comp );
950 return ERROR_SUCCESS;
951 }
952
953 UINT msi_load_all_components( MSIPACKAGE *package )
954 {
955 static const WCHAR query[] = {
956 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
957 '`','C','o','m','p','o','n','e','n','t','`',0};
958 MSIQUERY *view;
959 UINT r;
960
961 if (!list_empty(&package->components))
962 return ERROR_SUCCESS;
963
964 r = MSI_DatabaseOpenViewW( package->db, query, &view );
965 if (r != ERROR_SUCCESS)
966 return r;
967
968 if (!msi_init_assembly_caches( package ))
969 {
970 ERR("can't initialize assembly caches\n");
971 msiobj_release( &view->hdr );
972 return ERROR_FUNCTION_FAILED;
973 }
974
975 r = MSI_IterateRecords(view, NULL, load_component, package);
976 msiobj_release(&view->hdr);
977 return r;
978 }
979
980 typedef struct {
981 MSIPACKAGE *package;
982 MSIFEATURE *feature;
983 } _ilfs;
984
985 static UINT add_feature_component( MSIFEATURE *feature, MSICOMPONENT *comp )
986 {
987 ComponentList *cl;
988
989 cl = msi_alloc( sizeof (*cl) );
990 if ( !cl )
991 return ERROR_NOT_ENOUGH_MEMORY;
992 cl->component = comp;
993 list_add_tail( &feature->Components, &cl->entry );
994
995 return ERROR_SUCCESS;
996 }
997
998 static UINT add_feature_child( MSIFEATURE *parent, MSIFEATURE *child )
999 {
1000 FeatureList *fl;
1001
1002 fl = msi_alloc( sizeof(*fl) );
1003 if ( !fl )
1004 return ERROR_NOT_ENOUGH_MEMORY;
1005 fl->feature = child;
1006 list_add_tail( &parent->Children, &fl->entry );
1007
1008 return ERROR_SUCCESS;
1009 }
1010
1011 static UINT iterate_load_featurecomponents(MSIRECORD *row, LPVOID param)
1012 {
1013 _ilfs* ilfs = param;
1014 LPCWSTR component;
1015 MSICOMPONENT *comp;
1016
1017 component = MSI_RecordGetString(row,1);
1018
1019 /* check to see if the component is already loaded */
1020 comp = msi_get_loaded_component( ilfs->package, component );
1021 if (!comp)
1022 {
1023 WARN("ignoring unknown component %s\n", debugstr_w(component));
1024 return ERROR_SUCCESS;
1025 }
1026 add_feature_component( ilfs->feature, comp );
1027 comp->Enabled = TRUE;
1028
1029 return ERROR_SUCCESS;
1030 }
1031
1032 static UINT load_feature(MSIRECORD * row, LPVOID param)
1033 {
1034 static const WCHAR query[] = {
1035 'S','E','L','E','C','T',' ','`','C','o','m','p','o','n','e','n','t','_','`',
1036 ' ','F','R','O','M',' ','`','F','e','a','t','u','r','e',
1037 'C','o','m','p','o','n','e','n','t','s','`',' ','W','H','E','R','E',' ',
1038 '`','F','e', 'a','t','u','r','e','_','`',' ','=','\'','%','s','\'',0};
1039 MSIPACKAGE *package = param;
1040 MSIFEATURE *feature;
1041 MSIQUERY *view;
1042 _ilfs ilfs;
1043 UINT rc;
1044
1045 /* fill in the data */
1046
1047 feature = msi_alloc_zero( sizeof (MSIFEATURE) );
1048 if (!feature)
1049 return ERROR_NOT_ENOUGH_MEMORY;
1050
1051 list_init( &feature->Children );
1052 list_init( &feature->Components );
1053
1054 feature->Feature = msi_dup_record_field( row, 1 );
1055
1056 TRACE("Loading feature %s\n",debugstr_w(feature->Feature));
1057
1058 feature->Feature_Parent = msi_dup_record_field( row, 2 );
1059 feature->Title = msi_dup_record_field( row, 3 );
1060 feature->Description = msi_dup_record_field( row, 4 );
1061
1062 if (!MSI_RecordIsNull(row,5))
1063 feature->Display = MSI_RecordGetInteger(row,5);
1064
1065 feature->Level= MSI_RecordGetInteger(row,6);
1066 feature->Directory = msi_dup_record_field( row, 7 );
1067 feature->Attributes = MSI_RecordGetInteger(row,8);
1068
1069 feature->Installed = INSTALLSTATE_UNKNOWN;
1070 feature->Action = INSTALLSTATE_UNKNOWN;
1071 feature->ActionRequest = INSTALLSTATE_UNKNOWN;
1072
1073 list_add_tail( &package->features, &feature->entry );
1074
1075 /* load feature components */
1076
1077 rc = MSI_OpenQuery( package->db, &view, query, feature->Feature );
1078 if (rc != ERROR_SUCCESS)
1079 return ERROR_SUCCESS;
1080
1081 ilfs.package = package;
1082 ilfs.feature = feature;
1083
1084 rc = MSI_IterateRecords(view, NULL, iterate_load_featurecomponents , &ilfs);
1085 msiobj_release(&view->hdr);
1086 return rc;
1087 }
1088
1089 static UINT find_feature_children(MSIRECORD * row, LPVOID param)
1090 {
1091 MSIPACKAGE *package = param;
1092 MSIFEATURE *parent, *child;
1093
1094 child = msi_get_loaded_feature( package, MSI_RecordGetString( row, 1 ) );
1095 if (!child)
1096 return ERROR_FUNCTION_FAILED;
1097
1098 if (!child->Feature_Parent)
1099 return ERROR_SUCCESS;
1100
1101 parent = msi_get_loaded_feature( package, child->Feature_Parent );
1102 if (!parent)
1103 return ERROR_FUNCTION_FAILED;
1104
1105 add_feature_child( parent, child );
1106 return ERROR_SUCCESS;
1107 }
1108
1109 UINT msi_load_all_features( MSIPACKAGE *package )
1110 {
1111 static const WCHAR query[] = {
1112 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1113 '`','F','e','a','t','u','r','e','`',' ','O','R','D','E','R',' ','B','Y',' ',
1114 '`','D','i','s','p','l','a','y','`',0};
1115 MSIQUERY *view;
1116 UINT r;
1117
1118 if (!list_empty(&package->features))
1119 return ERROR_SUCCESS;
1120
1121 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1122 if (r != ERROR_SUCCESS)
1123 return r;
1124
1125 r = MSI_IterateRecords( view, NULL, load_feature, package );
1126 if (r != ERROR_SUCCESS)
1127 {
1128 msiobj_release( &view->hdr );
1129 return r;
1130 }
1131 r = MSI_IterateRecords( view, NULL, find_feature_children, package );
1132 msiobj_release( &view->hdr );
1133 return r;
1134 }
1135
1136 static LPWSTR folder_split_path(LPWSTR p, WCHAR ch)
1137 {
1138 if (!p)
1139 return p;
1140 p = strchrW(p, ch);
1141 if (!p)
1142 return p;
1143 *p = 0;
1144 return p+1;
1145 }
1146
1147 static UINT load_file_hash(MSIPACKAGE *package, MSIFILE *file)
1148 {
1149 static const WCHAR query[] = {
1150 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1151 '`','M','s','i','F','i','l','e','H','a','s','h','`',' ',
1152 'W','H','E','R','E',' ','`','F','i','l','e','_','`',' ','=',' ','\'','%','s','\'',0};
1153 MSIQUERY *view = NULL;
1154 MSIRECORD *row = NULL;
1155 UINT r;
1156
1157 TRACE("%s\n", debugstr_w(file->File));
1158
1159 r = MSI_OpenQuery(package->db, &view, query, file->File);
1160 if (r != ERROR_SUCCESS)
1161 goto done;
1162
1163 r = MSI_ViewExecute(view, NULL);
1164 if (r != ERROR_SUCCESS)
1165 goto done;
1166
1167 r = MSI_ViewFetch(view, &row);
1168 if (r != ERROR_SUCCESS)
1169 goto done;
1170
1171 file->hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
1172 file->hash.dwData[0] = MSI_RecordGetInteger(row, 3);
1173 file->hash.dwData[1] = MSI_RecordGetInteger(row, 4);
1174 file->hash.dwData[2] = MSI_RecordGetInteger(row, 5);
1175 file->hash.dwData[3] = MSI_RecordGetInteger(row, 6);
1176
1177 done:
1178 if (view) msiobj_release(&view->hdr);
1179 if (row) msiobj_release(&row->hdr);
1180 return r;
1181 }
1182
1183 static UINT load_file_disk_id( MSIPACKAGE *package, MSIFILE *file )
1184 {
1185 MSIRECORD *row;
1186 static const WCHAR query[] = {
1187 'S','E','L','E','C','T',' ','`','D','i','s','k','I','d','`',' ', 'F','R','O','M',' ',
1188 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
1189 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ','>','=',' ','%','i',0};
1190
1191 row = MSI_QueryGetRecord( package->db, query, file->Sequence );
1192 if (!row)
1193 {
1194 WARN("query failed\n");
1195 return ERROR_FUNCTION_FAILED;
1196 }
1197
1198 file->disk_id = MSI_RecordGetInteger( row, 1 );
1199 msiobj_release( &row->hdr );
1200 return ERROR_SUCCESS;
1201 }
1202
1203 static UINT load_file(MSIRECORD *row, LPVOID param)
1204 {
1205 MSIPACKAGE* package = param;
1206 LPCWSTR component;
1207 MSIFILE *file;
1208
1209 /* fill in the data */
1210
1211 file = msi_alloc_zero( sizeof (MSIFILE) );
1212 if (!file)
1213 return ERROR_NOT_ENOUGH_MEMORY;
1214
1215 file->File = msi_dup_record_field( row, 1 );
1216
1217 component = MSI_RecordGetString( row, 2 );
1218 file->Component = msi_get_loaded_component( package, component );
1219
1220 if (!file->Component)
1221 {
1222 WARN("Component not found: %s\n", debugstr_w(component));
1223 msi_free(file->File);
1224 msi_free(file);
1225 return ERROR_SUCCESS;
1226 }
1227
1228 file->FileName = msi_dup_record_field( row, 3 );
1229 msi_reduce_to_long_filename( file->FileName );
1230
1231 file->ShortName = msi_dup_record_field( row, 3 );
1232 file->LongName = strdupW( folder_split_path(file->ShortName, '|'));
1233
1234 file->FileSize = MSI_RecordGetInteger( row, 4 );
1235 file->Version = msi_dup_record_field( row, 5 );
1236 file->Language = msi_dup_record_field( row, 6 );
1237 file->Attributes = MSI_RecordGetInteger( row, 7 );
1238 file->Sequence = MSI_RecordGetInteger( row, 8 );
1239
1240 file->state = msifs_invalid;
1241
1242 /* if the compressed bits are not set in the file attributes,
1243 * then read the information from the package word count property
1244 */
1245 if (package->WordCount & msidbSumInfoSourceTypeAdminImage)
1246 {
1247 file->IsCompressed = FALSE;
1248 }
1249 else if (file->Attributes &
1250 (msidbFileAttributesCompressed | msidbFileAttributesPatchAdded))
1251 {
1252 file->IsCompressed = TRUE;
1253 }
1254 else if (file->Attributes & msidbFileAttributesNoncompressed)
1255 {
1256 file->IsCompressed = FALSE;
1257 }
1258 else
1259 {
1260 file->IsCompressed = package->WordCount & msidbSumInfoSourceTypeCompressed;
1261 }
1262
1263 load_file_hash(package, file);
1264 load_file_disk_id(package, file);
1265
1266 TRACE("File loaded (file %s sequence %u)\n", debugstr_w(file->File), file->Sequence);
1267
1268 list_add_tail( &package->files, &file->entry );
1269
1270 return ERROR_SUCCESS;
1271 }
1272
1273 static UINT load_all_files(MSIPACKAGE *package)
1274 {
1275 static const WCHAR query[] = {
1276 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1277 '`','F','i','l','e','`',' ', 'O','R','D','E','R',' ','B','Y',' ',
1278 '`','S','e','q','u','e','n','c','e','`', 0};
1279 MSIQUERY *view;
1280 UINT rc;
1281
1282 if (!list_empty(&package->files))
1283 return ERROR_SUCCESS;
1284
1285 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
1286 if (rc != ERROR_SUCCESS)
1287 return ERROR_SUCCESS;
1288
1289 rc = MSI_IterateRecords(view, NULL, load_file, package);
1290 msiobj_release(&view->hdr);
1291 return rc;
1292 }
1293
1294 static UINT load_media( MSIRECORD *row, LPVOID param )
1295 {
1296 MSIPACKAGE *package = param;
1297 UINT disk_id = MSI_RecordGetInteger( row, 1 );
1298 const WCHAR *cabinet = MSI_RecordGetString( row, 4 );
1299
1300 /* FIXME: load external cabinets and directory sources too */
1301 if (!cabinet || cabinet[0] != '#' || disk_id >= MSI_INITIAL_MEDIA_TRANSFORM_DISKID)
1302 return ERROR_SUCCESS;
1303
1304 return msi_add_cabinet_stream( package, disk_id, package->db->storage, cabinet );
1305 }
1306
1307 static UINT load_all_media( MSIPACKAGE *package )
1308 {
1309 static const WCHAR query[] = {
1310 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`',
1311 'M','e','d','i','a','`',' ','O','R','D','E','R',' ','B','Y',' ',
1312 '`','D','i','s','k','I','d','`',0};
1313 MSIQUERY *view;
1314 UINT r;
1315
1316 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1317 if (r != ERROR_SUCCESS)
1318 return ERROR_SUCCESS;
1319
1320 r = MSI_IterateRecords( view, NULL, load_media, package );
1321 msiobj_release( &view->hdr );
1322 return r;
1323 }
1324
1325 static UINT load_patch_disk_id( MSIPACKAGE *package, MSIFILEPATCH *patch )
1326 {
1327 static const WCHAR query[] =
1328 {'S','E','L','E','C','T',' ','`','D','i','s','k','I','d','`',' ', 'F','R','O','M',' ',
1329 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
1330 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ','>','=',' ','%','u',0};
1331 MSIRECORD *rec;
1332
1333 if (!(rec = MSI_QueryGetRecord( package->db, query, patch->Sequence )))
1334 {
1335 WARN("query failed\n");
1336 return ERROR_FUNCTION_FAILED;
1337 }
1338
1339 patch->disk_id = MSI_RecordGetInteger( rec, 1 );
1340 msiobj_release( &rec->hdr );
1341 return ERROR_SUCCESS;
1342 }
1343
1344 static UINT load_patch(MSIRECORD *row, LPVOID param)
1345 {
1346 MSIPACKAGE *package = param;
1347 MSIFILEPATCH *patch;
1348 const WCHAR *file_key;
1349
1350 patch = msi_alloc_zero( sizeof (MSIFILEPATCH) );
1351 if (!patch)
1352 return ERROR_NOT_ENOUGH_MEMORY;
1353
1354 file_key = MSI_RecordGetString( row, 1 );
1355 patch->File = msi_get_loaded_file( package, file_key );
1356 if (!patch->File)
1357 {
1358 ERR("Failed to find target for patch in File table\n");
1359 msi_free(patch);
1360 return ERROR_FUNCTION_FAILED;
1361 }
1362
1363 patch->Sequence = MSI_RecordGetInteger( row, 2 );
1364 patch->PatchSize = MSI_RecordGetInteger( row, 3 );
1365 patch->Attributes = MSI_RecordGetInteger( row, 4 );
1366
1367 /* FIXME:
1368 * Header field - for patch validation.
1369 * _StreamRef - External key into MsiPatchHeaders (instead of the header field)
1370 */
1371
1372 load_patch_disk_id( package, patch );
1373
1374 TRACE("Patch loaded (file %s sequence %u)\n", debugstr_w(patch->File->File), patch->Sequence);
1375
1376 list_add_tail( &package->filepatches, &patch->entry );
1377
1378 return ERROR_SUCCESS;
1379 }
1380
1381 static UINT load_all_patches(MSIPACKAGE *package)
1382 {
1383 static const WCHAR query[] = {
1384 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1385 '`','P','a','t','c','h','`',' ','O','R','D','E','R',' ','B','Y',' ',
1386 '`','S','e','q','u','e','n','c','e','`',0};
1387 MSIQUERY *view;
1388 UINT rc;
1389
1390 if (!list_empty(&package->filepatches))
1391 return ERROR_SUCCESS;
1392
1393 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
1394 if (rc != ERROR_SUCCESS)
1395 return ERROR_SUCCESS;
1396
1397 rc = MSI_IterateRecords(view, NULL, load_patch, package);
1398 msiobj_release(&view->hdr);
1399 return rc;
1400 }
1401
1402 static UINT load_folder_persistence( MSIPACKAGE *package, MSIFOLDER *folder )
1403 {
1404 static const WCHAR query[] = {
1405 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1406 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',' ','W','H','E','R','E',' ',
1407 '`','D','i','r','e','c','t','o','r','y','_','`',' ','=','\'','%','s','\'',0};
1408 MSIQUERY *view;
1409
1410 folder->persistent = FALSE;
1411 if (!MSI_OpenQuery( package->db, &view, query, folder->Directory ))
1412 {
1413 if (!MSI_ViewExecute( view, NULL ))
1414 {
1415 MSIRECORD *rec;
1416 if (!MSI_ViewFetch( view, &rec ))
1417 {
1418 TRACE("directory %s is persistent\n", debugstr_w(folder->Directory));
1419 folder->persistent = TRUE;
1420 msiobj_release( &rec->hdr );
1421 }
1422 }
1423 msiobj_release( &view->hdr );
1424 }
1425 return ERROR_SUCCESS;
1426 }
1427
1428 static UINT load_folder( MSIRECORD *row, LPVOID param )
1429 {
1430 MSIPACKAGE *package = param;
1431 static WCHAR szEmpty[] = { 0 };
1432 LPWSTR p, tgt_short, tgt_long, src_short, src_long;
1433 MSIFOLDER *folder;
1434
1435 if (!(folder = msi_alloc_zero( sizeof(*folder) ))) return ERROR_NOT_ENOUGH_MEMORY;
1436 list_init( &folder->children );
1437 folder->Directory = msi_dup_record_field( row, 1 );
1438 folder->Parent = msi_dup_record_field( row, 2 );
1439 p = msi_dup_record_field(row, 3);
1440
1441 TRACE("%s\n", debugstr_w(folder->Directory));
1442
1443 /* split src and target dir */
1444 tgt_short = p;
1445 src_short = folder_split_path( p, ':' );
1446
1447 /* split the long and short paths */
1448 tgt_long = folder_split_path( tgt_short, '|' );
1449 src_long = folder_split_path( src_short, '|' );
1450
1451 /* check for no-op dirs */
1452 if (tgt_short && !strcmpW( szDot, tgt_short ))
1453 tgt_short = szEmpty;
1454 if (src_short && !strcmpW( szDot, src_short ))
1455 src_short = szEmpty;
1456
1457 if (!tgt_long)
1458 tgt_long = tgt_short;
1459
1460 if (!src_short) {
1461 src_short = tgt_short;
1462 src_long = tgt_long;
1463 }
1464
1465 if (!src_long)
1466 src_long = src_short;
1467
1468 /* FIXME: use the target short path too */
1469 folder->TargetDefault = strdupW(tgt_long);
1470 folder->SourceShortPath = strdupW(src_short);
1471 folder->SourceLongPath = strdupW(src_long);
1472 msi_free(p);
1473
1474 TRACE("TargetDefault = %s\n",debugstr_w( folder->TargetDefault ));
1475 TRACE("SourceLong = %s\n", debugstr_w( folder->SourceLongPath ));
1476 TRACE("SourceShort = %s\n", debugstr_w( folder->SourceShortPath ));
1477
1478 load_folder_persistence( package, folder );
1479
1480 list_add_tail( &package->folders, &folder->entry );
1481 return ERROR_SUCCESS;
1482 }
1483
1484 static UINT add_folder_child( MSIFOLDER *parent, MSIFOLDER *child )
1485 {
1486 FolderList *fl;
1487
1488 if (!(fl = msi_alloc( sizeof(*fl) ))) return ERROR_NOT_ENOUGH_MEMORY;
1489 fl->folder = child;
1490 list_add_tail( &parent->children, &fl->entry );
1491 return ERROR_SUCCESS;
1492 }
1493
1494 static UINT find_folder_children( MSIRECORD *row, LPVOID param )
1495 {
1496 MSIPACKAGE *package = param;
1497 MSIFOLDER *parent, *child;
1498
1499 if (!(child = msi_get_loaded_folder( package, MSI_RecordGetString( row, 1 ) )))
1500 return ERROR_FUNCTION_FAILED;
1501
1502 if (!child->Parent) return ERROR_SUCCESS;
1503
1504 if (!(parent = msi_get_loaded_folder( package, child->Parent )))
1505 return ERROR_FUNCTION_FAILED;
1506
1507 return add_folder_child( parent, child );
1508 }
1509
1510 static UINT load_all_folders( MSIPACKAGE *package )
1511 {
1512 static const WCHAR query[] = {
1513 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1514 '`','D','i','r','e','c','t','o','r','y','`',0};
1515 MSIQUERY *view;
1516 UINT r;
1517
1518 if (!list_empty(&package->folders))
1519 return ERROR_SUCCESS;
1520
1521 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1522 if (r != ERROR_SUCCESS)
1523 return r;
1524
1525 r = MSI_IterateRecords( view, NULL, load_folder, package );
1526 if (r != ERROR_SUCCESS)
1527 {
1528 msiobj_release( &view->hdr );
1529 return r;
1530 }
1531 r = MSI_IterateRecords( view, NULL, find_folder_children, package );
1532 msiobj_release( &view->hdr );
1533 return r;
1534 }
1535
1536 static UINT ACTION_CostInitialize(MSIPACKAGE *package)
1537 {
1538 msi_set_property( package->db, szCostingComplete, szZero, -1 );
1539 msi_set_property( package->db, szRootDrive, szCRoot, -1 );
1540
1541 load_all_folders( package );
1542 msi_load_all_components( package );
1543 msi_load_all_features( package );
1544 load_all_files( package );
1545 load_all_patches( package );
1546 load_all_media( package );
1547
1548 return ERROR_SUCCESS;
1549 }
1550
1551 static UINT execute_script_action( MSIPACKAGE *package, UINT script, UINT index )
1552 {
1553 const WCHAR *action = package->script->Actions[script][index];
1554 ui_actionstart( package, action );
1555 TRACE("executing %s\n", debugstr_w(action));
1556 return ACTION_PerformAction( package, action, script );
1557 }
1558
1559 static UINT execute_script( MSIPACKAGE *package, UINT script )
1560 {
1561 UINT i, rc = ERROR_SUCCESS;
1562
1563 TRACE("executing script %u\n", script);
1564
1565 if (!package->script)
1566 {
1567 ERR("no script!\n");
1568 return ERROR_FUNCTION_FAILED;
1569 }
1570 if (script == SCRIPT_ROLLBACK)
1571 {
1572 for (i = package->script->ActionCount[script]; i > 0; i--)
1573 {
1574 rc = execute_script_action( package, script, i - 1 );
1575 if (rc != ERROR_SUCCESS) break;
1576 }
1577 }
1578 else
1579 {
1580 for (i = 0; i < package->script->ActionCount[script]; i++)
1581 {
1582 rc = execute_script_action( package, script, i );
1583 if (rc != ERROR_SUCCESS) break;
1584 }
1585 }
1586 msi_free_action_script(package, script);
1587 return rc;
1588 }
1589
1590 static UINT ACTION_FileCost(MSIPACKAGE *package)
1591 {
1592 return ERROR_SUCCESS;
1593 }
1594
1595 static void get_client_counts( MSIPACKAGE *package )
1596 {
1597 MSICOMPONENT *comp;
1598 HKEY hkey;
1599
1600 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
1601 {
1602 if (!comp->ComponentId) continue;
1603
1604 if (MSIREG_OpenUserDataComponentKey( comp->ComponentId, szLocalSid, &hkey, FALSE ) &&
1605 MSIREG_OpenUserDataComponentKey( comp->ComponentId, NULL, &hkey, FALSE ))
1606 {
1607 comp->num_clients = 0;
1608 continue;
1609 }
1610 RegQueryInfoKeyW( hkey, NULL, NULL, NULL, NULL, NULL, NULL, (DWORD *)&comp->num_clients,
1611 NULL, NULL, NULL, NULL );
1612 RegCloseKey( hkey );
1613 }
1614 }
1615
1616 static void ACTION_GetComponentInstallStates(MSIPACKAGE *package)
1617 {
1618 MSICOMPONENT *comp;
1619 UINT r;
1620
1621 LIST_FOR_EACH_ENTRY(comp, &package->components, MSICOMPONENT, entry)
1622 {
1623 if (!comp->ComponentId) continue;
1624
1625 r = MsiQueryComponentStateW( package->ProductCode, NULL,
1626 MSIINSTALLCONTEXT_USERMANAGED, comp->ComponentId,
1627 &comp->Installed );
1628 if (r == ERROR_SUCCESS) continue;
1629
1630 r = MsiQueryComponentStateW( package->ProductCode, NULL,
1631 MSIINSTALLCONTEXT_USERUNMANAGED, comp->ComponentId,
1632 &comp->Installed );
1633 if (r == ERROR_SUCCESS) continue;
1634
1635 r = MsiQueryComponentStateW( package->ProductCode, NULL,
1636 MSIINSTALLCONTEXT_MACHINE, comp->ComponentId,
1637 &comp->Installed );
1638 if (r == ERROR_SUCCESS) continue;
1639
1640 comp->Installed = INSTALLSTATE_ABSENT;
1641 }
1642 }
1643
1644 static void ACTION_GetFeatureInstallStates(MSIPACKAGE *package)
1645 {
1646 MSIFEATURE *feature;
1647
1648 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1649 {
1650 INSTALLSTATE state = MsiQueryFeatureStateW( package->ProductCode, feature->Feature );
1651
1652 if (state == INSTALLSTATE_UNKNOWN || state == INSTALLSTATE_INVALIDARG)
1653 feature->Installed = INSTALLSTATE_ABSENT;
1654 else
1655 feature->Installed = state;
1656 }
1657 }
1658
1659 static inline BOOL is_feature_selected( MSIFEATURE *feature, INT level )
1660 {
1661 return (feature->Level > 0 && feature->Level <= level);
1662 }
1663
1664 static BOOL process_state_property(MSIPACKAGE* package, int level,
1665 LPCWSTR property, INSTALLSTATE state)
1666 {
1667 LPWSTR override;
1668 MSIFEATURE *feature;
1669 BOOL remove = !strcmpW(property, szRemove);
1670 BOOL reinstall = !strcmpW(property, szReinstall);
1671
1672 override = msi_dup_property( package->db, property );
1673 if (!override)
1674 return FALSE;
1675
1676 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1677 {
1678 if (feature->Level <= 0)
1679 continue;
1680
1681 if (reinstall)
1682 state = (feature->Installed == INSTALLSTATE_ABSENT ? INSTALLSTATE_UNKNOWN : feature->Installed);
1683 else if (remove)
1684 state = (feature->Installed == INSTALLSTATE_ABSENT ? INSTALLSTATE_UNKNOWN : INSTALLSTATE_ABSENT);
1685
1686 if (!strcmpiW( override, szAll ))
1687 {
1688 feature->Action = state;
1689 feature->ActionRequest = state;
1690 }
1691 else
1692 {
1693 LPWSTR ptr = override;
1694 LPWSTR ptr2 = strchrW(override,',');
1695
1696 while (ptr)
1697 {
1698 int len = ptr2 - ptr;
1699
1700 if ((ptr2 && strlenW(feature->Feature) == len && !strncmpW(ptr, feature->Feature, len))
1701 || (!ptr2 && !strcmpW(ptr, feature->Feature)))
1702 {
1703 feature->Action = state;
1704 feature->ActionRequest = state;
1705 break;
1706 }
1707 if (ptr2)
1708 {
1709 ptr=ptr2+1;
1710 ptr2 = strchrW(ptr,',');
1711 }
1712 else
1713 break;
1714 }
1715 }
1716 }
1717 msi_free(override);
1718 return TRUE;
1719 }
1720
1721 static BOOL process_overrides( MSIPACKAGE *package, int level )
1722 {
1723 static const WCHAR szAddLocal[] =
1724 {'A','D','D','L','O','C','A','L',0};
1725 static const WCHAR szAddSource[] =
1726 {'A','D','D','S','O','U','R','C','E',0};
1727 static const WCHAR szAdvertise[] =
1728 {'A','D','V','E','R','T','I','S','E',0};
1729 BOOL ret = FALSE;
1730
1731 /* all these activation/deactivation things happen in order and things
1732 * later on the list override things earlier on the list.
1733 *
1734 * 0 INSTALLLEVEL processing
1735 * 1 ADDLOCAL
1736 * 2 REMOVE
1737 * 3 ADDSOURCE
1738 * 4 ADDDEFAULT
1739 * 5 REINSTALL
1740 * 6 ADVERTISE
1741 * 7 COMPADDLOCAL
1742 * 8 COMPADDSOURCE
1743 * 9 FILEADDLOCAL
1744 * 10 FILEADDSOURCE
1745 * 11 FILEADDDEFAULT
1746 */
1747 ret |= process_state_property( package, level, szAddLocal, INSTALLSTATE_LOCAL );
1748 ret |= process_state_property( package, level, szRemove, INSTALLSTATE_ABSENT );
1749 ret |= process_state_property( package, level, szAddSource, INSTALLSTATE_SOURCE );
1750 ret |= process_state_property( package, level, szReinstall, INSTALLSTATE_UNKNOWN );
1751 ret |= process_state_property( package, level, szAdvertise, INSTALLSTATE_ADVERTISED );
1752
1753 if (ret && !package->full_reinstall)
1754 msi_set_property( package->db, szPreselected, szOne, -1 );
1755
1756 return ret;
1757 }
1758
1759 static void disable_children( MSIFEATURE *feature, int level )
1760 {
1761 FeatureList *fl;
1762
1763 LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
1764 {
1765 if (!is_feature_selected( feature, level ))
1766 {
1767 TRACE("child %s (level %d request %d) follows disabled parent %s (level %d request %d)\n",
1768 debugstr_w(fl->feature->Feature), fl->feature->Level, fl->feature->ActionRequest,
1769 debugstr_w(feature->Feature), feature->Level, feature->ActionRequest);
1770
1771 fl->feature->Level = feature->Level;
1772 fl->feature->Action = INSTALLSTATE_UNKNOWN;
1773 fl->feature->ActionRequest = INSTALLSTATE_UNKNOWN;
1774 }
1775 disable_children( fl->feature, level );
1776 }
1777 }
1778
1779 static void follow_parent( MSIFEATURE *feature )
1780 {
1781 FeatureList *fl;
1782
1783 LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
1784 {
1785 if (fl->feature->Attributes & msidbFeatureAttributesFollowParent)
1786 {
1787 TRACE("child %s (level %d request %d) follows parent %s (level %d request %d)\n",
1788 debugstr_w(fl->feature->Feature), fl->feature->Level, fl->feature->ActionRequest,
1789 debugstr_w(feature->Feature), feature->Level, feature->ActionRequest);
1790
1791 fl->feature->Action = feature->Action;
1792 fl->feature->ActionRequest = feature->ActionRequest;
1793 }
1794 follow_parent( fl->feature );
1795 }
1796 }
1797
1798 UINT MSI_SetFeatureStates(MSIPACKAGE *package)
1799 {
1800 int level;
1801 MSICOMPONENT* component;
1802 MSIFEATURE *feature;
1803
1804 TRACE("Checking Install Level\n");
1805
1806 level = msi_get_property_int(package->db, szInstallLevel, 1);
1807
1808 if (!msi_get_property_int( package->db, szPreselected, 0 ))
1809 {
1810 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1811 {
1812 if (!is_feature_selected( feature, level )) continue;
1813
1814 if (feature->ActionRequest == INSTALLSTATE_UNKNOWN)
1815 {
1816 if (feature->Attributes & msidbFeatureAttributesFavorSource)
1817 {
1818 feature->Action = INSTALLSTATE_SOURCE;
1819 feature->ActionRequest = INSTALLSTATE_SOURCE;
1820 }
1821 else if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1822 {
1823 feature->Action = INSTALLSTATE_ADVERTISED;
1824 feature->ActionRequest = INSTALLSTATE_ADVERTISED;
1825 }
1826 else
1827 {
1828 feature->Action = INSTALLSTATE_LOCAL;
1829 feature->ActionRequest = INSTALLSTATE_LOCAL;
1830 }
1831 }
1832 }
1833 /* disable child features of unselected parent or follow parent */
1834 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1835 {
1836 if (feature->Feature_Parent) continue;
1837 disable_children( feature, level );
1838 follow_parent( feature );
1839 }
1840 }
1841 else /* preselected */
1842 {
1843 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1844 {
1845 if (!is_feature_selected( feature, level )) continue;
1846
1847 if (feature->ActionRequest == INSTALLSTATE_UNKNOWN)
1848 {
1849 if (feature->Installed == INSTALLSTATE_ABSENT)
1850 {
1851 feature->Action = INSTALLSTATE_UNKNOWN;
1852 feature->ActionRequest = INSTALLSTATE_UNKNOWN;
1853 }
1854 else
1855 {
1856 feature->Action = feature->Installed;
1857 feature->ActionRequest = feature->Installed;
1858 }
1859 }
1860 }
1861 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1862 {
1863 if (feature->Feature_Parent) continue;
1864 disable_children( feature, level );
1865 follow_parent( feature );
1866 }
1867 }
1868
1869 /* now we want to set component state based based on feature state */
1870 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1871 {
1872 ComponentList *cl;
1873
1874 TRACE("examining feature %s (level %d installed %d request %d action %d)\n",
1875 debugstr_w(feature->Feature), feature->Level, feature->Installed,
1876 feature->ActionRequest, feature->Action);
1877
1878 /* features with components that have compressed files are made local */
1879 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1880 {
1881 if (cl->component->ForceLocalState &&
1882 feature->ActionRequest == INSTALLSTATE_SOURCE)
1883 {
1884 feature->Action = INSTALLSTATE_LOCAL;
1885 feature->ActionRequest = INSTALLSTATE_LOCAL;
1886 break;
1887 }
1888 }
1889
1890 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1891 {
1892 component = cl->component;
1893
1894 switch (feature->ActionRequest)
1895 {
1896 case INSTALLSTATE_ABSENT:
1897 component->anyAbsent = 1;
1898 break;
1899 case INSTALLSTATE_ADVERTISED:
1900 component->hasAdvertisedFeature = 1;
1901 break;
1902 case INSTALLSTATE_SOURCE:
1903 component->hasSourceFeature = 1;
1904 break;
1905 case INSTALLSTATE_LOCAL:
1906 component->hasLocalFeature = 1;
1907 break;
1908 case INSTALLSTATE_DEFAULT:
1909 if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1910 component->hasAdvertisedFeature = 1;
1911 else if (feature->Attributes & msidbFeatureAttributesFavorSource)
1912 component->hasSourceFeature = 1;
1913 else
1914 component->hasLocalFeature = 1;
1915 break;
1916 default:
1917 break;
1918 }
1919 }
1920 }
1921
1922 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1923 {
1924 /* check if it's local or source */
1925 if (!(component->Attributes & msidbComponentAttributesOptional) &&
1926 (component->hasLocalFeature || component->hasSourceFeature))
1927 {
1928 if ((component->Attributes & msidbComponentAttributesSourceOnly) &&
1929 !component->ForceLocalState)
1930 {
1931 component->Action = INSTALLSTATE_SOURCE;
1932 component->ActionRequest = INSTALLSTATE_SOURCE;
1933 }
1934 else
1935 {
1936 component->Action = INSTALLSTATE_LOCAL;
1937 component->ActionRequest = INSTALLSTATE_LOCAL;
1938 }
1939 continue;
1940 }
1941
1942 /* if any feature is local, the component must be local too */
1943 if (component->hasLocalFeature)
1944 {
1945 component->Action = INSTALLSTATE_LOCAL;
1946 component->ActionRequest = INSTALLSTATE_LOCAL;
1947 continue;
1948 }
1949 if (component->hasSourceFeature)
1950 {
1951 component->Action = INSTALLSTATE_SOURCE;
1952 component->ActionRequest = INSTALLSTATE_SOURCE;
1953 continue;
1954 }
1955 if (component->hasAdvertisedFeature)
1956 {
1957 component->Action = INSTALLSTATE_ADVERTISED;
1958 component->ActionRequest = INSTALLSTATE_ADVERTISED;
1959 continue;
1960 }
1961 TRACE("nobody wants component %s\n", debugstr_w(component->Component));
1962 if (component->anyAbsent && component->ComponentId)
1963 {
1964 component->Action = INSTALLSTATE_ABSENT;
1965 component->ActionRequest = INSTALLSTATE_ABSENT;
1966 }
1967 }
1968
1969 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1970 {
1971 if (component->ActionRequest == INSTALLSTATE_DEFAULT)
1972 {
1973 TRACE("%s was default, setting to local\n", debugstr_w(component->Component));
1974 component->Action = INSTALLSTATE_LOCAL;
1975 component->ActionRequest = INSTALLSTATE_LOCAL;
1976 }
1977
1978 if (component->ActionRequest == INSTALLSTATE_SOURCE &&
1979 component->Installed == INSTALLSTATE_SOURCE &&
1980 component->hasSourceFeature)
1981 {
1982 component->Action = INSTALLSTATE_UNKNOWN;
1983 component->ActionRequest = INSTALLSTATE_UNKNOWN;
1984 }
1985
1986 TRACE("component %s (installed %d request %d action %d)\n",
1987 debugstr_w(component->Component), component->Installed, component->ActionRequest, component->Action);
1988
1989 if (component->Action == INSTALLSTATE_LOCAL || component->Action == INSTALLSTATE_SOURCE)
1990 component->num_clients++;
1991 else if (component->Action == INSTALLSTATE_ABSENT)
1992 component->num_clients--;
1993 }
1994
1995 return ERROR_SUCCESS;
1996 }
1997
1998 static UINT ITERATE_CostFinalizeConditions(MSIRECORD *row, LPVOID param)
1999 {
2000 MSIPACKAGE *package = param;
2001 LPCWSTR name;
2002 MSIFEATURE *feature;
2003
2004 name = MSI_RecordGetString( row, 1 );
2005
2006 feature = msi_get_loaded_feature( package, name );
2007 if (!feature)
2008 ERR("FAILED to find loaded feature %s\n",debugstr_w(name));
2009 else
2010 {
2011 LPCWSTR Condition;
2012 Condition = MSI_RecordGetString(row,3);
2013
2014 if (MSI_EvaluateConditionW(package,Condition) == MSICONDITION_TRUE)
2015 {
2016 int level = MSI_RecordGetInteger(row,2);
2017 TRACE("Resetting feature %s to level %i\n", debugstr_w(name), level);
2018 feature->Level = level;
2019 }
2020 }
2021 return ERROR_SUCCESS;
2022 }
2023
2024 VS_FIXEDFILEINFO *msi_get_disk_file_version( LPCWSTR filename )
2025 {
2026 static const WCHAR name[] = {'\\',0};
2027 VS_FIXEDFILEINFO *ptr, *ret;
2028 LPVOID version;
2029 DWORD versize, handle;
2030 UINT sz;
2031
2032 versize = GetFileVersionInfoSizeW( filename, &handle );
2033 if (!versize)
2034 return NULL;
2035
2036 version = msi_alloc( versize );
2037 if (!version)
2038 return NULL;
2039
2040 GetFileVersionInfoW( filename, 0, versize, version );
2041
2042 if (!VerQueryValueW( version, name, (LPVOID *)&ptr, &sz ))
2043 {
2044 msi_free( version );
2045 return NULL;
2046 }
2047
2048 ret = msi_alloc( sz );
2049 memcpy( ret, ptr, sz );
2050
2051 msi_free( version );
2052 return ret;
2053 }
2054
2055 int msi_compare_file_versions( VS_FIXEDFILEINFO *fi, const WCHAR *version )
2056 {
2057 DWORD ms, ls;
2058
2059 msi_parse_version_string( version, &ms, &ls );
2060
2061 if (fi->dwFileVersionMS > ms) return 1;
2062 else if (fi->dwFileVersionMS < ms) return -1;
2063 else if (fi->dwFileVersionLS > ls) return 1;
2064 else if (fi->dwFileVersionLS < ls) return -1;
2065 return 0;
2066 }
2067
2068 int msi_compare_font_versions( const WCHAR *ver1, const WCHAR *ver2 )
2069 {
2070 DWORD ms1, ms2;
2071
2072 msi_parse_version_string( ver1, &ms1, NULL );
2073 msi_parse_version_string( ver2, &ms2, NULL );
2074
2075 if (ms1 > ms2) return 1;
2076 else if (ms1 < ms2) return -1;
2077 return 0;
2078 }
2079
2080 DWORD msi_get_disk_file_size( LPCWSTR filename )
2081 {
2082 HANDLE file;
2083 DWORD size;
2084
2085 file = CreateFileW( filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL );
2086 if (file == INVALID_HANDLE_VALUE)
2087 return INVALID_FILE_SIZE;
2088
2089 size = GetFileSize( file, NULL );
2090 CloseHandle( file );
2091 return size;
2092 }
2093
2094 BOOL msi_file_hash_matches( MSIFILE *file )
2095 {
2096 UINT r;
2097 MSIFILEHASHINFO hash;
2098
2099 hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
2100 r = msi_get_filehash( file->TargetPath, &hash );
2101 if (r != ERROR_SUCCESS)
2102 return FALSE;
2103
2104 return !memcmp( &hash, &file->hash, sizeof(MSIFILEHASHINFO) );
2105 }
2106
2107 static WCHAR *create_temp_dir( MSIDATABASE *db )
2108 {
2109 static UINT id;
2110 WCHAR *ret;
2111
2112 if (!db->tempfolder)
2113 {
2114 WCHAR tmp[MAX_PATH];
2115 UINT len = sizeof(tmp)/sizeof(tmp[0]);
2116
2117 if (msi_get_property( db, szTempFolder, tmp, &len ) ||
2118 GetFileAttributesW( tmp ) != FILE_ATTRIBUTE_DIRECTORY)
2119 {
2120 GetTempPathW( MAX_PATH, tmp );
2121 }
2122 if (!(db->tempfolder = strdupW( tmp ))) return NULL;
2123 }
2124
2125 if ((ret = msi_alloc( (strlenW( db->tempfolder ) + 20) * sizeof(WCHAR) )))
2126 {
2127 for (;;)
2128 {
2129 if (!GetTempFileNameW( db->tempfolder, szMsi, ++id, ret ))
2130 {
2131 msi_free( ret );
2132 return NULL;
2133 }
2134 if (CreateDirectoryW( ret, NULL )) break;
2135 }
2136 }
2137
2138 return ret;
2139 }
2140
2141 /*
2142 * msi_build_directory_name()
2143 *
2144 * This function is to save messing round with directory names
2145 * It handles adding backslashes between path segments,
2146 * and can add \ at the end of the directory name if told to.
2147 *
2148 * It takes a variable number of arguments.
2149 * It always allocates a new string for the result, so make sure
2150 * to free the return value when finished with it.
2151 *
2152 * The first arg is the number of path segments that follow.
2153 * The arguments following count are a list of path segments.
2154 * A path segment may be NULL.
2155 *
2156 * Path segments will be added with a \ separating them.
2157 * A \ will not be added after the last segment, however if the
2158 * last segment is NULL, then the last character will be a \
2159 */
2160 WCHAR *msi_build_directory_name( DWORD count, ... )
2161 {
2162 DWORD sz = 1, i;
2163 WCHAR *dir;
2164 va_list va;
2165
2166 va_start( va, count );
2167 for (i = 0; i < count; i++)
2168 {
2169 const WCHAR *str = va_arg( va, const WCHAR * );
2170 if (str) sz += strlenW( str ) + 1;
2171 }
2172 va_end( va );
2173
2174 dir = msi_alloc( sz * sizeof(WCHAR) );
2175 dir[0] = 0;
2176
2177 va_start( va, count );
2178 for (i = 0; i < count; i++)
2179 {
2180 const WCHAR *str = va_arg( va, const WCHAR * );
2181 if (!str) continue;
2182 strcatW( dir, str );
2183 if ( i + 1 != count && dir[0] && dir[strlenW( dir ) - 1] != '\\') strcatW( dir, szBackSlash );
2184 }
2185 va_end( va );
2186 return dir;
2187 }
2188
2189 BOOL msi_is_global_assembly( MSICOMPONENT *comp )
2190 {
2191 return comp->assembly && !comp->assembly->application;
2192 }
2193
2194 static void set_target_path( MSIPACKAGE *package, MSIFILE *file )
2195 {
2196 msi_free( file->TargetPath );
2197 if (msi_is_global_assembly( file->Component ))
2198 {
2199 MSIASSEMBLY *assembly = file->Component->assembly;
2200
2201 if (!assembly->tempdir) assembly->tempdir = create_temp_dir( package->db );
2202 file->TargetPath = msi_build_directory_name( 2, assembly->tempdir, file->FileName );
2203 }
2204 else
2205 {
2206 const WCHAR *dir = msi_get_target_folder( package, file->Component->Directory );
2207 file->TargetPath = msi_build_directory_name( 2, dir, file->FileName );
2208 }
2209
2210 TRACE("file %s resolves to %s\n", debugstr_w(file->File), debugstr_w(file->TargetPath));
2211 }
2212
2213 static UINT calculate_file_cost( MSIPACKAGE *package )
2214 {
2215 VS_FIXEDFILEINFO *file_version;
2216 WCHAR *font_version;
2217 MSIFILE *file;
2218
2219 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2220 {
2221 MSICOMPONENT *comp = file->Component;
2222 DWORD file_size;
2223
2224 if (!comp->Enabled) continue;
2225
2226 if (file->IsCompressed)
2227 comp->ForceLocalState = TRUE;
2228
2229 set_target_path( package, file );
2230
2231 if ((comp->assembly && !comp->assembly->installed) ||
2232 GetFileAttributesW(file->TargetPath) == INVALID_FILE_ATTRIBUTES)
2233 {
2234 comp->Cost += file->FileSize;
2235 continue;
2236 }
2237 file_size = msi_get_disk_file_size( file->TargetPath );
2238 TRACE("%s (size %u)\n", debugstr_w(file->TargetPath), file_size);
2239
2240 if (file->Version)
2241 {
2242 if ((file_version = msi_get_disk_file_version( file->TargetPath )))
2243 {
2244 if (msi_compare_file_versions( file_version, file->Version ) < 0)
2245 {
2246 comp->Cost += file->FileSize - file_size;
2247 }
2248 msi_free( file_version );
2249 continue;
2250 }
2251 else if ((font_version = msi_font_version_from_file( file->TargetPath )))
2252 {
2253 if (msi_compare_font_versions( font_version, file->Version ) < 0)
2254 {
2255 comp->Cost += file->FileSize - file_size;
2256 }
2257 msi_free( font_version );
2258 continue;
2259 }
2260 }
2261 if (file_size != file->FileSize)
2262 {
2263 comp->Cost += file->FileSize - file_size;
2264 }
2265 }
2266 return ERROR_SUCCESS;
2267 }
2268
2269 WCHAR *msi_normalize_path( const WCHAR *in )
2270 {
2271 const WCHAR *p = in;
2272 WCHAR *q, *ret;
2273 int n, len = strlenW( in ) + 2;
2274
2275 if (!(q = ret = msi_alloc( len * sizeof(WCHAR) ))) return NULL;
2276
2277 len = 0;
2278 while (1)
2279 {
2280 /* copy until the end of the string or a space */
2281 while (*p != ' ' && (*q = *p))
2282 {
2283 p++, len++;
2284 /* reduce many backslashes to one */
2285 if (*p != '\\' || *q != '\\')
2286 q++;
2287 }
2288
2289 /* quit at the end of the string */
2290 if (!*p)
2291 break;
2292
2293 /* count the number of spaces */
2294 n = 0;
2295 while (p[n] == ' ')
2296 n++;
2297
2298 /* if it's leading or trailing space, skip it */
2299 if ( len == 0 || p[-1] == '\\' || p[n] == '\\' )
2300 p += n;
2301 else /* copy n spaces */
2302 while (n && (*q++ = *p++)) n--;
2303 }
2304 while (q - ret > 0 && q[-1] == ' ') q--;
2305 if (q - ret > 0 && q[-1] != '\\')
2306 {
2307 q[0] = '\\';
2308 q[1] = 0;
2309 }
2310 return ret;
2311 }
2312
2313 static WCHAR *get_install_location( MSIPACKAGE *package )
2314 {
2315 HKEY hkey;
2316 WCHAR *path;
2317
2318 if (!package->ProductCode) return NULL;
2319 if (MSIREG_OpenInstallProps( package->ProductCode, package->Context, NULL, &hkey, FALSE )) return NULL;
2320 if ((path = msi_reg_get_val_str( hkey, szInstallLocation )) && !path[0])
2321 {
2322 msi_free( path );
2323 path = NULL;
2324 }
2325 RegCloseKey( hkey );
2326 return path;
2327 }
2328
2329 void msi_resolve_target_folder( MSIPACKAGE *package, const WCHAR *name, BOOL load_prop )
2330 {
2331 FolderList *fl;
2332 MSIFOLDER *folder, *parent, *child;
2333 WCHAR *path, *normalized_path;
2334
2335 TRACE("resolving %s\n", debugstr_w(name));
2336
2337 if (!(folder = msi_get_loaded_folder( package, name ))) return;
2338
2339 if (!strcmpW( folder->Directory, szTargetDir )) /* special resolving for target root dir */
2340 {
2341 if (!(path = get_install_location( package )) &&
2342 (!load_prop || !(path = msi_dup_property( package->db, szTargetDir ))))
2343 {
2344 path = msi_dup_property( package->db, szRootDrive );
2345 }
2346 }
2347 else if (!load_prop || !(path = msi_dup_property( package->db, folder->Directory )))
2348 {
2349 if (folder->Parent && strcmpW( folder->Directory, folder->Parent ))
2350 {
2351 parent = msi_get_loaded_folder( package, folder->Parent );
2352 path = msi_build_directory_name( 3, parent->ResolvedTarget, folder->TargetDefault, NULL );
2353 }
2354 else
2355 path = msi_build_directory_name( 2, folder->TargetDefault, NULL );
2356 }
2357 normalized_path = msi_normalize_path( path );
2358 msi_free( path );
2359 if (folder->ResolvedTarget && !strcmpiW( normalized_path, folder->ResolvedTarget ))
2360 {
2361 TRACE("%s already resolved to %s\n", debugstr_w(name), debugstr_w(folder->ResolvedTarget));
2362 msi_free( normalized_path );
2363 return;
2364 }
2365 msi_set_property( package->db, folder->Directory, normalized_path, -1 );
2366 msi_free( folder->ResolvedTarget );
2367 folder->ResolvedTarget = normalized_path;
2368
2369 LIST_FOR_EACH_ENTRY( fl, &folder->children, FolderList, entry )
2370 {
2371 child = fl->folder;
2372 msi_resolve_target_folder( package, child->Directory, load_prop );
2373 }
2374 TRACE("%s resolves to %s\n", debugstr_w(name), debugstr_w(folder->ResolvedTarget));
2375 }
2376
2377 static ULONGLONG get_volume_space_required( MSIPACKAGE *package )
2378 {
2379 MSICOMPONENT *comp;
2380 ULONGLONG ret = 0;
2381
2382 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2383 {
2384 if (comp->Action == INSTALLSTATE_LOCAL) ret += comp->Cost;
2385 }
2386 return ret;
2387 }
2388
2389 static UINT ACTION_CostFinalize(MSIPACKAGE *package)
2390 {
2391 static const WCHAR query[] =
2392 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2393 '`','C','o','n','d','i','t','i','o','n','`',0};
2394 static const WCHAR szOutOfDiskSpace[] =
2395 {'O','u','t','O','f','D','i','s','k','S','p','a','c','e',0};
2396 static const WCHAR szPrimaryFolder[] =
2397 {'P','R','I','M','A','R','Y','F','O','L','D','E','R',0};
2398 static const WCHAR szPrimaryVolumePath[] =
2399 {'P','r','i','m','a','r','y','V','o','l','u','m','e','P','a','t','h',0};
2400 static const WCHAR szPrimaryVolumeSpaceAvailable[] =
2401 {'P','r','i','m','a','r','y','V','o','l','u','m','e','S','p','a','c','e',
2402 'A','v','a','i','l','a','b','l','e',0};
2403 static const WCHAR szPrimaryVolumeSpaceRequired[] =
2404 {'P','r','i','m','a','r','y','V','o','l','u','m','e','S','p','a','c','e',
2405 'R','e','q','u','i','r','e','d',0};
2406 static const WCHAR szPrimaryVolumeSpaceRemaining[] =
2407 {'P','r','i','m','a','r','y','V','o','l','u','m','e','S','p','a','c','e',
2408 'R','e','m','a','i','n','i','n','g',0};
2409 static const WCHAR szOutOfNoRbDiskSpace[] =
2410 {'O','u','t','O','f','N','o','R','b','D','i','s','k','S','p','a','c','e',0};
2411 MSICOMPONENT *comp;
2412 MSIQUERY *view;
2413 WCHAR *level, *primary_key, *primary_folder;
2414 UINT rc;
2415
2416 TRACE("Building directory properties\n");
2417 msi_resolve_target_folder( package, szTargetDir, TRUE );
2418
2419 TRACE("Evaluating component conditions\n");
2420 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2421 {
2422 if (MSI_EvaluateConditionW( package, comp->Condition ) == MSICONDITION_FALSE)
2423 {
2424 TRACE("Disabling component %s\n", debugstr_w(comp->Component));
2425 comp->Enabled = FALSE;
2426 }
2427 else
2428 comp->Enabled = TRUE;
2429 }
2430 get_client_counts( package );
2431
2432 /* read components states from the registry */
2433 ACTION_GetComponentInstallStates(package);
2434 ACTION_GetFeatureInstallStates(package);
2435
2436 if (!process_overrides( package, msi_get_property_int( package->db, szInstallLevel, 1 ) ))
2437 {
2438 TRACE("Evaluating feature conditions\n");
2439
2440 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
2441 if (rc == ERROR_SUCCESS)
2442 {
2443 rc = MSI_IterateRecords( view, NULL, ITERATE_CostFinalizeConditions, package );
2444 msiobj_release( &view->hdr );
2445 if (rc != ERROR_SUCCESS)
2446 return rc;
2447 }
2448 }
2449
2450 TRACE("Calculating file cost\n");
2451 calculate_file_cost( package );
2452
2453 msi_set_property( package->db, szCostingComplete, szOne, -1 );
2454 /* set default run level if not set */
2455 level = msi_dup_property( package->db, szInstallLevel );
2456 if (!level) msi_set_property( package->db, szInstallLevel, szOne, -1 );
2457 msi_free(level);
2458
2459 if ((rc = MSI_SetFeatureStates( package ))) return rc;
2460
2461 if ((primary_key = msi_dup_property( package->db, szPrimaryFolder )))
2462 {
2463 if ((primary_folder = msi_dup_property( package->db, primary_key )))
2464 {
2465 if (((primary_folder[0] >= 'A' && primary_folder[0] <= 'Z') ||
2466 (primary_folder[0] >= 'a' && primary_folder[0] <= 'z')) && primary_folder[1] == ':')
2467 {
2468 static const WCHAR fmtW[] = {'%','l','u',0};
2469 ULARGE_INTEGER free;
2470 ULONGLONG required;
2471 WCHAR buf[21];
2472
2473 primary_folder[2] = 0;
2474 if (GetDiskFreeSpaceExW( primary_folder, &free, NULL, NULL ))
2475 {
2476 sprintfW( buf, fmtW, free.QuadPart / 512 );
2477 msi_set_property( package->db, szPrimaryVolumeSpaceAvailable, buf, -1 );
2478 }
2479 required = get_volume_space_required( package );
2480 sprintfW( buf, fmtW, required / 512 );
2481 msi_set_property( package->db, szPrimaryVolumeSpaceRequired, buf, -1 );
2482
2483 sprintfW( buf, fmtW, (free.QuadPart - required) / 512 );
2484 msi_set_property( package->db, szPrimaryVolumeSpaceRemaining, buf, -1 );
2485 msi_set_property( package->db, szPrimaryVolumePath, primary_folder, 2 );
2486 }
2487 msi_free( primary_folder );
2488 }
2489 msi_free( primary_key );
2490 }
2491
2492 /* FIXME: check volume disk space */
2493 msi_set_property( package->db, szOutOfDiskSpace, szZero, -1 );
2494 msi_set_property( package->db, szOutOfNoRbDiskSpace, szZero, -1 );
2495
2496 return ERROR_SUCCESS;
2497 }
2498
2499 static BYTE *parse_value( MSIPACKAGE *package, const WCHAR *value, DWORD len, DWORD *type, DWORD *size )
2500 {
2501 BYTE *data;
2502
2503 if (!value)
2504 {
2505 *size = sizeof(WCHAR);
2506 *type = REG_SZ;
2507 if ((data = msi_alloc( *size ))) *(WCHAR *)data = 0;
2508 return data;
2509 }
2510 if (value[0]=='#' && value[1]!='#' && value[1]!='%')
2511 {
2512 if (value[1]=='x')
2513 {
2514 LPWSTR ptr;
2515 CHAR byte[5];
2516 LPWSTR deformated = NULL;
2517 int count;
2518
2519 deformat_string(package, &value[2], &deformated);
2520
2521 /* binary value type */
2522 ptr = deformated;
2523 *type = REG_BINARY;
2524 if (strlenW(ptr)%2)
2525 *size = (strlenW(ptr)/2)+1;
2526 else
2527 *size = strlenW(ptr)/2;
2528
2529 data = msi_alloc(*size);
2530
2531 byte[0] = '0';
2532 byte[1] = 'x';
2533 byte[4] = 0;
2534 count = 0;
2535 /* if uneven pad with a zero in front */
2536 if (strlenW(ptr)%2)
2537 {
2538 byte[2]= '0';
2539 byte[3]= *ptr;
2540 ptr++;
2541 data[count] = (BYTE)strtol(byte,NULL,0);
2542 count ++;
2543 TRACE("Uneven byte count\n");
2544 }
2545 while (*ptr)
2546 {
2547 byte[2]= *ptr;
2548 ptr++;
2549 byte[3]= *ptr;
2550 ptr++;
2551 data[count] = (BYTE)strtol(byte,NULL,0);
2552 count ++;
2553 }
2554 msi_free(deformated);
2555
2556 TRACE("Data %i bytes(%i)\n",*size,count);
2557 }
2558 else
2559 {
2560 LPWSTR deformated;
2561 LPWSTR p;
2562 DWORD d = 0;
2563 deformat_string(package, &value[1], &deformated);
2564
2565 *type=REG_DWORD;
2566 *size = sizeof(DWORD);
2567 data = msi_alloc(*size);
2568 p = deformated;
2569 if (*p == '-')
2570 p++;
2571 while (*p)
2572 {
2573 if ( (*p < '0') || (*p > '9') )
2574 break;
2575 d *= 10;
2576 d += (*p - '0');
2577 p++;
2578 }
2579 if (deformated[0] == '-')
2580 d = -d;
2581 *(LPDWORD)data = d;
2582 TRACE("DWORD %i\n",*(LPDWORD)data);
2583
2584 msi_free(deformated);
2585 }
2586 }
2587 else
2588 {
2589 const WCHAR *ptr = value;
2590
2591 *type = REG_SZ;
2592 if (value[0] == '#')
2593 {
2594 ptr++; len--;
2595 if (value[1] == '%')
2596 {
2597 ptr++; len--;
2598 *type = REG_EXPAND_SZ;
2599 }
2600 }
2601 data = (BYTE *)msi_strdupW( ptr, len );
2602 if (len > strlenW( (const WCHAR *)data )) *type = REG_MULTI_SZ;
2603 *size = (len + 1) * sizeof(WCHAR);
2604 }
2605 return data;
2606 }
2607
2608 static const WCHAR *get_root_key( MSIPACKAGE *package, INT root, HKEY *root_key )
2609 {
2610 const WCHAR *ret;
2611
2612 switch (root)
2613 {
2614 case -1:
2615 if (msi_get_property_int( package->db, szAllUsers, 0 ))
2616 {
2617 *root_key = HKEY_LOCAL_MACHINE;
2618 ret = szHLM;
2619 }
2620 else
2621 {
2622 *root_key = HKEY_CURRENT_USER;
2623 ret = szHCU;
2624 }
2625 break;
2626 case 0:
2627 *root_key = HKEY_CLASSES_ROOT;
2628 ret = szHCR;
2629 break;
2630 case 1:
2631 *root_key = HKEY_CURRENT_USER;
2632 ret = szHCU;
2633 break;
2634 case 2:
2635 *root_key = HKEY_LOCAL_MACHINE;
2636 ret = szHLM;
2637 break;
2638 case 3:
2639 *root_key = HKEY_USERS;
2640 ret = szHU;
2641 break;
2642 default:
2643 ERR("Unknown root %i\n", root);
2644 return NULL;
2645 }
2646
2647 return ret;
2648 }
2649
2650 static inline REGSAM get_registry_view( const MSICOMPONENT *comp )
2651 {
2652 REGSAM view = 0;
2653 if (is_wow64 || is_64bit)
2654 view |= (comp->Attributes & msidbComponentAttributes64bit) ? KEY_WOW64_64KEY : KEY_WOW64_32KEY;
2655 return view;
2656 }
2657
2658 static HKEY open_key( const MSICOMPONENT *comp, HKEY root, const WCHAR *path, BOOL create, REGSAM access )
2659 {
2660 WCHAR *subkey, *p, *q;
2661 HKEY hkey, ret = NULL;
2662 LONG res;
2663
2664 access |= get_registry_view( comp );
2665
2666 if (!(subkey = strdupW( path ))) return NULL;
2667 p = subkey;
2668 if ((q = strchrW( p, '\\' ))) *q = 0;
2669 if (create)
2670 res = RegCreateKeyExW( root, subkey, 0, NULL, 0, access, NULL, &hkey, NULL );
2671 else
2672 res = RegOpenKeyExW( root, subkey, 0, access, &hkey );
2673 if (res)
2674 {
2675 TRACE("failed to open key %s (%d)\n", debugstr_w(subkey), res);
2676 msi_free( subkey );
2677 return NULL;
2678 }
2679 if (q && q[1])
2680 {
2681 ret = open_key( comp, hkey, q + 1, create, access );
2682 RegCloseKey( hkey );
2683 }
2684 else ret = hkey;
2685 msi_free( subkey );
2686 return ret;
2687 }
2688
2689 static BOOL is_special_entry( const WCHAR *name )
2690 {
2691 return (name && (name[0] == '*' || name[0] == '+') && !name[1]);
2692 }
2693
2694 static WCHAR **split_multi_string_values( const WCHAR *str, DWORD len, DWORD *count )
2695 {
2696 const WCHAR *p = str;
2697 WCHAR **ret;
2698 int i = 0;
2699
2700 *count = 0;
2701 if (!str) return NULL;
2702 while ((p - str) < len)
2703 {
2704 p += strlenW( p ) + 1;
2705 (*count)++;
2706 }
2707 if (!(ret = msi_alloc( *count * sizeof(WCHAR *) ))) return NULL;
2708 p = str;
2709 while ((p - str) < len)
2710 {
2711 if (!(ret[i] = strdupW( p )))
2712 {
2713 for (; i >= 0; i--) msi_free( ret[i] );
2714 msi_free( ret );
2715 return NULL;
2716 }
2717 p += strlenW( p ) + 1;
2718 i++;
2719 }
2720 return ret;
2721 }
2722
2723 static WCHAR *flatten_multi_string_values( WCHAR **left, DWORD left_count,
2724 WCHAR **right, DWORD right_count, DWORD *size )
2725 {
2726 WCHAR *ret, *p;
2727 unsigned int i;
2728
2729 *size = sizeof(WCHAR);
2730 for (i = 0; i < left_count; i++) *size += (strlenW( left[i] ) + 1) * sizeof(WCHAR);
2731 for (i = 0; i < right_count; i++) *size += (strlenW( right[i] ) + 1) * sizeof(WCHAR);
2732
2733 if (!(ret = p = msi_alloc( *size ))) return NULL;
2734
2735 for (i = 0; i < left_count; i++)
2736 {
2737 strcpyW( p, left[i] );
2738 p += strlenW( p ) + 1;
2739 }
2740 for (i = 0; i < right_count; i++)
2741 {
2742 strcpyW( p, right[i] );
2743 p += strlenW( p ) + 1;
2744 }
2745 *p = 0;
2746 return ret;
2747 }
2748
2749 static DWORD remove_duplicate_values( WCHAR **old, DWORD old_count,
2750 WCHAR **new, DWORD new_count )
2751 {
2752 DWORD ret = old_count;
2753 unsigned int i, j, k;
2754
2755 for (i = 0; i < new_count; i++)
2756 {
2757 for (j = 0; j < old_count; j++)
2758 {
2759 if (old[j] && !strcmpW( new[i], old[j] ))
2760 {
2761 msi_free( old[j] );
2762 for (k = j; k < old_count - 1; k++) { old[k] = old[k + 1]; }
2763 old[k] = NULL;
2764 ret--;
2765 }
2766 }
2767 }
2768 return ret;
2769 }
2770
2771 enum join_op
2772 {
2773 JOIN_OP_APPEND,
2774 JOIN_OP_PREPEND,
2775 JOIN_OP_REPLACE
2776 };
2777
2778 static WCHAR *join_multi_string_values( enum join_op op, WCHAR **old, DWORD old_count,
2779 WCHAR **new, DWORD new_count, DWORD *size )
2780 {
2781 switch (op)
2782 {
2783 case JOIN_OP_APPEND:
2784 old_count = remove_duplicate_values( old, old_count, new, new_count );
2785 return flatten_multi_string_values( old, old_count, new, new_count, size );
2786
2787 case JOIN_OP_PREPEND:
2788 old_count = remove_duplicate_values( old, old_count, new, new_count );
2789 return flatten_multi_string_values( new, new_count, old, old_count, size );
2790
2791 case JOIN_OP_REPLACE:
2792 return flatten_multi_string_values( new, new_count, NULL, 0, size );
2793
2794 default:
2795 ERR("unhandled join op %u\n", op);
2796 return NULL;
2797 }
2798 }
2799
2800 static BYTE *build_multi_string_value( BYTE *old_value, DWORD old_size,
2801 BYTE *new_value, DWORD new_size, DWORD *size )
2802 {
2803 DWORD i, old_len = 0, new_len = 0, old_count = 0, new_count = 0;
2804 const WCHAR *new_ptr = NULL, *old_ptr = NULL;
2805 enum join_op op = JOIN_OP_REPLACE;
2806 WCHAR **old = NULL, **new = NULL;
2807 BYTE *ret;
2808
2809 if (new_size / sizeof(WCHAR) - 1 > 1)
2810 {
2811 new_ptr = (const WCHAR *)new_value;
2812 new_len = new_size / sizeof(WCHAR) - 1;
2813
2814 if (!new_ptr[0] && new_ptr[new_len - 1])
2815 {
2816 op = JOIN_OP_APPEND;
2817 new_len--;
2818 new_ptr++;
2819 }
2820 else if (new_ptr[0] && !new_ptr[new_len - 1])
2821 {
2822 op = JOIN_OP_PREPEND;
2823 new_len--;
2824 }
2825 else if (new_len > 2 && !new_ptr[0] && !new_ptr[new_len - 1])
2826 {
2827 op = JOIN_OP_REPLACE;
2828 new_len -= 2;
2829 new_ptr++;
2830 }
2831 new = split_multi_string_values( new_ptr, new_len, &new_count );
2832 }
2833 if (old_size / sizeof(WCHAR) - 1 > 1)
2834 {
2835 old_ptr = (const WCHAR *)old_value;
2836 old_len = old_size / sizeof(WCHAR) - 1;
2837 old = split_multi_string_values( old_ptr, old_len, &old_count );
2838 }
2839 ret = (BYTE *)join_multi_string_values( op, old, old_count, new, new_count, size );
2840 for (i = 0; i < old_count; i++) msi_free( old[i] );
2841 for (i = 0; i < new_count; i++) msi_free( new[i] );
2842 msi_free( old );
2843 msi_free( new );
2844 return ret;
2845 }
2846
2847 static BYTE *reg_get_value( HKEY hkey, const WCHAR *name, DWORD *type, DWORD *size )
2848 {
2849 BYTE *ret;
2850 if (RegQueryValueExW( hkey, name, NULL, NULL, NULL, size )) return NULL;
2851 if (!(ret = msi_alloc( *size ))) return NULL;
2852 RegQueryValueExW( hkey, name, NULL, type, ret, size );
2853 return ret;
2854 }
2855
2856 static UINT ITERATE_WriteRegistryValues(MSIRECORD *row, LPVOID param)
2857 {
2858 MSIPACKAGE *package = param;
2859 BYTE *new_value, *old_value = NULL;
2860 HKEY root_key, hkey;
2861 DWORD type, old_type, new_size, old_size = 0;
2862 LPWSTR deformated, uikey;
2863 const WCHAR *szRoot, *component, *name, *key, *str;
2864 MSICOMPONENT *comp;
2865 MSIRECORD * uirow;
2866 INT root;
2867 BOOL check_first = FALSE;
2868 int len;
2869
2870 msi_ui_progress( package, 2, REG_PROGRESS_VALUE, 0, 0 );
2871
2872 component = MSI_RecordGetString(row, 6);
2873 comp = msi_get_loaded_component(package,component);
2874 if (!comp)
2875 return ERROR_SUCCESS;
2876
2877 comp->Action = msi_get_component_action( package, comp );
2878 if (comp->Action != INSTALLSTATE_LOCAL)
2879 {
2880 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
2881 return ERROR_SUCCESS;
2882 }
2883
2884 name = MSI_RecordGetString(row, 4);
2885 if( MSI_RecordIsNull(row,5) && name )
2886 {
2887 /* null values can have special meanings */
2888 if (name[0]=='-' && name[1] == 0)
2889 return ERROR_SUCCESS;
2890 if ((name[0] == '+' || name[0] == '*') && !name[1])
2891 check_first = TRUE;
2892 }
2893
2894 root = MSI_RecordGetInteger(row,2);
2895 key = MSI_RecordGetString(row, 3);
2896
2897 szRoot = get_root_key( package, root, &root_key );
2898 if (!szRoot)
2899 return ERROR_SUCCESS;
2900
2901 deformat_string(package, key , &deformated);
2902 uikey = msi_alloc( (strlenW(deformated) + strlenW(szRoot) + 1) * sizeof(WCHAR) );
2903 strcpyW(uikey,szRoot);
2904 strcatW(uikey,deformated);
2905
2906 if (!(hkey = open_key( comp, root_key, deformated, TRUE, KEY_QUERY_VALUE | KEY_SET_VALUE )))
2907 {
2908 ERR("Could not create key %s\n", debugstr_w(deformated));
2909 msi_free(uikey);
2910 msi_free(deformated);
2911 return ERROR_FUNCTION_FAILED;
2912 }
2913 msi_free( deformated );
2914 str = msi_record_get_string( row, 5, NULL );
2915 len = deformat_string( package, str, &deformated );
2916 new_value = parse_value( package, deformated, len, &type, &new_size );
2917
2918 msi_free( deformated );
2919 deformat_string(package, name, &deformated);
2920
2921 if (!is_special_entry( name ))
2922 {
2923 old_value = reg_get_value( hkey, deformated, &old_type, &old_size );
2924 if (type == REG_MULTI_SZ)
2925 {
2926 BYTE *new;
2927 if (old_value && old_type != REG_MULTI_SZ)
2928 {
2929 msi_free( old_value );
2930 old_value = NULL;
2931 old_size = 0;
2932 }
2933 new = build_multi_string_value( old_value, old_size, new_value, new_size, &new_size );
2934 msi_free( new_value );
2935 new_value = new;
2936 }
2937 if (!check_first)
2938 {
2939 TRACE("setting value %s of %s type %u\n", debugstr_w(deformated), debugstr_w(uikey), type);
2940 RegSetValueExW( hkey, deformated, 0, type, new_value, new_size );
2941 }
2942 else if (!old_value)
2943 {
2944 if (deformated || new_size)
2945 {
2946 TRACE("setting value %s of %s type %u\n", debugstr_w(deformated), debugstr_w(uikey), type);
2947 RegSetValueExW( hkey, deformated, 0, type, new_value, new_size );
2948 }
2949 }
2950 else TRACE("not overwriting existing value %s of %s\n", debugstr_w(deformated), debugstr_w(uikey));
2951 }
2952 RegCloseKey(hkey);
2953
2954 uirow = MSI_CreateRecord(3);
2955 MSI_RecordSetStringW(uirow,2,deformated);
2956 MSI_RecordSetStringW(uirow,1,uikey);
2957 if (type == REG_SZ || type == REG_EXPAND_SZ)
2958 MSI_RecordSetStringW(uirow, 3, (LPWSTR)new_value);
2959 msi_ui_actiondata( package, szWriteRegistryValues, uirow );
2960 msiobj_release( &uirow->hdr );
2961
2962 msi_free(new_value);
2963 msi_free(old_value);
2964 msi_free(deformated);
2965 msi_free(uikey);
2966
2967 return ERROR_SUCCESS;
2968 }
2969
2970 static UINT ACTION_WriteRegistryValues(MSIPACKAGE *package)
2971 {
2972 static const WCHAR query[] = {
2973 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2974 '`','R','e','g','i','s','t','r','y','`',0};
2975 MSIQUERY *view;
2976 UINT rc;
2977
2978 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
2979 if (rc != ERROR_SUCCESS)
2980 return ERROR_SUCCESS;
2981
2982 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteRegistryValues, package);
2983 msiobj_release(&view->hdr);
2984 return rc;
2985 }
2986
2987 static void delete_key( const MSICOMPONENT *comp, HKEY root, const WCHAR *path )
2988 {
2989 REGSAM access = 0;
2990 WCHAR *subkey, *p;
2991 HKEY hkey;
2992 LONG res;
2993
2994 access |= get_registry_view( comp );
2995
2996 if (!(subkey = strdupW( path ))) return;
2997 do
2998 {
2999 if ((p = strrchrW( subkey, '\\' )))
3000 {
3001 *p = 0;
3002 if (!p[1]) continue; /* trailing backslash */
3003 hkey = open_key( comp, root, subkey, FALSE, access | READ_CONTROL );
3004 if (!hkey) break;
3005 res = RegDeleteKeyExW( hkey, p + 1, access, 0 );
3006 RegCloseKey( hkey );
3007 }
3008 else
3009 res = RegDeleteKeyExW( root, subkey, access, 0 );
3010 if (res)
3011 {
3012 TRACE("failed to delete key %s (%d)\n", debugstr_w(subkey), res);
3013 break;
3014 }
3015 } while (p);
3016 msi_free( subkey );
3017 }
3018
3019 static void delete_value( const MSICOMPONENT *comp, HKEY root, const WCHAR *path, const WCHAR *value )
3020 {
3021 LONG res;
3022 HKEY hkey;
3023 DWORD num_subkeys, num_values;
3024
3025 if ((hkey = open_key( comp, root, path, FALSE, KEY_SET_VALUE | KEY_QUERY_VALUE )))
3026 {
3027 if ((res = RegDeleteValueW( hkey, value )))
3028 TRACE("failed to delete value %s (%d)\n", debugstr_w(value), res);
3029
3030 res = RegQueryInfoKeyW( hkey, NULL, NULL, NULL, &num_subkeys, NULL, NULL, &num_values,
3031 NULL, NULL, NULL, NULL );
3032 RegCloseKey( hkey );
3033 if (!res && !num_subkeys && !num_values)
3034 {
3035 TRACE("removing empty key %s\n", debugstr_w(path));
3036 delete_key( comp, root, path );
3037 }
3038 }
3039 }
3040
3041 static void delete_tree( const MSICOMPONENT *comp, HKEY root, const WCHAR *path )
3042 {
3043 LONG res;
3044 HKEY hkey;
3045
3046 if (!(hkey = open_key( comp, root, path, FALSE, KEY_ALL_ACCESS ))) return;
3047 res = RegDeleteTreeW( hkey, NULL );
3048 if (res) TRACE("failed to delete subtree of %s (%d)\n", debugstr_w(path), res);
3049 delete_key( comp, root, path );
3050 RegCloseKey( hkey );
3051 }
3052
3053 static UINT ITERATE_RemoveRegistryValuesOnUninstall( MSIRECORD *row, LPVOID param )
3054 {
3055 MSIPACKAGE *package = param;
3056 LPCWSTR component, name, key_str, root_key_str;
3057 LPWSTR deformated_key, deformated_name, ui_key_str;
3058 MSICOMPONENT *comp;
3059 MSIRECORD *uirow;
3060 BOOL delete_key = FALSE;
3061 HKEY hkey_root;
3062 UINT size;
3063 INT root;
3064
3065 msi_ui_progress( package, 2, REG_PROGRESS_VALUE, 0, 0 );
3066
3067 component = MSI_RecordGetString( row, 6 );
3068 comp = msi_get_loaded_component( package, component );
3069 if (!comp)
3070 return ERROR_SUCCESS;
3071
3072 comp->Action = msi_get_component_action( package, comp );
3073 if (comp->Action != INSTALLSTATE_ABSENT)
3074 {
3075 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
3076 return ERROR_SUCCESS;
3077 }
3078
3079 name = MSI_RecordGetString( row, 4 );
3080 if (MSI_RecordIsNull( row, 5 ) && name )
3081 {
3082 if (name[0] == '+' && !name[1])
3083 return ERROR_SUCCESS;
3084 if ((name[0] == '-' || name[0] == '*') && !name[1])
3085 {
3086 delete_key = TRUE;
3087 name = NULL;
3088 }
3089 }
3090
3091 root = MSI_RecordGetInteger( row, 2 );
3092 key_str = MSI_RecordGetString( row, 3 );
3093
3094 root_key_str = get_root_key( package, root, &hkey_root );
3095 if (!root_key_str)
3096 return ERROR_SUCCESS;
3097
3098 deformat_string( package, key_str, &deformated_key );
3099 size = strlenW( deformated_key ) + strlenW( root_key_str ) + 1;
3100 ui_key_str = msi_alloc( size * sizeof(WCHAR) );
3101 strcpyW( ui_key_str, root_key_str );
3102 strcatW( ui_key_str, deformated_key );
3103
3104 deformat_string( package, name, &deformated_name );
3105
3106 if (delete_key) delete_tree( comp, hkey_root, deformated_key );
3107 else delete_value( comp, hkey_root, deformated_key, deformated_name );
3108 msi_free( deformated_key );
3109
3110 uirow = MSI_CreateRecord( 2 );
3111 MSI_RecordSetStringW( uirow, 1, ui_key_str );
3112 MSI_RecordSetStringW( uirow, 2, deformated_name );
3113 msi_ui_actiondata( package, szRemoveRegistryValues, uirow );
3114 msiobj_release( &uirow->hdr );
3115
3116 msi_free( ui_key_str );
3117 msi_free( deformated_name );
3118 return ERROR_SUCCESS;
3119 }
3120
3121 static UINT ITERATE_RemoveRegistryValuesOnInstall( MSIRECORD *row, LPVOID param )
3122 {
3123 MSIPACKAGE *package = param;
3124 LPCWSTR component, name, key_str, root_key_str;
3125 LPWSTR deformated_key, deformated_name, ui_key_str;
3126 MSICOMPONENT *comp;
3127 MSIRECORD *uirow;
3128 BOOL delete_key = FALSE;
3129 HKEY hkey_root;
3130 UINT size;
3131 INT root;
3132
3133 component = MSI_RecordGetString( row, 5 );
3134 comp = msi_get_loaded_component( package, component );
3135 if (!comp)
3136 return ERROR_SUCCESS;
3137
3138 comp->Action = msi_get_component_action( package, comp );
3139 if (comp->Action != INSTALLSTATE_LOCAL)
3140 {
3141 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
3142 return ERROR_SUCCESS;
3143 }
3144
3145 if ((name = MSI_RecordGetString( row, 4 )))
3146 {
3147 if (name[0] == '-' && !name[1])
3148 {
3149 delete_key = TRUE;
3150 name = NULL;
3151 }
3152 }
3153
3154 root = MSI_RecordGetInteger( row, 2 );
3155 key_str = MSI_RecordGetString( row, 3 );
3156
3157 root_key_str = get_root_key( package, root, &hkey_root );
3158 if (!root_key_str)
3159 return ERROR_SUCCESS;
3160
3161 deformat_string( package, key_str, &deformated_key );
3162 size = strlenW( deformated_key ) + strlenW( root_key_str ) + 1;
3163 ui_key_str = msi_alloc( size * sizeof(WCHAR) );
3164 strcpyW( ui_key_str, root_key_str );
3165 strcatW( ui_key_str, deformated_key );
3166
3167 deformat_string( package, name, &deformated_name );
3168
3169 if (delete_key) delete_tree( comp, hkey_root, deformated_key );
3170 else delete_value( comp, hkey_root, deformated_key, deformated_name );
3171 msi_free( deformated_key );
3172
3173 uirow = MSI_CreateRecord( 2 );
3174 MSI_RecordSetStringW( uirow, 1, ui_key_str );
3175 MSI_RecordSetStringW( uirow, 2, deformated_name );
3176 msi_ui_actiondata( package, szRemoveRegistryValues, uirow );
3177 msiobj_release( &uirow->hdr );
3178
3179 msi_free( ui_key_str );
3180 msi_free( deformated_name );
3181 return ERROR_SUCCESS;
3182 }
3183
3184 static UINT ACTION_RemoveRegistryValues( MSIPACKAGE *package )
3185 {
3186 static const WCHAR registry_query[] = {
3187 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3188 '`','R','e','g','i','s','t','r','y','`',0};
3189 static const WCHAR remove_registry_query[] = {
3190 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3191 '`','R','e','m','o','v','e','R','e','g','i','s','t','r','y','`',0};
3192 MSIQUERY *view;
3193 UINT rc;
3194
3195 rc = MSI_DatabaseOpenViewW( package->db, registry_query, &view );
3196 if (rc == ERROR_SUCCESS)
3197 {
3198 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveRegistryValuesOnUninstall, package );