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