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