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::windows::msiglobal; 23 24use Cwd; 25use Digest::MD5; 26use installer::converter; 27use installer::exiter; 28use installer::files; 29use installer::globals; 30use installer::logger; 31use installer::pathanalyzer; 32use installer::remover; 33use installer::scriptitems; 34use installer::systemactions; 35use installer::worker; 36use installer::windows::idtglobal; 37use installer::windows::language; 38use installer::patch::ReleasesList; 39 40use strict; 41 42########################################################################### 43# Generating the header of the ddf file. 44# The usage of ddf files is needed, because makecab.exe can only include 45# one sourcefile into a cab file 46########################################################################### 47 48sub write_ddf_file_header 49{ 50 my ($ddffileref, $cabinetfile, $installdir) = @_; 51 52 my $oneline; 53 54 $oneline = ".Set CabinetName1=" . $cabinetfile . "\n"; 55 push(@{$ddffileref} ,$oneline); 56 $oneline = ".Set ReservePerCabinetSize=128\n"; # This reserves space for a digital signature. 57 push(@{$ddffileref} ,$oneline); 58 $oneline = ".Set MaxDiskSize=2147483648\n"; # This allows the .cab file to get a size of 2 GB. 59 push(@{$ddffileref} ,$oneline); 60 $oneline = ".Set CompressionType=LZX\n"; 61 push(@{$ddffileref} ,$oneline); 62 $oneline = ".Set Compress=ON\n"; 63 push(@{$ddffileref} ,$oneline); 64 $oneline = ".Set CompressionLevel=$installer::globals::cabfilecompressionlevel\n"; 65 push(@{$ddffileref} ,$oneline); 66 $oneline = ".Set Cabinet=ON\n"; 67 push(@{$ddffileref} ,$oneline); 68 $oneline = ".Set DiskDirectoryTemplate=" . $installdir . "\n"; 69 push(@{$ddffileref} ,$oneline); 70} 71 72########################################################################## 73# Lines in ddf files must not contain more than 256 characters 74########################################################################## 75 76sub check_ddf_file 77{ 78 my ( $ddffile, $ddffilename ) = @_; 79 80 my $maxlength = 0; 81 my $maxline = 0; 82 my $linelength = 0; 83 my $linenumber = 0; 84 85 for ( my $i = 0; $i <= $#{$ddffile}; $i++ ) 86 { 87 my $oneline = ${$ddffile}[$i]; 88 89 $linelength = length($oneline); 90 $linenumber = $i + 1; 91 92 if ( $linelength > 256 ) 93 { 94 installer::exiter::exit_program("ERROR \"$ddffilename\" line $linenumber: Lines in ddf files must not contain more than 256 characters!", "check_ddf_file"); 95 } 96 97 if ( $linelength > $maxlength ) 98 { 99 $maxlength = $linelength; 100 $maxline = $linenumber; 101 } 102 } 103 104 my $infoline = "Check of ddf file \"$ddffilename\": Maximum length \"$maxlength\" in line \"$maxline\" (allowed line length: 256 characters)\n"; 105 $installer::logger::Lang->print($infoline); 106} 107 108########################################################################## 109# Lines in ddf files must not be longer than 256 characters. 110# Therefore it can be useful to use relative paths. Then it is 111# necessary to change into temp directory before calling 112# makecab.exe. 113########################################################################## 114 115sub make_relative_ddf_path 116{ 117 my ( $sourcepath ) = @_; 118 119 my $windowstemppath = $installer::globals::temppath; 120 121 if ( $^O =~ /cygwin/i ) 122 { 123 $windowstemppath = $installer::globals::cyg_temppath; 124 } 125 126 $sourcepath =~ s/\Q$windowstemppath\E//; 127 $sourcepath =~ s/^\\//; 128 129 return $sourcepath; 130} 131 132 133########################################################################## 134# Generating the list, in which the source of the files is connected 135# with the cabinet destination file. Because more than one file needs 136# to be included into a cab file, this has to be done via ddf files. 137########################################################################## 138 139sub generate_cab_file_list ($$$$) 140{ 141 my ($filesref, $installdir, $ddfdir, $allvariables) = @_; 142 143 installer::logger::include_header_into_logfile("Generating ddf files"); 144 145 if ( $^O =~ /cygwin/i ) 146 { 147 installer::worker::generate_cygwin_pathes($filesref); 148 } 149 150 # Make sure that all files point to the same cabinet file. 151 # Multiple cabinet files are not supported anymore. 152 my $cabinetfile = $filesref->[0]->{'cabinet'}; 153 foreach my $onefile (@$filesref) 154 { 155 if ($onefile->{'cabinet'} ne $cabinetfile) 156 { 157 installer::exiter::exit_program( 158 "ERROR: multiple cabinet files are not supported", 159 "generate_cab_file_list"); 160 } 161 } 162 163 # Sort files on the sequence number. 164 my @sorted_files = sort {$a->{'sequencenumber'} <=> $b->{'sequencenumber'}} @$filesref; 165 166 my @ddffile = (); 167 write_ddf_file_header(\@ddffile, $cabinetfile, $installdir); 168 foreach my $onefile (@sorted_files) 169 { 170 my $styles = $onefile->{'Styles'}; 171 $styles = "" unless defined $styles; 172 if ($styles =~ /\bDONT_PACK\b/) 173 { 174 $installer::logger::Lang->printf(" excluding '%s' from ddf\n", $onefile->{'uniquename'}); 175 } 176 177 my $uniquename = $onefile->{'uniquename'}; 178 my $sourcepath = $onefile->{'sourcepath'}; 179 if ( $^O =~ /cygwin/i ) 180 { 181 $sourcepath = $onefile->{'cyg_sourcepath'}; 182 } 183 184 # to avoid lines with more than 256 characters, it can be useful to use relative paths 185 if ($allvariables->{'RELATIVE_PATHES_IN_DDF'}) 186 { 187 $sourcepath = make_relative_ddf_path($sourcepath); 188 } 189 190 my $ddfline = "\"" . $sourcepath . "\"" . " " . $uniquename . "\n"; 191 push(@ddffile, $ddfline); 192 193 $installer::logger::Lang->printf(" adding '%s' with sequence %d to ddf\n", 194 $onefile->{'uniquename'}, 195 $onefile->{'sequencenumber'}); 196 } 197 # creating the DDF file 198 199 my $ddffilename = $cabinetfile; 200 $ddffilename =~ s/.cab/.ddf/; 201 $ddfdir =~ s/\Q$installer::globals::separator\E\s*$//; 202 $ddffilename = $ddfdir . $installer::globals::separator . $ddffilename; 203 204 installer::files::save_file($ddffilename ,\@ddffile); 205 $installer::logger::Lang->print("Created ddf file: %s\n", $ddffilename); 206 207 # lines in ddf files must not be longer than 256 characters 208 check_ddf_file(\@ddffile, $ddffilename); 209 210 # collecting all ddf files 211 push(@installer::globals::allddffiles, $ddffilename); 212 213 # Writing the makecab system call 214 # Return a list with all system calls for packaging process. 215 my @cabfilelist = ("makecab.exe /V3 /F " . $ddffilename . " 2\>\&1 |" . "\n"); 216 return \@cabfilelist; 217} 218 219 220 221################################################################# 222# Returning the name of the msi database 223################################################################# 224 225sub get_msidatabasename 226{ 227 my ($allvariableshashref, $language) = @_; 228 229 my $databasename = $allvariableshashref->{'PRODUCTNAME'} . $allvariableshashref->{'PRODUCTVERSION'}; 230 $databasename = lc($databasename); 231 $databasename =~ s/\.//g; 232 $databasename =~ s/\-//g; 233 $databasename =~ s/\s//g; 234 235 # possibility to overwrite the name with variable DATABASENAME 236 if ( $allvariableshashref->{'DATABASENAME'} ) 237 { 238 $databasename = $allvariableshashref->{'DATABASENAME'}; 239 } 240 241 if ( $language ) 242 { 243 if (!($language eq "")) 244 { 245 $databasename .= "_$language"; 246 } 247 } 248 249 $databasename .= ".msi"; 250 251 return $databasename; 252} 253 254################################################################# 255# Creating the msi database 256# This works only on Windows 257################################################################# 258 259sub create_msi_database 260{ 261 my ($idtdirbase ,$msifilename) = @_; 262 263 # -f : path containing the idt files 264 # -d : msi database, including path 265 # -c : create database 266 # -i : include the following tables ("*" includes all available tables) 267 268 my $msidb = "msidb.exe"; # Has to be in the path 269 my $extraslash = ""; # Has to be set for non-ActiveState perl 270 271 installer::logger::include_header_into_logfile("Creating msi database"); 272 273 $idtdirbase = installer::converter::make_path_conform($idtdirbase); 274 275 $msifilename = installer::converter::make_path_conform($msifilename); 276 277 if ( $^O =~ /cygwin/i ) { 278 # msidb.exe really wants backslashes. (And double escaping because system() expands the string.) 279 $idtdirbase =~ s/\//\\\\/g; 280 $msifilename =~ s/\//\\\\/g; 281 $extraslash = "\\"; 282 } 283 my $systemcall = $msidb . " -f " . $idtdirbase . " -d " . $msifilename . " -c " . "-i " . $extraslash . "*"; 284 285 my $returnvalue = system($systemcall); 286 287 my $infoline = "Systemcall: $systemcall\n"; 288 $installer::logger::Lang->print($infoline); 289 290 if ($returnvalue) 291 { 292 $infoline = "ERROR: Could not execute $msidb!\n"; 293 $installer::logger::Lang->print($infoline); 294 } 295 else 296 { 297 $infoline = "Success: Executed $msidb successfully!\n"; 298 $installer::logger::Lang->print($infoline); 299 } 300} 301 302##################################################################### 303# Returning the value from sis.mlf for Summary Information Stream 304##################################################################### 305 306sub get_value_from_sis_lng 307{ 308 my ($language, $languagefile, $searchstring) = @_; 309 310 my $language_block = installer::windows::idtglobal::get_language_block_from_language_file($searchstring, $languagefile); 311 my $newstring = installer::windows::idtglobal::get_language_string_from_language_block($language_block, $language, $searchstring); 312 $newstring = "\"" . $newstring . "\""; 313 314 return $newstring; 315} 316 317################################################################# 318# Returning the msi version for the Summary Information Stream 319################################################################# 320 321sub get_msiversion_for_sis 322{ 323 my $msiversion = "200"; 324 return $msiversion; 325} 326 327################################################################# 328# Returning the word count for the Summary Information Stream 329################################################################# 330 331sub get_wordcount_for_sis 332{ 333 my $wordcount = "0"; 334 return $wordcount; 335} 336 337################################################################# 338# Returning the codepage for the Summary Information Stream 339################################################################# 340 341sub get_codepage_for_sis 342{ 343 my ( $language ) = @_; 344 345 my $codepage = installer::windows::language::get_windows_encoding($language); 346 347 # Codepage 65001 does not work in Summary Information Stream 348 if ( $codepage == 65001 ) { $codepage = 0; } 349 350 # my $codepage = "1252"; # determine dynamically in a function 351 # my $codepage = "65001"; # UTF-8 352 return $codepage; 353} 354 355################################################################# 356# Returning the template for the Summary Information Stream 357################################################################# 358 359sub get_template_for_sis 360{ 361 my ( $language, $allvariables ) = @_; 362 363 my $windowslanguage = installer::windows::language::get_windows_language($language); 364 365 my $architecture = "Intel"; 366 367 # Adding 256, if this is a 64 bit installation set. 368 if (( $allvariables->{'64BITPRODUCT'} ) && ( $allvariables->{'64BITPRODUCT'} == 1 )) { $architecture = "x64"; } 369 370 my $value = "\"" . $architecture . ";" . $windowslanguage; # adding the Windows language 371 372 $value = $value . "\""; # adding ending '"' 373 374 return $value ; 375} 376 377################################################################# 378# Returning the PackageCode for the Summary Information Stream 379################################################################# 380 381sub get_packagecode_for_sis 382{ 383 # always generating a new package code for each package 384 385 my $guid = "\{" . create_guid() . "\}"; 386 387 my $infoline = "PackageCode: $guid\n"; 388 $installer::logger::Lang->print($infoline); 389 390 return $guid; 391} 392 393################################################################# 394# Returning the title for the Summary Information Stream 395################################################################# 396 397sub get_title_for_sis 398{ 399 my ( $language, $languagefile, $searchstring ) = @_; 400 401 my $title = get_value_from_sis_lng($language, $languagefile, $searchstring ); 402 403 return $title; 404} 405 406################################################################# 407# Returning the author for the Summary Information Stream 408################################################################# 409 410sub get_author_for_sis 411{ 412 my $author = $installer::globals::longmanufacturer; 413 414 $author = "\"" . $author . "\""; 415 416 return $author; 417} 418 419################################################################# 420# Returning the subject for the Summary Information Stream 421################################################################# 422 423sub get_subject_for_sis 424{ 425 my ( $allvariableshashref ) = @_; 426 427 my $subject = $allvariableshashref->{'PRODUCTNAME'} . " " . $allvariableshashref->{'PRODUCTVERSION'}; 428 429 $subject = "\"" . $subject . "\""; 430 431 return $subject; 432} 433 434################################################################# 435# Returning the comment for the Summary Information Stream 436################################################################# 437 438sub get_comment_for_sis 439{ 440 my ( $language, $languagefile, $searchstring ) = @_; 441 442 my $comment = get_value_from_sis_lng($language, $languagefile, $searchstring ); 443 444 return $comment; 445} 446 447################################################################# 448# Returning the keywords for the Summary Information Stream 449################################################################# 450 451sub get_keywords_for_sis 452{ 453 my ( $language, $languagefile, $searchstring ) = @_; 454 455 my $keywords = get_value_from_sis_lng($language, $languagefile, $searchstring ); 456 457 return $keywords; 458} 459 460###################################################################### 461# Returning the application name for the Summary Information Stream 462###################################################################### 463 464sub get_appname_for_sis 465{ 466 my ( $language, $languagefile, $searchstring ) = @_; 467 468 my $appname = get_value_from_sis_lng($language, $languagefile, $searchstring ); 469 470 return $appname; 471} 472 473###################################################################### 474# Returning the security for the Summary Information Stream 475###################################################################### 476 477sub get_security_for_sis 478{ 479 my $security = "0"; 480 return $security; 481} 482 483################################################################# 484# Writing the Summary information stream into the msi database 485# This works only on Windows 486################################################################# 487 488sub write_summary_into_msi_database 489{ 490 my ($msifilename, $language, $languagefile, $allvariableshashref) = @_; 491 492 # -g : required msi version 493 # -c : codepage 494 # -p : template 495 496 installer::logger::include_header_into_logfile("Writing summary information stream"); 497 498 my $msiinfo = "msiinfo.exe"; # Has to be in the path 499 500 my $sislanguage = "en-US"; # title, comment, keyword and appname always in English 501 502 my $msiversion = get_msiversion_for_sis(); 503 my $codepage = get_codepage_for_sis($language); 504 my $template = get_template_for_sis($language, $allvariableshashref); 505 my $guid = get_packagecode_for_sis(); 506 my $title = get_title_for_sis($sislanguage,$languagefile, "OOO_SIS_TITLE"); 507 my $author = get_author_for_sis(); 508 my $subject = get_subject_for_sis($allvariableshashref); 509 my $comment = get_comment_for_sis($sislanguage,$languagefile, "OOO_SIS_COMMENT"); 510 my $keywords = get_keywords_for_sis($sislanguage,$languagefile, "OOO_SIS_KEYWORDS"); 511 my $appname = get_appname_for_sis($sislanguage,$languagefile, "OOO_SIS_APPNAME"); 512 my $security = get_security_for_sis(); 513 my $wordcount = get_wordcount_for_sis(); 514 515 $msifilename = installer::converter::make_path_conform($msifilename); 516 517 my $systemcall = $msiinfo . " " . $msifilename . " -g " . $msiversion . " -c " . $codepage 518 . " -p " . $template . " -v " . $guid . " -t " . $title . " -a " . $author 519 . " -j " . $subject . " -o " . $comment . " -k " . $keywords . " -n " . $appname 520 . " -u " . $security . " -w " . $wordcount; 521 522 my $returnvalue = system($systemcall); 523 524 my $infoline = "Systemcall: $systemcall\n"; 525 $installer::logger::Lang->print($infoline); 526 527 if ($returnvalue) 528 { 529 $infoline = "ERROR: Could not execute $msiinfo!\n"; 530 $installer::logger::Lang->print($infoline); 531 } 532 else 533 { 534 $infoline = "Success: Executed $msiinfo successfully!\n"; 535 $installer::logger::Lang->print($infoline); 536 } 537} 538 539######################################################################### 540# For more than one language in the installation set: 541# Use one database and create Transformations for all other languages 542######################################################################### 543 544sub create_transforms 545{ 546 my ($languagesarray, $defaultlanguage, $installdir, $allvariableshashref) = @_; 547 548 installer::logger::include_header_into_logfile("Creating Transforms"); 549 550 my $msitran = "msitran.exe"; # Has to be in the path 551 552 $installdir = installer::converter::make_path_conform($installdir); 553 554 # Syntax for creating a transformation 555 # msitran.exe -g <baseDB> <referenceDB> <transformfile> [<errorhandling>} 556 557 my $basedbname = get_msidatabasename($allvariableshashref, $defaultlanguage); 558 $basedbname = $installdir . $installer::globals::separator . $basedbname; 559 560 my $errorhandling = "f"; # Suppress "change codepage" error 561 562 # Iterating over all files 563 564 foreach ( @{$languagesarray} ) 565 { 566 my $onelanguage = $_; 567 568 if ( $onelanguage eq $defaultlanguage ) { next; } 569 570 my $referencedbname = get_msidatabasename($allvariableshashref, $onelanguage); 571 $referencedbname = $installdir . $installer::globals::separator . $referencedbname; 572 573 my $transformfile = $installdir . $installer::globals::separator . "trans_" . $onelanguage . ".mst"; 574 575 my $systemcall = $msitran . " " . " -g " . $basedbname . " " . $referencedbname . " " . $transformfile . " " . $errorhandling; 576 577 my $returnvalue = system($systemcall); 578 579 my $infoline = "Systemcall: $systemcall\n"; 580 $installer::logger::Lang->print($infoline); 581 582 # Problem: msitran.exe in version 4.0 always returns "1", even if no failure occurred. 583 # Therefore it has to be checked, if this is version 4.0. If yes, if the mst file 584 # exists and if it is larger than 0 bytes. If this is true, then no error occurred. 585 # File Version of msitran.exe: 4.0.6000.16384 has checksum: "b66190a70145a57773ec769e16777b29". 586 # Same for msitran.exe from wntmsci12: "aa25d3445b94ffde8ef0c1efb77a56b8" 587 588 if ($returnvalue) 589 { 590 $infoline = "WARNING: Returnvalue of $msitran is not 0. Checking version of $msitran!\n"; 591 $installer::logger::Lang->print($infoline); 592 593 open(FILE, "<$installer::globals::msitranpath") or die "ERROR: Can't open $installer::globals::msitranpath for creating file hash"; 594 binmode(FILE); 595 my $digest = Digest::MD5->new->addfile(*FILE)->hexdigest; 596 close(FILE); 597 598 my @problemchecksums = ("b66190a70145a57773ec769e16777b29", "aa25d3445b94ffde8ef0c1efb77a56b8"); 599 my $isproblemchecksum = 0; 600 601 foreach my $problemchecksum ( @problemchecksums ) 602 { 603 $infoline = "Checksum of problematic MsiTran.exe: $problemchecksum\n"; 604 $installer::logger::Lang->print($infoline); 605 $infoline = "Checksum of used MsiTran.exe: $digest\n"; 606 $installer::logger::Lang->print($infoline); 607 if ( $digest eq $problemchecksum ) { $isproblemchecksum = 1; } 608 } 609 610 if ( $isproblemchecksum ) 611 { 612 # Check existence of mst 613 if ( -f $transformfile ) 614 { 615 $infoline = "File $transformfile exists.\n"; 616 $installer::logger::Lang->print($infoline); 617 my $filesize = ( -s $transformfile ); 618 $infoline = "Size of $transformfile: $filesize\n"; 619 $installer::logger::Lang->print($infoline); 620 621 if ( $filesize > 0 ) 622 { 623 $infoline = "Info: Returnvalue $returnvalue of $msitran is no problem :-) .\n"; 624 $installer::logger::Lang->print($infoline); 625 $returnvalue = 0; # reset the error 626 } 627 else 628 { 629 $infoline = "Filesize indicates that an error occurred.\n"; 630 $installer::logger::Lang->print($infoline); 631 } 632 } 633 else 634 { 635 $infoline = "File $transformfile does not exist -> An error occurred.\n"; 636 $installer::logger::Lang->print($infoline); 637 } 638 } 639 else 640 { 641 $infoline = "This is not a problematic version of msitran.exe. Therefore the error is not caused by problematic msitran.exe.\n"; 642 $installer::logger::Lang->print($infoline); 643 } 644 } 645 646 if ($returnvalue) 647 { 648 $infoline = "ERROR: Could not execute $msitran!\n"; 649 $installer::logger::Lang->print($infoline); 650 } 651 else 652 { 653 $infoline = "Success: Executed $msitran successfully!\n"; 654 $installer::logger::Lang->print($infoline); 655 } 656 657 # The reference database can be deleted 658 659 my $result = unlink($referencedbname); 660 # $result contains the number of deleted files 661 662 if ( $result == 0 ) 663 { 664 $infoline = "ERROR: Could not remove file $$referencedbname !\n"; 665 $installer::logger::Lang->print($infoline); 666 installer::exiter::exit_program($infoline, "create_transforms"); 667 } 668 } 669} 670 671######################################################################### 672# The default language msi database does not need to contain 673# the language in the database name. Therefore the file 674# is renamed. Example: "openofficeorg20_01.msi" to "openofficeorg20.msi" 675######################################################################### 676 677sub rename_msi_database_in_installset 678{ 679 my ($defaultlanguage, $installdir, $allvariableshashref) = @_; 680 681 installer::logger::include_header_into_logfile("Renaming msi database"); 682 683 my $olddatabasename = get_msidatabasename($allvariableshashref, $defaultlanguage); 684 $olddatabasename = $installdir . $installer::globals::separator . $olddatabasename; 685 686 my $newdatabasename = get_msidatabasename($allvariableshashref); 687 688 $installer::globals::shortmsidatabasename = $newdatabasename; 689 690 $newdatabasename = $installdir . $installer::globals::separator . $newdatabasename; 691 692 installer::systemactions::rename_one_file($olddatabasename, $newdatabasename); 693 694 $installer::globals::msidatabasename = $newdatabasename; 695} 696 697######################################################################### 698# Adding the language to the name of the msi databasename, 699# if this is required (ADDLANGUAGEINDATABASENAME) 700######################################################################### 701 702sub add_language_to_msi_database 703{ 704 my ($defaultlanguage, $installdir, $allvariables) = @_; 705 706 my $languagestring = $defaultlanguage; 707 if ( $allvariables->{'USELANGUAGECODE'} ) { $languagestring = installer::windows::language::get_windows_language($defaultlanguage); } 708 my $newdatabasename = $installer::globals::shortmsidatabasename; 709 $newdatabasename =~ s/\.msi\s*$/_$languagestring\.msi/; 710 $installer::globals::shortmsidatabasename = $newdatabasename; 711 $newdatabasename = $installdir . $installer::globals::separator . $newdatabasename; 712 713 my $olddatabasename = $installer::globals::msidatabasename; 714 715 installer::systemactions::rename_one_file($olddatabasename, $newdatabasename); 716 717 $installer::globals::msidatabasename = $newdatabasename; 718} 719 720########################################################################## 721# Writing the databasename into the setup.ini. 722########################################################################## 723 724sub put_databasename_into_setupini 725{ 726 my ($setupinifile, $allvariableshashref) = @_; 727 728 my $databasename = get_msidatabasename($allvariableshashref); 729 my $line = "database=" . $databasename . "\n"; 730 731 push(@{$setupinifile}, $line); 732} 733 734########################################################################## 735# Writing the required msi version into setup.ini 736########################################################################## 737 738sub put_msiversion_into_setupini 739{ 740 my ($setupinifile) = @_; 741 742 my $msiversion = "2.0"; 743 my $line = "msiversion=" . $msiversion . "\n"; 744 745 push(@{$setupinifile}, $line); 746} 747 748########################################################################## 749# Writing the productname into setup.ini 750########################################################################## 751 752sub put_productname_into_setupini 753{ 754 my ($setupinifile, $allvariableshashref) = @_; 755 756 my $productname = $allvariableshashref->{'PRODUCTNAME'}; 757 my $line = "productname=" . $productname . "\n"; 758 759 push(@{$setupinifile}, $line); 760} 761 762########################################################################## 763# Writing the productcode into setup.ini 764########################################################################## 765 766sub put_productcode_into_setupini 767{ 768 my ($setupinifile) = @_; 769 770 my $productcode = $installer::globals::productcode; 771 my $line = "productcode=" . $productcode . "\n"; 772 773 push(@{$setupinifile}, $line); 774} 775 776########################################################################## 777# Writing the ProductVersion from Property table into setup.ini 778########################################################################## 779 780sub put_productversion_into_setupini 781{ 782 my ($setupinifile) = @_; 783 784 my $line = "productversion=" . $installer::globals::msiproductversion . "\n"; 785 push(@{$setupinifile}, $line); 786} 787 788########################################################################## 789# Writing the key for Minor Upgrades into setup.ini 790########################################################################## 791 792sub put_upgradekey_into_setupini 793{ 794 my ($setupinifile) = @_; 795 796 if ( $installer::globals::minorupgradekey ne "" ) 797 { 798 my $line = "upgradekey=" . $installer::globals::minorupgradekey . "\n"; 799 push(@{$setupinifile}, $line); 800 } 801} 802 803########################################################################## 804# Writing the number of languages into setup.ini 805########################################################################## 806 807sub put_languagecount_into_setupini 808{ 809 my ($setupinifile, $languagesarray) = @_; 810 811 my $languagecount = $#{$languagesarray} + 1; 812 my $line = "count=" . $languagecount . "\n"; 813 814 push(@{$setupinifile}, $line); 815} 816 817########################################################################## 818# Writing the defaultlanguage into setup.ini 819########################################################################## 820 821sub put_defaultlanguage_into_setupini 822{ 823 my ($setupinifile, $defaultlanguage) = @_; 824 825 my $windowslanguage = installer::windows::language::get_windows_language($defaultlanguage); 826 my $line = "default=" . $windowslanguage . "\n"; 827 push(@{$setupinifile}, $line); 828} 829 830########################################################################## 831# Writing the information about transformations into setup.ini 832########################################################################## 833 834sub put_transforms_into_setupini 835{ 836 my ($setupinifile, $onelanguage, $counter) = @_; 837 838 my $windowslanguage = installer::windows::language::get_windows_language($onelanguage); 839 my $transformfilename = "trans_" . $onelanguage . ".mst"; 840 841 my $line = "lang" . $counter . "=" . $windowslanguage . "," . $transformfilename . "\n"; 842 843 push(@{$setupinifile}, $line); 844} 845 846################################################### 847# Including Windows line ends in ini files 848# Profiles on Windows shall have \r\n line ends 849################################################### 850 851sub include_windows_lineends 852{ 853 my ($onefile) = @_; 854 855 for ( my $i = 0; $i <= $#{$onefile}; $i++ ) 856 { 857 ${$onefile}[$i] =~ s/\r?\n$/\r\n/; 858 } 859} 860 861########################################################################## 862# Generating the file setup.ini, that is used by the loader setup.exe. 863########################################################################## 864 865sub create_setup_ini 866{ 867 my ($languagesarray, $defaultlanguage, $installdir, $allvariableshashref) = @_; 868 869 installer::logger::include_header_into_logfile("Creating setup.ini"); 870 871 my $setupinifilename = $installdir . $installer::globals::separator . "setup.ini"; 872 873 my @setupinifile = (); 874 my $setupinifile = \@setupinifile; 875 876 my $line = "\[setup\]\n"; 877 push(@setupinifile, $line); 878 879 put_databasename_into_setupini($setupinifile, $allvariableshashref); 880 put_msiversion_into_setupini($setupinifile); 881 put_productname_into_setupini($setupinifile, $allvariableshashref); 882 put_productcode_into_setupini($setupinifile); 883 put_productversion_into_setupini($setupinifile); 884 put_upgradekey_into_setupini($setupinifile); 885 886 $line = "\[languages\]\n"; 887 push(@setupinifile, $line); 888 889 put_languagecount_into_setupini($setupinifile, $languagesarray); 890 put_defaultlanguage_into_setupini($setupinifile, $defaultlanguage); 891 892 if ( $#{$languagesarray} > 0 ) # writing the transforms information 893 { 894 my $counter = 1; 895 896 for ( my $i = 0; $i <= $#{$languagesarray}; $i++ ) 897 { 898 if ( ${$languagesarray}[$i] eq $defaultlanguage ) { next; } 899 900 put_transforms_into_setupini($setupinifile, ${$languagesarray}[$i], $counter); 901 $counter++; 902 } 903 } 904 905 if ( $installer::globals::iswin && $installer::globals::plat =~ /cygwin/i) # Windows line ends only for Cygwin 906 { 907 include_windows_lineends($setupinifile); 908 } 909 910 installer::files::save_file($setupinifilename, $setupinifile); 911 912 $installer::logger::Lang->printf("Generated file %s\n", $setupinifilename); 913} 914 915################################################################# 916# Copying the files defined as ScpActions into the 917# installation set. 918################################################################# 919 920sub copy_scpactions_into_installset 921{ 922 my ($defaultlanguage, $installdir, $allscpactions) = @_; 923 924 installer::logger::include_header_into_logfile("Copying ScpAction files into installation set"); 925 926 for ( my $i = 0; $i <= $#{$allscpactions}; $i++ ) 927 { 928 my $onescpaction = ${$allscpactions}[$i]; 929 930 if ( $onescpaction->{'Name'} eq "loader.exe" ) { next; } # do not copy this ScpAction loader 931 932 # only copying language independent files or files with the correct language (the defaultlanguage) 933 934 my $filelanguage = $onescpaction->{'specificlanguage'}; 935 936 if ( ($filelanguage eq $defaultlanguage) || ($filelanguage eq "") ) 937 { 938 my $sourcefile = $onescpaction->{'sourcepath'}; 939 my $destfile = $installdir . $installer::globals::separator . $onescpaction->{'DestinationName'}; 940 941 installer::systemactions::copy_one_file($sourcefile, $destfile); 942 } 943 } 944} 945 946################################################################# 947# Copying the files for the Windows installer into the 948# installation set (setup.exe). 949################################################################# 950 951sub copy_windows_installer_files_into_installset 952{ 953 my ($installdir, $includepatharrayref, $allvariables) = @_; 954 955 installer::logger::include_header_into_logfile("Copying Windows installer files into installation set"); 956 957 my @copyfile = (); 958 push(@copyfile, "loader2.exe"); 959 960 if ( $allvariables->{'NOLOADERREQUIRED'} ) { @copyfile = (); } 961 962 for ( my $i = 0; $i <= $#copyfile; $i++ ) 963 { 964 my $filename = $copyfile[$i]; 965 my $sourcefileref = installer::scriptitems::get_sourcepath_from_filename_and_includepath(\$filename, $includepatharrayref, 1); 966 967 if ( ! -f $$sourcefileref ) { installer::exiter::exit_program("ERROR: msi file not found: $$sourcefileref !", "copy_windows_installer_files_into_installset"); } 968 969 my $destfile; 970 if ( $copyfile[$i] eq "loader2.exe" ) { $destfile = "setup.exe"; } # renaming the loader 971 else { $destfile = $copyfile[$i]; } 972 973 $destfile = $installdir . $installer::globals::separator . $destfile; 974 975 installer::systemactions::copy_one_file($$sourcefileref, $destfile); 976 } 977} 978 979################################################################# 980# Copying the child projects into the 981# installation set 982################################################################# 983 984sub copy_child_projects_into_installset 985{ 986 my ($installdir, $allvariables) = @_; 987 988 my $sourcefile = ""; 989 my $destdir = ""; 990 991 # adding Java 992 993 if ( $allvariables->{'JAVAPRODUCT'} ) 994 { 995 $sourcefile = $installer::globals::javafile->{'sourcepath'}; 996 $destdir = $installdir . $installer::globals::separator . $installer::globals::javafile->{'Subdir'}; 997 if ( ! -d $destdir) { installer::systemactions::create_directory($destdir); } 998 installer::systemactions::copy_one_file($sourcefile, $destdir); 999 } 1000 1001 if ( $allvariables->{'UREPRODUCT'} ) 1002 { 1003 $sourcefile = $installer::globals::urefile->{'sourcepath'}; 1004 $destdir = $installdir . $installer::globals::separator . $installer::globals::urefile->{'Subdir'}; 1005 if ( ! -d $destdir) { installer::systemactions::create_directory($destdir); } 1006 installer::systemactions::copy_one_file($sourcefile, $destdir); 1007 } 1008} 1009 1010 1011 1012=head2 create_guid () 1013 1014 Create a single UUID aka GUID via calling the external executable 'uuidgen'. 1015 There are Perl modules for that, but do they exist on the build bots? 1016 1017=cut 1018sub create_guid () 1019{ 1020 my $uuid = qx("uuidgen"); 1021 $uuid =~ s/\s*$//; 1022 return uc($uuid); 1023} 1024 1025################################################################# 1026# Calculating a GUID with a string using md5. 1027################################################################# 1028 1029sub calculate_guid 1030{ 1031 my ( $string ) = @_; 1032 1033 my $guid = ""; 1034 1035 my $md5 = Digest::MD5->new; 1036 $md5->add($string); 1037 my $digest = $md5->hexdigest; 1038 $digest = uc($digest); 1039 1040 # my $id = pack("A32", $digest); 1041 my ($first, $second, $third, $fourth, $fifth) = unpack ('A8 A4 A4 A4 A12', $digest); 1042 $guid = "$first-$second-$third-$fourth-$fifth"; 1043 1044 $installer::logger::Lang->printf("guid for '%s' is %s\n", 1045 $string, $guid); 1046 1047 return $guid; 1048} 1049 1050################################################################# 1051# Calculating a ID with a string using md5 (very fast). 1052################################################################# 1053 1054sub calculate_id 1055{ 1056 my ( $string, $length ) = @_; 1057 1058 my $id = ""; 1059 1060 my $md5 = Digest::MD5->new; 1061 $md5->add($string); 1062 my $digest = lc($md5->hexdigest); 1063 $id = substr($digest, 0, $length); 1064 1065 return $id; 1066} 1067 1068################################################################# 1069# Filling the component hash with the values of the 1070# component file. 1071################################################################# 1072 1073sub fill_component_hash 1074{ 1075 my ($componentfile) = @_; 1076 1077 my %components = (); 1078 1079 for ( my $i = 0; $i <= $#{$componentfile}; $i++ ) 1080 { 1081 my $line = ${$componentfile}[$i]; 1082 1083 if ( $line =~ /^\s*(.*?)\t(.*?)\s*$/ ) 1084 { 1085 my $key = $1; 1086 my $value = $2; 1087 1088 $components{$key} = $value; 1089 } 1090 } 1091 1092 return \%components; 1093} 1094 1095################################################################# 1096# Creating a new component file, if new guids were generated. 1097################################################################# 1098 1099sub create_new_component_file 1100{ 1101 my ($componenthash) = @_; 1102 1103 my @componentfile = (); 1104 1105 my $key; 1106 1107 foreach $key (keys %{$componenthash}) 1108 { 1109 my $value = $componenthash->{$key}; 1110 my $input = "$key\t$value\n"; 1111 push(@componentfile ,$input); 1112 } 1113 1114 return \@componentfile; 1115} 1116 1117################################################################# 1118# Filling real component GUID into the component table. 1119# This works only on Windows 1120################################################################# 1121 1122sub __set_uuid_into_component_table 1123{ 1124 my ($idtdirbase, $allvariables) = @_; 1125 1126 my $componenttablename = $idtdirbase . $installer::globals::separator . "Componen.idt"; 1127 1128 my $componenttable = installer::files::read_file($componenttablename); 1129 1130 # For update and patch reasons (small update) the GUID of an existing component must not change! 1131 # The collection of component GUIDs is saved in the directory $installer::globals::idttemplatepath in the file "components.txt" 1132 1133 my $infoline = ""; 1134 my $counter = 0; 1135 # my $componentfile = installer::files::read_file($installer::globals::componentfilename); 1136 # my $componenthash = fill_component_hash($componentfile); 1137 1138 for ( my $i = 3; $i <= $#{$componenttable}; $i++ ) # ignoring the first three lines 1139 { 1140 my $oneline = ${$componenttable}[$i]; 1141 my $componentname = ""; 1142 if ( $oneline =~ /^\s*(\S+?)\t/ ) { $componentname = $1; } 1143 1144 my $uuid = ""; 1145 1146 # if ( $componenthash->{$componentname} ) 1147 # { 1148 # $uuid = $componenthash->{$componentname}; 1149 # } 1150 # else 1151 # { 1152 1153 if ( exists($installer::globals::calculated_component_guids{$componentname})) 1154 { 1155 $uuid = $installer::globals::calculated_component_guids{$componentname}; 1156 } 1157 else 1158 { 1159 # Calculating new GUID with the help of the component name. 1160 my $useooobaseversion = 1; 1161 if ( exists($installer::globals::base_independent_components{$componentname})) 1162 { 1163 $useooobaseversion = 0; 1164 } 1165 my $sourcestring = $componentname; 1166 1167 if ( $useooobaseversion ) 1168 { 1169 if ( ! exists($allvariables->{'OOOBASEVERSION'}) ) 1170 { 1171 installer::exiter::exit_program( 1172 "ERROR: Could not find variable \"OOOBASEVERSION\" (required value for GUID creation)!", 1173 "set_uuid_into_component_table"); 1174 } 1175 $sourcestring = $sourcestring . "_" . $allvariables->{'OOOBASEVERSION'}; 1176 } 1177 $uuid = calculate_guid($sourcestring); 1178 $counter++; 1179 1180 # checking, if there is a conflict with an already created guid 1181 if ( exists($installer::globals::allcalculated_guids{$uuid}) ) 1182 { 1183 installer::exiter::exit_program( 1184 "ERROR: \"$uuid\" was already created before!", 1185 "set_uuid_into_component_table"); 1186 } 1187 $installer::globals::allcalculated_guids{$uuid} = 1; 1188 $installer::globals::calculated_component_guids{$componentname} = $uuid; 1189 1190 # Setting new uuid 1191 # $componenthash->{$componentname} = $uuid; 1192 1193 # Setting flag 1194 # $installer::globals::created_new_component_guid = 1; # this is very important! 1195 } 1196 # } 1197 1198 ${$componenttable}[$i] =~ s/COMPONENTGUID/$uuid/; 1199 } 1200 1201 installer::files::save_file($componenttablename, $componenttable); 1202 1203# if ( $installer::globals::created_new_component_guid ) 1204# { 1205# # create new component file! 1206# $componentfile = create_new_component_file($componenthash); 1207# installer::worker::sort_array($componentfile); 1208# 1209# # To avoid conflict the components file cannot be saved at the same place 1210# # All important data have to be saved in the directory: $installer::globals::infodirectory 1211# my $localcomponentfilename = $installer::globals::componentfilename; 1212# installer::pathanalyzer::make_absolute_filename_to_relative_filename(\$localcomponentfilename); 1213# $localcomponentfilename = $installer::globals::infodirectory . $installer::globals::separator . $localcomponentfilename; 1214# installer::files::save_file($localcomponentfilename, $componentfile); 1215# 1216# # installer::files::save_file($installer::globals::componentfilename, $componentfile); # version using new file in solver 1217# 1218# $infoline = "COMPONENTCODES: Created $counter new GUIDs for components ! \n"; 1219# $installer::logger::Lang->print($infoline); 1220# } 1221# else 1222# { 1223# $infoline = "SUCCESS COMPONENTCODES: All component codes exist! \n"; 1224# $installer::logger::Lang->print($infoline); 1225# } 1226 1227} 1228 1229######################################################################### 1230# Adding final 64 properties into msi database, if required. 1231# RegLocator : +16 in type column to search in 64 bit registry. 1232# All conditions: "VersionNT" -> "VersionNT64" (several tables). 1233# Already done: "+256" in Attributes column of table "Component". 1234# Still following: Setting "x64" instead of "Intel" in Summary 1235# Information Stream of msi database in "get_template_for_sis". 1236######################################################################### 1237 1238sub prepare_64bit_database 1239{ 1240 my ($basedir, $allvariables) = @_; 1241 1242 my $infoline = ""; 1243 1244 if (( $allvariables->{'64BITPRODUCT'} ) && ( $allvariables->{'64BITPRODUCT'} == 1 )) 1245 { 1246 # 1. Beginning with table "RegLocat.idt". Adding "16" to the type. 1247 1248 my $reglocatfile = ""; 1249 my $reglocatfilename = $basedir . $installer::globals::separator . "RegLocat.idt"; 1250 1251 if ( -f $reglocatfilename ) 1252 { 1253 my $saving_required = 0; 1254 $reglocatfile = installer::files::read_file($reglocatfilename); 1255 1256 for ( my $i = 3; $i <= $#{$reglocatfile}; $i++ ) # ignoring the first three lines 1257 { 1258 my $oneline = ${$reglocatfile}[$i]; 1259 1260 if ( $oneline =~ /^\s*\#/ ) { next; } # this is a comment line 1261 if ( $oneline =~ /^\s*$/ ) { next; } 1262 1263 if ( $oneline =~ /^\s*(.*?)\t(.*?)\t(.*?)\t(.*?)\t(\d+)\s*$/ ) 1264 { 1265 # Syntax: Signature_ Root Key Name Type 1266 my $sig = $1; 1267 my $root = $2; 1268 my $key = $3; 1269 my $name = $4; 1270 my $type = $5; 1271 1272 $type = $type + 16; 1273 1274 my $newline = $sig . "\t" . $root . "\t" . $key . "\t" . $name . "\t" . $type . "\n"; 1275 ${$reglocatfile}[$i] = $newline; 1276 1277 $saving_required = 1; 1278 } 1279 } 1280 1281 if ( $saving_required ) 1282 { 1283 # Saving the files 1284 installer::files::save_file($reglocatfilename ,$reglocatfile); 1285 $infoline = "Making idt file 64 bit conform: $reglocatfilename\n"; 1286 $installer::logger::Lang->print($infoline); 1287 } 1288 } 1289 1290 # 2. Replacing all occurrences of "VersionNT" by "VersionNT64" 1291 1292 my @versionnt_files = ("Componen.idt", "InstallE.idt", "InstallU.idt", "LaunchCo.idt"); 1293 1294 foreach my $onefile ( @versionnt_files ) 1295 { 1296 my $fullfilename = $basedir . $installer::globals::separator . $onefile; 1297 1298 if ( -f $fullfilename ) 1299 { 1300 my $saving_required = 0; 1301 my $filecontent = installer::files::read_file($fullfilename); 1302 1303 for ( my $i = 3; $i <= $#{$filecontent}; $i++ ) # ignoring the first three lines 1304 { 1305 my $oneline = ${$filecontent}[$i]; 1306 1307 if ( $oneline =~ /\bVersionNT\b/ ) 1308 { 1309 ${$filecontent}[$i] =~ s/\bVersionNT\b/VersionNT64/g; 1310 $saving_required = 1; 1311 } 1312 } 1313 1314 if ( $saving_required ) 1315 { 1316 # Saving the files 1317 installer::files::save_file($fullfilename ,$filecontent); 1318 $infoline = "Making idt file 64 bit conform: $fullfilename\n"; 1319 $installer::logger::Lang->print($infoline); 1320 } 1321 } 1322 } 1323 } 1324 1325} 1326 1327################################################################# 1328# Include all cab files into the msi database. 1329# This works only on Windows 1330################################################################# 1331 1332sub include_cabs_into_msi 1333{ 1334 my ($installdir) = @_; 1335 1336 installer::logger::include_header_into_logfile("Including cabs into msi database"); 1337 1338 my $from = cwd(); 1339 my $to = $installdir; 1340 1341 chdir($to); 1342 1343 my $infoline = "Changing into directory: $to"; 1344 $installer::logger::Lang->print($infoline); 1345 1346 my $msidb = "msidb.exe"; # Has to be in the path 1347 my $extraslash = ""; # Has to be set for non-ActiveState perl 1348 1349 my $msifilename = $installer::globals::msidatabasename; 1350 1351 $msifilename = installer::converter::make_path_conform($msifilename); 1352 1353 # msidb.exe really wants backslashes. (And double escaping because system() expands the string.) 1354 $msifilename =~ s/\//\\\\/g; 1355 $extraslash = "\\"; 1356 1357 my $allcabfiles = installer::systemactions::find_file_with_file_extension("cab", $installdir); 1358 1359 for ( my $i = 0; $i <= $#{$allcabfiles}; $i++ ) 1360 { 1361 my $systemcall = $msidb . " -d " . $msifilename . " -a " . ${$allcabfiles}[$i]; 1362 1363 my $returnvalue = system($systemcall); 1364 1365 $infoline = "Systemcall: $systemcall\n"; 1366 $installer::logger::Lang->print($infoline); 1367 1368 if ($returnvalue) 1369 { 1370 $infoline = "ERROR: Could not execute $systemcall !\n"; 1371 $installer::logger::Lang->print($infoline); 1372 } 1373 else 1374 { 1375 $infoline = "Success: Executed $systemcall successfully!\n"; 1376 $installer::logger::Lang->print($infoline); 1377 } 1378 1379 # deleting the cab file 1380 1381 unlink(${$allcabfiles}[$i]); 1382 1383 $infoline = "Deleted cab file: ${$allcabfiles}[$i]\n"; 1384 $installer::logger::Lang->print($infoline); 1385 } 1386 1387 $infoline = "Changing back into directory: $from"; 1388 $installer::logger::Lang->print($infoline); 1389 1390 chdir($from); 1391} 1392 1393################################################################# 1394# Executing the created batch file to pack all files. 1395# This works only on Windows 1396################################################################# 1397 1398sub execute_packaging 1399{ 1400 my ($localpackjobref, $loggingdir, $allvariables) = @_; 1401 1402 installer::logger::include_header_into_logfile("Packaging process"); 1403 1404 $installer::logger::Lang->add_timestamp("Performance Info: Execute packaging start"); 1405 1406 my $infoline = ""; 1407 my $from = cwd(); 1408 my $to = $loggingdir; 1409 1410 chdir($to); 1411 $infoline = "chdir: $to \n"; 1412 $installer::logger::Lang->print($infoline); 1413 1414 # if the ddf file contains relative paths, it is necessary to change into the temp directory 1415 if ( $allvariables->{'RELATIVE_PATHES_IN_DDF'} ) 1416 { 1417 $to = $installer::globals::temppath; 1418 chdir($to); 1419 $infoline = "chdir: $to \n"; 1420 $installer::logger::Lang->print($infoline); 1421 } 1422 1423 # changing the tmp directory, because makecab.exe generates temporary cab files 1424 my $origtemppath = ""; 1425 if ( $ENV{'TMP'} ) { $origtemppath = $ENV{'TMP'}; } 1426 $ENV{'TMP'} = $installer::globals::temppath; # setting TMP to the new unique directory! 1427 1428 my $maxmakecabcalls = 3; 1429 my $allmakecabcalls = $#{$localpackjobref} + 1; 1430 1431 for ( my $i = 0; $i <= $#{$localpackjobref}; $i++ ) 1432 { 1433 my $systemcall = ${$localpackjobref}[$i]; 1434 1435 my $callscounter = $i + 1; 1436 1437 $installer::logger::Info->printf("... makecab.exe (%s/%s) ... \n", $callscounter, $allmakecabcalls); 1438 1439 # my $returnvalue = system($systemcall); 1440 1441 for ( my $n = 1; $n <= $maxmakecabcalls; $n++ ) 1442 { 1443 my @ddfoutput = (); 1444 1445 $infoline = "Systemcall: $systemcall"; 1446 $installer::logger::Lang->print($infoline); 1447 1448 open (DDF, "$systemcall"); 1449 while (<DDF>) {push(@ddfoutput, $_); } 1450 close (DDF); 1451 1452 my $returnvalue = $?; # $? contains the return value of the systemcall 1453 1454 if ($returnvalue) 1455 { 1456 if ( $n < $maxmakecabcalls ) 1457 { 1458 $installer::logger::Info->printf("makecab_error (Try %s): Trying again\n", $n); 1459 $installer::logger::Lang->printf("makecab_error (Try %s): Trying again\n", $n); 1460 } 1461 else 1462 { 1463 $installer::logger::Info->printf("ERROR (Try %s): Abort packing \n", $n); 1464 $installer::logger::Lang->printf("ERROR (Try %s): Abort packing \n", $n); 1465 } 1466 1467 for ( my $m = 0; $m <= $#ddfoutput; $m++ ) 1468 { 1469 if ( $ddfoutput[$m] =~ /(ERROR\:.*?)\s*$/ ) 1470 { 1471 $infoline = $1 . "\n"; 1472 if ( $n < $maxmakecabcalls ) 1473 { 1474 $infoline =~ s/ERROR\:/makecab_error\:/i; 1475 } 1476 $installer::logger::Info->print($infoline); 1477 $installer::logger::Lang->print($infoline); 1478 } 1479 } 1480 1481 if ( $n == $maxmakecabcalls ) { installer::exiter::exit_program("ERROR: \"$systemcall\"!", "execute_packaging"); } 1482 } 1483 else 1484 { 1485 $infoline = "Success (Try $n): $systemcall"; 1486 $installer::logger::Lang->print($infoline); 1487 last; 1488 } 1489 } 1490 } 1491 1492 $installer::logger::Lang->add_timestamp("Performance Info: Execute packaging end"); 1493 1494 # setting back to the original tmp directory 1495 $ENV{'TMP'} = $origtemppath; 1496 1497 chdir($from); 1498 $infoline = "chdir: $from \n"; 1499 $installer::logger::Lang->print($infoline); 1500} 1501 1502 1503=head2 get_source_codes($languagesref) 1504 1505 Return product code and upgrade code from the source version. 1506 When no source version is defined then return undef for both. 1507 1508=cut 1509sub get_source_codes ($) 1510{ 1511 my ($languagesref) = @_; 1512 1513 if ( ! $installer::globals::is_release) 1514 { 1515 return (undef, undef); 1516 } 1517 elsif ( ! defined $installer::globals::source_version) 1518 { 1519 $installer::logger::Lang->printf("no source version defined\n"); 1520 return (undef, undef); 1521 } 1522 1523 my $onelanguage = installer::languages::get_key_language($languagesref); 1524 1525 my $release_data = installer::patch::ReleasesList::Instance() 1526 ->{$installer::globals::source_version} 1527 ->{$installer::globals::packageformat}; 1528 if (defined $release_data) 1529 { 1530 my $normalized_language = installer::languages::get_normalized_language($languagesref); 1531 my $language_data = $release_data->{$normalized_language}; 1532 if (defined $language_data) 1533 { 1534 $installer::logger::Lang->printf("source product code is %s\n", $language_data->{'product-code'}); 1535 $installer::logger::Lang->printf("source upgrade code is %s\n", $release_data->{'upgrade-code'}); 1536 1537 return ( 1538 $language_data->{'product-code'}, 1539 $release_data->{'upgrade-code'} 1540 ); 1541 } 1542 else 1543 { 1544 $installer::logger::Info->printf( 1545 "Warning: can not access information about previous version %s and language %s/%s/%s\n", 1546 $installer::globals::source_version, 1547 $onelanguage, 1548 join(", ",@$languagesref), 1549 $normalized_language); 1550 return (undef,undef); 1551 } 1552 } 1553 else 1554 { 1555 $installer::logger::Info->printf("Warning: can not access information about previous version %s\n", 1556 $installer::globals::source_version); 1557 return (undef,undef); 1558 } 1559} 1560 1561 1562 1563 1564=head2 set_global_code_variables ($languagesref, $allvariableshashref) 1565 1566 Determine values for the product code and upgrade code of the target version. 1567 1568 As preparation for building a Windows patch, certain conditions have to be fulfilled. 1569 - The upgrade code remains the same 1570 - The product code remains the same 1571 [this is still to be determined. For patches to work we need the same product codes but 1572 the install sets install only when the product codes differ.] 1573 In order to enforce that we have to access information about the source version. 1574 1575 The resulting values are stored as global variables 1576 $installer::globals::productcode 1577 $installer::globals::upgradecode 1578 and as variables in the given hash 1579 $allvariableshashref->{'PRODUCTCODE'} 1580 $allvariableshashref->{'UPGRADECODE'} 1581 1582=cut 1583sub set_global_code_variables ($$) 1584{ 1585 my ($languagesref, $allvariableshashref) = @_; 1586 1587 my ($source_product_code, $source_upgrade_code) = get_source_codes($languagesref); 1588 my ($target_product_code, $target_upgrade_code) = (undef, undef); 1589 1590 if (defined $source_product_code && defined $source_upgrade_code) 1591 { 1592 if ($installer::globals::is_major_release) 1593 { 1594 # For a major release we have to change the product code. 1595 $target_product_code = "{" . create_guid() . "}"; 1596 $installer::logger::Lang->printf("building a major release, created new product code %s\n", 1597 $target_product_code); 1598 1599 # Let's do a paranoia check that the new and the old guids are 1600 # different. In reality the new guid really has to be 1601 # different from all other guids used for * codes, components, 1602 # etc. 1603 if ($target_product_code eq $source_product_code) 1604 { 1605 installer::logger::PrintError( 1606 "new GUID for product code is the same as the old product code but should be different."); 1607 } 1608 } 1609 else 1610 { 1611 # For minor or micro releases we have to keep the old product code. 1612 $target_product_code = "{" . $source_product_code . "}"; 1613 $installer::logger::Lang->printf("building a minor or micro release, reusing product code %s\n", 1614 $target_product_code); 1615 } 1616 1617 $target_upgrade_code = "{" . create_guid() . "}"; 1618 # Again, just one test for paranoia. 1619 if ($target_upgrade_code eq $source_upgrade_code) 1620 { 1621 installer::logger::PrintError( 1622 "new GUID for upgrade code is the same as the old upgrade code but should be different."); 1623 } 1624 } 1625 else 1626 { 1627 # There is no previous version with which to compare the product code. 1628 # Just create two new uuids. 1629 $target_product_code = "{" . create_guid() . "}"; 1630 $target_upgrade_code = "{" . create_guid() . "}"; 1631 $installer::logger::Lang->printf("there is no source version => created new guids\n"); 1632 } 1633 1634 # Keep the upgrade code constant between versions. Read it from the codes.txt file. 1635 # Note that this handles regular installation sets and language packs. 1636 my $onelanguage = ${$languagesref}[0]; 1637 $installer::logger::Lang->printf("reading upgrade code for language %s from %s\n", 1638 $onelanguage, 1639 $installer::globals::codefilename); 1640 if (defined $installer::globals::codefilename) 1641 { 1642 my $code_filename = $installer::globals::codefilename; 1643 installer::files::check_file($code_filename); 1644 my $codefile = installer::files::read_file($code_filename); 1645 my $searchstring = "UPGRADECODE"; 1646 my $codeblock = installer::windows::idtglobal::get_language_block_from_language_file( 1647 $searchstring, 1648 $codefile); 1649 $target_upgrade_code = installer::windows::idtglobal::get_language_string_from_language_block( 1650 $codeblock, 1651 $onelanguage, 1652 ""); 1653 } 1654 # else use the previously generated upgrade code. 1655 1656 $installer::globals::productcode = $target_product_code; 1657 $installer::globals::upgradecode = $target_upgrade_code; 1658 $allvariableshashref->{'PRODUCTCODE'} = $target_product_code; 1659 $allvariableshashref->{'UPGRADECODE'} = $target_upgrade_code; 1660 1661 $installer::logger::Lang->printf("target product code is %s\n", $target_product_code); 1662 $installer::logger::Lang->printf("target upgrade code is %s\n", $target_upgrade_code); 1663} 1664 1665 1666 1667 1668############################################################### 1669# Setting the product version used in property table and 1670# upgrade table. Saving in global variable $msiproductversion 1671############################################################### 1672 1673sub set_msiproductversion 1674{ 1675 my ( $allvariables ) = @_; 1676 1677 my $productversion = $allvariables->{'PRODUCTVERSION'}; 1678 my $productmajor; 1679 my $productminor; # It must be 3 digits long 1680 1681 if (( $productversion =~ /^\s*\d+\s*$/ ) && ( $productversion > 255 )) { $productversion = $productversion%256; } 1682 1683 if ( $productversion =~ /^\s*(\d+)\.(\d+)\.(\d+)\s*$/ ) 1684 { 1685 my $productmicro = $3; 1686 $productmajor = $1; 1687 $productminor = $2; 1688 $productmicro = "0" . $productmicro while ( length ( $productminor . $productmicro ) < 3 ); 1689 $productminor .= $productmicro; 1690 } 1691 elsif ( $productversion =~ /^\s*(\d+)\.(\d+)\s*$/ ) 1692 { 1693 $productmajor = $1; 1694 $productminor = $2; 1695 } 1696 else 1697 { 1698 if (( $allvariables->{'PACKAGEVERSION'} ) && ( $allvariables->{'PACKAGEVERSION'} ne "" )) 1699 { 1700 if ( $allvariables->{'PACKAGEVERSION'} =~ /^\s*(\d+)\.(\d+)\.(\d+)\s*$/ ) { $productminor = $2; } 1701 } 1702 1703 $productmajor = $productversion; 1704 } 1705 $productminor .= "0" while ( length( $productminor ) < 3); 1706 $productversion = $productmajor . "\." . $productminor . "\." . $installer::globals::buildid; 1707 1708 $installer::globals::msiproductversion = $productversion; 1709 1710 # Setting $installer::globals::msimajorproductversion, to differ between old version in upgrade table 1711 1712 if ( $installer::globals::msiproductversion =~ /^\s*(\d+)\./ ) 1713 { 1714 my $major = $1; 1715 $installer::globals::msimajorproductversion = $major . "\.0\.0"; 1716 } 1717} 1718 1719################################################################################# 1720# Including the msi product version into the bootstrap.ini, Windows only 1721################################################################################# 1722 1723sub put_msiproductversion_into_bootstrapfile 1724{ 1725 my ($filesref) = @_; 1726 1727 for ( my $i = 0; $i <= $#{$filesref}; $i++ ) 1728 { 1729 my $onefile = ${$filesref}[$i]; 1730 1731 if ( $onefile->{'gid'} eq "gid_Profile_Version_Ini" ) 1732 { 1733 my $file = installer::files::read_file($onefile->{'sourcepath'}); 1734 1735 for ( my $j = 0; $j <= $#{$file}; $j++ ) 1736 { 1737 ${$file}[$j] =~ s/\<msiproductversion\>/$installer::globals::msiproductversion/; 1738 } 1739 1740 installer::files::save_file($onefile->{'sourcepath'}, $file); 1741 1742 last; 1743 } 1744 } 1745} 1746 1747#################################################################################### 1748# Updating the file Property.idt dynamically 1749# Content: 1750# Property Value 1751#################################################################################### 1752 1753sub update_reglocat_table 1754{ 1755 my ($basedir, $allvariables) = @_; 1756 1757 my $reglocatfilename = $basedir . $installer::globals::separator . "RegLocat.idt"; 1758 1759 # Only do something, if this file exists 1760 1761 if ( -f $reglocatfilename ) 1762 { 1763 my $reglocatfile = installer::files::read_file($reglocatfilename); 1764 1765 my $layername = ""; 1766 if ( $allvariables->{'REGISTRYLAYERNAME'} ) 1767 { 1768 $layername = $allvariables->{'REGISTRYLAYERNAME'}; 1769 } 1770 else 1771 { 1772 for ( my $i = 0; $i <= $#{$reglocatfile}; $i++ ) 1773 { 1774 if ( ${$reglocatfile}[$i] =~ /\bLAYERNAMETEMPLATE\b/ ) 1775 { 1776 installer::exiter::exit_program("ERROR: Variable \"REGISTRYLAYERNAME\" has to be defined", "update_reglocat_table"); 1777 } 1778 } 1779 } 1780 1781 if ( $layername ne "" ) 1782 { 1783 # Updating the layername in 1784 1785 for ( my $i = 0; $i <= $#{$reglocatfile}; $i++ ) 1786 { 1787 ${$reglocatfile}[$i] =~ s/\bLAYERNAMETEMPLATE\b/$layername/; 1788 } 1789 1790 # Saving the file 1791 installer::files::save_file($reglocatfilename ,$reglocatfile); 1792 my $infoline = "Updated idt file: $reglocatfilename\n"; 1793 $installer::logger::Lang->print($infoline); 1794 } 1795 } 1796} 1797 1798 1799 1800#################################################################################### 1801# Updating the file RemoveRe.idt dynamically (RemoveRegistry.idt) 1802# The name of the component has to be replaced. 1803#################################################################################### 1804 1805sub update_removere_table 1806{ 1807 my ($basedir) = @_; 1808 1809 my $removeregistryfilename = $basedir . $installer::globals::separator . "RemoveRe.idt"; 1810 1811 # Only do something, if this file exists 1812 1813 if ( -f $removeregistryfilename ) 1814 { 1815 my $removeregistryfile = installer::files::read_file($removeregistryfilename); 1816 1817 for ( my $i = 0; $i <= $#{$removeregistryfile}; $i++ ) 1818 { 1819 for ( my $i = 0; $i <= $#{$removeregistryfile}; $i++ ) 1820 { 1821 ${$removeregistryfile}[$i] =~ s/\bREGISTRYROOTCOMPONENT\b/$installer::globals::registryrootcomponent/; 1822 } 1823 } 1824 1825 # Saving the file 1826 installer::files::save_file($removeregistryfilename ,$removeregistryfile); 1827 my $infoline = "Updated idt file: $removeregistryfilename \n"; 1828 $installer::logger::Lang->print($infoline); 1829 } 1830} 1831 18321; 1833