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