summaryrefslogtreecommitdiffstats
path: root/solenv/bin/modules/installer/windows/msiglobal.pm
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 16:51:28 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 16:51:28 +0000
commit940b4d1848e8c70ab7642901a68594e8016caffc (patch)
treeeb72f344ee6c3d9b80a7ecc079ea79e9fba8676d /solenv/bin/modules/installer/windows/msiglobal.pm
parentInitial commit. (diff)
downloadlibreoffice-940b4d1848e8c70ab7642901a68594e8016caffc.tar.xz
libreoffice-940b4d1848e8c70ab7642901a68594e8016caffc.zip
Adding upstream version 1:7.0.4.upstream/1%7.0.4upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'solenv/bin/modules/installer/windows/msiglobal.pm')
-rw-r--r--solenv/bin/modules/installer/windows/msiglobal.pm1684
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: