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