diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:06:44 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:06:44 +0000 |
commit | ed5640d8b587fbcfed7dd7967f3de04b37a76f26 (patch) | |
tree | 7a5f7c6c9d02226d7471cb3cc8fbbf631b415303 /solenv/bin/modules/installer/windows/msiglobal.pm | |
parent | Initial commit. (diff) | |
download | libreoffice-upstream.tar.xz libreoffice-upstream.zip |
Adding upstream version 4:7.4.7.upstream/4%7.4.7upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r-- | solenv/bin/modules/installer/windows/msiglobal.pm | 1684 |
1 files changed, 1684 insertions, 0 deletions
diff --git a/solenv/bin/modules/installer/windows/msiglobal.pm b/solenv/bin/modules/installer/windows/msiglobal.pm new file mode 100644 index 000000000..f830c6eb0 --- /dev/null +++ b/solenv/bin/modules/installer/windows/msiglobal.pm @@ -0,0 +1,1684 @@ +# +# This file is part of the LibreOffice project. +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# +# This file incorporates work covered by the following license notice: +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed +# with this work for additional information regarding copyright +# ownership. The ASF licenses this file to you under the Apache +# License, Version 2.0 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.apache.org/licenses/LICENSE-2.0 . +# + +package installer::windows::msiglobal; + +use Cwd; +use Digest::MD5; +use installer::converter; +use installer::exiter; +use installer::files; +use installer::globals; +use installer::logger; +use installer::pathanalyzer; +use installer::remover; +use installer::scriptitems; +use installer::systemactions; +use installer::worker; +use installer::windows::idtglobal; +use installer::windows::language; + +########################################################################### +# Generating the header of the ddf file. +# The usage of ddf files is needed, because makecab.exe can only include +# one sourcefile into a cab file +########################################################################### + +sub write_ddf_file_header +{ + my ($ddffileref, $cabinetfile, $installdir) = @_; + + my $oneline; + + $oneline = ".Set CabinetName1=" . $cabinetfile . "\n"; + push(@{$ddffileref} ,$oneline); + $oneline = ".Set ReservePerCabinetSize=128\n"; # This reserves space for a digital signature. + push(@{$ddffileref} ,$oneline); + $oneline = ".Set MaxDiskSize=2147483648\n"; # This allows the .cab file to get a size of 2 GB. + push(@{$ddffileref} ,$oneline); + $oneline = ".Set CompressionType=LZX\n"; + push(@{$ddffileref} ,$oneline); + $oneline = ".Set Compress=ON\n"; + push(@{$ddffileref} ,$oneline); +# The window size for LZX compression +# CompressionMemory=15 | 16 | ... | 21 +# Reference: http://msdn.microsoft.com/en-us/library/bb417343.aspx + $oneline = ".Set CompressionMemory=$installer::globals::cabfilecompressionlevel\n"; + push(@{$ddffileref} ,$oneline); + $oneline = ".Set Cabinet=ON\n"; + push(@{$ddffileref} ,$oneline); + $oneline = ".Set DiskDirectoryTemplate=" . $installdir . "\n"; + push(@{$ddffileref} ,$oneline); +} + +########################################################################## +# Lines in ddf files must not contain more than 256 characters +########################################################################## + +sub check_ddf_file +{ + my ( $ddffile, $ddffilename ) = @_; + + my $maxlength = 0; + my $maxline = 0; + my $linelength = 0; + my $linenumber = 0; + + for ( my $i = 0; $i <= $#{$ddffile}; $i++ ) + { + my $oneline = ${$ddffile}[$i]; + + $linelength = length($oneline); + $linenumber = $i + 1; + + if ( $linelength > 256 ) + { + installer::exiter::exit_program("ERROR \"$ddffilename\" line $linenumber: Lines in ddf files must not contain more than 256 characters!", "check_ddf_file"); + } + + if ( $linelength > $maxlength ) + { + $maxlength = $linelength; + $maxline = $linenumber; + } + } + + my $infoline = "Check of ddf file \"$ddffilename\": Maximum length \"$maxlength\" in line \"$maxline\" (allowed line length: 256 characters)\n"; + push(@installer::globals::logfileinfo, $infoline); +} + +########################################################################## +# Lines in ddf files must not be longer than 256 characters. +# Therefore it can be useful to use relative paths. Then it is +# necessary to change into temp directory before calling +# makecab.exe. +########################################################################## + +sub make_relative_ddf_path +{ + my ( $sourcepath ) = @_; + + my $windowstemppath = $installer::globals::temppath; + + if ( $^O =~ /cygwin/i ) + { + $windowstemppath = $installer::globals::cyg_temppath; + } + + $sourcepath =~ s/\Q$windowstemppath\E//; + $sourcepath =~ s/^[\\\/]//; + + return $sourcepath; +} + +########################################################################## +# Returning the order of the sequences in the files array. +########################################################################## + +sub get_sequenceorder +{ + my ($filesref) = @_; + + my %order = (); + + for ( my $i = 0; $i <= $#{$filesref}; $i++ ) + { + my $onefile = ${$filesref}[$i]; + if ( ! $onefile->{'assignedsequencenumber'} ) { installer::exiter::exit_program("ERROR: No sequence number assigned to $onefile->{'gid'} ($onefile->{'uniquename'})!", "get_sequenceorder"); } + $order{$onefile->{'assignedsequencenumber'}} = $i; + } + + return \%order; +} + +########################################################################## +# Generation the list, in which the source of the files is connected +# with the cabinet destination file. Because more than one file needs +# to be included into a cab file, this has to be done via ddf files. +########################################################################## + +sub generate_cab_file_list +{ + my ($filesref, $installdir, $ddfdir, $allvariables) = @_; + + my @cabfilelist = (); + + installer::logger::include_header_into_logfile("Generating ddf files"); + + installer::logger::include_timestamp_into_logfile("Performance Info: ddf file generation start"); + + if ( $^O =~ /cygwin/i ) { installer::worker::generate_cygwin_paths($filesref); } + + if (( $installer::globals::fix_number_of_cab_files ) && ( $installer::globals::updatedatabase )) + { + my $sequenceorder = get_sequenceorder($filesref); + + my $counter = 1; + + while ( ( exists($sequenceorder->{$counter}) ) || ( exists($installer::globals::allmergemodulefilesequences{$counter}) ) ) # Taking care of files from merge modules + { +# if ( exists($installer::globals::allmergemodulefilesequences{$counter}) ) +# { +# # Skipping this sequence, it is not included in $filesref, because it is assigned to a file from a merge module.\n"; +# $counter++; +# next; +# } + + my $onefile = ${$filesref}[$sequenceorder->{$counter}]; + $counter++; + + my $cabinetfile = $onefile->{'cabinet'}; + my $sourcepath = $onefile->{'sourcepath'}; + if ( $^O =~ /cygwin/i ) { $sourcepath = $onefile->{'cyg_sourcepath'}; } + my $uniquename = $onefile->{'uniquename'}; + + my $styles = ""; + my $doinclude = 1; + if ( $onefile->{'Styles'} ) { $styles = $onefile->{'Styles'}; }; + if ( $styles =~ /\bDONT_PACK\b/ ) { $doinclude = 0; } + + # to avoid lines with more than 256 characters, it can be useful to use relative paths + $sourcepath = make_relative_ddf_path($sourcepath); + + my @ddffile = (); + + write_ddf_file_header(\@ddffile, $cabinetfile, $installdir); + + my $ddfline = "\"" . $sourcepath . "\" \"" . $uniquename . "\"\n"; + if ( $doinclude ) { push(@ddffile, $ddfline); } + + my $nextfile = ""; + if ( ${$filesref}[$sequenceorder->{$counter}] ) { $nextfile = ${$filesref}[$sequenceorder->{$counter}]; } + + my $nextcabinetfile = ""; + + if ( $nextfile->{'cabinet'} ) { $nextcabinetfile = $nextfile->{'cabinet'}; } + + while ( $nextcabinetfile eq $cabinetfile ) + { + $sourcepath = $nextfile->{'sourcepath'}; + if ( $^O =~ /cygwin/i ) { $sourcepath = $nextfile->{'cyg_sourcepath'}; } + # to avoid lines with more than 256 characters, it can be useful to use relative paths + $sourcepath = make_relative_ddf_path($sourcepath); + $uniquename = $nextfile->{'uniquename'}; + my $localdoinclude = 1; + my $nextfilestyles = ""; + if ( $nextfile->{'Styles'} ) { $nextfilestyles = $nextfile->{'Styles'}; } + if ( $nextfilestyles =~ /\bDONT_PACK\b/ ) { $localdoinclude = 0; } + $ddfline = "\"" . $sourcepath . "\" \"" . $uniquename . "\"\n"; + if ( $localdoinclude ) { push(@ddffile, $ddfline); } + $counter++; + $nextfile = ""; + $nextcabinetfile = "_lastfile_"; + if (( exists($sequenceorder->{$counter}) ) && ( ${$filesref}[$sequenceorder->{$counter}] )) + { + $nextfile = ${$filesref}[$sequenceorder->{$counter}]; + $nextcabinetfile = $nextfile->{'cabinet'}; + } + } + + # creating the DDF file + + my $ddffilename = $cabinetfile; + $ddffilename =~ s/.cab/.ddf/; + $ddfdir =~ s/\Q$installer::globals::separator\E\s*$//; + $ddffilename = $ddfdir . $installer::globals::separator . $ddffilename; + + installer::files::save_file($ddffilename ,\@ddffile); + my $infoline = "Created ddf file: $ddffilename\n"; + push(@installer::globals::logfileinfo, $infoline); + + # lines in ddf files must not be longer than 256 characters + check_ddf_file(\@ddffile, $ddffilename); + + # Writing the makecab system call + + my $oneline = "makecab.exe /V3 /F " . $ddffilename . " 2\>\&1 |" . "\n"; + + push(@cabfilelist, $oneline); + + # collecting all ddf files + push(@installer::globals::allddffiles, $ddffilename); + } + } + elsif ( $installer::globals::fix_number_of_cab_files ) + { + for ( my $i = 0; $i <= $#{$filesref}; $i++ ) + { + my $onefile = ${$filesref}[$i]; + my $cabinetfile = $onefile->{'cabinet'}; + my $sourcepath = $onefile->{'sourcepath'}; + if ( $^O =~ /cygwin/i ) { $sourcepath = $onefile->{'cyg_sourcepath'}; } + my $uniquename = $onefile->{'uniquename'}; + + my $styles = ""; + my $doinclude = 1; + if ( $onefile->{'Styles'} ) { $styles = $onefile->{'Styles'}; }; + if ( $styles =~ /\bDONT_PACK\b/ ) { $doinclude = 0; } + + + # to avoid lines with more than 256 characters, it can be useful to use relative paths + $sourcepath = make_relative_ddf_path($sourcepath); + + # all files with the same cabinetfile are directly behind each other in the files collector + + my @ddffile = (); + + write_ddf_file_header(\@ddffile, $cabinetfile, $installdir); + + my $ddfline = "\"" . $sourcepath . "\" \"" . $uniquename . "\"\n"; + if ( $doinclude ) { push(@ddffile, $ddfline); } + + my $nextfile = ${$filesref}[$i+1]; + my $nextcabinetfile = ""; + + if ( $nextfile->{'cabinet'} ) { $nextcabinetfile = $nextfile->{'cabinet'}; } + + while ( $nextcabinetfile eq $cabinetfile ) + { + $sourcepath = $nextfile->{'sourcepath'}; + if ( $^O =~ /cygwin/i ) { $sourcepath = $nextfile->{'cyg_sourcepath'}; } + # to avoid lines with more than 256 characters, it can be useful to use relative paths + $sourcepath = make_relative_ddf_path($sourcepath); + $uniquename = $nextfile->{'uniquename'}; + my $localdoinclude = 1; + my $nextfilestyles = ""; + if ( $nextfile->{'Styles'} ) { $nextfilestyles = $nextfile->{'Styles'}; } + if ( $nextfilestyles =~ /\bDONT_PACK\b/ ) { $localdoinclude = 0; } + $ddfline = "\"" . $sourcepath . "\" \"" . $uniquename . "\"\n"; + if ( $localdoinclude ) { push(@ddffile, $ddfline); } + $i++; # increasing the counter! + $nextfile = ${$filesref}[$i+1]; + if ( $nextfile ) { $nextcabinetfile = $nextfile->{'cabinet'}; } + else { $nextcabinetfile = "_lastfile_"; } + } + + # creating the DDF file + + my $ddffilename = $cabinetfile; + $ddffilename =~ s/.cab/.ddf/; + $ddfdir =~ s/\Q$installer::globals::separator\E\s*$//; + $ddffilename = $ddfdir . $installer::globals::separator . $ddffilename; + + installer::files::save_file($ddffilename ,\@ddffile); + my $infoline = "Created ddf file: $ddffilename\n"; + push(@installer::globals::logfileinfo, $infoline); + + # lines in ddf files must not be longer than 256 characters + check_ddf_file(\@ddffile, $ddffilename); + + # Writing the makecab system call + + my $oneline = "makecab.exe /V3 /F " . $ddffilename . " 2\>\&1 |" . "\n"; + + push(@cabfilelist, $oneline); + + # collecting all ddf files + push(@installer::globals::allddffiles, $ddffilename); + } + } + else + { + installer::exiter::exit_program("ERROR: No cab file specification in globals.pm !", "generate_cab_file_list"); + } + + installer::logger::include_timestamp_into_logfile("Performance Info: ddf file generation end"); + + return \@cabfilelist; # contains all system calls for packaging process +} + +######################################################################## +# For update and patch reasons the pack order needs to be saved. +# The pack order is saved in the ddf files; the names and locations +# of the ddf files are saved in @installer::globals::allddffiles. +# The outputfile "packorder.txt" can be saved in +# $installer::globals::infodirectory . +######################################################################## + +sub save_packorder +{ + installer::logger::include_header_into_logfile("Saving pack order"); + + installer::logger::include_timestamp_into_logfile("Performance Info: saving pack order start"); + + my $packorderfilename = "packorder.txt"; + $packorderfilename = $installer::globals::infodirectory . $installer::globals::separator . $packorderfilename; + + my @packorder = (); + + my $headerline = "\# Syntax\: Filetable_Sequence Cabinetfilename Physical_FileName Unique_FileName\n\n"; + push(@packorder, $headerline); + + for ( my $i = 0; $i <= $#installer::globals::allddffiles; $i++ ) + { + my $ddffilename = $installer::globals::allddffiles[$i]; + my $ddffile = installer::files::read_file($ddffilename); + my $cabinetfile = ""; + + for ( my $j = 0; $j <= $#{$ddffile}; $j++ ) + { + my $oneline = ${$ddffile}[$j]; + + # Getting the Cabinet file name + + if ( $oneline =~ /^\s*\.Set\s+CabinetName.*\=(.*?)\s*$/ ) { $cabinetfile = $1; } + if ( $oneline =~ /^\s*\.Set\s+/ ) { next; } + + if ( $oneline =~ /^\s*\"(.*?)\"\s+\"(.*?)\"\s*$/ ) + { + my $sourcefile = $1; + my $uniquefilename = $2; + + installer::pathanalyzer::make_absolute_filename_to_relative_filename(\$sourcefile); + + # Using the hash created in create_files_table for performance reasons to get the sequence number + my $filesequence = ""; + if ( exists($installer::globals::uniquefilenamesequence{$uniquefilename}) ) { $filesequence = $installer::globals::uniquefilenamesequence{$uniquefilename}; } + else { installer::exiter::exit_program("ERROR: No sequence number value for $uniquefilename !", "save_packorder"); } + + my $line = $filesequence . "\t" . $cabinetfile . "\t" . $sourcefile . "\t" . $uniquefilename . "\n"; + push(@packorder, $line); + } + } + } + + installer::files::save_file($packorderfilename ,\@packorder); + + installer::logger::include_timestamp_into_logfile("Performance Info: saving pack order end"); +} + +################################################################# +# Returning the name of the msi database +################################################################# + +sub get_msidatabasename +{ + my ($allvariableshashref, $language) = @_; + + my $databasename = $allvariableshashref->{'PRODUCTNAME'} . $allvariableshashref->{'PRODUCTVERSION'}; + $databasename = lc($databasename); + $databasename =~ s/\.//g; + $databasename =~ s/\-//g; + $databasename =~ s/\s//g; + + # possibility to overwrite the name with variable DATABASENAME + if ( $allvariableshashref->{'DATABASENAME'} ) + { + $databasename = $allvariableshashref->{'DATABASENAME'}; + } + + if ( $language ) + { + if (!($language eq "")) + { + $databasename .= "_$language"; + } + } + + $databasename .= ".msi"; + + return $databasename; +} + +################################################################# +# Creating the msi database +# This works only on Windows +################################################################# + +sub create_msi_database +{ + my ($idtdirbase ,$msifilename) = @_; + + # -f : path containing the idt files + # -d : msi database, including path + # -c : create database + # -i : include the following tables ("*" includes all available tables) + + my $msidb = "msidb.exe"; # Has to be in the path + my $extraslash = ""; # Has to be set for non-ActiveState perl + + installer::logger::include_header_into_logfile("Creating msi database"); + + $idtdirbase = installer::converter::make_path_conform($idtdirbase); + + $msifilename = installer::converter::make_path_conform($msifilename); + + if ( $^O =~ /cygwin/i ) { + # msidb.exe really wants backslashes. (And double escaping because system() expands the string.) + $idtdirbase =~ s/\//\\\\/g; + $msifilename =~ s/\//\\\\/g; + $extraslash = "\\"; + } + if ( $^O =~ /linux/i ) { + $extraslash = "\\"; + } + my $systemcall = $msidb . " -f " . $idtdirbase . " -d " . $msifilename . " -c " . "-i " . $extraslash . "*"; + + my $returnvalue = system($systemcall); + + my $infoline = "Systemcall: $systemcall\n"; + push( @installer::globals::logfileinfo, $infoline); + + if ($returnvalue) + { + $infoline = "ERROR: Could not execute $msidb!\n"; + push( @installer::globals::logfileinfo, $infoline); + } + else + { + $infoline = "Success: Executed $msidb successfully!\n"; + push( @installer::globals::logfileinfo, $infoline); + } +} + +################################################################# +# Returning the msi version for the Summary Information Stream +################################################################# + +sub get_msiversion_for_sis +{ + my $msiversion = "200"; + return $msiversion; +} + +################################################################# +# Returning the word count for the Summary Information Stream +################################################################# + +sub get_wordcount_for_sis +{ + my $wordcount = "0"; + return $wordcount; +} + +################################################################# +# Returning the template for the Summary Information Stream +################################################################# + +sub get_template_for_sis +{ + my ( $language, $allvariables ) = @_; + + my $windowslanguage = installer::windows::language::get_windows_language($language); + + my $architecture = "Intel"; + + if (( $allvariables->{'64BITPRODUCT'} ) && ( $allvariables->{'64BITPRODUCT'} == 1 )) { $architecture = "x64"; } + + my $value = "\"" . $architecture . ";" . $windowslanguage; # adding the Windows language + + $value = $value . "\""; # adding ending '"' + + return $value ; +} + +################################################################# +# Returning the PackageCode for the Summary Information Stream +################################################################# + +sub get_packagecode_for_sis +{ + # always generating a new package code for each package + + my $guidref = get_guid_list(1, 1); # only one GUID shall be generated + + ${$guidref}[0] =~ s/\s*$//; # removing ending spaces + + my $guid = "\{" . ${$guidref}[0] . "\}"; + + my $infoline = "PackageCode: $guid\n"; + push( @installer::globals::logfileinfo, $infoline); + + return $guid; +} + +################################################################# +# Returning the author for the Summary Information Stream +################################################################# + +sub get_author_for_sis +{ + my $author = $installer::globals::longmanufacturer; + + $author = "\"" . $author . "\""; + + return $author; +} + +################################################################# +# Returning the subject for the Summary Information Stream +################################################################# + +sub get_subject_for_sis +{ + my ( $allvariableshashref ) = @_; + + my $subject = $allvariableshashref->{'PRODUCTNAME'} . " " . $allvariableshashref->{'PRODUCTVERSION'}; + + $subject = "\"" . $subject . "\""; + + return $subject; +} + +###################################################################### +# Returning the security for the Summary Information Stream +###################################################################### + +sub get_security_for_sis +{ + my $security = "0"; + return $security; +} + +################################################################# +# Writing the Summary information stream into the msi database +# This works only on Windows +################################################################# + +sub write_summary_into_msi_database +{ + my ($msifilename, $language, $languagefile, $allvariableshashref) = @_; + + # -g : required msi version + # -c : codepage + # -p : template + + installer::logger::include_header_into_logfile("Writing summary information stream"); + + my $msiinfo = "msiinfo.exe"; # Has to be in the path + + my $msiversion = get_msiversion_for_sis(); + my $codepage = 0; # PID_CODEPAGE summary property in a signed short, therefore it is impossible to set 65001 here. + my $template = get_template_for_sis($language, $allvariableshashref); + my $guid = get_packagecode_for_sis(); + my $title = "\"Installation database\""; + my $author = get_author_for_sis(); + my $subject = get_subject_for_sis($allvariableshashref); + my $comment = "\"" . $allvariableshashref->{'PRODUCTNAME'} ."\""; + my $keywords = "\"Install,MSI\""; + my $appname = "\"Windows Installer\""; + my $security = get_security_for_sis(); + my $wordcount = get_wordcount_for_sis(); + + $msifilename = installer::converter::make_path_conform($msifilename); + + my $systemcall = $msiinfo . " " . $msifilename . " -g " . $msiversion . " -c " . $codepage + . " -p " . $template . " -v " . $guid . " -t " . $title . " -a " . $author + . " -j " . $subject . " -o " . $comment . " -k " . $keywords . " -n " . $appname + . " -u " . $security . " -w " . $wordcount; + + my $returnvalue = system($systemcall); + + my $infoline = "Systemcall: $systemcall\n"; + push( @installer::globals::logfileinfo, $infoline); + + if ($returnvalue) + { + $infoline = "ERROR: Could not execute $systemcall (return $returnvalue)\n"; + push( @installer::globals::logfileinfo, $infoline); + } + else + { + $infoline = "Success: Executed $msiinfo successfully!\n"; + push( @installer::globals::logfileinfo, $infoline); + } +} + +######################################################################### +# For more than one language in the installation set: +# Use one database and create Transformations for all other languages +######################################################################### + +sub create_transforms +{ + my ($languagesarray, $defaultlanguage, $installdir, $allvariableshashref) = @_; + + installer::logger::include_header_into_logfile("Creating Transforms"); + + my $cscript = "cscript.exe"; # Has to be in the path + my $msitran = "msitran.exe"; # Has to be in the path + my $msidb = "msidb.exe"; # Has to be in the path + my $wilangid = $ENV{WINDOWS_SDK_WILANGID}; + + my $from = cwd(); + + my $templatevalue = "1033"; + + $installdir = installer::converter::make_path_conform($installdir); + + # Syntax for creating a transformation + # msitran.exe -g <baseDB> <referenceDB> <transformfile> [<errorhandling>} + + my $basedbname = get_msidatabasename($allvariableshashref, $defaultlanguage); + $basedbname = $installdir . $installer::globals::separator . $basedbname; + + my $errorhandling = "f"; # Suppress "change codepage" error + + # Iterating over all files + + foreach ( @{$languagesarray} ) + { + my $onelanguage = $_; + + if ( $onelanguage eq $defaultlanguage ) { next; } + + my $referencedbname = get_msidatabasename($allvariableshashref, $onelanguage); + $referencedbname = $installdir . $installer::globals::separator . $referencedbname; + + my $windowslanguage = installer::windows::language::get_windows_language($onelanguage); + my $transformfile = $installdir . $installer::globals::separator . $windowslanguage; + + my $systemcall = $msitran . " " . " -g " . $basedbname . " " . $referencedbname . " " . $transformfile . " " . $errorhandling; + + my $returnvalue = system($systemcall); + + my $infoline = "Systemcall: $systemcall\n"; + push( @installer::globals::logfileinfo, $infoline); + + # Problem: msitran.exe in version 4.0 always returns "1", even if no failure occurred. + # Therefore it has to be checked, if this is version 4.0. If yes, if the mst file + # exists and if it is larger than 0 bytes. If this is true, then no error occurred. + # File Version of msitran.exe: 4.0.6000.16384 has checksum: "b66190a70145a57773ec769e16777b29". + # Same for msitran.exe from wntmsci12: "aa25d3445b94ffde8ef0c1efb77a56b8" + + if ($returnvalue) + { + $infoline = "WARNING: Returnvalue of $msitran is not 0. Checking version of $msitran!\n"; + push( @installer::globals::logfileinfo, $infoline); + + open(FILE, "<$installer::globals::msitranpath") or die "ERROR: Can't open $installer::globals::msitranpath for creating file hash"; + binmode(FILE); + my $digest = Digest::MD5->new->addfile(*FILE)->hexdigest; + close(FILE); + + my @problemchecksums = ("b66190a70145a57773ec769e16777b29", "aa25d3445b94ffde8ef0c1efb77a56b8", "748206e54fc93efe6a1aaa9d491f3ad1"); + my $isproblemchecksum = 0; + + foreach my $problemchecksum ( @problemchecksums ) + { + $infoline = "Checksum of problematic MsiTran.exe: $problemchecksum\n"; + push( @installer::globals::logfileinfo, $infoline); + $infoline = "Checksum of used MsiTran.exe: $digest\n"; + push( @installer::globals::logfileinfo, $infoline); + if ( $digest eq $problemchecksum ) { $isproblemchecksum = 1; } + } + + if ( $isproblemchecksum ) + { + # Check existence of mst + if ( -f $transformfile ) + { + $infoline = "File $transformfile exists.\n"; + push( @installer::globals::logfileinfo, $infoline); + my $filesize = ( -s $transformfile ); + $infoline = "Size of $transformfile: $filesize\n"; + push( @installer::globals::logfileinfo, $infoline); + + if ( $filesize > 0 ) + { + $infoline = "Info: Returnvalue $returnvalue of $msitran is no problem :-) .\n"; + push( @installer::globals::logfileinfo, $infoline); + $returnvalue = 0; # reset the error + } + else + { + $infoline = "Filesize indicates that an error occurred.\n"; + push( @installer::globals::logfileinfo, $infoline); + } + } + else + { + $infoline = "File $transformfile does not exist -> An error occurred.\n"; + push( @installer::globals::logfileinfo, $infoline); + } + } + else + { + $infoline = "This is not a problematic version of msitran.exe. Therefore the error is not caused by problematic msitran.exe.\n"; + push( @installer::globals::logfileinfo, $infoline); + } + } + + if ($returnvalue) + { + $infoline = "ERROR: Could not execute $msitran!\n"; + push( @installer::globals::logfileinfo, $infoline); + } + else + { + $infoline = "Success: Executed $msitran successfully!\n"; + push( @installer::globals::logfileinfo, $infoline); + } + + # The reference database can be deleted + + my $result = unlink($referencedbname); + # $result contains the number of deleted files + + if ( $result == 0 ) + { + $infoline = "ERROR while processing language $onelanguage: Could not remove file $referencedbname!\n"; + push( @installer::globals::logfileinfo, $infoline); + installer::exiter::exit_program($infoline, "create_transforms"); + } + + chdir($installdir); + $systemcall = $msidb . " " . " -d " . $basedbname . " -r " . $windowslanguage; + system($systemcall); + # fdo#46181 - zh-HK and zh-MO should have fallen back to zh-TW not to zh-CN + # we need to hack zh-HK and zh-MO LCIDs directly into the MSI + if($windowslanguage eq '1028') + { + rename 1028,3076; + $systemcall = $msidb . " " . " -d " . $basedbname . " -r " . 3076; + system($systemcall); + rename 3076,5124; + $systemcall = $msidb . " " . " -d " . $basedbname . " -r " . 5124; + system($systemcall); + $templatevalue = $templatevalue . "," . 3076 . "," . 5124; + rename 5124,1028; + } + chdir($from); + unlink($transformfile); + + $infoline = "Systemcall: $systemcall\n"; + push( @installer::globals::logfileinfo, $infoline); + + if ( $windowslanguage ne '1033') + { + $templatevalue = $templatevalue . "," . $windowslanguage; + } + } + + $systemcall = "TEMP=$ENV{'TMPDIR'} $cscript \"$wilangid\" $basedbname Package $templatevalue"; + + $returnvalue = system($systemcall); + + $infoline = "Systemcall: $systemcall\n"; + push( @installer::globals::logfileinfo, $infoline); + + if ($returnvalue) + { + $infoline = "ERROR: $returnvalue from $systemcall\n"; + push( @installer::globals::logfileinfo, $infoline); + } + else + { + $infoline = "Success: Executed WiLangId.vbs successfully!\n"; + push( @installer::globals::logfileinfo, $infoline); + } +} + +######################################################################### +# The default language msi database does not need to contain +# the language in the database name. Therefore the file +# is renamed. Example: "openofficeorg20_01.msi" to "openofficeorg20.msi" +######################################################################### + +sub rename_msi_database_in_installset +{ + my ($defaultlanguage, $installdir, $allvariableshashref) = @_; + + installer::logger::include_header_into_logfile("Renaming msi database"); + + my $olddatabasename = get_msidatabasename($allvariableshashref, $defaultlanguage); + $olddatabasename = $installdir . $installer::globals::separator . $olddatabasename; + + my $newdatabasename = get_msidatabasename($allvariableshashref); + + $installer::globals::shortmsidatabasename = $newdatabasename; + + $newdatabasename = $installdir . $installer::globals::separator . $newdatabasename; + + installer::systemactions::rename_one_file($olddatabasename, $newdatabasename); + + $installer::globals::msidatabasename = $newdatabasename; +} + +################################################################# +# Copying MergeModules for the Windows installer into the +# installation set. The list of MergeModules is located +# in %installer::globals::copy_msm_files +################################################################# + +sub copy_merge_modules_into_installset +{ + my ($installdir) = @_; + + installer::logger::include_header_into_logfile("Copying Merge files into installation set"); + + my $cabfile; + foreach $cabfile ( keys %installer::globals::copy_msm_files ) + { + my $sourcefile = $installer::globals::copy_msm_files{$cabfile}; + my $destfile = $installdir . $installer::globals::separator . $cabfile; + + installer::systemactions::copy_one_file($sourcefile, $destfile); + } +} + +################################################################# +# Getting a list of GUID using uuidgen.exe. +# This works only on Windows +################################################################# + +sub get_guid_list +{ + my ($number, $log) = @_; + + if ( $log ) { installer::logger::include_header_into_logfile("Generating $number GUID"); } + + my $uuidgen = $ENV{'UUIDGEN'}; # Has to be in the path + + # "-c" for uppercase output + + my $systemcall = "$uuidgen -n$number |"; + open (UUIDGEN, "$systemcall" ) or die("uuidgen is missing."); + my @uuidlist = <UUIDGEN>; + close (UUIDGEN); + + my $infoline = "Systemcall: $systemcall\n"; + if ( $log ) { push( @installer::globals::logfileinfo, $infoline); } + + my $comparenumber = $#uuidlist + 1; + + if ( $comparenumber == $number ) + { + $infoline = "Success: Executed $uuidgen successfully!\n"; + if ( $log ) { push( @installer::globals::logfileinfo, $infoline); } + } + else + { + $infoline = "ERROR: Could not execute $uuidgen successfully!\n"; + if ( $log ) { push( @installer::globals::logfileinfo, $infoline); } + } + + # uppercase, no longer "-c", because this is only supported in uuidgen.exe v.1.01 + for ( my $i = 0; $i <= $#uuidlist; $i++ ) { $uuidlist[$i] = uc($uuidlist[$i]); } + + return \@uuidlist; +} + +################################################################# +# Calculating a GUID with a string using md5. +################################################################# + +sub calculate_guid +{ + my ( $string ) = @_; + + my $guid = ""; + + my $md5 = Digest::MD5->new; + $md5->add($string); + my $digest = $md5->hexdigest; + $digest = uc($digest); + + my ($first, $second, $third, $fourth, $fifth) = unpack ('A8 A4 A4 A4 A12', $digest); + $guid = "$first-$second-$third-$fourth-$fifth"; + + return $guid; +} + +################################################################# +# Calculating a ID with a string using md5 (very fast). +################################################################# + +sub calculate_id +{ + my ( $string, $length ) = @_; + + my $id = ""; + + my $md5 = Digest::MD5->new; + $md5->add($string); + my $digest = lc($md5->hexdigest); + $id = substr($digest, 0, $length); + + return $id; +} + +################################################################# +# Filling real component GUID into the component table. +# This works only on Windows +################################################################# + +sub set_uuid_into_component_table +{ + my ($idtdirbase, $allvariables) = @_; + + my $componenttablename = $idtdirbase . $installer::globals::separator . "Componen.idt"; + + my $componenttable = installer::files::read_file($componenttablename); + + # For update and patch reasons (small update) the GUID of an existing component must not change! + # The collection of component GUIDs is saved in the directory $installer::globals::idttemplatepath in the file "components.txt" + + my $infoline = ""; + my $counter = 0; + + for ( my $i = 3; $i <= $#{$componenttable}; $i++ ) # ignoring the first three lines + { + my $oneline = ${$componenttable}[$i]; + my $componentname = ""; + if ( $oneline =~ /^\s*(\S+?)\t/ ) { $componentname = $1; } + + my $uuid = ""; + + if ( exists($installer::globals::calculated_component_guids{$componentname})) + { + $uuid = $installer::globals::calculated_component_guids{$componentname}; + } + else + { + # Calculating new GUID with the help of the component name. + + if ( ! exists($allvariables->{'PRODUCTVERSION'}) ) { installer::exiter::exit_program("ERROR: Could not find variable \"PRODUCTVERSION\" (required value for GUID creation)!", "set_uuid_into_component_table"); } + my $sourcestring = $componentname . "_" . $allvariables->{'PRODUCTVERSION'}; + $uuid = calculate_guid($sourcestring); + $counter++; + + # checking, if there is a conflict with an already created guid + if ( exists($installer::globals::allcalculated_guids{$uuid}) ) { installer::exiter::exit_program("ERROR: \"$uuid\" was already created before!", "set_uuid_into_component_table"); } + $installer::globals::allcalculated_guids{$uuid} = 1; + $installer::globals::calculated_component_guids{$componentname} = $uuid; + } + + ${$componenttable}[$i] =~ s/COMPONENTGUID/$uuid/; + } + + installer::files::save_file($componenttablename, $componenttable); +} + +######################################################################### +# Adding final 64 properties into msi database, if required. +# RegLocator : +16 in type column to search in 64 bit registry. +# All conditions: "VersionNT" -> "VersionNT64" (several tables). +# DrLocator: "SystemFolder" -> "System64Folder" +# Already done: "+256" in Attributes column of table "Component". +# Still following: Setting "x64" instead of "Intel" in Summary +# Information Stream of msi database in "get_template_for_sis". +######################################################################### + +sub prepare_64bit_database +{ + my ($basedir, $allvariables) = @_; + + my $infoline = ""; + + if (( $allvariables->{'64BITPRODUCT'} ) && ( $allvariables->{'64BITPRODUCT'} == 1 )) + { + # 1. Beginning with table "RegLocat.idt". Adding "16" to the type. + + my $reglocatfile = ""; + my $reglocatfilename = $basedir . $installer::globals::separator . "RegLocat.idt"; + + if ( -f $reglocatfilename ) + { + my $saving_required = 0; + $reglocatfile = installer::files::read_file($reglocatfilename); + + for ( my $i = 3; $i <= $#{$reglocatfile}; $i++ ) # ignoring the first three lines + { + my $oneline = ${$reglocatfile}[$i]; + + if ( $oneline =~ /^\s*\#/ ) { next; } # this is a comment line + if ( $oneline =~ /^\s*$/ ) { next; } + + if ( $oneline =~ /^\s*(.*?)\t(.*?)\t(.*?)\t(.*?)\t(\d+)\s*$/ ) + { + # Syntax: Signature_ Root Key Name Type + my $sig = $1; + my $root = $2; + my $key = $3; + my $name = $4; + my $type = $5; + + $type = $type + 16; + + my $newline = $sig . "\t" . $root . "\t" . $key . "\t" . $name . "\t" . $type . "\n"; + ${$reglocatfile}[$i] = $newline; + + $saving_required = 1; + } + } + + if ( $saving_required ) + { + # Saving the files + installer::files::save_file($reglocatfilename ,$reglocatfile); + $infoline = "Making idt file 64 bit conform: $reglocatfilename\n"; + push(@installer::globals::logfileinfo, $infoline); + } + } + + # 2. Replacing all occurrences of "VersionNT" by "VersionNT64" + + my @versionnt_files = ("Componen.idt", "InstallE.idt", "InstallU.idt", "LaunchCo.idt"); + + foreach my $onefile ( @versionnt_files ) + { + my $fullfilename = $basedir . $installer::globals::separator . $onefile; + + if ( -f $fullfilename ) + { + my $saving_required = 0; + $filecontent = installer::files::read_file($fullfilename); + + for ( my $i = 3; $i <= $#{$filecontent}; $i++ ) # ignoring the first three lines + { + my $oneline = ${$filecontent}[$i]; + + if ( $oneline =~ /\bVersionNT\b/ ) + { + ${$filecontent}[$i] =~ s/\bVersionNT\b/VersionNT64/g; + $saving_required = 1; + } + } + + if ( $saving_required ) + { + # Saving the files + installer::files::save_file($fullfilename ,$filecontent); + $infoline = "Making idt file 64 bit conform: $fullfilename\n"; + push(@installer::globals::logfileinfo, $infoline); + } + } + } + + # 3. Replacing all occurrences of "SystemFolder" by "System64Folder" in "DrLocato.idt" + + my $drlocatofilename = $basedir . $installer::globals::separator . "DrLocato.idt"; + if ( -f $drlocatofilename ) + { + my $saving_required = 0; + my $drlocatofile = installer::files::read_file($drlocatofilename); + + for ( my $i = 3; $i <= $#{$drlocatofile}; $i++ ) # ignoring the first three lines + { + my $oneline = ${$drlocatofile}[$i]; + + if ( $oneline =~ /\bSystemFolder\b/ ) + { + ${$drlocatofile}[$i] =~ s/\bSystemFolder\b/System64Folder/g; + $saving_required = 1; + } + } + + if ( $saving_required ) + { + # Saving the files + installer::files::save_file($drlocatofilename ,$drlocatofile); + $infoline = "Making idt file 64 bit conform: $drlocatofilename\n"; + push(@installer::globals::logfileinfo, $infoline); + } + } + } + +} + +################################################################# +# Include all cab files into the msi database. +# This works only on Windows +################################################################# + +sub include_cabs_into_msi +{ + my ($installdir) = @_; + + installer::logger::include_header_into_logfile("Including cabs into msi database"); + + my $from = cwd(); + my $to = $installdir; + + chdir($to); + + my $infoline = "Changing into directory: $to"; + push( @installer::globals::logfileinfo, $infoline); + + my $msidb = "msidb.exe"; # Has to be in the path + my $extraslash = ""; # Has to be set for non-ActiveState perl + + my $msifilename = $installer::globals::msidatabasename; + + $msifilename = installer::converter::make_path_conform($msifilename); + + # msidb.exe really wants backslashes. (And double escaping because system() expands the string.) + $msifilename =~ s/\//\\\\/g; + $extraslash = "\\"; + + my $allcabfiles = installer::systemactions::find_file_with_file_extension("cab", $installdir); + + for ( my $i = 0; $i <= $#{$allcabfiles}; $i++ ) + { + my $systemcall = $msidb . " -d " . $msifilename . " -a " . ${$allcabfiles}[$i]; + + my $returnvalue = system($systemcall); + + $infoline = "Systemcall: $systemcall\n"; + push( @installer::globals::logfileinfo, $infoline); + + if ($returnvalue) + { + $infoline = "ERROR: Could not execute $systemcall !\n"; + push( @installer::globals::logfileinfo, $infoline); + } + else + { + $infoline = "Success: Executed $systemcall successfully!\n"; + push( @installer::globals::logfileinfo, $infoline); + } + + # deleting the cab file + + unlink(${$allcabfiles}[$i]); + + $infoline = "Deleted cab file: ${$allcabfiles}[$i]\n"; + push( @installer::globals::logfileinfo, $infoline); + } + + $infoline = "Changing back into directory: $from"; + push( @installer::globals::logfileinfo, $infoline); + + chdir($from); +} + +################################################################# +# Executing the created batch file to pack all files. +# This works only on Windows +################################################################# + +sub execute_packaging +{ + my ($localpackjobref, $loggingdir, $allvariables) = @_; + + installer::logger::include_header_into_logfile("Packaging process"); + + installer::logger::include_timestamp_into_logfile("Performance Info: Execute packaging start"); + + my $infoline = ""; + my $from = cwd(); + my $to = $loggingdir; + + chdir($to); + $infoline = "chdir: $to \n"; + push( @installer::globals::logfileinfo, $infoline); + + # the ddf file contains relative paths, it is necessary to change into the temp directory + $to = $installer::globals::temppath; + chdir($to); + $infoline = "chdir: $to \n"; + push( @installer::globals::logfileinfo, $infoline); + + my $maxmakecabcalls = 3; + my $allmakecabcalls = $#{$localpackjobref} + 1; + + for ( my $i = 0; $i <= $#{$localpackjobref}; $i++ ) + { + my $systemcall = ${$localpackjobref}[$i]; + + my $callscounter = $i + 1; + + installer::logger::print_message( "... makecab.exe ($callscounter/$allmakecabcalls) ... \n" ); + + for ( my $n = 1; $n <= $maxmakecabcalls; $n++ ) + { + my @ddfoutput = (); + + $infoline = "Systemcall: $systemcall"; + push( @installer::globals::logfileinfo, $infoline); + + open (DDF, "$systemcall"); + while (<DDF>) {push(@ddfoutput, $_); } + close (DDF); + + my $returnvalue = $?; # $? contains the return value of the systemcall + + if ($returnvalue) + { + if ( $n < $maxmakecabcalls ) + { + installer::logger::print_message( "makecab_error (Try $n): Trying again \n" ); + $infoline = "makecab_error (Try $n): $systemcall !"; + } + else + { + installer::logger::print_message( "ERROR (Try $n): Abort packing \n" ); + $infoline = "ERROR (Try $n): $systemcall !"; + } + + push( @installer::globals::logfileinfo, $infoline); + + for ( my $m = 0; $m <= $#ddfoutput; $m++ ) + { + if ( $ddfoutput[$m] =~ /(ERROR\:.*?)\s*$/ ) + { + $infoline = $1 . "\n"; + if ( $n < $maxmakecabcalls ) { $infoline =~ s/ERROR\:/makecab_error\:/i; } + installer::logger::print_message( $infoline ); + push( @installer::globals::logfileinfo, $infoline); + } + } + + if ( $n == $maxmakecabcalls ) { installer::exiter::exit_program("ERROR: \"$systemcall\"!", "execute_packaging"); } + } + else + { + $infoline = "Success (Try $n): $systemcall"; + push( @installer::globals::logfileinfo, $infoline); + last; + } + } + } + + installer::logger::include_timestamp_into_logfile("Performance Info: Execute packaging end"); + + chdir($from); + $infoline = "chdir: $from \n"; + push( @installer::globals::logfileinfo, $infoline); +} + +############################################################### +# Setting the global variables ProductCode and the UpgradeCode +############################################################### + +sub set_global_code_variables +{ + my ( $languagesref, $languagestringref, $allvariableshashref, $alloldproperties ) = @_; + + # In the msi template directory a files "codes.txt" has to exist, in which the ProductCode + # and the UpgradeCode for the product are defined. + # The name "codes.txt" can be overwritten in Product definition with CODEFILENAME . + # Default $installer::globals::codefilename is defined in parameter.pm. + + if ( $allvariableshashref->{'CODEFILENAME'} ) + { + $installer::globals::codefilename = $installer::globals::idttemplatepath . $installer::globals::separator . $allvariableshashref->{'CODEFILENAME'}; + installer::files::check_file($installer::globals::codefilename); + } + + my $infoline = "Using Codes file: $installer::globals::codefilename \n"; + push( @installer::globals::logfileinfo, $infoline); + + my $codefile = installer::files::read_file($installer::globals::codefilename); + + my $onelanguage = ""; + + if ( $#{$languagesref} > 0 ) # more than one language + { + if (( $installer::globals::added_english ) && ( $#{$languagesref} == 1 )) # only multilingual because of added English + { + $onelanguage = ${$languagesref}[1]; # setting the first language, that is not english + } + else + { + if (( ${$languagesref}[1] =~ /jp/ ) || + ( ${$languagesref}[1] =~ /ko/ ) || + ( ${$languagesref}[1] =~ /zh/ )) + { + $onelanguage = "multiasia"; + } + else + { + $onelanguage = "multiwestern"; + } + } + } + else # only one language + { + $onelanguage = ${$languagesref}[0]; + } + + # ProductCode must not change, if Windows patches shall be applied + if ( $installer::globals::updatedatabase ) + { + $installer::globals::productcode = $alloldproperties->{'ProductCode'}; + } + elsif ( $installer::globals::prepare_winpatch ) + { + # ProductCode has to be specified in each language + my $searchstring = "PRODUCTCODE"; + my $codeblock = installer::windows::idtglobal::get_language_block_from_language_file($searchstring, $codefile); + $installer::globals::productcode = installer::windows::idtglobal::get_code_from_code_block($codeblock, $onelanguage); + } else { + my $guidref = get_guid_list(1, 1); # only one GUID shall be generated + ${$guidref}[0] =~ s/\s*$//; # removing ending spaces + $installer::globals::productcode = "\{" . ${$guidref}[0] . "\}"; + } + + # UpgradeCode can take english as default, if not defined in specified language + + $searchstring = "UPGRADECODE"; # searching in the codes.txt file + $codeblock = installer::windows::idtglobal::get_language_block_from_language_file($searchstring, $codefile); + $installer::globals::upgradecode = installer::windows::idtglobal::get_language_string_from_language_block($codeblock, $onelanguage, ""); + + if ( $installer::globals::upgradecode eq "" ) { installer::exiter::exit_program("ERROR: UpgradeCode not defined in $installer::globals::codefilename !", "set_global_code_variables"); } + + $infoline = "Setting ProductCode to: $installer::globals::productcode \n"; + push( @installer::globals::logfileinfo, $infoline); + $infoline = "Setting UpgradeCode to: $installer::globals::upgradecode \n"; + push( @installer::globals::logfileinfo, $infoline); + + # Adding both variables into the variables array + + $allvariableshashref->{'PRODUCTCODE'} = $installer::globals::productcode; + $allvariableshashref->{'UPGRADECODE'} = $installer::globals::upgradecode; + + $infoline = "Defined variable PRODUCTCODE: $installer::globals::productcode \n"; + push( @installer::globals::logfileinfo, $infoline); + + $infoline = "Defined variable UPGRADECODE: $installer::globals::upgradecode \n"; + push( @installer::globals::logfileinfo, $infoline); + +} + +############################################################### +# Setting the product version used in property table and +# upgrade table. Saving in global variable $msiproductversion +############################################################### + +sub set_msiproductversion +{ + my ( $allvariables ) = @_; + + my $productversion = $allvariables->{'PACKAGEVERSION'}; + + if ( $productversion =~ /^\s*(\d+)\.(\d+)\.(\d+)\s*$/ ) + { + $productversion = $1 . "\." . $2 . "\." . $3 . "\." . $installer::globals::buildid; + } + + $installer::globals::msiproductversion = $productversion; + + # Setting $installer::globals::msimajorproductversion, to differ between old version in upgrade table + + if ( $installer::globals::msiproductversion =~ /^\s*(\d+)\./ ) + { + my $major = $1; + $installer::globals::msimajorproductversion = $major . "\.0\.0"; + } +} + +################################################################################# +# Including the msi product version into the bootstrap.ini, Windows only +################################################################################# + +sub put_msiproductversion_into_bootstrapfile +{ + my ($filesref) = @_; + + for ( my $i = 0; $i <= $#{$filesref}; $i++ ) + { + my $onefile = ${$filesref}[$i]; + + if ( $onefile->{'gid'} eq "gid_Brand_Profile_Version_Ini" ) + { + my $file = installer::files::read_file($onefile->{'sourcepath'}); + + for ( my $j = 0; $j <= $#{$file}; $j++ ) + { + ${$file}[$j] =~ s/\<msiproductversion\>/$installer::globals::msiproductversion/; + } + + installer::files::save_file($onefile->{'sourcepath'}, $file); + + last; + } + } +} + +#################################################################################### +# Updating the file Property.idt dynamically +# Content: +# Property Value +#################################################################################### + +sub update_reglocat_table +{ + my ($basedir, $allvariables) = @_; + + my $reglocatfilename = $basedir . $installer::globals::separator . "RegLocat.idt"; + + # Only do something, if this file exists + + if ( -f $reglocatfilename ) + { + my $reglocatfile = installer::files::read_file($reglocatfilename); + + my $layername = ""; + if ( $allvariables->{'REGISTRYLAYERNAME'} ) + { + $layername = $allvariables->{'REGISTRYLAYERNAME'}; + } + else + { + for ( my $i = 0; $i <= $#{$reglocatfile}; $i++ ) + { + if ( ${$reglocatfile}[$i] =~ /\bLAYERNAMETEMPLATE\b/ ) + { + installer::exiter::exit_program("ERROR: Variable \"REGISTRYLAYERNAME\" has to be defined", "update_reglocat_table"); + } + } + } + + if ( $layername ne "" ) + { + # Updating the layername in + + for ( my $i = 0; $i <= $#{$reglocatfile}; $i++ ) + { + ${$reglocatfile}[$i] =~ s/\bLAYERNAMETEMPLATE\b/$layername/; + } + + # Saving the file + installer::files::save_file($reglocatfilename ,$reglocatfile); + my $infoline = "Updated idt file: $reglocatfilename\n"; + push(@installer::globals::logfileinfo, $infoline); + } + } +} + + + +#################################################################################### +# Updating the file RemoveRe.idt dynamically (RemoveRegistry.idt) +# The name of the component has to be replaced. +#################################################################################### + +sub update_removere_table +{ + my ($basedir) = @_; + + my $removeregistryfilename = $basedir . $installer::globals::separator . "RemoveRe.idt"; + + # Only do something, if this file exists + + if ( -f $removeregistryfilename ) + { + my $removeregistryfile = installer::files::read_file($removeregistryfilename); + + for ( my $i = 0; $i <= $#{$removeregistryfile}; $i++ ) + { + for ( my $i = 0; $i <= $#{$removeregistryfile}; $i++ ) + { + ${$removeregistryfile}[$i] =~ s/\bREGISTRYROOTCOMPONENT\b/$installer::globals::registryrootcomponent/; + } + } + + # Saving the file + installer::files::save_file($removeregistryfilename ,$removeregistryfile); + my $infoline = "Updated idt file: $removeregistryfilename \n"; + push(@installer::globals::logfileinfo, $infoline); + } +} + +########################################################################## +# Reading saved mappings in Files.idt and Director.idt. +# This is required, if installation sets shall be created, +# that can be used for creation of msp files. +########################################################################## + +sub read_saved_mappings +{ + installer::logger::include_header_into_logfile("Reading saved mappings from older installation sets:"); + + installer::logger::include_timestamp_into_logfile("Performance Info: Reading saved mappings start"); + + if ( $installer::globals::previous_idt_dir ) + { + my @errorlines = (); + my $errorstring = ""; + my $error_occurred = 0; + my $file_error_occurred = 0; + my $dir_error_occurred = 0; + + my $idtdir = $installer::globals::previous_idt_dir; + $idtdir =~ s/\Q$installer::globals::separator\E\s*$//; + + # Reading File.idt + + my $idtfile = $idtdir . $installer::globals::separator . "File.idt"; + push( @installer::globals::globallogfileinfo, "\nAnalyzing file: $idtfile\n" ); + if ( ! -f $idtfile ) { push( @installer::globals::globallogfileinfo, "Warning: File $idtfile does not exist!\n" ); } + + my $n = 0; + open (F, "<$idtfile") || installer::exiter::exit_program("ERROR: Cannot open file $idtfile for reading", "read_saved_mappings"); + <F>; <F>; <F>; + while (<F>) + { + m/^([^\t]+)\t([^\t]+)\t((.*)\|)?([^\t]*)/; + print "OUT1: \$1: $1, \$2: $2, \$3: $3, \$4: $4, \$5: $5\n"; + next if ("$1" eq "$5") && (!defined($3)); + my $lc1 = lc($1); + + if ( exists($installer::globals::savedmapping{"$2/$5"})) + { + if ( ! $file_error_occurred ) + { + $errorstring = "\nErrors in $idtfile: \n"; + push(@errorlines, $errorstring); + } + $errorstring = "Duplicate savedmapping{" . "$2/$5}\n"; + push(@errorlines, $errorstring); + $error_occurred = 1; + $file_error_occurred = 1; + } + + if ( exists($installer::globals::savedrevmapping{$lc1})) + { + if ( ! $file_error_occurred ) + { + $errorstring = "\nErrors in $idtfile: \n"; + push(@errorlines, $errorstring); + } + $errorstring = "Duplicate savedrevmapping{" . "$lc1}\n"; + push(@errorlines, $errorstring); + $error_occurred = 1; + $file_error_occurred = 1; + } + + my $shortname = $4 || ''; + + # Don't reuse illegal 8.3 mappings that we used to generate in 2.0.4 + if (index($shortname, '.') > 8 || + (index($shortname, '.') == -1 && length($shortname) > 8)) + { + $shortname = ''; + } + + if (( $shortname ne '' ) && ( index($shortname, '~') > 0 ) && ( exists($installer::globals::savedrev83mapping{$shortname}) )) + { + if ( ! $file_error_occurred ) + { + $errorstring = "\nErrors in $idtfile: \n"; + push(@errorlines, $errorstring); + } + $errorstring = "Duplicate savedrev83mapping{" . "$shortname}\n"; + push(@errorlines, $errorstring); + $error_occurred = 1; + $file_error_occurred = 1; + } + + $installer::globals::savedmapping{"$2/$5"} = "$1;$shortname"; + $installer::globals::savedrevmapping{lc($1)} = "$2/$5"; + $installer::globals::savedrev83mapping{$shortname} = "$2/$5" if $shortname ne ''; + $n++; + } + + close (F); + + push( @installer::globals::globallogfileinfo, "Read $n old file table key or 8.3 name mappings from $idtfile\n" ); + + # Reading Director.idt + + $idtfile = $idtdir . $installer::globals::separator . "Director.idt"; + push( @installer::globals::globallogfileinfo, "\nAnalyzing file $idtfile\n" ); + if ( ! -f $idtfile ) { push( @installer::globals::globallogfileinfo, "Warning: File $idtfile does not exist!\n" ); } + + $n = 0; + open (F, "<$idtfile") || installer::exiter::exit_program("ERROR: Cannot open file $idtfile for reading", "read_saved_mappings"); + <F>; <F>; <F>; + while (<F>) + { + m/^([^\t]+)\t([^\t]+)\t(([^~]+~\d.*)\|)?([^\t]*)/; + next if (!defined($3)); + my $lc1 = lc($1); + + print "OUT2: \$1: $1, \$2: $2, \$3: $3\n"; + + if ( exists($installer::globals::saved83dirmapping{$1}) ) + { + if ( ! $dir_error_occurred ) + { + $errorstring = "\nErrors in $idtfile: \n"; + push(@errorlines, $errorstring); + } + $errorstring = "Duplicate saved83dirmapping{" . "$1}\n"; + push(@errorlines, $errorstring); + $error_occurred = 1; + $dir_error_occurred = 1; + } + + $installer::globals::saved83dirmapping{$1} = $4; + $n++; + } + close (F); + + push( @installer::globals::globallogfileinfo, "Read $n old directory 8.3 name mappings from $idtfile\n" ); + + # Analyzing errors + + if ( $error_occurred ) + { + for ( my $i = 0; $i <= $#errorlines; $i++ ) + { + print "$errorlines[$i]"; + push( @installer::globals::globallogfileinfo, "$errorlines[$i]"); + } + installer::exiter::exit_program("ERROR: Duplicate entries in saved mappings!", "read_saved_mappings"); + } + } else { + installer::exiter::exit_program("ERROR: Windows patch shall be prepared, but environment variable PREVIOUS_IDT_DIR is not set!", "read_saved_mappings"); + } + + installer::logger::include_timestamp_into_logfile("Performance Info: Reading saved mappings end"); +} + +1; + +# vim:set shiftwidth=4 softtabstop=4 expandtab: |