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