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