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