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