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