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