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