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