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