0816eacb09269564b674133e364353a793838061
[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 BOOL remove = !strcmpW(property, szRemove);
1654 BOOL reinstall = !strcmpW(property, szReinstall);
1655
1656 override = msi_dup_property( package->db, property );
1657 if (!override)
1658 return FALSE;
1659
1660 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1661 {
1662 if (feature->Level <= 0)
1663 continue;
1664
1665 if (reinstall)
1666 state = (feature->Installed == INSTALLSTATE_ABSENT ? INSTALLSTATE_UNKNOWN : feature->Installed);
1667 else if (remove)
1668 state = (feature->Installed == INSTALLSTATE_ABSENT ? INSTALLSTATE_UNKNOWN : INSTALLSTATE_ABSENT);
1669
1670 if (!strcmpiW( override, szAll ))
1671 {
1672 feature->Action = state;
1673 feature->ActionRequest = state;
1674 }
1675 else
1676 {
1677 LPWSTR ptr = override;
1678 LPWSTR ptr2 = strchrW(override,',');
1679
1680 while (ptr)
1681 {
1682 int len = ptr2 - ptr;
1683
1684 if ((ptr2 && strlenW(feature->Feature) == len && !strncmpW(ptr, feature->Feature, len))
1685 || (!ptr2 && !strcmpW(ptr, feature->Feature)))
1686 {
1687 feature->Action = state;
1688 feature->ActionRequest = state;
1689 break;
1690 }
1691 if (ptr2)
1692 {
1693 ptr=ptr2+1;
1694 ptr2 = strchrW(ptr,',');
1695 }
1696 else
1697 break;
1698 }
1699 }
1700 }
1701 msi_free(override);
1702 return TRUE;
1703 }
1704
1705 static BOOL process_overrides( MSIPACKAGE *package, int level )
1706 {
1707 static const WCHAR szAddLocal[] =
1708 {'A','D','D','L','O','C','A','L',0};
1709 static const WCHAR szAddSource[] =
1710 {'A','D','D','S','O','U','R','C','E',0};
1711 static const WCHAR szAdvertise[] =
1712 {'A','D','V','E','R','T','I','S','E',0};
1713 BOOL ret = FALSE;
1714
1715 /* all these activation/deactivation things happen in order and things
1716 * later on the list override things earlier on the list.
1717 *
1718 * 0 INSTALLLEVEL processing
1719 * 1 ADDLOCAL
1720 * 2 REMOVE
1721 * 3 ADDSOURCE
1722 * 4 ADDDEFAULT
1723 * 5 REINSTALL
1724 * 6 ADVERTISE
1725 * 7 COMPADDLOCAL
1726 * 8 COMPADDSOURCE
1727 * 9 FILEADDLOCAL
1728 * 10 FILEADDSOURCE
1729 * 11 FILEADDDEFAULT
1730 */
1731 ret |= process_state_property( package, level, szAddLocal, INSTALLSTATE_LOCAL );
1732 ret |= process_state_property( package, level, szRemove, INSTALLSTATE_ABSENT );
1733 ret |= process_state_property( package, level, szAddSource, INSTALLSTATE_SOURCE );
1734 ret |= process_state_property( package, level, szReinstall, INSTALLSTATE_UNKNOWN );
1735 ret |= process_state_property( package, level, szAdvertise, INSTALLSTATE_ADVERTISED );
1736
1737 if (ret && !package->full_reinstall)
1738 msi_set_property( package->db, szPreselected, szOne, -1 );
1739
1740 return ret;
1741 }
1742
1743 UINT MSI_SetFeatureStates(MSIPACKAGE *package)
1744 {
1745 int level;
1746 MSICOMPONENT* component;
1747 MSIFEATURE *feature;
1748
1749 TRACE("Checking Install Level\n");
1750
1751 level = msi_get_property_int(package->db, szInstallLevel, 1);
1752
1753 if (!msi_get_property_int( package->db, szPreselected, 0 ))
1754 {
1755 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1756 {
1757 if (!is_feature_selected( feature, level )) continue;
1758
1759 if (feature->ActionRequest == INSTALLSTATE_UNKNOWN)
1760 {
1761 if (feature->Attributes & msidbFeatureAttributesFavorSource)
1762 {
1763 feature->Action = INSTALLSTATE_SOURCE;
1764 feature->ActionRequest = INSTALLSTATE_SOURCE;
1765 }
1766 else if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1767 {
1768 feature->Action = INSTALLSTATE_ADVERTISED;
1769 feature->ActionRequest = INSTALLSTATE_ADVERTISED;
1770 }
1771 else
1772 {
1773 feature->Action = INSTALLSTATE_LOCAL;
1774 feature->ActionRequest = INSTALLSTATE_LOCAL;
1775 }
1776 }
1777 }
1778 /* disable child features of unselected parent or follow parent */
1779 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1780 {
1781 FeatureList *fl;
1782
1783 LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
1784 {
1785 if (!is_feature_selected( feature, level ))
1786 {
1787 fl->feature->Action = INSTALLSTATE_UNKNOWN;
1788 fl->feature->ActionRequest = INSTALLSTATE_UNKNOWN;
1789 }
1790 else if (fl->feature->Attributes & msidbFeatureAttributesFollowParent)
1791 {
1792 TRACE("feature %s (level %d request %d) follows parent %s (level %d request %d)\n",
1793 debugstr_w(fl->feature->Feature), fl->feature->Level, fl->feature->ActionRequest,
1794 debugstr_w(feature->Feature), feature->Level, feature->ActionRequest);
1795 fl->feature->Action = feature->Action;
1796 fl->feature->ActionRequest = feature->ActionRequest;
1797 }
1798 }
1799 }
1800 }
1801 else /* preselected */
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->Installed == INSTALLSTATE_ABSENT)
1810 {
1811 feature->Action = INSTALLSTATE_UNKNOWN;
1812 feature->ActionRequest = INSTALLSTATE_UNKNOWN;
1813 }
1814 else
1815 {
1816 feature->Action = feature->Installed;
1817 feature->ActionRequest = feature->Installed;
1818 }
1819 }
1820 }
1821 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1822 {
1823 FeatureList *fl;
1824
1825 if (!is_feature_selected( feature, level )) continue;
1826
1827 LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
1828 {
1829 if (fl->feature->Attributes & msidbFeatureAttributesFollowParent &&
1830 (!(feature->Attributes & msidbFeatureAttributesFavorAdvertise)))
1831 {
1832 TRACE("feature %s (level %d request %d) follows parent %s (level %d request %d)\n",
1833 debugstr_w(fl->feature->Feature), fl->feature->Level, fl->feature->ActionRequest,
1834 debugstr_w(feature->Feature), feature->Level, feature->ActionRequest);
1835 fl->feature->Action = feature->Action;
1836 fl->feature->ActionRequest = feature->ActionRequest;
1837 }
1838 }
1839 }
1840 }
1841
1842 /* now we want to set component state based based on feature state */
1843 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1844 {
1845 ComponentList *cl;
1846
1847 TRACE("examining feature %s (level %d installed %d request %d action %d)\n",
1848 debugstr_w(feature->Feature), feature->Level, feature->Installed,
1849 feature->ActionRequest, feature->Action);
1850
1851 /* features with components that have compressed files are made local */
1852 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1853 {
1854 if (cl->component->ForceLocalState &&
1855 feature->ActionRequest == INSTALLSTATE_SOURCE)
1856 {
1857 feature->Action = INSTALLSTATE_LOCAL;
1858 feature->ActionRequest = INSTALLSTATE_LOCAL;
1859 break;
1860 }
1861 }
1862
1863 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1864 {
1865 component = cl->component;
1866
1867 switch (feature->ActionRequest)
1868 {
1869 case INSTALLSTATE_ABSENT:
1870 component->anyAbsent = 1;
1871 break;
1872 case INSTALLSTATE_ADVERTISED:
1873 component->hasAdvertisedFeature = 1;
1874 break;
1875 case INSTALLSTATE_SOURCE:
1876 component->hasSourceFeature = 1;
1877 break;
1878 case INSTALLSTATE_LOCAL:
1879 component->hasLocalFeature = 1;
1880 break;
1881 case INSTALLSTATE_DEFAULT:
1882 if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1883 component->hasAdvertisedFeature = 1;
1884 else if (feature->Attributes & msidbFeatureAttributesFavorSource)
1885 component->hasSourceFeature = 1;
1886 else
1887 component->hasLocalFeature = 1;
1888 break;
1889 default:
1890 break;
1891 }
1892 }
1893 }
1894
1895 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1896 {
1897 /* check if it's local or source */
1898 if (!(component->Attributes & msidbComponentAttributesOptional) &&
1899 (component->hasLocalFeature || component->hasSourceFeature))
1900 {
1901 if ((component->Attributes & msidbComponentAttributesSourceOnly) &&
1902 !component->ForceLocalState)
1903 {
1904 component->Action = INSTALLSTATE_SOURCE;
1905 component->ActionRequest = INSTALLSTATE_SOURCE;
1906 }
1907 else
1908 {
1909 component->Action = INSTALLSTATE_LOCAL;
1910 component->ActionRequest = INSTALLSTATE_LOCAL;
1911 }
1912 continue;
1913 }
1914
1915 /* if any feature is local, the component must be local too */
1916 if (component->hasLocalFeature)
1917 {
1918 component->Action = INSTALLSTATE_LOCAL;
1919 component->ActionRequest = INSTALLSTATE_LOCAL;
1920 continue;
1921 }
1922 if (component->hasSourceFeature)
1923 {
1924 component->Action = INSTALLSTATE_SOURCE;
1925 component->ActionRequest = INSTALLSTATE_SOURCE;
1926 continue;
1927 }
1928 if (component->hasAdvertisedFeature)
1929 {
1930 component->Action = INSTALLSTATE_ADVERTISED;
1931 component->ActionRequest = INSTALLSTATE_ADVERTISED;
1932 continue;
1933 }
1934 TRACE("nobody wants component %s\n", debugstr_w(component->Component));
1935 if (component->anyAbsent && component->ComponentId)
1936 {
1937 component->Action = INSTALLSTATE_ABSENT;
1938 component->ActionRequest = INSTALLSTATE_ABSENT;
1939 }
1940 }
1941
1942 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1943 {
1944 if (component->ActionRequest == INSTALLSTATE_DEFAULT)
1945 {
1946 TRACE("%s was default, setting to local\n", debugstr_w(component->Component));
1947 component->Action = INSTALLSTATE_LOCAL;
1948 component->ActionRequest = INSTALLSTATE_LOCAL;
1949 }
1950
1951 if (component->ActionRequest == INSTALLSTATE_SOURCE &&
1952 component->Installed == INSTALLSTATE_SOURCE &&
1953 component->hasSourceFeature)
1954 {
1955 component->Action = INSTALLSTATE_UNKNOWN;
1956 component->ActionRequest = INSTALLSTATE_UNKNOWN;
1957 }
1958
1959 TRACE("component %s (installed %d request %d action %d)\n",
1960 debugstr_w(component->Component), component->Installed, component->ActionRequest, component->Action);
1961
1962 if (component->Action == INSTALLSTATE_LOCAL || component->Action == INSTALLSTATE_SOURCE)
1963 component->num_clients++;
1964 else if (component->Action == INSTALLSTATE_ABSENT)
1965 component->num_clients--;
1966 }
1967
1968 return ERROR_SUCCESS;
1969 }
1970
1971 static UINT ITERATE_CostFinalizeConditions(MSIRECORD *row, LPVOID param)
1972 {
1973 MSIPACKAGE *package = param;
1974 LPCWSTR name;
1975 MSIFEATURE *feature;
1976
1977 name = MSI_RecordGetString( row, 1 );
1978
1979 feature = msi_get_loaded_feature( package, name );
1980 if (!feature)
1981 ERR("FAILED to find loaded feature %s\n",debugstr_w(name));
1982 else
1983 {
1984 LPCWSTR Condition;
1985 Condition = MSI_RecordGetString(row,3);
1986
1987 if (MSI_EvaluateConditionW(package,Condition) == MSICONDITION_TRUE)
1988 {
1989 int level = MSI_RecordGetInteger(row,2);
1990 TRACE("Resetting feature %s to level %i\n", debugstr_w(name), level);
1991 feature->Level = level;
1992 }
1993 }
1994 return ERROR_SUCCESS;
1995 }
1996
1997 VS_FIXEDFILEINFO *msi_get_disk_file_version( LPCWSTR filename )
1998 {
1999 static const WCHAR name[] = {'\\',0};
2000 VS_FIXEDFILEINFO *ptr, *ret;
2001 LPVOID version;
2002 DWORD versize, handle;
2003 UINT sz;
2004
2005 versize = GetFileVersionInfoSizeW( filename, &handle );
2006 if (!versize)
2007 return NULL;
2008
2009 version = msi_alloc( versize );
2010 if (!version)
2011 return NULL;
2012
2013 GetFileVersionInfoW( filename, 0, versize, version );
2014
2015 if (!VerQueryValueW( version, name, (LPVOID *)&ptr, &sz ))
2016 {
2017 msi_free( version );
2018 return NULL;
2019 }
2020
2021 ret = msi_alloc( sz );
2022 memcpy( ret, ptr, sz );
2023
2024 msi_free( version );
2025 return ret;
2026 }
2027
2028 int msi_compare_file_versions( VS_FIXEDFILEINFO *fi, const WCHAR *version )
2029 {
2030 DWORD ms, ls;
2031
2032 msi_parse_version_string( version, &ms, &ls );
2033
2034 if (fi->dwFileVersionMS > ms) return 1;
2035 else if (fi->dwFileVersionMS < ms) return -1;
2036 else if (fi->dwFileVersionLS > ls) return 1;
2037 else if (fi->dwFileVersionLS < ls) return -1;
2038 return 0;
2039 }
2040
2041 int msi_compare_font_versions( const WCHAR *ver1, const WCHAR *ver2 )
2042 {
2043 DWORD ms1, ms2;
2044
2045 msi_parse_version_string( ver1, &ms1, NULL );
2046 msi_parse_version_string( ver2, &ms2, NULL );
2047
2048 if (ms1 > ms2) return 1;
2049 else if (ms1 < ms2) return -1;
2050 return 0;
2051 }
2052
2053 DWORD msi_get_disk_file_size( LPCWSTR filename )
2054 {
2055 HANDLE file;
2056 DWORD size;
2057
2058 file = CreateFileW( filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL );
2059 if (file == INVALID_HANDLE_VALUE)
2060 return INVALID_FILE_SIZE;
2061
2062 size = GetFileSize( file, NULL );
2063 TRACE("size is %u\n", size);
2064 CloseHandle( file );
2065 return size;
2066 }
2067
2068 BOOL msi_file_hash_matches( MSIFILE *file )
2069 {
2070 UINT r;
2071 MSIFILEHASHINFO hash;
2072
2073 hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
2074 r = MsiGetFileHashW( file->TargetPath, 0, &hash );
2075 if (r != ERROR_SUCCESS)
2076 return FALSE;
2077
2078 return !memcmp( &hash, &file->hash, sizeof(MSIFILEHASHINFO) );
2079 }
2080
2081 static WCHAR *get_temp_dir( void )
2082 {
2083 static UINT id;
2084 WCHAR tmp[MAX_PATH], dir[MAX_PATH];
2085
2086 GetTempPathW( MAX_PATH, tmp );
2087 for (;;)
2088 {
2089 if (!GetTempFileNameW( tmp, szMsi, ++id, dir )) return NULL;
2090 if (CreateDirectoryW( dir, NULL )) break;
2091 }
2092 return strdupW( dir );
2093 }
2094
2095 /*
2096 * msi_build_directory_name()
2097 *
2098 * This function is to save messing round with directory names
2099 * It handles adding backslashes between path segments,
2100 * and can add \ at the end of the directory name if told to.
2101 *
2102 * It takes a variable number of arguments.
2103 * It always allocates a new string for the result, so make sure
2104 * to free the return value when finished with it.
2105 *
2106 * The first arg is the number of path segments that follow.
2107 * The arguments following count are a list of path segments.
2108 * A path segment may be NULL.
2109 *
2110 * Path segments will be added with a \ separating them.
2111 * A \ will not be added after the last segment, however if the
2112 * last segment is NULL, then the last character will be a \
2113 */
2114 WCHAR *msi_build_directory_name( DWORD count, ... )
2115 {
2116 DWORD sz = 1, i;
2117 WCHAR *dir;
2118 va_list va;
2119
2120 va_start( va, count );
2121 for (i = 0; i < count; i++)
2122 {
2123 const WCHAR *str = va_arg( va, const WCHAR * );
2124 if (str) sz += strlenW( str ) + 1;
2125 }
2126 va_end( va );
2127
2128 dir = msi_alloc( sz * sizeof(WCHAR) );
2129 dir[0] = 0;
2130
2131 va_start( va, count );
2132 for (i = 0; i < count; i++)
2133 {
2134 const WCHAR *str = va_arg( va, const WCHAR * );
2135 if (!str) continue;
2136 strcatW( dir, str );
2137 if ( i + 1 != count && dir[0] && dir[strlenW( dir ) - 1] != '\\') strcatW( dir, szBackSlash );
2138 }
2139 va_end( va );
2140 return dir;
2141 }
2142
2143 static void set_target_path( MSIPACKAGE *package, MSIFILE *file )
2144 {
2145 MSIASSEMBLY *assembly = file->Component->assembly;
2146
2147 TRACE("file %s is named %s\n", debugstr_w(file->File), debugstr_w(file->FileName));
2148
2149 msi_free( file->TargetPath );
2150 if (assembly && !assembly->application)
2151 {
2152 if (!assembly->tempdir) assembly->tempdir = get_temp_dir();
2153 file->TargetPath = msi_build_directory_name( 2, assembly->tempdir, file->FileName );
2154 msi_track_tempfile( package, file->TargetPath );
2155 }
2156 else
2157 {
2158 const WCHAR *dir = msi_get_target_folder( package, file->Component->Directory );
2159 file->TargetPath = msi_build_directory_name( 2, dir, file->FileName );
2160 }
2161
2162 TRACE("resolves to %s\n", debugstr_w(file->TargetPath));
2163 }
2164
2165 static UINT calculate_file_cost( MSIPACKAGE *package )
2166 {
2167 VS_FIXEDFILEINFO *file_version;
2168 WCHAR *font_version;
2169 MSIFILE *file;
2170
2171 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2172 {
2173 MSICOMPONENT *comp = file->Component;
2174 DWORD file_size;
2175
2176 if (!comp->Enabled) continue;
2177
2178 if (file->IsCompressed)
2179 comp->ForceLocalState = TRUE;
2180
2181 set_target_path( package, file );
2182
2183 if ((comp->assembly && !comp->assembly->installed) ||
2184 GetFileAttributesW(file->TargetPath) == INVALID_FILE_ATTRIBUTES)
2185 {
2186 comp->Cost += file->FileSize;
2187 continue;
2188 }
2189 file_size = msi_get_disk_file_size( file->TargetPath );
2190
2191 if (file->Version)
2192 {
2193 if ((file_version = msi_get_disk_file_version( file->TargetPath )))
2194 {
2195 if (msi_compare_file_versions( file_version, file->Version ) < 0)
2196 {
2197 comp->Cost += file->FileSize - file_size;
2198 }
2199 msi_free( file_version );
2200 continue;
2201 }
2202 else if ((font_version = msi_font_version_from_file( file->TargetPath )))
2203 {
2204 if (msi_compare_font_versions( font_version, file->Version ) < 0)
2205 {
2206 comp->Cost += file->FileSize - file_size;
2207 }
2208 msi_free( font_version );
2209 continue;
2210 }
2211 }
2212 if (file_size != file->FileSize)
2213 {
2214 comp->Cost += file->FileSize - file_size;
2215 }
2216 }
2217 return ERROR_SUCCESS;
2218 }
2219
2220 WCHAR *msi_normalize_path( const WCHAR *in )
2221 {
2222 const WCHAR *p = in;
2223 WCHAR *q, *ret;
2224 int n, len = strlenW( in ) + 2;
2225
2226 if (!(q = ret = msi_alloc( len * sizeof(WCHAR) ))) return NULL;
2227
2228 len = 0;
2229 while (1)
2230 {
2231 /* copy until the end of the string or a space */
2232 while (*p != ' ' && (*q = *p))
2233 {
2234 p++, len++;
2235 /* reduce many backslashes to one */
2236 if (*p != '\\' || *q != '\\')
2237 q++;
2238 }
2239
2240 /* quit at the end of the string */
2241 if (!*p)
2242 break;
2243
2244 /* count the number of spaces */
2245 n = 0;
2246 while (p[n] == ' ')
2247 n++;
2248
2249 /* if it's leading or trailing space, skip it */
2250 if ( len == 0 || p[-1] == '\\' || p[n] == '\\' )
2251 p += n;
2252 else /* copy n spaces */
2253 while (n && (*q++ = *p++)) n--;
2254 }
2255 while (q - ret > 0 && q[-1] == ' ') q--;
2256 if (q - ret > 0 && q[-1] != '\\')
2257 {
2258 q[0] = '\\';
2259 q[1] = 0;
2260 }
2261 return ret;
2262 }
2263
2264 static WCHAR *get_install_location( MSIPACKAGE *package )
2265 {
2266 HKEY hkey;
2267 WCHAR *path;
2268
2269 if (!package->ProductCode) return NULL;
2270 if (MSIREG_OpenInstallProps( package->ProductCode, package->Context, NULL, &hkey, FALSE )) return NULL;
2271 if ((path = msi_reg_get_val_str( hkey, szInstallLocation )) && !path[0])
2272 {
2273 msi_free( path );
2274 path = NULL;
2275 }
2276 RegCloseKey( hkey );
2277 return path;
2278 }
2279
2280 void msi_resolve_target_folder( MSIPACKAGE *package, const WCHAR *name, BOOL load_prop )
2281 {
2282 FolderList *fl;
2283 MSIFOLDER *folder, *parent, *child;
2284 WCHAR *path, *normalized_path;
2285
2286 TRACE("resolving %s\n", debugstr_w(name));
2287
2288 if (!(folder = msi_get_loaded_folder( package, name ))) return;
2289
2290 if (!strcmpW( folder->Directory, szTargetDir )) /* special resolving for target root dir */
2291 {
2292 if (!(path = get_install_location( package )) &&
2293 (!load_prop || !(path = msi_dup_property( package->db, szTargetDir ))))
2294 {
2295 path = msi_dup_property( package->db, szRootDrive );
2296 }
2297 }
2298 else if (!load_prop || !(path = msi_dup_property( package->db, folder->Directory )))
2299 {
2300 if (folder->Parent && strcmpW( folder->Directory, folder->Parent ))
2301 {
2302 parent = msi_get_loaded_folder( package, folder->Parent );
2303 path = msi_build_directory_name( 3, parent->ResolvedTarget, folder->TargetDefault, NULL );
2304 }
2305 else
2306 path = msi_build_directory_name( 2, folder->TargetDefault, NULL );
2307 }
2308 normalized_path = msi_normalize_path( path );
2309 msi_free( path );
2310 if (folder->ResolvedTarget && !strcmpiW( normalized_path, folder->ResolvedTarget ))
2311 {
2312 TRACE("%s already resolved to %s\n", debugstr_w(name), debugstr_w(folder->ResolvedTarget));
2313 msi_free( normalized_path );
2314 return;
2315 }
2316 msi_set_property( package->db, folder->Directory, normalized_path, -1 );
2317 msi_free( folder->ResolvedTarget );
2318 folder->ResolvedTarget = normalized_path;
2319
2320 LIST_FOR_EACH_ENTRY( fl, &folder->children, FolderList, entry )
2321 {
2322 child = fl->folder;
2323 msi_resolve_target_folder( package, child->Directory, load_prop );
2324 }
2325 TRACE("%s resolves to %s\n", debugstr_w(name), debugstr_w(folder->ResolvedTarget));
2326 }
2327
2328 static UINT ACTION_CostFinalize(MSIPACKAGE *package)
2329 {
2330 static const WCHAR query[] =
2331 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2332 '`','C','o','n','d','i','t','i','o','n','`',0};
2333 static const WCHAR szOutOfDiskSpace[] =
2334 {'O','u','t','O','f','D','i','s','k','S','p','a','c','e',0};
2335 static const WCHAR szPrimaryFolder[] =
2336 {'P','R','I','M','A','R','Y','F','O','L','D','E','R',0};
2337 static const WCHAR szPrimaryVolumePath[] =
2338 {'P','r','i','m','a','r','y','V','o','l','u','m','e','P','a','t','h',0};
2339 static const WCHAR szPrimaryVolumeSpaceAvailable[] =
2340 {'P','r','i','m','a','r','y','V','o','l','u','m','e','S','p','a','c','e',
2341 'A','v','a','i','l','a','b','l','e',0};
2342 static const WCHAR szOutOfNoRbDiskSpace[] =
2343 {'O','u','t','O','f','N','o','R','b','D','i','s','k','S','p','a','c','e',0};
2344 MSICOMPONENT *comp;
2345 MSIQUERY *view;
2346 WCHAR *level, *primary_key, *primary_folder;
2347 UINT rc;
2348
2349 TRACE("Building directory properties\n");
2350 msi_resolve_target_folder( package, szTargetDir, TRUE );
2351
2352 TRACE("Evaluating component conditions\n");
2353 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2354 {
2355 if (MSI_EvaluateConditionW( package, comp->Condition ) == MSICONDITION_FALSE)
2356 {
2357 TRACE("Disabling component %s\n", debugstr_w(comp->Component));
2358 comp->Enabled = FALSE;
2359 }
2360 else
2361 comp->Enabled = TRUE;
2362 }
2363 get_client_counts( package );
2364
2365 /* read components states from the registry */
2366 ACTION_GetComponentInstallStates(package);
2367 ACTION_GetFeatureInstallStates(package);
2368
2369 if (!process_overrides( package, msi_get_property_int( package->db, szInstallLevel, 1 ) ))
2370 {
2371 TRACE("Evaluating feature conditions\n");
2372
2373 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
2374 if (rc == ERROR_SUCCESS)
2375 {
2376 rc = MSI_IterateRecords( view, NULL, ITERATE_CostFinalizeConditions, package );
2377 msiobj_release( &view->hdr );
2378 if (rc != ERROR_SUCCESS)
2379 return rc;
2380 }
2381 }
2382
2383 TRACE("Calculating file cost\n");
2384 calculate_file_cost( package );
2385
2386 msi_set_property( package->db, szCostingComplete, szOne, -1 );
2387 /* set default run level if not set */
2388 level = msi_dup_property( package->db, szInstallLevel );
2389 if (!level) msi_set_property( package->db, szInstallLevel, szOne, -1 );
2390 msi_free(level);
2391
2392 if ((primary_key = msi_dup_property( package->db, szPrimaryFolder )))
2393 {
2394 if ((primary_folder = msi_dup_property( package->db, primary_key )))
2395 {
2396 if (((primary_folder[0] >= 'A' && primary_folder[0] <= 'Z') ||
2397 (primary_folder[0] >= 'a' && primary_folder[0] <= 'z')) && primary_folder[1] == ':')
2398 {
2399 ULARGE_INTEGER free;
2400
2401 primary_folder[2] = 0;
2402 if (GetDiskFreeSpaceExW( primary_folder, &free, NULL, NULL ))
2403 {
2404 static const WCHAR fmtW[] = {'%','l','u',0};
2405 WCHAR buf[21];
2406
2407 sprintfW( buf, fmtW, free.QuadPart / 512 );
2408 msi_set_property( package->db, szPrimaryVolumeSpaceAvailable, buf, -1 );
2409 }
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 msi_set_property( package->db, szOutOfNoRbDiskSpace, szZero, -1 );
2420
2421 return MSI_SetFeatureStates(package);
2422 }
2423
2424 static BYTE *parse_value( MSIPACKAGE *package, const WCHAR *value, DWORD len, DWORD *type, DWORD *size )
2425 {
2426 BYTE *data;
2427
2428 if (!value)
2429 {
2430 *size = sizeof(WCHAR);
2431 *type = REG_SZ;
2432 if ((data = msi_alloc( *size ))) *(WCHAR *)data = 0;
2433 return data;
2434 }
2435 if (value[0]=='#' && value[1]!='#' && value[1]!='%')
2436 {
2437 if (value[1]=='x')
2438 {
2439 LPWSTR ptr;
2440 CHAR byte[5];
2441 LPWSTR deformated = NULL;
2442 int count;
2443
2444 deformat_string(package, &value[2], &deformated);
2445
2446 /* binary value type */
2447 ptr = deformated;
2448 *type = REG_BINARY;
2449 if (strlenW(ptr)%2)
2450 *size = (strlenW(ptr)/2)+1;
2451 else
2452 *size = strlenW(ptr)/2;
2453
2454 data = msi_alloc(*size);
2455
2456 byte[0] = '0';
2457 byte[1] = 'x';
2458 byte[4] = 0;
2459 count = 0;
2460 /* if uneven pad with a zero in front */
2461 if (strlenW(ptr)%2)
2462 {
2463 byte[2]= '0';
2464 byte[3]= *ptr;
2465 ptr++;
2466 data[count] = (BYTE)strtol(byte,NULL,0);
2467 count ++;
2468 TRACE("Uneven byte count\n");
2469 }
2470 while (*ptr)
2471 {
2472 byte[2]= *ptr;
2473 ptr++;
2474 byte[3]= *ptr;
2475 ptr++;
2476 data[count] = (BYTE)strtol(byte,NULL,0);
2477 count ++;
2478 }
2479 msi_free(deformated);
2480
2481 TRACE("Data %i bytes(%i)\n",*size,count);
2482 }
2483 else
2484 {
2485 LPWSTR deformated;
2486 LPWSTR p;
2487 DWORD d = 0;
2488 deformat_string(package, &value[1], &deformated);
2489
2490 *type=REG_DWORD;
2491 *size = sizeof(DWORD);
2492 data = msi_alloc(*size);
2493 p = deformated;
2494 if (*p == '-')
2495 p++;
2496 while (*p)
2497 {
2498 if ( (*p < '0') || (*p > '9') )
2499 break;
2500 d *= 10;
2501 d += (*p - '0');
2502 p++;
2503 }
2504 if (deformated[0] == '-')
2505 d = -d;
2506 *(LPDWORD)data = d;
2507 TRACE("DWORD %i\n",*(LPDWORD)data);
2508
2509 msi_free(deformated);
2510 }
2511 }
2512 else
2513 {
2514 const WCHAR *ptr = value;
2515
2516 *type = REG_SZ;
2517 if (value[0] == '#')
2518 {
2519 ptr++; len--;
2520 if (value[1] == '%')
2521 {
2522 ptr++; len--;
2523 *type = REG_EXPAND_SZ;
2524 }
2525 }
2526 data = (BYTE *)msi_strdupW( ptr, len );
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 inline REGSAM get_registry_view( const MSICOMPONENT *comp )
2576 {
2577 REGSAM view = 0;
2578 if (is_wow64 || is_64bit)
2579 view |= (comp->Attributes & msidbComponentAttributes64bit) ? KEY_WOW64_64KEY : KEY_WOW64_32KEY;
2580 return view;
2581 }
2582
2583 static HKEY open_key( const MSICOMPONENT *comp, HKEY root, const WCHAR *path, BOOL create )
2584 {
2585 REGSAM access = KEY_ALL_ACCESS;
2586 WCHAR *subkey, *p, *q;
2587 HKEY hkey, ret = NULL;
2588 LONG res;
2589
2590 access |= get_registry_view( comp );
2591
2592 if (!(subkey = strdupW( path ))) return NULL;
2593 p = subkey;
2594 if ((q = strchrW( p, '\\' ))) *q = 0;
2595 if (create)
2596 res = RegCreateKeyExW( root, subkey, 0, NULL, 0, access, NULL, &hkey, NULL );
2597 else
2598 res = RegOpenKeyExW( root, subkey, 0, access, &hkey );
2599 if (res)
2600 {
2601 TRACE("failed to open key %s (%d)\n", debugstr_w(subkey), res);
2602 msi_free( subkey );
2603 return NULL;
2604 }
2605 if (q && q[1])
2606 {
2607 ret = open_key( comp, hkey, q + 1, create );
2608 RegCloseKey( hkey );
2609 }
2610 else ret = hkey;
2611 msi_free( subkey );
2612 return ret;
2613 }
2614
2615 static BOOL is_special_entry( const WCHAR *name )
2616 {
2617 return (name && (name[0] == '*' || name[0] == '+') && !name[1]);
2618 }
2619
2620 static WCHAR **split_multi_string_values( const WCHAR *str, DWORD len, DWORD *count )
2621 {
2622 const WCHAR *p = str;
2623 WCHAR **ret;
2624 int i = 0;
2625
2626 *count = 0;
2627 if (!str) return NULL;
2628 while ((p - str) < len)
2629 {
2630 p += strlenW( p ) + 1;
2631 (*count)++;
2632 }
2633 if (!(ret = msi_alloc( *count * sizeof(WCHAR *) ))) return NULL;
2634 p = str;
2635 while ((p - str) < len)
2636 {
2637 if (!(ret[i] = strdupW( p )))
2638 {
2639 for (; i >= 0; i--) msi_free( ret[i] );
2640 msi_free( ret );
2641 return NULL;
2642 }
2643 p += strlenW( p ) + 1;
2644 i++;
2645 }
2646 return ret;
2647 }
2648
2649 static WCHAR *flatten_multi_string_values( WCHAR **left, DWORD left_count,
2650 WCHAR **right, DWORD right_count, DWORD *size )
2651 {
2652 WCHAR *ret, *p;
2653 unsigned int i;
2654
2655 *size = sizeof(WCHAR);
2656 for (i = 0; i < left_count; i++) *size += (strlenW( left[i] ) + 1) * sizeof(WCHAR);
2657 for (i = 0; i < right_count; i++) *size += (strlenW( right[i] ) + 1) * sizeof(WCHAR);
2658
2659 if (!(ret = p = msi_alloc( *size ))) return NULL;
2660
2661 for (i = 0; i < left_count; i++)
2662 {
2663 strcpyW( p, left[i] );
2664 p += strlenW( p ) + 1;
2665 }
2666 for (i = 0; i < right_count; i++)
2667 {
2668 strcpyW( p, right[i] );
2669 p += strlenW( p ) + 1;
2670 }
2671 *p = 0;
2672 return ret;
2673 }
2674
2675 static DWORD remove_duplicate_values( WCHAR **old, DWORD old_count,
2676 WCHAR **new, DWORD new_count )
2677 {
2678 DWORD ret = old_count;
2679 unsigned int i, j, k;
2680
2681 for (i = 0; i < new_count; i++)
2682 {
2683 for (j = 0; j < old_count; j++)
2684 {
2685 if (old[j] && !strcmpW( new[i], old[j] ))
2686 {
2687 msi_free( old[j] );
2688 for (k = j; k < old_count - 1; k++) { old[k] = old[k + 1]; }
2689 old[k] = NULL;
2690 ret--;
2691 }
2692 }
2693 }
2694 return ret;
2695 }
2696
2697 enum join_op
2698 {
2699 JOIN_OP_APPEND,
2700 JOIN_OP_PREPEND,
2701 JOIN_OP_REPLACE
2702 };
2703
2704 static WCHAR *join_multi_string_values( enum join_op op, WCHAR **old, DWORD old_count,
2705 WCHAR **new, DWORD new_count, DWORD *size )
2706 {
2707 switch (op)
2708 {
2709 case JOIN_OP_APPEND:
2710 old_count = remove_duplicate_values( old, old_count, new, new_count );
2711 return flatten_multi_string_values( old, old_count, new, new_count, size );
2712
2713 case JOIN_OP_PREPEND:
2714 old_count = remove_duplicate_values( old, old_count, new, new_count );
2715 return flatten_multi_string_values( new, new_count, old, old_count, size );
2716
2717 case JOIN_OP_REPLACE:
2718 return flatten_multi_string_values( new, new_count, NULL, 0, size );
2719
2720 default:
2721 ERR("unhandled join op %u\n", op);
2722 return NULL;
2723 }
2724 }
2725
2726 static BYTE *build_multi_string_value( BYTE *old_value, DWORD old_size,
2727 BYTE *new_value, DWORD new_size, DWORD *size )
2728 {
2729 DWORD i, old_len = 0, new_len = 0, old_count = 0, new_count = 0;
2730 const WCHAR *new_ptr = NULL, *old_ptr = NULL;
2731 enum join_op op = JOIN_OP_REPLACE;
2732 WCHAR **old = NULL, **new = NULL;
2733 BYTE *ret;
2734
2735 if (new_size / sizeof(WCHAR) - 1 > 1)
2736 {
2737 new_ptr = (const WCHAR *)new_value;
2738 new_len = new_size / sizeof(WCHAR) - 1;
2739
2740 if (!new_ptr[0] && new_ptr[new_len - 1])
2741 {
2742 op = JOIN_OP_APPEND;
2743 new_len--;
2744 new_ptr++;
2745 }
2746 else if (new_ptr[0] && !new_ptr[new_len - 1])
2747 {
2748 op = JOIN_OP_PREPEND;
2749 new_len--;
2750 }
2751 else if (new_len > 2 && !new_ptr[0] && !new_ptr[new_len - 1])
2752 {
2753 op = JOIN_OP_REPLACE;
2754 new_len -= 2;
2755 new_ptr++;
2756 }
2757 new = split_multi_string_values( new_ptr, new_len, &new_count );
2758 }
2759 if (old_size / sizeof(WCHAR) - 1 > 1)
2760 {
2761 old_ptr = (const WCHAR *)old_value;
2762 old_len = old_size / sizeof(WCHAR) - 1;
2763 old = split_multi_string_values( old_ptr, old_len, &old_count );
2764 }
2765 ret = (BYTE *)join_multi_string_values( op, old, old_count, new, new_count, size );
2766 for (i = 0; i < old_count; i++) msi_free( old[i] );
2767 for (i = 0; i < new_count; i++) msi_free( new[i] );
2768 msi_free( old );
2769 msi_free( new );
2770 return ret;
2771 }
2772
2773 static BYTE *reg_get_value( HKEY hkey, const WCHAR *name, DWORD *type, DWORD *size )
2774 {
2775 BYTE *ret;
2776 if (RegQueryValueExW( hkey, name, NULL, NULL, NULL, size )) return NULL;
2777 if (!(ret = msi_alloc( *size ))) return NULL;
2778 RegQueryValueExW( hkey, name, NULL, type, ret, size );
2779 return ret;
2780 }
2781
2782 static UINT ITERATE_WriteRegistryValues(MSIRECORD *row, LPVOID param)
2783 {
2784 MSIPACKAGE *package = param;
2785 BYTE *new_value, *old_value = NULL;
2786 HKEY root_key, hkey;
2787 DWORD type, old_type, new_size, old_size = 0;
2788 LPWSTR deformated, uikey;
2789 const WCHAR *szRoot, *component, *name, *key, *str;
2790 MSICOMPONENT *comp;
2791 MSIRECORD * uirow;
2792 INT root;
2793 BOOL check_first = FALSE;
2794 int len;
2795
2796 msi_ui_progress( package, 2, REG_PROGRESS_VALUE, 0, 0 );
2797
2798 component = MSI_RecordGetString(row, 6);
2799 comp = msi_get_loaded_component(package,component);
2800 if (!comp)
2801 return ERROR_SUCCESS;
2802
2803 comp->Action = msi_get_component_action( package, comp );
2804 if (comp->Action != INSTALLSTATE_LOCAL)
2805 {
2806 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
2807 return ERROR_SUCCESS;
2808 }
2809
2810 name = MSI_RecordGetString(row, 4);
2811 if( MSI_RecordIsNull(row,5) && name )
2812 {
2813 /* null values can have special meanings */
2814 if (name[0]=='-' && name[1] == 0)
2815 return ERROR_SUCCESS;
2816 if ((name[0] == '+' || name[0] == '*') && !name[1])
2817 check_first = TRUE;
2818 }
2819
2820 root = MSI_RecordGetInteger(row,2);
2821 key = MSI_RecordGetString(row, 3);
2822
2823 szRoot = get_root_key( package, root, &root_key );
2824 if (!szRoot)
2825 return ERROR_SUCCESS;
2826
2827 deformat_string(package, key , &deformated);
2828 uikey = msi_alloc( (strlenW(deformated) + strlenW(szRoot) + 1) * sizeof(WCHAR) );
2829 strcpyW(uikey,szRoot);
2830 strcatW(uikey,deformated);
2831
2832 if (!(hkey = open_key( comp, root_key, deformated, TRUE )))
2833 {
2834 ERR("Could not create key %s\n", debugstr_w(deformated));
2835 msi_free(uikey);
2836 msi_free(deformated);
2837 return ERROR_FUNCTION_FAILED;
2838 }
2839 msi_free( deformated );
2840 str = msi_record_get_string( row, 5, NULL );
2841 len = deformat_string( package, str, &deformated );
2842 new_value = parse_value( package, deformated, len, &type, &new_size );
2843
2844 msi_free( deformated );
2845 deformat_string(package, name, &deformated);
2846
2847 if (!is_special_entry( name ))
2848 {
2849 old_value = reg_get_value( hkey, deformated, &old_type, &old_size );
2850 if (type == REG_MULTI_SZ)
2851 {
2852 BYTE *new;
2853 if (old_value && old_type != REG_MULTI_SZ)
2854 {
2855 msi_free( old_value );
2856 old_value = NULL;
2857 old_size = 0;
2858 }
2859 new = build_multi_string_value( old_value, old_size, new_value, new_size, &new_size );
2860 msi_free( new_value );
2861 new_value = new;
2862 }
2863 if (!check_first)
2864 {
2865 TRACE("setting value %s of %s type %u\n", debugstr_w(deformated), debugstr_w(uikey), type);
2866 RegSetValueExW( hkey, deformated, 0, type, new_value, new_size );
2867 }
2868 else if (!old_value)
2869 {
2870 if (deformated || new_size)
2871 {
2872 TRACE("setting value %s of %s type %u\n", debugstr_w(deformated), debugstr_w(uikey), type);
2873 RegSetValueExW( hkey, deformated, 0, type, new_value, new_size );
2874 }
2875 }
2876 else TRACE("not overwriting existing value %s of %s\n", debugstr_w(deformated), debugstr_w(uikey));
2877 }
2878 RegCloseKey(hkey);
2879
2880 uirow = MSI_CreateRecord(3);
2881 MSI_RecordSetStringW(uirow,2,deformated);
2882 MSI_RecordSetStringW(uirow,1,uikey);
2883 if (type == REG_SZ || type == REG_EXPAND_SZ)
2884 MSI_RecordSetStringW(uirow, 3, (LPWSTR)new_value);
2885 msi_ui_actiondata( package, szWriteRegistryValues, uirow );
2886 msiobj_release( &uirow->hdr );
2887
2888 msi_free(new_value);
2889 msi_free(old_value);
2890 msi_free(deformated);
2891 msi_free(uikey);
2892
2893 return ERROR_SUCCESS;
2894 }
2895
2896 static UINT ACTION_WriteRegistryValues(MSIPACKAGE *package)
2897 {
2898 static const WCHAR query[] = {
2899 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2900 '`','R','e','g','i','s','t','r','y','`',0};
2901 MSIQUERY *view;
2902 UINT rc;
2903
2904 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
2905 if (rc != ERROR_SUCCESS)
2906 return ERROR_SUCCESS;
2907
2908 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteRegistryValues, package);
2909 msiobj_release(&view->hdr);
2910 return rc;
2911 }
2912
2913 static void delete_key( const MSICOMPONENT *comp, HKEY root, const WCHAR *path )
2914 {
2915 REGSAM access = 0;
2916 WCHAR *subkey, *p;
2917 HKEY hkey;
2918 LONG res;
2919
2920 access |= get_registry_view( comp );
2921
2922 if (!(subkey = strdupW( path ))) return;
2923 for (;;)
2924 {
2925 if ((p = strrchrW( subkey, '\\' ))) *p = 0;
2926 hkey = open_key( comp, root, subkey, FALSE );
2927 if (!hkey) break;
2928 if (p && p[1])
2929 res = RegDeleteKeyExW( hkey, p + 1, access, 0 );
2930 else
2931 res = RegDeleteKeyExW( root, subkey, access, 0 );
2932 if (res)
2933 {
2934 TRACE("failed to delete key %s (%d)\n", debugstr_w(subkey), res);
2935 break;
2936 }
2937 if (p && p[1]) RegCloseKey( hkey );
2938 else break;
2939 }
2940 msi_free( subkey );
2941 }
2942
2943 static void delete_value( const MSICOMPONENT *comp, HKEY root, const WCHAR *path, const WCHAR *value )
2944 {
2945 LONG res;
2946 HKEY hkey;
2947 DWORD num_subkeys, num_values;
2948
2949 if ((hkey = open_key( comp, root, path, FALSE )))
2950 {
2951 if ((res = RegDeleteValueW( hkey, value )))
2952 TRACE("failed to delete value %s (%d)\n", debugstr_w(value), res);
2953
2954 res = RegQueryInfoKeyW( hkey, NULL, NULL, NULL, &num_subkeys, NULL, NULL, &num_values,
2955 NULL, NULL, NULL, NULL );
2956 RegCloseKey( hkey );
2957 if (!res && !num_subkeys && !num_values)
2958 {
2959 TRACE("removing empty key %s\n", debugstr_w(path));
2960 delete_key( comp, root, path );
2961 }
2962 }
2963 }
2964
2965 static void delete_tree( const MSICOMPONENT *comp, HKEY root, const WCHAR *path )
2966 {
2967 LONG res;
2968 HKEY hkey;
2969
2970 if (!(hkey = open_key( comp, root, path, FALSE ))) return;
2971 res = SHDeleteKeyW( hkey, NULL );
2972 if (res) TRACE("failed to delete subtree of %s (%d)\n", debugstr_w(path), res);
2973 delete_key( comp, root, path );
2974 RegCloseKey( hkey );
2975 }
2976
2977 static UINT ITERATE_RemoveRegistryValuesOnUninstall( MSIRECORD *row, LPVOID param )
2978 {
2979 MSIPACKAGE *package = param;
2980 LPCWSTR component, name, key_str, root_key_str;
2981 LPWSTR deformated_key, deformated_name, ui_key_str;
2982 MSICOMPONENT *comp;
2983 MSIRECORD *uirow;
2984 BOOL delete_key = FALSE;
2985 HKEY hkey_root;
2986 UINT size;
2987 INT root;
2988
2989 msi_ui_progress( package, 2, REG_PROGRESS_VALUE, 0, 0 );
2990
2991 component = MSI_RecordGetString( row, 6 );
2992 comp = msi_get_loaded_component( package, component );
2993 if (!comp)
2994 return ERROR_SUCCESS;
2995
2996 comp->Action = msi_get_component_action( package, comp );
2997 if (comp->Action != INSTALLSTATE_ABSENT)
2998 {
2999 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
3000 return ERROR_SUCCESS;
3001 }
3002
3003 name = MSI_RecordGetString( row, 4 );
3004 if (MSI_RecordIsNull( row, 5 ) && name )
3005 {
3006 if (name[0] == '+' && !name[1])
3007 return ERROR_SUCCESS;
3008 if ((name[0] == '-' || name[0] == '*') && !name[1])
3009 {
3010 delete_key = TRUE;
3011 name = NULL;
3012 }
3013 }
3014
3015 root = MSI_RecordGetInteger( row, 2 );
3016 key_str = MSI_RecordGetString( row, 3 );
3017
3018 root_key_str = get_root_key( package, root, &hkey_root );
3019 if (!root_key_str)
3020 return ERROR_SUCCESS;
3021
3022 deformat_string( package, key_str, &deformated_key );
3023 size = strlenW( deformated_key ) + strlenW( root_key_str ) + 1;
3024 ui_key_str = msi_alloc( size * sizeof(WCHAR) );
3025 strcpyW( ui_key_str, root_key_str );
3026 strcatW( ui_key_str, deformated_key );
3027
3028 deformat_string( package, name, &deformated_name );
3029
3030 if (delete_key) delete_tree( comp, hkey_root, deformated_key );
3031 else delete_value( comp, hkey_root, deformated_key, deformated_name );
3032 msi_free( deformated_key );
3033
3034 uirow = MSI_CreateRecord( 2 );
3035 MSI_RecordSetStringW( uirow, 1, ui_key_str );
3036 MSI_RecordSetStringW( uirow, 2, deformated_name );
3037 msi_ui_actiondata( package, szRemoveRegistryValues, uirow );
3038 msiobj_release( &uirow->hdr );
3039
3040 msi_free( ui_key_str );
3041 msi_free( deformated_name );
3042 return ERROR_SUCCESS;
3043 }
3044
3045 static UINT ITERATE_RemoveRegistryValuesOnInstall( MSIRECORD *row, LPVOID param )
3046 {
3047 MSIPACKAGE *package = param;
3048 LPCWSTR component, name, key_str, root_key_str;
3049 LPWSTR deformated_key, deformated_name, ui_key_str;
3050 MSICOMPONENT *comp;
3051 MSIRECORD *uirow;
3052 BOOL delete_key = FALSE;
3053 HKEY hkey_root;
3054 UINT size;
3055 INT root;
3056
3057 component = MSI_RecordGetString( row, 5 );
3058 comp = msi_get_loaded_component( package, component );
3059 if (!comp)
3060 return ERROR_SUCCESS;
3061
3062 comp->Action = msi_get_component_action( package, comp );
3063 if (comp->Action != INSTALLSTATE_LOCAL)
3064 {
3065 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
3066 return ERROR_SUCCESS;
3067 }
3068
3069 if ((name = MSI_RecordGetString( row, 4 )))
3070 {
3071 if (name[0] == '-' && !name[1])
3072 {
3073 delete_key = TRUE;
3074 name = NULL;
3075 }
3076 }
3077
3078 root = MSI_RecordGetInteger( row, 2 );
3079 key_str = MSI_RecordGetString( row, 3 );
3080
3081 root_key_str = get_root_key( package, root, &hkey_root );
3082 if (!root_key_str)
3083 return ERROR_SUCCESS;
3084
3085 deformat_string( package, key_str, &deformated_key );
3086 size = strlenW( deformated_key ) + strlenW( root_key_str ) + 1;
3087 ui_key_str = msi_alloc( size * sizeof(WCHAR) );
3088 strcpyW( ui_key_str, root_key_str );
3089 strcatW( ui_key_str, deformated_key );
3090
3091 deformat_string( package, name, &deformated_name );
3092
3093 if (delete_key) delete_tree( comp, hkey_root, deformated_key );
3094 else delete_value( comp, hkey_root, deformated_key, deformated_name );
3095 msi_free( deformated_key );
3096
3097 uirow = MSI_CreateRecord( 2 );
3098 MSI_RecordSetStringW( uirow, 1, ui_key_str );
3099 MSI_RecordSetStringW( uirow, 2, deformated_name );
3100 msi_ui_actiondata( package, szRemoveRegistryValues, uirow );
3101 msiobj_release( &uirow->hdr );
3102
3103 msi_free( ui_key_str );
3104 msi_free( deformated_name );
3105 return ERROR_SUCCESS;
3106 }
3107
3108 static UINT ACTION_RemoveRegistryValues( MSIPACKAGE *package )
3109 {
3110 static const WCHAR registry_query[] = {
3111 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3112 '`','R','e','g','i','s','t','r','y','`',0};
3113 static const WCHAR remove_registry_query[] = {
3114 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3115 '`','R','e','m','o','v','e','R','e','g','i','s','t','r','y','`',0};
3116 MSIQUERY *view;
3117 UINT rc;
3118
3119 rc = MSI_DatabaseOpenViewW( package->db, registry_query, &view );
3120 if (rc == ERROR_SUCCESS)
3121 {
3122 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveRegistryValuesOnUninstall, package );
3123 msiobj_release( &view->hdr );
3124 if (rc != ERROR_SUCCESS)
3125 return rc;
3126 }
3127 rc = MSI_DatabaseOpenViewW( package->db, remove_registry_query, &view );
3128 if (rc == ERROR_SUCCESS)
3129 {
3130 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveRegistryValuesOnInstall, package );
3131 msiobj_release( &view->hdr );
3132 if (rc != ERROR_SUCCESS)
3133 return rc;
3134 }
3135 return ERROR_SUCCESS;
3136 }
3137
3138 static UINT A