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