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