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