1 /* 2 * Implementation of the Microsoft Installer (msi.dll) 3 * 4 * Copyright 2004,2005 Aric Stewart for CodeWeavers 5 * Copyright 2011 Hans Leidekker for CodeWeavers 6 * 7 * This library is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU Lesser General Public 9 * License as published by the Free Software Foundation; either 10 * version 2.1 of the License, or (at your option) any later version. 11 * 12 * This library is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 * Lesser General Public License for more details. 16 * 17 * You should have received a copy of the GNU Lesser General Public 18 * License along with this library; if not, write to the Free Software 19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 20 */ 21 22 #include <stdarg.h> 23 #define COBJMACROS 24 #include "windef.h" 25 #include "winbase.h" 26 #include "winreg.h" 27 #include "objbase.h" 28 #include "shlwapi.h" 29 #include "wine/debug.h" 30 #include "msipriv.h" 31 32 WINE_DEFAULT_DEBUG_CHANNEL(msi); 33 34 static BOOL match_language( MSIPACKAGE *package, LANGID langid ) 35 { 36 UINT i; 37 38 if (!package->num_langids || !langid) return TRUE; 39 for (i = 0; i < package->num_langids; i++) 40 { 41 if (package->langids[i] == langid) return TRUE; 42 } 43 return FALSE; 44 } 45 46 struct transform_desc 47 { 48 WCHAR *product_code_from; 49 WCHAR *product_code_to; 50 WCHAR *version_from; 51 WCHAR *version_to; 52 WCHAR *upgrade_code; 53 }; 54 55 static void free_transform_desc( struct transform_desc *desc ) 56 { 57 msi_free( desc->product_code_from ); 58 msi_free( desc->product_code_to ); 59 msi_free( desc->version_from ); 60 msi_free( desc->version_to ); 61 msi_free( desc->upgrade_code ); 62 msi_free( desc ); 63 } 64 65 static struct transform_desc *parse_transform_desc( const WCHAR *str ) 66 { 67 struct transform_desc *ret; 68 const WCHAR *p = str, *q; 69 UINT len; 70 71 if (!(ret = msi_alloc_zero( sizeof(*ret) ))) return NULL; 72 73 q = wcschr( p, '}' ); 74 if (*p != '{' || !q) goto error; 75 76 len = q - p + 1; 77 if (!(ret->product_code_from = msi_alloc( (len + 1) * sizeof(WCHAR) ))) goto error; 78 memcpy( ret->product_code_from, p, len * sizeof(WCHAR) ); 79 ret->product_code_from[len] = 0; 80 81 p = q + 1; 82 if (!(q = wcschr( p, ';' ))) goto error; 83 len = q - p; 84 if (!(ret->version_from = msi_alloc( (len + 1) * sizeof(WCHAR) ))) goto error; 85 memcpy( ret->version_from, p, len * sizeof(WCHAR) ); 86 ret->version_from[len] = 0; 87 88 p = q + 1; 89 q = wcschr( p, '}' ); 90 if (*p != '{' || !q) goto error; 91 92 len = q - p + 1; 93 if (!(ret->product_code_to = msi_alloc( (len + 1) * sizeof(WCHAR) ))) goto error; 94 memcpy( ret->product_code_to, p, len * sizeof(WCHAR) ); 95 ret->product_code_to[len] = 0; 96 97 p = q + 1; 98 if (!(q = wcschr( p, ';' ))) goto error; 99 len = q - p; 100 if (!(ret->version_to = msi_alloc( (len + 1) * sizeof(WCHAR) ))) goto error; 101 memcpy( ret->version_to, p, len * sizeof(WCHAR) ); 102 ret->version_to[len] = 0; 103 104 p = q + 1; 105 q = wcschr( p, '}' ); 106 if (*p != '{' || !q) goto error; 107 108 len = q - p + 1; 109 if (!(ret->upgrade_code = msi_alloc( (len + 1) * sizeof(WCHAR) ))) goto error; 110 memcpy( ret->upgrade_code, p, len * sizeof(WCHAR) ); 111 ret->upgrade_code[len] = 0; 112 113 return ret; 114 115 error: 116 free_transform_desc( ret ); 117 return NULL; 118 } 119 120 static UINT check_transform_applicable( MSIPACKAGE *package, IStorage *transform ) 121 { 122 static const UINT supported_flags = 123 MSITRANSFORM_VALIDATE_PRODUCT | MSITRANSFORM_VALIDATE_LANGUAGE | 124 MSITRANSFORM_VALIDATE_PLATFORM | MSITRANSFORM_VALIDATE_MAJORVERSION | 125 MSITRANSFORM_VALIDATE_MINORVERSION | MSITRANSFORM_VALIDATE_UPGRADECODE; 126 MSISUMMARYINFO *si; 127 UINT r, valid_flags = 0, wanted_flags = 0; 128 WCHAR *template, *product, *p; 129 struct transform_desc *desc; 130 131 r = msi_get_suminfo( transform, 0, &si ); 132 if (r != ERROR_SUCCESS) 133 { 134 WARN("no summary information!\n"); 135 return r; 136 } 137 wanted_flags = msi_suminfo_get_int32( si, PID_CHARCOUNT ); 138 wanted_flags &= 0xffff; /* mask off error condition flags */ 139 TRACE("validation flags 0x%04x\n", wanted_flags); 140 141 /* native is not validating platform */ 142 wanted_flags &= ~MSITRANSFORM_VALIDATE_PLATFORM; 143 144 if (wanted_flags & ~supported_flags) 145 { 146 FIXME("unsupported validation flags 0x%04x\n", wanted_flags); 147 msiobj_release( &si->hdr ); 148 return ERROR_FUNCTION_FAILED; 149 } 150 if (!(template = msi_suminfo_dup_string( si, PID_TEMPLATE ))) 151 { 152 WARN("no template property!\n"); 153 msiobj_release( &si->hdr ); 154 return ERROR_FUNCTION_FAILED; 155 } 156 TRACE("template property: %s\n", debugstr_w(template)); 157 if (!(product = msi_get_suminfo_product( transform ))) 158 { 159 WARN("no product property!\n"); 160 msi_free( template ); 161 msiobj_release( &si->hdr ); 162 return ERROR_FUNCTION_FAILED; 163 } 164 TRACE("product property: %s\n", debugstr_w(product)); 165 if (!(desc = parse_transform_desc( product ))) 166 { 167 msi_free( template ); 168 msiobj_release( &si->hdr ); 169 return ERROR_FUNCTION_FAILED; 170 } 171 msi_free( product ); 172 173 if (wanted_flags & MSITRANSFORM_VALIDATE_LANGUAGE) 174 { 175 if (!template[0] || ((p = wcschr( template, ';' )) && match_language( package, wcstol( p + 1, NULL, 10 ) ))) 176 { 177 valid_flags |= MSITRANSFORM_VALIDATE_LANGUAGE; 178 } 179 } 180 if (wanted_flags & MSITRANSFORM_VALIDATE_PRODUCT) 181 { 182 WCHAR *product_code_installed = msi_dup_property( package->db, L"ProductCode" ); 183 184 if (!product_code_installed) 185 { 186 msi_free( template ); 187 free_transform_desc( desc ); 188 msiobj_release( &si->hdr ); 189 return ERROR_INSTALL_PACKAGE_INVALID; 190 } 191 if (!wcscmp( desc->product_code_from, product_code_installed )) 192 { 193 valid_flags |= MSITRANSFORM_VALIDATE_PRODUCT; 194 } 195 msi_free( product_code_installed ); 196 } 197 msi_free( template ); 198 if (wanted_flags & MSITRANSFORM_VALIDATE_MAJORVERSION) 199 { 200 WCHAR *product_version_installed = msi_dup_property( package->db, L"ProductVersion" ); 201 DWORD major_installed, minor_installed, major, minor; 202 203 if (!product_version_installed) 204 { 205 free_transform_desc( desc ); 206 msiobj_release( &si->hdr ); 207 return ERROR_INSTALL_PACKAGE_INVALID; 208 } 209 msi_parse_version_string( product_version_installed, &major_installed, &minor_installed ); 210 msi_parse_version_string( desc->version_from, &major, &minor ); 211 212 if (major_installed == major) 213 { 214 valid_flags |= MSITRANSFORM_VALIDATE_MAJORVERSION; 215 wanted_flags &= ~MSITRANSFORM_VALIDATE_MINORVERSION; 216 } 217 msi_free( product_version_installed ); 218 } 219 else if (wanted_flags & MSITRANSFORM_VALIDATE_MINORVERSION) 220 { 221 WCHAR *product_version_installed = msi_dup_property( package->db, L"ProductVersion" ); 222 DWORD major_installed, minor_installed, major, minor; 223 224 if (!product_version_installed) 225 { 226 free_transform_desc( desc ); 227 msiobj_release( &si->hdr ); 228 return ERROR_INSTALL_PACKAGE_INVALID; 229 } 230 msi_parse_version_string( product_version_installed, &major_installed, &minor_installed ); 231 msi_parse_version_string( desc->version_from, &major, &minor ); 232 233 if (major_installed == major && minor_installed == minor) 234 valid_flags |= MSITRANSFORM_VALIDATE_MINORVERSION; 235 msi_free( product_version_installed ); 236 } 237 if (wanted_flags & MSITRANSFORM_VALIDATE_UPGRADECODE) 238 { 239 WCHAR *upgrade_code_installed = msi_dup_property( package->db, L"UpgradeCode" ); 240 241 if (!upgrade_code_installed) 242 { 243 free_transform_desc( desc ); 244 msiobj_release( &si->hdr ); 245 return ERROR_INSTALL_PACKAGE_INVALID; 246 } 247 if (!wcscmp( desc->upgrade_code, upgrade_code_installed )) 248 valid_flags |= MSITRANSFORM_VALIDATE_UPGRADECODE; 249 msi_free( upgrade_code_installed ); 250 } 251 252 free_transform_desc( desc ); 253 msiobj_release( &si->hdr ); 254 if ((valid_flags & wanted_flags) != wanted_flags) return ERROR_FUNCTION_FAILED; 255 TRACE("applicable transform\n"); 256 return ERROR_SUCCESS; 257 } 258 259 static UINT apply_substorage_transform( MSIPACKAGE *package, MSIDATABASE *patch_db, LPCWSTR name ) 260 { 261 UINT ret = ERROR_FUNCTION_FAILED; 262 IStorage *stg = NULL; 263 HRESULT r; 264 265 TRACE("%p %s\n", package, debugstr_w(name)); 266 267 if (*name++ != ':') 268 { 269 ERR("expected a colon in %s\n", debugstr_w(name)); 270 return ERROR_FUNCTION_FAILED; 271 } 272 r = IStorage_OpenStorage( patch_db->storage, name, NULL, STGM_SHARE_EXCLUSIVE, NULL, 0, &stg ); 273 if (SUCCEEDED(r)) 274 { 275 ret = check_transform_applicable( package, stg ); 276 if (ret == ERROR_SUCCESS) 277 { 278 msi_table_apply_transform( package->db, stg, MSITRANSFORM_ERROR_VIEWTRANSFORM ); 279 msi_table_apply_transform( package->db, stg, 0 ); 280 } 281 else 282 { 283 TRACE("substorage transform %s wasn't applicable\n", debugstr_w(name)); 284 } 285 IStorage_Release( stg ); 286 } 287 else 288 { 289 ERR("failed to open substorage %s\n", debugstr_w(name)); 290 } 291 return ERROR_SUCCESS; 292 } 293 294 UINT msi_check_patch_applicable( MSIPACKAGE *package, MSISUMMARYINFO *si ) 295 { 296 LPWSTR guid_list, *guids, product_code; 297 UINT i, ret = ERROR_FUNCTION_FAILED; 298 299 product_code = msi_dup_property( package->db, L"ProductCode" ); 300 if (!product_code) 301 { 302 /* FIXME: the property ProductCode should be written into the DB somewhere */ 303 ERR("no product code to check\n"); 304 return ERROR_SUCCESS; 305 } 306 guid_list = msi_suminfo_dup_string( si, PID_TEMPLATE ); 307 guids = msi_split_string( guid_list, ';' ); 308 for (i = 0; guids[i] && ret != ERROR_SUCCESS; i++) 309 { 310 if (!wcscmp( guids[i], product_code )) ret = ERROR_SUCCESS; 311 } 312 msi_free( guids ); 313 msi_free( guid_list ); 314 msi_free( product_code ); 315 return ret; 316 } 317 318 static UINT msi_parse_patch_summary( MSISUMMARYINFO *si, MSIPATCHINFO **patch ) 319 { 320 MSIPATCHINFO *pi; 321 UINT r = ERROR_SUCCESS; 322 WCHAR *p; 323 324 if (!(pi = msi_alloc_zero( sizeof(MSIPATCHINFO) ))) 325 { 326 return ERROR_OUTOFMEMORY; 327 } 328 if (!(pi->patchcode = msi_suminfo_dup_string( si, PID_REVNUMBER ))) 329 { 330 msi_free( pi ); 331 return ERROR_OUTOFMEMORY; 332 } 333 p = pi->patchcode; 334 if (*p != '{') 335 { 336 msi_free( pi->patchcode ); 337 msi_free( pi ); 338 return ERROR_PATCH_PACKAGE_INVALID; 339 } 340 if (!(p = wcschr( p + 1, '}' ))) 341 { 342 msi_free( pi->patchcode ); 343 msi_free( pi ); 344 return ERROR_PATCH_PACKAGE_INVALID; 345 } 346 if (p[1]) 347 { 348 FIXME("patch obsoletes %s\n", debugstr_w(p + 1)); 349 p[1] = 0; 350 } 351 TRACE("patch code %s\n", debugstr_w(pi->patchcode)); 352 if (!(pi->products = msi_suminfo_dup_string( si, PID_TEMPLATE ))) 353 { 354 msi_free( pi->patchcode ); 355 msi_free( pi ); 356 return ERROR_OUTOFMEMORY; 357 } 358 if (!(pi->transforms = msi_suminfo_dup_string( si, PID_LASTAUTHOR ))) 359 { 360 msi_free( pi->patchcode ); 361 msi_free( pi->products ); 362 msi_free( pi ); 363 return ERROR_OUTOFMEMORY; 364 } 365 *patch = pi; 366 return r; 367 } 368 369 static UINT patch_set_media_source_prop( MSIPACKAGE *package ) 370 { 371 MSIQUERY *view; 372 MSIRECORD *rec; 373 const WCHAR *property; 374 WCHAR *patch; 375 UINT r; 376 377 r = MSI_DatabaseOpenViewW( package->db, L"SELECT `Source` FROM `Media` WHERE `Source` IS NOT NULL", &view ); 378 if (r != ERROR_SUCCESS) 379 return r; 380 381 r = MSI_ViewExecute( view, 0 ); 382 if (r != ERROR_SUCCESS) 383 goto done; 384 385 if (MSI_ViewFetch( view, &rec ) == ERROR_SUCCESS) 386 { 387 property = MSI_RecordGetString( rec, 1 ); 388 patch = msi_dup_property( package->db, L"PATCH" ); 389 msi_set_property( package->db, property, patch, -1 ); 390 msi_free( patch ); 391 msiobj_release( &rec->hdr ); 392 } 393 394 done: 395 msiobj_release( &view->hdr ); 396 return r; 397 } 398 399 struct patch_offset 400 { 401 struct list entry; 402 WCHAR *name; 403 UINT sequence; 404 }; 405 406 struct patch_offset_list 407 { 408 struct list files; 409 struct list patches; 410 UINT count, min, max; 411 UINT offset_to_apply; 412 }; 413 414 static struct patch_offset_list *patch_offset_list_create( void ) 415 { 416 struct patch_offset_list *pos = msi_alloc( sizeof(struct patch_offset_list) ); 417 list_init( &pos->files ); 418 list_init( &pos->patches ); 419 pos->count = pos->max = 0; 420 pos->min = 999999; 421 return pos; 422 } 423 424 static void patch_offset_list_free( struct patch_offset_list *pos ) 425 { 426 struct patch_offset *po, *po2; 427 428 LIST_FOR_EACH_ENTRY_SAFE( po, po2, &pos->files, struct patch_offset, entry ) 429 { 430 msi_free( po->name ); 431 msi_free( po ); 432 } 433 LIST_FOR_EACH_ENTRY_SAFE( po, po2, &pos->patches, struct patch_offset, entry ) 434 { 435 msi_free( po->name ); 436 msi_free( po ); 437 } 438 msi_free( pos ); 439 } 440 441 static void patch_offset_get_filepatches( MSIDATABASE *db, UINT last_sequence, struct patch_offset_list *pos ) 442 { 443 MSIQUERY *view; 444 MSIRECORD *rec; 445 UINT r; 446 447 r = MSI_DatabaseOpenViewW( db, L"SELECT * FROM `Patch` WHERE `Sequence` <= ? ORDER BY `Sequence`", &view ); 448 if (r != ERROR_SUCCESS) 449 return; 450 451 rec = MSI_CreateRecord( 1 ); 452 MSI_RecordSetInteger( rec, 1, last_sequence ); 453 454 r = MSI_ViewExecute( view, rec ); 455 msiobj_release( &rec->hdr ); 456 if (r != ERROR_SUCCESS) 457 return; 458 459 while (MSI_ViewFetch( view, &rec ) == ERROR_SUCCESS) 460 { 461 struct patch_offset *po = msi_alloc( sizeof(struct patch_offset) ); 462 463 po->name = msi_dup_record_field( rec, 1 ); 464 po->sequence = MSI_RecordGetInteger( rec, 2 ); 465 pos->min = min( pos->min, po->sequence ); 466 pos->max = max( pos->max, po->sequence ); 467 list_add_tail( &pos->patches, &po->entry ); 468 pos->count++; 469 470 msiobj_release( &rec->hdr ); 471 } 472 msiobj_release( &view->hdr ); 473 } 474 475 static void patch_offset_get_files( MSIDATABASE *db, UINT last_sequence, struct patch_offset_list *pos ) 476 { 477 MSIQUERY *view; 478 MSIRECORD *rec; 479 UINT r; 480 481 r = MSI_DatabaseOpenViewW( db, L"SELECT * FROM `File` WHERE `Sequence` <= ? ORDER BY `Sequence`", &view ); 482 if (r != ERROR_SUCCESS) 483 return; 484 485 rec = MSI_CreateRecord( 1 ); 486 MSI_RecordSetInteger( rec, 1, last_sequence ); 487 488 r = MSI_ViewExecute( view, rec ); 489 msiobj_release( &rec->hdr ); 490 if (r != ERROR_SUCCESS) 491 return; 492 493 while (MSI_ViewFetch( view, &rec ) == ERROR_SUCCESS) 494 { 495 UINT attributes = MSI_RecordGetInteger( rec, 7 ); 496 if (attributes & msidbFileAttributesPatchAdded) 497 { 498 struct patch_offset *po = msi_alloc( sizeof(struct patch_offset) ); 499 500 po->name = msi_dup_record_field( rec, 1 ); 501 po->sequence = MSI_RecordGetInteger( rec, 8 ); 502 pos->min = min( pos->min, po->sequence ); 503 pos->max = max( pos->max, po->sequence ); 504 list_add_tail( &pos->files, &po->entry ); 505 pos->count++; 506 } 507 msiobj_release( &rec->hdr ); 508 } 509 msiobj_release( &view->hdr ); 510 } 511 512 static UINT patch_update_file_sequence( MSIDATABASE *db, const struct patch_offset_list *pos, 513 MSIQUERY *view, MSIRECORD *rec ) 514 { 515 struct patch_offset *po; 516 const WCHAR *file = MSI_RecordGetString( rec, 1 ); 517 UINT r = ERROR_SUCCESS, seq = MSI_RecordGetInteger( rec, 8 ); 518 519 LIST_FOR_EACH_ENTRY( po, &pos->files, struct patch_offset, entry ) 520 { 521 if (!wcsicmp( file, po->name )) 522 { 523 MSI_RecordSetInteger( rec, 8, seq + pos->offset_to_apply ); 524 r = MSI_ViewModify( view, MSIMODIFY_UPDATE, rec ); 525 if (r != ERROR_SUCCESS) 526 ERR("Failed to update offset for file %s (%u)\n", debugstr_w(file), r); 527 break; 528 } 529 } 530 return r; 531 } 532 533 static UINT patch_update_filepatch_sequence( MSIDATABASE *db, const struct patch_offset_list *pos, 534 MSIQUERY *view, MSIRECORD *rec ) 535 { 536 struct patch_offset *po; 537 const WCHAR *file = MSI_RecordGetString( rec, 1 ); 538 UINT r = ERROR_SUCCESS, seq = MSI_RecordGetInteger( rec, 2 ); 539 540 LIST_FOR_EACH_ENTRY( po, &pos->patches, struct patch_offset, entry ) 541 { 542 if (seq == po->sequence && !wcsicmp( file, po->name )) 543 { 544 MSIQUERY *delete_view, *insert_view; 545 MSIRECORD *rec2; 546 547 r = MSI_DatabaseOpenViewW( db, L"DELETE FROM `Patch` WHERE `File_` = ? AND `Sequence` = ?", &delete_view ); 548 if (r != ERROR_SUCCESS) return r; 549 550 rec2 = MSI_CreateRecord( 2 ); 551 MSI_RecordSetStringW( rec2, 1, po->name ); 552 MSI_RecordSetInteger( rec2, 2, po->sequence ); 553 r = MSI_ViewExecute( delete_view, rec2 ); 554 msiobj_release( &delete_view->hdr ); 555 msiobj_release( &rec2->hdr ); 556 if (r != ERROR_SUCCESS) return r; 557 558 r = MSI_DatabaseOpenViewW( db, L"INSERT INTO `Patch` (`File_`,`Sequence`,`PatchSize`,`Attributes`," 559 L"`Header`,`StreamRef_`) VALUES (?,?,?,?,?,?)", &insert_view ); 560 if (r != ERROR_SUCCESS) return r; 561 562 MSI_RecordSetInteger( rec, 2, po->sequence + pos->offset_to_apply ); 563 564 r = MSI_ViewExecute( insert_view, rec ); 565 msiobj_release( &insert_view->hdr ); 566 if (r != ERROR_SUCCESS) 567 ERR("Failed to update offset for filepatch %s (%u)\n", debugstr_w(file), r); 568 break; 569 } 570 } 571 return r; 572 } 573 574 static UINT patch_offset_modify_db( MSIDATABASE *db, struct patch_offset_list *pos ) 575 { 576 MSIRECORD *rec; 577 MSIQUERY *view; 578 UINT r, min = pos->min, max = pos->max, r_fetch; 579 580 r = MSI_DatabaseOpenViewW( db, 581 L"SELECT * FROM `File` WHERE `Sequence` >= ? AND `Sequence` <= ? ORDER BY `Sequence`", 582 &view ); 583 if (r != ERROR_SUCCESS) 584 return ERROR_SUCCESS; 585 586 rec = MSI_CreateRecord( 2 ); 587 MSI_RecordSetInteger( rec, 1, min ); 588 MSI_RecordSetInteger( rec, 2, max ); 589 590 r = MSI_ViewExecute( view, rec ); 591 msiobj_release( &rec->hdr ); 592 if (r != ERROR_SUCCESS) 593 goto done; 594 595 while ((r_fetch = MSI_ViewFetch( view, &rec )) == ERROR_SUCCESS) 596 { 597 r = patch_update_file_sequence( db, pos, view, rec ); 598 msiobj_release( &rec->hdr ); 599 if (r != ERROR_SUCCESS) goto done; 600 } 601 msiobj_release( &view->hdr ); 602 603 r = MSI_DatabaseOpenViewW( db, 604 L"SELECT *FROM `Patch` WHERE `Sequence` >= ? AND `Sequence` <= ? ORDER BY `Sequence`", 605 &view ); 606 if (r != ERROR_SUCCESS) 607 return ERROR_SUCCESS; 608 609 rec = MSI_CreateRecord( 2 ); 610 MSI_RecordSetInteger( rec, 1, min ); 611 MSI_RecordSetInteger( rec, 2, max ); 612 613 r = MSI_ViewExecute( view, rec ); 614 msiobj_release( &rec->hdr ); 615 if (r != ERROR_SUCCESS) 616 goto done; 617 618 while ((r_fetch = MSI_ViewFetch( view, &rec )) == ERROR_SUCCESS) 619 { 620 r = patch_update_filepatch_sequence( db, pos, view, rec ); 621 msiobj_release( &rec->hdr ); 622 if (r != ERROR_SUCCESS) goto done; 623 } 624 625 done: 626 msiobj_release( &view->hdr ); 627 return r; 628 } 629 630 static const WCHAR patch_media_query[] = 631 L"SELECT * FROM `Media` WHERE `Source` IS NOT NULL AND `Cabinet` IS NOT NULL ORDER BY `DiskId`"; 632 633 struct patch_media 634 { 635 struct list entry; 636 UINT disk_id; 637 UINT last_sequence; 638 WCHAR *prompt; 639 WCHAR *cabinet; 640 WCHAR *volume; 641 WCHAR *source; 642 }; 643 644 static UINT patch_add_media( MSIPACKAGE *package, IStorage *storage, MSIPATCHINFO *patch ) 645 { 646 MSIQUERY *view; 647 MSIRECORD *rec; 648 UINT r, disk_id; 649 struct list media_list; 650 struct patch_media *media, *next; 651 652 r = MSI_DatabaseOpenViewW( package->db, patch_media_query, &view ); 653 if (r != ERROR_SUCCESS) return r; 654 655 r = MSI_ViewExecute( view, 0 ); 656 if (r != ERROR_SUCCESS) 657 { 658 msiobj_release( &view->hdr ); 659 TRACE("query failed %u\n", r); 660 return r; 661 } 662 list_init( &media_list ); 663 while (MSI_ViewFetch( view, &rec ) == ERROR_SUCCESS) 664 { 665 disk_id = MSI_RecordGetInteger( rec, 1 ); 666 TRACE("disk_id %u\n", disk_id); 667 if (disk_id >= MSI_INITIAL_MEDIA_TRANSFORM_DISKID) 668 { 669 msiobj_release( &rec->hdr ); 670 continue; 671 } 672 if (!(media = msi_alloc( sizeof( *media )))) 673 { 674 msiobj_release( &rec->hdr ); 675 goto done; 676 } 677 media->disk_id = disk_id; 678 media->last_sequence = MSI_RecordGetInteger( rec, 2 ); 679 media->prompt = msi_dup_record_field( rec, 3 ); 680 media->cabinet = msi_dup_record_field( rec, 4 ); 681 media->volume = msi_dup_record_field( rec, 5 ); 682 media->source = msi_dup_record_field( rec, 6 ); 683 684 list_add_tail( &media_list, &media->entry ); 685 msiobj_release( &rec->hdr ); 686 } 687 LIST_FOR_EACH_ENTRY( media, &media_list, struct patch_media, entry ) 688 { 689 MSIQUERY *delete_view, *insert_view; 690 691 r = MSI_DatabaseOpenViewW( package->db, L"DELETE FROM `Media` WHERE `DiskId`=?", &delete_view ); 692 if (r != ERROR_SUCCESS) goto done; 693 694 rec = MSI_CreateRecord( 1 ); 695 MSI_RecordSetInteger( rec, 1, media->disk_id ); 696 697 r = MSI_ViewExecute( delete_view, rec ); 698 msiobj_release( &delete_view->hdr ); 699 msiobj_release( &rec->hdr ); 700 if (r != ERROR_SUCCESS) goto done; 701 702 r = MSI_DatabaseOpenViewW( package->db, L"INSERT INTO `Media` (`DiskId`,`LastSequence`,`DiskPrompt`," 703 L"`Cabinet`,`VolumeLabel`,`Source`) VALUES (?,?,?,?,?,?)", 704 &insert_view ); 705 if (r != ERROR_SUCCESS) goto done; 706 707 disk_id = package->db->media_transform_disk_id; 708 TRACE("disk id %u\n", disk_id); 709 TRACE("last sequence %u\n", media->last_sequence); 710 TRACE("prompt %s\n", debugstr_w(media->prompt)); 711 TRACE("cabinet %s\n", debugstr_w(media->cabinet)); 712 TRACE("volume %s\n", debugstr_w(media->volume)); 713 TRACE("source %s\n", debugstr_w(media->source)); 714 715 rec = MSI_CreateRecord( 6 ); 716 MSI_RecordSetInteger( rec, 1, disk_id ); 717 MSI_RecordSetInteger( rec, 2, media->last_sequence ); 718 MSI_RecordSetStringW( rec, 3, media->prompt ); 719 MSI_RecordSetStringW( rec, 4, media->cabinet ); 720 MSI_RecordSetStringW( rec, 5, media->volume ); 721 MSI_RecordSetStringW( rec, 6, media->source ); 722 723 r = MSI_ViewExecute( insert_view, rec ); 724 msiobj_release( &insert_view->hdr ); 725 msiobj_release( &rec->hdr ); 726 if (r != ERROR_SUCCESS) goto done; 727 728 r = msi_add_cabinet_stream( package, disk_id, storage, media->cabinet ); 729 if (r != ERROR_SUCCESS) ERR("failed to add cabinet stream %u\n", r); 730 else 731 { 732 patch->disk_id = disk_id; 733 package->db->media_transform_disk_id++; 734 } 735 } 736 737 done: 738 msiobj_release( &view->hdr ); 739 LIST_FOR_EACH_ENTRY_SAFE( media, next, &media_list, struct patch_media, entry ) 740 { 741 list_remove( &media->entry ); 742 msi_free( media->prompt ); 743 msi_free( media->cabinet ); 744 msi_free( media->volume ); 745 msi_free( media->source ); 746 msi_free( media ); 747 } 748 return r; 749 } 750 751 static UINT patch_set_offsets( MSIDATABASE *db, MSIPATCHINFO *patch ) 752 { 753 MSIQUERY *view; 754 MSIRECORD *rec; 755 UINT r; 756 757 r = MSI_DatabaseOpenViewW( db, patch_media_query, &view ); 758 if (r != ERROR_SUCCESS) 759 return r; 760 761 r = MSI_ViewExecute( view, 0 ); 762 if (r != ERROR_SUCCESS) 763 goto done; 764 765 while (MSI_ViewFetch( view, &rec ) == ERROR_SUCCESS) 766 { 767 UINT offset, last_sequence = MSI_RecordGetInteger( rec, 2 ); 768 struct patch_offset_list *pos; 769 770 /* FIXME: set/check Source field instead? */ 771 if (last_sequence >= MSI_INITIAL_MEDIA_TRANSFORM_OFFSET) 772 { 773 msiobj_release( &rec->hdr ); 774 continue; 775 } 776 pos = patch_offset_list_create(); 777 patch_offset_get_files( db, last_sequence, pos ); 778 patch_offset_get_filepatches( db, last_sequence, pos ); 779 780 offset = db->media_transform_offset - pos->min; 781 last_sequence = offset + pos->max; 782 783 last_sequence += pos->min; 784 pos->offset_to_apply = offset; 785 if (pos->count) 786 { 787 r = patch_offset_modify_db( db, pos ); 788 if (r != ERROR_SUCCESS) 789 ERR("Failed to set offsets, expect breakage (%u)\n", r); 790 } 791 MSI_RecordSetInteger( rec, 2, last_sequence ); 792 r = MSI_ViewModify( view, MSIMODIFY_UPDATE, rec ); 793 if (r != ERROR_SUCCESS) 794 ERR("Failed to update Media table entry, expect breakage (%u)\n", r); 795 796 db->media_transform_offset = last_sequence + 1; 797 798 patch_offset_list_free( pos ); 799 msiobj_release( &rec->hdr ); 800 } 801 802 done: 803 msiobj_release( &view->hdr ); 804 return r; 805 } 806 807 static DWORD is_uninstallable( MSIDATABASE *db ) 808 { 809 MSIQUERY *view; 810 MSIRECORD *rec; 811 DWORD ret = 0; 812 813 if (MSI_DatabaseOpenViewW( db, L"SELECT `Value` FROM `MsiPatchMetadata` WHERE `Company` IS NULL " 814 L"AND `Property`='AllowRemoval'", &view ) != ERROR_SUCCESS) return 0; 815 if (MSI_ViewExecute( view, 0 ) != ERROR_SUCCESS) 816 { 817 msiobj_release( &view->hdr ); 818 return 0; 819 } 820 821 if (MSI_ViewFetch( view, &rec ) == ERROR_SUCCESS) 822 { 823 const WCHAR *value = MSI_RecordGetString( rec, 1 ); 824 ret = wcstol( value, NULL, 10 ); 825 msiobj_release( &rec->hdr ); 826 } 827 828 FIXME( "check other criteria\n" ); 829 830 msiobj_release( &view->hdr ); 831 return ret; 832 } 833 834 static UINT msi_apply_patch_db( MSIPACKAGE *package, MSIDATABASE *patch_db, MSIPATCHINFO *patch ) 835 { 836 UINT i, r = ERROR_SUCCESS; 837 WCHAR **substorage; 838 839 /* apply substorage transforms */ 840 substorage = msi_split_string( patch->transforms, ';' ); 841 for (i = 0; substorage && substorage[i] && r == ERROR_SUCCESS; i++) 842 { 843 r = apply_substorage_transform( package, patch_db, substorage[i] ); 844 if (r == ERROR_SUCCESS) 845 { 846 r = patch_set_offsets( package->db, patch ); 847 if (r == ERROR_SUCCESS) 848 r = patch_add_media( package, patch_db->storage, patch ); 849 } 850 } 851 msi_free( substorage ); 852 if (r != ERROR_SUCCESS) 853 return r; 854 855 r = patch_set_media_source_prop( package ); 856 if (r != ERROR_SUCCESS) 857 return r; 858 859 patch->uninstallable = is_uninstallable( patch_db ); 860 patch->state = MSIPATCHSTATE_APPLIED; 861 list_add_tail( &package->patches, &patch->entry ); 862 return ERROR_SUCCESS; 863 } 864 865 void msi_free_patchinfo( MSIPATCHINFO *patch ) 866 { 867 msi_free( patch->patchcode ); 868 msi_free( patch->products ); 869 msi_free( patch->transforms ); 870 msi_free( patch->filename ); 871 msi_free( patch->localfile ); 872 msi_free( patch ); 873 } 874 875 static UINT msi_apply_patch_package( MSIPACKAGE *package, const WCHAR *file ) 876 { 877 MSIDATABASE *patch_db = NULL; 878 WCHAR localfile[MAX_PATH]; 879 MSISUMMARYINFO *si; 880 MSIPATCHINFO *patch = NULL; 881 UINT r; 882 883 TRACE("%p, %s\n", package, debugstr_w(file)); 884 885 r = MSI_OpenDatabaseW( file, MSIDBOPEN_READONLY + MSIDBOPEN_PATCHFILE, &patch_db ); 886 if (r != ERROR_SUCCESS) 887 { 888 ERR("failed to open patch collection %s\n", debugstr_w( file ) ); 889 return r; 890 } 891 r = msi_get_suminfo( patch_db->storage, 0, &si ); 892 if (r != ERROR_SUCCESS) 893 { 894 msiobj_release( &patch_db->hdr ); 895 return r; 896 } 897 r = msi_check_patch_applicable( package, si ); 898 if (r != ERROR_SUCCESS) 899 { 900 TRACE("patch not applicable\n"); 901 r = ERROR_SUCCESS; 902 goto done; 903 } 904 r = msi_parse_patch_summary( si, &patch ); 905 if ( r != ERROR_SUCCESS ) 906 goto done; 907 908 r = msi_create_empty_local_file( localfile, L".msp" ); 909 if ( r != ERROR_SUCCESS ) 910 goto done; 911 912 r = ERROR_OUTOFMEMORY; 913 patch->registered = FALSE; 914 if (!(patch->filename = strdupW( file ))) goto done; 915 if (!(patch->localfile = strdupW( localfile ))) goto done; 916 917 r = msi_apply_patch_db( package, patch_db, patch ); 918 if (r != ERROR_SUCCESS) WARN("patch failed to apply %u\n", r); 919 920 done: 921 msiobj_release( &si->hdr ); 922 msiobj_release( &patch_db->hdr ); 923 if (patch && r != ERROR_SUCCESS) 924 { 925 DeleteFileW( patch->localfile ); 926 msi_free_patchinfo( patch ); 927 } 928 return r; 929 } 930 931 /* get the PATCH property, and apply all the patches it specifies */ 932 UINT msi_apply_patches( MSIPACKAGE *package ) 933 { 934 LPWSTR patch_list, *patches; 935 UINT i, r = ERROR_SUCCESS; 936 937 patch_list = msi_dup_property( package->db, L"PATCH" ); 938 939 TRACE("patches to be applied: %s\n", debugstr_w(patch_list)); 940 941 patches = msi_split_string( patch_list, ';' ); 942 for (i = 0; patches && patches[i] && r == ERROR_SUCCESS; i++) 943 r = msi_apply_patch_package( package, patches[i] ); 944 945 msi_free( patches ); 946 msi_free( patch_list ); 947 return r; 948 } 949 950 UINT msi_apply_transforms( MSIPACKAGE *package ) 951 { 952 LPWSTR xform_list, *xforms; 953 UINT i, r = ERROR_SUCCESS; 954 955 xform_list = msi_dup_property( package->db, L"TRANSFORMS" ); 956 xforms = msi_split_string( xform_list, ';' ); 957 958 for (i = 0; xforms && xforms[i] && r == ERROR_SUCCESS; i++) 959 { 960 if (xforms[i][0] == ':') 961 r = apply_substorage_transform( package, package->db, xforms[i] ); 962 else 963 { 964 WCHAR *transform; 965 966 if (!PathIsRelativeW( xforms[i] )) transform = xforms[i]; 967 else 968 { 969 WCHAR *p = wcsrchr( package->PackagePath, '\\' ); 970 DWORD len = p - package->PackagePath + 1; 971 972 if (!(transform = msi_alloc( (len + lstrlenW( xforms[i] ) + 1) * sizeof(WCHAR)) )) 973 { 974 msi_free( xforms ); 975 msi_free( xform_list ); 976 return ERROR_OUTOFMEMORY; 977 } 978 memcpy( transform, package->PackagePath, len * sizeof(WCHAR) ); 979 memcpy( transform + len, xforms[i], (lstrlenW( xforms[i] ) + 1) * sizeof(WCHAR) ); 980 } 981 r = MSI_DatabaseApplyTransformW( package->db, transform, 0 ); 982 if (transform != xforms[i]) msi_free( transform ); 983 } 984 } 985 msi_free( xforms ); 986 msi_free( xform_list ); 987 return r; 988 } 989 990 UINT msi_apply_registered_patch( MSIPACKAGE *package, LPCWSTR patch_code ) 991 { 992 UINT r; 993 DWORD len; 994 WCHAR patch_file[MAX_PATH]; 995 MSIDATABASE *patch_db; 996 MSIPATCHINFO *patch_info; 997 MSISUMMARYINFO *si; 998 999 TRACE("%p, %s\n", package, debugstr_w(patch_code)); 1000 1001 len = ARRAY_SIZE( patch_file ); 1002 r = MsiGetPatchInfoExW( patch_code, package->ProductCode, NULL, package->Context, 1003 INSTALLPROPERTY_LOCALPACKAGEW, patch_file, &len ); 1004 if (r != ERROR_SUCCESS) 1005 { 1006 ERR("failed to get patch filename %u\n", r); 1007 return r; 1008 } 1009 r = MSI_OpenDatabaseW( patch_file, MSIDBOPEN_READONLY + MSIDBOPEN_PATCHFILE, &patch_db ); 1010 if (r != ERROR_SUCCESS) 1011 { 1012 ERR("failed to open patch database %s\n", debugstr_w( patch_file )); 1013 return r; 1014 } 1015 r = msi_get_suminfo( patch_db->storage, 0, &si ); 1016 if (r != ERROR_SUCCESS) 1017 { 1018 msiobj_release( &patch_db->hdr ); 1019 return r; 1020 } 1021 r = msi_parse_patch_summary( si, &patch_info ); 1022 msiobj_release( &si->hdr ); 1023 if (r != ERROR_SUCCESS) 1024 { 1025 ERR("failed to parse patch summary %u\n", r); 1026 msiobj_release( &patch_db->hdr ); 1027 return r; 1028 } 1029 patch_info->registered = TRUE; 1030 patch_info->localfile = strdupW( patch_file ); 1031 if (!patch_info->localfile) 1032 { 1033 msiobj_release( &patch_db->hdr ); 1034 msi_free_patchinfo( patch_info ); 1035 return ERROR_OUTOFMEMORY; 1036 } 1037 r = msi_apply_patch_db( package, patch_db, patch_info ); 1038 msiobj_release( &patch_db->hdr ); 1039 if (r != ERROR_SUCCESS) 1040 { 1041 ERR("failed to apply patch %u\n", r); 1042 msi_free_patchinfo( patch_info ); 1043 } 1044 return r; 1045 } 1046