1#************************************************************** 2# 3# Licensed to the Apache Software Foundation (ASF) under one 4# or more contributor license agreements. See the NOTICE file 5# distributed with this work for additional information 6# regarding copyright ownership. The ASF licenses this file 7# to you under the Apache License, Version 2.0 (the 8# "License"); you may not use this file except in compliance 9# with the License. You may obtain a copy of the License at 10# 11# http://www.apache.org/licenses/LICENSE-2.0 12# 13# Unless required by applicable law or agreed to in writing, 14# software distributed under the License is distributed on an 15# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16# KIND, either express or implied. See the License for the 17# specific language governing permissions and limitations 18# under the License. 19# 20#************************************************************** 21 22package installer::patch::InstallationSet; 23 24use installer::patch::Tools; 25use installer::patch::Version; 26use installer::logger; 27 28use strict; 29 30# Call Get7Zip() to get access to the filename of the 7z executable. 31my $SevenZip = undef; 32 33 34=head1 NAME 35 36 package installer::patch::InstallationSet - Functions for handling installation sets 37 38=head1 DESCRIPTION 39 40 This package contains functions for unpacking the .exe files that 41 are created by the NSIS installer creator and the .cab files in 42 the installation sets. 43 44=cut 45 46 47 48 49=head2 Detect7ZipOnWindows () 50 51 7Zip seems to be the only program able to unpack an NSIS installer. 52 Search for it. 53 54=cut 55 56sub Detect7ZipOnWindows () 57{ 58 # Use 'reg query' to read registry entry from Windows registry. 59 my $registry_key = "HKEY_CURRENT_USER\\\\Software\\\\7-Zip"; 60 my $registry_value_name = "Path"; 61 my $command = sprintf("reg query %s /v %s", $registry_key, $registry_value_name); 62 my $response = qx($command); 63 64 # Process the response. 65 my $path_to_7zip = undef; 66 if ($response =~ /\s+REG_SZ\s+([^\r\n]*)/m) 67 { 68 $path_to_7zip = $1; 69 } 70 71 # If that failed, then make an educated guess. 72 if ( ! defined $path_to_7zip) 73 { 74 $path_to_7zip = "c:\\Program Files\\7-Zip\\"; 75 } 76 77 # Check if the executable exists and is, well, executable. 78 return undef unless -d $path_to_7zip; 79 my $fullname = File::Spec->catfile($path_to_7zip, "7z.exe"); 80 return undef unless -f $fullname; 81 return undef unless -x $fullname; 82 83 return $fullname; 84} 85 86 87 88 89sub Get7Zip () 90{ 91 if ( ! defined $SevenZip) 92 { 93 if ($ENV{'OS'} eq "WNT") 94 { 95 $SevenZip = Detect7ZipOnWindows(); 96 } 97 if ( ! defined $SevenZip) 98 { 99 # Use an empty string to avoid repeated (and failing) detections of a missing 7z. 100 $SevenZip = ""; 101 } 102 } 103 104 return $SevenZip eq "" ? undef : $SevenZip; 105} 106 107 108 109 110sub UnpackExe ($$) 111{ 112 my ($filename, $destination_path) = @_; 113 114 $installer::logger::Info->printf("unpacking installation set to '%s'\n", $destination_path); 115 116 # Unpack to a temporary path and change its name to the destination path 117 # only when the unpacking has completed successfully. 118 File::Path::make_path($destination_path); 119 120 my $windows_filename = installer::patch::Tools::ToEscapedWindowsPath($filename); 121 my $windows_destination_path = installer::patch::Tools::ToEscapedWindowsPath($destination_path); 122 my $command = join(" ", 123 "\"".Get7Zip()."\"", 124 "x", 125 "-y", 126 "-o".$windows_destination_path, 127 $windows_filename); 128 my $result = qx($command); 129 if ( ! $result) 130 { 131 installer::exiter::exit_program( 132 "ERROR: can not unpack downloadable installation set: ".$!, 133 "installer::patch::InstallationSet::UnpackExe"); 134 } 135 136 # Check the existence of the .cab files. 137 my $cab_filename = File::Spec->catfile($destination_path, "openoffice1.cab"); 138 if ( ! -f $cab_filename) 139 { 140 installer::logger::PrintError("cab file '%s' was not extracted from installation set\n", $cab_filename); 141 return 0; 142 } 143 return 1; 144} 145 146 147 148 149=head2 UnpackCab($cab_filename, $destination_path) 150 151 Unpacking the cabinet file inside an .exe installation set is a 152 three step process because there is no directory information stored 153 inside the cab file. This has to be taken from the 'File' and 154 'Directory' tables in the .msi file. 155 156 1. Setup the directory structure of all files in the cab from the 'File' and 'Directory' tables in the msi. 157 158 2. Unpack the cab file. 159 160 3. Move the files to their destination directories. 161 162=cut 163sub UnpackCab ($$$) 164{ 165 my ($cab_filename, $msi, $destination_path) = @_; 166 167 # Step 1 168 # Extract the directory structure from the 'File' and 'Directory' tables in the given msi. 169 $installer::logger::Info->printf("setting up directory tree\n"); 170 my $file_table = $msi->GetTable("File"); 171 my $file_map = $msi->GetFileMap(); 172 173 # Step 2 174 # Unpack the .cab file to a temporary path. 175 my $temporary_destination_path = $destination_path . ".tmp"; 176 if ( -d $temporary_destination_path) 177 { 178 # Temporary directory already exists => cab file has already been unpacked (flat), nothing to do. 179 printf("%s exists\n", $temporary_destination_path); 180 $installer::logger::Info->printf("cab file has already been unpacked to flat structure\n"); 181 } 182 else 183 { 184 UnpackCabFlat($cab_filename, $temporary_destination_path, $file_table); 185 } 186 187 # Step 3 188 # Move the files to their destinations. 189 File::Path::make_path($destination_path); 190 $installer::logger::Info->printf("moving files to their directories\n"); 191 my $directory_map = $msi->GetDirectoryMap(); 192 my $office_menu_folder_name = $directory_map->{'INSTALLLOCATION'}->{'target_long_name'}; 193 my $count = 0; 194 foreach my $file_row (@{$file_table->GetAllRows()}) 195 { 196 my $unique_name = $file_row->GetValue('File'); 197 my $file_item = $file_map->{$unique_name}; 198 my $directory_item = $file_item->{'directory'}; 199 my $long_file_name = $file_item->{'long_name'}; 200 my $full_name = $directory_item->{'full_source_long_name'}; 201 # Strip away the leading OfficeMenuFolder part. 202 $full_name =~ s/^$office_menu_folder_name\///; 203 my $flat_filename = File::Spec->catfile($temporary_destination_path, $unique_name); 204 my $dir_path = File::Spec->catfile($destination_path, $full_name); 205 my $dir_filename = File::Spec->catfile($dir_path, $long_file_name); 206 207 if ( ! -d $dir_path) 208 { 209 File::Path::make_path($dir_path); 210 } 211 212 $installer::logger::Lang->printf("moving %s to %s\n", $flat_filename, $dir_filename); 213 File::Copy::move($flat_filename, $dir_filename) 214 || die("can not move file ".$flat_filename.":".$!); 215 216 ++$count; 217 } 218 219 # Cleanup. Remove the temporary directory. It should be empty by now. 220 rmdir($temporary_destination_path); 221} 222 223 224 225 226=head2 UnpackCabFlat ($cab_filename, $destination_path, $file_table) 227 228 Unpack the flat file structure of the $cab_filename to $destination_path. 229 230 In order to detect and handle an incomplete (aborted) previous 231 extraction, the cab file is unpacked to a temporary directory 232 that, after successful extraction, is renamed to $destination_path. 233 234=cut 235sub UnpackCabFlat ($$$) 236{ 237 my ($cab_filename, $destination_path, $file_table) = @_; 238 239 # Unpack the .cab file to a temporary path (note that 240 # $destination_path may already be a temporary path). Using a 241 # second one prevents the lengthy flat unpacking to be repeated 242 # when another step fails. 243 244 $installer::logger::Info->printf("unpacking cab file\n"); 245 File::Path::make_path($destination_path); 246 my $windows_cab_filename = installer::patch::Tools::ToEscapedWindowsPath($cab_filename); 247 my $windows_destination_path = installer::patch::Tools::ToEscapedWindowsPath($destination_path); 248 my $command = join(" ", 249 "\"".Get7Zip()."\"", 250 "x", "-o".$windows_destination_path, 251 $windows_cab_filename, 252 "-y"); 253 open my $cmd, $command."|"; 254 my $extraction_count = 0; 255 my $file_count = $file_table->GetRowCount(); 256 while (<$cmd>) 257 { 258 my $message = $_; 259 chomp($message); 260 ++$extraction_count; 261 printf("%4d/%4d %3.2f%% \r", 262 $extraction_count, 263 $file_count, 264 $extraction_count*100/$file_count); 265 } 266 close $cmd; 267} 268 269 270 271 272=head GetUnpackedExePath ($version, $is_current_version, $language, $package_format, $product) 273 274 Convenience function that returns where a downloadable installation set is extracted to. 275 276=cut 277sub GetUnpackedExePath ($$$$$) 278{ 279 my ($version, $is_current_version, $language, $package_format, $product) = @_; 280 281 my $path = GetUnpackedPath($version, $is_current_version, $language, $package_format, $product); 282 return File::Spec->catfile($path, "unpacked"); 283} 284 285 286 287 288=head GetUnpackedCabPath ($version, $is_current_version, $language, $package_format, $product) 289 290 Convenience function that returns where a cab file is extracted 291 (with injected directory structure from the msi file) to. 292 293=cut 294sub GetUnpackedCabPath ($$$$$) 295{ 296 my ($version, $is_current_version, $language, $package_format, $product) = @_; 297 298 my $path = GetUnpackedPath($version, $is_current_version, $language, $package_format, $product); 299 return File::Spec->catfile($path, "unpacked"); 300} 301 302 303 304 305=head2 GetUnpackedPath($version, $is_current_version, $language, $package_format, $product) 306 307 Internal function for creating paths to where archives are unpacked. 308 309=cut 310sub GetUnpackedPath ($$$$$) 311{ 312 my ($version, $is_current_version, $language, $package_format, $product) = @_; 313 314 return File::Spec->catfile( 315 $ENV{'SRC_ROOT'}, 316 "instsetoo_native", 317 $ENV{'INPATH'}, 318 $product, 319 $package_format, 320 installer::patch::Version::ArrayToDirectoryName( 321 installer::patch::Version::StringToNumberArray($version)), 322 installer::languages::get_normalized_language($language)); 323} 324 325 326 327 328sub GetMsiFilename ($$) 329{ 330 my ($path, $version) = @_; 331 332 my $no_dot_version = installer::patch::Version::ArrayToNoDotName( 333 installer::patch::Version::StringToNumberArray( 334 $version)); 335 return File::Spec->catfile( 336 $path, 337 "openoffice" . $no_dot_version . ".msi"); 338} 339 340 341 342 343sub GetCabFilename ($$) 344{ 345 my ($path, $version) = @_; 346 347 return File::Spec->catfile( 348 $path, 349 "openoffice1.cab"); 350} 351 352 353 354 355=head2 Download($language, $release_data, $filename) 356 357 Download an installation set to $filename. The URL for the 358 download is taken from $release_data, a snippet from the 359 instsetoo_native/data/releases.xml file. 360 361=cut 362sub Download ($$$) 363{ 364 my ($language, $release_data, $filename) = @_; 365 366 my $url = $release_data->{'URL'}; 367 $release_data->{'URL'} =~ /^(.*)\/([^\/]+)$/; 368 my ($location, $basename) = ($1,$2); 369 370 $installer::logger::Info->printf("downloading %s\n", $basename); 371 $installer::logger::Info->printf(" from '%s'\n", $location); 372 my $filesize = $release_data->{'file-size'}; 373 if (defined $filesize) 374 { 375 $installer::logger::Info->printf(" expected size is %d\n", $filesize); 376 } 377 else 378 { 379 $installer::logger::Info->printf(" file size is not yet known\n"); 380 } 381 my $temporary_filename = $filename . ".part"; 382 my $resume_size = 0; 383 384 # Prepare checksum. 385 my $checksum = undef; 386 my $checksum_type = $release_data->{'checksum-type'}; 387 my $checksum_value = $release_data->{'checksum-value'}; 388 my $digest = undef; 389 if ( ! defined $checksum_value) 390 { 391 # No checksum available. Skip test. 392 } 393 elsif ($checksum_type eq "sha256") 394 { 395 $digest = Digest->new("SHA-256"); 396 } 397 elsif ($checksum_type eq "md5") 398 { 399 $digest = Digest->new("md5"); 400 } 401 else 402 { 403 installer::logger::PrintError( 404 "checksum type %s is not supported. Supported checksum types are: sha256,md5\n", 405 $checksum_type); 406 return 0; 407 } 408 409 # Download the extension. 410 open my $out, ">$temporary_filename"; 411 binmode($out); 412 413 my $mode = $|; 414 my $handle = select STDOUT; 415 $| = 1; 416 select $handle; 417 418 my $agent = LWP::UserAgent->new(); 419 $agent->timeout(120); 420 $agent->show_progress(0); 421 my $last_was_redirect = 0; 422 my $bytes_read = 0; 423 $agent->add_handler('response_redirect' 424 => sub{ 425 $last_was_redirect = 1; 426 return; 427 }); 428 $agent->add_handler('response_data' 429 => sub{ 430 if ($last_was_redirect) 431 { 432 $last_was_redirect = 0; 433 # Throw away the data we got so far. 434 $digest->reset() if defined $digest; 435 close $out; 436 open $out, ">$temporary_filename"; 437 binmode($out); 438 } 439 my($response,$agent,$h,$data)=@_; 440 print $out $data; 441 $digest->add($data) if defined $digest; 442 $bytes_read += length($data); 443 if (defined $filesize) 444 { 445 printf("read %*d / %d %d%% \r", 446 length($filesize), 447 $bytes_read, 448 $filesize, 449 $bytes_read*100/$filesize); 450 } 451 else 452 { 453 printf("read %6.2f MB\r", $bytes_read/(1024.0*1024.0)); 454 } 455 }); 456 my $response; 457 if ($resume_size > 0) 458 { 459 $response = $agent->get($url, 'Range' => "bytes=$resume_size-"); 460 } 461 else 462 { 463 $response = $agent->get($url); 464 } 465 close $out; 466 467 $handle = select STDOUT; 468 $| = $mode; 469 select $handle; 470 471 $installer::logger::Info->print(" \r"); 472 473 if ($response->is_success()) 474 { 475 if ( ! defined $digest 476 || $digest->hexdigest() eq $checksum_value) 477 { 478 $installer::logger::Info->print("download was successful\n"); 479 if ( ! rename($temporary_filename, $filename)) 480 { 481 installer::logger::PrintError("can not rename '%s' to '%s'\n", $temporary_filename, $filename); 482 return 0; 483 } 484 else 485 { 486 return 1; 487 } 488 } 489 else 490 { 491 installer::logger::PrintError("%s checksum is wrong\n", $checksum_type); 492 return 0; 493 } 494 } 495 else 496 { 497 installer::logger::PrintError("there was a download error\n"); 498 return 0; 499 } 500} 501 502 503 504 505=head2 ProvideDownloadSet ($version, $language, $package_format) 506 507 Download an installation set when it is not yet present to 508 $ENV{'TARFILE_LOCATION'}. Verify the downloaded file with the 509 checksum that is extracted from the 510 instsetoo_native/data/releases.xml file. 511 512=cut 513sub ProvideDownloadSet ($$$) 514{ 515 my ($version, $language, $package_format) = @_; 516 517 my $release_item = installer::patch::ReleasesList::Instance()->{$version}->{$package_format}->{$language}; 518 return undef unless defined $release_item; 519 520 # Get basename of installation set from URL. 521 $release_item->{'URL'} =~ /^(.*)\/([^\/]+)$/; 522 my ($location, $basename) = ($1,$2); 523 524 # Is the installation set already present in ext_sources/ ? 525 my $need_download = 0; 526 my $ext_sources_filename = File::Spec->catfile( 527 $ENV{'TARFILE_LOCATION'}, 528 $basename); 529 if ( ! -f $ext_sources_filename) 530 { 531 $installer::logger::Info->printf("download set is not in ext_sources/ (%s)\n", $ext_sources_filename); 532 $need_download = 1; 533 } 534 else 535 { 536 $installer::logger::Info->printf("download set exists at '%s'\n", $ext_sources_filename); 537 if (defined $release_item->{'checksum-value'} 538 && $release_item->{'checksum-type'} eq 'sha256') 539 { 540 $installer::logger::Info->printf("checking SHA256 checksum\n"); 541 my $digest = Digest->new("SHA-256"); 542 open my $in, "<", $ext_sources_filename; 543 $digest->addfile($in); 544 close $in; 545 if ($digest->hexdigest() ne $release_item->{'checksum-value'}) 546 { 547 $installer::logger::Info->printf(" mismatch\n", $ext_sources_filename); 548 $need_download = 1; 549 } 550 else 551 { 552 $installer::logger::Info->printf(" match\n"); 553 } 554 } 555 } 556 557 if ($need_download) 558 { 559 if ( ! installer::patch::InstallationSet::Download( 560 $language, 561 $release_item, 562 $ext_sources_filename)) 563 { 564 return 0; 565 } 566 if ( ! -f $ext_sources_filename) 567 { 568 $installer::logger::Info->printf("download set could not be downloaded\n"); 569 return 0; 570 } 571 } 572 573 return $ext_sources_filename; 574} 575 576 577 578 579sub ProvideUnpackedExe ($$$$$) 580{ 581 my ($version, $is_current_version, $language, $package_format, $product_name) = @_; 582 583 # Check if the exe has already been unpacked. 584 my $unpacked_exe_path = installer::patch::InstallationSet::GetUnpackedExePath( 585 $version, 586 $is_current_version, 587 $language, 588 $package_format, 589 $product_name); 590 my $unpacked_exe_flag_filename = File::Spec->catfile($unpacked_exe_path, "__exe_is_unpacked"); 591 my $exe_is_unpacked = -f $unpacked_exe_flag_filename; 592 593 if ($exe_is_unpacked) 594 { 595 # Yes, exe has already been unpacked. There is nothing more to do. 596 $installer::logger::Info->printf("downloadable installation set has already been unpacked to\n"); 597 $installer::logger::Info->printf(" %s\n", $unpacked_exe_path); 598 return 1; 599 } 600 elsif ($is_current_version) 601 { 602 # For the current version the exe is created from the unpacked 603 # content and both are expected to be already present. 604 605 # In order to have the .cab and its unpacked content in one 606 # directory and don't interfere with the creation of regular 607 # installation sets, we copy the unpacked .exe into a separate 608 # directory. 609 610 my $original_path = File::Spec->catfile( 611 $ENV{'SRC_ROOT'}, 612 "instsetoo_native", 613 $ENV{'INPATH'}, 614 $product_name, 615 $package_format, 616 "install", 617 $language); 618 $installer::logger::Info->printf("creating a copy\n"); 619 $installer::logger::Info->printf(" of %s\n", $original_path); 620 $installer::logger::Info->printf(" at %s\n", $unpacked_exe_path); 621 File::Path::make_path($unpacked_exe_path) unless -d $unpacked_exe_path; 622 my ($file_count,$directory_count) = CopyRecursive($original_path, $unpacked_exe_path); 623 return 0 if ( ! defined $file_count); 624 $installer::logger::Info->printf(" copied %d files in %d directories\n", 625 $file_count, 626 $directory_count); 627 628 installer::patch::Tools::touch($unpacked_exe_flag_filename); 629 630 return 1; 631 } 632 else 633 { 634 # No, we have to unpack the exe. 635 636 # Provide the exe. 637 my $filename = installer::patch::InstallationSet::ProvideDownloadSet( 638 $version, 639 $language, 640 $package_format); 641 642 # Unpack it. 643 if (defined $filename) 644 { 645 if (installer::patch::InstallationSet::UnpackExe($filename, $unpacked_exe_path)) 646 { 647 $installer::logger::Info->printf("downloadable installation set has been unpacked to\n"); 648 $installer::logger::Info->printf(" %s\n", $unpacked_exe_path); 649 650 installer::patch::Tools::touch($unpacked_exe_flag_filename); 651 652 return 1; 653 } 654 } 655 else 656 { 657 installer::logger::PrintError("could not provide .exe installation set at '%s'\n", $filename); 658 } 659 } 660 661 return 0; 662} 663 664 665 666 667sub CopyRecursive ($$) 668{ 669 my ($source_path, $destination_path) = @_; 670 671 return (undef,undef) unless -d $source_path; 672 673 my @todo = ([$source_path, $destination_path]); 674 my $file_count = 0; 675 my $directory_count = 0; 676 while (scalar @todo > 0) 677 { 678 my ($source,$destination) = @{shift @todo}; 679 680 next if ! -d $source; 681 File::Path::make_path($destination); 682 ++$directory_count; 683 684 # Read list of files in the current source directory. 685 opendir( my $dir, $source); 686 my @files = readdir $dir; 687 closedir $dir; 688 689 # Copy all files and push all directories to @todo. 690 foreach my $file (@files) 691 { 692 next if $file =~ /^\.+$/; 693 694 my $source_file = File::Spec->catfile($source, $file); 695 my $destination_file = File::Spec->catfile($destination, $file); 696 if ( -f $source_file) 697 { 698 File::Copy::copy($source_file, $destination_file); 699 ++$file_count; 700 } 701 elsif ( -d $source_file) 702 { 703 push @todo, [$source_file, $destination_file]; 704 } 705 } 706 } 707 708 return ($file_count, $directory_count); 709} 710 711 712 713 714sub CheckLocalCopy ($$$$) 715{ 716 my ($version, $language, $package_format, $product_name) = @_; 717 718 # Compare creation times of the original .msi and its copy. 719 720 my $original_path = File::Spec->catfile( 721 $ENV{'SRC_ROOT'}, 722 "instsetoo_native", 723 $ENV{'INPATH'}, 724 $product_name, 725 $package_format, 726 "install", 727 $language); 728 729 my $copy_path = installer::patch::InstallationSet::GetUnpackedExePath( 730 $version, 731 1, 732 $language, 733 $package_format, 734 $product_name); 735 736 my $msi_basename = "openoffice" 737 . installer::patch::Version::ArrayToNoDotName( 738 installer::patch::Version::StringToNumberArray($version)) 739 . ".msi"; 740 741 my $original_msi_filename = File::Spec->catfile($original_path, $msi_basename); 742 my $copied_msi_filename = File::Spec->catfile($copy_path, $msi_basename); 743 744 my @original_msi_stats = stat($original_msi_filename); 745 my @copied_msi_stats = stat($copied_msi_filename); 746 my $original_msi_mtime = $original_msi_stats[9]; 747 my $copied_msi_mtime = $copied_msi_stats[9]; 748 749 if (defined $original_msi_mtime 750 && defined $copied_msi_mtime 751 && $original_msi_mtime > $copied_msi_mtime) 752 { 753 # The installation set is newer than its copy. 754 # Remove the copy. 755 $installer::logger::Info->printf( 756 "removing copy of installation set (version %s) because it is out of date\n", 757 $version); 758 File::Path::remove_tree($copy_path); 759 } 760} 761 762 763 764 765=head2 ProvideUnpackedCab 766 767 1a. Make sure that a downloadable installation set is present. 768 1b. or that a freshly built installation set (packed and unpacked is present) 769 2. Unpack the downloadable installation set 770 3. Unpack the .cab file. 771 772 The 'Provide' in the function name means that any step that has 773 already been made is not made again. 774 775=cut 776sub ProvideUnpackedCab ($$$$$) 777{ 778 my ($version, $is_current_version, $language, $package_format, $product_name) = @_; 779 780 if ($is_current_version) 781 { 782 # For creating patches we maintain a copy of the unpacked .exe. Make sure that that is updated when 783 # a new installation set has been built. 784 CheckLocalCopy($version, $language, $package_format, $product_name); 785 } 786 787 # Check if the cab file has already been unpacked. 788 my $unpacked_cab_path = installer::patch::InstallationSet::GetUnpackedCabPath( 789 $version, 790 $is_current_version, 791 $language, 792 $package_format, 793 $product_name); 794 my $unpacked_cab_flag_filename = File::Spec->catfile($unpacked_cab_path, "__cab_is_unpacked"); 795 my $cab_is_unpacked = -f $unpacked_cab_flag_filename; 796 797 if ($cab_is_unpacked) 798 { 799 # Yes. Cab was already unpacked. There is nothing more to do. 800 $installer::logger::Info->printf("cab has already been unpacked to\n"); 801 $installer::logger::Info->printf(" %s\n", $unpacked_cab_path); 802 803 return 1; 804 } 805 else 806 { 807 # Make sure that the exe is unpacked and the cab file exists. 808 ProvideUnpackedExe($version, $is_current_version, $language, $package_format, $product_name); 809 810 # Unpack the cab file. 811 my $unpacked_exe_path = installer::patch::InstallationSet::GetUnpackedExePath( 812 $version, 813 $is_current_version, 814 $language, 815 $package_format, 816 $product_name); 817 my $msi = new installer::patch::Msi( 818 installer::patch::InstallationSet::GetMsiFilename($unpacked_exe_path, $version), 819 $version, 820 $is_current_version, 821 $language, 822 $product_name); 823 824 my $cab_filename = installer::patch::InstallationSet::GetCabFilename( 825 $unpacked_exe_path, 826 $version); 827 if ( ! -f $cab_filename) 828 { 829 # Cab file does not exist. 830 installer::logger::PrintError( 831 "could not find .cab file at '%s'. Extraction of .exe seems to have failed.\n", 832 $cab_filename); 833 return 0; 834 } 835 836 if (installer::patch::InstallationSet::UnpackCab( 837 $cab_filename, 838 $msi, 839 $unpacked_cab_path)) 840 { 841 $installer::logger::Info->printf("unpacked cab file '%s'\n", $cab_filename); 842 $installer::logger::Info->printf(" to '%s'\n", $unpacked_cab_path); 843 844 installer::patch::Tools::touch($unpacked_cab_flag_filename); 845 846 return 1; 847 } 848 else 849 { 850 return 0; 851 } 852 } 853} 8541; 855