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