bb559c016af72eba6d073acabdca1e0c31ce2458
[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->Action == 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->Action == 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->Action == 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->Action)
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 *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 *)&ret, &sz ))
2006 {
2007 msi_free( version );
2008 return NULL;
2009 }
2010
2011 msi_free( version );
2012 return ret;
2013 }
2014
2015 int msi_compare_file_versions( VS_FIXEDFILEINFO *fi, const WCHAR *version )
2016 {
2017 DWORD ms, ls;
2018
2019 msi_parse_version_string( version, &ms, &ls );
2020
2021 if (fi->dwFileVersionMS > ms) return 1;
2022 else if (fi->dwFileVersionMS < ms) return -1;
2023 else if (fi->dwFileVersionLS > ls) return 1;
2024 else if (fi->dwFileVersionLS < ls) return -1;
2025 return 0;
2026 }
2027
2028 static DWORD get_disk_file_size( LPCWSTR filename )
2029 {
2030 HANDLE file;
2031 DWORD size;
2032
2033 TRACE("%s\n", debugstr_w(filename));
2034
2035 file = CreateFileW( filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL );
2036 if (file == INVALID_HANDLE_VALUE)
2037 return INVALID_FILE_SIZE;
2038
2039 size = GetFileSize( file, NULL );
2040 CloseHandle( file );
2041 return size;
2042 }
2043
2044 static BOOL hash_matches( MSIFILE *file )
2045 {
2046 UINT r;
2047 MSIFILEHASHINFO hash;
2048
2049 hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
2050 r = MsiGetFileHashW( file->TargetPath, 0, &hash );
2051 if (r != ERROR_SUCCESS)
2052 return FALSE;
2053
2054 return !memcmp( &hash, &file->hash, sizeof(MSIFILEHASHINFO) );
2055 }
2056
2057 static UINT set_file_install_states( MSIPACKAGE *package )
2058 {
2059 VS_FIXEDFILEINFO *file_version;
2060 MSIFILE *file;
2061
2062 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2063 {
2064 MSICOMPONENT* comp = file->Component;
2065 DWORD file_size;
2066 LPWSTR p;
2067
2068 if (!comp)
2069 continue;
2070
2071 if (file->IsCompressed)
2072 comp->ForceLocalState = TRUE;
2073
2074 /* calculate target */
2075 p = resolve_folder(package, comp->Directory, FALSE, FALSE, TRUE, NULL);
2076 msi_free(file->TargetPath);
2077
2078 TRACE("file %s is named %s\n", debugstr_w(file->File), debugstr_w(file->FileName));
2079
2080 file->TargetPath = build_directory_name(2, p, file->FileName);
2081 msi_free(p);
2082
2083 TRACE("file %s resolves to %s\n", debugstr_w(file->File), debugstr_w(file->TargetPath));
2084
2085 if (GetFileAttributesW(file->TargetPath) == INVALID_FILE_ATTRIBUTES)
2086 {
2087 file->state = msifs_missing;
2088 comp->Cost += file->FileSize;
2089 continue;
2090 }
2091 if (file->Version && (file_version = msi_get_disk_file_version( file->TargetPath )))
2092 {
2093 TRACE("new %s old %u.%u.%u.%u\n", debugstr_w(file->Version),
2094 HIWORD(file_version->dwFileVersionMS),
2095 LOWORD(file_version->dwFileVersionMS),
2096 HIWORD(file_version->dwFileVersionLS),
2097 LOWORD(file_version->dwFileVersionLS));
2098
2099 if (msi_compare_file_versions( file_version, file->Version ) < 0)
2100 {
2101 file->state = msifs_overwrite;
2102 comp->Cost += file->FileSize;
2103 }
2104 else
2105 {
2106 TRACE("Destination file version equal or greater, not overwriting\n");
2107 file->state = msifs_present;
2108 }
2109 msi_free( file_version );
2110 continue;
2111 }
2112 if ((file_size = get_disk_file_size( file->TargetPath )) != file->FileSize)
2113 {
2114 file->state = msifs_overwrite;
2115 comp->Cost += file->FileSize - file_size;
2116 continue;
2117 }
2118 if (file->hash.dwFileHashInfoSize && hash_matches( file ))
2119 {
2120 TRACE("File hashes match, not overwriting\n");
2121 file->state = msifs_present;
2122 continue;
2123 }
2124 file->state = msifs_overwrite;
2125 comp->Cost += file->FileSize - file_size;
2126 }
2127
2128 return ERROR_SUCCESS;
2129 }
2130
2131 /*
2132 * A lot is done in this function aside from just the costing.
2133 * The costing needs to be implemented at some point but for now I am going
2134 * to focus on the directory building
2135 *
2136 */
2137 static UINT ACTION_CostFinalize(MSIPACKAGE *package)
2138 {
2139 static const WCHAR ExecSeqQuery[] =
2140 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2141 '`','D','i','r','e','c','t','o','r','y','`',0};
2142 static const WCHAR ConditionQuery[] =
2143 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2144 '`','C','o','n','d','i','t','i','o','n','`',0};
2145 static const WCHAR szCosting[] =
2146 {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
2147 static const WCHAR szlevel[] =
2148 {'I','N','S','T','A','L','L','L','E','V','E','L',0};
2149 static const WCHAR szOutOfDiskSpace[] =
2150 {'O','u','t','O','f','D','i','s','k','S','p','a','c','e',0};
2151 MSICOMPONENT *comp;
2152 UINT rc = ERROR_SUCCESS;
2153 MSIQUERY * view;
2154 LPWSTR level;
2155
2156 TRACE("Building Directory properties\n");
2157
2158 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2159 if (rc == ERROR_SUCCESS)
2160 {
2161 rc = MSI_IterateRecords(view, NULL, ITERATE_CostFinalizeDirectories,
2162 package);
2163 msiobj_release(&view->hdr);
2164 }
2165
2166 /* read components states from the registry */
2167 ACTION_GetComponentInstallStates(package);
2168 ACTION_GetFeatureInstallStates(package);
2169
2170 TRACE("Calculating file install states\n");
2171 set_file_install_states( package );
2172
2173 if (!process_overrides( package, msi_get_property_int( package->db, szlevel, 1 ) ))
2174 {
2175 TRACE("Evaluating feature conditions\n");
2176
2177 rc = MSI_DatabaseOpenViewW( package->db, ConditionQuery, &view );
2178 if (rc == ERROR_SUCCESS)
2179 {
2180 rc = MSI_IterateRecords( view, NULL, ITERATE_CostFinalizeConditions, package );
2181 msiobj_release( &view->hdr );
2182 }
2183 }
2184 TRACE("Evaluating component conditions\n");
2185
2186 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2187 {
2188 if (MSI_EvaluateConditionW( package, comp->Condition ) == MSICONDITION_FALSE)
2189 {
2190 TRACE("Disabling component %s\n", debugstr_w(comp->Component));
2191 comp->Enabled = FALSE;
2192 }
2193 else
2194 comp->Enabled = TRUE;
2195 }
2196
2197 msi_set_property( package->db, szCosting, szOne );
2198 /* set default run level if not set */
2199 level = msi_dup_property( package->db, szlevel );
2200 if (!level)
2201 msi_set_property( package->db, szlevel, szOne );
2202 msi_free(level);
2203
2204 /* FIXME: check volume disk space */
2205 msi_set_property( package->db, szOutOfDiskSpace, szZero );
2206
2207 return MSI_SetFeatureStates(package);
2208 }
2209
2210 /* OK this value is "interpreted" and then formatted based on the
2211 first few characters */
2212 static LPSTR parse_value(MSIPACKAGE *package, LPCWSTR value, DWORD *type,
2213 DWORD *size)
2214 {
2215 LPSTR data = NULL;
2216
2217 if (value[0]=='#' && value[1]!='#' && value[1]!='%')
2218 {
2219 if (value[1]=='x')
2220 {
2221 LPWSTR ptr;
2222 CHAR byte[5];
2223 LPWSTR deformated = NULL;
2224 int count;
2225
2226 deformat_string(package, &value[2], &deformated);
2227
2228 /* binary value type */
2229 ptr = deformated;
2230 *type = REG_BINARY;
2231 if (strlenW(ptr)%2)
2232 *size = (strlenW(ptr)/2)+1;
2233 else
2234 *size = strlenW(ptr)/2;
2235
2236 data = msi_alloc(*size);
2237
2238 byte[0] = '0';
2239 byte[1] = 'x';
2240 byte[4] = 0;
2241 count = 0;
2242 /* if uneven pad with a zero in front */
2243 if (strlenW(ptr)%2)
2244 {
2245 byte[2]= '0';
2246 byte[3]= *ptr;
2247 ptr++;
2248 data[count] = (BYTE)strtol(byte,NULL,0);
2249 count ++;
2250 TRACE("Uneven byte count\n");
2251 }
2252 while (*ptr)
2253 {
2254 byte[2]= *ptr;
2255 ptr++;
2256 byte[3]= *ptr;
2257 ptr++;
2258 data[count] = (BYTE)strtol(byte,NULL,0);
2259 count ++;
2260 }
2261 msi_free(deformated);
2262
2263 TRACE("Data %i bytes(%i)\n",*size,count);
2264 }
2265 else
2266 {
2267 LPWSTR deformated;
2268 LPWSTR p;
2269 DWORD d = 0;
2270 deformat_string(package, &value[1], &deformated);
2271
2272 *type=REG_DWORD;
2273 *size = sizeof(DWORD);
2274 data = msi_alloc(*size);
2275 p = deformated;
2276 if (*p == '-')
2277 p++;
2278 while (*p)
2279 {
2280 if ( (*p < '0') || (*p > '9') )
2281 break;
2282 d *= 10;
2283 d += (*p - '0');
2284 p++;
2285 }
2286 if (deformated[0] == '-')
2287 d = -d;
2288 *(LPDWORD)data = d;
2289 TRACE("DWORD %i\n",*(LPDWORD)data);
2290
2291 msi_free(deformated);
2292 }
2293 }
2294 else
2295 {
2296 static const WCHAR szMulti[] = {'[','~',']',0};
2297 LPCWSTR ptr;
2298 *type=REG_SZ;
2299
2300 if (value[0]=='#')
2301 {
2302 if (value[1]=='%')
2303 {
2304 ptr = &value[2];
2305 *type=REG_EXPAND_SZ;
2306 }
2307 else
2308 ptr = &value[1];
2309 }
2310 else
2311 ptr=value;
2312
2313 if (strstrW(value,szMulti))
2314 *type = REG_MULTI_SZ;
2315
2316 /* remove initial delimiter */
2317 if (!strncmpW(value, szMulti, 3))
2318 ptr = value + 3;
2319
2320 *size = deformat_string(package, ptr,(LPWSTR*)&data);
2321
2322 /* add double NULL terminator */
2323 if (*type == REG_MULTI_SZ)
2324 {
2325 *size += 2 * sizeof(WCHAR); /* two NULL terminators */
2326 data = msi_realloc_zero(data, *size);
2327 }
2328 }
2329 return data;
2330 }
2331
2332 static const WCHAR *get_root_key( MSIPACKAGE *package, INT root, HKEY *root_key )
2333 {
2334 const WCHAR *ret;
2335
2336 switch (root)
2337 {
2338 case -1:
2339 if (msi_get_property_int( package->db, szAllUsers, 0 ))
2340 {
2341 *root_key = HKEY_LOCAL_MACHINE;
2342 ret = szHLM;
2343 }
2344 else
2345 {
2346 *root_key = HKEY_CURRENT_USER;
2347 ret = szHCU;
2348 }
2349 break;
2350 case 0:
2351 *root_key = HKEY_CLASSES_ROOT;
2352 ret = szHCR;
2353 break;
2354 case 1:
2355 *root_key = HKEY_CURRENT_USER;
2356 ret = szHCU;
2357 break;
2358 case 2:
2359 *root_key = HKEY_LOCAL_MACHINE;
2360 ret = szHLM;
2361 break;
2362 case 3:
2363 *root_key = HKEY_USERS;
2364 ret = szHU;
2365 break;
2366 default:
2367 ERR("Unknown root %i\n", root);
2368 return NULL;
2369 }
2370
2371 return ret;
2372 }
2373
2374 static UINT ITERATE_WriteRegistryValues(MSIRECORD *row, LPVOID param)
2375 {
2376 MSIPACKAGE *package = param;
2377 LPSTR value_data = NULL;
2378 HKEY root_key, hkey;
2379 DWORD type,size;
2380 LPWSTR deformated;
2381 LPCWSTR szRoot, component, name, key, value;
2382 MSICOMPONENT *comp;
2383 MSIRECORD * uirow;
2384 LPWSTR uikey;
2385 INT root;
2386 BOOL check_first = FALSE;
2387 UINT rc;
2388
2389 ui_progress(package,2,0,0,0);
2390
2391 component = MSI_RecordGetString(row, 6);
2392 comp = get_loaded_component(package,component);
2393 if (!comp)
2394 return ERROR_SUCCESS;
2395
2396 if (!comp->Enabled)
2397 {
2398 TRACE("component is disabled\n");
2399 return ERROR_SUCCESS;
2400 }
2401
2402 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
2403 {
2404 TRACE("Component not scheduled for installation: %s\n", debugstr_w(component));
2405 comp->Action = comp->Installed;
2406 return ERROR_SUCCESS;
2407 }
2408 comp->Action = INSTALLSTATE_LOCAL;
2409
2410 name = MSI_RecordGetString(row, 4);
2411 if( MSI_RecordIsNull(row,5) && name )
2412 {
2413 /* null values can have special meanings */
2414 if (name[0]=='-' && name[1] == 0)
2415 return ERROR_SUCCESS;
2416 else if ((name[0]=='+' && name[1] == 0) ||
2417 (name[0] == '*' && name[1] == 0))
2418 name = NULL;
2419 check_first = TRUE;
2420 }
2421
2422 root = MSI_RecordGetInteger(row,2);
2423 key = MSI_RecordGetString(row, 3);
2424
2425 szRoot = get_root_key( package, root, &root_key );
2426 if (!szRoot)
2427 return ERROR_SUCCESS;
2428
2429 deformat_string(package, key , &deformated);
2430 size = strlenW(deformated) + strlenW(szRoot) + 1;
2431 uikey = msi_alloc(size*sizeof(WCHAR));
2432 strcpyW(uikey,szRoot);
2433 strcatW(uikey,deformated);
2434
2435 if (RegCreateKeyW( root_key, deformated, &hkey))
2436 {
2437 ERR("Could not create key %s\n",debugstr_w(deformated));
2438 msi_free(deformated);
2439 msi_free(uikey);
2440 return ERROR_SUCCESS;
2441 }
2442 msi_free(deformated);
2443
2444 value = MSI_RecordGetString(row,5);
2445 if (value)
2446 value_data = parse_value(package, value, &type, &size);
2447 else
2448 {
2449 value_data = (LPSTR)strdupW(szEmpty);
2450 size = sizeof(szEmpty);
2451 type = REG_SZ;
2452 }
2453
2454 deformat_string(package, name, &deformated);
2455
2456 if (!check_first)
2457 {
2458 TRACE("Setting value %s of %s\n",debugstr_w(deformated),
2459 debugstr_w(uikey));
2460 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE)value_data, size);
2461 }
2462 else
2463 {
2464 DWORD sz = 0;
2465 rc = RegQueryValueExW(hkey, deformated, NULL, NULL, NULL, &sz);
2466 if (rc == ERROR_SUCCESS || rc == ERROR_MORE_DATA)
2467 {
2468 TRACE("value %s of %s checked already exists\n",
2469 debugstr_w(deformated), debugstr_w(uikey));
2470 }
2471 else
2472 {
2473 TRACE("Checked and setting value %s of %s\n",
2474 debugstr_w(deformated), debugstr_w(uikey));
2475 if (deformated || size)
2476 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE) value_data, size);
2477 }
2478 }
2479 RegCloseKey(hkey);
2480
2481 uirow = MSI_CreateRecord(3);
2482 MSI_RecordSetStringW(uirow,2,deformated);
2483 MSI_RecordSetStringW(uirow,1,uikey);
2484 if (type == REG_SZ || type == REG_EXPAND_SZ)
2485 MSI_RecordSetStringW(uirow,3,(LPWSTR)value_data);
2486 ui_actiondata(package,szWriteRegistryValues,uirow);
2487 msiobj_release( &uirow->hdr );
2488
2489 msi_free(value_data);
2490 msi_free(deformated);
2491 msi_free(uikey);
2492
2493 return ERROR_SUCCESS;
2494 }
2495
2496 static UINT ACTION_WriteRegistryValues(MSIPACKAGE *package)
2497 {
2498 UINT rc;
2499 MSIQUERY * view;
2500 static const WCHAR ExecSeqQuery[] =
2501 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2502 '`','R','e','g','i','s','t','r','y','`',0 };
2503
2504 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2505 if (rc != ERROR_SUCCESS)
2506 return ERROR_SUCCESS;
2507
2508 /* increment progress bar each time action data is sent */
2509 ui_progress(package,1,REG_PROGRESS_VALUE,1,0);
2510
2511 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteRegistryValues, package);
2512
2513 msiobj_release(&view->hdr);
2514 return rc;
2515 }
2516
2517 static void delete_reg_key_or_value( HKEY hkey_root, LPCWSTR key, LPCWSTR value, BOOL delete_key )
2518 {
2519 LONG res;
2520 HKEY hkey;
2521 DWORD num_subkeys, num_values;
2522
2523 if (delete_key)
2524 {
2525 if ((res = RegDeleteTreeW( hkey_root, key )))
2526 {
2527 WARN("Failed to delete key %s (%d)\n", debugstr_w(key), res);
2528 }
2529 return;
2530 }
2531
2532 if (!(res = RegOpenKeyW( hkey_root, key, &hkey )))
2533 {
2534 if ((res = RegDeleteValueW( hkey, value )))
2535 {
2536 WARN("Failed to delete value %s (%d)\n", debugstr_w(value), res);
2537 }
2538 res = RegQueryInfoKeyW( hkey, NULL, NULL, NULL, &num_subkeys, NULL, NULL, &num_values,
2539 NULL, NULL, NULL, NULL );
2540 RegCloseKey( hkey );
2541
2542 if (!res && !num_subkeys && !num_values)
2543 {
2544 TRACE("Removing empty key %s\n", debugstr_w(key));
2545 RegDeleteKeyW( hkey_root, key );
2546 }
2547 return;
2548 }
2549 WARN("Failed to open key %s (%d)\n", debugstr_w(key), res);
2550 }
2551
2552
2553 static UINT ITERATE_RemoveRegistryValuesOnUninstall( MSIRECORD *row, LPVOID param )
2554 {
2555 MSIPACKAGE *package = param;
2556 LPCWSTR component, name, key_str, root_key_str;
2557 LPWSTR deformated_key, deformated_name, ui_key_str;
2558 MSICOMPONENT *comp;
2559 MSIRECORD *uirow;
2560 BOOL delete_key = FALSE;
2561 HKEY hkey_root;
2562 UINT size;
2563 INT root;
2564
2565 ui_progress( package, 2, 0, 0, 0 );
2566
2567 component = MSI_RecordGetString( row, 6 );
2568 comp = get_loaded_component( package, component );
2569 if (!comp)
2570 return ERROR_SUCCESS;
2571
2572 if (!comp->Enabled)
2573 {
2574 TRACE("component is disabled\n");
2575 return ERROR_SUCCESS;
2576 }
2577
2578 if (comp->ActionRequest != INSTALLSTATE_ABSENT)
2579 {
2580 TRACE("Component not scheduled for removal: %s\n", debugstr_w(component));
2581 comp->Action = comp->Installed;
2582 return ERROR_SUCCESS;
2583 }
2584 comp->Action = INSTALLSTATE_ABSENT;
2585
2586 name = MSI_RecordGetString( row, 4 );
2587 if (MSI_RecordIsNull( row, 5 ) && name )
2588 {
2589 if (name[0] == '+' && !name[1])
2590 return ERROR_SUCCESS;
2591 else if ((name[0] == '-' && !name[1]) || (name[0] == '*' && !name[1]))
2592 {
2593 delete_key = TRUE;
2594 name = NULL;
2595 }
2596 }
2597
2598 root = MSI_RecordGetInteger( row, 2 );
2599 key_str = MSI_RecordGetString( row, 3 );
2600
2601 root_key_str = get_root_key( package, root, &hkey_root );
2602 if (!root_key_str)
2603 return ERROR_SUCCESS;
2604
2605 deformat_string( package, key_str, &deformated_key );
2606 size = strlenW( deformated_key ) + strlenW( root_key_str ) + 1;
2607 ui_key_str = msi_alloc( size * sizeof(WCHAR) );
2608 strcpyW( ui_key_str, root_key_str );
2609 strcatW( ui_key_str, deformated_key );
2610
2611 deformat_string( package, name, &deformated_name );
2612
2613 delete_reg_key_or_value( hkey_root, deformated_key, deformated_name, delete_key );
2614 msi_free( deformated_key );
2615
2616 uirow = MSI_CreateRecord( 2 );
2617 MSI_RecordSetStringW( uirow, 1, ui_key_str );
2618 MSI_RecordSetStringW( uirow, 2, deformated_name );
2619
2620 ui_actiondata( package, szRemoveRegistryValues, uirow );
2621 msiobj_release( &uirow->hdr );
2622
2623 msi_free( ui_key_str );
2624 msi_free( deformated_name );
2625 return ERROR_SUCCESS;
2626 }
2627
2628 static UINT ITERATE_RemoveRegistryValuesOnInstall( MSIRECORD *row, LPVOID param )
2629 {
2630 MSIPACKAGE *package = param;
2631 LPCWSTR component, name, key_str, root_key_str;
2632 LPWSTR deformated_key, deformated_name, ui_key_str;
2633 MSICOMPONENT *comp;
2634 MSIRECORD *uirow;
2635 BOOL delete_key = FALSE;
2636 HKEY hkey_root;
2637 UINT size;
2638 INT root;
2639
2640 ui_progress( package, 2, 0, 0, 0 );
2641
2642 component = MSI_RecordGetString( row, 5 );
2643 comp = get_loaded_component( package, component );
2644 if (!comp)
2645 return ERROR_SUCCESS;
2646
2647 if (!comp->Enabled)
2648 {
2649 TRACE("component is disabled\n");
2650 return ERROR_SUCCESS;
2651 }
2652
2653 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
2654 {
2655 TRACE("Component not scheduled for installation: %s\n", debugstr_w(component));
2656 comp->Action = comp->Installed;
2657 return ERROR_SUCCESS;
2658 }
2659 comp->Action = INSTALLSTATE_LOCAL;
2660
2661 if ((name = MSI_RecordGetString( row, 4 )))
2662 {
2663 if (name[0] == '-' && !name[1])
2664 {
2665 delete_key = TRUE;
2666 name = NULL;
2667 }
2668 }
2669
2670 root = MSI_RecordGetInteger( row, 2 );
2671 key_str = MSI_RecordGetString( row, 3 );
2672
2673 root_key_str = get_root_key( package, root, &hkey_root );
2674 if (!root_key_str)
2675 return ERROR_SUCCESS;
2676
2677 deformat_string( package, key_str, &deformated_key );
2678 size = strlenW( deformated_key ) + strlenW( root_key_str ) + 1;
2679 ui_key_str = msi_alloc( size * sizeof(WCHAR) );
2680 strcpyW( ui_key_str, root_key_str );
2681 strcatW( ui_key_str, deformated_key );
2682
2683 deformat_string( package, name, &deformated_name );
2684
2685 delete_reg_key_or_value( hkey_root, deformated_key, deformated_name, delete_key );
2686 msi_free( deformated_key );
2687
2688 uirow = MSI_CreateRecord( 2 );
2689 MSI_RecordSetStringW( uirow, 1, ui_key_str );
2690 MSI_RecordSetStringW( uirow, 2, deformated_name );
2691
2692 ui_actiondata( package, szRemoveRegistryValues, uirow );
2693 msiobj_release( &uirow->hdr );
2694
2695 msi_free( ui_key_str );
2696 msi_free( deformated_name );
2697 return ERROR_SUCCESS;
2698 }
2699
2700 static UINT ACTION_RemoveRegistryValues( MSIPACKAGE *package )
2701 {
2702 UINT rc;
2703 MSIQUERY *view;
2704 static const WCHAR registry_query[] =
2705 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2706 '`','R','e','g','i','s','t','r','y','`',0 };
2707 static const WCHAR remove_registry_query[] =
2708 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2709 '`','R','e','m','o','v','e','R','e','g','i','s','t','r','y','`',0 };
2710
2711 /* increment progress bar each time action data is sent */
2712 ui_progress( package, 1, REG_PROGRESS_VALUE, 1, 0 );
2713
2714 rc = MSI_DatabaseOpenViewW( package->db, registry_query, &view );
2715 if (rc == ERROR_SUCCESS)
2716 {
2717 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveRegistryValuesOnUninstall, package );
2718 msiobj_release( &view->hdr );
2719 if (rc != ERROR_SUCCESS)
2720 return rc;
2721 }
2722
2723 rc = MSI_DatabaseOpenViewW( package->db, remove_registry_query, &view );
2724 if (rc == ERROR_SUCCESS)
2725 {
2726 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveRegistryValuesOnInstall, package );
2727 msiobj_release( &view->hdr );
2728 if (rc != ERROR_SUCCESS)
2729 return rc;
2730 }
2731
2732 return ERROR_SUCCESS;
2733 }
2734
2735 static UINT ACTION_InstallInitialize(MSIPACKAGE *package)
2736 {
2737 package->script->CurrentlyScripting = TRUE;
2738
2739 return ERROR_SUCCESS;
2740 }
2741
2742
2743 static UINT ACTION_InstallValidate(MSIPACKAGE *package)
2744 {
2745 MSICOMPONENT *comp;
2746 DWORD progress = 0;
2747 DWORD total = 0;
2748 static const WCHAR q1[]=
2749 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
2750 '`','R','e','g','i','s','t','r','y','`',0};
2751 UINT rc;
2752 MSIQUERY * view;
2753 MSIFEATURE *feature;
2754 MSIFILE *file;
2755
2756 TRACE("InstallValidate\n");
2757
2758 rc = MSI_DatabaseOpenViewW(package->db, q1, &view);
2759 if (rc == ERROR_SUCCESS)
2760 {
2761 MSI_IterateRecords( view, &progress, NULL, package );
2762 msiobj_release( &view->hdr );
2763 total += progress * REG_PROGRESS_VALUE;
2764 }
2765
2766 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2767 total += COMPONENT_PROGRESS_VALUE;
2768
2769 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2770 total += file->FileSize;
2771
2772 ui_progress(package,0,total,0,0);
2773
2774 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2775 {
2776 TRACE("Feature: %s Installed %d Request %d Action %d\n",
2777 debugstr_w(feature->Feature), feature->Installed,
2778 feature->ActionRequest, feature->Action);
2779 }
2780
2781 return ERROR_SUCCESS;
2782 }
2783
2784 static UINT ITERATE_LaunchConditions(MSIRECORD *row, LPVOID param)
2785 {
2786 MSIPACKAGE* package = param;
2787 LPCWSTR cond = NULL;
2788 LPCWSTR message = NULL;
2789 UINT r;
2790
2791 static const WCHAR title[]=
2792 {'I','n','s','t','a','l','l',' ','F','a', 'i','l','e','d',0};
2793
2794 cond = MSI_RecordGetString(row,1);
2795
2796 r = MSI_EvaluateConditionW(package,cond);
2797 if (r == MSICONDITION_FALSE)
2798 {
2799 if ((gUILevel & INSTALLUILEVEL_MASK) != INSTALLUILEVEL_NONE)
2800 {
2801 LPWSTR deformated;
2802 message = MSI_RecordGetString(row,2);
2803 deformat_string(package,message,&deformated);
2804 MessageBoxW(NULL,deformated,title,MB_OK);
2805 msi_free(deformated);
2806 }
2807
2808 return ERROR_INSTALL_FAILURE;
2809 }
2810
2811 return ERROR_SUCCESS;
2812 }
2813
2814 static UINT ACTION_LaunchConditions(MSIPACKAGE *package)
2815 {
2816 UINT rc;
2817 MSIQUERY * view = NULL;
2818 static const WCHAR ExecSeqQuery[] =
2819 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2820 '`','L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','`',0};
2821
2822 TRACE("Checking launch conditions\n");
2823
2824 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2825 if (rc != ERROR_SUCCESS)
2826 return ERROR_SUCCESS;
2827
2828 rc = MSI_IterateRecords(view, NULL, ITERATE_LaunchConditions, package);
2829 msiobj_release(&view->hdr);
2830
2831 return rc;
2832 }
2833
2834 static LPWSTR resolve_keypath( MSIPACKAGE* package, MSICOMPONENT *cmp )
2835 {
2836
2837 if (!cmp->KeyPath)
2838 return resolve_folder(package,cmp->Directory,FALSE,FALSE,TRUE,NULL);
2839
2840 if (cmp->Attributes & msidbComponentAttributesRegistryKeyPath)
2841 {
2842 MSIRECORD * row = 0;
2843 UINT root,len;
2844 LPWSTR deformated,buffer,deformated_name;
2845 LPCWSTR key,name;
2846 static const WCHAR ExecSeqQuery[] =
2847 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2848 '`','R','e','g','i','s','t','r','y','`',' ',
2849 'W','H','E','R','E',' ', '`','R','e','g','i','s','t','r','y','`',
2850 ' ','=',' ' ,'\'','%','s','\'',0 };
2851 static const WCHAR fmt[]={'%','0','2','i',':','\\','%','s','\\',0};
2852 static const WCHAR fmt2[]=
2853 {'%','0','2','i',':','\\','%','s','\\','%','s',0};
2854
2855 row = MSI_QueryGetRecord(package->db, ExecSeqQuery,cmp->KeyPath);
2856 if (!row)
2857 return NULL;
2858
2859 root = MSI_RecordGetInteger(row,2);
2860 key = MSI_RecordGetString(row, 3);
2861 name = MSI_RecordGetString(row, 4);
2862 deformat_string(package, key , &deformated);
2863 deformat_string(package, name, &deformated_name);
2864
2865 len = strlenW(deformated) + 6;
2866 if (deformated_name)
2867 len+=strlenW(deformated_name);
2868
2869 buffer = msi_alloc( len *sizeof(WCHAR));
2870
2871 if (deformated_name)
2872 sprintfW(buffer,fmt2,root,deformated,deformated_name);
2873 else
2874 sprintfW(buffer,fmt,root,deformated);
2875
2876 msi_free(deformated);
2877 msi_free(deformated_name);
2878 msiobj_release(&row->hdr);
2879
2880 return buffer;
2881 }
2882 else if (cmp->Attributes & msidbComponentAttributesODBCDataSource)
2883 {
2884 FIXME("UNIMPLEMENTED keypath as ODBC Source\n");
2885 return NULL;
2886 }
2887 else
2888 {
2889 MSIFILE *file = get_loaded_file( package, cmp->KeyPath );
2890
2891 if (file)
2892 return strdupW( file->TargetPath );
2893 }
2894 return NULL;
2895 }
2896
2897 static HKEY openSharedDLLsKey(void)
2898 {
2899 HKEY hkey=0;
2900 static const WCHAR path[] =
2901 {'S','o','f','t','w','a','r','e','\\',
2902 'M','i','c','r','o','s','o','f','t','\\',
2903 'W','i','n','d','o','w','s','\\',
2904 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
2905 'S','h','a','r','e','d','D','L','L','s',0};
2906
2907 RegCreateKeyW(HKEY_LOCAL_MACHINE,path,&hkey);
2908 return hkey;
2909 }
2910
2911 static UINT ACTION_GetSharedDLLsCount(LPCWSTR dll)
2912 {
2913 HKEY hkey;
2914 DWORD count=0;
2915 DWORD type;
2916 DWORD sz = sizeof(count);
2917 DWORD rc;
2918
2919 hkey = openSharedDLLsKey();
2920 rc = RegQueryValueExW(hkey, dll, NULL, &type, (LPBYTE)&count, &sz);
2921 if (rc != ERROR_SUCCESS)
2922 count = 0;
2923 RegCloseKey(hkey);
2924 return count;
2925 }
2926
2927 static UINT ACTION_WriteSharedDLLsCount(LPCWSTR path, UINT count)
2928 {
2929 HKEY hkey;
2930
2931 hkey = openSharedDLLsKey();
2932 if (count > 0)
2933 msi_reg_set_val_dword( hkey, path, count );
2934 else
2935 RegDeleteValueW(hkey,path);
2936 RegCloseKey(hkey);
2937 return count;
2938 }
2939
2940 /*
2941 * Return TRUE if the count should be written out and FALSE if not
2942 */
2943 static void ACTION_RefCountComponent( MSIPACKAGE* package, MSICOMPONENT *comp )
2944 {
2945 MSIFEATURE *feature;
2946 INT count = 0;
2947 BOOL write = FALSE;
2948
2949 /* only refcount DLLs */
2950 if (comp->KeyPath == NULL ||
2951 comp->Attributes & msidbComponentAttributesRegistryKeyPath ||
2952 comp->Attributes & msidbComponentAttributesODBCDataSource)
2953 write = FALSE;
2954 else
2955 {
2956 count = ACTION_GetSharedDLLsCount( comp->FullKeypath);
2957 write = (count > 0);
2958
2959 if (comp->Attributes & msidbComponentAttributesSharedDllRefCount)
2960 write = TRUE;
2961 }
2962
2963 /* increment counts */
2964 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2965 {
2966 ComponentList *cl;
2967
2968 if (feature->ActionRequest != INSTALLSTATE_LOCAL)
2969 continue;
2970
2971 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
2972 {
2973 if ( cl->component == comp )
2974 count++;
2975 }
2976 }
2977
2978 /* decrement counts */
2979 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2980 {
2981 ComponentList *cl;
2982
2983 if (feature->ActionRequest != INSTALLSTATE_ABSENT)
2984 continue;
2985
2986 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
2987 {
2988 if ( cl->component == comp )
2989 count--;
2990 }
2991 }
2992
2993 /* ref count all the files in the component */
2994 if (write)
2995 {
2996 MSIFILE *file;
2997
2998 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2999 {
3000 if (file->Component == comp)
3001 ACTION_WriteSharedDLLsCount( file->TargetPath, count );
3002 }
3003 }
3004
3005 /* add a count for permanent */
3006 if (comp->Attributes & msidbComponentAttributesPermanent)
3007 count ++;
3008
3009 comp->RefCount = count;
3010
3011 if (write)
3012 ACTION_WriteSharedDLLsCount( comp->FullKeypath, comp->RefCount );
3013 }
3014
3015 static UINT ACTION_ProcessComponents(MSIPACKAGE *package)
3016 {
3017 WCHAR squished_pc[GUID_SIZE];
3018 WCHAR squished_cc[GUID_SIZE];
3019 UINT rc;
3020 MSICOMPONENT *comp;
3021 HKEY hkey;
3022
3023 TRACE("\n");
3024
3025 squash_guid(package->ProductCode,squished_pc);
3026 ui_progress(package,1,COMPONENT_PROGRESS_VALUE,1,0);
3027
3028 msi_set_sourcedir_props(package, FALSE);
3029
3030 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
3031 {
3032 MSIRECORD * uirow;
3033
3034 ui_progress(package,2,0,0,0);
3035 if (!comp->ComponentId)
3036 continue;
3037
3038 squash_guid(comp->ComponentId,squished_cc);
3039
3040 msi_free(comp->FullKeypath);
3041 comp->FullKeypath = resolve_keypath( package, comp );
3042
3043 ACTION_RefCountComponent( package, comp );
3044
3045 TRACE("Component %s (%s), Keypath=%s, RefCount=%i Request=%u\n",
3046 debugstr_w(comp->Component),
3047 debugstr_w(squished_cc),
3048 debugstr_w(comp->FullKeypath),
3049 comp->RefCount,
3050 comp->ActionRequest);
3051
3052 if (comp->ActionRequest == INSTALLSTATE_LOCAL ||
3053 comp->ActionRequest == INSTALLSTATE_SOURCE)
3054 {
3055 if (!comp->FullKeypath)
3056 continue;
3057
3058 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3059 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, szLocalSid,
3060 &hkey, TRUE);
3061 else
3062 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, NULL,
3063 &hkey, TRUE);
3064
3065 if (rc != ERROR_SUCCESS)
3066 continue;
3067
3068 if (comp->Attributes & msidbComponentAttributesPermanent)
3069 {
3070 static const WCHAR szPermKey[] =
3071 { '0','0','0','0','0','0','0','0','0','0','0','0',
3072 '0','0','0','0','0','0','0','0','0','0','0','0',
3073 '0','0','0','0','0','0','0','0',0 };
3074
3075 msi_reg_set_val_str(hkey, szPermKey, comp->FullKeypath);
3076 }
3077
3078 if (comp->ActionRequest == INSTALLSTATE_LOCAL)
3079 msi_reg_set_val_str(hkey, squished_pc, comp->FullKeypath);
3080 else
3081 {
3082 MSIFILE *file;
3083 MSIRECORD *row;
3084 LPWSTR ptr, ptr2;
3085 WCHAR source[MAX_PATH];
3086 WCHAR base[MAX_PATH];
3087 LPWSTR sourcepath;
3088
3089 static const WCHAR fmt[] = {'%','0','2','d','\\',0};
3090 static const WCHAR query[] = {
3091 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
3092 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
3093 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ',
3094 '>','=',' ','%','i',' ','O','R','D','E','R',' ','B','Y',' ',
3095 '`','D','i','s','k','I','d','`',0};
3096
3097 file = get_loaded_file(package, comp->KeyPath);
3098 if (!file)
3099 continue;
3100
3101 row = MSI_QueryGetRecord(package->db, query, file->Sequence);
3102 sprintfW(source, fmt, MSI_RecordGetInteger(row, 1));
3103 ptr2 = strrchrW(source, '\\') + 1;
3104 msiobj_release(&row->hdr);
3105
3106 lstrcpyW(base, package->PackagePath);
3107 ptr = strrchrW(base, '\\');
3108 *(ptr + 1) = '\0';
3109
3110 sourcepath = resolve_file_source(package, file);
3111 ptr = sourcepath + lstrlenW(base);
3112 lstrcpyW(ptr2, ptr);
3113 msi_free(sourcepath);
3114
3115 msi_reg_set_val_str(hkey, squished_pc, source);
3116 }
3117 RegCloseKey(hkey);
3118 }
3119 else if (comp->ActionRequest == INSTALLSTATE_ABSENT)
3120 {
3121 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3122 MSIREG_DeleteUserDataComponentKey(comp->ComponentId, szLocalSid);
3123 else
3124 MSIREG_DeleteUserDataComponentKey(comp->ComponentId, NULL);
3125 }
3126 comp->Action = comp->ActionRequest;
3127
3128 /* UI stuff */
3129 uirow = MSI_CreateRecord(3);
3130 MSI_RecordSetStringW(uirow,1,package->ProductCode);
3131 MSI_RecordSetStringW(uirow,2,comp->ComponentId);
3132 MSI_RecordSetStringW(uirow,3,comp->FullKeypath);
3133 ui_actiondata(package,szProcessComponents,uirow);
3134 msiobj_release( &uirow->hdr );
3135 }
3136
3137 return ERROR_SUCCESS;
3138 }
3139
3140 typedef struct {
3141 CLSID clsid;
3142 LPWSTR source;
3143
3144 LPWSTR path;
3145 ITypeLib *ptLib;
3146 } typelib_struct;
3147
3148 static BOOL CALLBACK Typelib_EnumResNameProc( HMODULE hModule, LPCWSTR lpszType,
3149 LPWSTR lpszName, LONG_PTR lParam)
3150 {
3151 TLIBATTR *attr;
3152 typelib_struct *tl_struct = (typelib_struct*) lParam;
3153 static const WCHAR fmt[] = {'%','s','\\','%','i',0};
3154 int sz;
3155 HRESULT res;
3156
3157 if (!IS_INTRESOURCE(lpszName))
3158 {
3159 ERR("Not Int Resource Name %s\n",debugstr_w(lpszName));
3160 return TRUE;
3161 }
3162
3163 sz = strlenW(tl_struct->source)+4;
3164 sz *= sizeof(WCHAR);
3165
3166 if ((INT_PTR)lpszName == 1)
3167 tl_struct->path = strdupW(tl_struct->source);
3168 else
3169 {
3170 tl_struct->path = msi_alloc(sz);
3171 sprintfW(tl_struct->path,fmt,tl_struct->source, lpszName);
3172 }
3173
3174 TRACE("trying %s\n", debugstr_w(tl_struct->path));
3175 res = LoadTypeLib(tl_struct->path,&tl_struct->ptLib);