diff options
Diffstat (limited to 'solenv/bin/modules/installer/windows')
29 files changed, 16890 insertions, 0 deletions
diff --git a/solenv/bin/modules/installer/windows/admin.pm b/solenv/bin/modules/installer/windows/admin.pm new file mode 100644 index 000000000000..c6c175fa5027 --- /dev/null +++ b/solenv/bin/modules/installer/windows/admin.pm @@ -0,0 +1,839 @@ +#************************************************************************* +# +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# Copyright 2008 by Sun Microsystems, Inc. +# +# OpenOffice.org - a multi-platform office productivity suite +# +# $RCSfile: admin.pm,v $ +# +# $Revision: 1.1.2.2 $ +# +# This file is part of OpenOffice.org. +# +# OpenOffice.org is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License version 3 +# only, as published by the Free Software Foundation. +# +# OpenOffice.org is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License version 3 for more details +# (a copy is included in the LICENSE file that accompanied this code). +# +# You should have received a copy of the GNU Lesser General Public License +# version 3 along with OpenOffice.org. If not, see +# <http://www.openoffice.org/license.html> +# for a copy of the LGPLv3 License. +# +#************************************************************************* + +package installer::windows::admin; + +use File::Copy; +use installer::exiter; +use installer::files; +use installer::globals; +use installer::pathanalyzer; +use installer::systemactions; +use installer::worker; +use installer::windows::idtglobal; + +################################################################################# +# Unpacking cabinet files with expand +################################################################################# + +sub unpack_cabinet_file +{ + my ($cabfilename, $unpackdir) = @_; + + my $infoline = "Unpacking cabinet file: $cabfilename\n"; + push( @installer::globals::logfileinfo, $infoline); + + my $expandfile = "expand.exe"; # Has to be in the path + + # expand.exe has to be located in the system directory. + # Cygwin has another tool expand.exe, that converts tabs to spaces. This cannot be used of course. + # But this wrong expand.exe is typically in the PATH before this expand.exe, to unpack + # cabinet files. + +# if ( $^O =~ /cygwin/i ) +# { +# $expandfile = $ENV{'SYSTEMROOT'} . "/system32/expand.exe"; # Has to be located in the systemdirectory +# $expandfile =~ s/\\/\//; +# if ( ! -f $expandfile ) { exit_program("ERROR: Did not find file $expandfile in the Windows system folder!"); } +# } + + if ( $^O =~ /cygwin/i ) + { + $expandfile = qx(cygpath -u "$ENV{WINDIR}"/System32/expand.exe); + chomp $expandfile; + } + + my $expandlogfile = $unpackdir . $installer::globals::separator . "expand.log"; + + # exclude cabinet file + # my $systemcall = $cabarc . " -o X " . $mergemodulehash->{'cabinetfile'}; + + my $systemcall = ""; + if ( $^O =~ /cygwin/i ) { + my $localunpackdir = qx{cygpath -w "$unpackdir"}; + chomp ($localunpackdir); + $localunpackdir =~ s/\\/\\\\/g; + $cabfilename =~ s/\\/\\\\/g; + $cabfilename =~ s/\s*$//g; + $systemcall = $expandfile . " " . $cabfilename . " -F:\* " . $localunpackdir . " \> " . $expandlogfile; + } + else + { + $systemcall = $expandfile . " " . $cabfilename . " -F:\* " . $unpackdir . " \> " . $expandlogfile; + } + + 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); + installer::exiter::exit_program("ERROR: Could not extract cabinet file: $mergemodulehash->{'cabinetfile'} !", "change_file_table"); + } + else + { + $infoline = "Success: Executed $systemcall successfully!\n"; + push( @installer::globals::logfileinfo, $infoline); + } +} + +################################################################################# +# Include tables into a msi database +################################################################################# + +sub include_tables_into_pcpfile +{ + my ($fullmsidatabasepath, $workdir, $tables) = @_; + + my $msidb = "msidb.exe"; # Has to be in the path + my $infoline = ""; + my $systemcall = ""; + my $returnvalue = ""; + + # Make all table 8+3 conform + my $alltables = installer::converter::convert_stringlist_into_array(\$tables, " "); + + for ( my $i = 0; $i <= $#{$alltables}; $i++ ) + { + my $tablename = ${$alltables}[$i]; + $tablename =~ s/\s*$//; + my $namelength = length($tablename); + if ( $namelength > 8 ) + { + my $newtablename = substr($tablename, 0, 8); # name, offset, length + my $oldfile = $workdir . $installer::globals::separator . $tablename . ".idt"; + my $newfile = $workdir . $installer::globals::separator . $newtablename . ".idt"; + if ( -f $newfile ) { unlink $newfile; } + installer::systemactions::copy_one_file($oldfile, $newfile); + my $savfile = $oldfile . ".orig"; + installer::systemactions::copy_one_file($oldfile, $savfile); + } + } + + # Import of tables + + $systemcall = $msidb . " -d " . $fullmsidatabasepath . " -f " . $workdir . " -i " . $tables; + + $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); + installer::exiter::exit_program("ERROR: Could not include tables into msi database: $fullmsidatabasepath !", "include_tables_into_pcpfile"); + } + else + { + $infoline = "Success: Executed $systemcall successfully!\n"; + push( @installer::globals::logfileinfo, $infoline); + } +} + +################################################################################# +# Extracting tables from msi database +################################################################################# + +sub extract_tables_from_pcpfile +{ + my ($fullmsidatabasepath, $workdir, $tablelist) = @_; + + my $msidb = "msidb.exe"; # Has to be in the path + my $infoline = ""; + my $systemcall = ""; + my $returnvalue = ""; + + my $localfullmsidatabasepath = $fullmsidatabasepath; + + # Export of all tables by using "*" + + if ( $^O =~ /cygwin/i ) { + # Copying the msi database locally guarantees the format of the directory. + # Otherwise it is defined in the file of UPDATE_DATABASE_LISTNAME + + my $msifilename = $localfullmsidatabasepath; + installer::pathanalyzer::make_absolute_filename_to_relative_filename(\$msifilename); + my $destdatabasename = $workdir . $installer::globals::separator . $msifilename; + installer::systemactions::copy_one_file($localfullmsidatabasepath, $destdatabasename); + $localfullmsidatabasepath = $destdatabasename; + + chomp( $localfullmsidatabasepath = qx{cygpath -w "$localfullmsidatabasepath"} ); + chomp( $workdir = qx{cygpath -w "$workdir"} ); + + # msidb.exe really wants backslashes. (And double escaping because system() expands the string.) + $localfullmsidatabasepath =~ s/\\/\\\\/g; + $workdir =~ s/\\/\\\\/g; + + # and if there are still slashes, they also need to be double backslash + $localfullmsidatabasepath =~ s/\//\\\\/g; + $workdir =~ s/\//\\\\/g; + } + + $systemcall = $msidb . " -d " . $localfullmsidatabasepath . " -f " . $workdir . " -e $tablelist"; + $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); + installer::exiter::exit_program("ERROR: Could not exclude tables from pcp file: $localfullmsidatabasepath !", "extract_tables_from_pcpfile"); + } + else + { + $infoline = "Success: Executed $systemcall successfully!\n"; + push( @installer::globals::logfileinfo, $infoline); + } +} + +################################################################################ +# Analyzing the content of Directory.idt +################################################################################# + +sub analyze_directory_file +{ + my ($filecontent) = @_; + + my %table = (); + + for ( my $i = 0; $i <= $#{$filecontent}; $i++ ) + { + if (( $i == 0 ) || ( $i == 1 ) || ( $i == 2 )) { next; } + + if ( ${$filecontent}[$i] =~ /^\s*(.*?)\t(.*?)\t(.*?)\s*$/ ) + { + my $dir = $1; + my $parent = $2; + my $name = $3; + + if ( $name =~ /^\s*(.*?)\s*\:\s*(.*?)\s*$/ ) { $name = $2; } + if ( $name =~ /^\s*(.*?)\s*\|\s*(.*?)\s*$/ ) { $name = $2; } + + my %helphash = (); + $helphash{'Directory_Parent'} = $parent; + $helphash{'DefaultDir'} = $name; + $table{$dir} = \%helphash; + } + } + + return \%table; +} + +################################################################################# +# Analyzing the content of Component.idt +################################################################################# + +sub analyze_component_file +{ + my ($filecontent) = @_; + + my %table = (); + + for ( my $i = 0; $i <= $#{$filecontent}; $i++ ) + { + if (( $i == 0 ) || ( $i == 1 ) || ( $i == 2 )) { next; } + + if ( ${$filecontent}[$i] =~ /^\s*(.*?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\s*$/ ) + { + my $component = $1; + my $dir = $3; + + $table{$component} = $dir; + } + } + + return \%table; +} + +################################################################################# +# Analyzing the full content of Component.idt +################################################################################# + +sub analyze_keypath_component_file +{ + my ($filecontent) = @_; + + my %keypathtable = (); + + for ( my $i = 0; $i <= $#{$filecontent}; $i++ ) + { + if (( $i == 0 ) || ( $i == 1 ) || ( $i == 2 )) { next; } + + if ( ${$filecontent}[$i] =~ /^\s*(.*?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\s*$/ ) + { + my $component = $1; + my $keypath = $6; + + $keypathtable{$keypath} = $component; + } + } + + return (\%keypathtable); + +} + +################################################################################# +# Analyzing the content of Registry.idt +################################################################################# + +sub analyze_registry_file +{ + my ($filecontent) = @_; + + my %table = (); + + for ( my $i = 0; $i <= $#{$filecontent}; $i++ ) + { + if (( $i == 0 ) || ( $i == 1 ) || ( $i == 2 )) { next; } + + if ( ${$filecontent}[$i] =~ /^\s*(.*?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\s*$/ ) + { + my $registry = $1; + my $root = $2; + my $key = $3; + my $name = $4; + my $value = $5; + my $component = $6; + + my %helphash = (); + # $helphash{'Registry'} = $registry; + $helphash{'Root'} = $root; + $helphash{'Key'} = $key; + $helphash{'Name'} = $name; + $helphash{'Value'} = $value; + $helphash{'Component'} = $component; + + $table{$registry} = \%helphash; + } + } + + return \%table; +} + +################################################################################# +# Analyzing the content of File.idt +################################################################################# + +sub analyze_file_file +{ + my ($filecontent) = @_; + + my %table = (); + my %fileorder = (); + my $maxsequence = 0; + + for ( my $i = 0; $i <= $#{$filecontent}; $i++ ) + { + if (( $i == 0 ) || ( $i == 1 ) || ( $i == 2 )) { next; } + + if ( ${$filecontent}[$i] =~ /^\s*(.*?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\s*$/ ) + { + my $file = $1; + my $comp = $2; + my $filename = $3; + my $sequence = $8; + + if ( $filename =~ /^\s*(.*?)\s*\|\s*(.*?)\s*$/ ) { $filename = $2; } + + my %helphash = (); + $helphash{'Component'} = $comp; + $helphash{'FileName'} = $filename; + $helphash{'Sequence'} = $sequence; + + $table{$file} = \%helphash; + + $fileorder{$sequence} = $file; + + if ( $sequence > $maxsequence ) { $maxsequence = $sequence; } + } + } + + return (\%table, \%fileorder, $maxsequence); +} + +#################################################################################### +# Recursively creating the directory tree +#################################################################################### + +sub create_directory_tree +{ + my ($parent, $pathcollector, $fulldir, $dirhash) = @_; + + foreach my $dir ( keys %{$dirhash} ) + { + if (( $dirhash->{$dir}->{'Directory_Parent'} eq $parent ) && ( $dirhash->{$dir}->{'DefaultDir'} ne "." )) + { + my $dirname = $dirhash->{$dir}->{'DefaultDir'}; + # Create the directory + my $newdir = $fulldir . $installer::globals::separator . $dirname; + if ( ! -f $newdir ) { mkdir $newdir; } + # Saving in collector + $pathcollector->{$dir} = $newdir; + # Iteration + create_directory_tree($dir, $pathcollector, $newdir, $dirhash); + } + } +} + +#################################################################################### +# Creating the directory tree +#################################################################################### + +sub create_directory_structure +{ + my ($dirhash, $targetdir) = @_; + + my %fullpathhash = (); + + my @startparents = ("TARGETDIR", "INSTALLLOCATION"); + + foreach $dir (@startparents) { create_directory_tree($dir, \%fullpathhash, $targetdir, $dirhash); } + + return \%fullpathhash; +} + +#################################################################################### +# Copying files into installation set +#################################################################################### + +sub copy_files_into_directory_structure +{ + my ($fileorder, $filehash, $componenthash, $fullpathhash, $maxsequence, $unpackdir, $installdir, $dirhash) = @_; + + for ( my $i = 1; $i <= $maxsequence; $i++ ) + { + if ( exists($fileorder->{$i}) ) + { + my $file = $fileorder->{$i}; + if ( ! exists($filehash->{$file}->{'Component'}) ) { installer::exiter::exit_program("ERROR: Did not find component for file: \"$file\".", "copy_files_into_directory_structure"); } + my $component = $filehash->{$file}->{'Component'}; + if ( ! exists($componenthash->{$component}) ) { installer::exiter::exit_program("ERROR: Did not find directory for component: \"$component\".", "copy_files_into_directory_structure"); } + my $dirname = $componenthash->{$component}; + if ( ! exists($fullpathhash->{$dirname}) ) { installer::exiter::exit_program("ERROR: Did not find full directory path for dir: \"$dirname\".", "copy_files_into_directory_structure"); } + my $destdir = $fullpathhash->{$dirname}; + if ( ! exists($filehash->{$file}->{'FileName'}) ) { installer::exiter::exit_program("ERROR: Did not find \"FileName\" for file: \"$file\".", "copy_files_into_directory_structure"); } + my $destfile = $filehash->{$file}->{'FileName'}; + + $destfile = $destdir . $installer::globals::separator . $destfile; + my $sourcefile = $unpackdir . $installer::globals::separator . $file; + + if ( ! -f $sourcefile ) + { + # It is possible, that this was an unpacked file + # Looking in the dirhash, to find the subdirectory in the installation set (the id is $dirname) + # subdir is not recursively analyzed, only one directory. + + my $oldsourcefile = $sourcefile; + my $subdir = ""; + if ( exists($dirhash->{$dirname}->{'DefaultDir'}) ) { $subdir = $dirhash->{$dirname}->{'DefaultDir'} . $installer::globals::separator; } + my $realfilename = $filehash->{$file}->{'FileName'}; + my $localinstalldir = $installdir; + + $localinstalldir =~ s/\\\s*$//; + $localinstalldir =~ s/\/\s*$//; + + $sourcefile = $localinstalldir . $installer::globals::separator . $subdir . $realfilename; + + if ( ! -f $sourcefile ) + { + installer::exiter::exit_program("ERROR: File not found: \"$oldsourcefile\" (or \"$sourcefile\").", "copy_files_into_directory_structure"); + } + } + + my $copyreturn = copy($sourcefile, $destfile); + + if ( ! $copyreturn) # only logging problems + { + my $infoline = "ERROR: Could not copy $sourcefile to $destfile (insufficient disc space for $destfile ?)\n"; + $returnvalue = 0; + push(@installer::globals::logfileinfo, $infoline); + installer::exiter::exit_program($infoline, "copy_files_into_directory_structure"); + } + + # installer::systemactions::copy_one_file($sourcefile, $destfile); + } + # else # allowing missing sequence numbers ? + # { + # installer::exiter::exit_program("ERROR: No file assigned to sequence $i", "copy_files_into_directory_structure"); + # } + } +} + + +############################################################### +# Setting the time string for the +# Summary Information stream in the +# msi database of the admin installations. +############################################################### + +sub get_sis_time_string +{ + # Syntax: <yyyy/mm/dd hh:mm:ss> + my $second = (localtime())[0]; + my $minute = (localtime())[1]; + my $hour = (localtime())[2]; + my $day = (localtime())[3]; + my $month = (localtime())[4]; + my $year = 1900 + (localtime())[5]; + + $month++; # zero based month + + if ( $second < 10 ) { $second = "0" . $second; } + if ( $minute < 10 ) { $minute = "0" . $minute; } + if ( $hour < 10 ) { $hour = "0" . $hour; } + if ( $day < 10 ) { $day = "0" . $day; } + if ( $month < 10 ) { $month = "0" . $month; } + + my $timestring = $year . "/" . $month . "/" . $day . " " . $hour . ":" . $minute . ":" . $second; + + return $timestring; +} + +############################################################### +# Windows registry entries containing properties are not set +# correctly during msp patch process. The properties are +# empty or do get their default values. This destroys the +# values of many entries in Windows registry. +# This can be fixed by removing all entries in Registry table, +# containing a property before starting msimsp.exe. +############################################################### + +sub remove_properties_from_registry_table +{ + my ($registryhash, $componentkeypathhash, $registryfilecontent) = @_; + + installer::logger::include_timestamp_into_logfile("\nPerformance Info: Start remove_properties_from_registry_table"); + + my @registrytable = (); + + # Registry hash + # Collecting all RegistryItems with values containing a property: [...] + # To which component do they belong + # Is this after removal an empty component? Create a replacement, so that + # no Component has to be removed. + # Is this RegistryItem a KeyPath of a component. Then it cannot be removed. + + my %problemitems = (); + my %problemcomponents = (); + my %securecomponents = (); + my $changevalue = ""; + my $changeroot = ""; + my $infoline = ""; + + my $newitemcounter = 0; + my $olditemcounter = 0; + + foreach my $regitem ( keys %{$registryhash} ) + { + my $value = ""; + if ( exists($registryhash->{$regitem}->{'Value'}) ) { $value = $registryhash->{$regitem}->{'Value'}; } + + if ( $value =~ /^.*(\[.*?\]).*$/ ) + { + my $property = $1; + + # Collecting registry item + $problemitems{$regitem} = 1; # "1" -> can be removed + if ( exists($componentkeypathhash->{$regitem}) ) { $problemitems{$regitem} = 2; } # "2" -> cannot be removed, KeyPath + + # Collecting component (and number of problematic registry items + # my $component = $registryhash->{$regitem}->{'Component'}; + # if ( exists($problemcomponents{$regitem}) ) { $problemcomponents{$regitem} = $problemcomponents{$regitem} + 1; } + # else { $problemcomponents{$regitem} = 1; } + } + else + { + # Collecting all components with secure regisry items + my $component = ""; + if ( exists($registryhash->{$regitem}->{'Component'}) ) { $component = $registryhash->{$regitem}->{'Component'}; } + if ( $component eq "" ) { installer::exiter::exit_program("ERROR: Did not find component for registry item \"$regitem\".", "remove_properties_from_registry_table"); } + $securecomponents{$component} = 1; + } + + # Searching for change value + my $localkey = ""; + if ( exists($registryhash->{$regitem}->{'Key'}) ) { $localkey = $registryhash->{$regitem}->{'Key'}; } + if (( $localkey =~ /^\s*(Software\\.*\\)StartMenu\s*$/ ) && ( $changevalue eq "" )) + { + $changevalue = $1; + $changeroot = $registryhash->{$regitem}->{'Root'}; + } + + $olditemcounter++; + } + + my $removecounter = 0; + my $renamecounter = 0; + + foreach my $regitem ( keys %{$registryhash} ) + { + my $value = ""; + if ( exists($registryhash->{$regitem}->{'Value'}) ) { $value = $registryhash->{$regitem}->{'Value'}; } + + if ( $value =~ /^.*(\[.*?\]).*$/ ) + { + # Removing registry items, that are no KeyPath and that belong to components, + # that have other secure registry items. + + my $component = ""; + if ( exists($registryhash->{$regitem}->{'Component'}) ) { $component = $registryhash->{$regitem}->{'Component'}; } + if ( $component eq "" ) { installer::exiter::exit_program("ERROR: Did not find component for registry item (2) \"$regitem\".", "remove_properties_from_registry_table"); } + + if (( $problemitems{$regitem} == 1 ) && ( exists($securecomponents{$component}) )) + { + # remove complete registry item + delete($registryhash->{$regitem}); + $removecounter++; + $infoline = "Removing registry item: $regitem : $value\n"; + push( @installer::globals::logfileinfo, $infoline); + } + else + { + # Changing values of registry items, that are KeyPath or that contain to + # components with only unsecure registry items. + + if (( $problemitems{$regitem} == 2 ) || ( ! exists($securecomponents{$component}) )) + { + # change value of registry item + if ( $changevalue eq "" ) { installer::exiter::exit_program("ERROR: Did not find good change value for registry items", "remove_properties_from_registry_table"); } + + my $oldkey = ""; + if ( exists($registryhash->{$regitem}->{'Key'}) ) { $oldkey = $registryhash->{$regitem}->{'Key'}; }; + my $oldname = ""; + if ( exists($registryhash->{$regitem}->{'Name'}) ) { $oldname = $registryhash->{$regitem}->{'Name'}; } + my $oldvalue = ""; + if ( exists($registryhash->{$regitem}->{'Value'}) ) { $oldvalue = $registryhash->{$regitem}->{'Value'}; } + + $registryhash->{$regitem}->{'Key'} = $changevalue . "RegistryItem"; + $registryhash->{$regitem}->{'Root'} = $changeroot; + $registryhash->{$regitem}->{'Name'} = $regitem; + $registryhash->{$regitem}->{'Value'} = 1; + $renamecounter++; + + $infoline = "Changing registry item: $regitem\n"; + $infoline = "Old: $oldkey : $oldname : $oldvalue\n"; + $infoline = "New: $registryhash->{$regitem}->{'Key'} : $registryhash->{$regitem}->{'Name'} : $registryhash->{$regitem}->{'Value'}\n"; + push( @installer::globals::logfileinfo, $infoline); + } + } + } + } + + $infoline = "Number of removed registry items: $removecounter\n"; + push( @installer::globals::logfileinfo, $infoline); + $infoline = "Number of changed registry items: $renamecounter\n"; + push( @installer::globals::logfileinfo, $infoline); + + # Creating the new content of Registry table + # First three lines from $registryfilecontent + # All further files from changed $registryhash + + for ( my $i = 0; $i <= 2; $i++ ) { push(@registrytable, ${$registryfilecontent}[$i]); } + + foreach my $regitem ( keys %{$registryhash} ) + { + my $root = ""; + if ( exists($registryhash->{$regitem}->{'Root'}) ) { $root = $registryhash->{$regitem}->{'Root'}; } + else { installer::exiter::exit_program("ERROR: Did not find root in registry table for item: \"$regitem\".", "remove_properties_from_registry_table"); } + my $localkey = ""; + if ( exists($registryhash->{$regitem}->{'Key'}) ) { $localkey = $registryhash->{$regitem}->{'Key'}; } + my $name = ""; + if ( exists($registryhash->{$regitem}->{'Name'}) ) { $name = $registryhash->{$regitem}->{'Name'}; } + my $value = ""; + if ( exists($registryhash->{$regitem}->{'Value'}) ) { $value = $registryhash->{$regitem}->{'Value'}; } + my $comp = ""; + if ( exists($registryhash->{$regitem}->{'Component'}) ) { $comp = $registryhash->{$regitem}->{'Component'}; } + + my $oneline = $regitem . "\t" . $root . "\t" . $localkey . "\t" . $name . "\t" . $value . "\t" . $comp . "\n"; + push(@registrytable, $oneline); + + $newitemcounter++; + } + + $infoline = "Number of registry items: $newitemcounter. Old value: $olditemcounter.\n"; + push( @installer::globals::logfileinfo, $infoline); + + installer::logger::include_timestamp_into_logfile("\nPerformance Info: End remove_properties_from_registry_table"); + + return (\@registrytable); +} + +############################################################### +# Writing content of administrative installations into +# Summary Information Stream of msi database. +# This is required for example for following +# patch processes using Windows Installer service. +############################################################### + +sub write_sis_info +{ + my ($msidatabase) = @_ ; + + if ( ! -f $msidatabase ) { installer::exiter::exit_program("ERROR: Cannot find file $msidatabase", "write_sis_info"); } + + my $msiinfo = "msiinfo.exe"; # Has to be in the path + my $infoline = ""; + my $systemcall = ""; + my $returnvalue = ""; + + # Required setting for administrative installations: + # -w 4 (source files are unpacked), wordcount + # -s <date of admin installation>, LastPrinted, Syntax: <yyyy/mm/dd hh:mm:ss> + # -l <person_making_admin_installation>, LastSavedBy + + my $wordcount = 4; # Unpacked files + my $lastprinted = get_sis_time_string(); + my $lastsavedby = "Installer"; + + my $localmsidatabase = $msidatabase; + + if( $^O =~ /cygwin/i ) + { + $localmsidatabase = qx{cygpath -w "$localmsidatabase"}; + $localmsidatabase =~ s/\\/\\\\/g; + $localmsidatabase =~ s/\s*$//g; + } + + $systemcall = $msiinfo . " " . "\"" . $localmsidatabase . "\"" . " -w " . $wordcount . " -s " . "\"" . $lastprinted . "\"" . " -l $lastsavedby"; + push(@installer::globals::logfileinfo, $systemcall); + $returnvalue = system($systemcall); + + if ($returnvalue) + { + $infoline = "ERROR: Could not execute $systemcall !\n"; + push(@installer::globals::logfileinfo, $infoline); + installer::exiter::exit_program($infoline, "write_sis_info"); + } +} + +#################################################################################### +# Simulating an administrative installation +#################################################################################### + +sub make_admin_install +{ + my ($databasepath, $targetdir) = @_; + + # Create helper directory + + installer::logger::print_message( "... installing $databasepath in directory $targetdir ...\n" ); + + my $helperdir = $targetdir . $installer::globals::separator . "installhelper"; + installer::systemactions::create_directory($helperdir); + + # Get File.idt, Component.idt and Directory.idt from database + + my $tablelist = "File Directory Component Registry"; + extract_tables_from_pcpfile($databasepath, $helperdir, $tablelist); + + # Unpack all cab files into $helperdir, cab files must be located next to msi database + my $installdir = $databasepath; + + if ( $^O =~ /cygwin/i ) { $installdir =~ s/\\/\//g; } # backslash to slash + + installer::pathanalyzer::get_path_from_fullqualifiedname(\$installdir); + + if ( $^O =~ /cygwin/i ) { $installdir =~ s/\//\\/g; } # slash to backslash + + my $databasefilename = $databasepath; + installer::pathanalyzer::make_absolute_filename_to_relative_filename(\$databasefilename); + + my $cabfiles = installer::systemactions::find_file_with_file_extension("cab", $installdir); + + if ( $#{$cabfiles} < 0 ) { installer::exiter::exit_program("ERROR: Did not find any cab file in directory $installdir", "make_admin_install"); } + + # Set unpackdir + my $unpackdir = $helperdir . $installer::globals::separator . "unpack"; + installer::systemactions::create_directory($unpackdir); + + for ( my $i = 0; $i <= $#{$cabfiles}; $i++ ) + { + my $cabfile = ""; + if ( $^O =~ /cygwin/i ) + { + $cabfile = $installdir . ${$cabfiles}[$i]; + } + else + { + $cabfile = $installdir . $installer::globals::separator . ${$cabfiles}[$i]; + } + unpack_cabinet_file($cabfile, $unpackdir); + } + + # Reading tables + my $filename = $helperdir . $installer::globals::separator . "Directory.idt"; + my $filecontent = installer::files::read_file($filename); + my $dirhash = analyze_directory_file($filecontent); + + $filename = $helperdir . $installer::globals::separator . "Component.idt"; + my $componentfilecontent = installer::files::read_file($filename); + my $componenthash = analyze_component_file($componentfilecontent); + + $filename = $helperdir . $installer::globals::separator . "File.idt"; + $filecontent = installer::files::read_file($filename); + my ( $filehash, $fileorder, $maxsequence ) = analyze_file_file($filecontent); + + # Creating the directory structure + my $fullpathhash = create_directory_structure($dirhash, $targetdir); + + # Copying files + copy_files_into_directory_structure($fileorder, $filehash, $componenthash, $fullpathhash, $maxsequence, $unpackdir, $installdir, $dirhash); + + my $msidatabase = $targetdir . $installer::globals::separator . $databasefilename; + installer::systemactions::copy_one_file($databasepath, $msidatabase); + + # Editing registry table because of wrong Property value + # my $registryfilename = $helperdir . $installer::globals::separator . "Registry.idt"; + # my $componentfilename = $helperdir . $installer::globals::separator . "Component.idt"; + # my $componentkeypathhash = analyze_keypath_component_file($componentfilecontent); + + # my $registryfilecontent = installer::files::read_file($registryfilename); + # my $registryhash = analyze_registry_file($registryfilecontent); + + # $registryfilecontent = remove_properties_from_registry_table($registryhash, $componentkeypathhash, $registryfilecontent); + + # installer::files::save_file($registryfilename, $registryfilecontent); + # $tablelist = "Registry"; + # include_tables_into_pcpfile($msidatabase, $helperdir, $tablelist); + + # Saving info in Summary Information Stream of msi database (required for following patches) + write_sis_info($msidatabase); + + return $msidatabase; +} + +1; diff --git a/solenv/bin/modules/installer/windows/assembly.pm b/solenv/bin/modules/installer/windows/assembly.pm new file mode 100644 index 000000000000..7b2372603b70 --- /dev/null +++ b/solenv/bin/modules/installer/windows/assembly.pm @@ -0,0 +1,375 @@ +#************************************************************************* +# +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# Copyright 2008 by Sun Microsystems, Inc. +# +# OpenOffice.org - a multi-platform office productivity suite +# +# $RCSfile: assembly.pm,v $ +# +# $Revision: 1.11 $ +# +# This file is part of OpenOffice.org. +# +# OpenOffice.org is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License version 3 +# only, as published by the Free Software Foundation. +# +# OpenOffice.org is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License version 3 for more details +# (a copy is included in the LICENSE file that accompanied this code). +# +# You should have received a copy of the GNU Lesser General Public License +# version 3 along with OpenOffice.org. If not, see +# <http://www.openoffice.org/license.html> +# for a copy of the LGPLv3 License. +# +#************************************************************************* + +package installer::windows::assembly; + +use installer::files; +use installer::globals; +use installer::worker; +use installer::windows::idtglobal; + +############################################################## +# Returning the first module of a file from the +# comma separated list of modules. +############################################################## + +sub get_msiassembly_feature +{ + my ( $onefile ) = @_; + + my $module = ""; + + if ( $onefile->{'modules'} ) { $module = $onefile->{'modules'}; } + + # If modules contains a list of modules, only taking the first one. + + if ( $module =~ /^\s*(.*?)\,/ ) { $module = $1; } + + # Attention: Maximum feature length is 38! + installer::windows::idtglobal::shorten_feature_gid(\$module); + + return $module; +} + +############################################################## +# Returning the component of a file. +############################################################## + +sub get_msiassembly_component +{ + my ( $onefile ) = @_; + + my $component = ""; + + $component = $onefile->{'componentname'}; + + return $component; +} + +############################################################## +# Returning the file name as manifest file +############################################################## + +sub get_msiassembly_filemanifest +{ + my ( $onefile ) = @_; + + my $filemanifest = ""; + + $filemanifest = $onefile->{'uniquename'}; + # $filemanifest = $onefile->{'Name'}; + + return $filemanifest; +} + + +############################################################## +# Returning the file application +############################################################## + +sub get_msiassembly_fileapplication +{ + my ( $onefile ) = @_; + + my $fileapplication = ""; + + return $fileapplication; +} + +############################################################## +# Returning the file attributes +############################################################## + +sub get_msiassembly_attributes +{ + my ( $onefile ) = @_; + + my $fileattributes = ""; + + if ( $onefile->{'Attributes'} ne "" ) { $fileattributes = $onefile->{'Attributes'}; } + + return $fileattributes; +} + +############################################################## +# Returning the file object for the msiassembly table. +############################################################## + +sub get_msiassembly_file +{ + my ( $filesref, $filename ) = @_; + + my $foundfile = 0; + my $onefile; + + for ( my $i = 0; $i <= $#{$filesref}; $i++ ) + { + $onefile = ${$filesref}[$i]; + my $name = $onefile->{'Name'}; + + if ( $name eq $filename ) + { + $foundfile = 1; + last; + } + } + + # It does not need to exist. For example products that do not contain the libraries. + # if (! $foundfile ) { installer::exiter::exit_program("ERROR: No unique file name found for $filename !", "get_selfreg_file"); } + + if (! $foundfile ) { $onefile = ""; } + + return $onefile; +} + +############################################################## +# Returning the file object for the msiassembly table. +############################################################## + +sub get_msiassembly_file_by_gid +{ + my ( $filesref, $gid ) = @_; + + my $foundfile = 0; + my $onefile; + + for ( my $i = 0; $i <= $#{$filesref}; $i++ ) + { + $onefile = ${$filesref}[$i]; + my $filegid = $onefile->{'gid'}; + + if ( $filegid eq $gid ) + { + $foundfile = 1; + last; + } + } + + # It does not need to exist. For example products that do not contain the libraries. + # if (! $foundfile ) { installer::exiter::exit_program("ERROR: No unique file name found for $filename !", "get_selfreg_file"); } + + if (! $foundfile ) { $onefile = ""; } + + return $onefile; +} + +#################################################################################### +# Creating the file MsiAssembly.idt dynamically +# Content: +# Component_ Feature_ File_Manifest File_Application Attributes +# s72 s38 S72 S72 I2 +# MsiAssembly Component_ +#################################################################################### + +sub create_msiassembly_table +{ + my ($filesref, $basedir) = @_; + + $installer::globals::msiassemblyfiles = installer::worker::collect_all_items_with_special_flag($filesref, "ASSEMBLY"); + + my @msiassemblytable = (); + + installer::windows::idtglobal::write_idt_header(\@msiassemblytable, "msiassembly"); + + # Registering all libraries listed in $installer::globals::msiassemblyfiles + + for ( my $i = 0; $i <= $#{$installer::globals::msiassemblyfiles}; $i++ ) + { + my $onefile = ${$installer::globals::msiassemblyfiles}[$i]; + + my %msiassembly = (); + + $msiassembly{'Component_'} = get_msiassembly_component($onefile); + $msiassembly{'Feature_'} = get_msiassembly_feature($onefile); + $msiassembly{'File_Manifest'} = get_msiassembly_filemanifest($onefile); + $msiassembly{'File_Application'} = get_msiassembly_fileapplication($onefile); + $msiassembly{'Attributes'} = get_msiassembly_attributes($onefile); + + my $oneline = $msiassembly{'Component_'} . "\t" . $msiassembly{'Feature_'} . "\t" . + $msiassembly{'File_Manifest'} . "\t" . $msiassembly{'File_Application'} . "\t" . + $msiassembly{'Attributes'} . "\n"; + + push(@msiassemblytable, $oneline); + } + + # Saving the file + + my $msiassemblytablename = $basedir . $installer::globals::separator . "MsiAssem.idt"; + installer::files::save_file($msiassemblytablename ,\@msiassemblytable); + my $infoline = "Created idt file: $msiassemblytablename\n"; + push(@installer::globals::logfileinfo, $infoline); +} + +#################################################################################### +# Returning the name for the table MsiAssemblyName +#################################################################################### + +sub get_msiassemblyname_name +{ + ( $number ) = @_; + + my $name = ""; + + if ( $number == 1 ) { $name = "name"; } + elsif ( $number == 2 ) { $name = "publicKeyToken"; } + elsif ( $number == 3 ) { $name = "version"; } + elsif ( $number == 4 ) { $name = "culture"; } + + return $name; +} + +#################################################################################### +# Creating the file MsiAssemblyName.idt dynamically +# Content: +# Component_ Name Value +# s72 s255 s255 +# MsiAssemblyName Component_ Name +#################################################################################### + +sub create_msiassemblyname_table +{ + my ($filesref, $basedir) = @_; + + my @msiassemblynametable = (); + + installer::windows::idtglobal::write_idt_header(\@msiassemblynametable, "msiassemblyname"); + + for ( my $i = 0; $i <= $#{$installer::globals::msiassemblyfiles}; $i++ ) + { + my $onefile = ${$installer::globals::msiassemblyfiles}[$i]; + + my $component = get_msiassembly_component($onefile); + my $oneline = ""; + + # Order: (Assembly)name, publicKeyToken, version, culture. + + if ( $onefile->{'Assemblyname'} ) + { + $oneline = $component . "\t" . "name" . "\t" . $onefile->{'Assemblyname'} . "\n"; + push(@msiassemblynametable, $oneline); + } + + if ( $onefile->{'PublicKeyToken'} ) + { + $oneline = $component . "\t" . "publicKeyToken" . "\t" . $onefile->{'PublicKeyToken'} . "\n"; + push(@msiassemblynametable, $oneline); + } + + if ( $onefile->{'Version'} ) + { + $oneline = $component . "\t" . "version" . "\t" . $onefile->{'Version'} . "\n"; + push(@msiassemblynametable, $oneline); + } + + if ( $onefile->{'Culture'} ) + { + $oneline = $component . "\t" . "culture" . "\t" . $onefile->{'Culture'} . "\n"; + push(@msiassemblynametable, $oneline); + } + + if ( $onefile->{'ProcessorArchitecture'} ) + { + $oneline = $component . "\t" . "processorArchitecture" . "\t" . $onefile->{'ProcessorArchitecture'} . "\n"; + push(@msiassemblynametable, $oneline); + } + } + + # Saving the file + + my $msiassemblynametablename = $basedir . $installer::globals::separator . "MsiAsseN.idt"; + installer::files::save_file($msiassemblynametablename ,\@msiassemblynametable); + my $infoline = "Created idt file: $msiassemblynametablename\n"; + push(@installer::globals::logfileinfo, $infoline); + +} + +#################################################################################### +# setting an installation condition for the assembly libraries saved in +# @installer::globals::msiassemblynamecontent +#################################################################################### + +sub add_assembly_condition_into_component_table +{ + my ($filesref, $basedir) = @_; + + my $componenttablename = $basedir . $installer::globals::separator . "Componen.idt"; + my $componenttable = installer::files::read_file($componenttablename); + my $changed = 0; + my $infoline = ""; + + for ( my $i = 0; $i <= $#{$installer::globals::msiassemblyfiles}; $i++ ) + { + my $onefile = ${$installer::globals::msiassemblyfiles}[$i]; + + my $filecomponent = get_msiassembly_component($onefile); + + for ( my $j = 0; $j <= $#{$componenttable}; $j++ ) + { + my $oneline = ${$componenttable}[$j]; + + if ( $oneline =~ /(.*)\t(.*)\t(.*)\t(.*)\t(.*)\t(.*)/ ) + { + my $component = $1; + my $componentid = $2; + my $directory = $3; + my $attributes = $4; + my $condition = $5; + my $keypath = $6; + + if ( $component eq $filecomponent ) + { + # setting the condition + + # $condition = "MsiNetAssemblySupport"; + $condition = "DOTNET_SUFFICIENT=1"; + $oneline = $component . "\t" . $componentid . "\t" . $directory . "\t" . $attributes . "\t" . $condition . "\t" . $keypath . "\n"; + ${$componenttable}[$j] = $oneline; + $changed = 1; + $infoline = "Changing $componenttablename :\n"; + push(@installer::globals::logfileinfo, $infoline); + $infoline = $oneline; + push(@installer::globals::logfileinfo, $infoline); + last; + } + } + } + } + + if ( $changed ) + { + # Saving the file + installer::files::save_file($componenttablename ,$componenttable); + $infoline = "Saved idt file: $componenttablename\n"; + push(@installer::globals::logfileinfo, $infoline); + } +} + +1;
\ No newline at end of file diff --git a/solenv/bin/modules/installer/windows/binary.pm b/solenv/bin/modules/installer/windows/binary.pm new file mode 100644 index 000000000000..4f327b6d93c0 --- /dev/null +++ b/solenv/bin/modules/installer/windows/binary.pm @@ -0,0 +1,81 @@ +#************************************************************************* +# +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# Copyright 2008 by Sun Microsystems, Inc. +# +# OpenOffice.org - a multi-platform office productivity suite +# +# $RCSfile: binary.pm,v $ +# +# $Revision: 1.5 $ +# +# This file is part of OpenOffice.org. +# +# OpenOffice.org is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License version 3 +# only, as published by the Free Software Foundation. +# +# OpenOffice.org is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License version 3 for more details +# (a copy is included in the LICENSE file that accompanied this code). +# +# You should have received a copy of the GNU Lesser General Public License +# version 3 along with OpenOffice.org. If not, see +# <http://www.openoffice.org/license.html> +# for a copy of the LGPLv3 License. +# +#************************************************************************* + +package installer::windows::binary; + +use installer::existence; +use installer::files; +use installer::globals; + +########################################################################################################### +# Updating the table Binary dynamically with all files from $binarytablefiles +# Content: +# Name Data +# s72 v0 +# Binary Name +########################################################################################################### + +sub update_binary_table +{ + my ($languageidtdir, $filesref, $binarytablefiles) = @_; + + my $binaryidttablename = $languageidtdir . $installer::globals::separator . "Binary.idt"; + my $binaryidttable = installer::files::read_file($binaryidttablename); + + # Only the iconfiles, that are used in the shortcut table for the + # FolderItems (entries in Windows startmenu) are added into the icon table. + + for ( my $i = 0; $i <= $#{$binarytablefiles}; $i++ ) + { + my $binaryfile = ${$binarytablefiles}[$i]; + my $binaryfilename = $binaryfile->{'Name'}; + my $binaryfiledata = $binaryfilename; + + $binaryfilename =~ s/\.//g; # removing "." in filename: "abc.dll" to "abcdll" in name column + + my %binary = (); + + $binary{'Name'} = $binaryfilename; + $binary{'Data'} = $binaryfiledata; + + my $oneline = $binary{'Name'} . "\t" . $binary{'Data'} . "\n"; + + push(@{$binaryidttable}, $oneline); + } + + # Saving the file + + installer::files::save_file($binaryidttablename ,$binaryidttable); + my $infoline = "Updated idt file: $binaryidttablename\n"; + push(@installer::globals::logfileinfo, $infoline); +} + +1; diff --git a/solenv/bin/modules/installer/windows/component.pm b/solenv/bin/modules/installer/windows/component.pm new file mode 100644 index 000000000000..1cddc98cf6af --- /dev/null +++ b/solenv/bin/modules/installer/windows/component.pm @@ -0,0 +1,532 @@ +#************************************************************************* +# +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# Copyright 2008 by Sun Microsystems, Inc. +# +# OpenOffice.org - a multi-platform office productivity suite +# +# $RCSfile: component.pm,v $ +# +# $Revision: 1.14 $ +# +# This file is part of OpenOffice.org. +# +# OpenOffice.org is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License version 3 +# only, as published by the Free Software Foundation. +# +# OpenOffice.org is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License version 3 for more details +# (a copy is included in the LICENSE file that accompanied this code). +# +# You should have received a copy of the GNU Lesser General Public License +# version 3 along with OpenOffice.org. If not, see +# <http://www.openoffice.org/license.html> +# for a copy of the LGPLv3 License. +# +#************************************************************************* + +package installer::windows::component; + +use installer::converter; +use installer::existence; +use installer::exiter; +use installer::files; +use installer::globals; +use installer::windows::idtglobal; +use installer::windows::language; + +############################################################## +# Returning a globally unique ID (GUID) for a component +# If the component is new, a unique guid has to be created. +# If the component already exists, the guid has to be +# taken from a list component <-> guid +# Sample for a guid: {B68FD953-3CEF-4489-8269-8726848056E8} +############################################################## + +sub get_component_guid +{ + my ( $componentname, $componentidhashref ) = @_; + + # At this time only a template + my $returnvalue = "\{COMPONENTGUID\}"; + + if (( $installer::globals::updatedatabase ) && ( exists($componentidhashref->{$componentname}) )) + { + $returnvalue = $componentidhashref->{$componentname}; + } + + # Returning a ComponentID, that is assigned in scp project + if ( exists($installer::globals::componentid{$componentname}) ) + { + $returnvalue = "\{" . $installer::globals::componentid{$componentname} . "\}"; + } + + return $returnvalue; +} + +############################################################## +# Returning the directory for a file component. +############################################################## + +sub get_file_component_directory +{ + my ($componentname, $filesref, $dirref) = @_; + + my ($onefile, $component, $onedir, $hostname, $uniquedir); + my $found = 0; + + for ( my $i = 0; $i <= $#{$filesref}; $i++ ) + { + $onefile = ${$filesref}[$i]; + $component = $onefile->{'componentname'}; + + if ( $component eq $componentname ) + { + $found = 1; + last; + } + } + + if (!($found)) + { + # This component can be ignored, if it exists in a version with extension "_pff" (this was renamed in file::get_sequence_for_file() ) + my $ignore_this_component = 0; + my $origcomponentname = $componentname; + my $componentname = $componentname . "_pff"; + + for ( my $j = 0; $j <= $#{$filesref}; $j++ ) + { + $onefile = ${$filesref}[$j]; + $component = $onefile->{'componentname'}; + + if ( $component eq $componentname ) + { + $ignore_this_component = 1; + last; + } + } + + if ( $ignore_this_component ) { return "IGNORE_COMP"; } + else { installer::exiter::exit_program("ERROR: Did not find component \"$origcomponentname\" in file collection", "get_file_component_directory"); } + } + + my $localstyles = ""; + + if ( $onefile->{'Styles'} ) { $localstyles = $onefile->{'Styles'}; } + + if ( $localstyles =~ /\bFONT\b/ ) # special handling for font files + { + return $installer::globals::fontsfolder; + } + + my $destdir = ""; + + if ( $onefile->{'Dir'} ) { $destdir = $onefile->{'Dir'}; } + + if ( $destdir =~ /\bPREDEFINED_OSSHELLNEWDIR\b/ ) # special handling for shellnew files + { + return $installer::globals::templatefolder; + } + + my $destination = $onefile->{'destination'}; + + installer::pathanalyzer::get_path_from_fullqualifiedname(\$destination); + + $destination =~ s/\Q$installer::globals::separator\E\s*$//; + + # This path has to be defined in the directory collection at "HostName" + + if ($destination eq "") # files in the installation root + { + $uniquedir = "INSTALLLOCATION"; + } + else + { + $found = 0; + + for ( my $i = 0; $i <= $#{$dirref}; $i++ ) + { + $onedir = ${$dirref}[$i]; + $hostname = $onedir->{'HostName'}; + + if ( $hostname eq $destination ) + { + $found = 1; + last; + } + } + + if (!($found)) + { + installer::exiter::exit_program("ERROR: Did not find destination $destination in directory collection", "get_file_component_directory"); + } + + $uniquedir = $onedir->{'uniquename'}; + } + + $onefile->{'uniquedirname'} = $uniquedir; # saving it in the file collection + + return $uniquedir +} + +############################################################## +# Returning the directory for a registry component. +# This cannot be a useful value +############################################################## + +sub get_registry_component_directory +{ + my $componentdir = ""; + + if ( $installer::globals::officeinstalldirectoryset ) + { + $componentdir = $installer::globals::officeinstalldirectory; + } + else + { + $componentdir = "INSTALLLOCATION"; + } + + return $componentdir; +} + +############################################################## +# Returning the attributes for a file component. +# Always 8 in this first try? +############################################################## + +sub get_file_component_attributes +{ + my ($componentname, $filesref) = @_; + + my $attributes; + + $attributes = 2; + + # special handling for font files + + my $onefile; + my $found = 0; + + for ( my $i = 0; $i <= $#{$filesref}; $i++ ) + { + $onefile = ${$filesref}[$i]; + my $component = $onefile->{'componentname'}; + + if ( $component eq $componentname ) + { + $found = 1; + last; + } + } + + if (!($found)) + { + installer::exiter::exit_program("ERROR: Did not find component in file collection", "get_file_component_attributes"); + } + + my $localstyles = ""; + + if ( $onefile->{'Styles'} ) { $localstyles = $onefile->{'Styles'}; } + + if ( $localstyles =~ /\bFONT\b/ ) + { + $attributes = 16; # font files will not be deinstalled + } + + if ( $localstyles =~ /\bASSEMBLY\b/ ) + { + $attributes = 0; # Assembly files cannot run from source + } + + if (( $onefile->{'Dir'} =~ /\bPREDEFINED_OSSHELLNEWDIR\b/ ) || ( $onefile->{'needs_user_registry_key'} )) + { + $attributes = 4; # Files in shellnew dir and in non advertised startmenu entries must have user registry key as KeyPath + } + + return $attributes +} + +############################################################## +# Returning the attributes for a registry component. +# Always 4, indicating, the keypath is a defined in +# table registry +############################################################## + +sub get_registry_component_attributes +{ + my ($componentname) = @_; + + my $attributes; + + $attributes = 4; + + if ( exists($installer::globals::dontdeletecomponents{$componentname}) ) { $attributes = $attributes + 16; } + + return $attributes +} + +############################################################## +# Returning the conditions for a component. +# This is important for language dependent components +# in multilingual installation sets. +############################################################## + +sub get_file_component_condition +{ + my ($componentname, $filesref) = @_; + + my $condition = ""; + + if (exists($installer::globals::componentcondition{$componentname})) + { + $condition = $installer::globals::componentcondition{$componentname}; + } + + # there can be also tree conditions for multilayer products + if (exists($installer::globals::treeconditions{$componentname})) + { + if ( $condition eq "" ) + { + $condition = $installer::globals::treeconditions{$componentname}; + } + else + { + $condition = "($condition) And ($installer::globals::treeconditions{$componentname})"; + } + } + + return $condition +} + +############################################################## +# Returning the conditions for a registry component. +############################################################## + +sub get_component_condition +{ + my ($componentname) = @_; + + my $condition; + + $condition = ""; # Always ? + + if (exists($installer::globals::componentcondition{$componentname})) + { + $condition = $installer::globals::componentcondition{$componentname}; + } + + return $condition +} + +#################################################################### +# Returning the keypath for a component. +# This will be the name of the first file/registry, found in the +# collection $itemsref +# Attention: This has to be the unique (file)name, not the +# real filename! +#################################################################### + +sub get_component_keypath +{ + my ($componentname, $itemsref, $componentidkeypathhashref) = @_; + + my $oneitem; + my $found = 0; + my $infoline = ""; + + for ( my $i = 0; $i <= $#{$itemsref}; $i++ ) + { + $oneitem = ${$itemsref}[$i]; + my $component = $oneitem->{'componentname'}; + + if ( $component eq $componentname ) + { + $found = 1; + last; + } + } + + if (!($found)) + { + installer::exiter::exit_program("ERROR: Did not find component in file/registry collection, function get_component_keypath", "get_component_keypath"); + } + + my $keypath = $oneitem->{'uniquename'}; # "uniquename", not "Name" + + # Special handling for updates from existing databases, because KeyPath must not change + if (( $installer::globals::updatedatabase ) && ( exists($componentidkeypathhashref->{$componentname}) )) + { + $keypath = $componentidkeypathhashref->{$componentname}; + # -> check, if this is a valid key path?! + if ( $keypath ne $oneitem->{'uniquename'} ) + { + # Warning: This keypath was changed because of info from old database + $infoline = "WARNING: The KeyPath for component \"$componentname\" was changed from \"$oneitem->{'uniquename'}\" to \"$keypath\" because of information from update database"; + push(@installer::globals::logfileinfo, $infoline); + } + } + + # Special handling for components in PREDEFINED_OSSHELLNEWDIR. These components + # need as KeyPath a RegistryItem in HKCU + if ( $oneitem->{'userregkeypath'} ) { $keypath = $oneitem->{'userregkeypath'}; } + + # saving it in the file and registry collection + $oneitem->{'keypath'} = $keypath; + + return $keypath +} + +################################################################### +# Creating the file Componen.idt dynamically +# Content: +# Component ComponentId Directory_ Attributes Condition KeyPath +################################################################### + +sub create_component_table +{ + my ($filesref, $registryref, $dirref, $allfilecomponentsref, $allregistrycomponents, $basedir, $componentidhashref, $componentidkeypathhashref) = @_; + + my @componenttable = (); + + my ($oneline, $infoline); + + installer::windows::idtglobal::write_idt_header(\@componenttable, "component"); + + # collect_layer_conditions(); + + + # File components + + for ( my $i = 0; $i <= $#{$allfilecomponentsref}; $i++ ) + { + my %onecomponent = (); + + $onecomponent{'name'} = ${$allfilecomponentsref}[$i]; + $onecomponent{'guid'} = get_component_guid($onecomponent{'name'}, $componentidhashref); + $onecomponent{'directory'} = get_file_component_directory($onecomponent{'name'}, $filesref, $dirref); + if ( $onecomponent{'directory'} eq "IGNORE_COMP" ) { next; } + $onecomponent{'attributes'} = get_file_component_attributes($onecomponent{'name'}, $filesref); + $onecomponent{'condition'} = get_file_component_condition($onecomponent{'name'}, $filesref); + $onecomponent{'keypath'} = get_component_keypath($onecomponent{'name'}, $filesref, $componentidkeypathhashref); + + $oneline = $onecomponent{'name'} . "\t" . $onecomponent{'guid'} . "\t" . $onecomponent{'directory'} . "\t" + . $onecomponent{'attributes'} . "\t" . $onecomponent{'condition'} . "\t" . $onecomponent{'keypath'} . "\n"; + + push(@componenttable, $oneline); + } + + # Registry components + + for ( my $i = 0; $i <= $#{$allregistrycomponents}; $i++ ) + { + my %onecomponent = (); + + $onecomponent{'name'} = ${$allregistrycomponents}[$i]; + $onecomponent{'guid'} = get_component_guid($onecomponent{'name'}, $componentidhashref); + $onecomponent{'directory'} = get_registry_component_directory(); + $onecomponent{'attributes'} = get_registry_component_attributes($onecomponent{'name'}); + $onecomponent{'condition'} = get_component_condition($onecomponent{'name'}); + $onecomponent{'keypath'} = get_component_keypath($onecomponent{'name'}, $registryref, $componentidkeypathhashref); + + $oneline = $onecomponent{'name'} . "\t" . $onecomponent{'guid'} . "\t" . $onecomponent{'directory'} . "\t" + . $onecomponent{'attributes'} . "\t" . $onecomponent{'condition'} . "\t" . $onecomponent{'keypath'} . "\n"; + + push(@componenttable, $oneline); + } + + # Saving the file + + my $componenttablename = $basedir . $installer::globals::separator . "Componen.idt"; + installer::files::save_file($componenttablename ,\@componenttable); + $infoline = "Created idt file: $componenttablename\n"; + push(@installer::globals::logfileinfo, $infoline); +} + +#################################################################################### +# Returning a component for a scp module gid. +# Pairs are saved in the files collector. +#################################################################################### + +sub get_component_name_from_modulegid +{ + my ($modulegid, $filesref) = @_; + + my $componentname = ""; + + for ( my $i = 0; $i <= $#{$filesref}; $i++ ) + { + my $onefile = ${$filesref}[$i]; + + if ( $onefile->{'modules'} ) + { + my $filemodules = $onefile->{'modules'}; + my $filemodulesarrayref = installer::converter::convert_stringlist_into_array_without_newline(\$filemodules, ","); + + if (installer::existence::exists_in_array($modulegid, $filemodulesarrayref)) + { + $componentname = $onefile->{'componentname'}; + last; + } + } + } + + return $componentname; +} + +#################################################################################### +# Updating the file Environm.idt dynamically +# Content: +# Environment Name Value Component_ +#################################################################################### + +sub set_component_in_environment_table +{ + my ($basedir, $filesref) = @_; + + my $infoline = ""; + + my $environmentfilename = $basedir . $installer::globals::separator . "Environm.idt"; + + if ( -f $environmentfilename ) # only do something, if file exists + { + my $environmentfile = installer::files::read_file($environmentfilename); + + for ( my $i = 3; $i <= $#{$environmentfile}; $i++ ) # starting in line 4 of Environm.idt + { + if ( ${$environmentfile}[$i] =~ /^\s*(.*?)\t(.*?)\t(.*?)\t(.*?)\s*$/ ) + { + my $modulegid = $4; # in Environment table a scp module gid can be used as component replacement + + my $componentname = get_component_name_from_modulegid($modulegid, $filesref); + + if ( $componentname ) # only do something if a component could be found + { + $infoline = "Updated Environment table:\n"; + push(@installer::globals::logfileinfo, $infoline); + $infoline = "Old line: ${$environmentfile}[$i]\n"; + push(@installer::globals::logfileinfo, $infoline); + + ${$environmentfile}[$i] =~ s/$modulegid/$componentname/; + + $infoline = "New line: ${$environmentfile}[$i]\n"; + push(@installer::globals::logfileinfo, $infoline); + + } + } + } + + # Saving the file + + installer::files::save_file($environmentfilename ,$environmentfile); + $infoline = "Updated idt file: $environmentfilename\n"; + push(@installer::globals::logfileinfo, $infoline); + + } +} + +1;
\ No newline at end of file diff --git a/solenv/bin/modules/installer/windows/createfolder.pm b/solenv/bin/modules/installer/windows/createfolder.pm new file mode 100644 index 000000000000..3abb3acad3d3 --- /dev/null +++ b/solenv/bin/modules/installer/windows/createfolder.pm @@ -0,0 +1,157 @@ +#************************************************************************* +# +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# Copyright 2008 by Sun Microsystems, Inc. +# +# OpenOffice.org - a multi-platform office productivity suite +# +# $RCSfile: createfolder.pm,v $ +# +# $Revision: 1.7 $ +# +# This file is part of OpenOffice.org. +# +# OpenOffice.org is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License version 3 +# only, as published by the Free Software Foundation. +# +# OpenOffice.org is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License version 3 for more details +# (a copy is included in the LICENSE file that accompanied this code). +# +# You should have received a copy of the GNU Lesser General Public License +# version 3 along with OpenOffice.org. If not, see +# <http://www.openoffice.org/license.html> +# for a copy of the LGPLv3 License. +# +#************************************************************************* + +package installer::windows::createfolder; + +use installer::existence; +use installer::exiter; +use installer::files; +use installer::globals; +use installer::windows::idtglobal; + +############################################################## +# Returning directory for createfolder table. +############################################################## + +sub get_createfolder_directory +{ + my ($onedir) = @_; + + my $uniquename = $onedir->{'uniquename'}; + + return $uniquename; +} + +############################################################## +# Searching the correct file for language pack directories. +############################################################## + +sub get_languagepack_file +{ + my ($filesref, $onedir) = @_; + + my $language = $onedir->{'specificlanguage'}; + my $foundfile = 0; + my $onefile = ""; + + for ( my $i = 0; $i <= $#{$filesref}; $i++ ) + { + $onefile = ${$filesref}[$i]; + + if ( $onefile->{'specificlanguage'} eq $onedir->{'specificlanguage'} ) + { + $foundfile = 1; + last; + } + } + + if ( ! $foundfile ) { installer::exiter::exit_program("ERROR: No file with correct language found (language pack build)!", "get_languagepack_file"); } + + return $onefile; +} + +############################################################## +# Returning component for createfolder table. +############################################################## + +sub get_createfolder_component +{ + my ($onedir, $filesref, $allvariableshashref) = @_; + + # Directories do not belong to a module. + # Therefore they can only belong to the root module and + # will be added to a component at the root module. + # All directories will be added to the component + # containing the file $allvariableshashref->{'GLOBALFILEGID'} + + if ( ! $allvariableshashref->{'GLOBALFILEGID'} ) { installer::exiter::exit_program("ERROR: GLOBALFILEGID must be defined in list file!", "get_createfolder_component"); } + if (( $installer::globals::patch ) && ( ! $allvariableshashref->{'GLOBALFILEGID'} )) { installer::exiter::exit_program("ERROR: GLOBALPATCHFILEGID must be defined in list file!", "get_createfolder_component"); } + + my $globalfilegid = $allvariableshashref->{'GLOBALFILEGID'}; + if ( $installer::globals::patch ) { $globalfilegid = $allvariableshashref->{'GLOBALPATCHFILEGID'}; } + + my $onefile = ""; + if ( $installer::globals::languagepack ) { $onefile = get_languagepack_file($filesref, $onedir); } + else { $onefile = installer::existence::get_specified_file($filesref, $globalfilegid); } + + return $onefile->{'componentname'}; +} + +#################################################################################### +# Creating the file CreateFo.idt dynamically for creation of empty directories +# Content: +# Directory_ Component_ +#################################################################################### + +sub create_createfolder_table +{ + my ($dirref, $filesref, $basedir, $allvariableshashref) = @_; + + my @createfoldertable = (); + + my $infoline; + + installer::windows::idtglobal::write_idt_header(\@createfoldertable, "createfolder"); + + for ( my $i = 0; $i <= $#{$dirref}; $i++ ) + { + my $onedir = ${$dirref}[$i]; + + # language packs get only language dependent directories + if (( $installer::globals::languagepack ) && ( $onedir->{'specificlanguage'} eq "" )) { next }; + + my $styles = ""; + + if ( $onedir->{'Styles'} ) { $styles = $onedir->{'Styles'}; } + + if ( $styles =~ /\bCREATE\b/ ) + { + my %directory = (); + + $directory{'Directory_'} = get_createfolder_directory($onedir); + $directory{'Component_'} = get_createfolder_component($onedir, $filesref, $allvariableshashref); + + my $oneline = $directory{'Directory_'} . "\t" . $directory{'Component_'} . "\n"; + + push(@createfoldertable, $oneline); + } + } + + # Saving the file + + my $createfoldertablename = $basedir . $installer::globals::separator . "CreateFo.idt"; + installer::files::save_file($createfoldertablename ,\@createfoldertable); + $infoline = "Created idt file: $createfoldertablename\n"; + push(@installer::globals::logfileinfo, $infoline); + +} + +1;
\ No newline at end of file diff --git a/solenv/bin/modules/installer/windows/directory.pm b/solenv/bin/modules/installer/windows/directory.pm new file mode 100644 index 000000000000..94ac41d8cb92 --- /dev/null +++ b/solenv/bin/modules/installer/windows/directory.pm @@ -0,0 +1,439 @@ +#************************************************************************* +# +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# Copyright 2008 by Sun Microsystems, Inc. +# +# OpenOffice.org - a multi-platform office productivity suite +# +# $RCSfile: directory.pm,v $ +# +# $Revision: 1.30.126.1 $ +# +# This file is part of OpenOffice.org. +# +# OpenOffice.org is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License version 3 +# only, as published by the Free Software Foundation. +# +# OpenOffice.org is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License version 3 for more details +# (a copy is included in the LICENSE file that accompanied this code). +# +# You should have received a copy of the GNU Lesser General Public License +# version 3 along with OpenOffice.org. If not, see +# <http://www.openoffice.org/license.html> +# for a copy of the LGPLv3 License. +# +#************************************************************************* + +package installer::windows::directory; + +use installer::exiter; +use installer::files; +use installer::globals; +use installer::pathanalyzer; +use installer::windows::idtglobal; + +############################################################## +# Collecting all directory trees in global hash +############################################################## + +sub collectdirectorytrees +{ + my ( $directoryref ) = @_; + + for ( my $i = 0; $i <= $#{$directoryref}; $i++ ) + { + my $onedir = ${$directoryref}[$i]; + my $styles = ""; + if ( $onedir->{'Styles'} ) { $styles = $onedir->{'Styles'}; } + + if ( $styles ne "" ) + { + foreach my $treestyle ( keys %installer::globals::treestyles ) + { + if ( $styles =~ /\b$treestyle\b/ ) + { + my $hostname = $onedir->{'HostName'}; + # -> hostname is the key, the style the value! + $installer::globals::hostnametreestyles{$hostname} = $treestyle; + } + } + } + } +} + +############################################################## +# Overwriting global programfilesfolder, if required +############################################################## + +sub overwrite_programfilesfolder +{ + my ( $allvariables ) = @_; + + if ( $allvariables->{'PROGRAMFILESFOLDERNAME'} ) + { + $installer::globals::programfilesfolder = $allvariables->{'PROGRAMFILESFOLDERNAME'}; + } +} + +############################################################## +# Adding unique directory names to the directory collection +############################################################## + +sub create_unique_directorynames +{ + my ($directoryref) = @_; + + $installer::globals::officeinstalldirectoryset = 0; + + for ( my $i = 0; $i <= $#{$directoryref}; $i++ ) + { + my $onedir = ${$directoryref}[$i]; + my $uniquename = $onedir->{'HostName'}; + my $styles = ""; + if ( $onedir->{'Styles'} ) { $styles = $onedir->{'Styles'}; } + # get_path_from_fullqualifiedname(\$uniqueparentname); + # making /registry/schema/org/openoffice/VCL.xcs to VCL.xcs + + $uniquename =~ s/^\s*//g; # removing beginning white spaces + $uniquename =~ s/\s*$//g; # removing ending white spaces + $uniquename =~ s/\s//g; # removing white spaces + $uniquename =~ s/\_//g; # removing existing underlines + $uniquename =~ s/\.//g; # removing dots in directoryname + $uniquename =~ s/\Q$installer::globals::separator\E/\_/g; # replacing slash and backslash with underline + + my $uniqueparentname = $uniquename; + + if ( $uniqueparentname =~ /^\s*(.*)\_(.*?)\s*$/ ) # the underline is now the separator + { + $uniqueparentname = $1; + } + else + { + $uniqueparentname = "INSTALLLOCATION"; + } + + if ( $styles =~ /\bPROGRAMFILESFOLDER\b/ ) { $uniqueparentname = $installer::globals::programfilesfolder; } + if ( $styles =~ /\bCOMMONFILESFOLDER\b/ ) { $uniqueparentname = $installer::globals::commonfilesfolder; } + if ( $styles =~ /\bCOMMONAPPDATAFOLDER\b/ ) { $uniqueparentname = $installer::globals::commonappdatafolder; } + if ( $styles =~ /\bLOCALAPPDATAFOLDER\b/ ) { $uniqueparentname = $installer::globals::localappdatafolder; } + + if ( $styles =~ /\bSHAREPOINTPATH\b/ ) + { + $uniqueparentname = "SHAREPOINTPATH"; + $installer::globals::usesharepointpath = 1; + } + + $uniquename =~ s/\-/\_/g; # making "-" to "_" + $uniqueparentname =~ s/\-/\_/g; # making "-" to "_" + + $onedir->{'uniquename'} = $uniquename; + $onedir->{'uniqueparentname'} = $uniqueparentname; + + # setting the office installation directory + if ( $styles =~ /\bOFFICEDIRECTORY\b/ ) + { + if ( $installer::globals::officeinstalldirectoryset ) { installer::exiter::exit_program("ERROR: Directory with flag OFFICEDIRECTORY alread set: \"$installer::globals::officeinstalldirectory\".", "create_unique_directorynames"); } + $installer::globals::officeinstalldirectory = $uniquename; + $installer::globals::officeinstalldirectoryset = 1; + if ( $installer::globals::officeinstalldirectory =~ /sun_/i ) { $installer::globals::sundirexists = 1; } + } + + # setting the bais installation directory + if ( $styles =~ /\bBASISDIRECTORY\b/ ) + { + if ( $installer::globals::basisinstalldirectoryset ) { installer::exiter::exit_program("ERROR: Directory with flag BASISDIRECTORY alread set: \"$installer::globals::basisinstalldirectory\".", "create_unique_directorynames"); } + $installer::globals::basisinstalldirectory = $uniquename; + $installer::globals::basisinstalldirectoryset = 1; + } + + # setting the ure installation directory + if ( $styles =~ /\bUREDIRECTORY\b/ ) + { + if ( $installer::globals::ureinstalldirectoryset ) { installer::exiter::exit_program("ERROR: Directory with flag UREDIRECTORY alread set: \"$installer::globals::ureinstalldirectory\".", "create_unique_directorynames"); } + $installer::globals::ureinstalldirectory = $uniquename; + $installer::globals::ureinstalldirectoryset = 1; + } + } +} + +##################################################### +# Getting the name of the top level directory. This +# can have only one letter +##################################################### + +sub get_last_directory_name +{ + my ($completepathref) = @_; + + if ( $$completepathref =~ /^.*[\/\\](.+?)\s*$/ ) + { + $$completepathref = $1; + } +} + +##################################################### +# Creating the defaultdir for the file Director.idt +##################################################### + +sub create_defaultdir_directorynames +{ + my ($directoryref, $shortdirnamehashref) = @_; + + my @shortnames = (); + if ( $installer::globals::updatedatabase ) { @shortnames = values(%{$shortdirnamehashref}); } + elsif ( $installer::globals::prepare_winpatch ) { @shortnames = values(%installer::globals::saved83dirmapping); } + + for ( my $i = 0; $i <= $#{$directoryref}; $i++ ) + { + my $onedir = ${$directoryref}[$i]; + my $hostname = $onedir->{'HostName'}; + + $hostname =~ s/\Q$installer::globals::separator\E\s*$//; + get_last_directory_name(\$hostname); + # installer::pathanalyzer::make_absolute_filename_to_relative_filename(\$hostname); # making program/classes to classes + my $uniquename = $onedir->{'uniquename'}; + my $shortstring; + if (( $installer::globals::updatedatabase ) && ( exists($shortdirnamehashref->{$uniquename}) )) + { + $shortstring = $shortdirnamehashref->{$uniquename}; + } + elsif (( $installer::globals::prepare_winpatch ) && ( exists($installer::globals::saved83dirmapping{$uniquename}) )) + { + $shortstring = $installer::globals::saved83dirmapping{$uniquename}; + } + else + { + $shortstring = installer::windows::idtglobal::make_eight_three_conform($hostname, "dir", \@shortnames); + } + + my $defaultdir; + + if ( $shortstring eq $hostname ) + { + $defaultdir = $hostname; + } + else + { + $defaultdir = $shortstring . "|" . $hostname; + } + + $onedir->{'defaultdir'} = $defaultdir; + + my $fontdir = ""; + if ( $onedir->{'Dir'} ) { $fontdir = $onedir->{'Dir'}; } + + my $fontdefaultdir = ""; + if ( $onedir->{'defaultdir'} ) { $fontdefaultdir = $onedir->{'defaultdir'}; } + + if (( $fontdir eq "PREDEFINED_OSSYSTEMFONTDIR" ) && ( $fontdefaultdir eq $installer::globals::fontsdirhostname )) + { + $installer::globals::fontsdirname = $onedir->{'defaultdir'}; + $installer::globals::fontsdirparent = $onedir->{'uniqueparentname'}; + } + } +} + +############################################### +# Fill content into the directory table +############################################### + +sub create_directorytable_from_collection +{ + my ($directorytableref, $directoryref) = @_; + + for ( my $i = 0; $i <= $#{$directoryref}; $i++ ) + { + my $onedir = ${$directoryref}[$i]; + my $hostname = $onedir->{'HostName'}; + my $dir = ""; + + if ( $onedir->{'Dir'} ) { $dir = $onedir->{'Dir'}; } + + if (( $dir eq "PREDEFINED_PROGDIR" ) && ( $hostname eq "" )) { next; } # removing files from root directory + + my $oneline = $onedir->{'uniquename'} . "\t" . $onedir->{'uniqueparentname'} . "\t" . $onedir->{'defaultdir'} . "\n"; + + push(@{$directorytableref}, $oneline); + } +} + +############################################### +# Defining the root installation structure +############################################### + +sub add_root_directories +{ + my ($directorytableref, $allvariableshashref) = @_; + + my $oneline = "TARGETDIR\t\tSourceDir\n"; + push(@{$directorytableref}, $oneline); + + my $sourcediraddon = ""; + if (($installer::globals::addchildprojects) || + ($installer::globals::patch) || + ($installer::globals::languagepack) || + ($allvariableshashref->{'CHANGETARGETDIR'})) + { + $sourcediraddon = "\:\."; + } + + if (!($installer::globals::product =~ /ada/i )) # the following directories not for ada products + { + $oneline = "$installer::globals::programfilesfolder\tTARGETDIR\t.\n"; + push(@{$directorytableref}, $oneline); + + # my $manufacturer = $installer::globals::manufacturer; + # my $shortmanufacturer = installer::windows::idtglobal::make_eight_three_conform($manufacturer, "dir"); # third parameter not used + # $shortmanufacturer =~ s/\s/\_/g; # changing empty space to underline + + my $productname = $allvariableshashref->{'PRODUCTNAME'}; + my $productversion = $allvariableshashref->{'PRODUCTVERSION'}; + my $baseproductversion = $productversion; + + if (( $installer::globals::prepare_winpatch ) && ( $allvariableshashref->{'BASEPRODUCTVERSION'} )) + { + $baseproductversion = $allvariableshashref->{'BASEPRODUCTVERSION'}; # for example "2.0" for OOo + } + + my $realproductkey = $productname . " " . $productversion; + my $productkey = $productname . " " . $baseproductversion; + + if (( $allvariableshashref->{'POSTVERSIONEXTENSION'} ) && ( ! $allvariableshashref->{'DONTUSEEXTENSIONINDEFAULTDIR'} )) + { + $productkey = $productkey . " " . $allvariableshashref->{'POSTVERSIONEXTENSION'}; + $realproductkey = $realproductkey . " " . $allvariableshashref->{'POSTVERSIONEXTENSION'}; + } + if ( $allvariableshashref->{'NOVERSIONINDIRNAME'} ) + { + $productkey = $productname; + $realproductkey = $realproductname; + } + if ( $allvariableshashref->{'NOSPACEINDIRECTORYNAME'} ) + { + $productkey =~ s/\ /\_/g; + $realproductkey =~ s/\ /\_/g; + } + + my $shortproductkey = installer::windows::idtglobal::make_eight_three_conform($productkey, "dir"); # third parameter not used + $shortproductkey =~ s/\s/\_/g; # changing empty space to underline + + if ( $allvariableshashref->{'SUNDIR'} ) + { + $oneline = "sundirectory\t$installer::globals::programfilesfolder\t$installer::globals::sundirname$sourcediraddon\n"; + push(@{$directorytableref}, $oneline); + + $oneline = "INSTALLLOCATION\tsundirectory\t$shortproductkey|$productkey$sourcediraddon\n"; + push(@{$directorytableref}, $oneline); + } + else + { + if ( $allvariableshashref->{'PROGRAMFILESROOT'} ) + { + $oneline = "INSTALLLOCATION\t$installer::globals::programfilesfolder\t.\n"; + } + else + { + $oneline = "INSTALLLOCATION\t$installer::globals::programfilesfolder\t$shortproductkey|$productkey$sourcediraddon\n"; + } + + push(@{$directorytableref}, $oneline); + } + + $oneline = "$installer::globals::programmenufolder\tTARGETDIR\t.\n"; + push(@{$directorytableref}, $oneline); + + if (( ! $installer::globals::patch ) && ( ! $installer::globals::languagepack ) && ( ! $allvariableshashref->{'DONTUSESTARTMENUFOLDER'} )) + { + $oneline = "$installer::globals::officemenufolder\t$installer::globals::programmenufolder\t$shortproductkey|$realproductkey\n"; + push(@{$directorytableref}, $oneline); + } + + $oneline = "$installer::globals::startupfolder\tTARGETDIR\t.\n"; + push(@{$directorytableref}, $oneline); + + $oneline = "$installer::globals::desktopfolder\tTARGETDIR\t.\n"; + push(@{$directorytableref}, $oneline); + + $oneline = "$installer::globals::startmenufolder\tTARGETDIR\t.\n"; + push(@{$directorytableref}, $oneline); + + $oneline = "$installer::globals::commonfilesfolder\tTARGETDIR\t.\n"; + push(@{$directorytableref}, $oneline); + + $oneline = "$installer::globals::commonappdatafolder\tTARGETDIR\t.\n"; + push(@{$directorytableref}, $oneline); + + $oneline = "$installer::globals::localappdatafolder\tTARGETDIR\t.\n"; + push(@{$directorytableref}, $oneline); + + if ( $installer::globals::usesharepointpath ) + { + $oneline = "SHAREPOINTPATH\tTARGETDIR\t.\n"; + push(@{$directorytableref}, $oneline); + } + + $oneline = "$installer::globals::systemfolder\tTARGETDIR\t.\n"; + push(@{$directorytableref}, $oneline); + + my $localtemplatefoldername = $installer::globals::templatefoldername; + my $directorytableentry = $localtemplatefoldername; + my $shorttemplatefoldername = installer::windows::idtglobal::make_eight_three_conform($localtemplatefoldername, "dir"); + if ( $shorttemplatefoldername ne $localtemplatefoldername ) { $directorytableentry = "$shorttemplatefoldername|$localtemplatefoldername"; } + $oneline = "$installer::globals::templatefolder\tTARGETDIR\t$directorytableentry\n"; + push(@{$directorytableref}, $oneline); + + if ( $installer::globals::fontsdirname ) + { + $oneline = "$installer::globals::fontsfolder\t$installer::globals::fontsdirparent\t$installer::globals::fontsfoldername\:$installer::globals::fontsdirname\n"; + } + else + { + $oneline = "$installer::globals::fontsfolder\tTARGETDIR\t$installer::globals::fontsfoldername\n"; + } + + push(@{$directorytableref}, $oneline); + } + +} + +############################################### +# Creating the file Director.idt dynamically +############################################### + +sub create_directory_table +{ + my ($directoryref, $basedir, $allvariableshashref, $shortdirnamehashref) = @_; + + # Structure of the directory table: + # Directory Directory_Parent DefaultDir + # Directory is a unique identifier + # Directory_Parent is the unique identifier of the parent + # DefaultDir is .:APPLIC~1|Application Data with + # Before ":" : [sourcedir]:[destdir] (not programmed yet) + # After ":" : 8+3 and not 8+3 the destination directory name + + my @directorytable = (); + my $infoline; + + overwrite_programfilesfolder($allvariableshashref); + create_unique_directorynames($directoryref); + create_defaultdir_directorynames($directoryref, $shortdirnamehashref); # only destdir! + installer::windows::idtglobal::write_idt_header(\@directorytable, "directory"); + add_root_directories(\@directorytable, $allvariableshashref); + create_directorytable_from_collection(\@directorytable, $directoryref); + + # Saving the file + + my $directorytablename = $basedir . $installer::globals::separator . "Director.idt"; + installer::files::save_file($directorytablename ,\@directorytable); + $infoline = "Created idt file: $directorytablename\n"; + push(@installer::globals::logfileinfo, $infoline); + +} + +1; diff --git a/solenv/bin/modules/installer/windows/feature.pm b/solenv/bin/modules/installer/windows/feature.pm new file mode 100644 index 000000000000..b422dc45eb65 --- /dev/null +++ b/solenv/bin/modules/installer/windows/feature.pm @@ -0,0 +1,449 @@ +#************************************************************************* +# +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# Copyright 2008 by Sun Microsystems, Inc. +# +# OpenOffice.org - a multi-platform office productivity suite +# +# $RCSfile: feature.pm,v $ +# +# $Revision: 1.24 $ +# +# This file is part of OpenOffice.org. +# +# OpenOffice.org is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License version 3 +# only, as published by the Free Software Foundation. +# +# OpenOffice.org is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License version 3 for more details +# (a copy is included in the LICENSE file that accompanied this code). +# +# You should have received a copy of the GNU Lesser General Public License +# version 3 along with OpenOffice.org. If not, see +# <http://www.openoffice.org/license.html> +# for a copy of the LGPLv3 License. +# +#************************************************************************* + +package installer::windows::feature; + +use installer::existence; +use installer::exiter; +use installer::files; +use installer::globals; +use installer::sorter; +use installer::worker; +use installer::windows::idtglobal; +use installer::windows::language; + +############################################################## +# Returning the gid for a feature. +# Attention: Maximum length +############################################################## + +sub get_feature_gid +{ + my ($onefeature) = @_; + + my $gid = ""; + + if ( $onefeature->{'gid'} ) { $gid = $onefeature->{'gid'}; } + + # Attention: Maximum feature length is 38! + installer::windows::idtglobal::shorten_feature_gid(\$gid); + + return $gid +} + +############################################################## +# Returning the gid of the parent. +# Attention: Maximum length +############################################################## + +sub get_feature_parent +{ + my ($onefeature) = @_; + + my $parentgid = ""; + + if ( $onefeature->{'ParentID'} ) { $parentgid = $onefeature->{'ParentID'}; } + + # The modules, hanging directly below the root, have to be root modules. + # Only then it is possible to make the "real" root module invisible by + # setting the display to "0". + + if ( $parentgid eq $installer::globals::rootmodulegid ) { $parentgid = ""; } + + # Attention: Maximum feature length is 38! + installer::windows::idtglobal::shorten_feature_gid(\$parentgid); + + return $parentgid +} + +############################################################## +# Returning the display for a feature. +# 0: Feature is not shown +# odd: subfeatures are shown +# even: subfeatures are not shown +############################################################## + +sub get_feature_display +{ + my ($onefeature) = @_; + + my $display; + my $parentid = ""; + + if ( $onefeature->{'ParentID'} ) { $parentid = $onefeature->{'ParentID'}; } + + if ( $parentid eq "" ) + { + $display = "0"; # root module is not visible + } + elsif ( $onefeature->{'gid'} eq "gid_Module_Prg") # program module shows subfeatures + { + $display = "1"; # root module shows subfeatures + } + else + { + $display = "2"; # all other modules do not show subfeatures + } + + # special case: Feature has flag "HIDDEN_ROOT" -> $display is 0 + my $styles = ""; + if ( $onefeature->{'Styles'} ) { $styles = $onefeature->{'Styles'}; } + if ( $styles =~ /\bHIDDEN_ROOT\b/ ) { $display = "0"; } + + # Special handling for language modules. Only visible in multilingual installation set + if (( $styles =~ /\bSHOW_MULTILINGUAL_ONLY\b/ ) && ( ! $installer::globals::ismultilingual )) { $display = "0"; } + + # Special handling for c05office. No program module visible. + if (( $onefeature->{'gid'} eq "gid_Module_Prg" ) && ( $installer::globals::product =~ /c05office/i )) { $display = "0"; } + + # making all feature invisible in Language packs! + if ( $installer::globals::languagepack ) { $display = "0"; } + + return $display +} + +############################################################## +# Returning the level for a feature. +############################################################## + +sub get_feature_level +{ + my ($onefeature) = @_; + + my $level = "20"; # the default + + my $localdefault = ""; + + if ( $onefeature->{'Default'} ) { $localdefault = $onefeature->{'Default'}; } + + if ( $localdefault eq "NO" ) # explicitely set Default = "NO" + { + $level = "200"; # deselected in default installation, base is 100 + if ( $installer::globals::patch ) { $level = "20"; } + } + + # special handling for Java and Ada + if ( $onefeature->{'Name'} ) + { + if ( $onefeature->{'Name'} =~ /java/i ) { $level = $level + 40; } + } + + # if FeatureLevel is defined in scp, this will be used + + if ( $onefeature->{'FeatureLevel'} ) { $level = $onefeature->{'FeatureLevel'}; } + + return $level +} + +############################################################## +# Returning the directory for a feature. +############################################################## + +sub get_feature_directory +{ + my ($onefeature) = @_; + + my $directory; + + $directory = "INSTALLLOCATION"; + + return $directory +} + +############################################################## +# Returning the directory for a feature. +############################################################## + +sub get_feature_attributes +{ + my ($onefeature) = @_; + + my $attributes; + + # No advertising of features and no leaving on network. + # Feature without parent must not have the "2" + + my $parentgid = ""; + if ( $onefeature->{'ParentID'} ) { $parentgid = $onefeature->{'ParentID'}; } + + if (( $parentgid eq "" ) || ( $parentgid eq $installer::globals::rootmodulegid )) { $attributes = "8"; } + else { $attributes = "10"; } + + return $attributes +} + +################################################################################# +# Replacing one variable in one files +################################################################################# + +sub replace_one_variable +{ + my ($translationfile, $variable, $searchstring) = @_; + + for ( my $i = 0; $i <= $#{$translationfile}; $i++ ) + { + ${$translationfile}[$i] =~ s/\%$searchstring/$variable/g; + } +} + +################################################################################# +# Replacing the variables in the feature names and descriptions +################################################################################# + +sub replace_variables +{ + my ($translationfile, $variableshashref) = @_; + + foreach $key (keys %{$variableshashref}) + { + my $value = $variableshashref->{$key}; + replace_one_variable($translationfile, $value, $key); + } +} + +################################################################################# +# Collecting the feature recursively. +################################################################################# + +sub collect_modules_recursive +{ + my ($modulesref, $parentid, $feature, $directaccess, $directgid, $directparent, $directsortkey, $sorted) = @_; + + my @allchildren = (); + my $childrenexist = 0; + + # Collecting children from Module $parentid + + my $modulegid; + foreach $modulegid ( keys %{$directparent}) + { + if ( $directparent->{$modulegid} eq $parentid ) + { + my %childhash = ( "gid" => "$modulegid", "Sortkey" => "$directsortkey->{$modulegid}"); + push(@allchildren, \%childhash); + $childrenexist = 1; + } + } + + # Sorting children + + if ( $childrenexist ) + { + # Sort children + installer::sorter::sort_array_of_hashes_numerically(\@allchildren, "Sortkey"); + + # Adding children to new array + my $childhashref; + foreach $childhashref ( @allchildren ) + { + my $gid = $childhashref->{'gid'}; + + # Saving all lines, that have this 'gid' + + my $unique; + foreach $unique ( keys %{$directgid} ) + { + if ( $directgid->{$unique} eq $gid ) + { + push(@{$feature}, ${$modulesref}[$directaccess->{$unique}]); + if ( $sorted->{$unique} == 1 ) { installer::exiter::exit_program("ERROR: Sorting feature failed! \"$unique\" already sorted.", "sort_feature"); } + $sorted->{$unique} = 1; + } + } + + collect_modules_recursive($modulesref, $gid, $feature, $directaccess, $directgid, $directparent, $directsortkey, $sorted); + } + } +} + +################################################################################# +# Sorting the feature in specified order. Evaluated is the key "Sortkey", that +# is set in scp2 projects. +# The display order of modules in Windows Installer is dependent from the order +# in the idt file. Therefore the order of the modules array has to be adapted +# to the Sortkey order, before the idt file is created. +################################################################################# + +sub sort_feature +{ + my ($modulesref) = @_; + + my @feature = (); + + my %directaccess = (); + my %directparent = (); + my %directgid = (); + my %directsortkey = (); + my %sorted = (); + + for ( my $i = 0; $i <= $#{$modulesref}; $i++ ) + { + my $onefeature = ${$modulesref}[$i]; + + my $uniquekey = $onefeature->{'uniquekey'}; + my $modulegid = $onefeature->{'gid'}; + + $directaccess{$uniquekey} = $i; + + $directgid{$uniquekey} = $onefeature->{'gid'}; + + # ParentID and Sortkey are not saved for the 'uniquekey', but only for the 'gid' + + if ( $onefeature->{'ParentID'} ) { $directparent{$modulegid} = $onefeature->{'ParentID'}; } + else { $directparent{$modulegid} = ""; } + + if ( $onefeature->{'Sortkey'} ) { $directsortkey{$modulegid} = $onefeature->{'Sortkey'}; } + else { $directsortkey{$modulegid} = "9999"; } + + # Bookkeeping: + $sorted{$uniquekey} = 0; + } + + # Searching all feature recursively, beginning with ParentID = "" + my $parentid = ""; + collect_modules_recursive($modulesref, $parentid, \@feature, \%directaccess, \%directgid, \%directparent, \%directsortkey, \%sorted); + + # Bookkeeping + my $modulekey; + foreach $modulekey ( keys %sorted ) + { + if ( $sorted{$modulekey} == 0 ) + { + my $infoline = "Warning: Module \"$modulekey\" could not be sorted. Added to the end of the module array.\n"; + push(@installer::globals::logfileinfo, $infoline); + push(@feature, ${$modulesref}[$directaccess{$modulekey}]); + } + } + + return \@feature; +} + +################################################################################# +# Adding a unique key to the modules array. The gid is not unique for +# multilingual modules. Only the combination from gid and specific language +# is unique. Uniqueness is required for sorting mechanism. +################################################################################# + +sub add_uniquekey +{ + my ( $modulesref ) = @_; + + for ( my $i = 0; $i <= $#{$modulesref}; $i++ ) + { + my $uniquekey = ${$modulesref}[$i]->{'gid'}; + if ( ${$modulesref}[$i]->{'specificlanguage'} ) { $uniquekey = $uniquekey . "_" . ${$modulesref}[$i]->{'specificlanguage'}; } + ${$modulesref}[$i]->{'uniquekey'} = $uniquekey; + } +} + +################################################################################# +# Creating the file Feature.idt dynamically +# Content: +# Feature Feature_Parent Title Description Display Level Directory_ Attributes +################################################################################# + +sub create_feature_table +{ + my ($modulesref, $basedir, $languagesarrayref, $allvariableshashref) = @_; + + for ( my $m = 0; $m <= $#{$languagesarrayref}; $m++ ) + { + my $onelanguage = ${$languagesarrayref}[$m]; + + my $infoline; + + my @featuretable = (); + + installer::windows::idtglobal::write_idt_header(\@featuretable, "feature"); + + for ( my $i = 0; $i <= $#{$modulesref}; $i++ ) + { + my $onefeature = ${$modulesref}[$i]; + + # Java and Ada only, if the correct settings are set + my $styles = ""; + if ( $onefeature->{'Styles'} ) { $styles = $onefeature->{'Styles'}; } + if (( $styles =~ /\bJAVAMODULE\b/ ) && ( ! ($allvariableshashref->{'JAVAPRODUCT'} ))) { next; } + if (( $styles =~ /\bADAMODULE\b/ ) && ( ! ($allvariableshashref->{'ADAPRODUCT'} ))) { next; } + + # Controlling the language! + # Only language independent feature or feature with the correct language will be included into the table + + if (! (!(( $onefeature->{'ismultilingual'} )) || ( $onefeature->{'specificlanguage'} eq $onelanguage )) ) { next; } + + my %feature = (); + + $feature{'feature'} = get_feature_gid($onefeature); + $feature{'feature_parent'} = get_feature_parent($onefeature); + # if ( $onefeature->{'ParentID'} eq "" ) { $feature{'feature_parent'} = ""; } # Root has no parent + $feature{'Title'} = $onefeature->{'Name'}; + $feature{'Description'} = $onefeature->{'Description'}; + $feature{'Display'} = get_feature_display($onefeature); + $feature{'Level'} = get_feature_level($onefeature); + $feature{'Directory_'} = get_feature_directory($onefeature); + $feature{'Attributes'} = get_feature_attributes($onefeature); + + my $oneline = $feature{'feature'} . "\t" . $feature{'feature_parent'} . "\t" . $feature{'Title'} . "\t" + . $feature{'Description'} . "\t" . $feature{'Display'} . "\t" . $feature{'Level'} . "\t" + . $feature{'Directory_'} . "\t" . $feature{'Attributes'} . "\n"; + + push(@featuretable, $oneline); + + # collecting all feature in global feature collector (so that properties can be set in property table) + if ( ! installer::existence::exists_in_array($feature{'feature'}, \@installer::globals::featurecollector) ) + { + push(@installer::globals::featurecollector, $feature{'feature'}); + } + + # collecting all language feature in feature collector for check of language selection + if (( $styles =~ /\bSHOW_MULTILINGUAL_ONLY\b/ ) && ( $onefeature->{'ParentID'} ne $installer::globals::rootmodulegid )) + { + $installer::globals::multilingual_only_modules{$feature{'feature'}} = 1; + } + + # collecting all application feature in global feature collector for check of application selection + if ( $styles =~ /\bAPPLICATIONMODULE\b/ ) + { + $installer::globals::application_modules{$feature{'feature'}} = 1; + } + } + + # Saving the file + + my $featuretablename = $basedir . $installer::globals::separator . "Feature.idt" . "." . $onelanguage; + installer::files::save_file($featuretablename ,\@featuretable); + $infoline = "Created idt file: $featuretablename\n"; + push(@installer::globals::logfileinfo, $infoline); + } + +} + +1; diff --git a/solenv/bin/modules/installer/windows/featurecomponent.pm b/solenv/bin/modules/installer/windows/featurecomponent.pm new file mode 100644 index 000000000000..0c3d5763325e --- /dev/null +++ b/solenv/bin/modules/installer/windows/featurecomponent.pm @@ -0,0 +1,248 @@ +#************************************************************************* +# +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# Copyright 2008 by Sun Microsystems, Inc. +# +# OpenOffice.org - a multi-platform office productivity suite +# +# $RCSfile: featurecomponent.pm,v $ +# +# $Revision: 1.5 $ +# +# This file is part of OpenOffice.org. +# +# OpenOffice.org is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License version 3 +# only, as published by the Free Software Foundation. +# +# OpenOffice.org is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License version 3 for more details +# (a copy is included in the LICENSE file that accompanied this code). +# +# You should have received a copy of the GNU Lesser General Public License +# version 3 along with OpenOffice.org. If not, see +# <http://www.openoffice.org/license.html> +# for a copy of the LGPLv3 License. +# +#************************************************************************* + +package installer::windows::featurecomponent; + +use installer::converter; +use installer::existence; +use installer::exiter; +use installer::files; +use installer::globals; +use installer::windows::idtglobal; + +################################################################################# +# Collecting all pairs of features and components from the files collector +################################################################################# + +sub create_featurecomponent_table_from_files_collector +{ + my ($featurecomponenttableref, $filesref) = @_; + + for ( my $i = 0; $i <= $#{$filesref}; $i++ ) + { + my $onefile = ${$filesref}[$i]; + + my $filecomponent = $onefile->{'componentname'}; + my $filemodules = $onefile->{'modules'}; + + if ( $filecomponent eq "" ) + { + installer::exiter::exit_program("ERROR: No component defined for file $onefile->{'Name'}", "create_featurecomponent_table_from_files_collector"); + } + if ( $filemodules eq "" ) + { + installer::exiter::exit_program("ERROR: No modules found for file $onefile->{'Name'}", "create_featurecomponent_table_from_files_collector"); + } + + my $filemodulesarrayref = installer::converter::convert_stringlist_into_array(\$filemodules, ","); + + for ( my $j = 0; $j <= $#{$filemodulesarrayref}; $j++ ) + { + my %featurecomponent = (); + + my $onemodule = ${$filemodulesarrayref}[$j]; + $onemodule =~ s/\s*$//; + $featurecomponent{'Feature'} = $onemodule; + $featurecomponent{'Component'} = $filecomponent; + + # Attention: Features are renamed, because the maximum length is 38. + # But in the files collector ($filesref), the original names are saved. + + installer::windows::idtglobal::shorten_feature_gid(\$featurecomponent{'Feature'}); + + $oneline = "$featurecomponent{'Feature'}\t$featurecomponent{'Component'}\n"; + + # control of uniqueness + + if (! installer::existence::exists_in_array($oneline, $featurecomponenttableref)) + { + push(@{$featurecomponenttableref}, $oneline); + } + } + } +} + +################################################################################# +# Collecting all pairs of features and components from the registry collector +################################################################################# + +sub create_featurecomponent_table_from_registry_collector +{ + my ($featurecomponenttableref, $registryref) = @_; + + for ( my $i = 0; $i <= $#{$registryref}; $i++ ) + { + my $oneregistry = ${$registryref}[$i]; + + my $registrycomponent = $oneregistry->{'componentname'}; + my $registrymodule = $oneregistry->{'ModuleID'}; + + if ( $registrycomponent eq "" ) + { + installer::exiter::exit_program("ERROR: No component defined for registry $oneregistry->{'gid'}", "create_featurecomponent_table_from_registry_collector"); + } + if ( $registrymodule eq "" ) + { + installer::exiter::exit_program("ERROR: No modules found for registry $oneregistry->{'gid'}", "create_featurecomponent_table_from_registry_collector"); + } + + my %featurecomponent = (); + + $featurecomponent{'Feature'} = $registrymodule; + $featurecomponent{'Component'} = $registrycomponent; + + # Attention: Features are renamed, because the maximum length is 38. + # But in the files collector ($filesref), the original names are saved. + + installer::windows::idtglobal::shorten_feature_gid(\$featurecomponent{'Feature'}); + + $oneline = "$featurecomponent{'Feature'}\t$featurecomponent{'Component'}\n"; + + # control of uniqueness + + if (! installer::existence::exists_in_array($oneline, $featurecomponenttableref)) + { + push(@{$featurecomponenttableref}, $oneline); + } + } +} + +################################################################################# +# Collecting all feature that are listed in the featurecomponent table. +################################################################################# + +sub collect_all_feature +{ + my ($featurecomponenttable) = @_; + + my @allfeature = (); + + for ( my $i = 3; $i <= $#{$featurecomponenttable}; $i++ ) # beginning in line 4 + { + my $oneline = ${$featurecomponenttable}[$i]; + + if ( $oneline =~ /^\s*(\S+)\s+(\S+)\s*$/ ) + { + my $feature = $1; + + if (! installer::existence::exists_in_array($feature, \@allfeature)) { push(@allfeature, $feature); } + } + } + + return \@allfeature; +} + +################################################################################# +# On Win98 and Win Me there seems to be the problem, that maximum 817 +# components can be added to a feature. Even if Windows Installer 2.0 +# is used. +################################################################################# + +sub check_number_of_components_at_feature +{ + my ($featurecomponenttable) = @_; + + my $infoline = "\nChecking number of components at features. Maximum is 817 (for Win 98 and Win Me)\n"; + push(@installer::globals::logfileinfo, $infoline); + + my $allfeature = collect_all_feature($featurecomponenttable); + + for ( my $i = 0; $i <= $#{$allfeature}; $i++ ) + { + my $onefeature = ${$allfeature}[$i]; + my $featurecomponents = 0; + + for ( my $j = 0; $j <= $#{$featurecomponenttable}; $j++ ) + { + if ( ${$featurecomponenttable}[$j] =~ /^\s*\Q$onefeature\E\s+(\S+)\s*$/ ) { $featurecomponents++; } + } + + if ( $featurecomponents > 816 ) + { + installer::exiter::exit_program("ERROR: More than 816 components ($featurecomponents) at feature $onefeature. This causes problems on Win 98 and Win Me!", "check_number_of_components_at_feature"); + } + + # Logging the result + + $infoline = "Number of components at feature $onefeature : $featurecomponents\n"; + push(@installer::globals::logfileinfo, $infoline); + } + + $infoline = "\n"; + push(@installer::globals::logfileinfo, $infoline); +} + +################################################################################# +# Creating the file FeatureC.idt dynamically +# Content: +# Feature Component +################################################################################# + +sub create_featurecomponent_table +{ + my ($filesref, $registryref, $basedir) = @_; + + my @featurecomponenttable = (); + my $infoline; + + installer::windows::idtglobal::write_idt_header(\@featurecomponenttable, "featurecomponent"); + + # This is the first time, that features and componentes are related + # Problem: How about created profiles, configurationfiles, services.rdb + # -> simple solution: putting them all to the root module + # Otherwise profiles and configurationfiles cannot be created the way, they are now created + # -> especially a problem for the configurationfiles! # ToDo + # Very good: All ProfileItems belong to the root + # services.rdb belongs to the root anyway. + + # At the moment only the files are related to components (and the files know their modules). + # The component for each file is written into the files collector $filesinproductlanguageresolvedarrayref + + create_featurecomponent_table_from_files_collector(\@featurecomponenttable, $filesref); + + create_featurecomponent_table_from_registry_collector(\@featurecomponenttable, $registryref); + + # Additional components have to be added here + + # Checking, whether there are more than 817 components at a feature + + check_number_of_components_at_feature(\@featurecomponenttable); + + # Saving the file + + my $featurecomponenttablename = $basedir . $installer::globals::separator . "FeatureC.idt"; + installer::files::save_file($featurecomponenttablename ,\@featurecomponenttable); + $infoline = "Created idt file: $featurecomponenttablename\n"; + push(@installer::globals::logfileinfo, $infoline); + +} + +1;
\ No newline at end of file diff --git a/solenv/bin/modules/installer/windows/file.pm b/solenv/bin/modules/installer/windows/file.pm new file mode 100644 index 000000000000..e8d46c124317 --- /dev/null +++ b/solenv/bin/modules/installer/windows/file.pm @@ -0,0 +1,975 @@ +#************************************************************************* +# +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# Copyright 2008 by Sun Microsystems, Inc. +# +# OpenOffice.org - a multi-platform office productivity suite +# +# $RCSfile: file.pm,v $ +# +# $Revision: 1.24 $ +# +# This file is part of OpenOffice.org. +# +# OpenOffice.org is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License version 3 +# only, as published by the Free Software Foundation. +# +# OpenOffice.org is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License version 3 for more details +# (a copy is included in the LICENSE file that accompanied this code). +# +# You should have received a copy of the GNU Lesser General Public License +# version 3 along with OpenOffice.org. If not, see +# <http://www.openoffice.org/license.html> +# for a copy of the LGPLv3 License. +# +#************************************************************************* + +package installer::windows::file; + +use Digest::MD5; +use installer::existence; +use installer::exiter; +use installer::files; +use installer::globals; +use installer::logger; +use installer::pathanalyzer; +use installer::worker; +use installer::windows::font; +use installer::windows::idtglobal; +use installer::windows::language; + +########################################################################## +# Assigning one cabinet file to each file. This is requrired, +# if cabinet files shall be equivalent to packages. +########################################################################## + +sub assign_cab_to_files +{ + my ( $filesref ) = @_; + + my $infoline = ""; + + for ( my $i = 0; $i <= $#{$filesref}; $i++ ) + { + if ( ! exists(${$filesref}[$i]->{'modules'}) ) { installer::exiter::exit_program("ERROR: No module assignment found for ${$filesref}[$i]->{'gid'} !", "assign_cab_to_files"); } + my $module = ${$filesref}[$i]->{'modules'}; + # If modules contains a list of modules, only taking the first one. + if ( $module =~ /^\s*(.*?)\,/ ) { $module = $1; } + + if ( ! exists($installer::globals::allcabinetassigns{$module}) ) { installer::exiter::exit_program("ERROR: No cabinet file assigned to module \"$module\" (${$filesref}[$i]->{'gid'}) !", "assign_cab_to_files"); } + ${$filesref}[$i]->{'assignedcabinetfile'} = $installer::globals::allcabinetassigns{$module}; + + # Counting the files in each cabinet file + if ( ! exists($installer::globals::cabfilecounter{${$filesref}[$i]->{'assignedcabinetfile'}}) ) + { + $installer::globals::cabfilecounter{${$filesref}[$i]->{'assignedcabinetfile'}} = 1; + } + else + { + $installer::globals::cabfilecounter{${$filesref}[$i]->{'assignedcabinetfile'}}++; + } + } + + # logging the number of files in each cabinet file + + $infoline = "\nCabinet file content:\n"; + push(@installer::globals::logfileinfo, $infoline); + my $cabfile; + foreach $cabfile ( sort keys %installer::globals::cabfilecounter ) + { + $infoline = "$cabfile : $installer::globals::cabfilecounter{$cabfile} files\n"; + push(@installer::globals::logfileinfo, $infoline); + } + + # assigning startsequencenumbers for each cab file + + my $offset = 1; + foreach $cabfile ( sort keys %installer::globals::cabfilecounter ) + { + my $filecount = $installer::globals::cabfilecounter{$cabfile}; + $installer::globals::cabfilecounter{$cabfile} = $offset; + $offset = $offset + $filecount; + + $installer::globals::lastsequence{$cabfile} = $offset - 1; + } + + # logging the start sequence numbers + + $infoline = "\nCabinet file start sequences:\n"; + push(@installer::globals::logfileinfo, $infoline); + foreach $cabfile ( sort keys %installer::globals::cabfilecounter ) + { + $infoline = "$cabfile : $installer::globals::cabfilecounter{$cabfile}\n"; + push(@installer::globals::logfileinfo, $infoline); + } + + # logging the last sequence numbers + + $infoline = "\nCabinet file last sequences:\n"; + push(@installer::globals::logfileinfo, $infoline); + foreach $cabfile ( sort keys %installer::globals::lastsequence ) + { + $infoline = "$cabfile : $installer::globals::lastsequence{$cabfile}\n"; + push(@installer::globals::logfileinfo, $infoline); + } +} + +########################################################################## +# Assigning sequencenumbers to files. This is requrired, +# if cabinet files shall be equivalent to packages. +########################################################################## + +sub assign_sequencenumbers_to_files +{ + my ( $filesref ) = @_; + + my %directaccess = (); + my %allassigns = (); + + for ( my $i = 0; $i <= $#{$filesref}; $i++ ) + { + my $onefile = ${$filesref}[$i]; + + # Keeping order in cabinet files + # -> collecting all files in one cabinet file + # -> sorting files and assigning numbers + + # Saving counter $i for direct access into files array + # "destination" of the file is a unique identifier ('Name' is not unique!) + if ( exists($directaccess{$onefile->{'destination'}}) ) { installer::exiter::exit_program("ERROR: 'destination' at file not unique: $onefile->{'destination'}", "assign_sequencenumbers_to_files"); } + $directaccess{$onefile->{'destination'}} = $i; + + my $cabfilename = $onefile->{'assignedcabinetfile'}; + # collecting files in cabinet files + if ( ! exists($allassigns{$cabfilename}) ) + { + my %onecabfile = (); + $onecabfile{$onefile->{'destination'}} = 1; + $allassigns{$cabfilename} = \%onecabfile; + } + else + { + $allassigns{$cabfilename}->{$onefile->{'destination'}} = 1; + } + } + + # Sorting each hash and assigning numbers + # The destination of the file determines the sort order, not the filename! + my $cabfile; + foreach $cabfile ( sort keys %allassigns ) + { + my $counter = $installer::globals::cabfilecounter{$cabfile}; + my $dest; + foreach $dest ( sort keys %{$allassigns{$cabfile}} ) # <- sorting the destination! + { + my $directaccessnumber = $directaccess{$dest}; + ${$filesref}[$directaccessnumber]->{'assignedsequencenumber'} = $counter; + $counter++; + } + } +} + +############################################### +# Generating the component name from a file +############################################### + +sub get_file_component_name +{ + my ($fileref, $filesref) = @_; + + # In this function exists the rule to create components from files + # Rule: + # Two files get the same componentid, if: + # both have the same destination directory. + # both have the same "gid" -> both were packed in the same zip file + # All other files are included into different components! + + # my $componentname = $fileref->{'gid'} . "_" . $fileref->{'Dir'}; + + # $fileref->{'Dir'} is not sufficient! All files in a zip file have the same $fileref->{'Dir'}, + # but can be in different subdirectories. + # Solution: destination=share\Scripts\beanshell\Capitalise\capitalise.bsh + # in which the filename (capitalise.bsh) has to be removed and all backslashes (slashes) are + # converted into underline. + + my $destination = $fileref->{'destination'}; + installer::pathanalyzer::get_path_from_fullqualifiedname(\$destination); + $destination =~ s/\s//g; + $destination =~ s/\\/\_/g; + $destination =~ s/\//\_/g; + $destination =~ s/\_\s*$//g; # removing ending underline + + my $componentname = $fileref->{'gid'} . "__" . $destination; + + # Files with different languages, need to be packed into different components. + # Then the installation of the language specific component is determined by a language condition. + + if ( $fileref->{'ismultilingual'} ) + { + my $officelanguage = $fileref->{'specificlanguage'}; + $componentname = $componentname . "_" . $officelanguage; + } + + $componentname = lc($componentname); # componentnames always lowercase + + $componentname =~ s/\-/\_/g; # converting "-" to "_" + $componentname =~ s/\./\_/g; # converting "-" to "_" + + # Attention: Maximum length for the componentname is 72 + + $componentname =~ s/gid_file_/g_f_/g; + $componentname =~ s/_extra_/_e_/g; + $componentname =~ s/_config_/_c_/g; + $componentname =~ s/_org_openoffice_/_o_o_/g; + $componentname =~ s/_program_/_p_/g; + $componentname =~ s/_typedetection_/_td_/g; + $componentname =~ s/_linguistic_/_l_/g; + $componentname =~ s/_module_/_m_/g; + $componentname =~ s/_optional_/_opt_/g; + $componentname =~ s/_packages/_pack/g; + $componentname =~ s/_menubar/_mb/g; + $componentname =~ s/_common_/_cm_/g; + $componentname =~ s/_export_/_exp_/g; + $componentname =~ s/_table_/_tb_/g; + $componentname =~ s/_sofficecfg_/_sc_/g; + $componentname =~ s/_startmodulecommands_/_smc_/g; + $componentname =~ s/_drawimpresscommands_/_dic_/g; + $componentname =~ s/_basiccommands_/_bac_/g; + $componentname =~ s/_basicidecommands_/_baic_/g; + $componentname =~ s/_genericcommands_/_genc_/g; + $componentname =~ s/_bibliographycommands_/_bibc_/g; + $componentname =~ s/_share_/_s_/g; + $componentname =~ s/_modules_/_ms_/g; + $componentname =~ s/_uiconfig_zip_/_ucz_/g; + $componentname =~ s/_soffice_cfg_/_sc_/g; + + # All this is not necessary for files, which have the flag ASSIGNCOMPOMENT + + my $styles = ""; + if ( $fileref->{'Styles'} ) { $styles = $fileref->{'Styles'}; } + if ( $styles =~ /\bASSIGNCOMPOMENT\b/ ) { $componentname = get_component_from_assigned_file($fileref->{'AssignComponent'}, $filesref); } + + return $componentname; +} + +#################################################################### +# Returning the component name for a defined file gid. +# This is necessary for files with flag ASSIGNCOMPOMENT +#################################################################### + +sub get_component_from_assigned_file +{ + my ($gid, $filesref) = @_; + + my $onefile = installer::existence::get_specified_file($filesref, $gid); + my $componentname = ""; + if ( $onefile->{'componentname'} ) { $componentname = $onefile->{'componentname'}; } + else { installer::exiter::exit_program("ERROR: No component defined for file: $gid", "get_component_from_assigned_file"); } + + return $componentname; +} + +#################################################################### +# Generating the special filename for the database file File.idt +# Sample: CONTEXTS, CONTEXTS1 +# This name has to be unique. +# In most cases this is simply the filename. +#################################################################### + +sub generate_unique_filename_for_filetable +{ + my ($fileref, $component, $uniquefilenamehashref) = @_; + + # This new filename has to be saved into $fileref, because this is needed to find the source. + # The filename sbasic.idx/OFFSETS is changed to OFFSETS, but OFFSETS is not unique. + # In this procedure names like OFFSETS5 are produced. And exactly this string has to be added to + # the array of all files. + + my $uniquefilename = ""; + my $counter = 0; + + if ( $fileref->{'Name'} ) { $uniquefilename = $fileref->{'Name'}; } + + installer::pathanalyzer::make_absolute_filename_to_relative_filename(\$uniquefilename); # making /registry/schema/org/openoffice/VCL.xcs to VCL.xcs + + # Reading unique filename with help of "Component_" in File table from old database + if (( $installer::globals::updatedatabase ) && ( exists($uniquefilenamehashref->{"$component/$uniquefilename"}) )) + { + $uniquefilename = $uniquefilenamehashref->{"$component/$uniquefilename"}; # syntax of $value: ($uniquename;$shortname) + if ( $uniquefilename =~ /^\s*(.*?)\;\s*(.*?)\s*$/ ) { $uniquefilename = $1; } + $lcuniquefilename = lc($uniquefilename); + $installer::globals::alluniquefilenames{$uniquefilename} = 1; + $installer::globals::alllcuniquefilenames{$lcuniquefilename} = 1; + return $uniquefilename; + } + elsif (( $installer::globals::prepare_winpatch ) && ( exists($installer::globals::savedmapping{"$component/$uniquefilename"}) )) + { + # If we have a FTK mapping for this component/file, use it. + $installer::globals::savedmapping{"$component/$uniquefilename"} =~ m/^(.*);/; + $uniquefilename = $1; + $lcuniquefilename = lc($uniquefilename); + $installer::globals::alluniquefilenames{$uniquefilename} = 1; + $installer::globals::alllcuniquefilenames{$lcuniquefilename} = 1; + return $uniquefilename; + } + + $uniquefilename =~ s/\-/\_/g; # no "-" allowed + $uniquefilename =~ s/\@/\_/g; # no "@" allowed + $uniquefilename =~ s/\$/\_/g; # no "$" allowed + $uniquefilename =~ s/^\s*\./\_/g; # no "." at the beginning allowed allowed + $uniquefilename =~ s/^\s*\d/\_d/g; # no number at the beginning allowed allowed (even file "0.gif", replacing to "_d.gif") + $uniquefilename =~ s/org_openoffice_/ooo_/g; # shorten the unique file name + + my $lcuniquefilename = lc($uniquefilename); # only lowercase names + + my $newname = 0; + + if ( ! exists($installer::globals::alllcuniquefilenames{$lcuniquefilename}) && + ! exists($installer::globals::savedrevmapping{$lcuniquefilename}) ) + { + $installer::globals::alluniquefilenames{$uniquefilename} = 1; + $installer::globals::alllcuniquefilenames{$lcuniquefilename} = 1; + $newname = 1; + } + + if ( ! $newname ) + { + # adding a number until the name is really unique: OFFSETS, OFFSETS1, OFFSETS2, ... + # But attention: Making "abc.xcu" to "abc1.xcu" + + my $uniquefilenamebase = $uniquefilename; + + do + { + $counter++; + + if ( $uniquefilenamebase =~ /\./ ) + { + $uniquefilename = $uniquefilenamebase; + $uniquefilename =~ s/\./$counter\./; + } + else + { + $uniquefilename = $uniquefilenamebase . $counter; + } + + $newname = 0; + $lcuniquefilename = lc($uniquefilename); # only lowercase names + + if ( ! exists($installer::globals::alllcuniquefilenames{$lcuniquefilename}) && + ! exists($installer::globals::savedrevmapping{$lcuniquefilename}) ) + { + $installer::globals::alluniquefilenames{$uniquefilename} = 1; + $installer::globals::alllcuniquefilenames{$lcuniquefilename} = 1; + $newname = 1; + } + } + until ( $newname ) + } + + return $uniquefilename; +} + +#################################################################### +# Generating the special file column for the database file File.idt +# Sample: NAMETR~1.TAB|.nametranslation.table +# The first part has to be 8.3 conform. +#################################################################### + +sub generate_filename_for_filetable +{ + my ($fileref, $shortnamesref, $uniquefilenamehashref) = @_; + + my $returnstring = ""; + + my $filename = $fileref->{'Name'}; + + installer::pathanalyzer::make_absolute_filename_to_relative_filename(\$filename); # making /registry/schema/org/openoffice/VCL.xcs to VCL.xcs + + my $shortstring; + + # Reading short string with help of "FileName" in File table from old database + if (( $installer::globals::updatedatabase ) && ( exists($uniquefilenamehashref->{"$fileref->{'componentname'}/$filename"}) )) + { + my $value = $uniquefilenamehashref->{"$fileref->{'componentname'}/$filename"}; # syntax of $value: ($uniquename;$shortname) + if ( $value =~ /^\s*(.*?)\;\s*(.*?)\s*$/ ) { $shortstring = $2; } # already collected in function "collect_shortnames_from_old_database" + else { $shortstring = $filename; } + } + elsif (( $installer::globals::prepare_winpatch ) && ( exists($installer::globals::savedmapping{"$fileref->{'componentname'}/$filename"}) )) + { + $installer::globals::savedmapping{"$fileref->{'componentname'}/$filename"} =~ m/.*;(.*)/; + if ($1 ne '') + { + $shortstring = $1; + } + else + { + $shortstring = installer::windows::idtglobal::make_eight_three_conform_with_hash($filename, "file", $shortnamesref); + } + } + else + { + $shortstring = installer::windows::idtglobal::make_eight_three_conform_with_hash($filename, "file", $shortnamesref); + } + + if ( $shortstring eq $filename ) { $returnstring = $filename; } # nothing changed + else {$returnstring = $shortstring . "\|" . $filename; } + + return $returnstring; +} + +######################################### +# Returning the filesize of a file +######################################### + +sub get_filesize +{ + my ($fileref) = @_; + + my $file = $fileref->{'sourcepath'}; + + my $filesize; + + if ( -f $file ) # test of existence. For instance services.rdb does not always exist + { + $filesize = ( -s $file ); # file size can be "0" + } + else + { + $filesize = -1; + } + + return $filesize; +} + +############################################# +# Returning the file version, if required +# Sample: "8.0.1.8976"; +############################################# + +sub get_fileversion +{ + my ($onefile, $allvariables, $styles) = @_; + + my $fileversion = ""; + + if ( $allvariables->{'USE_FILEVERSION'} ) + { + if ( ! $allvariables->{'LIBRARYVERSION'} ) { installer::exiter::exit_program("ERROR: USE_FILEVERSION is set, but not LIBRARYVERSION", "get_fileversion"); } + my $libraryversion = $allvariables->{'LIBRARYVERSION'}; + if ( $libraryversion =~ /^\s*(\d+)\.(\d+)\.(\d+)\s*$/ ) + { + my $major = $1; + my $minor = $2; + my $micro = $3; + my $concat = 100 * $minor + $micro; + $libraryversion = $major . "\." . $concat; + } + my $vendornumber = 0; + if ( $allvariables->{'VENDORPATCHVERSION'} ) { $vendornumber = $allvariables->{'VENDORPATCHVERSION'}; } + $fileversion = $libraryversion . "\." . $installer::globals::buildid . "\." . $vendornumber; + if ( $onefile->{'FileVersion'} ) { $fileversion = $onefile->{'FileVersion'}; } # overriding FileVersion in scp + + # if ( $styles =~ /\bFONT\b/ ) + # { + # my $newfileversion = installer::windows::font::get_font_version($onefile->{'sourcepath'}); + # if ( $newfileversion != 0 ) { $fileversion = $newfileversion; } + # } + } + + if ( $installer::globals::prepare_winpatch ) { $fileversion = ""; } # Windows patches do not allow this version # -> who says so? + + return $fileversion; +} + +############################################# +# Returning the sequence for a file +############################################# + +sub get_sequence_for_file +{ + my ($number, $onefile, $fileentry, $allupdatesequenceshashref, $allupdatecomponentshashref, $allupdatefileorderhashref, $allfilecomponents) = @_; + + my $sequence = ""; + my $infoline = ""; + my $pffcomponentname = $onefile->{'componentname'} . "_pff"; + + if ( $installer::globals::updatedatabase ) + { + if (( exists($allupdatesequenceshashref->{$onefile->{'uniquename'}}) ) && + (( $onefile->{'componentname'} eq $allupdatecomponentshashref->{$onefile->{'uniquename'}} ) || + ( $pffcomponentname eq $allupdatecomponentshashref->{$onefile->{'uniquename'}} ))) + { + # The second condition is necessary to find shifted files, that have same "uniquename", but are now + # located in another directory. This can be seen at the component name. + $sequence = $allupdatesequenceshashref->{$onefile->{'uniquename'}}; + $onefile->{'assignedsequencenumber'} = $sequence; + # Collecting all used sequences, to guarantee, that no number is unused + $installer::globals::allusedupdatesequences{$sequence} = 1; + # Special help for files, that already have a "pff" component name (for example after ServicePack 1) + if ( $pffcomponentname eq $allupdatecomponentshashref->{$onefile->{'uniquename'}} ) + { + $infoline = "Warning: Special handling for component \"$pffcomponentname\". This file was added after the final, but before this ServicePack.\n"; + push(@installer::globals::logfileinfo, $infoline); + $onefile->{'componentname'} = $pffcomponentname; # pff for "post final file" + $fileentry->{'Component_'} = $onefile->{'componentname'}; + if ( ! exists($allfilecomponents->{$fileentry->{'Component_'}}) ) { $allfilecomponents->{$fileentry->{'Component_'}} = 1; } + } + } + else + { + $installer::globals::updatesequencecounter++; + $sequence = $installer::globals::updatesequencecounter; + $onefile->{'assignedsequencenumber'} = $sequence; + # $onefile->{'assignedcabinetfile'} = $installer::globals::pffcabfilename; # assigning to cabinet file for "post final files" + # Collecting all new files + $installer::globals::newupdatefiles{$sequence} = $onefile; + # Saving in sequence hash + $allupdatefileorderhashref->{$sequence} = $onefile->{'uniquename'}; + + # If the new file is part of an existing component, this must be changed now. All files + # of one component have to be included in one cabinet file. But because the order must + # not change, all new files have to be added to new components. + # $onefile->{'componentname'} = $file{'Component_'}; + + $onefile->{'componentname'} = $onefile->{'componentname'} . "_pff"; # pff for "post final file" + $fileentry->{'Component_'} = $onefile->{'componentname'}; + if ( ! exists($allfilecomponents->{$fileentry->{'Component_'}}) ) { $allfilecomponents->{$fileentry->{'Component_'}} = 1; } + $onefile->{'PostFinalFile'} = 1; + # $installer::globals::pfffileexists = 1; + # The sequence for this file has changed. It has to be inserted at the end of the files collector. + $installer::globals::insert_file_at_end = 1; + $installer::globals::newfilescollector{$sequence} = $onefile; # Adding new files to the end of the filescollector + $installer::globals::newfilesexist = 1; + } + } + elsif (( $onefile->{'assignedsequencenumber'} ) && ( $installer::globals::use_packages_for_cabs )) + { + $sequence = $onefile->{'assignedsequencenumber'}; + } + else + { + $sequence = $number; + # my $sequence = $number + 1; + + # Idea: Each component is packed into a cab file. + # This requires that all files in one cab file have sequences directly follwing each other, + # for instance from 1456 to 1466. Then in the media table the LastSequence for this cab file + # is 1466. + # Because all files belonging to one component are directly behind each other in the file + # collector, it is possible to use simply an increasing number as sequence value. + # If files belonging to one component are not directly behind each other in the files collector + # this mechanism will no longer work. + } + + return $sequence; +} + +############################################# +# Returning the Windows language of a file +############################################# + +sub get_language_for_file +{ + my ($fileref) = @_; + + my $language = ""; + + if ( $fileref->{'specificlanguage'} ) { $language = $fileref->{'specificlanguage'}; } + + if (!($language eq "")) + { + $language = installer::windows::language::get_windows_language($language); + } + + return $language; +} + +#################################################################### +# Creating a new KeyPath for components in TemplatesFolder. +#################################################################### + +sub generate_registry_keypath +{ + my ($onefile) = @_; + + my $keypath = $onefile->{'Name'}; + $keypath =~ s/\.//g; + $keypath = lc($keypath); + $keypath = "userreg_" . $keypath; + + return $keypath; +} + +#################################################################### +# Check, if in an update process files are missing. No removal +# of files allowed for Windows Patch creation. +# Also logging all new files, that have to be included in extra +# components and cab files. +#################################################################### + +sub check_file_sequences +{ + my ($allupdatefileorderhashref, $allupdatecomponentorderhashref) = @_; + + # All used sequences stored in %installer::globals::allusedupdatesequences + # Maximum sequence number of old database stored in $installer::globals::updatelastsequence + # All new files stored in %installer::globals::newupdatefiles + + my $infoline = ""; + + my @missing_sequences = (); + my @really_missing_sequences = (); + + for ( my $i = 1; $i <= $installer::globals::updatelastsequence; $i++ ) + { + if ( ! exists($installer::globals::allusedupdatesequences{$i}) ) { push(@missing_sequences, $i); } + } + + if ( $#missing_sequences > -1 ) + { + # Missing sequences can also be caused by files included in merge modules. This files are added later into the file table. + # Therefore now it is time to check the content of the merge modules. + + for ( my $j = 0; $j <= $#missing_sequences; $j++ ) + { + my $filename = $allupdatefileorderhashref->{$missing_sequences[$j]}; + + # Is this a file from a merge module? Then this is no error. + if ( ! exists($installer::globals::mergemodulefiles{$filename}) ) + { + push(@really_missing_sequences, $missing_sequences[$j]); + } + } + } + + if ( $#really_missing_sequences > -1 ) + { + my $errorstring = ""; + for ( my $j = 0; $j <= $#really_missing_sequences; $j++ ) + { + my $filename = $allupdatefileorderhashref->{$really_missing_sequences[$j]}; + my $comp = $allupdatecomponentorderhashref->{$really_missing_sequences[$j]}; + $errorstring = "$errorstring$filename (Sequence: $really_missing_sequences[$j], Component: \"$comp\")\n"; + } + + $infoline = "ERROR: Files are removed compared with update database.\nThe following files are missing:\n$errorstring"; + push(@installer::globals::logfileinfo, $infoline); + installer::exiter::exit_program($infoline, "check_file_sequences"); + } + + # Searching for new files + + my $counter = 0; + + foreach my $key ( keys %installer::globals::newupdatefiles ) + { + my $onefile = $installer::globals::newupdatefiles{$key}; + $counter++; + if ( $counter == 1 ) + { + $infoline = "\nNew files compared to the update database:\n"; + push(@installer::globals::logfileinfo, $infoline); + } + + $infoline = "$onefile->{'Name'} ($onefile->{'gid'}) Sequence: $onefile->{'assignedsequencenumber'}\n"; + push(@installer::globals::logfileinfo, $infoline); + } + + if ( $counter == 0 ) + { + $infoline = "Info: No new file compared with update database!\n"; + push(@installer::globals::logfileinfo, $infoline); + } + +} + +################################################################### +# Collecting further conditions for the component table. +# This is used by multilayer products, to enable installation +# of separate layers. +################################################################### + +sub get_tree_condition_for_component +{ + my ($onefile, $componentname) = @_; + + if ( $onefile->{'destination'} ) + { + my $dest = $onefile->{'destination'}; + + # Comparing the destination path with + # $installer::globals::hostnametreestyles{$hostname} = $treestyle; + # (-> hostname is the key, the style the value!) + + foreach my $hostname ( keys %installer::globals::hostnametreestyles ) + { + if (( $dest eq $hostname ) || ( $dest =~ /^\s*\Q$hostname\E\\/ )) + { + # the value is the style + my $style = $installer::globals::hostnametreestyles{$hostname}; + # the condition is saved in %installer::globals::treestyles + my $condition = $installer::globals::treestyles{$style}; + # Saving condition to be added in table Property + $installer::globals::usedtreeconditions{$condition} = 1; + $condition = $condition . "=1"; + # saving this condition + $installer::globals::treeconditions{$componentname} = $condition; + + # saving also at the file, for usage in fileinfo + $onefile->{'layer'} = $installer::globals::treelayername{$style}; + } + } + } +} + +############################################ +# Collecting all short names, that are +# already used by the old database +############################################ + +sub collect_shortnames_from_old_database +{ + my ($uniquefilenamehashref, $shortnameshashref) = @_; + + foreach my $key ( keys %{$uniquefilenamehashref} ) + { + my $value = $uniquefilenamehashref->{$key}; # syntax of $value: ($uniquename;$shortname) + + if ( $value =~ /^\s*(.*?)\;\s*(.*?)\s*$/ ) + { + my $shortstring = $2; + $shortnameshashref->{$shortstring} = 1; # adding the shortname to the array of all shortnames + } + } +} + +############################################ +# Creating the file File.idt dynamically +############################################ + +sub create_files_table +{ + my ($filesref, $allfilecomponentsref, $basedir, $allvariables, $uniquefilenamehashref, $allupdatesequenceshashref, $allupdatecomponentshashref, $allupdatefileorderhashref) = @_; + + installer::logger::include_timestamp_into_logfile("Performance Info: File Table start"); + + # Structure of the files table: + # File Component_ FileName FileSize Version Language Attributes Sequence + # In this function, all components are created. + # + # $allfilecomponentsref is empty at the beginning + + my $infoline; + + my @allfiles = (); + my @filetable = (); + my @filehashtable = (); + my %allfilecomponents = (); + my $counter = 0; + + if ( $^O =~ /cygwin/i ) { installer::worker::generate_cygwin_pathes($filesref); } + + # The filenames must be collected because of uniqueness + # 01-44-~1.DAT, 01-44-~2.DAT, ... + # my @shortnames = (); + my %shortnames = (); + + if ( $installer::globals::updatedatabase ) { collect_shortnames_from_old_database($uniquefilenamehashref, \%shortnames); } + + installer::windows::idtglobal::write_idt_header(\@filetable, "file"); + installer::windows::idtglobal::write_idt_header(\@filehashtable, "filehash"); + + for ( my $i = 0; $i <= $#{$filesref}; $i++ ) + { + my %file = (); + + my $onefile = ${$filesref}[$i]; + + my $styles = ""; + if ( $onefile->{'Styles'} ) { $styles = $onefile->{'Styles'}; } + if (( $styles =~ /\bJAVAFILE\b/ ) && ( ! ($allvariables->{'JAVAPRODUCT'} ))) { next; } + + $file{'Component_'} = get_file_component_name($onefile, $filesref); + $file{'File'} = generate_unique_filename_for_filetable($onefile, $file{'Component_'}, $uniquefilenamehashref); + + $onefile->{'uniquename'} = $file{'File'}; + $onefile->{'componentname'} = $file{'Component_'}; + + # Collecting all components + # if (!(installer::existence::exists_in_array($file{'Component_'}, $allfilecomponentsref))) { push(@{$allfilecomponentsref}, $file{'Component_'}); } + + if ( ! exists($allfilecomponents{$file{'Component_'}}) ) { $allfilecomponents{$file{'Component_'}} = 1; } + + $file{'FileName'} = generate_filename_for_filetable($onefile, \%shortnames, $uniquefilenamehashref); + + $file{'FileSize'} = get_filesize($onefile); + + $file{'Version'} = get_fileversion($onefile, $allvariables, $styles); + + $file{'Language'} = get_language_for_file($onefile); + + if ( $styles =~ /\bDONT_PACK\b/ ) { $file{'Attributes'} = "8192"; } + else { $file{'Attributes'} = "16384"; } + + # $file{'Attributes'} = "16384"; # Sourcefile is packed + # $file{'Attributes'} = "8192"; # Sourcefile is unpacked + + $installer::globals::insert_file_at_end = 0; + $counter++; + $file{'Sequence'} = get_sequence_for_file($counter, $onefile, \%file, $allupdatesequenceshashref, $allupdatecomponentshashref, $allupdatefileorderhashref, \%allfilecomponents); + + $onefile->{'sequencenumber'} = $file{'Sequence'}; + + my $oneline = $file{'File'} . "\t" . $file{'Component_'} . "\t" . $file{'FileName'} . "\t" + . $file{'FileSize'} . "\t" . $file{'Version'} . "\t" . $file{'Language'} . "\t" + . $file{'Attributes'} . "\t" . $file{'Sequence'} . "\n"; + + push(@filetable, $oneline); + + if ( ! $installer::globals::insert_file_at_end ) { push(@allfiles, $onefile); } + + # Collecting all component conditions + if ( $onefile->{'ComponentCondition'} ) + { + if ( ! exists($installer::globals::componentcondition{$file{'Component_'}})) + { + $installer::globals::componentcondition{$file{'Component_'}} = $onefile->{'ComponentCondition'}; + } + } + + # Collecting also all tree conditions for multilayer products + get_tree_condition_for_component($onefile, $file{'Component_'}); + + # Collecting all component names, that have flag VERSION_INDEPENDENT_COMP_ID + # This should be all components with constant API, for example URE + if ( $styles =~ /\bVERSION_INDEPENDENT_COMP_ID\b/ ) + { + $installer::globals::base_independent_components{$onefile->{'componentname'}} = 1; + } + + # Collecting all component ids, that are defined at files in scp project (should not be used anymore) + if ( $onefile->{'CompID'} ) + { + if ( ! exists($installer::globals::componentid{$onefile->{'componentname'}})) + { + $installer::globals::componentid{$onefile->{'componentname'}} = $onefile->{'CompID'}; + } + else + { + if ( $installer::globals::componentid{$onefile->{'componentname'}} ne $onefile->{'CompID'} ) + { + installer::exiter::exit_program("ERROR: There is already a ComponentID for component \"$onefile->{'componentname'}\" : \"$installer::globals::componentid{$onefile->{'componentname'}}\" . File \"$onefile->{'gid'}\" uses \"$onefile->{'CompID'}\" !", "create_files_table"); + } + } + + # Also checking vice versa. Is this ComponentID already used? If yes, is the componentname the same? + + if ( ! exists($installer::globals::comparecomponentname{$onefile->{'CompID'}})) + { + $installer::globals::comparecomponentname{$onefile->{'CompID'}} = $onefile->{'componentname'}; + } + else + { + if ( $installer::globals::comparecomponentname{$onefile->{'CompID'}} ne $onefile->{'componentname'} ) + { + installer::exiter::exit_program("ERROR: There is already a component for ComponentID \"$onefile->{'CompID'}\" : \"$installer::globals::comparecomponentname{$onefile->{'CompID'}}\" . File \"$onefile->{'gid'}\" has same component id but is included in component \"$onefile->{'componentname'}\" !", "create_files_table"); + } + } + } + + # Collecting all language specific conditions + # if ( $onefile->{'haslanguagemodule'} ) + if ( $onefile->{'ismultilingual'} ) + { + if ( $onefile->{'ComponentCondition'} ) { installer::exiter::exit_program("ERROR: Cannot set language condition. There is already another component condition for file $onefile->{'gid'}: \"$onefile->{'ComponentCondition'}\" !", "create_files_table"); } + + if ( $onefile->{'specificlanguage'} eq "" ) { installer::exiter::exit_program("ERROR: There is no specific language for file at language module: $onefile->{'gid'} !", "create_files_table"); } + my $locallanguage = $onefile->{'specificlanguage'}; + my $property = "IS" . $file{'Language'}; + my $value = 1; + my $condition = $property . "=" . $value; + + $onefile->{'ComponentCondition'} = $condition; + + if ( exists($installer::globals::componentcondition{$file{'Component_'}})) + { + if ( $installer::globals::componentcondition{$file{'Component_'}} ne $condition ) { installer::exiter::exit_program("ERROR: There is already another component condition for file $onefile->{'gid'}: \"$installer::globals::componentcondition{$file{'Component_'}}\" and \"$condition\" !", "create_files_table"); } + } + else + { + $installer::globals::componentcondition{$file{'Component_'}} = $condition; + } + + # collecting all properties for table Property + if ( ! exists($installer::globals::languageproperties{$property}) ) { $installer::globals::languageproperties{$property} = $value; } + } + + if ( $installer::globals::prepare_winpatch ) + { + my $path = $onefile->{'sourcepath'}; + if ( $^O =~ /cygwin/i ) { $path = $onefile->{'cyg_sourcepath'}; } + + open(FILE, $path) or die "ERROR: Can't open $path for creating file hash"; + binmode(FILE); + my $hashinfo = pack("l", 20); + $hashinfo .= Digest::MD5->new->addfile(*FILE)->digest; + + my @i = unpack ('x[l]l4', $hashinfo); + $oneline = $file{'File'} . "\t" . + "0" . "\t" . + $i[0] . "\t" . + $i[1] . "\t" . + $i[2] . "\t" . + $i[3] . "\n"; + push (@filehashtable, $oneline); + } + + # Saving the sequence number in a hash with uniquefilename as key. + # This is used for better performance in "save_packorder" + $installer::globals::uniquefilenamesequence{$onefile->{'uniquename'}} = $onefile->{'sequencenumber'}; + + # Special handling for files in PREDEFINED_OSSHELLNEWDIR. These components + # need as KeyPath a RegistryItem in HKCU + my $destdir = ""; + if ( $onefile->{'Dir'} ) { $destdir = $onefile->{'Dir'}; } + + if (( $destdir =~ /\bPREDEFINED_OSSHELLNEWDIR\b/ ) || ( $onefile->{'needs_user_registry_key'} )) + { + my $keypath = generate_registry_keypath($onefile); + $onefile->{'userregkeypath'} = $keypath; + push(@installer::globals::userregistrycollector, $onefile); + $installer::globals::addeduserregitrykeys = 1; + } + } + + # putting content from %allfilecomponents to $allfilecomponentsref for later usage + foreach $localkey (keys %allfilecomponents ) { push( @{$allfilecomponentsref}, $localkey); } + + my $filetablename = $basedir . $installer::globals::separator . "File.idt"; + installer::files::save_file($filetablename ,\@filetable); + $infoline = "\nCreated idt file: $filetablename\n"; + push(@installer::globals::logfileinfo, $infoline); + + installer::logger::include_timestamp_into_logfile("Performance Info: File Table end"); + + my $filehashtablename = $basedir . $installer::globals::separator . "MsiFileHash.idt"; + installer::files::save_file($filehashtablename ,\@filehashtable); + $infoline = "\nCreated idt file: $filehashtablename\n"; + push(@installer::globals::logfileinfo, $infoline); + + # Now the new files can be added to the files collector (only in update packaging processes) + if ( $installer::globals::newfilesexist ) + { + foreach my $seq (sort keys %installer::globals::newfilescollector) { push(@allfiles, $installer::globals::newfilescollector{$seq}) } + } + + return \@allfiles; +} + +1; diff --git a/solenv/bin/modules/installer/windows/font.pm b/solenv/bin/modules/installer/windows/font.pm new file mode 100644 index 000000000000..fec60e719150 --- /dev/null +++ b/solenv/bin/modules/installer/windows/font.pm @@ -0,0 +1,114 @@ +#************************************************************************* +# +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# Copyright 2008 by Sun Microsystems, Inc. +# +# OpenOffice.org - a multi-platform office productivity suite +# +# $RCSfile: font.pm,v $ +# +# $Revision: 1.4 $ +# +# This file is part of OpenOffice.org. +# +# OpenOffice.org is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License version 3 +# only, as published by the Free Software Foundation. +# +# OpenOffice.org is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License version 3 for more details +# (a copy is included in the LICENSE file that accompanied this code). +# +# You should have received a copy of the GNU Lesser General Public License +# version 3 along with OpenOffice.org. If not, see +# <http://www.openoffice.org/license.html> +# for a copy of the LGPLv3 License. +# +#************************************************************************* + +package installer::windows::font; + +use installer::files; +use installer::globals; +use installer::windows::idtglobal; + + +################################################################################# +# Creating the file Font.idt dynamically +# Content: +# File_ FontTitle +################################################################################# + +sub create_font_table +{ + my ($filesref, $basedir) = @_; + + my @fonttable = (); + + installer::windows::idtglobal::write_idt_header(\@fonttable, "font"); + + for ( my $i = 0; $i <= $#{$filesref}; $i++ ) + { + my $onefile = ${$filesref}[$i]; + my $styles = ""; + + if ( $onefile->{'Styles'} ) { $styles = $onefile->{'Styles'}; } + + if ( $styles =~ /\bFONT\b/ ) + { + my %font = (); + + $font{'File_'} = $onefile->{'uniquename'}; + # $font{'FontTitle'} = $onefile->{'FontName'}; # results in a warning during validation + $font{'FontTitle'} = ""; + + my $oneline = $font{'File_'} . "\t" . $font{'FontTitle'} . "\n"; + + push(@fonttable, $oneline); + } + } + + # Saving the file + + my $fonttablename = $basedir . $installer::globals::separator . "Font.idt"; + installer::files::save_file($fonttablename ,\@fonttable); + my $infoline = "Created idt file: $fonttablename\n"; + push(@installer::globals::logfileinfo, $infoline); + +} + +################################################################################# +# Reading the Font version from the ttf file, to avoid installation +# of older files over newer files. +################################################################################# + +sub get_font_version +{ + my ( $fontfile ) = @_; + + if ( ! -f $fontfile ) { installer::exiter::exit_program("ERROR: Font file does not exist: \"$fontfile\"", "get_font_version"); } + + my $fontversion = 0; + my $infoline = ""; + + my $onefile = installer::files::read_binary_file($fontfile); + + if ( $onefile =~ /Version\s+(\d+\.\d+\.*\d*)/ ) + { + $fontversion = $1; + $infoline = "FONT: Font \"$fontfile\" version: $fontversion\n"; + push(@installer::globals::logfileinfo, $infoline); + } + else + { + $infoline = "FONT: Could not determine font version: \"$fontfile\"\n"; + push(@installer::globals::logfileinfo, $infoline); + } + + return $fontversion; +} + +1; diff --git a/solenv/bin/modules/installer/windows/icon.pm b/solenv/bin/modules/installer/windows/icon.pm new file mode 100644 index 000000000000..5f101131ff4f --- /dev/null +++ b/solenv/bin/modules/installer/windows/icon.pm @@ -0,0 +1,81 @@ +#************************************************************************* +# +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# Copyright 2008 by Sun Microsystems, Inc. +# +# OpenOffice.org - a multi-platform office productivity suite +# +# $RCSfile: icon.pm,v $ +# +# $Revision: 1.4 $ +# +# This file is part of OpenOffice.org. +# +# OpenOffice.org is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License version 3 +# only, as published by the Free Software Foundation. +# +# OpenOffice.org is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License version 3 for more details +# (a copy is included in the LICENSE file that accompanied this code). +# +# You should have received a copy of the GNU Lesser General Public License +# version 3 along with OpenOffice.org. If not, see +# <http://www.openoffice.org/license.html> +# for a copy of the LGPLv3 License. +# +#************************************************************************* + +package installer::windows::icon; + +use installer::files; +use installer::globals; +use installer::pathanalyzer; +use installer::windows::idtglobal; + +########################################################################################################### +# Creating the file Icon.idt dynamically +# Content: +# Name Data +########################################################################################################### + +sub create_icon_table +{ + my ($iconfilecollector, $basedir) = @_; + + my @icontable = (); + + installer::windows::idtglobal::write_idt_header(\@icontable, "icon"); + + # Only the iconfiles, that are used in the shortcut table for the + # FolderItems (entries in Windows startmenu) are added into the icon table. + + for ( my $i = 0; $i <= $#{$iconfilecollector}; $i++ ) + { + my $iconfile = ${$iconfilecollector}[$i]; + + installer::pathanalyzer::make_absolute_filename_to_relative_filename(\$iconfile); + + my %icon = (); + + $icon{'Name'} = $iconfile; # simply soffice.exe + $icon{'Data'} = $iconfile; # simply soffice.exe + + my $oneline = $icon{'Name'} . "\t" . $icon{'Data'} . "\n"; + + push(@icontable, $oneline); + } + + # Saving the file + + my $icontablename = $basedir . $installer::globals::separator . "Icon.idt"; + installer::files::save_file($icontablename ,\@icontable); + my $infoline = "Created idt file: $icontablename\n"; + push(@installer::globals::logfileinfo, $infoline); + +} + +1; diff --git a/solenv/bin/modules/installer/windows/idtglobal.pm b/solenv/bin/modules/installer/windows/idtglobal.pm new file mode 100644 index 000000000000..e9ad0c73d249 --- /dev/null +++ b/solenv/bin/modules/installer/windows/idtglobal.pm @@ -0,0 +1,2460 @@ +#************************************************************************* +# +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# Copyright 2008 by Sun Microsystems, Inc. +# +# OpenOffice.org - a multi-platform office productivity suite +# +# $RCSfile: idtglobal.pm,v $ +# +# $Revision: 1.45 $ +# +# This file is part of OpenOffice.org. +# +# OpenOffice.org is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License version 3 +# only, as published by the Free Software Foundation. +# +# OpenOffice.org is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License version 3 for more details +# (a copy is included in the LICENSE file that accompanied this code). +# +# You should have received a copy of the GNU Lesser General Public License +# version 3 along with OpenOffice.org. If not, see +# <http://www.openoffice.org/license.html> +# for a copy of the LGPLv3 License. +# +#************************************************************************* + +package installer::windows::idtglobal; + +use Cwd; +use installer::converter; +use installer::existence; +use installer::exiter; +use installer::files; +use installer::globals; +use installer::pathanalyzer; +use installer::remover; +use installer::scriptitems; +use installer::systemactions; +use installer::windows::language; + +############################################################## +# Shorten the gid for a feature. +# Attention: Maximum length is 38 +############################################################## + +sub shorten_feature_gid +{ + my ($stringref) = @_; + + $$stringref =~ s/gid_Module_/gm_/; + $$stringref =~ s/_Root_/_r_/; + $$stringref =~ s/_Prg_/_p_/; + $$stringref =~ s/_Optional_/_o_/; + $$stringref =~ s/_Wrt_Flt_/_w_f_/; + $$stringref =~ s/_Javafilter_/_jf_/; +} + +############################################ +# Getting the next free number, that +# can be added. +# Sample: 01-44-~1.DAT, 01-44-~2.DAT, ... +############################################ + +sub get_next_free_number +{ + my ($name, $shortnamesref) = @_; + + my $counter = 0; + my $dontsave = 0; + my $alreadyexists; + my ($newname, $shortname); + + do + { + $alreadyexists = 0; + $counter++; + $newname = $name . $counter; + + for ( my $i = 0; $i <= $#{$shortnamesref}; $i++ ) + { + $shortname = ${$shortnamesref}[$i]; + + if ( uc($shortname) eq uc($newname) ) # case insensitive + { + $alreadyexists = 1; + last; + } + } + } + until (!($alreadyexists)); + + if (( $counter > 9 ) && ( length($name) > 6 )) + { + $dontsave = 1; + } + + if (!($dontsave)) + { + push(@{$shortnamesref}, $newname); # adding the new shortname to the array of shortnames + } + + return $counter +} + +############################################ +# Getting the next free number, that +# can be added. +# Sample: 01-44-~1.DAT, 01-44-~2.DAT, ... +############################################ + +sub get_next_free_number_with_hash +{ + my ($name, $shortnamesref, $ext) = @_; + + my $counter = 0; + my $dontsave = 0; + my $saved = 0; + my $alreadyexists; + my ($newname, $shortname); + + do + { + $alreadyexists = 0; + $counter++; + $newname = $name . $counter; + $newname = uc($newname); # case insensitive, always upper case + if ( exists($shortnamesref->{$newname}) || + exists($installer::globals::savedrev83mapping{$newname.$ext}) ) + { + $alreadyexists = 1; + } + } + until (!($alreadyexists)); + + if (( $counter > 9 ) && ( length($name) > 6 )) { $dontsave = 1; } + if (( $counter > 99 ) && ( length($name) > 5 )) { $dontsave = 1; } + + if (!($dontsave)) + { + # push(@{$shortnamesref}, $newname); # adding the new shortname to the array of shortnames + $shortnamesref->{$newname} = 1; # adding the new shortname to the array of shortnames, always uppercase + $saved = 1; + } + + return ( $counter, $saved ) +} + +######################################### +# 8.3 for filenames and directories +######################################### + +sub make_eight_three_conform +{ + my ($inputstring, $pattern, $shortnamesref) = @_; + + # all shortnames are collected in $shortnamesref, because of uniqueness + + my ($name, $namelength, $number); + my $conformstring = ""; + my $changed = 0; + + if (( $inputstring =~ /^\s*(.*?)\.(.*?)\s*$/ ) && ( $pattern eq "file" )) # files with a dot + { + $name = $1; + my $extension = $2; + + $namelength = length($name); + my $extensionlength = length($extension); + + if ( $extensionlength > 3 ) + { + # simply taking the first three letters + $extension = substr($extension, 0, 3); # name, offset, length + } + + # Attention: readme.html -> README~1.HTM + + if (( $namelength > 8 ) || ( $extensionlength > 3 )) + { + # taking the first six letters + $name = substr($name, 0, 6); # name, offset, length + $name =~ s/\s*$//; # removing ending whitespaces + $name = $name . "\~"; + $number = get_next_free_number($name, $shortnamesref); + + # if $number>9 the new name would be "abcdef~10.xyz", which is 9+3, and therefore not allowed + + if ( $number > 9 ) + { + $name = substr($name, 0, 5); # name, offset, length + $name =~ s/\s*$//; # removing ending whitespaces + $name = $name . "\~"; + $number = get_next_free_number($name, $shortnamesref); + } + + $name = $name . "$number"; + + $changed = 1; + } + + $conformstring = $name . "\." . $extension; + + if ( $changed ) { $conformstring= uc($conformstring); } + } + else # no dot in filename or directory (also used for shortcuts) + { + $name = $inputstring; + $namelength = length($name); + + if ( $namelength > 8 ) + { + # taking the first six letters + $name = substr($name, 0, 6); # name, offset, length + $name =~ s/\s*$//; # removing ending whitespaces + $name = $name . "\~"; + $number = get_next_free_number($name, $shortnamesref); + + # if $number>9 the new name would be "abcdef~10.xyz", which is 9+3, and therefore not allowed + + if ( $number > 9 ) + { + $name = substr($name, 0, 5); # name, offset, length + $name =~ s/\s*$//; # removing ending whitespaces + $name = $name . "\~"; + $number = get_next_free_number($name, $shortnamesref); + } + + $name = $name . "$number"; + $changed = 1; + if ( $pattern eq "dir" ) { $name =~ s/\./\_/g; } # in directories replacing "." with "_" + } + + $conformstring = $name; + + if ( $changed ) { $conformstring = uc($name); } + } + + return $conformstring; +} + +######################################### +# 8.3 for filenames and directories +# $shortnamesref is a hash in this case +# -> performance reasons +######################################### + +sub make_eight_three_conform_with_hash +{ + my ($inputstring, $pattern, $shortnamesref) = @_; + + # all shortnames are collected in $shortnamesref, because of uniqueness (a hash!) + + my ($name, $namelength, $number); + my $conformstring = ""; + my $changed = 0; + my $saved; + + # if (( $inputstring =~ /^\s*(.*?)\.(.*?)\s*$/ ) && ( $pattern eq "file" )) # files with a dot + if (( $inputstring =~ /^\s*(.*)\.(.*?)\s*$/ ) && ( $pattern eq "file" )) # files with a dot + { + # extension has to be non-greedy, but name is. This is important to find the last dot in the filename + $name = $1; + my $extension = $2; + + if ( $name =~ /^\s*(.*?)\s*$/ ) { $name = $1; } # now the name is also non-greedy + $name =~ s/\.//g; # no dots in 8+3 conform filename + + $namelength = length($name); + my $extensionlength = length($extension); + + if ( $extensionlength > 3 ) + { + # simply taking the first three letters + $extension = substr($extension, 0, 3); # name, offset, length + $changed = 1; + } + + # Attention: readme.html -> README~1.HTM + + if (( $namelength > 8 ) || ( $extensionlength > 3 )) + { + # taking the first six letters, if filename is longer than 6 characters + if ( $namelength > 6 ) + { + $name = substr($name, 0, 6); # name, offset, length + $name =~ s/\s*$//; # removing ending whitespaces + $name = $name . "\~"; + ($number, $saved) = get_next_free_number_with_hash($name, $shortnamesref, '.'.uc($extension)); + + # if $number>9 the new name would be "abcdef~10.xyz", which is 9+3, and therefore not allowed + + if ( ! $saved ) + { + $name = substr($name, 0, 5); # name, offset, length + $name =~ s/\s*$//; # removing ending whitespaces + $name = $name . "\~"; + ($number, $saved) = get_next_free_number_with_hash($name, $shortnamesref, '.'.uc($extension)); + + # if $number>99 the new name would be "abcde~100.xyz", which is 9+3, and therefore not allowed + + if ( ! $saved ) + { + $name = substr($name, 0, 4); # name, offset, length + $name =~ s/\s*$//; # removing ending whitespaces + $name = $name . "\~"; + ($number, $saved) = get_next_free_number_with_hash($name, $shortnamesref, '.'.uc($extension)); + + if ( ! $saved ) + { + installer::exiter::exit_program("ERROR: Could not set 8+3 conform name for $inputstring !", "make_eight_three_conform_with_hash"); + } + } + } + + $name = $name . "$number"; + $changed = 1; + } + } + + $conformstring = $name . "\." . $extension; + + if ( $changed ) { $conformstring= uc($conformstring); } + } + else # no dot in filename or directory (also used for shortcuts) + { + $name = $inputstring; + $namelength = length($name); + + if ( $namelength > 8 ) + { + # taking the first six letters + $name = substr($name, 0, 6); # name, offset, length + $name =~ s/\s*$//; # removing ending whitespaces + $name = $name . "\~"; + ( $number, $saved ) = get_next_free_number_with_hash($name, $shortnamesref, ''); + + # if $number>9 the new name would be "abcdef~10", which is 9+0, and therefore not allowed + + if ( ! $saved ) + { + $name = substr($name, 0, 5); # name, offset, length + $name =~ s/\s*$//; # removing ending whitespaces + $name = $name . "\~"; + ( $number, $saved ) = get_next_free_number_with_hash($name, $shortnamesref, ''); + + # if $number>99 the new name would be "abcde~100", which is 9+0, and therefore not allowed + + if ( ! $saved ) + { + $name = substr($name, 0, 4); # name, offset, length + $name =~ s/\s*$//; # removing ending whitespaces + $name = $name . "\~"; + ( $number, $saved ) = get_next_free_number_with_hash($name, $shortnamesref, ''); + + if ( ! $saved ) { installer::exiter::exit_program("ERROR: Could not set 8+3 conform name for $inputstring !", "make_eight_three_conform_with_hash"); } + } + } + + $name = $name . "$number"; + $changed = 1; + if ( $pattern eq "dir" ) { $name =~ s/\./\_/g; } # in directories replacing "." with "_" + } + + $conformstring = $name; + + if ( $changed ) { $conformstring = uc($name); } + } + + return $conformstring; +} + +######################################### +# Writing the header for idt files +######################################### + +sub write_idt_header +{ + my ($idtref, $definestring) = @_; + + my $oneline; + + if ( $definestring eq "file" ) + { + $oneline = "File\tComponent_\tFileName\tFileSize\tVersion\tLanguage\tAttributes\tSequence\n"; + push(@{$idtref}, $oneline); + $oneline = "s72\ts72\tl255\ti4\tS72\tS20\tI2\ti2\n"; + push(@{$idtref}, $oneline); + $oneline = "File\tFile\n"; + push(@{$idtref}, $oneline); + } + + if ( $definestring eq "filehash" ) + { + $oneline = "File_\tOptions\tHashPart1\tHashPart2\tHashPart3\tHashPart4\n"; + push(@{$idtref}, $oneline); + $oneline = "s72\ti2\ti4\ti4\ti4\ti4\n"; + push(@{$idtref}, $oneline); + $oneline = "MsiFileHash\tFile_\n"; + push(@{$idtref}, $oneline); + } + + if ( $definestring eq "directory" ) + { + $oneline = "Directory\tDirectory_Parent\tDefaultDir\n"; + push(@{$idtref}, $oneline); + $oneline = "s72\tS72\tl255\n"; + push(@{$idtref}, $oneline); + $oneline = "Directory\tDirectory\n"; + push(@{$idtref}, $oneline); + } + + if ( $definestring eq "component" ) + { + $oneline = "Component\tComponentId\tDirectory_\tAttributes\tCondition\tKeyPath\n"; + push(@{$idtref}, $oneline); + $oneline = "s72\tS38\ts72\ti2\tS255\tS72\n"; + push(@{$idtref}, $oneline); + $oneline = "Component\tComponent\n"; + push(@{$idtref}, $oneline); + } + + if ( $definestring eq "feature" ) + { + $oneline = "Feature\tFeature_Parent\tTitle\tDescription\tDisplay\tLevel\tDirectory_\tAttributes\n"; + push(@{$idtref}, $oneline); + $oneline = "s38\tS38\tL64\tL255\tI2\ti2\tS72\ti2\n"; + push(@{$idtref}, $oneline); + $oneline = "WINDOWSENCODINGTEMPLATE\tFeature\tFeature\n"; + push(@{$idtref}, $oneline); + } + + if ( $definestring eq "featurecomponent" ) + { + $oneline = "Feature_\tComponent_\n"; + push(@{$idtref}, $oneline); + $oneline = "s38\ts72\n"; + push(@{$idtref}, $oneline); + $oneline = "FeatureComponents\tFeature_\tComponent_\n"; + push(@{$idtref}, $oneline); + } + + if ( $definestring eq "media" ) + { + $oneline = "DiskId\tLastSequence\tDiskPrompt\tCabinet\tVolumeLabel\tSource\n"; + push(@{$idtref}, $oneline); + $oneline = "i2\ti2\tL64\tS255\tS32\tS72\n"; + push(@{$idtref}, $oneline); + $oneline = "Media\tDiskId\n"; + push(@{$idtref}, $oneline); + } + + if ( $definestring eq "font" ) + { + $oneline = "File_\tFontTitle\n"; + push(@{$idtref}, $oneline); + $oneline = "s72\tS128\n"; + push(@{$idtref}, $oneline); + $oneline = "Font\tFile_\n"; + push(@{$idtref}, $oneline); + } + + if ( $definestring eq "shortcut" ) + { + $oneline = "Shortcut\tDirectory_\tName\tComponent_\tTarget\tArguments\tDescription\tHotkey\tIcon_\tIconIndex\tShowCmd\tWkDir\n"; + push(@{$idtref}, $oneline); + $oneline = "s72\ts72\tl128\ts72\ts72\tS255\tL255\tI2\tS72\tI2\tI2\tS72\n"; + push(@{$idtref}, $oneline); + $oneline = "WINDOWSENCODINGTEMPLATE\tShortcut\tShortcut\n"; + push(@{$idtref}, $oneline); + } + + if ( $definestring eq "registry" ) + { + $oneline = "Registry\tRoot\tKey\tName\tValue\tComponent_\n"; + push(@{$idtref}, $oneline); + $oneline = "s72\ti2\tl255\tL255\tL0\ts72\n"; + push(@{$idtref}, $oneline); + $oneline = "Registry\tRegistry\n"; + push(@{$idtref}, $oneline); + } + + if ( $definestring eq "reg64" ) + { + $oneline = "Registry\tRoot\tKey\tName\tValue\tComponent_\n"; + push(@{$idtref}, $oneline); + $oneline = "s72\ti2\tl255\tL255\tL0\ts72\n"; + push(@{$idtref}, $oneline); + $oneline = "Reg64\tRegistry\n"; + push(@{$idtref}, $oneline); + } + + if ( $definestring eq "createfolder" ) + { + $oneline = "Directory_\tComponent_\n"; + push(@{$idtref}, $oneline); + $oneline = "s72\ts72\n"; + push(@{$idtref}, $oneline); + $oneline = "CreateFolder\tDirectory_\tComponent_\n"; + push(@{$idtref}, $oneline); + } + + if ( $definestring eq "removefile" ) + { + $oneline = "FileKey\tComponent_\tFileName\tDirProperty\tInstallMode\n"; + push(@{$idtref}, $oneline); + $oneline = "s72\ts72\tL255\ts72\ti2\n"; + push(@{$idtref}, $oneline); + $oneline = "RemoveFile\tFileKey\n"; + push(@{$idtref}, $oneline); + } + + if ( $definestring eq "upgrade" ) + { + $oneline = "UpgradeCode\tVersionMin\tVersionMax\tLanguage\tAttributes\tRemove\tActionProperty\n"; + push(@{$idtref}, $oneline); + $oneline = "s38\tS20\tS20\tS255\ti4\tS255\ts72\n"; + push(@{$idtref}, $oneline); + $oneline = "Upgrade\tUpgradeCode\tVersionMin\tVersionMax\tLanguage\tAttributes\n"; + push(@{$idtref}, $oneline); + } + + if ( $definestring eq "icon" ) + { + $oneline = "Name\tData\n"; + push(@{$idtref}, $oneline); + $oneline = "s72\tv0\n"; + push(@{$idtref}, $oneline); + $oneline = "Icon\tName\n"; + push(@{$idtref}, $oneline); + } + + if ( $definestring eq "inifile" ) + { + $oneline = "IniFile\tFileName\tDirProperty\tSection\tKey\tValue\tAction\tComponent_\n"; + push(@{$idtref}, $oneline); + $oneline = "s72\tl255\tS72\tl96\tl128\tl255\ti2\ts72\n"; + push(@{$idtref}, $oneline); + $oneline = "IniFile\tIniFile\n"; + push(@{$idtref}, $oneline); + } + + if ( $definestring eq "selfreg" ) + { + $oneline = "File_\tCost\n"; + push(@{$idtref}, $oneline); + $oneline = "s72\tI2\n"; + push(@{$idtref}, $oneline); + $oneline = "SelfReg\tFile_\n"; + push(@{$idtref}, $oneline); + } + + if ( $definestring eq "msiassembly" ) + { + $oneline = "Component_\tFeature_\tFile_Manifest\tFile_Application\tAttributes\n"; + push(@{$idtref}, $oneline); + $oneline = "s72\ts38\tS72\tS72\tI2\n"; + push(@{$idtref}, $oneline); + $oneline = "MsiAssembly\tComponent_\n"; + push(@{$idtref}, $oneline); + } + + if ( $definestring eq "msiassemblyname" ) + { + $oneline = "Component_\tName\tValue\n"; + push(@{$idtref}, $oneline); + $oneline = "s72\ts255\ts255\n"; + push(@{$idtref}, $oneline); + $oneline = "MsiAssemblyName\tComponent_\tName\n"; + push(@{$idtref}, $oneline); + } + + if ( $definestring eq "appsearch" ) + { + $oneline = "Property\tSignature_\n"; + push(@{$idtref}, $oneline); + $oneline = "s72\ts72\n"; + push(@{$idtref}, $oneline); + $oneline = "AppSearch\tProperty\tSignature_\n"; + push(@{$idtref}, $oneline); + } + + if ( $definestring eq "reglocat" ) + { + $oneline = "Signature_\tRoot\tKey\tName\tType\n"; + push(@{$idtref}, $oneline); + $oneline = "s72\ti2\ts255\tS255\tI2\n"; + push(@{$idtref}, $oneline); + $oneline = "RegLocator\tSignature_\n"; + push(@{$idtref}, $oneline); + } + + if ( $definestring eq "signatur" ) + { + $oneline = "Signature\tFileName\tMinVersion\tMaxVersion\tMinSize\tMaxSize\tMinDate\tMaxDate\tLanguages\n"; + push(@{$idtref}, $oneline); + $oneline = "s72\ts255\tS20\tS20\tI4\tI4\tI4\tI4\tS255\n"; + push(@{$idtref}, $oneline); + $oneline = "Signature\tSignature\n"; + push(@{$idtref}, $oneline); + } + +} + +############################################################## +# Returning the name of the rranslation file for a +# given language. +# Sample: "01" oder "en-US" -> "1033.txt" +############################################################## + +sub get_languagefilename +{ + my ($idtfilename, $basedir) = @_; + + # $idtfilename =~ s/\.idt/\.ulf/; + $idtfilename =~ s/\.idt/\.mlf/; + + my $languagefilename = $basedir . $installer::globals::separator . $idtfilename; + + return $languagefilename; +} + +############################################################## +# Returning the complete block in all languages +# for a specified string +############################################################## + +sub get_language_block_from_language_file +{ + my ($searchstring, $languagefile) = @_; + + my @language_block = (); + + for ( my $i = 0; $i <= $#{$languagefile}; $i++ ) + { + if ( ${$languagefile}[$i] =~ /^\s*\[\s*$searchstring\s*\]\s*$/ ) + { + my $counter = $i; + + push(@language_block, ${$languagefile}[$counter]); + $counter++; + + while (( $counter <= $#{$languagefile} ) && (!( ${$languagefile}[$counter] =~ /^\s*\[/ ))) + { + push(@language_block, ${$languagefile}[$counter]); + $counter++; + } + + last; + } + } + + return \@language_block; +} + +############################################################## +# Returning a specific language string from the block +# of all translations +############################################################## + +sub get_language_string_from_language_block +{ + my ($language_block, $language, $oldstring) = @_; + + my $newstring = ""; + + for ( my $i = 0; $i <= $#{$language_block}; $i++ ) + { + if ( ${$language_block}[$i] =~ /^\s*$language\s*\=\s*\"(.*)\"\s*$/ ) + { + $newstring = $1; + last; + } + } + + if ( $newstring eq "" ) + { + $language = "en-US"; # defaulting to english + + for ( my $i = 0; $i <= $#{$language_block}; $i++ ) + { + if ( ${$language_block}[$i] =~ /^\s*$language\s*\=\s*\"(.*)\"\s*$/ ) + { + $newstring = $1; + last; + } + } + } + + return $newstring; +} + +############################################################## +# Returning a specific code from the block +# of all codes. No defaulting to english! +############################################################## + +sub get_code_from_code_block +{ + my ($codeblock, $language) = @_; + + my $newstring = ""; + + for ( my $i = 0; $i <= $#{$codeblock}; $i++ ) + { + if ( ${$codeblock}[$i] =~ /^\s*$language\s*\=\s*\"(.*)\"\s*$/ ) + { + $newstring = $1; + last; + } + } + + return $newstring; +} + +############################################################## +# Translating an idt file +############################################################## + +sub translate_idtfile +{ + my ($idtfile, $languagefile, $onelanguage) = @_; + + for ( my $i = 0; $i <= $#{$idtfile}; $i++ ) + { + my @allstrings = (); + + my $oneline = ${$idtfile}[$i]; + + while ( $oneline =~ /\b(OOO_\w+)\b/ ) + { + my $replacestring = $1; + push(@allstrings, $replacestring); + $oneline =~ s/$replacestring//; + } + + my $oldstring; + + foreach $oldstring (@allstrings) + { + my $language_block = get_language_block_from_language_file($oldstring, $languagefile); + my $newstring = get_language_string_from_language_block($language_block, $onelanguage, $oldstring); + + # if (!( $newstring eq "" )) { ${$idtfile}[$i] =~ s/$oldstring/$newstring/; } + ${$idtfile}[$i] =~ s/$oldstring/$newstring/; # always substitute, even if $newstring eq "" (there are empty strings for control.idt) + } + } +} + +############################################################## +# Copying all needed files to create a msi database +# into one language specific directory +############################################################## + +sub prepare_language_idt_directory +{ + my ($destinationdir, $newidtdir, $onelanguage, $filesref, $iconfilecollector, $binarytablefiles, $allvariables) = @_; + + # Copying all idt-files from the source $installer::globals::idttemplatepath to the destination $destinationdir + # Copying all files in the subdirectory "Binary" + # Copying all files in the subdirectory "Icon" + + my $infoline = ""; + + installer::systemactions::copy_directory($installer::globals::idttemplatepath, $destinationdir); + + if ( -d $installer::globals::idttemplatepath . $installer::globals::separator . "Binary") + { + installer::systemactions::create_directory($destinationdir . $installer::globals::separator . "Binary"); + installer::systemactions::copy_directory($installer::globals::idttemplatepath . $installer::globals::separator . "Binary", $destinationdir . $installer::globals::separator . "Binary"); + + if (( $installer::globals::patch ) && ( $allvariables->{'WINDOWSPATCHBITMAPDIRECTORY'} )) + { + my $newsourcedir = $installer::globals::unpackpath . $installer::globals::separator . $allvariables->{'WINDOWSPATCHBITMAPDIRECTORY'}; # path setting in list file dependent from unpackpath !? + $infoline = "\nOverwriting files in directory \"" . $destinationdir . $installer::globals::separator . "Binary" . "\" with files from directory \"" . $newsourcedir . "\".\n"; + push( @installer::globals::logfileinfo, $infoline); + if ( ! -d $newsourcedir ) + { + my $currentdir = cwd(); + installer::exiter::exit_program("ERROR: Directory $newsourcedir does not exist! Current directory is: $currentdir", "prepare_language_idt_directory"); + } + installer::systemactions::copy_directory($newsourcedir, $destinationdir . $installer::globals::separator . "Binary"); + } + } + + installer::systemactions::create_directory($destinationdir . $installer::globals::separator . "Icon"); + + if ( -d $installer::globals::idttemplatepath . $installer::globals::separator . "Icon") + { + installer::systemactions::copy_directory($installer::globals::idttemplatepath . $installer::globals::separator . "Icon", $destinationdir . $installer::globals::separator . "Icon"); + } + + # Copying all files in $iconfilecollector, that describe icons of folderitems + + for ( my $i = 0; $i <= $#{$iconfilecollector}; $i++ ) + { + my $iconfilename = ${$iconfilecollector}[$i]; + installer::pathanalyzer::make_absolute_filename_to_relative_filename(\$iconfilename); + installer::systemactions::copy_one_file(${$iconfilecollector}[$i], $destinationdir . $installer::globals::separator . "Icon" . $installer::globals::separator . $iconfilename); + } + + # Copying all files in $binarytablefiles in the binary directory + + for ( my $i = 0; $i <= $#{$binarytablefiles}; $i++ ) + { + my $binaryfile = ${$binarytablefiles}[$i]; + my $binaryfilepath = $binaryfile->{'sourcepath'}; + my $binaryfilename = $binaryfilepath; + installer::pathanalyzer::make_absolute_filename_to_relative_filename(\$binaryfilename); + installer::systemactions::copy_one_file($binaryfilepath, $destinationdir . $installer::globals::separator . "Binary" . $installer::globals::separator . $binaryfilename); + } + + # Copying all new created and language independent idt-files to the destination $destinationdir. + # Example: "File.idt" + + installer::systemactions::copy_directory_with_fileextension($newidtdir, $destinationdir, "idt"); + + # Copying all new created and language dependent idt-files to the destination $destinationdir. + # Example: "Feature.idt.01" + + installer::systemactions::copy_directory_with_fileextension($newidtdir, $destinationdir, $onelanguage); + installer::systemactions::rename_files_with_fileextension($destinationdir, $onelanguage); + +} + +############################################################## +# Returning the source path of the rtf licensefile for +# a specified language +############################################################## + +sub get_rtflicensefilesource +{ + my ($language, $includepatharrayref) = @_; + + my $licensefilename = "license_" . $language . ".rtf"; + + my $sourcefileref = installer::scriptitems::get_sourcepath_from_filename_and_includepath(\$licensefilename, $includepatharrayref, 1); + + if ($$sourcefileref eq "") { installer::exiter::exit_program("ERROR: Could not find $licensefilename!", "get_rtflicensefilesource"); } + + my $infoline = "Using licensefile: $$sourcefileref\n"; + push( @installer::globals::logfileinfo, $infoline); + + return $$sourcefileref; +} + +############################################################## +# Returning the source path of the licensefile for +# a specified language +############################################################## + +sub get_licensefilesource +{ + my ($language, $filesref) = @_; + + my $licensefilename = "license_" . $language . ".txt"; + my $sourcepath = ""; + my $foundlicensefile = 0; + + for ( my $i = 0; $i <= $#{$filesref}; $i++ ) + { + my $onefile = ${$filesref}[$i]; + my $filename = $onefile->{'Name'}; + + if ($filename eq $licensefilename) + { + $sourcepath = $onefile->{'sourcepath'}; + $foundlicensefile = 1; + last; + } + } + + if ( ! $foundlicensefile ) { installer::exiter::exit_program("ERROR: Did not find file $licensefilename in file collector!", "get_licensefilesource"); } + + return $sourcepath; +} + +############################################################## +# A simple converter to create the license text +# in rtf format +############################################################## + +sub get_rtf_licensetext +{ + my ($licensefile) = @_; + + # A very simple rtf converter + + # The static header + + my $rtf_licensetext = '{\rtf1\ansi\deff0'; + $rtf_licensetext = $rtf_licensetext . '{\fonttbl{\f0\froman\fprq2\fcharset0 Times New Roman;}}'; + $rtf_licensetext = $rtf_licensetext . '{\colortbl\red0\green0\blue0;\red255\green255\blue255;\red128\green128\blue128;}'; + $rtf_licensetext = $rtf_licensetext . '{\stylesheet{\s1\snext1 Standard;}}'; + $rtf_licensetext = $rtf_licensetext . '{\info{\comment StarWriter}{\vern5690}}\deftab709'; + $rtf_licensetext = $rtf_licensetext . '{\*\pgdsctbl'; + $rtf_licensetext = $rtf_licensetext . '{\pgdsc0\pgdscuse195\pgwsxn11905\pghsxn16837\marglsxn1134\margrsxn1134\margtsxn1134\margbsxn1134\pgdscnxt0 Standard;}}'; + $rtf_licensetext = $rtf_licensetext . '\paperh16837\paperw11905\margl1134\margr1134\margt1134\margb1134\sectd\sbknone\pgwsxn11905\pghsxn16837\marglsxn1134\margrsxn1134\margtsxn1134\margbsxn1134\ftnbj\ftnstart1\ftnrstcont\ftnnar\aenddoc\aftnrstcont\aftnstart1\aftnnrlc'; + $rtf_licensetext = $rtf_licensetext . '\pard\plain \s1'; + + for ( my $i = 0; $i <= $#{$licensefile}; $i++ ) + { + my $oneline = ${$licensefile}[$i]; + # if ( $oneline =~ /^\s*$/ ) { $oneline = '\par'; } # empty lines + + if ( $i == 0 ) { $oneline =~ s/^\W*//; } + + $oneline =~ s/\t/ /g; # no tabs allowed, converting to four spaces + $oneline =~ s/\n$//g; # no newline at line end + +# $oneline =~ s/ä/\\\'e4/g; # converting "ä" +# $oneline =~ s/ö/\\\'f6/g; # converting "ö" +# $oneline =~ s/ü/\\\'fc/g; # converting "ü" +# $oneline =~ s/ß/\\\'df/g; # converting "ß" + + # german replacements + + $oneline =~ s/\Ã\„/\\\'c4/g; # converting "Ä" + $oneline =~ s/\Ã\–/\\\'d6/g; # converting "Ö" + $oneline =~ s/\Ã\œ/\\\'dc/g; # converting "Ü" + $oneline =~ s/\Ã\¤/\\\'e4/g; # converting "ä" + $oneline =~ s/\Ã\¶/\\\'f6/g; # converting "ö" + $oneline =~ s/\Ã\¼/\\\'fc/g; # converting "ü" + $oneline =~ s/\Ã\Ÿ/\\\'df/g; # converting "ß" + + # french replacements + + $oneline =~ s/\Ã\‰/\\\'c9/g; + $oneline =~ s/\Ã\€/\\\'c0/g; + $oneline =~ s/\Â\«/\\\'ab/g; + $oneline =~ s/\Â\»/\\\'bb/g; + $oneline =~ s/\Ã\©/\\\'e9/g; + $oneline =~ s/\Ã\¨/\\\'e8/g; + $oneline =~ s/\Ã\ /\\\'e0/g; + $oneline =~ s/\Ã\´/\\\'f4/g; + $oneline =~ s/\Ã\§/\\\'e7/g; + $oneline =~ s/\Ã\ª/\\\'ea/g; + $oneline =~ s/\Ã\Š/\\\'ca/g; + $oneline =~ s/\Ã\»/\\\'fb/g; + $oneline =~ s/\Ã\¹/\\\'f9/g; + $oneline =~ s/\Ã\®/\\\'ee/g; + + # quotation marks + + $oneline =~ s/\â\€\ž/\\\'84/g; + $oneline =~ s/\â\€\œ/\\ldblquote/g; + $oneline =~ s/\â\€\™/\\rquote/g; + + + $oneline =~ s/\Â\ /\\\~/g; + + $oneline = '\par ' . $oneline; + + $rtf_licensetext = $rtf_licensetext . $oneline; + } + + # and the end + + $rtf_licensetext = $rtf_licensetext . '\par \par }'; + + return $rtf_licensetext; +} + +############################################################## +# A simple converter to create a license txt string from +# the rtf format +############################################################## + +sub make_string_licensetext +{ + my ($licensefile) = @_; + + my $rtf_licensetext = ""; + + for ( my $i = 0; $i <= $#{$licensefile}; $i++ ) + { + my $oneline = ${$licensefile}[$i]; + $oneline =~ s/\s*$//g; # no whitespace at line end + + $rtf_licensetext = $rtf_licensetext . $oneline . " "; + } + + return $rtf_licensetext; +} + +############################################################## +# Setting the path, where the soffice.exe is installed, into +# the CustomAction table +############################################################## + +sub add_officedir_to_database +{ + my ($basedir, $allvariables) = @_; + + my $customactionfilename = $basedir . $installer::globals::separator . "CustomAc.idt"; + + my $customacfile = installer::files::read_file($customactionfilename); + + my $found = 0; + + # Updating the values + + if ( $installer::globals::officeinstalldirectoryset ) + { + $found = 0; + + for ( my $i = 0; $i <= $#{$customacfile}; $i++ ) + { + if ( ${$customacfile}[$i] =~ /\bOFFICEDIRECTORYGID\b/ ) + { + ${$customacfile}[$i] =~ s/\bOFFICEDIRECTORYGID\b/$installer::globals::officeinstalldirectory/; + $found = 1; + } + } + + if (( ! $found ) && ( ! $allvariables->{'IGNOREDIRECTORYLAYER'} )) + { + installer::exiter::exit_program("ERROR: \"OFFICEDIRECTORYGID\" not found in \"$customactionfilename\" !", "add_officedir_to_database"); + } + } + + if ( $installer::globals::basisinstalldirectoryset ) + { + $found = 0; + + for ( my $i = 0; $i <= $#{$customacfile}; $i++ ) + { + if ( ${$customacfile}[$i] =~ /\bBASISDIRECTORYGID\b/ ) + { + ${$customacfile}[$i] =~ s/\bBASISDIRECTORYGID\b/$installer::globals::basisinstalldirectory/; + $found = 1; + } + } + + if (( ! $found ) && ( ! $allvariables->{'IGNOREDIRECTORYLAYER'} )) + { + installer::exiter::exit_program("ERROR: \"BASISDIRECTORYGID\" not found in \"$customactionfilename\" !", "add_officedir_to_database"); + } + } + + if ( $installer::globals::ureinstalldirectoryset ) + { + $found = 0; + + for ( my $i = 0; $i <= $#{$customacfile}; $i++ ) + { + if ( ${$customacfile}[$i] =~ /\bUREDIRECTORYGID\b/ ) + { + ${$customacfile}[$i] =~ s/\bUREDIRECTORYGID\b/$installer::globals::ureinstalldirectory/; + $found = 1; + } + } + + if (( ! $found ) && ( ! $allvariables->{'IGNOREDIRECTORYLAYER'} )) + { + installer::exiter::exit_program("ERROR: \"UREDIRECTORYGID\" not found in \"$customactionfilename\" !", "add_officedir_to_database"); + } + } + + # Saving the file + + installer::files::save_file($customactionfilename ,$customacfile); + my $infoline = "Updated idt file: $customactionfilename\n"; + push(@installer::globals::logfileinfo, $infoline); + +} + +############################################################## +# Including the license text into the table control.idt +############################################################## + +sub add_licensefile_to_database +{ + my ($licensefile, $controltable) = @_; + + # Nine tabs before the license text and two tabs after it + # The license text has to be included into the dialog + # LicenseAgreement into the control Memo. + + my $foundlicenseline = 0; + my ($number, $line); + + for ( my $i = 0; $i <= $#{$controltable}; $i++ ) + { + $line = ${$controltable}[$i]; + + if ( $line =~ /^\s*\bLicenseAgreement\b\t\bMemo\t/ ) + { + $foundlicenseline = 1; + $number = $i; + last; + } + } + + if (!($foundlicenseline)) + { + installer::exiter::exit_program("ERROR: Line for license file in Control.idt not found!", "add_licensefile_to_database"); + } + else + { + my %control = (); + + if ( $line =~ /^\s*(.*?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\s*$/ ) + { + $control{'Dialog_'} = $1; + $control{'Control'} = $2; + $control{'Type'} = $3; + $control{'X'} = $4; + $control{'Y'} = $5; + $control{'Width'} = $6; + $control{'Height'} = $7; + $control{'Attributes'} = $8; + $control{'Property'} = $9; + $control{'Text'} = $10; + $control{'Control_Next'} = $11; + $control{'Help'} = $12; + } + else + { + installer::exiter::exit_program("ERROR: Could not split line correctly!", "add_licensefile_to_database"); + } + + # my $licensetext = get_rtf_licensetext($licensefile); + my $licensetext = make_string_licensetext($licensefile); + + $control{'Text'} = $licensetext; + + my $newline = $control{'Dialog_'} . "\t" . $control{'Control'} . "\t" . $control{'Type'} . "\t" . + $control{'X'} . "\t" . $control{'Y'} . "\t" . $control{'Width'} . "\t" . + $control{'Height'} . "\t" . $control{'Attributes'} . "\t" . $control{'Property'} . "\t" . + $control{'Text'} . "\t" . $control{'Control_Next'} . "\t" . $control{'Help'} . "\n"; + + ${$controltable}[$number] = $newline + } +} + +################################################################################################ +# Including the checkboxes for the language selection dialog +# into the table control.idt . This is only relevant for +# multilingual installation sets. +# +# old: +# LanguageSelection CheckBox1 CheckBox 22 60 15 24 3 IS1033 CheckBox2 +# LanguageSelection Text1 Text 40 60 70 15 65539 OOO_CONTROL_LANG_1033 +# LanguageSelection CheckBox2 CheckBox 22 90 15 24 3 IS1031 Next +# LanguageSelection Text2 Text 40 90 70 15 65539 OOO_CONTROL_LANG_1031 +# new: +# LanguageSelection CheckBox1 CheckBox 22 60 15 24 3 IS1033 Text CheckBox2 +# LanguageSelection CheckBox2 CheckBox 22 90 15 24 3 IS1031 Text Next +################################################################################################ + +sub add_language_checkboxes_to_database +{ + my ($controltable, $languagesarrayref) = @_; + + # for each language, two lines have to be inserted + + for ( my $i = 0; $i <= $#{$languagesarrayref}; $i++ ) + { + my $last = 0; + if ( $i == $#{$languagesarrayref} ) { $last = 1; } # special handling for the last + + my $onelanguage = ${$languagesarrayref}[$i]; + my $windowslanguage = installer::windows::language::get_windows_language($onelanguage); + + # my $is_english = 0; + # if ( $windowslanguage eq "1033" ) { $is_english = 1; } + + my $checkboxattribute = "3"; + # if ( $is_english ) { $checkboxattribute = "1"; } # english is not deselectable + + my $count = $i + 1; + my $nextcount = $i + 2; + my $checkboxcount = "CheckBox" . $count; + + my $multiplier = 20; + my $offset = 60; + if ( $#{$languagesarrayref} > 7 ) + { + $multiplier = 15; # smaller differences for more than 7 languages + $offset = 50; # smaller offset for more than 7 languages + } + + my $yvalue = $offset + $i * $multiplier; + + my $property = "IS" . $windowslanguage; + # if ( ! exists($installer::globals::languageproperties{$property}) ) { installer::exiter::exit_program("ERROR: Could not find property \"$property\" in the list of language properties!", "add_language_checkboxes_to_database"); } + + my $controlnext = ""; + if ( $last ) { $controlnext = "Next"; } + else { $controlnext = "CheckBox" . $nextcount; } + + my $stringname = "OOO_CONTROL_LANG_" . $windowslanguage; + + my $line1 = "LanguageSelection" . "\t" . $checkboxcount . "\t" . "CheckBox" . "\t" . + "22" . "\t" . $yvalue . "\t" . "200" . "\t" . "15" . "\t" . $checkboxattribute . "\t" . + $property . "\t" . $stringname . "\t" . $controlnext . "\t" . "\n"; + + push(@{$controltable}, $line1); + + # my $textcount = "Text" . $count; + # my $stringname = "OOO_CONTROL_LANG_" . $windowslanguage; + # + # $yvalue = $yvalue + 2; # text 2 pixel lower than checkbox + # + # my $line2 = "LanguageSelection" . "\t" . $textcount . "\t" . "Text" . "\t" . + # "40" . "\t" . $yvalue . "\t" . "70" . "\t" . "15" . "\t" . "65539" . "\t" . + # "\t" . $stringname . "\t" . "\t" . "\n"; + # + # push(@{$controltable}, $line2); + } +} + +################################################################### +# Determining the last position in a sequencetable +# into the tables CustomAc.idt and InstallE.idt. +################################################################### + +sub get_last_position_in_sequencetable +{ + my ($sequencetable) = @_; + + my $position = 0; + + for ( my $i = 0; $i <= $#{$sequencetable}; $i++ ) + { + my $line = ${$sequencetable}[$i]; + + if ( $line =~ /^\s*\w+\t.*\t\s*(\d+)\s$/ ) + { + my $newposition = $1; + if ( $newposition > $position ) { $position = $newposition; } + } + } + + return $position; +} + +######################################################################### +# Determining the position of a specified Action in the sequencetable +######################################################################### + +sub get_position_in_sequencetable +{ + my ($action, $sequencetable) = @_; + + my $position = 0; + + $action =~ s/^\s*behind_//; + + for ( my $i = 0; $i <= $#{$sequencetable}; $i++ ) + { + my $line = ${$sequencetable}[$i]; + + if ( $line =~ /^\s*(\w+)\t.*\t\s*(\d+)\s$/ ) + { + my $compareaction = $1; + $position = $2; + if ( $compareaction eq $action ) { last; } + } + } + + return $position; +} + +################################################################################################ +# Including the CustomAction for the configuration +# into the tables CustomAc.idt and InstallE.idt. +# +# CustomAc.idt: ExecutePkgchk 82 pkgchk.exe -s +# InstallE.idt: ExecutePkgchk Not REMOVE="ALL" 3175 +# +# CustomAc.idt: ExecuteQuickstart 82 install_quickstart.exe +# InstallE.idt: ExecuteQuickstart &gm_o_Quickstart=3 3200 +# +# CustomAc.idt: ExecuteInstallRegsvrex 82 regsvrex.exe shlxthdl.dll +# InstallE.idt: ExecuteInstallRegsvrex Not REMOVE="ALL" 3225 +# +# CustomAc.idt: ExecuteUninstallRegsvrex 82 regsvrex.exe /u shlxthdl.dll +# InstallE.idt: ExecuteUninstallRegsvrex REMOVE="ALL" 690 +# +# CustomAc.idt: Regmsdocmsidll1 1 reg4msdocmsidll Reg4MsDocEntry +# InstallU.idt: Regmsdocmsidll1 Not REMOVE="ALL" 610 +# +# CustomAc.idt: Regmsdocmsidll2 1 reg4msdocmsidll Reg4MsDocEntry +# InstallE.idt: Regmsdocmsidll2 Not REMOVE="ALL" 3160 +################################################################################################ + +sub set_custom_action +{ + my ($customactionidttable, $actionname, $actionflags, $exefilename, $actionparameter, $inbinarytable, $filesref, $customactionidttablename, $styles) = @_; + + my $included_customaction = 0; + my $infoline = ""; + my $customaction_exefilename = $exefilename; + my $uniquename = ""; + + # when the style NO_FILE is set, no searching for the file is needed, no filtering is done, we can add that custom action + if ( $styles =~ /\bNO_FILE\b/ ) + { + my $line = $actionname . "\t" . $actionflags . "\t" . $customaction_exefilename . "\t" . $actionparameter . "\n"; + push(@{$customactionidttable}, $line); + + $infoline = "Added $actionname CustomAction into table $customactionidttablename (NO_FILE has been set)\n"; + push(@installer::globals::logfileinfo, $infoline); + + $included_customaction = 1; + return $included_customaction; + } + + # is the $exefilename a library that is included into the binary table + + if ( $inbinarytable ) { $customaction_exefilename =~ s/\.//; } # this is the entry in the binary table ("abc.dll" -> "abcdll") + + # is the $exefilename included into the product? + + my $contains_file = 0; + + # All files are located in $filesref and in @installer::globals::binarytableonlyfiles. + # Both must be added together + my $localfilesref = installer::converter::combine_arrays_from_references(\@installer::globals::binarytableonlyfiles, $filesref); + + for ( my $i = 0; $i <= $#{$localfilesref}; $i++ ) + { + my $onefile = ${$localfilesref}[$i]; + my $filename = ""; + if ( exists($onefile->{'Name'}) ) + { + $filename = $onefile->{'Name'}; + + if ( $filename eq $exefilename ) + { + $contains_file = 1; + $uniquename = ${$localfilesref}[$i]->{'uniquename'}; + last; + } + } + else + { + installer::exiter::exit_program("ERROR: Did not find \"Name\" for file \"$onefile->{'uniquename'}\" ($onefile->{'gid'})!", "set_custom_action"); + } + } + + if ( $contains_file ) + { + # Now the CustomAction can be included into the CustomAc.idt + + if ( ! $inbinarytable ) { $customaction_exefilename = $uniquename; } # the unique file name has to be added to the custom action table + + my $line = $actionname . "\t" . $actionflags . "\t" . $customaction_exefilename . "\t" . $actionparameter . "\n"; + push(@{$customactionidttable}, $line); + + $included_customaction = 1; + } + + if ( $included_customaction ) { $infoline = "Added $actionname CustomAction into table $customactionidttablename\n"; } + else { $infoline = "Did not add $actionname CustomAction into table $customactionidttablename\n"; } + push(@installer::globals::logfileinfo, $infoline); + + return $included_customaction; +} + +#################################################################### +# Adding a Custom Action to InstallExecuteTable or InstallUITable +#################################################################### + +sub add_custom_action_to_install_table +{ + my ($installtable, $exefilename, $actionname, $actioncondition, $position, $filesref, $installtablename, $styles) = @_; + + my $included_customaction = 0; + my $feature = ""; + my $infoline = ""; + + # when the style NO_FILE is set, no searching for the file is needed, no filtering is done, we can add that custom action + if ( $styles =~ /\bNO_FILE\b/ ) + { + # then the InstallE.idt.idt or InstallU.idt.idt + $actioncondition =~ s/FEATURETEMPLATE/$feature/g; # only execute Custom Action, if feature of the file is installed + + my $actionposition = 0; + + if ( $position eq "end" ) { $actionposition = get_last_position_in_sequencetable($installtable) + 25; } + elsif ( $position =~ /^\s*behind_/ ) { $actionposition = get_position_in_sequencetable($position, $installtable) + 2; } + else { $actionposition = get_position_in_sequencetable($position, $installtable) - 2; } + + my $line = $actionname . "\t" . $actioncondition . "\t" . $actionposition . "\n"; + push(@{$installtable}, $line); + + $infoline = "Added $actionname CustomAction into table $installtablename (NO_FILE has been set)\n"; + push(@installer::globals::logfileinfo, $infoline); + return; + } + + my $contains_file = 0; + + # All files are located in $filesref and in @installer::globals::binarytableonlyfiles. + # Both must be added together + my $localfilesref = installer::converter::combine_arrays_from_references(\@installer::globals::binarytableonlyfiles, $filesref); + + for ( my $i = 0; $i <= $#{$localfilesref}; $i++ ) + { + my $filename = ${$localfilesref}[$i]->{'Name'}; + + if ( $filename eq $exefilename ) + { + $contains_file = 1; + + # Determining the feature of the file + + if ( ${$localfilesref}[$i] ) { $feature = ${$localfilesref}[$i]->{'modules'}; } + + # If modules contains a list of modules, only taking the first one. + if ( $feature =~ /^\s*(.*?)\,/ ) { $feature = $1; } + # Attention: Maximum feature length is 38! + shorten_feature_gid(\$feature); + + last; + } + } + + if ( $contains_file ) + { + # then the InstallE.idt.idt or InstallU.idt.idt + + $actioncondition =~ s/FEATURETEMPLATE/$feature/g; # only execute Custom Action, if feature of the file is installed + +# my $actionposition = 0; +# if ( $position eq "end" ) { $actionposition = get_last_position_in_sequencetable($installtable) + 25; } +# elsif ( $position =~ /^\s*behind_/ ) { $actionposition = get_position_in_sequencetable($position, $installtable) + 2; } +# else { $actionposition = get_position_in_sequencetable($position, $installtable) - 2; } +# my $line = $actionname . "\t" . $actioncondition . "\t" . $actionposition . "\n"; + + my $positiontemplate = ""; + if ( $position =~ /^\s*\d+\s*$/ ) { $positiontemplate = $position; } # setting the position directly, number defined in scp2 + else { $positiontemplate = "POSITIONTEMPLATE_" . $position; } + + my $line = $actionname . "\t" . $actioncondition . "\t" . $positiontemplate . "\n"; + push(@{$installtable}, $line); + + $included_customaction = 1; + } + + if ( $included_customaction ) { $infoline = "Added $actionname CustomAction into table $installtablename\n"; } + else { $infoline = "Did not add $actionname CustomAction into table $installtablename\n"; } + push(@installer::globals::logfileinfo, $infoline); + +} + +################################################################## +# A line in the table ControlEvent connects a Control +# with a Custom Action +################################################################# + +sub connect_custom_action_to_control +{ + my ( $table, $tablename, $dialog, $control, $event, $argument, $condition, $ordering) = @_; + + my $line = $dialog . "\t" . $control. "\t" . $event. "\t" . $argument. "\t" . $condition. "\t" . $ordering . "\n"; + + push(@{$table}, $line); + + $line =~ s/\s*$//g; + + $infoline = "Added line \"$line\" into table $tablename\n"; + push(@installer::globals::logfileinfo, $infoline); +} + +################################################################## +# A line in the table ControlCondition connects a Control state +# with a condition +################################################################## + +sub connect_condition_to_control +{ + my ( $table, $tablename, $dialog, $control, $event, $condition) = @_; + + my $line = $dialog . "\t" . $control. "\t" . $event. "\t" . $condition. "\n"; + + push(@{$table}, $line); + + $line =~ s/\s*$//g; + + $infoline = "Added line \"$line\" into table $tablename\n"; + push(@installer::globals::logfileinfo, $infoline); +} + +################################################################## +# Searching for a sequencenumber in InstallUISequence table +# "ExecuteAction" must be the last action +################################################################## + +sub get_free_number_in_uisequence_table +{ + my ( $installuitable ) = @_; + + # determining the sequence of "ExecuteAction" + + my $executeactionnumber = 0; + + for ( my $i = 0; $i <= $#{$installuitable}; $i++ ) + { + if ( ${$installuitable}[$i] =~ /^\s*(\w+)\t\w*\t(\d+)\s*$/ ) + { + my $actionname = $1; + my $actionnumber = $2; + + if ( $actionname eq "ExecuteAction" ) + { + $executeactionnumber = $actionnumber; + last; + } + } + } + + if ( $executeactionnumber == 0 ) { installer::exiter::exit_program("ERROR: Did not find \"ExecuteAction\" in InstallUISequence table!", "get_free_number_in_uisequence_table"); } + + # determining the sequence of the action before "ExecuteAction" + + my $lastactionnumber = 0; + + for ( my $i = 0; $i <= $#{$installuitable}; $i++ ) + { + if ( ${$installuitable}[$i] =~ /^\s*\w+\t\w*\t(\d+)\s*$/ ) + { + my $actionnumber = $1; + + if (( $actionnumber > $lastactionnumber ) && ( $actionnumber != $executeactionnumber )) + { + $lastactionnumber = $actionnumber; + } + } + } + + # the new number can now be calculated + + my $newnumber = 0; + + if ((( $lastactionnumber + $executeactionnumber ) % 2 ) == 0 ) { $newnumber = ( $lastactionnumber + $executeactionnumber ) / 2; } + else { $newnumber = ( $lastactionnumber + $executeactionnumber -1 ) / 2; } + + return $newnumber; +} + +################################################################## +# Searching for a specified string in the feature table +################################################################## + +sub get_feature_name +{ + my ( $string, $featuretable ) = @_; + + my $featurename = ""; + + for ( my $i = 0; $i <= $#{$featuretable}; $i++ ) + { + if ( ${$featuretable}[$i] =~ /^\s*(\w+$string)\t/ ) + { + $featurename = $1; + last; + } + } + + return $featurename; +} + +###################################################################### +# Returning the toplevel directory name of one specific file +###################################################################### + +sub get_directory_name_from_file +{ + my ($onefile) = @_; + + my $destination = $onefile->{'destination'}; + my $name = $onefile->{'Name'}; + + $destination =~ s/\Q$name\E\s*$//; + $destination =~ s/\Q$installer::globals::separator\E\s*$//; + + my $path = ""; + + if ( $destination =~ /\Q$installer::globals::separator\E/ ) + { + if ( $destination =~ /^\s*(\S.*\S\Q$installer::globals::separator\E)(\S.+\S?)/ ) + { + $path = $2; + } + } + else + { + $path = $destination; + } + + return $path; +} + +############################################################# +# Including the new subdir into the directory table +############################################################# + +sub include_subdirname_into_directory_table +{ + my ($dirname, $directorytable, $directorytablename, $onefile) = @_; + + my $subdir = ""; + if ( $onefile->{'Subdir'} ) { $subdir = $onefile->{'Subdir'}; } + if ( $subdir eq "" ) { installer::exiter::exit_program("ERROR: No \"Subdir\" defined for $onefile->{'Name'}", "include_subdirname_into_directory_table"); } + + # program INSTALLLOCATION program -> subjava INSTALLLOCATION program:java + + my $uniquename = ""; + my $parent = ""; + my $name = ""; + + my $includedline = 0; + + my $newdir = ""; + + for ( my $i = 0; $i <= $#{$directorytable}; $i++ ) + { + + if ( ${$directorytable}[$i] =~ /^\s*(.*?)\t(.*?)\t(.*?)\s*$/ ) + { + $uniquename = $1; + $parent = $2; + $name = $3; + + if ( $dirname eq $name ) + { + my $newuniquename = "sub" . $subdir; + $newdir = $newuniquename; + my $newparent = $parent; + my $newname = $name . "\:" . $subdir; + my $newline = + $line = "$newuniquename\t$newparent\t$newname\n"; + push(@{$directorytable}, $line); + installer::remover::remove_leading_and_ending_whitespaces(\$line); + $infoline = "Added $line into directory table $directorytablename\n"; + push(@installer::globals::logfileinfo, $infoline); + + $includedline = 1; + last; + } + } + } + + if ( ! $includedline ) { installer::exiter::exit_program("ERROR: Could not include new subdirectory into directory table for file $onefile->{'Name'}!", "include_subdirname_into_directory_table"); } + + return $newdir; +} + +################################################################## +# Including the new sub directory into the component table +################################################################## + +sub include_subdir_into_componenttable +{ + my ($subdir, $onefile, $componenttable) = @_; + + my $componentname = $onefile->{'componentname'}; + + my $changeddirectory = 0; + + for ( my $i = 0; $i <= $#{$componenttable}; $i++ ) + { + if ( ${$componenttable}[$i] =~ /^\s*(.*?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\s*$/ ) + { + my $localcomponentname = $1; + my $directory = $3; + + if ( $componentname eq $localcomponentname ) + { + my $oldvalue = ${$componenttable}[$i]; + ${$componenttable}[$i] =~ s/\b\Q$directory\E\b/$subdir/; + my $newvalue = ${$componenttable}[$i]; + + installer::remover::remove_leading_and_ending_whitespaces(\$oldvalue); + installer::remover::remove_leading_and_ending_whitespaces(\$newvalue); + $infoline = "Change in Component table: From \"$oldvalue\" to \"$newvalue\"\n"; + push(@installer::globals::logfileinfo, $infoline); + + $changeddirectory = 1; + last; + } + } + } + + if ( ! $changeddirectory ) { installer::exiter::exit_program("ERROR: Could not change directory for component: $onefile->{'Name'}!", "include_subdir_into_componenttable"); } + +} + +################################################################################################ +# Including the content for the child installations +# into the tables: +# CustomAc.idt, InstallU.idt, Feature.idt +################################################################################################ + +sub add_childprojects +{ + my ($languageidtdir, $filesref, $allvariables) = @_; + + my $customactiontablename = $languageidtdir . $installer::globals::separator . "CustomAc.idt"; + my $customactiontable = installer::files::read_file($customactiontablename); + my $installuitablename = $languageidtdir . $installer::globals::separator . "InstallU.idt"; + my $installuitable = installer::files::read_file($installuitablename); + my $featuretablename = $languageidtdir . $installer::globals::separator . "Feature.idt"; + my $featuretable = installer::files::read_file($featuretablename); + my $directorytablename = $languageidtdir . $installer::globals::separator . "Director.idt"; + my $directorytable = installer::files::read_file($directorytablename); + my $componenttablename = $languageidtdir . $installer::globals::separator . "Componen.idt"; + my $componenttable = installer::files::read_file($componenttablename); + + my $infoline = ""; + my $line = ""; + + $installer::globals::javafile = installer::worker::return_first_item_with_special_flag($filesref ,"JAVAFILE"); + $installer::globals::urefile = installer::worker::return_first_item_with_special_flag($filesref ,"UREFILE"); + + if (( $installer::globals::javafile eq "" ) && ( $allvariables->{'JAVAPRODUCT'} )) { installer::exiter::exit_program("ERROR: No JAVAFILE found in files collector!", "add_childprojects"); } + if (( $installer::globals::urefile eq "" ) && ( $allvariables->{'UREPRODUCT'} )) { installer::exiter::exit_program("ERROR: No UREFILE found in files collector!", "add_childprojects"); } + + # Content for Directory table + # SystemFolder TARGETDIR . + + my $contains_systemfolder = 0; + + for ( my $i = 0; $i <= $#{$directorytable}; $i++ ) + { + if ( ${$directorytable}[$i] =~ /^\s*SystemFolder\t/ ) + { + $contains_systemfolder = 1; + last; + } + } + + if ( ! $contains_systemfolder ) + { + $line = "SystemFolder\tTARGETDIR\t\.\n"; + push(@{$directorytable}, $line); + installer::remover::remove_leading_and_ending_whitespaces(\$line); + $infoline = "Added $line into table $directorytablename\n"; + } + else + { + $infoline = "SystemFolder already exists in table $directorytablename\n"; + } + + push(@installer::globals::logfileinfo, $infoline); + + # Additional content for the directory table + # subjava INSTALLLOCATION program:java + # subure INSTALLLOCATION program:ure + + my $dirname = ""; + my $subjavadir = ""; + my $suburedir = ""; + + if ( $allvariables->{'JAVAPRODUCT'} ) + { + $dirname = get_directory_name_from_file($installer::globals::javafile); + $subjavadir = include_subdirname_into_directory_table($dirname, $directorytable, $directorytablename, $installer::globals::javafile); + } + + if ( $allvariables->{'UREPRODUCT'} ) + { + $dirname = get_directory_name_from_file($installer::globals::urefile); + $suburedir = include_subdirname_into_directory_table($dirname, $directorytable, $directorytablename, $installer::globals::urefile); + } + + # Content for the Component table + # The Java and Ada components have new directories + + if ( $allvariables->{'JAVAPRODUCT'} ) { include_subdir_into_componenttable($subjavadir, $installer::globals::javafile, $componenttable); } + if ( $allvariables->{'UREPRODUCT'} ) { include_subdir_into_componenttable($suburedir, $installer::globals::urefile, $componenttable); } + + # Content for CustomAction table + + if ( $allvariables->{'JAVAPRODUCT'} ) + { + $line = "InstallJava\t98\tSystemFolder\t[SourceDir]$installer::globals::javafile->{'Subdir'}\\$installer::globals::javafile->{'Name'} \/qb REBOOT=Suppress SPONSORS=0 DISABLEAD=1\n"; + push(@{$customactiontable} ,$line); + installer::remover::remove_leading_and_ending_whitespaces(\$line); + $infoline = "Added $line into table $customactiontablename\n"; + push(@installer::globals::logfileinfo, $infoline); + } + + if ( $allvariables->{'UREPRODUCT'} ) + { + $line = "InstallUre\t98\tSystemFolder\t$installer::globals::urefile->{'Subdir'}\\$installer::globals::urefile->{'Name'} /S\n"; + push(@{$customactiontable} ,$line); + installer::remover::remove_leading_and_ending_whitespaces(\$line); + $infoline = "Added $line into table $customactiontablename\n"; + push(@installer::globals::logfileinfo, $infoline); + } + + if ( $allvariables->{'JAVAPRODUCT'} ) + { + $line = "MaintenanceJava\t82\t$installer::globals::javafile->{'uniquename'}\t\/qb REBOOT=Suppress SPONSORS=0 DISABLEAD=1\n"; + push(@{$customactiontable} ,$line); + installer::remover::remove_leading_and_ending_whitespaces(\$line); + $infoline = "Added $line into table $customactiontablename\n"; + push(@installer::globals::logfileinfo, $infoline); + } + + if ( $allvariables->{'UREPRODUCT'} ) + { + $line = "MaintenanceUre\t82\t$installer::globals::urefile->{'uniquename'}\t\/S\n"; + push(@{$customactiontable} ,$line); + installer::remover::remove_leading_and_ending_whitespaces(\$line); + $infoline = "Added $line into table $customactiontablename\n"; + push(@installer::globals::logfileinfo, $infoline); + } + + # Content for InstallUISequence table + # InstallAdabas &gm_o_Adabas=3 825 + # InstallJava &gm_o_Java=3 827 + + my $number = ""; + my $featurename = ""; + + if ( $allvariables->{'ADAPRODUCT'} ) + { + $number = get_free_number_in_uisequence_table($installuitable); + $featurename = get_feature_name("_Adabas", $featuretable); + $line = "InstallAdabas\t\&$featurename\=3 And Not Installed And Not PATCH\t$number\n"; + push(@{$installuitable} ,$line); + installer::remover::remove_leading_and_ending_whitespaces(\$line); + $infoline = "Added $line into table $installuitablename\n"; + push(@installer::globals::logfileinfo, $infoline); + } + + if ( $allvariables->{'JAVAPRODUCT'} ) + { + $number = get_free_number_in_uisequence_table($installuitable) + 2; + $featurename = get_feature_name("_Java", $featuretable); + if ( $featurename ) { $line = "InstallJava\t\&$featurename\=3 And Not Installed And JAVAPATH\=\"\" And Not PATCH\t$number\n"; } + else { $line = "InstallJava\tNot Installed And JAVAPATH\=\"\" And Not PATCH\t$number\n"; } # feature belongs to root + push(@{$installuitable} ,$line); + installer::remover::remove_leading_and_ending_whitespaces(\$line); + $infoline = "Added $line into table $installuitablename\n"; + push(@installer::globals::logfileinfo, $infoline); + } + + if ( $allvariables->{'ADAPRODUCT'} ) + { + $number = get_free_number_in_uisequence_table($installuitable) + 4; + $featurename = get_feature_name("_Adabas", $featuretable); + $line = "MaintenanceAdabas\t\&$featurename\=3 And Installed And Not PATCH\t$number\n"; + push(@{$installuitable} ,$line); + installer::remover::remove_leading_and_ending_whitespaces(\$line); + $infoline = "Added $line into table $installuitablename\n"; + push(@installer::globals::logfileinfo, $infoline); + } + + if ( $allvariables->{'JAVAPRODUCT'} ) + { + $number = get_free_number_in_uisequence_table($installuitable) + 6; + $featurename = get_feature_name("_Java", $featuretable); + if ( $featurename ) { $line = "MaintenanceJava\t\&$featurename\=3 And Installed And JAVAPATH\=\"\" And Not PATCH\t$number\n"; } + else { $line = "MaintenanceJava\tInstalled And JAVAPATH\=\"\" And Not PATCH\t$number\n"; } # feature belongs to root + push(@{$installuitable} ,$line); + installer::remover::remove_leading_and_ending_whitespaces(\$line); + $infoline = "Added $line into table $installuitablename\n"; + push(@installer::globals::logfileinfo, $infoline); + } + + if ( $allvariables->{'UREPRODUCT'} ) + { + $number = get_free_number_in_uisequence_table($installuitable) + 8; + $featurename = get_feature_name("_Ure", $featuretable); + if ( $featurename ) { $line = "InstallUre\t\&$featurename\=3 And Not Installed\t$number\n"; } + else { $line = "InstallUre\tNot Installed\t$number\n"; } # feature belongs to root + push(@{$installuitable} ,$line); + installer::remover::remove_leading_and_ending_whitespaces(\$line); + $infoline = "Added $line into table $installuitablename\n"; + push(@installer::globals::logfileinfo, $infoline); + } + + if ( $allvariables->{'UREPRODUCT'} ) + { + $number = get_free_number_in_uisequence_table($installuitable) + 10; + $featurename = get_feature_name("_Ure", $featuretable); + if ( $featurename ) { $line = "MaintenanceUre\t\&$featurename\=3 And Installed\t$number\n"; } + else { $line = "MaintenanceUre\tInstalled\t$number\n"; } # feature belongs to root + push(@{$installuitable} ,$line); + installer::remover::remove_leading_and_ending_whitespaces(\$line); + $infoline = "Added $line into table $installuitablename\n"; + push(@installer::globals::logfileinfo, $infoline); + } + + # Content for Feature table, better from scp (translation) + # gm_o_java gm_optional Java 1.4.2 Description 2 200 + + installer::files::save_file($customactiontablename, $customactiontable); + installer::files::save_file($installuitablename, $installuitable); + installer::files::save_file($featuretablename, $featuretable); + installer::files::save_file($directorytablename, $directorytable); + installer::files::save_file($componenttablename, $componenttable); +} + +################################################################## +# Setting the encoding in all idt files. Replacing the +# variable WINDOWSENCODINGTEMPLATE +################################################################## + +sub setencoding +{ + my ( $languageidtdir, $onelanguage ) = @_; + + my $encoding = installer::windows::language::get_windows_encoding($onelanguage); + + # collecting all idt files in the directory $languageidtdir and substituting the string + + my $idtfiles = installer::systemactions::find_file_with_file_extension("idt", $languageidtdir); + + for ( my $i = 0; $i <= $#{$idtfiles}; $i++ ) + { + my $onefilename = $languageidtdir . $installer::globals::separator . ${$idtfiles}[$i]; + my $onefile = installer::files::read_file($onefilename); + + for ( my $j = 0; $j <= $#{$onefile}; $j++ ) + { + ${$onefile}[$j] =~ s/WINDOWSENCODINGTEMPLATE/$encoding/g; + } + + installer::files::save_file($onefilename, $onefile); + } +} + +################################################################## +# Setting the condition, that at least one module is selected. +# All modules with flag SHOW_MULTILINGUAL_ONLY were already +# collected. In table ControlE.idt, the string +# LANGUAGECONDITIONINSTALL needs to be replaced. +# Also for APPLICATIONCONDITIONINSTALL for the applications +# with flag APPLICATIONMODULE. +################################################################## + +sub set_multilanguageonly_condition +{ + my ( $languageidtdir ) = @_; + + my $onefilename = $languageidtdir . $installer::globals::separator . "ControlE.idt"; + my $onefile = installer::files::read_file($onefilename); + + # Language modules + + my $condition = ""; + + foreach my $module ( sort keys %installer::globals::multilingual_only_modules ) + { + $condition = $condition . " &$module=3 Or"; + } + + $condition =~ s/^\s*//; + $condition =~ s/\s*Or\s*$//; # removing the ending "Or" + + if ( $condition eq "" ) { $condition = "1"; } + + for ( my $j = 0; $j <= $#{$onefile}; $j++ ) + { + ${$onefile}[$j] =~ s/LANGUAGECONDITIONINSTALL/$condition/; + } + + # Application modules + + $condition = ""; + + foreach my $module ( sort keys %installer::globals::application_modules ) + { + $condition = $condition . " &$module=3 Or"; + } + + $condition =~ s/^\s*//; + $condition =~ s/\s*Or\s*$//; # removing the ending "Or" + + if ( $condition eq "" ) { $condition = "1"; } + + for ( my $j = 0; $j <= $#{$onefile}; $j++ ) + { + ${$onefile}[$j] =~ s/APPLICATIONCONDITIONINSTALL/$condition/; + } + + installer::files::save_file($onefilename, $onefile); +} + +############################################# +# Putting array values into hash +############################################# + +sub fill_assignment_hash +{ + my ($gid, $name, $key, $assignmenthashref, $parameter, $tablename, $assignmentarray) = @_; + + my $max = $parameter - 1; + + if ( $max != $#{$assignmentarray} ) + { + my $definedparameter = $#{$assignmentarray} + 1; + installer::exiter::exit_program("ERROR: gid: $gid, key: $key ! Wrong parameter in scp. For table $tablename $parameter parameter are required ! You defined: $definedparameter", "fill_assignment_hash"); + } + + for ( my $i = 0; $i <= $#{$assignmentarray}; $i++ ) + { + my $counter = $i + 1; + my $key = "parameter". $counter; + + my $localvalue = ${$assignmentarray}[$i]; + installer::remover::remove_leading_and_ending_quotationmarks(\$localvalue); + $localvalue =~ s/\\\"/\"/g; + $localvalue =~ s/\\\!/\!/g; + $localvalue =~ s/\\\&/\&/g; + $localvalue =~ s/\\\</\</g; + $localvalue =~ s/\\\>/\>/g; + $assignmenthashref->{$key} = $localvalue; + } +} + +########################################################################## +# Checking the assignment of a Windows CustomAction and putting it +# into a hash +########################################################################## + +sub create_customaction_assignment_hash +{ + my ($gid, $name, $key, $assignmentarray) = @_; + + my %assignment = (); + my $assignmenthashref = \%assignment; + + my $tablename = ${$assignmentarray}[0]; + installer::remover::remove_leading_and_ending_quotationmarks(\$tablename); + + my $tablename_defined = 0; + my $parameter = 0; + + if ( $tablename eq "InstallUISequence" ) + { + $tablename_defined = 1; + $parameter = 3; + fill_assignment_hash($gid, $name, $key, $assignmenthashref, $parameter, $tablename, $assignmentarray); + } + + if ( $tablename eq "InstallExecuteSequence" ) + { + $tablename_defined = 1; + $parameter = 3; + fill_assignment_hash($gid, $name, $key, $assignmenthashref, $parameter, $tablename, $assignmentarray); + } + + if ( $tablename eq "AdminExecuteSequence" ) + { + $tablename_defined = 1; + $parameter = 3; + fill_assignment_hash($gid, $name, $key, $assignmenthashref, $parameter, $tablename, $assignmentarray); + } + + if ( $tablename eq "ControlEvent" ) + { + $tablename_defined = 1; + $parameter = 7; + fill_assignment_hash($gid, $name, $key, $assignmenthashref, $parameter, $tablename, $assignmentarray); + } + + if ( $tablename eq "ControlCondition" ) + { + $tablename_defined = 1; + $parameter = 5; + fill_assignment_hash($gid, $name, $key, $assignmenthashref, $parameter, $tablename, $assignmentarray); + } + + if ( ! $tablename_defined ) + { + installer::exiter::exit_program("ERROR: gid: $gid, key: $key ! Unknown Windows CustomAction table: $tablename ! Currently supported: InstallUISequence, InstallExecuteSequence, ControlEvent, ControlCondition", "create_customaction_assignment_hash"); + } + + return $assignmenthashref; +} + +########################################################################## +# Finding the position of a specified CustomAction. +# If the CustomAction is not found, the return value is "-1". +# If the CustomAction position is not defined yet, +# the return value is also "-1". +########################################################################## + +sub get_customaction_position +{ + my ($action, $sequencetable) = @_; + + my $position = -1; + + for ( my $i = 0; $i <= $#{$sequencetable}; $i++ ) + { + my $line = ${$sequencetable}[$i]; + + if ( $line =~ /^\s*([\w\.]+)\t.*\t\s*(\d+)\s$/ ) # matching only, if position is a number! + { + my $compareaction = $1; + my $localposition = $2; + + if ( $compareaction eq $action ) + { + $position = $localposition; + last; + } + } + } + + return $position; +} + +########################################################################## +# Setting the position of CustomActions in sequence tables. +# Replacing all occurences of "POSITIONTEMPLATE_" +########################################################################## + +sub set_positions_in_table +{ + my ( $sequencetable, $tablename ) = @_; + + my $infoline = "\nSetting positions in table \"$tablename\".\n"; + push(@installer::globals::logfileinfo, $infoline); + + # Step 1: Resolving all occurences of "POSITIONTEMPLATE_end" + + my $lastposition = get_last_position_in_sequencetable($sequencetable); + + for ( my $i = 0; $i <= $#{$sequencetable}; $i++ ) + { + if ( ${$sequencetable}[$i] =~ /^\s*([\w\.]+)\t.*\t\s*POSITIONTEMPLATE_end\s*$/ ) + { + my $customaction = $1; + $lastposition = $lastposition + 25; + ${$sequencetable}[$i] =~ s/POSITIONTEMPLATE_end/$lastposition/; + $infoline = "Setting position \"$lastposition\" for custom action \"$customaction\".\n"; + push(@installer::globals::logfileinfo, $infoline); + } + } + + # Step 2: Resolving all occurences of "POSITIONTEMPLATE_abc" or "POSITIONTEMPLATE_behind_abc" + # where abc is the name of the reference Custom Action. + # This has to be done, until there is no more occurence of POSITIONTEMPLATE (success) + # or there is no replacement in one circle (failure). + + my $template_exists = 0; + my $template_replaced = 0; + my $counter = 0; + + do + { + $template_exists = 0; + $template_replaced = 0; + $counter++; + + for ( my $i = 0; $i <= $#{$sequencetable}; $i++ ) + { + if ( ${$sequencetable}[$i] =~ /^\s*([\w\.]+)\t.*\t\s*(POSITIONTEMPLATE_.*?)\s*$/ ) + { + my $onename = $1; + my $templatename = $2; + my $positionname = $templatename; + my $customaction = $templatename; + $customaction =~ s/POSITIONTEMPLATE_//; + $template_exists = 1; + + # Trying to find the correct number. + # This can fail, if the custom action has no number + + my $setbehind = 0; + if ( $customaction =~ /^\s*behind_(.*?)\s*$/ ) + { + $customaction = $1; + $setbehind = 1; + } + + my $position = get_customaction_position($customaction, $sequencetable); + + if ( $position >= 0 ) # Found CustomAction and is has a position. Otherwise return value is "-1". + { + my $newposition = 0; + if ( $setbehind ) { $newposition = $position + 2; } + else { $newposition = $position - 2; } + ${$sequencetable}[$i] =~ s/$templatename/$newposition/; + $template_replaced = 1; + $infoline = "Setting position \"$newposition\" for custom action \"$onename\" (scp: \"$positionname\" at position $position).\n"; + push(@installer::globals::logfileinfo, $infoline); + } + else + { + $infoline = "Could not assign position for custom action \"$onename\" yet (scp: \"$positionname\").\n"; + push(@installer::globals::logfileinfo, $infoline); + } + } + } + } while (( $template_exists ) && ( $template_replaced )); + + # An error occured, because templates still exist, but could not be replaced. + # Reason: + # 1. Wrong name of CustomAction in scp2 (typo?) + # 2. Circular dependencies of CustomActions (A after B and B after A) + + # Problem: It is allowed, that a CustomAction is defined in scp2 in a library that is + # part of product ABC, but this CustomAction is not used in this product + # and the reference CustomAction is not part of this product. + # Therefore this cannot be an error, but only produce a warning. The assigned number + # must be the last sequence number. + + if (( $template_exists ) && ( ! $template_replaced )) + { + # Giving a precise error message, collecting all unresolved templates + # my $templatestring = ""; + + for ( my $i = 0; $i <= $#{$sequencetable}; $i++ ) + { + if ( ${$sequencetable}[$i] =~ /^\s*([\w\.]+)\t.*\t\s*(POSITIONTEMPLATE_.*?)\s*$/ ) + { + my $customactionname = $1; + my $fulltemplate = $2; + my $template = $fulltemplate; + $template =~ s/POSITIONTEMPLATE_//; + # my $newstring = $customactionname . " (" . $template . ")"; + # $templatestring = $templatestring . $newstring . ", "; + # Setting at the end! + $lastposition = $lastposition + 25; + ${$sequencetable}[$i] =~ s/$fulltemplate/$lastposition/; + $infoline = "WARNING: Setting position \"$lastposition\" for custom action \"$customactionname\". Could not find CustomAction \"$template\".\n"; + push(@installer::globals::logfileinfo, $infoline); + } + } + # $templatestring =~ s/,\s*$//; + + # $infoline = "Error: Saving table \"$tablename\"\n"; + # push(@installer::globals::logfileinfo, $infoline); + # print $infoline; + # installer::files::save_file($tablename, $sequencetable); + # installer::exiter::exit_program("ERROR: Unresolved positions in CustomActions in scp2: $templatestring", "set_positions_in_table"); + } +} + +########################################################################## +# Setting the Windows custom actions into different tables +# CustomAc.idt, InstallE.idt, InstallU.idt, ControlE.idt, ControlC.idt +########################################################################## + +sub addcustomactions +{ + my ($languageidtdir, $customactions, $filesarray) = @_; + + installer::logger::include_timestamp_into_logfile("\nPerformance Info: addcustomactions start\n"); + + my $customactionidttablename = $languageidtdir . $installer::globals::separator . "CustomAc.idt"; + my $customactionidttable = installer::files::read_file($customactionidttablename); + my $installexecutetablename = $languageidtdir . $installer::globals::separator . "InstallE.idt"; + my $installexecutetable = installer::files::read_file($installexecutetablename); + my $adminexecutetablename = $languageidtdir . $installer::globals::separator . "AdminExe.idt"; + my $adminexecutetable = installer::files::read_file($adminexecutetablename); + my $installuitablename = $languageidtdir . $installer::globals::separator . "InstallU.idt"; + my $installuitable = installer::files::read_file($installuitablename); + my $controleventtablename = $languageidtdir . $installer::globals::separator . "ControlE.idt"; + my $controleventtable = installer::files::read_file($controleventtablename); + my $controlconditiontablename = $languageidtdir . $installer::globals::separator . "ControlC.idt"; + my $controlconditiontable = installer::files::read_file($controlconditiontablename); + + # Iterating over all Windows custom actions + + for ( my $i = 0; $i <= $#{$customactions}; $i++ ) + { + my $customaction = ${$customactions}[$i]; + my $name = $customaction->{'Name'}; + my $typ = $customaction->{'Typ'}; + my $source = $customaction->{'Source'}; + my $target = $customaction->{'Target'}; + my $inbinarytable = $customaction->{'Inbinarytable'}; + my $gid = $customaction->{'gid'}; + + my $styles = ""; + if ( $customaction->{'Styles'} ) { $styles = $customaction->{'Styles'}; } + + my $added_customaction = set_custom_action($customactionidttable, $name, $typ, $source, $target, $inbinarytable, $filesarray, $customactionidttablename, $styles); + + if ( $added_customaction ) + { + # If the CustomAction was added into the CustomAc.idt, it can be connected to the installation. + # There are currently two different ways for doing this: + # 1. Using "add_custom_action_to_install_table", which adds the CustomAction to the install sequences, + # which are saved in InstallE.idt and InstallU.idt + # 2. Using "connect_custom_action_to_control" and "connect_custom_action_to_control". The first method + # connects a CustomAction to a control in ControlE.idt. The second method sets a condition for a control, + # which might be influenced by the CustomAction. This happens in ControlC.idt. + + # Any Windows CustomAction can have a lot of different assignments. + + for ( my $j = 1; $j <= 50; $j++ ) + { + my $key = "Assignment" . $j; + my $value = ""; + if ( $customaction->{$key} ) + { + $value = $customaction->{$key}; + + # in a patch the Assignment can be overwritten by a PatchAssignment + if ( $installer::globals::patch ) + { + $patchkey = "PatchAssignment" . $j; + if ( $customaction->{$patchkey} ) + { + $value = $customaction->{$patchkey}; + $key = $patchkey; + } + } + + } + else { last; } + + # $value is now a comma separated list + if ( $value =~ /^\s*\(\s*(.*)\s*\);?\s*$/ ) { $value = $1; } + my $assignmentarray = installer::converter::convert_stringlist_into_array(\$value, ","); + my $assignment = create_customaction_assignment_hash($gid, $name, $key, $assignmentarray); + + if ( $assignment->{'parameter1'} eq "InstallExecuteSequence" ) + { + add_custom_action_to_install_table($installexecutetable, $source, $name, $assignment->{'parameter2'}, $assignment->{'parameter3'}, $filesarray, $installexecutetablename, $styles); + } + elsif ( $assignment->{'parameter1'} eq "AdminExecuteSequence" ) + { + add_custom_action_to_install_table($adminexecutetable, $source, $name, $assignment->{'parameter2'}, $assignment->{'parameter3'}, $filesarray, $adminexecutetablename, $styles); + } + elsif ( $assignment->{'parameter1'} eq "InstallUISequence" ) + { + add_custom_action_to_install_table($installuitable, $source, $name, $assignment->{'parameter2'}, $assignment->{'parameter3'}, $filesarray, $installuitablename, $styles); + } + elsif ( $assignment->{'parameter1'} eq "ControlEvent" ) + { + connect_custom_action_to_control($controleventtable, $controleventtablename, $assignment->{'parameter2'}, $assignment->{'parameter3'}, $assignment->{'parameter4'}, $assignment->{'parameter5'}, $assignment->{'parameter6'}, $assignment->{'parameter7'}); + } + elsif ( $assignment->{'parameter1'} eq "ControlCondition" ) + { + connect_condition_to_control($controlconditiontable, $controlconditiontablename, $assignment->{'parameter2'}, $assignment->{'parameter3'}, $assignment->{'parameter4'}, $assignment->{'parameter5'}); + } + else + { + installer::exiter::exit_program("ERROR: gid: $gid, key: $key ! Unknown Windows CustomAction table: $assignmenthashref->{'parameter1'} ! Currently supported: InstallUISequence, InstallESequence, ControlEvent, ControlCondition", "addcustomactions"); + } + } + } + } + + # Setting the positions in the tables + + set_positions_in_table($installexecutetable, $installexecutetablename); + set_positions_in_table($installuitable, $installuitablename); + set_positions_in_table($adminexecutetable, $adminexecutetablename); + + # Saving the files + + installer::files::save_file($customactionidttablename, $customactionidttable); + installer::files::save_file($installexecutetablename, $installexecutetable); + installer::files::save_file($adminexecutetablename, $adminexecutetable); + installer::files::save_file($installuitablename, $installuitable); + installer::files::save_file($controleventtablename, $controleventtable); + installer::files::save_file($controlconditiontablename, $controlconditiontable); + + my $infoline = "Updated idt file: $customactionidttablename\n"; + push(@installer::globals::logfileinfo, $infoline); + $infoline = "Updated idt file: $installexecutetablename\n"; + push(@installer::globals::logfileinfo, $infoline); + $infoline = "Updated idt file: $adminexecutetablename\n"; + push(@installer::globals::logfileinfo, $infoline); + $infoline = "Updated idt file: $installuitablename\n"; + push(@installer::globals::logfileinfo, $infoline); + $infoline = "Updated idt file: $controleventtablename\n"; + push(@installer::globals::logfileinfo, $infoline); + $infoline = "Updated idt file: $controlconditiontablename\n"; + push(@installer::globals::logfileinfo, $infoline); + + installer::logger::include_timestamp_into_logfile("\nPerformance Info: addcustomactions end\n"); +} + +########################################################################## +# Setting bidi attributes in idt tables +########################################################################## + +sub setbidiattributes +{ + my ($languageidtdir, $onelanguage) = @_; + + # Editing the files Dialog.idt and Control.idt + + my $dialogfilename = $languageidtdir . $installer::globals::separator . "Dialog.idt"; + my $controlfilename = $languageidtdir . $installer::globals::separator . "Control.idt"; + + my $dialogfile = installer::files::read_file($dialogfilename); + my $controlfile = installer::files::read_file($controlfilename); + + # Searching attributes in Dialog.idt and adding "896". + # Attributes are in column 6 (from 10). + + my $bidiattribute = 896; + for ( my $i = 0; $i <= $#{$dialogfile}; $i++ ) + { + if ( $i < 3 ) { next; } + if ( ${$dialogfile}[$i] =~ /^\s*(.*?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\s*$/ ) + { + my $one = $1; + my $two = $2; + my $three = $3; + my $four = $4; + my $five = $5; + my $attribute = $6; + my $seven = $7; + my $eight = $8; + $attribute = $attribute + $bidiattribute; + ${$dialogfile}[$i] = "$one\t$two\t$three\t$four\t$five\t$attribute\t$seven\t$eight\n"; + } + } + + # Searching attributes in Control.idt and adding "224". + # Attributes are in column 8 (from 12). + + $bidiattribute = 224; + for ( my $i = 0; $i <= $#{$controlfile}; $i++ ) + { + if ( $i < 3 ) { next; } + if ( ${$controlfile}[$i] =~ /^\s*(.*?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\s*$/ ) + { + my $one = $1; + my $two = $2; + my $three = $3; + my $four = $4; + my $five = $5; + my $six = $6; + my $seven = $7; + my $attribute = $8; + my $nine = $9; + my $ten = $10; + my $eleven = $11; + my $twelve = $12; + $attribute = $attribute + $bidiattribute; + ${$controlfile}[$i] = "$one\t$two\t$three\t$four\t$five\t$six\t$seven\t$attribute\t$nine\t$ten\t$eleven\t$twelve\n"; + } + } + + # Saving the file + + installer::files::save_file($dialogfilename, $dialogfile); + $infoline = "Set bidi support in idt file \"$dialogfilename\" for language $onelanguage\n"; + push(@installer::globals::logfileinfo, $infoline); + + installer::files::save_file($controlfilename, $controlfile); + $infoline = "Set bidi support in idt file \"$controlfilename\" for language $onelanguage\n"; + push(@installer::globals::logfileinfo, $infoline); +} + +1; diff --git a/solenv/bin/modules/installer/windows/inifile.pm b/solenv/bin/modules/installer/windows/inifile.pm new file mode 100644 index 000000000000..b6737495279a --- /dev/null +++ b/solenv/bin/modules/installer/windows/inifile.pm @@ -0,0 +1,150 @@ +#************************************************************************* +# +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# Copyright 2008 by Sun Microsystems, Inc. +# +# OpenOffice.org - a multi-platform office productivity suite +# +# $RCSfile: inifile.pm,v $ +# +# $Revision: 1.4 $ +# +# This file is part of OpenOffice.org. +# +# OpenOffice.org is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License version 3 +# only, as published by the Free Software Foundation. +# +# OpenOffice.org is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License version 3 for more details +# (a copy is included in the LICENSE file that accompanied this code). +# +# You should have received a copy of the GNU Lesser General Public License +# version 3 along with OpenOffice.org. If not, see +# <http://www.openoffice.org/license.html> +# for a copy of the LGPLv3 License. +# +#************************************************************************* + +package installer::windows::inifile; + +use installer::existence; +use installer::files; +use installer::globals; +use installer::windows::idtglobal; + +#################################################### +# Setting the profile for a special profileitem +#################################################### + +sub get_profile_for_profileitem +{ + my ($profileid, $filesref) = @_; + + my $profile = installer::existence::get_specified_file($filesref, $profileid); + + return $profile; +} + +#################################################### +# Checking whether profile is included in patch +#################################################### + +sub profile_has_patch_flag +{ + my ($profile) = @_; + + my $in_patch = 0; + + my $styles = ""; + if ( $profile->{'Styles'} ) { $styles = $profile->{'Styles'}; } + if ( $styles =~ /\bPATCH\b/ ) { $in_patch = 1; } + + return $in_patch; +} + +#################################################### +# Checking whether profile is part of product +#################################################### + +sub file_is_part_of_product +{ + my ($profilegid, $filesref) = @_; + + my $part_of_product = 0; + + for ( my $i = 0; $i <= $#{$filesref}; $i++ ) + { + $onefile = ${$filesref}[$i]; + my $filegid = $onefile->{'gid'}; + + if ( $filegid eq $profilegid ) + { + $part_of_product = 1; + last; + } + } + + return $part_of_product; +} + +########################################################################################################### +# Creating the file IniFile.idt dynamically +# Content: +# IniFile\tFileName\tDirProperty\tSection\tKey\tValue\tAction\tComponent_ +########################################################################################################### + +sub create_inifile_table +{ + my ($inifiletableentries, $filesref, $basedir) = @_; + + my @inifiletable = (); + + installer::windows::idtglobal::write_idt_header(\@inifiletable, "inifile"); + + for ( my $i = 0; $i <= $#{$inifiletableentries}; $i++ ) + { + my $profileitem = ${$inifiletableentries}[$i]; + + my $profileid = $profileitem->{'ProfileID'}; + + # Is this profile part of the product? This is not sure, for example in patch process. + # If the profile is not part of the product, this ProfileItem must be ignored. + + if ( ! file_is_part_of_product($profileid, $filesref) ) { next; } + + my $profile = get_profile_for_profileitem($profileid, $filesref); + + if (( $installer::globals::patch ) && ( ! profile_has_patch_flag($profile) )) { next; } + + my %inifile = (); + + $inifile{'IniFile'} = $profileitem->{'Inifiletablekey'}; + $inifile{'FileName'} = $profile->{'Name'}; + $inifile{'DirProperty'} = $profile->{'uniquedirname'}; + $inifile{'Section'} = $profileitem->{'Section'}; + $inifile{'Key'} = $profileitem->{'Key'}; + $inifile{'Value'} = $profileitem->{'Value'}; + $inifile{'Action'} = $profileitem->{'Inifiletableaction'}; + $inifile{'Component_'} = $profile->{'componentname'}; + + my $oneline = $inifile{'IniFile'} . "\t" . $inifile{'FileName'} . "\t" . $inifile{'DirProperty'} . "\t" + . $inifile{'Section'} . "\t" . $inifile{'Key'} . "\t" . $inifile{'Value'} . "\t" + . $inifile{'Action'} . "\t" . $inifile{'Component_'} . "\n"; + + push(@inifiletable, $oneline); + } + + # Saving the file + + my $inifiletablename = $basedir . $installer::globals::separator . "IniFile.idt"; + installer::files::save_file($inifiletablename ,\@inifiletable); + my $infoline = "Created idt file: $inifiletablename\n"; + push(@installer::globals::logfileinfo, $infoline); + +} + +1; diff --git a/solenv/bin/modules/installer/windows/java.pm b/solenv/bin/modules/installer/windows/java.pm new file mode 100644 index 000000000000..6b9cb7d725aa --- /dev/null +++ b/solenv/bin/modules/installer/windows/java.pm @@ -0,0 +1,124 @@ +#************************************************************************* +# +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# Copyright 2008 by Sun Microsystems, Inc. +# +# OpenOffice.org - a multi-platform office productivity suite +# +# $RCSfile: java.pm,v $ +# +# $Revision: 1.5 $ +# +# This file is part of OpenOffice.org. +# +# OpenOffice.org is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License version 3 +# only, as published by the Free Software Foundation. +# +# OpenOffice.org is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License version 3 for more details +# (a copy is included in the LICENSE file that accompanied this code). +# +# You should have received a copy of the GNU Lesser General Public License +# version 3 along with OpenOffice.org. If not, see +# <http://www.openoffice.org/license.html> +# for a copy of the LGPLv3 License. +# +#************************************************************************* + +package installer::windows::java; + +use installer::exiter; +use installer::files; +use installer::globals; +use installer::windows::idtglobal; + +#################################################################################### +# Writing content into RegLocat.idt and AppSearc.idt to find Java on system +#################################################################################### + +sub update_java_tables +{ + my ($basedir, $allvariables) = @_; + + my $reglocatfile = ""; + my $appsearchfile = ""; + + my $reglocatfilename = $basedir . $installer::globals::separator . "RegLocat.idt"; + my $appsearchfilename = $basedir . $installer::globals::separator . "AppSearc.idt"; + my $signaturefilename = $basedir . $installer::globals::separator . "Signatur.idt"; + + if ( -f $reglocatfilename ) + { + $reglocatfile = installer::files::read_file($reglocatfilename); + } + else + { + my @reglocattable = (); + $reglocatfile = \@reglocattable; + installer::windows::idtglobal::write_idt_header($reglocatfile, "reglocat"); + } + + if ( -f $appsearchfilename ) + { + $appsearchfile = installer::files::read_file($appsearchfilename); + } + else + { + my @appsearchtable = (); + $appsearchfile = \@appsearchtable; + installer::windows::idtglobal::write_idt_header($appsearchfile, "appsearch"); + } + + if ( -f $signaturefilename ) + { + $signaturefile = installer::files::read_file($signaturefilename); + } + else + { + my @signaturetable = (); + $signaturefile = \@signaturetable; + installer::windows::idtglobal::write_idt_header($signaturefile, "signatur"); + } + + # Writing content into this tables + # Java version is saved in scp project + # $installer::globals::javafile was defined in installer::windows::idtglobal::add_childprojects + + if ( ! $installer::globals::javafile->{'Javaversion'} ) { installer::exiter::exit_program("ERROR: \"Javaversion\" has to be defined in $installer::globals::javafile->{'gid'} in scp project!", "update_java_tables"); } + + my $javastring = $installer::globals::javafile->{'Javaversion'}; + + my $signature = "JavaReg"; + my $rootvalue = "2"; + my $key = "Software\\JavaSoft\\Java Runtime Environment\\" . $javastring; + my $name = "JavaHome"; + my $type = 2; + my $property = "JAVAPATH"; + + my $oneline = $signature . "\t" . $rootvalue . "\t" . $key . "\t" . $name . "\t" . $type . "\n"; + push(@{$reglocatfile}, $oneline); + + $oneline = $property . "\t" . $signature . "\n"; + push(@{$appsearchfile}, $oneline); + + # Saving the files + + installer::files::save_file($reglocatfilename ,$reglocatfile); + my $infoline = "Updated idt file for Java: $reglocatfilename\n"; + push(@installer::globals::logfileinfo, $infoline); + + installer::files::save_file($appsearchfilename ,$appsearchfile); + $infoline = "Updated idt file for Java: $appsearchfilename\n"; + push(@installer::globals::logfileinfo, $infoline); + + installer::files::save_file($signaturefilename ,$signaturefile); + $infoline = "Updated idt file: $signaturefilename\n"; + push(@installer::globals::logfileinfo, $infoline); + +} + +1; diff --git a/solenv/bin/modules/installer/windows/language.pm b/solenv/bin/modules/installer/windows/language.pm new file mode 100644 index 000000000000..11a31c6c5f60 --- /dev/null +++ b/solenv/bin/modules/installer/windows/language.pm @@ -0,0 +1,78 @@ +#************************************************************************* +# +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# Copyright 2008 by Sun Microsystems, Inc. +# +# OpenOffice.org - a multi-platform office productivity suite +# +# $RCSfile: language.pm,v $ +# +# $Revision: 1.10 $ +# +# This file is part of OpenOffice.org. +# +# OpenOffice.org is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License version 3 +# only, as published by the Free Software Foundation. +# +# OpenOffice.org is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License version 3 for more details +# (a copy is included in the LICENSE file that accompanied this code). +# +# You should have received a copy of the GNU Lesser General Public License +# version 3 along with OpenOffice.org. If not, see +# <http://www.openoffice.org/license.html> +# for a copy of the LGPLv3 License. +# +#************************************************************************* + +package installer::windows::language; + +use installer::exiter; + +#################################################### +# Determining the Windows language (LCID) +# English: 1033 +#################################################### + +sub get_windows_language +{ + my ($language) = @_; + + my $windowslanguage = ""; + + if ( $installer::globals::msilanguage->{$language} ) { $windowslanguage = $installer::globals::msilanguage->{$language}; } + + if ( $windowslanguage eq "" ) { installer::exiter::exit_program("ERROR: Unknown language $language in function get_windows_language", "get_windows_language"); } + + return $windowslanguage; +} + +#################################################### +# Determining the Windows language ANSI-Codepage +# English: 1252 +#################################################### + +sub get_windows_encoding +{ + my ($language) = @_; + + my $windowsencoding = ""; + + if ( $installer::globals::msiencoding->{$language} ) { $windowsencoding = $installer::globals::msiencoding->{$language}; } + + # if ( $windowsencoding eq "" ) { installer::exiter::exit_program("ERROR: Unknown language $language in function get_windows_encoding", "get_windows_encoding"); } + if ( $windowsencoding eq "" ) { $windowsencoding = "0"; } # setting value, if the language is not listed in the encodinglist + + if ( $windowsencoding eq "0" ) { $windowsencoding = "65001"; } # languages with "0" have to be available in UTF-8 (65001) + + # Asian multilingual installation sets need a code neutral Windows Installer database -> $windowsencoding = 0 + if (( $language eq "en-US" ) && (( $installer::globals::product =~ /suitemulti/i ) || ( $installer::globals::product =~ /officemulti/i ) || ( $installer::globals::product =~ /c05office/i ) || ( $installer::globals::added_english ))) { $windowsencoding = "0"; } + + return $windowsencoding; +} + +1;
\ No newline at end of file diff --git a/solenv/bin/modules/installer/windows/media.pm b/solenv/bin/modules/installer/windows/media.pm new file mode 100644 index 000000000000..c8b1e03ab5e3 --- /dev/null +++ b/solenv/bin/modules/installer/windows/media.pm @@ -0,0 +1,462 @@ +#************************************************************************* +# +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# Copyright 2008 by Sun Microsystems, Inc. +# +# OpenOffice.org - a multi-platform office productivity suite +# +# $RCSfile: media.pm,v $ +# +# $Revision: 1.12 $ +# +# This file is part of OpenOffice.org. +# +# OpenOffice.org is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License version 3 +# only, as published by the Free Software Foundation. +# +# OpenOffice.org is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License version 3 for more details +# (a copy is included in the LICENSE file that accompanied this code). +# +# You should have received a copy of the GNU Lesser General Public License +# version 3 along with OpenOffice.org. If not, see +# <http://www.openoffice.org/license.html> +# for a copy of the LGPLv3 License. +# +#************************************************************************* + +package installer::windows::media; + +use installer::exiter; +use installer::files; +use installer::globals; +use installer::windows::idtglobal; + +############################################################## +# Returning the diskid for the media table. +############################################################## + +sub get_media_diskid +{ + my ($id) = @_; + + return $id; +} + +############################################################## +# Returning the lastsequence for the media table. +############################################################## + +sub get_media_lastsequence +{ + my ($fileref) = @_; + + return $fileref->{'sequencenumber'}; +} + +############################################################## +# Returning the diskprompt for the media table. +############################################################## + +sub get_media_diskprompt +{ + return 1; +} + +############################################################## +# Returning the cabinet file name for the media table. +############################################################## + +sub get_media_cabinet +{ + my ($id) = @_; + + my $number = 1000 + $id; + my $filename = "f_" . $number . ".cab"; + + if ( $installer::globals::include_cab_in_msi ) { $filename = "\#" . $filename; } + + return $filename; +} + +############################################################## +# Returning the volumelabel for the media table. +############################################################## + +sub get_media_volumelabel +{ + return "DISK1"; +} + +############################################################## +# Returning the source for the media table. +############################################################## + +sub get_media_source +{ + return ""; +} + +############################################################## +# Saving the cabinet file name in the files collector. +# This is useful for making a list to connect the +# source of each file with the destination cabinet file. +############################################################## + +sub set_cabinetfilename_for_component_in_file_collector +{ + my ($cabinetfilename, $filesref, $componentname, $max) = @_; + + for ( my $i = 0; $i <= $max; $i++ ) + { + my $onefile = ${$filesref}[$i]; + my $component = $onefile->{'componentname'}; + + if ( $component eq $componentname ) + { + my $cabinet = ""; + + if ( $onefile->{'cabinet'} ) { $cabinet = $onefile->{'cabinet'}; } + + if ( $cabinet eq "" ) + { + $onefile->{'cabinet'} = $cabinetfilename; + } + } + } +} + +################################################# +# Creating the cab file name dynamically +################################################# + +sub generate_cab_filename_for_some_cabs +{ + my ( $allvariables, $id ) = @_; + + my $name = $allvariables->{'PRODUCTNAME'}; + + $name = lc($name); + $name =~ s/\.//g; + $name =~ s/\s//g; + + # possibility to overwrite the name with variable CABFILENAME + if ( $allvariables->{'CABFILENAME'} ) { $name = $allvariables->{'CABFILENAME'}; } + + $name = $name . $id . ".cab"; + + if ( $installer::globals::include_cab_in_msi ) { $name = "\#" . $name; } + + return $name; +} + +################################################# +# Creating the cab file name for cab files +# defined in packages. +################################################# + +sub get_cabfilename +{ + my ($name) = @_; + + if ( $installer::globals::include_cab_in_msi ) { $name = "\#" . $name; } + + return $name; +} + +################################################# +# Creating the cab file name dynamically +################################################# + +sub generate_cab_filename +{ + my ( $allvariables ) = @_; + + my $name = $allvariables->{'PRODUCTNAME'}; + + $name = lc($name); + $name =~ s/\.//g; + $name =~ s/\s//g; + + # possibility to overwrite the name with variable CABFILENAME + if ( $allvariables->{'CABFILENAME'} ) { $name = $allvariables->{'CABFILENAME'}; } + + $name = $name . ".cab"; + + if ( $installer::globals::include_cab_in_msi ) { $name = "\#" . $name; } + + return $name; +} + +sub get_maximum_filenumber +{ + my ($allfiles, $maxcabfilenumber) = @_; + + my $maxfile = 0; + + while ( ! ( $allfiles%$maxcabfilenumber == 0 )) + { + $allfiles++; + } + + $maxfile = $allfiles / $maxcabfilenumber; + + $maxfile++; # for securitry + + return $maxfile; +} + +################################################################################# +# Setting the last sequence for the cabinet files +################################################################################# + +sub get_last_sequence +{ + my ( $cabfilename, $alludpatelastsequences ) = @_; + + my $sequence = 0; + + if (( $installer::globals::updatedatabase ) && ( exists($alludpatelastsequences->{$cabfilename}) )) + { + $sequence = $alludpatelastsequences->{$cabfilename}; + } + else + { + $sequence = $installer::globals::lastsequence{$cabfilename}; + } + + return $sequence; +} + +################################################################################# +# Creating the file Media.idt dynamically +# Content: +# DiskId LastSequence DiskPrompt Cabinet VolumeLabel Source +# Idea: Every component is packed into each own cab file +################################################################################# + +sub create_media_table +{ + my ($filesref, $basedir, $allvariables, $alludpatelastsequences, $allupdatediskids) = @_; + + my @mediatable = (); + + my $diskid = 0; + + installer::windows::idtglobal::write_idt_header(\@mediatable, "media"); + + if ( $allvariables->{'INCLUDE_CAB_IN_MSI'} ) { $installer::globals::include_cab_in_msi = 1; } + + if ( $installer::globals::use_packages_for_cabs ) + { + my $cabfile; + foreach $cabfile ( sort keys %installer::globals::lastsequence ) + { + my %media = (); + $diskid++; + + $media{'DiskId'} = get_media_diskid($diskid); + $media{'LastSequence'} = get_last_sequence($cabfile, $alludpatelastsequences); + $media{'DiskPrompt'} = get_media_diskprompt(); + $media{'Cabinet'} = get_cabfilename($cabfile); + $media{'VolumeLabel'} = get_media_volumelabel(); + $media{'Source'} = get_media_source(); + + my $oneline = $media{'DiskId'} . "\t" . $media{'LastSequence'} . "\t" . $media{'DiskPrompt'} . "\t" + . $media{'Cabinet'} . "\t" . $media{'VolumeLabel'} . "\t" . $media{'Source'} . "\n"; + + push(@mediatable, $oneline); + + # Comparing the disk id with the disk id from update database. Both have to be identical. New files have to be added + # to the new pff cabinet file. And existing cab files must not be removed. + if ( $installer::globals::updatedatabase ) + { + # Comparing lines in new media table with line from media table in udpate database. + if ( exists($allupdatediskids->{$media{'Cabinet'}}) ) + { + if ( $media{'DiskId'} != $allupdatediskids->{$media{'Cabinet'}} ) + { + installer::exiter::exit_program("ERROR: Different DiskIDs for cab file \"$media{'Cabinet'}\".\nCurrent installation set: \"$media{'DiskId'}\", but update database used \"$allupdatediskids->{$media{'Cabinet'}}\".\nWere cabinet files removed or added?", "create_media_table"); + } + } + else + { + my $localinfoline = "Warning: Could not find cabinet file \"$media{'Cabinet'}}\" in update database. This seems to be an new cabinet file!?\n"; + push(@installer::globals::logfileinfo, $localinfoline); + } + } + } + + # one new cabinet file for all files added after the final release + if (( $installer::globals::updatedatabase ) && ( $installer::globals::pfffileexists )) + { + my %media = (); + $diskid++; + + $media{'DiskId'} = get_media_diskid($diskid) + $installer::globals::mergemodulenumber; # Adding mergemodulenumber, because this files are included later + $media{'LastSequence'} = $installer::globals::updatesequencecounter; + $media{'DiskPrompt'} = get_media_diskprompt(); + $media{'Cabinet'} = get_cabfilename($installer::globals::pffcabfilename); + $media{'VolumeLabel'} = get_media_volumelabel(); + $media{'Source'} = get_media_source(); + + my $oneline = $media{'DiskId'} . "\t" . $media{'LastSequence'} . "\t" . $media{'DiskPrompt'} . "\t" + . $media{'Cabinet'} . "\t" . $media{'VolumeLabel'} . "\t" . $media{'Source'} . "\n"; + + push(@mediatable, $oneline); + } + + } + elsif ( $installer::globals::cab_file_per_component ) + { + for ( my $i = 0; $i <= $#{$filesref}; $i++ ) + { + my $onefile = ${$filesref}[$i]; + my $nextfile = ${$filesref}[$i+1]; + + my $filecomponent = ""; + my $nextcomponent = ""; + + if ( $onefile->{'componentname'} ) { $filecomponent = $onefile->{'componentname'}; } + if ( $nextfile->{'componentname'} ) { $nextcomponent = $nextfile->{'componentname'}; } + + if ( $filecomponent eq $nextcomponent ) + { + next; # nothing to do, this is not the last file of a component + } + + my %media = (); + $diskid++; + + $media{'DiskId'} = get_media_diskid($diskid); + $media{'LastSequence'} = get_media_lastsequence($onefile); + $media{'DiskPrompt'} = get_media_diskprompt(); + $media{'Cabinet'} = get_media_cabinet($diskid); + $media{'VolumeLabel'} = get_media_volumelabel(); + $media{'Source'} = get_media_source(); + + my $oneline = $media{'DiskId'} . "\t" . $media{'LastSequence'} . "\t" . $media{'DiskPrompt'} . "\t" + . $media{'Cabinet'} . "\t" . $media{'VolumeLabel'} . "\t" . $media{'Source'} . "\n"; + + push(@mediatable, $oneline); + + $media{'Cabinet'} =~ s/^\s*\#//; # removing leading hash + set_cabinetfilename_for_component_in_file_collector($media{'Cabinet'}, $filesref, $filecomponent, $i); + } + } + elsif ( $installer::globals::fix_number_of_cab_files ) + { + # number of cabfiles + my $maxcabfilenumber = $installer::globals::number_of_cabfiles; + if ( $allvariables->{'CABFILENUMBER'} ) { $maxcabfilenumber = $allvariables->{'CABFILENUMBER'}; } + my $allfiles = $#{$filesref} + 1; + my $maxfilenumber = get_maximum_filenumber($allfiles, $maxcabfilenumber); + # my $maxfilenumber = 1000; # maximum 1000 files in each cabinet file + my $cabfilenumber = 0; + my $cabfull = 0; + my $counter = 0; + + # Sorting of files collector files required ! + # Attention: The order in the cab file is not guaranteed (especially in udpate process) + + for ( my $i = 0; $i <= $#{$filesref}; $i++ ) + { + if (( $counter >= $maxfilenumber ) || ( $i == $#{$filesref} )) { $cabfull = 1; } + + $counter++; # counting the files in the cab file + + my $onefile = ${$filesref}[$i]; + my $nextfile = ${$filesref}[$i+1]; + + my $filecomponent = ""; + my $nextcomponent = ""; + + if ( $onefile->{'componentname'} ) { $filecomponent = $onefile->{'componentname'}; } + if ( $nextfile->{'componentname'} ) { $nextcomponent = $nextfile->{'componentname'}; } + + if ( $filecomponent eq $nextcomponent ) # all files of one component have to be in one cab file + { + next; # nothing to do, this is not the last file of a component + } + + if ( $cabfull ) + { + my %media = (); + $cabfilenumber++; + + $media{'DiskId'} = get_media_diskid($cabfilenumber); + # $media{'LastSequence'} = get_media_lastsequence($onefile); + $media{'LastSequence'} = $i + 1; # This should be correct, also for unsorted files collectors + $media{'DiskPrompt'} = get_media_diskprompt(); + $media{'Cabinet'} = generate_cab_filename_for_some_cabs($allvariables, $cabfilenumber); + $media{'VolumeLabel'} = get_media_volumelabel(); + $media{'Source'} = get_media_source(); + + my $oneline = $media{'DiskId'} . "\t" . $media{'LastSequence'} . "\t" . $media{'DiskPrompt'} . "\t" + . $media{'Cabinet'} . "\t" . $media{'VolumeLabel'} . "\t" . $media{'Source'} . "\n"; + + push(@mediatable, $oneline); + + # Saving the cabinet file name in the file collector + + $media{'Cabinet'} =~ s/^\s*\#//; # removing leading hash + + for ( my $j = 0; $j <= $i; $j++ ) + { + my $onefile = ${$filesref}[$j]; + if ( ! $onefile->{'cabinet'} ) { $onefile->{'cabinet'} = $media{'Cabinet'}; } + } + + $cabfull = 0; + $counter = 0; + } + } + } + elsif ( $installer::globals::one_cab_file ) + { + my %media = (); + $diskid++; + + my $maximumfile = $#{$filesref}; + + $media{'DiskId'} = get_media_diskid($diskid); + # $media{'LastSequence'} = ${$filesref}[$maximumfile]->{'sequencenumber'}; # sequence number of the last file + $media{'LastSequence'} = $maximumfile + 1; # This works also for unsorted file collector + $media{'DiskPrompt'} = get_media_diskprompt(); + $media{'Cabinet'} = generate_cab_filename($allvariables); + $media{'VolumeLabel'} = get_media_volumelabel(); + $media{'Source'} = get_media_source(); + + my $oneline = $media{'DiskId'} . "\t" . $media{'LastSequence'} . "\t" . $media{'DiskPrompt'} . "\t" + . $media{'Cabinet'} . "\t" . $media{'VolumeLabel'} . "\t" . $media{'Source'} . "\n"; + + push(@mediatable, $oneline); + + # Saving the cabinet file name in the file collector + + $media{'Cabinet'} =~ s/^\s*\#//; # removing leading hash + + for ( my $i = 0; $i <= $#{$filesref}; $i++ ) + { + my $onefile = ${$filesref}[$i]; + $onefile->{'cabinet'} = $media{'Cabinet'}; + } + } + else + { + installer::exiter::exit_program("ERROR: No cab file specification in globals.pm !", "create_media_table"); + } + + # Saving the file + + my $mediatablename = $basedir . $installer::globals::separator . "Media.idt"; + installer::files::save_file($mediatablename ,\@mediatable); + my $infoline = "Created idt file: $mediatablename\n"; + push(@installer::globals::logfileinfo, $infoline); +} + +1; diff --git a/solenv/bin/modules/installer/windows/mergemodule.pm b/solenv/bin/modules/installer/windows/mergemodule.pm new file mode 100755 index 000000000000..7dfd8ff49c76 --- /dev/null +++ b/solenv/bin/modules/installer/windows/mergemodule.pm @@ -0,0 +1,1656 @@ +#************************************************************************* +# +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# Copyright 2008 by Sun Microsystems, Inc. +# +# OpenOffice.org - a multi-platform office productivity suite +# +# $RCSfile: mergemodule.pm,v $ +# +# $Revision: 1.8 $ +# +# This file is part of OpenOffice.org. +# +# OpenOffice.org is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License version 3 +# only, as published by the Free Software Foundation. +# +# OpenOffice.org is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License version 3 for more details +# (a copy is included in the LICENSE file that accompanied this code). +# +# You should have received a copy of the GNU Lesser General Public License +# version 3 along with OpenOffice.org. If not, see +# <http://www.openoffice.org/license.html> +# for a copy of the LGPLv3 License. +# +#************************************************************************* + +package installer::windows::mergemodule; + +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; + +################################################################# +# Merging the Windows MergeModules into the msi database. +################################################################# + +sub merge_mergemodules_into_msi_database +{ + my ($mergemodules, $filesref, $msifilename, $languagestringref, $language, $languagefile, $allvariables, $includepatharrayref, $allupdatesequences, $allupdatelastsequences, $allupdatediskids) = @_; + + my $domerge = 0; + if (( $#{$mergemodules} > -1 ) && ( ! $installer::globals::patch ) && ( ! $installer::globals::languagepack )) { $domerge = 1; } + + if ( $domerge ) + { + installer::logger::include_header_into_logfile("Merging merge modules into msi database"); + installer::logger::print_message( "... merging msm files into msi database ... \n" ); + installer::logger::include_timestamp_into_logfile("\nPerformance Info: MergeModule into msi database, start"); + + my $msidb = "msidb.exe"; # Has to be in the path + my $cabinetfile = "MergeModule.CABinet"; # the name of each cabinet file in a merge file + my $infoline = ""; + my $systemcall = ""; + my $returnvalue = ""; + + # 1. Analyzing the MergeModule (has only to be done once) + # a. -> Extracting cabinet file: msidb.exe -d <msmfile> -x MergeModule.CABinet + # b. -> Number of files in cabinet file: msidb.exe -d <msmfile> -f <directory> -e File + # c. -> List of components: msidb.exe -d <msmfile> -f <directory> -e Component + + if ( ! $installer::globals::mergemodules_analyzed ) + { + installer::logger::include_timestamp_into_logfile("\nPerformance Info: Analyzing MergeModules, start"); + $infoline = "Analyzing all Merge Modules\n\n"; + push( @installer::globals::logfileinfo, $infoline); + + %installer::globals::mergemodules = (); + + my $mergemoduledir = installer::systemactions::create_directories("mergefiles", $languagestringref); + # push(@installer::globals::removedirs, $mergemoduledir); + + my $mergemodule; + foreach $mergemodule ( @{$mergemodules} ) + { + my $filename = $mergemodule->{'Name'}; + my $mergefile = installer::scriptitems::get_sourcepath_from_filename_and_includepath(\$filename, $includepatharrayref, 1); + + if ( ! -f $$mergefile ) { installer::exiter::exit_program("ERROR: msm file not found: $filename !", "merge_mergemodules_into_msi_database"); } + my $completesource = $$mergefile; + + my $mergegid = $mergemodule->{'gid'}; + my $workdir = $mergemoduledir . $installer::globals::separator . $mergegid; + if ( ! -d $workdir ) { installer::systemactions::create_directory($workdir); } + + $infoline = "Analyzing Merge Module: $filename\n"; + push( @installer::globals::logfileinfo, $infoline); + + # copy msm file into working directory + my $completedest = $workdir . $installer::globals::separator . $filename; + installer::systemactions::copy_one_file($completesource, $completedest); + if ( ! -f $completedest ) { installer::exiter::exit_program("ERROR: msm file not found: $completedest !", "merge_mergemodules_into_msi_database"); } + + # changing directory + my $from = cwd(); + my $to = $workdir; + chdir($to); + + # remove an existing cabinet file + if ( -f $cabinetfile ) { unlink($cabinetfile); } + + # exclude cabinet file + $systemcall = $msidb . " -d " . $filename . " -x " . $cabinetfile; + $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); + installer::exiter::exit_program("ERROR: Could not extract cabinet file from merge file: $completedest !", "merge_mergemodules_into_msi_database"); + } + else + { + $infoline = "Success: Executed $systemcall successfully!\n"; + push( @installer::globals::logfileinfo, $infoline); + } + + # exclude tables from mergefile + # Attention: All listed tables have to exist in the database. If they not exist, an error window pops up + # and the return value of msidb.exe is not zero. The error window makes it impossible to check the existence + # of a table with the help of the return value. + # Solution: Export of all tables by using "*" . Some tables must exist (File Component Directory), other + # tables do not need to exist (MsiAssembly). + + if ( $^O =~ /cygwin/i ) { + # msidb.exe really wants backslashes. (And double escaping because system() expands the string.) + my $localworkdir = $workdir; + $localworkdir =~ s/\//\\\\/g; + $systemcall = $msidb . " -d " . $filename . " -f " . $localworkdir . " -e \\\*"; + } + else + { + # $systemcall = $msidb . " -d " . $filename . " -f " . $workdir . " -e File Component MsiAssembly Directory"; + $systemcall = $msidb . " -d " . $filename . " -f " . $workdir . " -e \*"; + } + + $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); + installer::exiter::exit_program("ERROR: Could not exclude tables from merge file: $completedest !", "merge_mergemodules_into_msi_database"); + } + else + { + $infoline = "Success: Executed $systemcall successfully!\n"; + push( @installer::globals::logfileinfo, $infoline); + } + + # Determining files + my $idtfilename = "File.idt"; # must exist + if ( ! -f $idtfilename ) { installer::exiter::exit_program("ERROR: File \"$idtfilename\" not found in directory \"$workdir\" !", "merge_mergemodules_into_msi_database"); } + my $filecontent = installer::files::read_file($idtfilename); + my @file_idt_content = (); + my $filecounter = 0; + my %mergefilesequence = (); + for ( my $i = 0; $i <= $#{$filecontent}; $i++ ) + { + if ( $i <= 2 ) { next; } # ignoring first three lines + if ( ${$filecontent}[$i] =~ /^\s*$/ ) { next; } # ignoring empty lines + $filecounter++; + push(@file_idt_content, ${$filecontent}[$i]); + if ( ${$filecontent}[$i] =~ /^\s*(.+?)\t(.+?)\t(.+?)\t(.+?)\t(.*?)\t(.*?)\t(.*?)\t(\d+?)\s*$/ ) + { + my $filename = $1; + my $filesequence = $8; + $mergefilesequence{$filename} = $filesequence; + } + else + { + my $linecount = $i + 1; + installer::exiter::exit_program("ERROR: Unknown line format in table \"$idtfilename\" (line $linecount) !", "merge_mergemodules_into_msi_database"); + } + } + + # Determining components + $idtfilename = "Component.idt"; # must exist + if ( ! -f $idtfilename ) { installer::exiter::exit_program("ERROR: File \"$idtfilename\" not found in directory \"$workdir\" !", "merge_mergemodules_into_msi_database"); } + $filecontent = installer::files::read_file($idtfilename); + my %componentnames = (); + for ( my $i = 0; $i <= $#{$filecontent}; $i++ ) + { + if ( $i <= 2 ) { next; } # ignoring first three lines + if ( ${$filecontent}[$i] =~ /^\s*$/ ) { next; } # ignoring empty lines + if ( ${$filecontent}[$i] =~ /^\s*(\S+)\s+/ ) { $componentnames{$1} = 1; } + } + + # Determining directories + $idtfilename = "Directory.idt"; # must exist + if ( ! -f $idtfilename ) { installer::exiter::exit_program("ERROR: File \"$idtfilename\" not found in directory \"$workdir\" !", "merge_mergemodules_into_msi_database"); } + $filecontent = installer::files::read_file($idtfilename); + my %mergedirectories = (); + for ( my $i = 0; $i <= $#{$filecontent}; $i++ ) + { + if ( $i <= 2 ) { next; } # ignoring first three lines + if ( ${$filecontent}[$i] =~ /^\s*$/ ) { next; } # ignoring empty lines + if ( ${$filecontent}[$i] =~ /^\s*(\S+)\s+/ ) { $mergedirectories{$1} = 1; } + } + + # Determining assemblies + $idtfilename = "MsiAssembly.idt"; # does not need to exist + my $hasmsiassemblies = 0; + my %mergeassemblies = (); + if ( -f $idtfilename ) + { + $filecontent = installer::files::read_file($idtfilename); + $hasmsiassemblies = 1; + for ( my $i = 0; $i <= $#{$filecontent}; $i++ ) + { + if ( $i <= 2 ) { next; } # ignoring first three lines + if ( ${$filecontent}[$i] =~ /^\s*$/ ) { next; } # ignoring empty lines + if ( ${$filecontent}[$i] =~ /^\s*(\S+)\s+/ ) { $mergeassemblies{$1} = 1; } + } + } + + # It is possible, that other tables have to be checked here. This happens, if tables in the + # merge module have to know the "Feature" or the "Directory", under which the content of the + # msm file is integrated into the msi database. + + # Determining name of cabinet file in installation set + my $cabfilename = $mergemodule->{'Cabfilename'}; + installer::packagelist::resolve_packagevariables(\$cabfilename, $allvariables, 0); + + # Analyzing styles + # Flag REMOVE_FILE_TABLE is required for msvc9 Merge-Module, because otherwise msidb.exe + # fails during integration of msm file into msi database. + + my $styles = ""; + my $removefiletable = 0; + if ( $mergemodule->{'Styles'} ) { $styles = $mergemodule->{'Styles'}; } + if ( $styles =~ /\bREMOVE_FILE_TABLE\b/ ) { $removefiletable = 1; } + + if ( $removefiletable ) + { + my $removeworkdir = $workdir . $installer::globals::separator . "remove_file_idt"; + if ( ! -d $removeworkdir ) { installer::systemactions::create_directory($removeworkdir); } + my $completeremovedest = $removeworkdir . $installer::globals::separator . $filename; + installer::systemactions::copy_one_file($completedest, $completeremovedest); + if ( ! -f $completeremovedest ) { installer::exiter::exit_program("ERROR: msm file not found: $completeremovedest !", "merge_mergemodules_into_msi_database"); } + + # Unpacking msm file + if ( $^O =~ /cygwin/i ) { + # msidb.exe really wants backslashes. (And double escaping because system() expands the string.) + my $localcompleteremovedest = $completeremovedest; + my $localremoveworkdir = $removeworkdir; + $localcompleteremovedest =~ s/\//\\\\/g; + $localremoveworkdir =~ s/\//\\\\/g; + $systemcall = $msidb . " -d " . $localcompleteremovedest . " -f " . $localremoveworkdir . " -e \\\*"; + } + else + { + $systemcall = $msidb . " -d " . $completeremovedest . " -f " . $removeworkdir . " -e \*"; + } + + $returnvalue = system($systemcall); + + my $idtfilename = $removeworkdir . $installer::globals::separator . "File.idt"; + if ( -f $idtfilename ) { unlink $idtfilename; } + unlink $completeremovedest; + + # Packing msm file without "File.idt" + if ( $^O =~ /cygwin/i ) { + # msidb.exe really wants backslashes. (And double escaping because system() expands the string.) + my $localcompleteremovedest = $completeremovedest; + my $localremoveworkdir = $removeworkdir; + $localcompleteremovedest =~ s/\//\\\\/g; + $localremoveworkdir =~ s/\//\\\\/g; + $systemcall = $msidb . " -c -d " . $localcompleteremovedest . " -f " . $localremoveworkdir . " -i \\\*"; + } + else + { + $systemcall = $msidb . " -c -d " . $completeremovedest . " -f " . $removeworkdir . " -i \*"; + } + $returnvalue = system($systemcall); + + # Using this msm file for merging + if ( -f $completeremovedest ) { $completedest = $completeremovedest; } + else { installer::exiter::exit_program("ERROR: Could not find msm file without File.idt: $completeremovedest !", "merge_mergemodules_into_msi_database"); } + } + + # Saving MergeModule info + + my %onemergemodulehash = (); + $onemergemodulehash{'mergefilepath'} = $completedest; + $onemergemodulehash{'workdir'} = $workdir; + $onemergemodulehash{'cabinetfile'} = $workdir . $installer::globals::separator . $cabinetfile; + $onemergemodulehash{'filenumber'} = $filecounter; + $onemergemodulehash{'componentnames'} = \%componentnames; + $onemergemodulehash{'cabfilename'} = $cabfilename; + $onemergemodulehash{'feature'} = $mergemodule->{'Feature'}; + $onemergemodulehash{'rootdir'} = $mergemodule->{'RootDir'}; + $onemergemodulehash{'name'} = $mergemodule->{'Name'}; + $onemergemodulehash{'mergefilesequence'} = \%mergefilesequence; + $onemergemodulehash{'mergeassemblies'} = \%mergeassemblies; + $onemergemodulehash{'mergedirectories'} = \%mergedirectories; + $onemergemodulehash{'hasmsiassemblies'} = $hasmsiassemblies; + $onemergemodulehash{'removefiletable'} = $removefiletable; + $onemergemodulehash{'fileidtcontent'} = \@file_idt_content; + + $installer::globals::mergemodules{$mergegid} = \%onemergemodulehash; + + # Collecting all cab files, to copy them into installation set + $installer::globals::copy_msm_files{$cabfilename} = $onemergemodulehash{'cabinetfile'}; + + chdir($from); + } + + $infoline = "All Merge Modules successfully analyzed\n"; + push( @installer::globals::logfileinfo, $infoline); + + $installer::globals::mergemodules_analyzed = 1; + installer::logger::include_timestamp_into_logfile("\nPerformance Info: Analyzing MergeModules, stop"); + + $infoline = "\n"; + push( @installer::globals::logfileinfo, $infoline); + } + + # 2. Change msi database (has to be done for every msi database -> for every language) + # a. Merge msm file into msi database: msidb.exe -d <msifile> -m <mergefile> + # b. Extracting tables from msi database: msidb.exe -d <msifile> -f <directory> -e File Media, ... + # c. Changing content of msi database in tables: File, Media, Directory, FeatureComponent + # d. Including tables into msi database: msidb.exe -d <msifile> -f <directory> -i File Media, ... + # e. Copying cabinet file into installation set (later) + + my $counter = 0; + my $mergemodulegid; + foreach $mergemodulegid (keys %installer::globals::mergemodules) + { + my $mergemodulehash = $installer::globals::mergemodules{$mergemodulegid}; + $counter++; + + installer::logger::include_header_into_logfile("Merging Module: $mergemodulehash->{'name'}"); + installer::logger::print_message( "\t... $mergemodulehash->{'name'} ... \n" ); + + $msifilename = installer::converter::make_path_conform($msifilename); + my $workdir = $msifilename; + installer::pathanalyzer::get_path_from_fullqualifiedname(\$workdir); + + # changing directory + my $from = cwd(); + my $to = $workdir; + chdir($to); + + # Saving original msi database + installer::systemactions::copy_one_file($msifilename, "$msifilename\.$counter"); + + # Merging msm file, this is the "real" merge command + + installer::logger::include_timestamp_into_logfile("\nPerformance Info: Before merging database"); + + if ( $^O =~ /cygwin/i ) { + # msidb.exe really wants backslashes. (And double escaping because system() expands the string.) + my $localmergemodulepath = $mergemodulehash->{'mergefilepath'}; + my $localmsifilename = $msifilename; + $localmergemodulepath =~ s/\//\\\\/g; + $localmsifilename =~ s/\//\\\\/g; + $systemcall = $msidb . " -d " . $localmsifilename . " -m " . $localmergemodulepath; + } + else + { + $systemcall = $msidb . " -d " . $msifilename . " -m " . $mergemodulehash->{'mergefilepath'}; + } + $returnvalue = system($systemcall); + + $infoline = "Systemcall: $systemcall\n"; + push( @installer::globals::logfileinfo, $infoline); + + if ($returnvalue) + { + $infoline = "ERROR: Could not execute $systemcall . Returnvalue: $returnvalue!\n"; + push( @installer::globals::logfileinfo, $infoline); + installer::exiter::exit_program("ERROR: Could not merge msm file into database: $mergemodulehash->{'mergefilepath'} !", "merge_mergemodules_into_msi_database"); + } + else + { + $infoline = "Success: Executed $systemcall successfully!\n"; + push( @installer::globals::logfileinfo, $infoline); + } + + installer::logger::include_timestamp_into_logfile("\nPerformance Info: After merging database"); + + # Saving original idt files + if ( -f "File.idt" ) { installer::systemactions::rename_one_file("File.idt", "File.idt.$counter"); } + if ( -f "Media.idt" ) { installer::systemactions::rename_one_file("Media.idt", "Media.idt.$counter"); } + if ( -f "Directory.idt" ) { installer::systemactions::rename_one_file("Directory.idt", "Directory.idt.$counter"); } + if ( -f "Director.idt" ) { installer::systemactions::rename_one_file("Director.idt", "Director.idt.$counter"); } + if ( -f "FeatureComponents.idt" ) { installer::systemactions::rename_one_file("FeatureComponents.idt", "FeatureComponents.idt.$counter"); } + if ( -f "FeatureC.idt" ) { installer::systemactions::rename_one_file("FeatureC.idt", "FeatureC.idt.$counter"); } + if ( -f "MsiAssembly.idt" ) { installer::systemactions::rename_one_file("MsiAssembly.idt", "MsiAssembly.idt.$counter"); } + if ( -f "MsiAssem.idt" ) { installer::systemactions::rename_one_file("MsiAssem.idt", "MsiAssem.idt.$counter"); } + + # Extracting tables + + installer::logger::include_timestamp_into_logfile("\nPerformance Info: Before extracting tables"); + + my $workingtables = "File Media Directory FeatureComponents"; # required tables + # Optional tables can be added now + if ( $mergemodulehash->{'hasmsiassemblies'} ) { $workingtables = $workingtables . " MsiAssembly"; } + + # Table "Feature" has to be exported, but it is not necessary to import it. + if ( $^O =~ /cygwin/i ) { + # msidb.exe really wants backslashes. (And double escaping because system() expands the string.) + my $localmsifilename = $msifilename; + my $localworkdir = $workdir; + $localmsifilename =~ s/\//\\\\/g; + $localworkdir =~ s/\//\\\\/g; + $systemcall = $msidb . " -d " . $localmsifilename . " -f " . $localworkdir . " -e " . "Feature " . $workingtables; + } + else + { + $systemcall = $msidb . " -d " . $msifilename . " -f " . $workdir . " -e " . "Feature " . $workingtables; + } + $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); + installer::exiter::exit_program("ERROR: Could not exclude tables from msi database: $msifilename !", "merge_mergemodules_into_msi_database"); + } + else + { + $infoline = "Success: Executed $systemcall successfully!\n"; + push( @installer::globals::logfileinfo, $infoline); + } + + installer::logger::include_timestamp_into_logfile("\nPerformance Info: After extracting tables"); + + # Using 8+3 table names, that are used, when tables are integrated into database. The export of tables + # creates idt-files, that have long names. + + if ( -f "Directory.idt" ) { installer::systemactions::rename_one_file("Directory.idt", "Director.idt"); } + if ( -f "FeatureComponents.idt" ) { installer::systemactions::rename_one_file("FeatureComponents.idt", "FeatureC.idt"); } + if ( -f "MsiAssembly.idt" ) { installer::systemactions::rename_one_file("MsiAssembly.idt", "MsiAssem.idt"); } + + # Changing content of tables: File, Media, Directory, FeatureComponent, MsiAssembly + installer::logger::include_timestamp_into_logfile("\nPerformance Info: Changing Media table"); + change_media_table($mergemodulehash, $workdir, $mergemodulegid, $allupdatelastsequences, $allupdatediskids); + installer::logger::include_timestamp_into_logfile("\nPerformance Info: Changing File table"); + $filesref = change_file_table($mergemodulehash, $workdir, $allupdatesequences, $includepatharrayref, $filesref, $mergemodulegid); + installer::logger::include_timestamp_into_logfile("\nPerformance Info: Changing FeatureComponent table"); + change_featurecomponent_table($mergemodulehash, $workdir); + installer::logger::include_timestamp_into_logfile("\nPerformance Info: Changing Directory table"); + change_directory_table($mergemodulehash, $workdir); + if ( $mergemodulehash->{'hasmsiassemblies'} ) + { + installer::logger::include_timestamp_into_logfile("\nPerformance Info: Changing MsiAssembly table"); + change_msiassembly_table($mergemodulehash, $workdir); + } + + # msidb.exe does not merge InstallExecuteSequence, AdminExecuteSequence and AdvtExecuteSequence. Instead it creates + # new tables ModuleInstallExecuteSequence, ModuleAdminExecuteSequence and ModuleAdvtExecuteSequence that need to be + # merged into the three ExecuteSequences with the following process (also into InstallUISequence.idt). + + # Saving original idt files + if ( -f "InstallE.idt" ) { installer::systemactions::rename_one_file("InstallE.idt", "InstallE.idt.$counter"); } + if ( -f "InstallU.idt" ) { installer::systemactions::rename_one_file("InstallU.idt", "InstallU.idt.$counter"); } + if ( -f "AdminExe.idt" ) { installer::systemactions::rename_one_file("AdminExe.idt", "AdminExe.idt.$counter"); } + if ( -f "AdvtExec.idt" ) { installer::systemactions::rename_one_file("AdvtExec.idt", "AdvtExec.idt.$counter"); } + if ( -f "ModuleInstallExecuteSequence.idt" ) { installer::systemactions::rename_one_file("ModuleInstallExecuteSequence.idt", "ModuleInstallExecuteSequence.idt.$counter"); } + if ( -f "ModuleAdminExecuteSequence.idt" ) { installer::systemactions::rename_one_file("ModuleAdminExecuteSequence.idt", "ModuleAdminExecuteSequence.idt.$counter"); } + if ( -f "ModuleAdvtExecuteSequence.idt" ) { installer::systemactions::rename_one_file("ModuleAdvtExecuteSequence.idt", "ModuleAdvtExecuteSequence.idt.$counter"); } + + # Extracting tables + my $moduleexecutetables = "ModuleInstallExecuteSequence ModuleAdminExecuteSequence ModuleAdvtExecuteSequence"; # new tables + my $executetables = "InstallExecuteSequence InstallUISequence AdminExecuteSequence AdvtExecuteSequence"; # tables to be merged + + + if ( $^O =~ /cygwin/i ) { + # msidb.exe really wants backslashes. (And double escaping because system() expands the string.) + my $localmsifilename = $msifilename; + my $localworkdir = $workdir; + $localmsifilename =~ s/\//\\\\/g; + $localworkdir =~ s/\//\\\\/g; + $systemcall = $msidb . " -d " . $localmsifilename . " -f " . $localworkdir . " -e " . "Feature " . $moduleexecutetables; + } + else + { + $systemcall = $msidb . " -d " . $msifilename . " -f " . $workdir . " -e " . "Feature " . $moduleexecutetables; + } + $returnvalue = system($systemcall); + + if ( $^O =~ /cygwin/i ) { + # msidb.exe really wants backslashes. (And double escaping because system() expands the string.) + my $localmsifilename = $msifilename; + my $localworkdir = $workdir; + $localmsifilename =~ s/\//\\\\/g; + $localworkdir =~ s/\//\\\\/g; + $systemcall = $msidb . " -d " . $localmsifilename . " -f " . $localworkdir . " -e " . "Feature " . $executetables; + } + else + { + $systemcall = $msidb . " -d " . $msifilename . " -f " . $workdir . " -e " . "Feature " . $executetables; + } + $returnvalue = system($systemcall); + + # Using 8+3 table names, that are used, when tables are integrated into database. The export of tables + # creates idt-files, that have long names. + + if ( -f "InstallExecuteSequence.idt" ) { installer::systemactions::rename_one_file("InstallExecuteSequence.idt", "InstallE.idt"); } + if ( -f "InstallUISequence.idt" ) { installer::systemactions::rename_one_file("InstallUISequence.idt", "InstallU.idt"); } + if ( -f "AdminExecuteSequence.idt" ) { installer::systemactions::rename_one_file("AdminExecuteSequence.idt", "AdminExe.idt"); } + if ( -f "AdvtExecuteSequence.idt" ) { installer::systemactions::rename_one_file("AdvtExecuteSequence.idt", "AdvtExec.idt"); } + + # Merging content of tables ModuleInstallExecuteSequence, ModuleAdminExecuteSequence and ModuleAdvtExecuteSequence + # into tables InstallExecuteSequence, AdminExecuteSequence and AdvtExecuteSequence + if ( -f "ModuleInstallExecuteSequence.idt" ) + { + installer::logger::include_timestamp_into_logfile("\nPerformance Info: Changing InstallExecuteSequence table"); + change_executesequence_table($mergemodulehash, $workdir, "InstallE.idt", "ModuleInstallExecuteSequence.idt"); + installer::logger::include_timestamp_into_logfile("\nPerformance Info: Changing InstallUISequence table"); + change_executesequence_table($mergemodulehash, $workdir, "InstallU.idt", "ModuleInstallExecuteSequence.idt"); + } + + if ( -f "ModuleAdminExecuteSequence.idt" ) + { + installer::logger::include_timestamp_into_logfile("\nPerformance Info: Changing AdminExecuteSequence table"); + change_executesequence_table($mergemodulehash, $workdir, "AdminExe.idt", "ModuleAdminExecuteSequence.idt"); + } + + if ( -f "ModuleAdvtExecuteSequence.idt" ) + { + installer::logger::include_timestamp_into_logfile("\nPerformance Info: Changing AdvtExecuteSequence table"); + change_executesequence_table($mergemodulehash, $workdir, "AdvtExec.idt", "ModuleAdvtExecuteSequence.idt"); + } + + installer::logger::include_timestamp_into_logfile("\nPerformance Info: All tables edited"); + + # Including tables into msi database + + installer::logger::include_timestamp_into_logfile("\nPerformance Info: Before including tables"); + + if ( $^O =~ /cygwin/i ) { + # msidb.exe really wants backslashes. (And double escaping because system() expands the string.) + my $localmsifilename = $msifilename; + my $localworkdir = $workdir; + $localmsifilename =~ s/\//\\\\/g; + $localworkdir =~ s/\//\\\\/g; + $systemcall = $msidb . " -d " . $localmsifilename . " -f " . $localworkdir . " -i " . $workingtables. " " . $executetables; + } + else + { + $systemcall = $msidb . " -d " . $msifilename . " -f " . $workdir . " -i " . $workingtables. " " . $executetables; + } + $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); + installer::exiter::exit_program("ERROR: Could not include tables into msi database: $msifilename !", "merge_mergemodules_into_msi_database"); + } + else + { + $infoline = "Success: Executed $systemcall successfully!\n"; + push( @installer::globals::logfileinfo, $infoline); + } + + installer::logger::include_timestamp_into_logfile("\nPerformance Info: After including tables"); + + chdir($from); + } + + if ( ! $installer::globals::mergefiles_added_into_collector ) { $installer::globals::mergefiles_added_into_collector = 1; } # Now all mergemodules are merged for one language. + + installer::logger::include_timestamp_into_logfile("\nPerformance Info: MergeModule into msi database, stop"); + } + + return $filesref; +} + +######################################################################### +# Analyzing the content of the media table. +######################################################################### + +sub analyze_media_file +{ + my ($filecontent, $workdir) = @_; + + my %filehash = (); + my $linecount = 0; + my $counter = 0; + my $filename = "Media.idt"; + + for ( my $i = 0; $i <= $#{$filecontent}; $i++ ) + { + if ( $i <= 2 ) { next; } # ignoring first three lines + if ( ${$filecontent}[$i] =~ /^\s*$/ ) { next; } # ignoring empty lines + if ( ${$filecontent}[$i] =~ /^\s*(.+?)\t(.+?)\t(.+?)\t(.+?)\t(.+?)\t(.*?)\s*$/ ) + { + my %line = (); + # Format: DiskId LastSequence DiskPrompt Cabinet VolumeLabel Source + $line{'DiskId'} = $1; + $line{'LastSequence'} = $2; + $line{'DiskPrompt'} = $3; + $line{'Cabinet'} = $4; + $line{'VolumeLabel'} = $5; + $line{'Source'} = $6; + + $counter++; + $filehash{$counter} = \%line; + } + else + { + $linecount = $i + 1; + installer::exiter::exit_program("ERROR: Unknown line format in table \"$filename\" in \"$workdir\" (line $linecount) !", "analyze_media_file"); + } + } + + return \%filehash; +} + +######################################################################### +# Setting the DiskID for the new cabinet file +######################################################################### + +sub get_diskid +{ + my ($mediafile, $allupdatediskids, $cabfilename) = @_; + + my $diskid = 0; + my $line; + + if (( $installer::globals::updatedatabase ) && ( exists($allupdatediskids->{$cabfilename}) )) + { + $diskid = $allupdatediskids->{$cabfilename}; + } + else + { + foreach $line ( keys %{$mediafile} ) + { + if ( $mediafile->{$line}->{'DiskId'} > $diskid ) { $diskid = $mediafile->{$line}->{'DiskId'}; } + } + + $diskid++; + } + + return $diskid; +} + +######################################################################### +# Setting the global LastSequence variable +######################################################################### + +sub set_current_last_sequence +{ + my ($mediafile) = @_; + + my $lastsequence = 0; + my $line; + foreach $line ( keys %{$mediafile} ) + { + if ( $mediafile->{$line}->{'LastSequence'} > $lastsequence ) { $lastsequence = $mediafile->{$line}->{'LastSequence'}; } + } + + $installer::globals::lastsequence_before_merge = $lastsequence; +} + +######################################################################### +# Setting the LastSequence for the new cabinet file +######################################################################### + +sub get_lastsequence +{ + my ($mergemodulehash, $allupdatelastsequences) = @_; + + my $lastsequence = 0; + + if (( $installer::globals::updatedatabase ) && ( exists($allupdatelastsequences->{$mergemodulehash->{'cabfilename'}}) )) + { + $lastsequence = $allupdatelastsequences->{$mergemodulehash->{'cabfilename'}}; + } + else + { + $lastsequence = $installer::globals::lastsequence_before_merge + $mergemodulehash->{'filenumber'}; + } + + return $lastsequence; +} + +######################################################################### +# Setting the DiskPrompt for the new cabinet file +######################################################################### + +sub get_diskprompt +{ + my ($mediafile) = @_; + + my $diskprompt = ""; + my $line; + foreach $line ( keys %{$mediafile} ) + { + if ( exists($mediafile->{$line}->{'DiskPrompt'}) ) + { + $diskprompt = $mediafile->{$line}->{'DiskPrompt'}; + last; + } + } + + return $diskprompt; +} + +######################################################################### +# Setting the VolumeLabel for the new cabinet file +######################################################################### + +sub get_volumelabel +{ + my ($mediafile) = @_; + + my $volumelabel = ""; + my $line; + foreach $line ( keys %{$mediafile} ) + { + if ( exists($mediafile->{$line}->{'VolumeLabel'}) ) + { + $volumelabel = $mediafile->{$line}->{'VolumeLabel'}; + last; + } + } + + return $volumelabel; +} + +######################################################################### +# Setting the Source for the new cabinet file +######################################################################### + +sub get_source +{ + my ($mediafile) = @_; + + my $source = ""; + my $line; + foreach $line ( keys %{$mediafile} ) + { + if ( exists($mediafile->{$line}->{'Source'}) ) + { + $diskprompt = $mediafile->{$line}->{'Source'}; + last; + } + } + + return $source; +} + +######################################################################### +# For each Merge Module one new line has to be included into the +# media table. +######################################################################### + +sub create_new_media_line +{ + my ($mergemodulehash, $mediafile, $allupdatelastsequences, $allupdatediskids) = @_; + + my $diskid = get_diskid($mediafile, $allupdatediskids, $mergemodulehash->{'cabfilename'}); + my $lastsequence = get_lastsequence($mergemodulehash, $allupdatelastsequences); + my $diskprompt = get_diskprompt($mediafile); + my $cabinet = $mergemodulehash->{'cabfilename'}; + my $volumelabel = get_volumelabel($mediafile); + my $source = get_source($mediafile); + + if ( $installer::globals::include_cab_in_msi ) { $cabinet = "\#" . $cabinet; } + + my $newline = "$diskid\t$lastsequence\t$diskprompt\t$cabinet\t$volumelabel\t$source\n"; + + return $newline; +} + +######################################################################### +# Setting the last diskid in media table. +######################################################################### + +sub get_last_diskid +{ + my ($mediafile) = @_; + + my $lastdiskid = 0; + my $line; + foreach $line ( keys %{$mediafile} ) + { + if ( $mediafile->{$line}->{'DiskId'} > $lastdiskid ) { $lastdiskid = $mediafile->{$line}->{'DiskId'}; } + } + + return $lastdiskid; +} + +######################################################################### +# Setting global variable for last cab file name. +######################################################################### + +sub set_last_cabfile_name +{ + my ($mediafile, $lastdiskid) = @_; + + my $line; + foreach $line ( keys %{$mediafile} ) + { + if ( $mediafile->{$line}->{'DiskId'} == $lastdiskid ) { $installer::globals::lastcabfilename = $mediafile->{$line}->{'Cabinet'}; } + } + my $infoline = "Setting last cabinet file: $installer::globals::lastcabfilename\n"; + push( @installer::globals::logfileinfo, $infoline); +} + +######################################################################### +# In the media table the new cabinet file has to be added or the +# number of the last cabinet file has to be increased. +######################################################################### + +sub change_media_table +{ + my ( $mergemodulehash, $workdir, $mergemodulegid, $allupdatelastsequences, $allupdatediskids ) = @_; + + my $infoline = "Changing content of table \"Media\"\n"; + push( @installer::globals::logfileinfo, $infoline); + + my $filename = "Media.idt"; + if ( ! -f $filename ) { installer::exiter::exit_program("ERROR: Could not find file \"$filename\" in \"$workdir\" !", "change_media_table"); } + + my $filecontent = installer::files::read_file($filename); + my $mediafile = analyze_media_file($filecontent, $workdir); + set_current_last_sequence($mediafile); + + if ( $installer::globals::fix_number_of_cab_files ) + { + # Determining the line with the highest sequencenumber. That file needs to be updated. + my $lastdiskid = get_last_diskid($mediafile); + if ( $installer::globals::lastcabfilename eq "" ) { set_last_cabfile_name($mediafile, $lastdiskid); } + my $newmaxsequencenumber = $installer::globals::lastsequence_before_merge + $mergemodulehash->{'filenumber'}; + + for ( my $i = 0; $i <= $#{$filecontent}; $i++ ) + { + if ( $i <= 2 ) { next; } # ignoring first three lines + if ( ${$filecontent}[$i] =~ /^\s*$/ ) { next; } # ignoring empty lines + if ( ${$filecontent}[$i] =~ /^\s*(\Q$lastdiskid\E\t)\Q$installer::globals::lastsequence_before_merge\E(\t.*)$/ ) + { + my $start = $1; + my $final = $2; + $infoline = "Merge: Old line in media table: ${$filecontent}[$i]\n"; + push( @installer::globals::logfileinfo, $infoline); + my $newline = $start . $newmaxsequencenumber . $final . "\n"; + ${$filecontent}[$i] = $newline; + $infoline = "Merge: Changed line in media table: ${$filecontent}[$i]\n"; + push( @installer::globals::logfileinfo, $infoline); + } + } + } + else + { + # the new line is identical for all localized databases, but has to be created for each MergeModule ($mergemodulegid) + if ( ! exists($installer::globals::merge_media_line{$mergemodulegid}) ) + { + $installer::globals::merge_media_line{$mergemodulegid} = create_new_media_line($mergemodulehash, $mediafile, $allupdatelastsequences, $allupdatediskids); + } + + $infoline = "Adding line: $installer::globals::merge_media_line{$mergemodulegid}\n"; + push( @installer::globals::logfileinfo, $infoline); + + # adding new line + push(@{$filecontent}, $installer::globals::merge_media_line{$mergemodulegid}); + } + + # saving file + installer::files::save_file($filename, $filecontent); +} + +######################################################################### +# Putting the directory table content into a hash. +######################################################################### + +sub analyze_directorytable_file +{ + my ($filecontent, $idtfilename) = @_; + + my %dirhash = (); + # Iterating over the file content + for ( my $i = 0; $i <= $#{$filecontent}; $i++ ) + { + if ( $i <= 2 ) { next; } # ignoring first three lines + if ( ${$filecontent}[$i] =~ /^\s*$/ ) { next; } # ignoring empty lines + if ( ${$filecontent}[$i] =~ /^\s*(.+?)\t(.*?)\t(.*?)\s*$/ ) + { + my %line = (); + # Format: Directory Directory_Parent DefaultDir + $line{'Directory'} = $1; + $line{'Directory_Parent'} = $2; + $line{'DefaultDir'} = $3; + $line{'linenumber'} = $i; # saving also the line number for direct access + + my $uniquekey = $line{'Directory'}; + $dirhash{$uniquekey} = \%line; + } + else + { + my $linecount = $i + 1; + installer::exiter::exit_program("ERROR: Unknown line format in table \"$idtfilename\" (line $linecount) !", "analyze_directorytable_file"); + } + } + + return \%dirhash; +} + +######################################################################### +# Putting the msi assembly table content into a hash. +######################################################################### + +sub analyze_msiassemblytable_file +{ + my ($filecontent, $idtfilename) = @_; + + my %assemblyhash = (); + # Iterating over the file content + for ( my $i = 0; $i <= $#{$filecontent}; $i++ ) + { + if ( $i <= 2 ) { next; } # ignoring first three lines + if ( ${$filecontent}[$i] =~ /^\s*$/ ) { next; } # ignoring empty lines + if ( ${$filecontent}[$i] =~ /^\s*(.+?)\t(.+?)\t(.+?)\t(.*?)\t(.*?)\s*$/ ) + { + my %line = (); + # Format: Component_ Feature_ File_Manifest File_Application Attributes + $line{'Component'} = $1; + $line{'Feature'} = $2; + $line{'File_Manifest'} = $3; + $line{'File_Application'} = $4; + $line{'Attributes'} = $5; + $line{'linenumber'} = $i; # saving also the line number for direct access + + my $uniquekey = $line{'Component'}; + $assemblyhash{$uniquekey} = \%line; + } + else + { + my $linecount = $i + 1; + installer::exiter::exit_program("ERROR: Unknown line format in table \"$idtfilename\" (line $linecount) !", "analyze_msiassemblytable_file"); + } + } + + return \%assemblyhash; +} + +######################################################################### +# Putting the file table content into a hash. +######################################################################### + +sub analyze_filetable_file +{ + my ( $filecontent, $idtfilename ) = @_; + + my %filehash = (); + # Iterating over the file content + for ( my $i = 0; $i <= $#{$filecontent}; $i++ ) + { + if ( $i <= 2 ) { next; } # ignoring first three lines + if ( ${$filecontent}[$i] =~ /^\s*$/ ) { next; } # ignoring empty lines + if ( ${$filecontent}[$i] =~ /^\s*(.+?)\t(.+?)\t(.+?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\t(.+?)\s*$/ ) + { + my %line = (); + # Format: File Component_ FileName FileSize Version Language Attributes Sequence + $line{'File'} = $1; + $line{'Component'} = $2; + $line{'FileName'} = $3; + $line{'FileSize'} = $4; + $line{'Version'} = $5; + $line{'Language'} = $6; + $line{'Attributes'} = $7; + $line{'Sequence'} = $8; + $line{'linenumber'} = $i; # saving also the line number for direct access + + my $uniquekey = $line{'File'}; + $filehash{$uniquekey} = \%line; + } + else + { + my $linecount = $i + 1; + installer::exiter::exit_program("ERROR: Unknown line format in table \"$idtfilename\" (line $linecount) !", "analyze_filetable_file"); + } + } + + return \%filehash; +} + +######################################################################### +# Creating a new line for the directory table. +######################################################################### + +sub get_new_line_for_directory_table +{ + my ($dir) = @_; + + my $newline = "$dir->{'Directory'}\t$dir->{'Directory_Parent'}\t$dir->{'DefaultDir'}\n"; + + return $newline; +} + +######################################################################### +# Creating a new line for the file table. +######################################################################### + +sub get_new_line_for_file_table +{ + my ($file) = @_; + + my $newline = "$file->{'File'}\t$file->{'Component'}\t$file->{'FileName'}\t$file->{'FileSize'}\t$file->{'Version'}\t$file->{'Language'}\t$file->{'Attributes'}\t$file->{'Sequence'}\n"; + + return $newline; +} + +######################################################################### +# Creating a new line for the msiassembly table. +######################################################################### + +sub get_new_line_for_msiassembly_table +{ + my ($assembly) = @_; + + my $newline = "$assembly->{'Component'}\t$assembly->{'Feature'}\t$assembly->{'File_Manifest'}\t$assembly->{'File_Application'}\t$assembly->{'Attributes'}\n"; + + return $newline; +} + +######################################################################### +# Sorting the files collector, if there are files, following +# the merge module files. +######################################################################### + +sub sort_files_collector_for_sequence +{ + my ($filesref) = @_; + + my @sortarray = (); + my %helphash = (); + + for ( my $i = 0; $i <= $#{$filesref}; $i++ ) + { + my $onefile = ${$filesref}[$i]; + if ( ! exists($onefile->{'sequencenumber'}) ) { installer::exiter::exit_program("ERROR: Could not find sequencenumber for file: $onefile->{'uniquename'} !", "sort_files_collector_for_sequence"); } + my $sequence = $onefile->{'sequencenumber'}; + $helphash{$sequence} = $onefile; + } + + foreach my $seq ( sort { $a <=> $b } keys %helphash ) { push(@sortarray, $helphash{$seq}); } + + return \@sortarray; +} + +######################################################################### +# In the file table "Sequence" and "Attributes" have to be changed. +######################################################################### + +sub change_file_table +{ + my ($mergemodulehash, $workdir, $allupdatesequenceshashref, $includepatharrayref, $filesref, $mergemodulegid) = @_; + + my $infoline = "Changing content of table \"File\"\n"; + push( @installer::globals::logfileinfo, $infoline); + + my $idtfilename = "File.idt"; + if ( ! -f $idtfilename ) { installer::exiter::exit_program("ERROR: Could not find file \"$idtfilename\" in \"$workdir\" !", "change_file_table"); } + + my $filecontent = installer::files::read_file($idtfilename); + + # If File.idt needed to be removed before the msm database was merged into the msi database, + # now it is time to add the content into File.idt + if ( $mergemodulehash->{'removefiletable'} ) + { + for ( my $i = 0; $i <= $#{$mergemodulehash->{'fileidtcontent'}}; $i++ ) + { + push(@{$filecontent}, ${$mergemodulehash->{'fileidtcontent'}}[$i]); + } + } + + # Unpacking the MergeModule.CABinet (only once) + # Unpacking into temp directory. Warning: expand.exe has problems with very long unpack directories. + + my $unpackdir = installer::systemactions::create_directories("cab", ""); + push(@installer::globals::removedirs, $unpackdir); + $unpackdir = $unpackdir . $installer::globals::separator . $mergemodulegid; + + my %newfileshash = (); + if (( $installer::globals::fix_number_of_cab_files ) && ( ! $installer::globals::mergefiles_added_into_collector )) + { + if ( ! -d $unpackdir ) { installer::systemactions::create_directory($unpackdir); } + + # Unpack the cab file, so that in can be included into the last office cabinet file. Attention: cararc.exe from cabsdk required. + # cabarc.exe -o X <fullcabfilepath> + + # my $cabarcfilename = "cabarc.exe"; + # my $cabarcfile = installer::scriptitems::get_sourcepath_from_filename_and_includepath(\$cabarcfilename, $includepatharrayref, 1); + + # if ( ! -f $$cabarcfile ) + # { + # $cabarcfilename = "CABARC.EXE"; + # $cabarcfile = installer::scriptitems::get_sourcepath_from_filename_and_includepath(\$cabarcfilename, $includepatharrayref, 1); + # if ( ! -f $$cabarcfile ) + # { + # installer::exiter::exit_program("ERROR: cabarc.exe not found !", "change_file_table"); + # } + # } + # my $cabarc = $$cabarcfile; + + # changing directory + my $from = cwd(); + my $to = $mergemodulehash->{'workdir'}; + if ( $^O =~ /cygwin/i ) { + $to = qx(cygpath -u "$to"); + chomp $to; + } + + chdir($to) || die "Could not chdir to \"$to\"\n"; + + # Unpack the cab file, so that in can be included into the last office cabinet file. + # Not using cabarc.exe from cabsdk for unpacking cabinet files, but "expand.exe" that + # should be available on every Windows system. + + $infoline = "Unpacking cabinet file: $mergemodulehash->{'cabinetfile'}\n"; + push( @installer::globals::logfileinfo, $infoline); + + # Avoid the Cygwin expand command + my $expandfile = "expand.exe"; # Has to be in the path + if ( $^O =~ /cygwin/i ) { + $expandfile = qx(cygpath -u "$ENV{WINDIR}"/System32/expand.exe); + chomp $expandfile; + } + + my $cabfilename = "MergeModule.CABinet"; + + # exclude cabinet file + # my $systemcall = $cabarc . " -o X " . $mergemodulehash->{'cabinetfile'}; + + my $systemcall = ""; + if ( $^O =~ /cygwin/i ) { + my $localunpackdir = qx(cygpath -m "$unpackdir"); + chomp $localunpackdir; + $systemcall = $expandfile . " " . $cabfilename . " -F:\\\* " . $localunpackdir; + } + else + { + $systemcall = $expandfile . " " . $cabfilename . " -F:\* " . $unpackdir . " 2\>\&1"; + } + + 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); + installer::exiter::exit_program("ERROR: Could not extract cabinet file: $mergemodulehash->{'cabinetfile'} !", "change_file_table"); + } + else + { + $infoline = "Success: Executed $systemcall successfully!\n"; + push( @installer::globals::logfileinfo, $infoline); + } + + chdir($from); + } + + # For performance reasons creating a hash with file names and rows + # The content of File.idt is changed after every merge -> content cannot be saved in global hash + $merge_filetablehashref = analyze_filetable_file($filecontent, $idtfilename); + + my $attributes = "16384"; # Always + + my $filename; + foreach $filename (keys %{$mergemodulehash->{'mergefilesequence'}} ) + { + my $mergefilesequence = $mergemodulehash->{'mergefilesequence'}->{$filename}; + + if ( ! exists($merge_filetablehashref->{$filename}) ) { installer::exiter::exit_program("ERROR: Could not find file \"$filename\" in \"$idtfilename\" !", "change_file_table"); } + my $filehash = $merge_filetablehashref->{$filename}; + my $linenumber = $filehash->{'linenumber'}; + + # <- this line has to be changed concerning "Sequence" and "Attributes" + $filehash->{'Attributes'} = $attributes; + + # If this is an update process, the sequence numbers have to be reused. + if ( $installer::globals::updatedatabase ) + { + if ( ! exists($allupdatesequenceshashref->{$filehash->{'File'}}) ) { installer::exiter::exit_program("ERROR: Sequence not defined for file \"$filehash->{'File'}\" !", "change_file_table"); } + $filehash->{'Sequence'} = $allupdatesequenceshashref->{$filehash->{'File'}}; + # Saving all mergemodule sequence numbers. This is important for creating ddf files + $installer::globals::allmergemodulefilesequences{$filehash->{'Sequence'}} = 1; + } + else + { + # Important saved data: $installer::globals::lastsequence_before_merge. + # This mechanism keeps the correct order inside the new cabinet file. + $filehash->{'Sequence'} = $filehash->{'Sequence'} + $installer::globals::lastsequence_before_merge; + } + + my $oldline = ${$filecontent}[$linenumber]; + my $newline = get_new_line_for_file_table($filehash); + ${$filecontent}[$linenumber] = $newline; + + $infoline = "Merge, replacing line:\n"; + push( @installer::globals::logfileinfo, $infoline); + $infoline = "Old: $oldline\n"; + push( @installer::globals::logfileinfo, $infoline); + $infoline = "New: $newline\n"; + push( @installer::globals::logfileinfo, $infoline); + + # Adding files to the files collector (but only once) + if (( $installer::globals::fix_number_of_cab_files ) && ( ! $installer::globals::mergefiles_added_into_collector )) + { + # If the number of cabinet files is kept constant, + # all files from the mergemodule cabinet files will + # be integrated into the last office cabinet file + # (installer::globals::lastcabfilename). + # Therefore the files must now be added to the filescollector, + # so that they will be integrated into the ddf files. + + # Problem with very long filenames -> copying to shorter filenames + my $newfilename = "f" . $filehash->{'Sequence'}; + my $completesource = $unpackdir . $installer::globals::separator . $filehash->{'File'}; + my $completedest = $unpackdir . $installer::globals::separator . $newfilename; + installer::systemactions::copy_one_file($completesource, $completedest); + + my $locallastcabfilename = $installer::globals::lastcabfilename; + if ( $locallastcabfilename =~ /^\s*\#/ ) { $locallastcabfilename =~ s/^\s*\#//; } # removing beginning hashes + + # Create new file hash for file collector + my %newfile = (); + $newfile{'sequencenumber'} = $filehash->{'Sequence'}; + $newfile{'assignedsequencenumber'} = $filehash->{'Sequence'}; + $newfile{'cabinet'} = $locallastcabfilename; + $newfile{'sourcepath'} = $completedest; + $newfile{'componentname'} = $filehash->{'Component'}; + $newfile{'uniquename'} = $filehash->{'File'}; + $newfile{'Name'} = $filehash->{'File'}; + + # Saving in globals sequence hash + $installer::globals::uniquefilenamesequence{$filehash->{'File'}} = $filehash->{'Sequence'}; + + if ( ! -f $newfile{'sourcepath'} ) { installer::exiter::exit_program("ERROR: File \"$newfile{'sourcepath'}\" must exist!", "change_file_table"); } + + # Collecting all new files. Attention: This files must be included into files collector in correct order! + $newfileshash{$filehash->{'Sequence'}} = \%newfile; + # push(@{$filesref}, \%newfile); -> this is not the correct order + } + } + + # Now the files can be added to the files collector + # In the case of an update process, there can be new files, that have to be added after the merge module files. + # Warning: In multilingual installation sets, the files only have to be added once to the files collector! + + if ( ! $installer::globals::mergefiles_added_into_collector ) + { + foreach my $localsequence ( sort { $a <=> $b } keys %newfileshash ) { push(@{$filesref}, $newfileshash{$localsequence}); } + if ( $installer::globals::newfilesexist ) { $filesref = sort_files_collector_for_sequence($filesref); } + # $installer::globals::mergefiles_added_into_collector = 1; -> Not yet. Only if all mergemodules are merged for one language. + } + + # Saving the idt file (for every language) + installer::files::save_file($idtfilename, $filecontent); + + return $filesref; +} + +######################################################################### +# Reading the file "Director.idt". The Directory, that is defined in scp +# has to be defined in this table. +######################################################################### + +sub collect_directories +{ + my $idtfilename = "Director.idt"; + my $filecontent = installer::files::read_file($idtfilename); + + for ( my $i = 0; $i <= $#{$filecontent}; $i++ ) + { + if ( $i <= 2 ) { next; } # ignoring first three lines + if ( ${$filecontent}[$i] =~ /^\s*$/ ) { next; } # ignoring empty lines + # Format: Directory Directory_Parent DefaultDir + if ( ${$filecontent}[$i] =~ /^\s*(.+?)\t(.*?)\t(.*?)\s*$/ ) + { + $installer::globals::merge_alldirectory_hash{$1} = 1; + } + else + { + my $linecount = $i + 1; + installer::exiter::exit_program("ERROR: Unknown line format in table \"$idtfilename\" (line $linecount) !", "collect_directories"); + } + } +} + +######################################################################### +# Reading the file "Feature.idt". The Feature, that is defined in scp +# has to be defined in this table. +######################################################################### + +sub collect_feature +{ + my $idtfilename = "Feature.idt"; + if ( ! -f $idtfilename ) { installer::exiter::exit_program("ERROR: Could not find file \"$idtfilename\" in \"$workdir\" !", "collect_feature"); } + my $filecontent = installer::files::read_file($idtfilename); + + for ( my $i = 0; $i <= $#{$filecontent}; $i++ ) + { + if ( $i <= 2 ) { next; } # ignoring first three lines + if ( ${$filecontent}[$i] =~ /^\s*$/ ) { next; } # ignoring empty lines + # Format: Feature Feature_Parent Title Description Display Level Directory_ Attributes + if ( ${$filecontent}[$i] =~ /^\s*(.+?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\s*$/ ) + { + $installer::globals::merge_allfeature_hash{$1} = 1; + } + else + { + my $linecount = $i + 1; + installer::exiter::exit_program("ERROR: Unknown line format in table \"$idtfilename\" (line $linecount) !", "collect_feature"); + } + } +} + +######################################################################### +# In the featurecomponent table, the new connections have to be added. +######################################################################### + +sub change_featurecomponent_table +{ + my ($mergemodulehash, $workdir) = @_; + + my $infoline = "Changing content of table \"FeatureComponents\"\n"; + push( @installer::globals::logfileinfo, $infoline); + + my $idtfilename = "FeatureC.idt"; + if ( ! -f $idtfilename ) { installer::exiter::exit_program("ERROR: Could not find file \"$idtfilename\" in \"$workdir\" !", "change_featurecomponent_table"); } + + my $filecontent = installer::files::read_file($idtfilename); + + # Simply adding for each new component one line. The Feature has to be defined in scp project. + my $feature = $mergemodulehash->{'feature'}; + + if ( ! $installer::globals::mergefeaturecollected ) + { + collect_feature(); # putting content into hash %installer::globals::merge_allfeature_hash + $installer::globals::mergefeaturecollected = 1; + } + + if ( ! exists($installer::globals::merge_allfeature_hash{$feature}) ) + { + installer::exiter::exit_program("ERROR: Unknown feature defined in scp: \"$feature\" . Not defined in table \"Feature\" !", "change_featurecomponent_table"); + } + + my $component; + foreach $component ( keys %{$mergemodulehash->{'componentnames'}} ) + { + my $line = "$feature\t$component\n"; + push(@{$filecontent}, $line); + $infoline = "Adding line: $line\n"; + push( @installer::globals::logfileinfo, $infoline); + } + + # saving file + installer::files::save_file($idtfilename, $filecontent); +} + +######################################################################### +# In the directory table, the directory parent has to be changed, +# if it is not TARGETDIR. +######################################################################### + +sub change_directory_table +{ + my ($mergemodulehash, $workdir) = @_; + + # directory for MergeModule has to be defined in scp project + my $scpdirectory = $mergemodulehash->{'rootdir'}; + + if ( $scpdirectory ne "TARGETDIR" ) # TARGETDIR works fine, when using msidb.exe + { + my $infoline = "Changing content of table \"Directory\"\n"; + push( @installer::globals::logfileinfo, $infoline); + + my $idtfilename = "Director.idt"; + if ( ! -f $idtfilename ) { installer::exiter::exit_program("ERROR: Could not find file \"$idtfilename\" in \"$workdir\" !", "change_directory_table"); } + + my $filecontent = installer::files::read_file($idtfilename); + + if ( ! $installer::globals::mergedirectoriescollected ) + { + collect_directories(); # putting content into %installer::globals::merge_alldirectory_hash, only first column! + $installer::globals::mergedirectoriescollected = 1; + } + + if ( ! exists($installer::globals::merge_alldirectory_hash{$scpdirectory}) ) + { + installer::exiter::exit_program("ERROR: Unknown directory defined in scp: \"$scpdirectory\" . Not defined in table \"Directory\" !", "change_directory_table"); + } + + # If the definition in scp is okay, now the complete content of "Director.idt" can be analyzed + my $merge_directorytablehashref = analyze_directorytable_file($filecontent, $idtfilename); + + my $directory; + foreach $directory (keys %{$mergemodulehash->{'mergedirectories'}} ) + { + if ( ! exists($merge_directorytablehashref->{$directory}) ) { installer::exiter::exit_program("ERROR: Could not find directory \"$directory\" in \"$idtfilename\" !", "change_directory_table"); } + my $dirhash = $merge_directorytablehashref->{$directory}; + my $linenumber = $dirhash->{'linenumber'}; + + # <- this line has to be changed concerning "Directory_Parent", + # if the current value is "TARGETDIR", which is the default value from msidb.exe + + if ( $dirhash->{'Directory_Parent'} eq "TARGETDIR" ) + { + $dirhash->{'Directory_Parent'} = $scpdirectory; + + my $oldline = ${$filecontent}[$linenumber]; + my $newline = get_new_line_for_directory_table($dirhash); + ${$filecontent}[$linenumber] = $newline; + + $infoline = "Merge, replacing line:\n"; + push( @installer::globals::logfileinfo, $infoline); + $infoline = "Old: $oldline\n"; + push( @installer::globals::logfileinfo, $infoline); + $infoline = "New: $newline\n"; + push( @installer::globals::logfileinfo, $infoline); + } + } + + # saving file + installer::files::save_file($idtfilename, $filecontent); + } +} + +######################################################################### +# In the msiassembly table, the feature has to be changed. +######################################################################### + +sub change_msiassembly_table +{ + my ($mergemodulehash, $workdir) = @_; + + my $infoline = "Changing content of table \"MsiAssembly\"\n"; + push( @installer::globals::logfileinfo, $infoline); + + my $idtfilename = "MsiAssem.idt"; + if ( ! -f $idtfilename ) { installer::exiter::exit_program("ERROR: Could not find file \"$idtfilename\" in \"$workdir\" !", "change_msiassembly_table"); } + + my $filecontent = installer::files::read_file($idtfilename); + + # feature has to be defined in scp project + my $feature = $mergemodulehash->{'feature'}; + + if ( ! $installer::globals::mergefeaturecollected ) + { + collect_feature(); # putting content into hash %installer::globals::merge_allfeature_hash + $installer::globals::mergefeaturecollected = 1; + } + + if ( ! exists($installer::globals::merge_allfeature_hash{$feature}) ) + { + installer::exiter::exit_program("ERROR: Unknown feature defined in scp: \"$feature\" . Not defined in table \"Feature\" !", "change_msiassembly_table"); + } + + my $merge_msiassemblytablehashref = analyze_msiassemblytable_file($filecontent, $idtfilename); + + my $component; + foreach $component (keys %{$mergemodulehash->{'mergeassemblies'}} ) + { + if ( ! exists($merge_msiassemblytablehashref->{$component}) ) { installer::exiter::exit_program("ERROR: Could not find component \"$component\" in \"$idtfilename\" !", "change_msiassembly_table"); } + my $assemblyhash = $merge_msiassemblytablehashref->{$component}; + my $linenumber = $assemblyhash->{'linenumber'}; + + # <- this line has to be changed concerning "Feature" + $assemblyhash->{'Feature'} = $feature; + + my $oldline = ${$filecontent}[$linenumber]; + my $newline = get_new_line_for_msiassembly_table($assemblyhash); + ${$filecontent}[$linenumber] = $newline; + + $infoline = "Merge, replacing line:\n"; + push( @installer::globals::logfileinfo, $infoline); + $infoline = "Old: $oldline\n"; + push( @installer::globals::logfileinfo, $infoline); + $infoline = "New: $newline\n"; + push( @installer::globals::logfileinfo, $infoline); + } + + # saving file + installer::files::save_file($idtfilename, $filecontent); +} + +######################################################################### +# Creating file content hash +######################################################################### + +sub make_executeidtcontent_hash +{ + my ($filecontent, $idtfilename) = @_; + + my %newhash = (); + + for ( my $i = 0; $i <= $#{$filecontent}; $i++ ) + { + if ( $i <= 2 ) { next; } # ignoring first three lines + if ( ${$filecontent}[$i] =~ /^\s*$/ ) { next; } # ignoring empty lines + # Format for all sequence tables: Action Condition Sequence + if ( ${$filecontent}[$i] =~ /^\s*(.+?)\t(.*?)\t(.*?)\s*$/ ) + { + my %onehash = (); + $onehash{'Action'} = $1; + $onehash{'Condition'} = $2; + $onehash{'Sequence'} = $3; + $newhash{$onehash{'Action'}} = \%onehash; + } + else + { + my $linecount = $i + 1; + installer::exiter::exit_program("ERROR: Unknown line format in table \"$idtfilename\" (line $linecount) !", "make_executeidtcontent_hash"); + } + } + + return \%newhash; +} + +######################################################################### +# Creating file content hash +######################################################################### + +sub make_moduleexecuteidtcontent_hash +{ + my ($filecontent, $idtfilename) = @_; + + my %newhash = (); + + for ( my $i = 0; $i <= $#{$filecontent}; $i++ ) + { + if ( $i <= 2 ) { next; } # ignoring first three lines + if ( ${$filecontent}[$i] =~ /^\s*$/ ) { next; } # ignoring empty lines + # Format for all module sequence tables: Action Sequence BaseAction After Condition + if ( ${$filecontent}[$i] =~ /^\s*(.+?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\s*$/ ) + { + my %onehash = (); + $onehash{'Action'} = $1; + $onehash{'Sequence'} = $2; + $onehash{'BaseAction'} = $3; + $onehash{'After'} = $4; + $onehash{'Condition'} = $5; + $newhash{$onehash{'Action'}} = \%onehash; + } + else + { + my $linecount = $i + 1; + installer::exiter::exit_program("ERROR: Unknown line format in table \"$idtfilename\" (line $linecount) !", "make_executeidtcontent_hash"); + } + } + + return \%newhash; +} + +######################################################################### +# ExecuteSequence tables need to be merged with +# ModuleExecuteSequence tables created by msidb.exe. +######################################################################### + +sub change_executesequence_table +{ + my ($mergemodulehash, $workdir, $idtfilename, $moduleidtfilename) = @_; + + my $infoline = "Changing content of table \"$idtfilename\"\n"; + push( @installer::globals::logfileinfo, $infoline); + + if ( ! -f $idtfilename ) { installer::exiter::exit_program("ERROR: Could not find file \"$idtfilename\" in \"$workdir\" !", "change_executesequence_table"); } + if ( ! -f $moduleidtfilename ) { installer::exiter::exit_program("ERROR: Could not find file \"$moduleidtfilename\" in \"$workdir\" !", "change_executesequence_table"); } + + # Reading file content + my $idtfilecontent = installer::files::read_file($idtfilename); + my $moduleidtfilecontent = installer::files::read_file($moduleidtfilename); + + # Converting to hash + my $idtcontenthash = make_executeidtcontent_hash($idtfilecontent, $idtfilename); + my $moduleidtcontenthash = make_moduleexecuteidtcontent_hash($moduleidtfilecontent, $moduleidtfilename); + + # Merging + foreach my $action ( keys %{$moduleidtcontenthash} ) + { + if ( exists($idtcontenthash->{$action}) ) { next; } # Action already exists, can be ignored + + if (( $idtfilename eq "InstallU.idt" ) && ( ! ( $action =~ /^\s*WindowsFolder\./ ))) { next; } # Only "WindowsFolder.*" CustomActions for UI Sequence table + + my $actionhashref = $moduleidtcontenthash->{$action}; + if ( $actionhashref->{'Sequence'} ne "" ) + { + # Format for all sequence tables: Action Condition Sequence + my $newline = $actionhashref->{'Action'} . "\t" . $actionhashref->{'Condition'} . "\t" . $actionhashref->{'Sequence'} . "\n"; + # Adding to table + push(@{$idtfilecontent}, $newline); + # Also adding to hash + my %idttablehash = (); + $idttablehash{'Action'} = $actionhashref->{'Action'}; + $idttablehash{'Condition'} = $actionhashref->{'Condition'}; + $idttablehash{'Sequence'} = $actionhashref->{'Sequence'}; + $idtcontenthash->{$action} = \%idttablehash; + + } + else # no sequence defined, using syntax "BaseAction" and "After" + { + my $baseactionname = $actionhashref->{'BaseAction'}; + # If this baseactionname is not defined in execute idt file, it is not possible to merge + if ( ! exists($idtcontenthash->{$baseactionname}) ) { installer::exiter::exit_program("ERROR: Merge problem: Could not find action \"$baseactionname\" in file \"$idtfilename\" !", "change_executesequence_table"); } + + my $baseaction = $idtcontenthash->{$baseactionname}; + my $sequencenumber = $baseaction->{'Sequence'}; + if ( $actionhashref->{'After'} == 1 ) { $sequencenumber = $sequencenumber + 1; } + else { $sequencenumber = $sequencenumber - 1; } + + # Format for all sequence tables: Action Condition Sequence + my $newline = $actionhashref->{'Action'} . "\t" . $actionhashref->{'Condition'} . "\t" . $sequencenumber . "\n"; + # Adding to table + push(@{$idtfilecontent}, $newline); + # Also adding to hash + my %idttablehash = (); + $idttablehash{'Action'} = $actionhashref->{'Action'}; + $idttablehash{'Condition'} = $actionhashref->{'Condition'}; + $idttablehash{'Sequence'} = $sequencenumber; + $idtcontenthash->{$action} = \%idttablehash; + } + } + + # saving file + installer::files::save_file($idtfilename, $idtfilecontent); +} + + +1; diff --git a/solenv/bin/modules/installer/windows/msiglobal.pm b/solenv/bin/modules/installer/windows/msiglobal.pm new file mode 100644 index 000000000000..6c49a8db3cf2 --- /dev/null +++ b/solenv/bin/modules/installer/windows/msiglobal.pm @@ -0,0 +1,2260 @@ +#************************************************************************* +# +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# Copyright 2008 by Sun Microsystems, Inc. +# +# OpenOffice.org - a multi-platform office productivity suite +# +# $RCSfile: msiglobal.pm,v $ +# +# This file is part of OpenOffice.org. +# +# OpenOffice.org is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License version 3 +# only, as published by the Free Software Foundation. +# +# OpenOffice.org is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License version 3 for more details +# (a copy is included in the LICENSE file that accompanied this code). +# +# You should have received a copy of the GNU Lesser General Public License +# version 3 along with OpenOffice.org. If not, see +# <http://www.openoffice.org/license.html> +# for a copy of the LGPLv3 License. +# +#************************************************************************* + +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=CDROM\n"; # This allows the .cab file to be as large as needed. + push(@{$ddffileref} ,$oneline); + $oneline = ".Set CompressionType=LZX\n"; + push(@{$ddffileref} ,$oneline); + $oneline = ".Set Compress=ON\n"; + push(@{$ddffileref} ,$oneline); + $oneline = ".Set CompressionLevel=$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 pathes. 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_pathes($filesref); } + + if ( $installer::globals::use_packages_for_cabs ) + { + my $sequenceorder = get_sequenceorder($filesref); + + my $counter = 1; + my $currentcabfile = ""; + + 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; + } + + # Files with increasing sequencerorder are included in one cab file + my $onefile = ${$filesref}[$sequenceorder->{$counter}]; + my $cabinetfile = $onefile->{'assignedcabinetfile'}; + 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 pathes + if ( $allvariables->{'RELATIVE_PATHES_IN_DDF'} ) { $sourcepath = make_relative_ddf_path($sourcepath); } + + # all files with the same cabinetfile have increasing sequencenumbers + + my @ddffile = (); + + write_ddf_file_header(\@ddffile, $cabinetfile, $installdir); + + my $ddfline = "\"" . $sourcepath . "\"" . " " . $uniquename . "\n"; + if ( $doinclude ) { push(@ddffile, $ddfline); } + + $counter++; # increasing the counter + my $nextfile = ""; + my $nextcabinetfile = ""; + if ( exists($sequenceorder->{$counter}) ) { $nextfile = ${$filesref}[$sequenceorder->{$counter}]; } + if ( $nextfile->{'assignedcabinetfile'} ) { $nextcabinetfile = $nextfile->{'assignedcabinetfile'}; } + + 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 pathes + if ( $allvariables->{'RELATIVE_PATHES_IN_DDF'} ) { $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++; # increasing the counter! + $nextcabinetfile = "_lastfile_"; + if ( exists($sequenceorder->{$counter}) ) + { + $nextfile = ${$filesref}[$sequenceorder->{$counter}]; + $nextcabinetfile = $nextfile->{'assignedcabinetfile'}; + } + } + + # 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::cab_file_per_component ) || ( $installer::globals::fix_number_of_cab_files )) && ( $installer::globals::updatedatabase )) + { + my $sequenceorder = get_sequenceorder($filesref); + + my $counter = 1; + my $currentcabfile = ""; + + 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 pathes + if ( $allvariables->{'RELATIVE_PATHES_IN_DDF'} ) { $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 pathes + if ( $allvariables->{'RELATIVE_PATHES_IN_DDF'} ) { $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++; # increasing the 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::cab_file_per_component ) || ( $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 pathes + if ( $allvariables->{'RELATIVE_PATHES_IN_DDF'} ) { $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 pathes + if ( $allvariables->{'RELATIVE_PATHES_IN_DDF'} ) { $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); + } + } + elsif (( $installer::globals::one_cab_file ) && ( $installer::globals::updatedatabase )) + { + my $sequenceorder = get_sequenceorder($filesref); + + my $counter = 1; + my $currentcabfile = ""; + + 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}]; + + $cabinetfile = $onefile->{'cabinet'}; + my $sourcepath = $onefile->{'sourcepath'}; + if ( $^O =~ /cygwin/i ) { $sourcepath = $onefile->{'cyg_sourcepath'}; } + my $uniquename = $onefile->{'uniquename'}; + + # to avoid lines with more than 256 characters, it can be useful to use relative pathes + if ( $allvariables->{'RELATIVE_PATHES_IN_DDF'} ) { $sourcepath = make_relative_ddf_path($sourcepath); } + + if ( $counter == 1 ) { write_ddf_file_header(\@ddffile, $cabinetfile, $installdir); } + + my $styles = ""; + my $doinclude = 1; + if ( $onefile->{'Styles'} ) { $styles = $onefile->{'Styles'}; }; + if ( $styles =~ /\bDONT_PACK\b/ ) { $doinclude = 0; } + + my $ddfline = "\"" . $sourcepath . "\"" . " " . $uniquename . "\n"; + if ( $doinclude ) { push(@ddffile, $ddfline); } + + $counter++; # increasing the counter + } + + # creating the DDF file + + my $ddffilename = $cabinetfile; + $ddffilename =~ s/.cab/.ddf/; + $ddfdir =~ s/[\/\\]\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 /F " . $ddffilename . "\n"; + 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::one_cab_file ) + { + my @ddffile = (); + + my $cabinetfile = ""; + + for ( my $i = 0; $i <= $#{$filesref}; $i++ ) + { + my $onefile = ${$filesref}[$i]; + $cabinetfile = $onefile->{'cabinet'}; + my $sourcepath = $onefile->{'sourcepath'}; + if ( $^O =~ /cygwin/i ) { $sourcepath = $onefile->{'cyg_sourcepath'}; } + my $uniquename = $onefile->{'uniquename'}; + + # to avoid lines with more than 256 characters, it can be useful to use relative pathes + if ( $allvariables->{'RELATIVE_PATHES_IN_DDF'} ) { $sourcepath = make_relative_ddf_path($sourcepath); } + + if ( $i == 0 ) { write_ddf_file_header(\@ddffile, $cabinetfile, $installdir); } + + my $styles = ""; + my $doinclude = 1; + if ( $onefile->{'Styles'} ) { $styles = $onefile->{'Styles'}; }; + if ( $styles =~ /\bDONT_PACK\b/ ) { $doinclude = 0; } + + my $ddfline = "\"" . $sourcepath . "\"" . " " . $uniquename . "\n"; + if ( $doinclude ) { push(@ddffile, $ddfline); } + } + + # creating the DDF file + + my $ddffilename = $cabinetfile; + $ddffilename =~ s/.cab/.ddf/; + $ddfdir =~ s/[\/\\]\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 /F " . $ddffilename . "\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 !", "create_media_table"); + } + + installer::logger::include_timestamp_into_logfile("Performance Info: ddf file generation end"); + + return \@cabfilelist; # contains all system calls for packaging process +} + +######################################################################## +# Returning the file sequence of a specified file. +######################################################################## + +sub get_file_sequence +{ + my ($filesref, $uniquefilename) = @_; + + my $sequence = ""; + my $found_sequence = 0; + + for ( my $i = 0; $i <= $#{$filesref}; $i++ ) + { + my $onefile = ${$filesref}[$i]; + my $uniquename = $onefile->{'uniquename'}; + + if ( $uniquename eq $uniquefilename ) + { + $sequence = $onefile->{'sequencenumber'}; + $found_sequence = 1; + last; + } + } + + if ( ! $found_sequence ) { installer::exiter::exit_program("ERROR: No sequence found for $uniquefilename !", "get_file_sequence"); } + + return $sequence; +} + +######################################################################## +# 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 = "\\"; + } + 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 value from sis.mlf for Summary Information Stream +##################################################################### + +sub get_value_from_sis_lng +{ + my ($language, $languagefile, $searchstring) = @_; + + my $language_block = installer::windows::idtglobal::get_language_block_from_language_file($searchstring, $languagefile); + my $newstring = installer::windows::idtglobal::get_language_string_from_language_block($language_block, $language, $searchstring); + $newstring = "\"" . $newstring . "\""; + + return $newstring; +} + +################################################################# +# 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 codepage for the Summary Information Stream +################################################################# + +sub get_codepage_for_sis +{ + my ( $language ) = @_; + + my $codepage = installer::windows::language::get_windows_encoding($language); + + # Codepage 65001 does not work in Summary Information Stream + if ( $codepage == 65001 ) { $codepage = 0; } + + # my $codepage = "1252"; # determine dynamically in a function + # my $codepage = "65001"; # UTF-8 + return $codepage; +} + +################################################################# +# Returning the template for the Summary Information Stream +################################################################# + +sub get_template_for_sis +{ + my ( $language ) = @_; + + my $windowslanguage = installer::windows::language::get_windows_language($language); + + my $value = "\"Intel;" . $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 title for the Summary Information Stream +################################################################# + +sub get_title_for_sis +{ + my ( $language, $languagefile, $searchstring ) = @_; + + my $title = get_value_from_sis_lng($language, $languagefile, $searchstring ); + + return $title; +} + +################################################################# +# 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 comment for the Summary Information Stream +################################################################# + +sub get_comment_for_sis +{ + my ( $language, $languagefile, $searchstring ) = @_; + + my $comment = get_value_from_sis_lng($language, $languagefile, $searchstring ); + + return $comment; +} + +################################################################# +# Returning the keywords for the Summary Information Stream +################################################################# + +sub get_keywords_for_sis +{ + my ( $language, $languagefile, $searchstring ) = @_; + + my $keywords = get_value_from_sis_lng($language, $languagefile, $searchstring ); + + return $keywords; +} + +###################################################################### +# Returning the application name for the Summary Information Stream +###################################################################### + +sub get_appname_for_sis +{ + my ( $language, $languagefile, $searchstring ) = @_; + + my $appname = get_value_from_sis_lng($language, $languagefile, $searchstring ); + + return $appname; +} + +###################################################################### +# 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 : requrired 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 $sislanguage = "en-US"; # title, comment, keyword and appname alway in english + + my $msiversion = get_msiversion_for_sis(); + my $codepage = get_codepage_for_sis($language); + my $template = get_template_for_sis($language); + my $guid = get_packagecode_for_sis(); + my $title = get_title_for_sis($sislanguage,$languagefile, "OOO_SIS_TITLE"); + my $author = get_author_for_sis(); + my $subject = get_subject_for_sis($allvariableshashref); + my $comment = get_comment_for_sis($sislanguage,$languagefile, "OOO_SIS_COMMENT"); + my $keywords = get_keywords_for_sis($sislanguage,$languagefile, "OOO_SIS_KEYWORDS"); + my $appname = get_appname_for_sis($sislanguage,$languagefile, "OOO_SIS_APPNAME"); + 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 $msiinfo!\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 $msitran = "msitran.exe"; # Has to be in the path + + $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 $transformfile = $installdir . $installer::globals::separator . "trans_" . $onelanguage . ".mst"; + + 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 occured. + # 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 occured. + # 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"); + 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 occured.\n"; + push( @installer::globals::logfileinfo, $infoline); + } + } + else + { + $infoline = "File $transformfile does not exist -> An error occured.\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: Could not remove file $$referencedbname !\n"; + push( @installer::globals::logfileinfo, $infoline); + installer::exiter::exit_program($infoline, "create_transforms"); + } + } +} + +######################################################################### +# 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; +} + +######################################################################### +# Adding the language to the name of the msi databasename, +# if this is required (ADDLANGUAGEINDATABASENAME) +######################################################################### + +sub add_language_to_msi_database +{ + my ($defaultlanguage, $installdir, $allvariables) = @_; + + my $languagestring = $defaultlanguage; + if ( $allvariables->{'USELANGUAGECODE'} ) { $languagestring = installer::windows::language::get_windows_language($defaultlanguage); } + my $newdatabasename = $installer::globals::shortmsidatabasename; + $newdatabasename =~ s/\.msi\s*$/_$languagestring\.msi/; + $installer::globals::shortmsidatabasename = $newdatabasename; + $newdatabasename = $installdir . $installer::globals::separator . $newdatabasename; + + my $olddatabasename = $installer::globals::msidatabasename; + + installer::systemactions::rename_one_file($olddatabasename, $newdatabasename); + + $installer::globals::msidatabasename = $newdatabasename; +} + +########################################################################## +# Writing the databasename into the setup.ini. +########################################################################## + +sub put_databasename_into_setupini +{ + my ($setupinifile, $allvariableshashref) = @_; + + my $databasename = get_msidatabasename($allvariableshashref); + my $line = "database=" . $databasename . "\n"; + + push(@{$setupinifile}, $line); +} + +########################################################################## +# Writing the required msi version into setup.ini +########################################################################## + +sub put_msiversion_into_setupini +{ + my ($setupinifile) = @_; + + my $msiversion = "2.0"; + my $line = "msiversion=" . $msiversion . "\n"; + + push(@{$setupinifile}, $line); +} + +########################################################################## +# Writing the productname into setup.ini +########################################################################## + +sub put_productname_into_setupini +{ + my ($setupinifile, $allvariableshashref) = @_; + + my $productname = $allvariableshashref->{'PRODUCTNAME'}; + my $line = "productname=" . $productname . "\n"; + + push(@{$setupinifile}, $line); +} + +########################################################################## +# Writing the productcode into setup.ini +########################################################################## + +sub put_productcode_into_setupini +{ + my ($setupinifile) = @_; + + my $productcode = $installer::globals::productcode; + my $line = "productcode=" . $productcode . "\n"; + + push(@{$setupinifile}, $line); +} + +########################################################################## +# Writing the ProductVersion from Property table into setup.ini +########################################################################## + +sub put_productversion_into_setupini +{ + my ($setupinifile) = @_; + + my $line = "productversion=" . $installer::globals::msiproductversion . "\n"; + push(@{$setupinifile}, $line); +} + +########################################################################## +# Writing the key for Minor Upgrades into setup.ini +########################################################################## + +sub put_upgradekey_into_setupini +{ + my ($setupinifile) = @_; + + if ( $installer::globals::minorupgradekey ne "" ) + { + my $line = "upgradekey=" . $installer::globals::minorupgradekey . "\n"; + push(@{$setupinifile}, $line); + } +} + +########################################################################## +# Writing the number of languages into setup.ini +########################################################################## + +sub put_languagecount_into_setupini +{ + my ($setupinifile, $languagesarray) = @_; + + my $languagecount = $#{$languagesarray} + 1; + my $line = "count=" . $languagecount . "\n"; + + push(@{$setupinifile}, $line); +} + +########################################################################## +# Writing the defaultlanguage into setup.ini +########################################################################## + +sub put_defaultlanguage_into_setupini +{ + my ($setupinifile, $defaultlanguage) = @_; + + my $windowslanguage = installer::windows::language::get_windows_language($defaultlanguage); + my $line = "default=" . $windowslanguage . "\n"; + push(@{$setupinifile}, $line); +} + +########################################################################## +# Writing the information about transformations into setup.ini +########################################################################## + +sub put_transforms_into_setupini +{ + my ($setupinifile, $onelanguage, $counter) = @_; + + my $windowslanguage = installer::windows::language::get_windows_language($onelanguage); + my $transformfilename = "trans_" . $onelanguage . ".mst"; + + my $line = "lang" . $counter . "=" . $windowslanguage . "," . $transformfilename . "\n"; + + push(@{$setupinifile}, $line); +} + +################################################### +# Including Windows line ends in ini files +# Profiles on Windows shall have \r\n line ends +################################################### + +sub include_windows_lineends +{ + my ($onefile) = @_; + + for ( my $i = 0; $i <= $#{$onefile}; $i++ ) + { + ${$onefile}[$i] =~ s/\r?\n$/\r\n/; + } +} + +########################################################################## +# Generation the file setup.ini, that is used by the loader setup.exe. +########################################################################## + +sub create_setup_ini +{ + my ($languagesarray, $defaultlanguage, $installdir, $allvariableshashref) = @_; + + installer::logger::include_header_into_logfile("Creating setup.ini"); + + my $setupinifilename = $installdir . $installer::globals::separator . "setup.ini"; + + my @setupinifile = (); + my $setupinifile = \@setupinifile; + + my $line = "\[setup\]\n"; + push(@setupinifile, $line); + + put_databasename_into_setupini($setupinifile, $allvariableshashref); + put_msiversion_into_setupini($setupinifile); + put_productname_into_setupini($setupinifile, $allvariableshashref); + put_productcode_into_setupini($setupinifile); + put_productversion_into_setupini($setupinifile); + put_upgradekey_into_setupini($setupinifile); + + $line = "\[languages\]\n"; + push(@setupinifile, $line); + + put_languagecount_into_setupini($setupinifile, $languagesarray); + put_defaultlanguage_into_setupini($setupinifile, $defaultlanguage); + + if ( $#{$languagesarray} > 0 ) # writing the transforms information + { + my $counter = 1; + + for ( my $i = 0; $i <= $#{$languagesarray}; $i++ ) + { + if ( ${$languagesarray}[$i] eq $defaultlanguage ) { next; } + + put_transforms_into_setupini($setupinifile, ${$languagesarray}[$i], $counter); + $counter++; + } + } + + if ( $installer::globals::iswin && $installer::globals::plat =~ /cygwin/i) # Windows line ends only for Cygwin + { + include_windows_lineends($setupinifile); + } + + installer::files::save_file($setupinifilename, $setupinifile); + + $infoline = "Generated file $setupinifilename !\n"; + push( @installer::globals::logfileinfo, $infoline); +} + +################################################################# +# Copying the files defined as ScpActions into the +# installation set. +################################################################# + +sub copy_scpactions_into_installset +{ + my ($defaultlanguage, $installdir, $allscpactions) = @_; + + installer::logger::include_header_into_logfile("Copying ScpAction files into installation set"); + + for ( my $i = 0; $i <= $#{$allscpactions}; $i++ ) + { + my $onescpaction = ${$allscpactions}[$i]; + + if ( $onescpaction->{'Name'} eq "loader.exe" ) { next; } # do not copy this ScpAction loader + + # only copying language independent files or files with the correct language (the defaultlanguage) + + my $filelanguage = $onescpaction->{'specificlanguage'}; + + if ( ($filelanguage eq $defaultlanguage) || ($filelanguage eq "") ) + { + my $sourcefile = $onescpaction->{'sourcepath'}; + my $destfile = $installdir . $installer::globals::separator . $onescpaction->{'DestinationName'}; + + installer::systemactions::copy_one_file($sourcefile, $destfile); + } + } +} + +################################################################# +# Copying the files for the Windows installer into the +# installation set (setup.exe). +################################################################# + +sub copy_windows_installer_files_into_installset +{ + my ($installdir, $includepatharrayref, $allvariables) = @_; + + installer::logger::include_header_into_logfile("Copying Windows installer files into installation set"); + + @copyfile = (); + push(@copyfile, "loader2.exe"); + + if ( $allvariables->{'NOLOADERREQUIRED'} ) { @copyfile = (); } + + for ( my $i = 0; $i <= $#copyfile; $i++ ) + { + my $filename = $copyfile[$i]; + my $sourcefileref = installer::scriptitems::get_sourcepath_from_filename_and_includepath(\$filename, $includepatharrayref, 1); + + if ( ! -f $$sourcefileref ) { installer::exiter::exit_program("ERROR: msi file not found: $$sourcefileref !", "copy_windows_installer_files_into_installset"); } + + my $destfile; + if ( $copyfile[$i] eq "loader2.exe" ) { $destfile = "setup.exe"; } # renaming the loader + else { $destfile = $copyfile[$i]; } + + $destfile = $installdir . $installer::globals::separator . $destfile; + + installer::systemactions::copy_one_file($$sourcefileref, $destfile); + } +} + +################################################################# +# 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); + } +} + +################################################################# +# Copying the child projects into the +# installation set +################################################################# + +sub copy_child_projects_into_installset +{ + my ($installdir, $allvariables) = @_; + + my $sourcefile = ""; + my $destdir = ""; + + # adding Java + + if ( $allvariables->{'JAVAPRODUCT'} ) + { + $sourcefile = $installer::globals::javafile->{'sourcepath'}; + $destdir = $installdir . $installer::globals::separator . $installer::globals::javafile->{'Subdir'}; + if ( ! -d $destdir) { installer::systemactions::create_directory($destdir); } + installer::systemactions::copy_one_file($sourcefile, $destdir); + } + + if ( $allvariables->{'UREPRODUCT'} ) + { + $sourcefile = $installer::globals::urefile->{'sourcepath'}; + $destdir = $installdir . $installer::globals::separator . $installer::globals::urefile->{'Subdir'}; + if ( ! -d $destdir) { installer::systemactions::create_directory($destdir); } + installer::systemactions::copy_one_file($sourcefile, $destdir); + } +} + +################################################################# +# 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 = "uuidgen.exe"; # Has to be in the path + + # "-c" for uppercase output + + # my $systemcall = "$uuidgen -n$number -c |"; + 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 $id = pack("A32", $digest); + my ($first, $second, $third, $fourth, $fifth) = unpack ('A8 A4 A4 A4 A12', $digest); + $guid = "$first-$second-$third-$fourth-$fifth"; + + return $guid; +} + +################################################################# +# Filling the component hash with the values of the +# component file. +################################################################# + +sub fill_component_hash +{ + my ($componentfile) = @_; + + my %components = (); + + for ( my $i = 0; $i <= $#{$componentfile}; $i++ ) + { + my $line = ${$componentfile}[$i]; + + if ( $line =~ /^\s*(.*?)\t(.*?)\s*$/ ) + { + my $key = $1; + my $value = $2; + + $components{$key} = $value; + } + } + + return \%components; +} + +################################################################# +# Creating a new component file, if new guids were generated. +################################################################# + +sub create_new_component_file +{ + my ($componenthash) = @_; + + my @componentfile = (); + + my $key; + + foreach $key (keys %{$componenthash}) + { + my $value = $componenthash->{$key}; + my $input = "$key\t$value\n"; + push(@componentfile ,$input); + } + + return \@componentfile; +} + +################################################################# +# 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; + # my $componentfile = installer::files::read_file($installer::globals::componentfilename); + # my $componenthash = fill_component_hash($componentfile); + + 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 ( $componenthash->{$componentname} ) + # { + # $uuid = $componenthash->{$componentname}; + # } + # else + # { + + 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. + my $useooobaseversion = 1; + if ( exists($installer::globals::base_independent_components{$componentname})) { $useooobaseversion = 0; } + my $sourcestring = $componentname; + + if ( $useooobaseversion ) + { + if ( ! exists($allvariables->{'OOOBASEVERSION'}) ) { installer::exiter::exit_program("ERROR: Could not find variable \"OOOBASEVERSION\" (required value for GUID creation)!", "set_uuid_into_component_table"); } + $sourcestring = $sourcestring . "_" . $allvariables->{'OOOBASEVERSION'}; + } + $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; + + # Setting new uuid + # $componenthash->{$componentname} = $uuid; + + # Setting flag + # $installer::globals::created_new_component_guid = 1; # this is very important! + } + # } + + ${$componenttable}[$i] =~ s/COMPONENTGUID/$uuid/; + } + + installer::files::save_file($componenttablename, $componenttable); + +# if ( $installer::globals::created_new_component_guid ) +# { +# # create new component file! +# $componentfile = create_new_component_file($componenthash); +# installer::worker::sort_array($componentfile); +# +# # To avoid conflict the components file cannot be saved at the same place +# # All important data have to be saved in the directory: $installer::globals::infodirectory +# my $localcomponentfilename = $installer::globals::componentfilename; +# installer::pathanalyzer::make_absolute_filename_to_relative_filename(\$localcomponentfilename); +# $localcomponentfilename = $installer::globals::infodirectory . $installer::globals::separator . $localcomponentfilename; +# installer::files::save_file($localcomponentfilename, $componentfile); +# +# # installer::files::save_file($installer::globals::componentfilename, $componentfile); # version using new file in solver +# +# $infoline = "COMPONENTCODES: Created $counter new GUIDs for components ! \n"; +# push( @installer::globals::logfileinfo, $infoline); +# } +# else +# { +# $infoline = "SUCCESS COMPONENTCODES: All component codes exist! \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.) + $idtdirbase =~ s/\//\\\\/g; + $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); + + # if the ddf file contains relative pathes, it is necessary to change into the temp directory + if ( $allvariables->{'RELATIVE_PATHES_IN_DDF'} ) + { + $to = $installer::globals::temppath; + chdir($to); + $infoline = "chdir: $to \n"; + push( @installer::globals::logfileinfo, $infoline); + } + + # changing the tmp directory, because makecab.exe generates temporary cab files + my $origtemppath = ""; + if ( $ENV{'TMP'} ) { $origtemppath = $ENV{'TMP'}; } + $ENV{'TMP'} = $installer::globals::temppath; # setting TMP to the new unique directory! + + 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" ); + + # my $returnvalue = system($systemcall); + + 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 $j = 0; $j <= $#ddfoutput; $j++ ) { push( @installer::globals::logfileinfo, "$ddfoutput[$j]"); } + + 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 + { + # installer::logger::print_message( "Success (Try $n): \"$systemcall\"\n" ); + $infoline = "Success (Try $n): $systemcall"; + push( @installer::globals::logfileinfo, $infoline); + last; + } + } + } + + installer::logger::include_timestamp_into_logfile("Performance Info: Execute packaging end"); + + # setting back to the original tmp directory + $ENV{'TMP'} = $origtemppath; + + 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 $isopensource = 0; + if ( $allvariableshashref->{'OPENSOURCE'} ) { $isopensource = $allvariableshashref->{'OPENSOURCE'}; } + + my $onelanguage = ""; + + if ( $#{$languagesref} > 0 ) # more than one language + { + 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] . "\}"; + } + + if ( $installer::globals::patch ) # patch upgrade codes are defined in soffice.lst + { + if ( $allvariableshashref->{'PATCHUPGRADECODE'} ) { $installer::globals::upgradecode = $allvariableshashref->{'PATCHUPGRADECODE'}; } + else { installer::exiter::exit_program("ERROR: PATCHUPGRADECODE not defined in list file!", "set_global_code_variables"); } + } + else + { + # 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::productcode eq "" ) && ( ! $isopensource )) { installer::exiter::exit_program("ERROR: ProductCode for language $onelanguage not defined in $installer::globals::codefilename !", "set_global_code_variables"); } + 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->{'PRODUCTVERSION'}; + + if (( $productversion =~ /^\s*\d+\s*$/ ) && ( $productversion > 255 )) { $productversion = $productversion%256; } + + if ( $productversion =~ /^\s*(\d+)\.(\d+)\.(\d+)\s*$/ ) + { + $productversion = $1 . "\." . $2 . $3 . "\." . $installer::globals::buildid; + } + elsif ( $productversion =~ /^\s*(\d+)\.(\d+)\s*$/ ) + { + $productversion = $1 . "\." . $2 . "\." . $installer::globals::buildid; + } + else + { + my $productminor = "00"; + if (( $allvariables->{'PACKAGEVERSION'} ) && ( $allvariables->{'PACKAGEVERSION'} ne "" )) + { + if ( $allvariables->{'PACKAGEVERSION'} =~ /^\s*(\d+)\.(\d+)\.(\d+)\s*$/ ) { $productminor = $2; } + } + + $productversion = $productversion . "\." . $productminor . "\." . $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_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_occured = 0; + my $file_error_occured = 0; + my $dir_error = 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_occured ) + { + $errorstring = "\nErrors in $idtfile: \n"; + push(@errorlines, $errorstring); + } + $errorstring = "Duplicate savedmapping{" . "$2/$5}\n"; + push(@errorlines, $errorstring); + $error_occured = 1; + $file_error_occured = 1; + } + + if ( exists($installer::globals::savedrevmapping{$lc1})) + { + if ( ! $file_error_occured ) + { + $errorstring = "\nErrors in $idtfile: \n"; + push(@errorlines, $errorstring); + } + $errorstring = "Duplicate savedrevmapping{" . "$lc1}\n"; + push(@errorlines, $errorstring); + $error_occured = 1; + $file_error_occured = 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_occured ) + { + $errorstring = "\nErrors in $idtfile: \n"; + push(@errorlines, $errorstring); + } + $errorstring = "Duplicate savedrev83mapping{" . "$shortname}\n"; + push(@errorlines, $errorstring); + $error_occured = 1; + $file_error_occured = 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_occured ) + { + $errorstring = "\nErrors in $idtfile: \n"; + push(@errorlines, $errorstring); + } + $errorstring = "Duplicate saved83dirmapping{" . "$1}\n"; + push(@errorlines, $errorstring); + $error_occured = 1; + $dir_error_occured = 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_occured ) + { + 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 { + # push( @installer::globals::globallogfileinfo, "WARNING: Windows patch shall be prepared, but PREVIOUS_IDT_DIR is not set!\n" ); + 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; + diff --git a/solenv/bin/modules/installer/windows/msp.pm b/solenv/bin/modules/installer/windows/msp.pm new file mode 100644 index 000000000000..0a5a35f05693 --- /dev/null +++ b/solenv/bin/modules/installer/windows/msp.pm @@ -0,0 +1,1297 @@ +#************************************************************************* +# +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# Copyright 2008 by Sun Microsystems, Inc. +# +# OpenOffice.org - a multi-platform office productivity suite +# +# $RCSfile: msp.pm,v $ +# +# $Revision: 1.1.2.4 $ +# +# This file is part of OpenOffice.org. +# +# OpenOffice.org is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License version 3 +# only, as published by the Free Software Foundation. +# +# OpenOffice.org is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License version 3 for more details +# (a copy is included in the LICENSE file that accompanied this code). +# +# You should have received a copy of the GNU Lesser General Public License +# version 3 along with OpenOffice.org. If not, see +# <http://www.openoffice.org/license.html> +# for a copy of the LGPLv3 License. +# +#************************************************************************* + +package installer::windows::msp; + +use installer::control; +use installer::converter; +use installer::exiter; +use installer::files; +use installer::globals; +use installer::logger; +use installer::pathanalyzer; +use installer::systemactions; +use installer::windows::admin; +use installer::windows::idtglobal; +use installer::windows::update; + +################################################################################# +# Making all required administrative installations +################################################################################# + +sub install_installation_sets +{ + my ($installationdir) = @_; + + # Finding the msi database in the new installation set, that is located in $installationdir + + my $msifiles = installer::systemactions::find_file_with_file_extension("msi", $installationdir); + + if ( $#{$msifiles} < 0 ) { installer::exiter::exit_program("ERROR: Did not find msi database in directory $installationdir", "create_msp_patch"); } + if ( $#{$msifiles} > 0 ) { installer::exiter::exit_program("ERROR: Did find more than one msi database in directory $installationdir", "create_msp_patch"); } + + my $newinstallsetdatabasepath = $installationdir . $installer::globals::separator . ${$msifiles}[0]; + my $oldinstallsetdatabasepath = $installer::globals::updatedatabasepath; + + # Creating temp directory again + installer::systemactions::create_directory_structure($installer::globals::temppath); + + # Creating old installation directory + my $dirname = "admin"; + my $installpath = $installer::globals::temppath . $installer::globals::separator . $dirname; + if ( ! -d $installpath) { installer::systemactions::create_directory($installpath); } + + my $oldinstallpath = $installpath . $installer::globals::separator . "old"; + my $newinstallpath = $installpath . $installer::globals::separator . "new"; + + if ( ! -d $oldinstallpath) { installer::systemactions::create_directory($oldinstallpath); } + if ( ! -d $newinstallpath) { installer::systemactions::create_directory($newinstallpath); } + + my $olddatabase = installer::windows::admin::make_admin_install($oldinstallsetdatabasepath, $oldinstallpath); + my $newdatabase = installer::windows::admin::make_admin_install($newinstallsetdatabasepath, $newinstallpath); + + if ( $^O =~ /cygwin/i ) { + $olddatabase = qx{cygpath -w "$olddatabase"}; + $olddatabase =~ s/\s*$//g; + $newdatabase = qx{cygpath -w "$newdatabase"}; + $newdatabase =~ s/\s*$//g; + } + + return ($olddatabase, $newdatabase); +} + +################################################################################# +# Extracting all tables from a pcp file +################################################################################# + +sub extract_all_tables_from_pcpfile +{ + my ($fullpcpfilepath, $workdir) = @_; + + my $msidb = "msidb.exe"; # Has to be in the path + my $infoline = ""; + my $systemcall = ""; + my $returnvalue = ""; + my $extraslash = ""; # Has to be set for non-ActiveState perl + + my $localfullpcpfile = $fullpcpfilepath; + my $localworkdir = $workdir; + + if ( $^O =~ /cygwin/i ) { + # msidb.exe really wants backslashes. (And double escaping because system() expands the string.) + $localfullpcpfile =~ s/\//\\\\/g; + $localworkdir =~ s/\//\\\\/g; + $extraslash = "\\"; + } + + # Export of all tables by using "*" + + $systemcall = $msidb . " -d " . $localfullpcpfile . " -f " . $localworkdir . " -e " . $extraslash . "*"; + $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); + installer::exiter::exit_program("ERROR: Could not exclude tables from pcp file: $fullpcpfilepath !", "extract_all_tables_from_msidatabase"); + } + else + { + $infoline = "Success: Executed $systemcall successfully!\n"; + push( @installer::globals::logfileinfo, $infoline); + } +} + +################################################################################# +# Include tables into a pcp file +################################################################################# + +sub include_tables_into_pcpfile +{ + my ($fullpcpfilepath, $workdir, $tables) = @_; + + my $msidb = "msidb.exe"; # Has to be in the path + my $infoline = ""; + my $systemcall = ""; + my $returnvalue = ""; + + # Make all table 8+3 conform + my $alltables = installer::converter::convert_stringlist_into_array(\$tables, " "); + + for ( my $i = 0; $i <= $#{$alltables}; $i++ ) + { + my $tablename = ${$alltables}[$i]; + $tablename =~ s/\s*$//; + my $namelength = length($tablename); + if ( $namelength > 8 ) + { + my $newtablename = substr($tablename, 0, 8); # name, offset, length + my $oldfile = $workdir . $installer::globals::separator . $tablename . ".idt"; + my $newfile = $workdir . $installer::globals::separator . $newtablename . ".idt"; + if ( -f $newfile ) { unlink $newfile; } + installer::systemactions::copy_one_file($oldfile, $newfile); + } + } + + # Import of tables + + my $localworkdir = $workdir; + my $localfullpcpfilepath = $fullpcpfilepath; + + if ( $^O =~ /cygwin/i ) { + # msidb.exe really wants backslashes. (And double escaping because system() expands the string.) + $localfullpcpfilepath =~ s/\//\\\\/g; + $localworkdir =~ s/\//\\\\/g; + } + + $systemcall = $msidb . " -d " . $localfullpcpfilepath . " -f " . $localworkdir . " -i " . $tables; + + $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); + installer::exiter::exit_program("ERROR: Could not include tables into pcp file: $fullpcpfilepath !", "include_tables_into_pcpfile"); + } + else + { + $infoline = "Success: Executed $systemcall successfully!\n"; + push( @installer::globals::logfileinfo, $infoline); + } +} + +################################################################################# +# Calling msimsp.exe +################################################################################# + +sub execute_msimsp +{ + my ($fullpcpfilename, $mspfilename, $localmspdir) = @_; + + my $msimsp = "msimsp.exe"; # Has to be in the path + my $infoline = ""; + my $systemcall = ""; + my $returnvalue = ""; + my $logfilename = $localmspdir . $installer::globals::separator . "msimsp.log"; + + # Using a specific temp for each msimsp.exe process + # Creating temp directory again (should already have happened) + installer::systemactions::create_directory_structure($installer::globals::temppath); + + # Creating old installation directory + my $dirname = "msimsptemp"; + my $msimsptemppath = $installer::globals::temppath . $installer::globals::separator . $dirname; + if ( ! -d $msimsptemppath) { installer::systemactions::create_directory($msimsptemppath); } + + # r:\msvc9p\PlatformSDK\v6.1\bin\msimsp.exe -s c:\patch\hotfix_qfe1.pcp -p c:\patch\patch_ooo3_m2_m3.msp -l c:\patch\patch_ooo3_m2_m3.log + + if ( -f $logfilename ) { unlink $logfilename; } + + my $localfullpcpfilename = $fullpcpfilename; + my $localmspfilename = $mspfilename; + my $locallogfilename = $logfilename; + my $localmsimsptemppath = $msimsptemppath; + + if ( $^O =~ /cygwin/i ) { + # msimsp.exe really wants backslashes. (And double escaping because system() expands the string.) + $localfullpcpfilename =~ s/\//\\\\/g; + $locallogfilename =~ s/\//\\\\/g; + + $localmspfilename =~ s/\\/\\\\/g; # path already contains backslash + # $localmspfilename =~ s/\//\\\\/g; + + $localmsimsptemppath = qx{cygpath -w "$localmsimsptemppath"}; + $localmsimsptemppath =~ s/\\/\\\\/g; + $localmsimsptemppath =~ s/\s*$//g; + } + + $systemcall = $msimsp . " -s " . $localfullpcpfilename . " -p " . $localmspfilename . " -l " . $locallogfilename . " -f " . $localmsimsptemppath; + installer::logger::print_message( "... $systemcall ...\n" ); + + $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); + installer::exiter::exit_program("ERROR: Could not execute $systemcall !", "execute_msimsp"); + } + else + { + $infoline = "Success: Executed $systemcall successfully!\n"; + push( @installer::globals::logfileinfo, $infoline); + } + + return $logfilename; +} + +#################################################################### +# Checking existence and saving all tables, that need to be edited +#################################################################### + +sub check_and_save_tables +{ + my ($tablelist, $workdir) = @_; + + my $tables = installer::converter::convert_stringlist_into_array(\$tablelist, " "); + + for ( my $i = 0; $i <= $#{$tables}; $i++ ) + { + my $filename = ${$tables}[$i]; + $filename =~ s/\s*$//; + my $fullfilename = $workdir . $installer::globals::separator . $filename . ".idt"; + + if ( ! -f $fullfilename ) { installer::exiter::exit_program("ERROR: Required idt file could not be found: \"$fullfilename\"!", "check_and_save_tables"); } + + my $savfilename = $fullfilename . ".sav"; + installer::systemactions::copy_one_file($fullfilename, $savfilename); + } +} + +#################################################################### +# Setting the languages for the service packs +#################################################################### + +sub create_langstring +{ + my ( $languagesarrayref ) = @_; + + my $langstring = ""; + for ( my $i = 0; $i <= $#{$languagesarrayref}; $i++ ) { $langstring = $langstring . "_" . ${$languagesarrayref}[$i]; } + + return $langstring; +} + +#################################################################### +# Setting the name of the msp database +#################################################################### + +sub set_mspfilename +{ + my ($allvariables, $mspdir, $languagesarrayref) = @_; + + my $databasename = $allvariables->{'PRODUCTNAME'}; + $databasename = lc($databasename); + $databasename =~ s/\.//g; + $databasename =~ s/\-//g; + $databasename =~ s/\s//g; + + if ( $allvariables->{'MSPPRODUCTVERSION'} ) { $databasename = $databasename . $allvariables->{'MSPPRODUCTVERSION'}; } + + # possibility to overwrite the name with variable DATABASENAME + # if ( $allvariables->{'DATABASENAME'} ) { $databasename = $allvariables->{'DATABASENAME'}; } + + # Adding patch info to database name + # if ( $installer::globals::buildid ) { $databasename = $databasename . "_" . $installer::globals::buildid; } + + # if ( $allvariables->{'VENDORPATCHVERSION'} ) { $databasename = $databasename . "_" . $allvariables->{'VENDORPATCHVERSION'}; } + + + if (( $allvariables->{'SERVICEPACK'} ) && ( $allvariables->{'SERVICEPACK'} == 1 )) + { + my $windowspatchlevel = 0; + if ( $allvariables->{'MSPPATCHLEVEL'} ) { $windowspatchlevel = $allvariables->{'MSPPATCHLEVEL'}; } + $databasename = $databasename . "_servicepack_" . $windowspatchlevel; + my $languagestring = create_langstring($languagesarrayref); + $databasename = $databasename . $languagestring; + } + else + { + my $hotfixaddon = "hotfix_"; + $hotfixaddon = $hotfixaddon . $installer::globals::buildid; + my $cwsname = ""; + if ( $ENV{'CWS_WORK_STAMP'} ) { $hotfixaddon = $ENV{'CWS_WORK_STAMP'}; } + if ( $allvariables->{'OVERWRITE_CWSNAME'} ) { $hotfixaddon = $allvariables->{'OVERWRITE_CWSNAME'}; } + $databasename = $databasename . "_" . $hotfixaddon; + } + + $databasename = $databasename . ".msp"; + + my $fullmspname = $mspdir . $installer::globals::separator . $databasename; + + if ( $^O =~ /cygwin/i ) { $fullmspname =~ s/\//\\/g; } + + return $fullmspname; +} + +#################################################################### +# Editing table Properties +#################################################################### + +sub change_properties_table +{ + my ($localmspdir, $mspfilename) = @_; + + my $infoline = "Changing content of table \"Properties\"\n"; + push( @installer::globals::logfileinfo, $infoline); + + my $filename = $localmspdir . $installer::globals::separator . "Properties.idt"; + if ( ! -f $filename ) { installer::exiter::exit_program("ERROR: Could not find file \"$filename\" !", "change_properties_table"); } + + my $filecontent = installer::files::read_file($filename); + + + my $guidref = installer::windows::msiglobal::get_guid_list(1, 1); + ${$guidref}[0] =~ s/\s*$//; # removing ending spaces + my $patchcode = "\{" . ${$guidref}[0] . "\}"; + + # Setting "PatchOutputPath" + my $found_patchoutputpath = 0; + my $found_patchguid = 0; + + for ( my $i = 0; $i <= $#{$filecontent}; $i++ ) + { + if ( ${$filecontent}[$i] =~ /^\s*PatchOutputPath\t(.*?)\s*$/ ) + { + my $oldvalue = $1; + ${$filecontent}[$i] =~ s/\Q$oldvalue\E/$mspfilename/; + $found_patchoutputpath = 1; + } + + if ( ${$filecontent}[$i] =~ /^\s*PatchGUID\t(.*?)\s*$/ ) + { + my $oldvalue = $1; + ${$filecontent}[$i] =~ s/\Q$oldvalue\E/$patchcode/; + $found_patchguid = 1; + } + } + + if ( ! $found_patchoutputpath ) + { + my $newline = "PatchOutputPath\t$mspfilename\n"; + push(@{$filecontent}, $newline); + } + + if ( ! $found_patchguid ) + { + my $newline = "PatchGUID\t$patchcode\n"; + push(@{$filecontent}, $newline); + } + + # saving file + installer::files::save_file($filename, $filecontent); +} + +#################################################################### +# Editing table TargetImages +#################################################################### + +sub change_targetimages_table +{ + my ($localmspdir, $olddatabase) = @_; + + my $infoline = "Changing content of table \"TargetImages\"\n"; + push( @installer::globals::logfileinfo, $infoline); + + my $filename = $localmspdir . $installer::globals::separator . "TargetImages.idt"; + if ( ! -f $filename ) { installer::exiter::exit_program("ERROR: Could not find file \"$filename\" !", "change_targetimages_table"); } + + my $filecontent = installer::files::read_file($filename); + my @newcontent = (); + + # Copying the header + for ( my $i = 0; $i <= $#{$filecontent}; $i++ ) { if ( $i < 3 ) { push(@newcontent, ${$filecontent}[$i]); } } + + #Adding all targets + my $newline = "T1\t$olddatabase\t\tU1\t1\t0x00000922\t1\n"; + push(@newcontent, $newline); + + # saving file + installer::files::save_file($filename, \@newcontent); +} + +#################################################################### +# Editing table UpgradedImages +#################################################################### + +sub change_upgradedimages_table +{ + my ($localmspdir, $newdatabase) = @_; + + my $infoline = "Changing content of table \"UpgradedImages\"\n"; + push( @installer::globals::logfileinfo, $infoline); + + my $filename = $localmspdir . $installer::globals::separator . "UpgradedImages.idt"; + if ( ! -f $filename ) { installer::exiter::exit_program("ERROR: Could not find file \"$filename\" !", "change_upgradedimages_table"); } + + my $filecontent = installer::files::read_file($filename); + my @newcontent = (); + + # Copying the header + for ( my $i = 0; $i <= $#{$filecontent}; $i++ ) { if ( $i < 3 ) { push(@newcontent, ${$filecontent}[$i]); } } + + # Syntax: Upgraded MsiPath PatchMsiPath SymbolPaths Family + + # default values + my $upgraded = "U1"; + my $msipath = $newdatabase; + my $patchmsipath = ""; + my $symbolpaths = ""; + my $family = "22334455"; + + if ( $#{$filecontent} >= 3 ) + { + my $line = ${$filecontent}[3]; + if ( $line =~ /^\s*(.*?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\s*$/ ) + { + $upgraded = $1; + $patchmsipath = $3; + $symbolpaths = $4; + $family = $5; + } + } + + #Adding sequence line, saving PatchFamily + my $newline = "$upgraded\t$msipath\t$patchmsipath\t$symbolpaths\t$family\n"; + push(@newcontent, $newline); + + # saving file + installer::files::save_file($filename, \@newcontent); +} + +#################################################################### +# Editing table ImageFamilies +#################################################################### + +sub change_imagefamilies_table +{ + my ($localmspdir) = @_; + + my $infoline = "Changing content of table \"ImageFamilies\"\n"; + push( @installer::globals::logfileinfo, $infoline); + + my $filename = $localmspdir . $installer::globals::separator . "ImageFamilies.idt"; + if ( ! -f $filename ) { installer::exiter::exit_program("ERROR: Could not find file \"$filename\" !", "change_imagefamilies_table"); } + + my $filecontent = installer::files::read_file($filename); + my @newcontent = (); + + # Copying the header + for ( my $i = 0; $i <= $#{$filecontent}; $i++ ) { if ( $i < 3 ) { push(@newcontent, ${$filecontent}[$i]); } } + + # Syntax: Family MediaSrcPropName MediaDiskId FileSequenceStart DiskPrompt VolumeLabel + # "FileSequenceStart has to be set + + # Default values: + + my $family = "22334455"; + my $mediasrcpropname = "MediaSrcPropName"; + my $mediadiskid = "2"; + my $filesequencestart = get_filesequencestart(); + my $diskprompt = ""; + my $volumelabel = ""; + + if ( $#{$filecontent} >= 3 ) + { + my $line = ${$filecontent}[3]; + if ( $line =~ /^\s*(.*?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\s*$/ ) + { + $family = $1; + $mediasrcpropname = $2; + $mediadiskid = $3; + $diskprompt = $5; + $volumelabel = $6; + } + } + + #Adding sequence line + my $newline = "$family\t$mediasrcpropname\t$mediadiskid\t$filesequencestart\t$diskprompt\t$volumelabel\n"; + push(@newcontent, $newline); + + # saving file + installer::files::save_file($filename, \@newcontent); +} + +#################################################################### +# Setting start sequence for patch +#################################################################### + +sub get_filesequencestart +{ + my $sequence = 1000; # default + + if ( $installer::globals::updatelastsequence ) { $sequence = $installer::globals::updatelastsequence + 500; } + + return $sequence; +} + +#################################################################### +# Setting time value into pcp file +# Format mm/dd/yyyy hh:mm +#################################################################### + +sub get_patchtime_value +{ + # Syntax: 8/8/2008 11:55 + my $minute = (localtime())[1]; + my $hour = (localtime())[2]; + my $day = (localtime())[3]; + my $month = (localtime())[4]; + my $year = 1900 + (localtime())[5]; + + $month++; # zero based month + if ( $minute < 10 ) { $minute = "0" . $minute; } + if ( $hour < 10 ) { $hour = "0" . $hour; } + + my $timestring = $month . "/" . $day . "/" . $year . " " . $hour . ":" . $minute; + + return $timestring; +} + +################################################################################# +# Checking, if this is the correct database. +################################################################################# + +sub correct_langs +{ + my ($langs, $languagestringref) = @_; + + my $correct_langs = 0; + + # Comparing $langs with $languagestringref + + my $langlisthash = installer::converter::convert_stringlist_into_hash(\$langs, ","); + my $langstringhash = installer::converter::convert_stringlist_into_hash($languagestringref, "_"); + + my $not_included = 0; + foreach my $onelang ( keys %{$langlisthash} ) + { + if ( ! exists($langstringhash->{$onelang}) ) + { + $not_included = 1; + last; + } + } + + if ( ! $not_included ) + { + foreach my $onelanguage ( keys %{$langstringhash} ) + { + if ( ! exists($langlisthash->{$onelanguage}) ) + { + $not_included = 1; + last; + } + } + + if ( ! $not_included ) { $correct_langs = 1; } + } + + return $correct_langs; +} + +################################################################################# +# Searching for the path to the reference database for this special product. +################################################################################# + +sub get_patchid_from_list +{ + my ($filecontent, $languagestringref, $filename) = @_; + + my $patchid = ""; + + for ( my $i = 0; $i <= $#{$filecontent}; $i++ ) + { + my $line = ${$filecontent}[$i]; + if ( $line =~ /^\s*$/ ) { next; } # empty line + if ( $line =~ /^\s*\#/ ) { next; } # comment line + + if ( $line =~ /^\s*(.+?)\s*=\s*(.+?)\s*$/ ) + { + my $langs = $1; + my $localpatchid = $2; + + if ( correct_langs($langs, $languagestringref) ) + { + $patchid = $localpatchid; + last; + } + } + else + { + installer::exiter::exit_program("ERROR: Wrong syntax in file: $filename! Line: \"$line\"", "get_patchid_from_list"); + } + } + + return $patchid; +} + +#################################################################### +# Editing table PatchMetadata +#################################################################### + +sub change_patchmetadata_table +{ + my ($localmspdir, $allvariables, $languagestringref) = @_; + + my $infoline = "Changing content of table \"PatchMetadata\"\n"; + push( @installer::globals::logfileinfo, $infoline); + + my $filename = $localmspdir . $installer::globals::separator . "PatchMetadata.idt"; + if ( ! -f $filename ) { installer::exiter::exit_program("ERROR: Could not find file \"$filename\" !", "change_patchmetadata_table"); } + + my $filecontent = installer::files::read_file($filename); + my @newcontent = (); + + # Syntax: Company Property Value + # Interesting properties: "Classification" and "CreationTimeUTC" + + my $classification_set = 0; + my $creationtime_set = 0; + my $targetproductname_set = 0; + my $manufacturer_set = 0; + my $displayname_set = 0; + my $description_set = 0; + my $allowremoval_set = 0; + + my $defaultcompany = ""; + + my $classificationstring = "Classification"; + my $classificationvalue = "Hotfix"; + if (( $allvariables->{'SERVICEPACK'} ) && ( $allvariables->{'SERVICEPACK'} == 1 )) { $classificationvalue = "ServicePack"; } + + my $allowremovalstring = "AllowRemoval"; + my $allowremovalvalue = "1"; + if (( exists($allvariables->{'MSPALLOWREMOVAL'}) ) && ( $allvariables->{'MSPALLOWREMOVAL'} == 0 )) { $allowremovalvalue = 0; } + + my $timestring = "CreationTimeUTC"; + # Syntax: 8/8/2008 11:55 + my $timevalue = get_patchtime_value(); + + my $targetproductnamestring = "TargetProductName"; + my $targetproductnamevalue = $allvariables->{'PRODUCTNAME'}; + if ( $allvariables->{'PROPERTYTABLEPRODUCTNAME'} ) { $targetproductnamevalue = $allvariables->{'PROPERTYTABLEPRODUCTNAME'}; } + + my $manufacturerstring = "ManufacturerName"; + my $manufacturervalue = "OpenOffice.org"; + if ( $installer::globals::longmanufacturer ) { $manufacturervalue = $installer::globals::longmanufacturer; } + + my $displaynamestring = "DisplayName"; + my $descriptionstring = "Description"; + my $displaynamevalue = ""; + my $descriptionvalue = ""; + + my $base = $allvariables->{'PRODUCTNAME'} . " " . $allvariables->{'PRODUCTVERSION'}; + if ( $installer::globals::languagepack ) { $base = $targetproductnamevalue; } + + my $windowspatchlevel = 0; + if ( $allvariables->{'WINDOWSPATCHLEVEL'} ) { $windowspatchlevel = $allvariables->{'WINDOWSPATCHLEVEL'}; } + + my $displayaddon = ""; + if ( $allvariables->{'PATCHDISPLAYADDON'} ) { $displayaddon = $allvariables->{'PATCHDISPLAYADDON'}; } + + my $cwsname = ""; + if ( $ENV{'CWS_WORK_STAMP'} ) { $cwsname = $ENV{'CWS_WORK_STAMP'}; } + if (( $cwsname ne "" ) && ( $allvariables->{'OVERWRITE_CWSNAME'} )) { $cwsname = $allvariables->{'OVERWRITE_CWSNAME'}; } + + my $patchsequence = get_patchsequence($allvariables); + + if (( $allvariables->{'SERVICEPACK'} ) && ( $allvariables->{'SERVICEPACK'} == 1 )) + { + $displaynamevalue = $base . " ServicePack " . $windowspatchlevel . " " . $patchsequence . " Build: " . $installer::globals::buildid; + $descriptionvalue = $base . " ServicePack " . $windowspatchlevel . " " . $patchsequence . " Build: " . $installer::globals::buildid; + } + else + { + $displaynamevalue = $base . " Hotfix " . $cwsname . " " . $displayaddon . " " . $patchsequence . " Build: " . $installer::globals::buildid; + $descriptionvalue = $base . " Hotfix " . $cwsname . " " . $displayaddon . " " . $patchsequence . " Build: " . $installer::globals::buildid; + $displaynamevalue =~ s/ / /g; + $descriptionvalue =~ s/ / /g; + $displaynamevalue =~ s/ / /g; + $descriptionvalue =~ s/ / /g; + $displaynamevalue =~ s/ / /g; + $descriptionvalue =~ s/ / /g; + } + + if ( $allvariables->{'MSPPATCHNAMELIST'} ) + { + my $patchnamelistfile = $allvariables->{'MSPPATCHNAMELIST'}; + $patchnamelistfile = $installer::globals::idttemplatepath . $installer::globals::separator . $patchnamelistfile; + if ( ! -f $patchnamelistfile ) { installer::exiter::exit_program("ERROR: Could not find file \"$patchnamelistfile\".", "change_patchmetadata_table"); } + my $filecontent = installer::files::read_file($patchnamelistfile); + + # Get name and path of reference database + my $patchid = get_patchid_from_list($filecontent, $languagestringref, $patchnamelistfile); + + if ( $patchid eq "" ) { installer::exiter::exit_program("ERROR: Could not find file patchid in file \"$patchnamelistfile\" for language(s) \"$$languagestringref\".", "change_patchmetadata_table"); } + + # Setting language specific patch id + } + + for ( my $i = 0; $i <= $#{$filecontent}; $i++ ) + { + if ( ${$filecontent}[$i] =~ /^\s*(.*?)\t(.*?)\t(.*?)\s*$/ ) + { + my $company = $1; + my $property = $2; + my $value = $3; + + if ( $property eq $classificationstring ) + { + ${$filecontent}[$i] = "$company\t$property\t$classificationvalue\n"; + $classification_set = 1; + } + + if ( $property eq $allowremovalstring ) + { + ${$filecontent}[$i] = "$company\t$property\t$allowremovalvalue\n"; + $allowremoval_set = 1; + } + + if ( $property eq $timestring ) + { + ${$filecontent}[$i] = "$company\t$property\t$timevalue\n"; + $creationtime_set = 1; + } + + if ( $property eq $targetproductnamestring ) + { + ${$filecontent}[$i] = "$company\t$property\t$targetproductnamevalue\n"; + $targetproductname_set = 1; + } + + if ( $property eq $manufacturerstring ) + { + ${$filecontent}[$i] = "$company\t$property\t$manufacturervalue\n"; + $manufacturer_set = 1; + } + + if ( $property eq $displaynamestring ) + { + ${$filecontent}[$i] = "$company\t$property\t$displaynamevalue\n"; + $displayname_set = 1; + } + + if ( $property eq $descriptionstring ) + { + ${$filecontent}[$i] = "$company\t$property\t$descriptionvalue\n"; + $description_set = 1; + } + } + + push(@newcontent, ${$filecontent}[$i]); + } + + if ( ! $classification_set ) + { + my $line = "$defaultcompany\t$classificationstring\t$classificationvalue\n"; + push(@newcontent, $line); + } + + if ( ! $allowremoval_set ) + { + my $line = "$defaultcompany\t$classificationstring\t$allowremovalvalue\n"; + push(@newcontent, $line); + } + + if ( ! $allowremoval_set ) + { + my $line = "$defaultcompany\t$classificationstring\t$allowremovalvalue\n"; + push(@newcontent, $line); + } + + if ( ! $creationtime_set ) + { + my $line = "$defaultcompany\t$timestring\t$timevalue\n"; + push(@newcontent, $line); + } + + if ( ! $targetproductname_set ) + { + my $line = "$defaultcompany\t$targetproductnamestring\t$targetproductnamevalue\n"; + push(@newcontent, $line); + } + + if ( ! $manufacturer_set ) + { + my $line = "$defaultcompany\t$manufacturerstring\t$manufacturervalue\n"; + push(@newcontent, $line); + } + + if ( ! $displayname_set ) + { + my $line = "$defaultcompany\t$displaynamestring\t$displaynamevalue\n"; + push(@newcontent, $line); + } + + if ( ! $description_set ) + { + my $line = "$defaultcompany\t$descriptionstring\t$descriptionvalue\n"; + push(@newcontent, $line); + } + + # saving file + installer::files::save_file($filename, \@newcontent); +} + +#################################################################### +# Editing table PatchSequence +#################################################################### + +sub change_patchsequence_table +{ + my ($localmspdir, $allvariables) = @_; + + my $infoline = "Changing content of table \"PatchSequence\"\n"; + push( @installer::globals::logfileinfo, $infoline); + + my $filename = $localmspdir . $installer::globals::separator . "PatchSequence.idt"; + if ( ! -f $filename ) { installer::exiter::exit_program("ERROR: Could not find file \"$filename\" !", "change_patchsequence_table"); } + + my $filecontent = installer::files::read_file($filename); + my @newcontent = (); + + # Copying the header + for ( my $i = 0; $i <= $#{$filecontent}; $i++ ) { if ( $i < 3 ) { push(@newcontent, ${$filecontent}[$i]); } } + + # Syntax: PatchFamily Target Sequence Supersede + + my $patchfamily = "SO"; + my $target = ""; + my $patchsequence = get_patchsequence($allvariables); + my $supersede = get_supersede($allvariables); + + if ( $#{$filecontent} >= 3 ) + { + my $line = ${$filecontent}[3]; + if ( $line =~ /^\s*(.*?)\t(.*?)\t(.*?)\t(.*?)\s$/ ) + { + $patchfamily = $1; + $target = $2; + } + } + + #Adding sequence line, saving PatchFamily + my $newline = "$patchfamily\t$target\t$patchsequence\t$supersede\n"; + push(@newcontent, $newline); + + # saving file + installer::files::save_file($filename, \@newcontent); +} + +#################################################################### +# Setting supersede, "0" for Hotfixes, "1" for ServicePack +#################################################################### + +sub get_supersede +{ + my ( $allvariables ) = @_; + + my $supersede = 0; # if not defined, this is a Hotfix + + if (( $allvariables->{'SERVICEPACK'} ) && ( $allvariables->{'SERVICEPACK'} == 1 )) { $supersede = 1; } + + return $supersede; +} + +#################################################################### +# Setting the sequence of the patch +#################################################################### + +sub get_patchsequence +{ + my ( $allvariables ) = @_; + + my $patchsequence = "1.0"; + + if ( ! $allvariables->{'PACKAGEVERSION'} ) { installer::exiter::exit_program("ERROR: PACKAGEVERSION must be set for msp patch creation!", "get_patchsequence"); } + + my $packageversion = $allvariables->{'PACKAGEVERSION'}; + + if ( $packageversion =~ /^\s*(\d+)\.(\d+)\.(\d+)\s*$/ ) + { + my $major = $1; + my $minor = $2; + my $micro = $3; + my $concat = 100 * $minor + $micro; + $packageversion = $major . "\." . $concat; + } + my $vendornumber = 0; + if ( $allvariables->{'VENDORPATCHVERSION'} ) { $vendornumber = $allvariables->{'VENDORPATCHVERSION'}; } + $patchsequence = $packageversion . "\." . $installer::globals::buildid . "\." . $vendornumber; + + if ( $allvariables->{'PATCHSEQUENCE'} ) { $patchsequence = $allvariables->{'PATCHSEQUENCE'}; } + + return $patchsequence; +} + +#################################################################### +# Editing all tables from pcp file, that need to be edited +#################################################################### + +sub edit_tables +{ + my ($tablelist, $localmspdir, $olddatabase, $newdatabase, $mspfilename, $allvariables, $languagestringref) = @_; + + # table list contains: my $tablelist = "Properties TargetImages UpgradedImages ImageFamilies PatchMetadata PatchSequence"; + + change_properties_table($localmspdir, $mspfilename); + change_targetimages_table($localmspdir, $olddatabase); + change_upgradedimages_table($localmspdir, $newdatabase); + change_imagefamilies_table($localmspdir); + change_patchmetadata_table($localmspdir, $allvariables, $languagestringref); + change_patchsequence_table($localmspdir, $allvariables); +} + +################################################################################# +# Checking, if this is the correct database. +################################################################################# + +sub correct_patch +{ + my ($product, $pro, $langs, $languagestringref) = @_; + + my $correct_patch = 0; + + # Comparing $product with $installer::globals::product and + # $pro with $installer::globals::pro and + # $langs with $languagestringref + + my $product_is_good = 0; + + my $localproduct = $installer::globals::product; + if ( $installer::globals::languagepack ) { $localproduct = $localproduct . "LanguagePack"; } + + if ( $product eq $localproduct ) { $product_is_good = 1; } + + if ( $product_is_good ) + { + my $pro_is_good = 0; + + if ((( $pro eq "pro" ) && ( $installer::globals::pro )) || (( $pro eq "nonpro" ) && ( ! $installer::globals::pro ))) { $pro_is_good = 1; } + + if ( $pro_is_good ) + { + my $langlisthash = installer::converter::convert_stringlist_into_hash(\$langs, ","); + my $langstringhash = installer::converter::convert_stringlist_into_hash($languagestringref, "_"); + + my $not_included = 0; + foreach my $onelang ( keys %{$langlisthash} ) + { + if ( ! exists($langstringhash->{$onelang}) ) + { + $not_included = 1; + last; + } + } + + if ( ! $not_included ) + { + foreach my $onelanguage ( keys %{$langstringhash} ) + { + if ( ! exists($langlisthash->{$onelanguage}) ) + { + $not_included = 1; + last; + } + } + + if ( ! $not_included ) { $correct_patch = 1; } + } + } + } + + return $correct_patch; +} + +################################################################################# +# Searching for the path to the required patch for this special product. +################################################################################# + +sub get_requiredpatchfile_from_list +{ + my ($filecontent, $languagestringref, $filename) = @_; + + my $patchpath = ""; + + for ( my $i = 0; $i <= $#{$filecontent}; $i++ ) + { + my $line = ${$filecontent}[$i]; + if ( $line =~ /^\s*$/ ) { next; } # empty line + if ( $line =~ /^\s*\#/ ) { next; } # comment line + + if ( $line =~ /^\s*(.+?)\s*\t+\s*(.+?)\s*\t+\s*(.+?)\s*\t+\s*(.+?)\s*$/ ) + { + my $product = $1; + my $pro = $2; + my $langs = $3; + my $path = $4; + + if (( $pro ne "pro" ) && ( $pro ne "nonpro" )) { installer::exiter::exit_program("ERROR: Wrong syntax in file: $filename. Only \"pro\" or \"nonpro\" allowed in column 1! Line: \"$line\"", "get_databasename_from_list"); } + + if ( correct_patch($product, $pro, $langs, $languagestringref) ) + { + $patchpath = $path; + last; + } + } + else + { + installer::exiter::exit_program("ERROR: Wrong syntax in file: $filename! Line: \"$line\"", "get_requiredpatchfile_from_list"); + } + } + + return $patchpath; +} + +################################################################## +# Converting unicode file to ascii +# to be more precise: uft-16 little endian to ascii +################################################################## + +sub convert_unicode_to_ascii +{ + my ( $filename ) = @_; + + my @localfile = (); + + my $savfilename = $filename . "_before.unicode"; + installer::systemactions::copy_one_file($filename, $savfilename); + +# open( IN, "<:utf16", $filename ) || installer::exiter::exit_program("ERROR: Cannot open file $filename for reading", "convert_unicode_to_ascii"); +# open( IN, "<:para:crlf:uni", $filename ) || installer::exiter::exit_program("ERROR: Cannot open file $filename for reading", "convert_unicode_to_ascii"); + open( IN, "<:encoding(UTF16-LE)", $filename ) || installer::exiter::exit_program("ERROR: Cannot open file $filename for reading", "convert_unicode_to_ascii"); +# open( IN, "<:encoding(UTF-8)", $filename ) || installer::exiter::exit_program("ERROR: Cannot open file $filename for reading", "convert_unicode_to_ascii"); + while ( $line = <IN> ) { + push @localfile, $line; + } + close( IN ); + + if ( open( OUT, ">", $filename ) ) + { + print OUT @localfile; + close(OUT); + } +} + +#################################################################### +# Analyzing the log file created by msimsp.exe to find all +# files included into the patch. +#################################################################### + +sub analyze_msimsp_logfile +{ + my ($logfile, $filesarray) = @_; + + # Reading log file after converting from utf-16 (LE) to ascii + convert_unicode_to_ascii($logfile); + my $logfilecontent = installer::files::read_file($logfile); + + # Creating hash from $filesarray: unique file name -> destination of file + my %filehash = (); + my %destinationcollector = (); + + for ( my $i = 0; $i <= $#{$filesarray}; $i++ ) + { + my $onefile = ${$filesarray}[$i]; + + # Only collecting files with "uniquename" and "destination" + if (( exists($onefile->{'uniquename'}) ) && ( exists($onefile->{'uniquename'}) )) + { + my $uniquefilename = $onefile->{'uniquename'}; + my $destpath = $onefile->{'destination'}; + $filehash{$uniquefilename} = $destpath; + } + } + + # Analyzing log file of msimsp.exe, finding all changed files + # and searching all destinations of unique file names. + # Content in log file: "INFO File Key: <file key> is modified" + # Collecting content in @installer::globals::patchfilecollector + + for ( my $i = 0; $i <= $#{$logfilecontent}; $i++ ) + { + if ( ${$logfilecontent}[$i] =~ /Key\:\s*(.*?) is modified\s*$/ ) + { + my $filekey = $1; + if ( exists($filehash{$filekey}) ) { $destinationcollector{$filehash{$filekey}} = 1; } + else { installer::exiter::exit_program("ERROR: Could not find file key \"$filekey\" in file collector.", "analyze_msimsp_logfile"); } + } + } + + foreach my $onedest ( sort keys %destinationcollector ) { push(@installer::globals::patchfilecollector, "$onedest\n"); } + +} + +#################################################################### +# Creating msp patch files for Windows +#################################################################### + +sub create_msp_patch +{ + my ($installationdir, $includepatharrayref, $allvariables, $languagestringref, $languagesarrayref, $filesarray) = @_; + + my $force = 1; # print this message even in 'quiet' mode + installer::logger::print_message( "\n******************************************\n" ); + installer::logger::print_message( "... creating msp installation set ...\n", $force ); + installer::logger::print_message( "******************************************\n" ); + + $installer::globals::creating_windows_installer_patch = 1; + + my @needed_files = ("msimsp.exe"); # only required for patch creation process + installer::control::check_needed_files_in_path(\@needed_files); + + installer::logger::include_header_into_logfile("Creating msp installation sets:"); + + my $firstdir = $installationdir; + installer::pathanalyzer::get_path_from_fullqualifiedname(\$firstdir); + + my $lastdir = $installationdir; + installer::pathanalyzer::make_absolute_filename_to_relative_filename(\$lastdir); + + if ( $lastdir =~ /\./ ) { $lastdir =~ s/\./_msp_inprogress\./ } + else { $lastdir = $lastdir . "_msp_inprogress"; } + + # Removing existing directory "_native_packed_inprogress" and "_native_packed_witherror" and "_native_packed" + + my $mspdir = $firstdir . $lastdir; + if ( -d $mspdir ) { installer::systemactions::remove_complete_directory($mspdir); } + + my $olddir = $mspdir; + $olddir =~ s/_inprogress/_witherror/; + if ( -d $olddir ) { installer::systemactions::remove_complete_directory($olddir); } + + $olddir = $mspdir; + $olddir =~ s/_inprogress//; + if ( -d $olddir ) { installer::systemactions::remove_complete_directory($olddir); } + + # Creating the new directory for new installation set + installer::systemactions::create_directory($mspdir); + + $installer::globals::saveinstalldir = $mspdir; + + installer::logger::include_timestamp_into_logfile("\nPerformance Info: Starting product installation"); + + # Installing both installation sets + installer::logger::print_message( "... installing products ...\n" ); + my ($olddatabase, $newdatabase) = install_installation_sets($installationdir); + + installer::logger::include_timestamp_into_logfile("\nPerformance Info: Starting pcp file creation"); + + # Create pcp file + installer::logger::print_message( "... creating pcp file ...\n" ); + + my $localmspdir = installer::systemactions::create_directories("msp", $languagestringref); + + if ( ! $allvariables->{'PCPFILENAME'} ) { installer::exiter::exit_program("ERROR: Property \"PCPFILENAME\" has to be defined.", "create_msp_patch"); } + my $pcpfilename = $allvariables->{'PCPFILENAME'}; + + if ( $installer::globals::languagepack ) { $pcpfilename =~ s/.pcp\s*$/languagepack.pcp/; } + + # Searching the pcp file in the include pathes + my $fullpcpfilenameref = installer::scriptitems::get_sourcepath_from_filename_and_includepath(\$pcpfilename, $includepatharrayref, 1); + if ( $$fullpcpfilenameref eq "" ) { installer::exiter::exit_program("ERROR: pcp file not found: $pcpfilename !", "create_msp_patch"); } + my $fullpcpfilenamesource = $$fullpcpfilenameref; + + # Copying pcp file + my $fullpcpfilename = $localmspdir . $installer::globals::separator . $pcpfilename; + installer::systemactions::copy_one_file($fullpcpfilenamesource, $fullpcpfilename); + + # a. Extracting tables from msi database: msidb.exe -d <msifile> -f <directory> -e File Media, ... + # b. Changing content of msi database in tables: File, Media, Directory, FeatureComponent + # c. Including tables into msi database: msidb.exe -d <msifile> -f <directory> -i File Media, ... + + # Unpacking tables from pcp file + extract_all_tables_from_pcpfile($fullpcpfilename, $localmspdir); + + # Tables, that need to be edited + my $tablelist = "Properties TargetImages UpgradedImages ImageFamilies PatchMetadata PatchSequence"; # required tables + + # Saving all tables + check_and_save_tables($tablelist, $localmspdir); + + # Setting the name of the new msp file + my $mspfilename = set_mspfilename($allvariables, $mspdir, $languagesarrayref); + + # Editing tables + edit_tables($tablelist, $localmspdir, $olddatabase, $newdatabase, $mspfilename, $allvariables, $languagestringref); + + # Adding edited tables into pcp file + include_tables_into_pcpfile($fullpcpfilename, $localmspdir, $tablelist); + + # Start msimsp.exe + installer::logger::include_timestamp_into_logfile("\nPerformance Info: Starting msimsp.exe"); + my $msimsplogfile = execute_msimsp($fullpcpfilename, $mspfilename, $localmspdir); + + # Copy final installation set next to msp file + installer::logger::include_timestamp_into_logfile("\nPerformance Info: Copying installation set"); + installer::logger::print_message( "... copying installation set ...\n" ); + + my $oldinstallationsetpath = $installer::globals::updatedatabasepath; + + if ( $^O =~ /cygwin/i ) { $oldinstallationsetpath =~ s/\\/\//g; } + + installer::pathanalyzer::get_path_from_fullqualifiedname(\$oldinstallationsetpath); + installer::systemactions::copy_complete_directory($oldinstallationsetpath, $mspdir); + + # Copying additional patches into the installation set, if required + if (( $allvariables->{'ADDITIONALREQUIREDPATCHES'} ) && ( $allvariables->{'ADDITIONALREQUIREDPATCHES'} ne "" ) && ( ! $installer::globals::languagepack )) + { + my $filename = $allvariables->{'ADDITIONALREQUIREDPATCHES'}; + + my $fullfilenameref = installer::scriptitems::get_sourcepath_from_filename_and_includepath(\$filename, $includepatharrayref, 1); + if ( $$fullfilenameref eq "" ) { installer::exiter::exit_program("ERROR: Could not find file with required patches, although it is defined: $filename !", "create_msp_patch"); } + my $fullfilename = $$fullfilenameref; + + # Reading list file + my $listfile = installer::files::read_file($fullfilename); + + # Get name and path of reference database + my $requiredpatchfile = get_requiredpatchfile_from_list($listfile, $languagestringref, $fullfilename); + if ( $requiredpatchfile eq "" ) { installer::exiter::exit_program("ERROR: Could not find path to required patch in file $fullfilename for language(s) $$languagestringref!", "create_msp_patch"); } + + # Copying patch file + installer::systemactions::copy_one_file($requiredpatchfile, $mspdir); + # my $infoline = "Copy $requiredpatchfile to $mspdir\n"; + # push( @installer::globals::logfileinfo, $infoline); + } + + # Find all files included into the patch + # Analyzing the msimsp log file $msimsplogfile + analyze_msimsp_logfile($msimsplogfile, $filesarray); + + # Done + installer::logger::include_timestamp_into_logfile("\nPerformance Info: msp creation done"); + + return $mspdir; +} + +1; diff --git a/solenv/bin/modules/installer/windows/patch.pm b/solenv/bin/modules/installer/windows/patch.pm new file mode 100644 index 000000000000..25f8fab6caa2 --- /dev/null +++ b/solenv/bin/modules/installer/windows/patch.pm @@ -0,0 +1,159 @@ +#************************************************************************* +# +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# Copyright 2008 by Sun Microsystems, Inc. +# +# OpenOffice.org - a multi-platform office productivity suite +# +# $RCSfile: patch.pm,v $ +# +# $Revision: 1.6 $ +# +# This file is part of OpenOffice.org. +# +# OpenOffice.org is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License version 3 +# only, as published by the Free Software Foundation. +# +# OpenOffice.org is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License version 3 for more details +# (a copy is included in the LICENSE file that accompanied this code). +# +# You should have received a copy of the GNU Lesser General Public License +# version 3 along with OpenOffice.org. If not, see +# <http://www.openoffice.org/license.html> +# for a copy of the LGPLv3 License. +# +#************************************************************************* + +package installer::windows::patch; + +use installer::exiter; +use installer::files; +use installer::globals; +use installer::windows::idtglobal; + +#################################################################################### +# Creating the file Upgrade.idt dynamically +# Content: +# UpgradeCode VersionMin VersionMax Language Attributes Remove ActionProperty +#################################################################################### + +sub update_patch_tables +{ + my ($basedir, $allvariables) = @_; + + my $reglocatfile = ""; + my $appsearchfile = ""; + + my $reglocatfilename = $basedir . $installer::globals::separator . "RegLocat.idt"; + my $appsearchfilename = $basedir . $installer::globals::separator . "AppSearc.idt"; + my $signaturefilename = $basedir . $installer::globals::separator . "Signatur.idt"; + + if ( -f $reglocatfilename ) + { + $reglocatfile = installer::files::read_file($reglocatfilename); + } + else + { + my @reglocattable = (); + $reglocatfile = \@reglocattable; + installer::windows::idtglobal::write_idt_header($reglocatfile, "reglocat"); + } + + if ( -f $appsearchfilename ) + { + $appsearchfile = installer::files::read_file($appsearchfilename); + } + else + { + my @appsearchtable = (); + $appsearchfile = \@appsearchtable; + installer::windows::idtglobal::write_idt_header($appsearchfile, "appsearch"); + } + + if ( -f $signaturefilename ) + { + $signaturefile = installer::files::read_file($signaturefilename); + } + else + { + my @signaturetable = (); + $signaturefile = \@signaturetable; + installer::windows::idtglobal::write_idt_header($signaturefile, "signatur"); + } + + # Writing content into this tables + + if ( ! $allvariables->{'PATCHCODEFILE'} ) { installer::exiter::exit_program("ERROR: Variable PATCHCODEFILE must be defined for Windows patches!", "update_patch_tables"); } + my $patchcodesfilename = $installer::globals::idttemplatepath . $installer::globals::separator . $allvariables->{'PATCHCODEFILE'}; + my $patchcodefile = installer::files::read_file($patchcodesfilename); + + my $number = 0; + + for ( my $i = 0; $i <= $#{$patchcodefile}; $i++ ) + { + my $oneline = ${$patchcodefile}[$i]; + + if ( $oneline =~ /^\s*\#/ ) { next; } # this is a comment line + if ( $oneline =~ /^\s*$/ ) { next; } + + my $code = ""; + if ( $oneline =~ /^\s*(\S+)\s/ ) { $code = $1; } + + foreach my $name ( sort keys %installer::globals::installlocations ) + { + $number++; + my $signature = "dir" . $number . "user"; + my $rootvalue = "1"; + my $registryname = ""; + my $registryversion = ""; + + if ( $allvariables->{'SEARCHPRODUCTNAME'} ) { $registryname = $allvariables->{'SEARCHPRODUCTNAME'}; } + else { $registryname = $allvariables->{'PRODUCTNAME'}; } + + if ( $allvariables->{'SEARCHPRODUCTVERSION'} ) { $registryversion = $allvariables->{'SEARCHPRODUCTVERSION'}; } + else { $registryversion = $allvariables->{'PRODUCTVERSION'}; } + + my $key = "Software\\" . $allvariables->{'MANUFACTURER'} . "\\" . $registryname . "\\" . $registryversion . "\\" . $code; + + my $type = 2; + my $property = $name; + + $oneline = $signature . "\t" . $rootvalue . "\t" . $key . "\t" . $name . "\t" . $type . "\n"; + push(@{$reglocatfile}, $oneline); + + $oneline = $property . "\t" . $signature . "\n"; + push(@{$appsearchfile}, $oneline); + + $signature = "dir" . $number . "mach"; + $rootvalue = "2"; + + $oneline = $signature . "\t" . $rootvalue . "\t" . $key . "\t" . $name . "\t" . $type . "\n"; + push(@{$reglocatfile}, $oneline); + + $oneline = $property . "\t" . $signature . "\n"; + push(@{$appsearchfile}, $oneline); + } + } + + # Saving the files + + installer::files::save_file($reglocatfilename ,$reglocatfile); + my $infoline = "Updated idt file: $reglocatfilename\n"; + push(@installer::globals::logfileinfo, $infoline); + + installer::files::save_file($appsearchfilename ,$appsearchfile); + $infoline = "Updated idt file: $appsearchfilename\n"; + push(@installer::globals::logfileinfo, $infoline); + + installer::files::save_file($signaturefilename ,$signaturefile); + $infoline = "Updated idt file: $signaturefilename\n"; + push(@installer::globals::logfileinfo, $infoline); + +} + +1; diff --git a/solenv/bin/modules/installer/windows/property.pm b/solenv/bin/modules/installer/windows/property.pm new file mode 100644 index 000000000000..9ce90d8bc7eb --- /dev/null +++ b/solenv/bin/modules/installer/windows/property.pm @@ -0,0 +1,651 @@ +#************************************************************************* +# +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# Copyright 2008 by Sun Microsystems, Inc. +# +# OpenOffice.org - a multi-platform office productivity suite +# +# $RCSfile: property.pm,v $ +# +# $Revision: 1.31 $ +# +# This file is part of OpenOffice.org. +# +# OpenOffice.org is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License version 3 +# only, as published by the Free Software Foundation. +# +# OpenOffice.org is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License version 3 for more details +# (a copy is included in the LICENSE file that accompanied this code). +# +# You should have received a copy of the GNU Lesser General Public License +# version 3 along with OpenOffice.org. If not, see +# <http://www.openoffice.org/license.html> +# for a copy of the LGPLv3 License. +# +#************************************************************************* + +package installer::windows::property; + +use installer::exiter; +use installer::files; +use installer::globals; +use installer::windows::idtglobal; +use installer::windows::language; + +############################################# +# Setting the properties dynamically +# for the table Property.idt +############################################# + +sub get_arpcomments_for_property_table +{ + my ( $allvariables, $languagestringref ) = @_; + + my $name = $allvariables->{'PRODUCTNAME'}; + my $version = $allvariables->{'PRODUCTVERSION'}; + my $comment = $name . " " . $version; + + my $postversionextension = ""; + if ( $allvariables->{'POSTVERSIONEXTENSION'} ) + { + $postversionextension = $allvariables->{'POSTVERSIONEXTENSION'}; + $comment = $comment . " " . $postversionextension; + } + + if ( $installer::globals::languagepack ) { $comment = $comment . " " . "Language Pack"; } + + if ( $installer::globals::patch ) + { + if ( ! $allvariables->{'WINDOWSPATCHLEVEL'} ) { installer::exiter::exit_program("ERROR: No Patch level defined for Windows patch: WINDOWSPATCHLEVEL", "get_arpcomments_for_property_table"); } + my $patchstring = "Product Update" . " " . $allvariables->{'WINDOWSPATCHLEVEL'}; + $comment = $comment . " " . $patchstring; + } + + my $languagestring = $$languagestringref; + $languagestring =~ s/\_/\,/g; + + $comment = $comment . " ($languagestring)"; + + my $localminor = ""; + if ( $installer::globals::updatepack ) { $localminor = $installer::globals::lastminor; } + else { $localminor = $installer::globals::minor; } + + my $buildidstring = "(" . $installer::globals::build . $localminor . "(Build:" . $installer::globals::buildid . "))"; + + # the environment variable CWS_WORK_STAMP is set only in CWS + if ( $ENV{'CWS_WORK_STAMP'} ) { $buildidstring = $buildidstring . "\[CWS\:" . $ENV{'CWS_WORK_STAMP'} . "\]"; } + + $comment = $comment . " " . $buildidstring; + + return $comment; +} + +sub get_installlevel_for_property_table +{ + my $installlevel = "100"; + return $installlevel; +} + +sub get_ischeckforproductupdates_for_property_table +{ + my $ischeckforproductupdates = "1"; + return $ischeckforproductupdates; +} + +sub get_manufacturer_for_property_table +{ + return $installer::globals::manufacturer; +} + +sub get_productlanguage_for_property_table +{ + my ($language) = @_; + my $windowslanguage = installer::windows::language::get_windows_language($language); + return $windowslanguage; +} + +sub get_language_string +{ + my $langstring = ""; + + for ( my $i = 0; $i <= $#installer::globals::languagenames; $i++ ) + { + $langstring = $langstring . $installer::globals::languagenames[$i] . ", "; + } + + $langstring =~ s/\,\s*$//; + $langstring = "(" . $langstring . ")"; + + return $langstring; +} + +sub get_english_language_string +{ + my $langstring = ""; + + # Sorting value not keys, therefore collecting all values + my %helper = (); + foreach my $lang ( keys %installer::globals::all_required_english_languagestrings ) + { + $helper{$installer::globals::all_required_english_languagestrings{$lang}} = 1; + } + + foreach my $lang ( sort keys %helper ) + { + $langstring = $langstring . $lang . ", "; + } + + $langstring =~ s/\,\s*$//; + $langstring = "(" . $langstring . ")"; + + return $langstring; +} + +sub get_productname_for_property_table +{ + my ( $allvariables ) = @_; + + my $name = $allvariables->{'PRODUCTNAME'}; + my $version = $allvariables->{'PRODUCTVERSION'}; + my $productname = $name . " " . $version; + + my $postversionextension = ""; + if ( $allvariables->{'POSTVERSIONEXTENSION'} ) + { + $postversionextension = $allvariables->{'POSTVERSIONEXTENSION'}; + $productname = $productname . " " . $postversionextension; + } + + my $productextension = ""; + if ( $allvariables->{'PRODUCTEXTENSION'} ) + { + $productextension = $allvariables->{'PRODUCTEXTENSION'}; + $productname = $productname . " " . $productextension; + } + + if ( $installer::globals::languagepack ) + { + # my $langstring = get_language_string(); # Example (English, Deutsch) + my $langstring = get_english_language_string(); # New: (English, German) + $productname = $name . " " . $version . " Language Pack" . " " . $langstring; + } + + if ( $installer::globals::patch ) + { + if ( ! $allvariables->{'WINDOWSPATCHLEVEL'} ) { installer::exiter::exit_program("ERROR: No Patch level defined for Windows patch: WINDOWSPATCHLEVEL", "get_productname_for_property_table"); } + my $patchstring = "Product Update" . " " . $allvariables->{'WINDOWSPATCHLEVEL'}; + $productname = $productname . " " . $patchstring; + } + + # Saving this name in hash $allvariables for further usage + $allvariables->{'PROPERTYTABLEPRODUCTNAME'} = $productname; + my $infoline = "Defined variable PROPERTYTABLEPRODUCTNAME: $productname\n"; + push(@installer::globals::logfileinfo, $infoline); + + return $productname; +} + +sub get_quickstarterlinkname_for_property_table +{ + my ( $allvariables ) = @_; + + # no usage of POSTVERSIONEXTENSION for Quickstarter link name! + + my $name = $allvariables->{'PRODUCTNAME'}; + my $version = $allvariables->{'PRODUCTVERSION'}; + my $quickstartername = $name . " " . $version; + + my $infoline = "Defined Quickstarter Link name: $quickstartername\n"; + push(@installer::globals::logfileinfo, $infoline); + + return $quickstartername; +} + +sub get_productversion_for_property_table +{ + return $installer::globals::msiproductversion; +} + +####################################################### +# Setting all feature names as Properties. This is +# required for the Windows patch process. +####################################################### + +sub set_featurename_properties_for_patch +{ + ($propertyfile) = @_; + + for ( my $i = 0; $i <= $#installer::globals::featurecollector; $i++ ) + { + my $onepropertyline = $installer::globals::featurecollector[$i] . "\t" . "1" . "\n"; + push(@{$propertyfile}, $onepropertyline); + } + +} + +####################################################### +# Setting some important properties +# (for finding the product in deinstallation process) +####################################################### + +sub set_important_properties +{ + my ($propertyfile, $allvariables, $languagestringref) = @_; + + # Setting new variables with the content of %PRODUCTNAME and %PRODUCTVERSION + if ( $allvariables->{'PRODUCTNAME'} ) + { + my $onepropertyline = "DEFINEDPRODUCT" . "\t" . $allvariables->{'PRODUCTNAME'} . "\n"; + push(@{$propertyfile}, $onepropertyline); + } + + if ( $allvariables->{'PRODUCTVERSION'} ) + { + my $onepropertyline = "DEFINEDVERSION" . "\t" . $allvariables->{'PRODUCTVERSION'} . "\n"; + push(@{$propertyfile}, $onepropertyline); + } + + if (( $allvariables->{'PRODUCTNAME'} ) && ( $allvariables->{'PRODUCTVERSION'} ) && ( $allvariables->{'MANUFACTURER'} ) && ( $allvariables->{'PRODUCTCODE'} )) + { + my $onepropertyline = "FINDPRODUCT" . "\t" . "Software\\" . $allvariables->{'MANUFACTURER'} . "\\" . $allvariables->{'PRODUCTNAME'} . $allvariables->{'PRODUCTADDON'} . "\\" . $allvariables->{'PRODUCTVERSION'} . "\\" . $allvariables->{'PRODUCTCODE'} . "\n"; + push(@{$propertyfile}, $onepropertyline); + } + + if ( $allvariables->{'PRODUCTMAJOR'} ) + { + my $onepropertyline = "PRODUCTMAJOR" . "\t" . $allvariables->{'PRODUCTMAJOR'} . "\n"; + push(@{$propertyfile}, $onepropertyline); + } + + if ( $allvariables->{'PRODUCTMINOR'} ) + { + my $onepropertyline = "PRODUCTMINOR" . "\t" . $allvariables->{'PRODUCTMINOR'} . "\n"; + push(@{$propertyfile}, $onepropertyline); + } + + if ( $allvariables->{'PRODUCTBUILDID'} ) + { + my $onepropertyline = "PRODUCTBUILDID" . "\t" . $allvariables->{'PRODUCTBUILDID'} . "\n"; + push(@{$propertyfile}, $onepropertyline); + } + + if ( $allvariables->{'OOOBASEVERSION'} ) + { + my $onepropertyline = "OOOBASEVERSION" . "\t" . $allvariables->{'OOOBASEVERSION'} . "\n"; + push(@{$propertyfile}, $onepropertyline); + } + + if ( $allvariables->{'URELAYERVERSION'} ) + { + my $onepropertyline = "URELAYERVERSION" . "\t" . $allvariables->{'URELAYERVERSION'} . "\n"; + push(@{$propertyfile}, $onepropertyline); + } + + if ( $allvariables->{'BRANDPACKAGEVERSION'} ) + { + my $onepropertyline = "BRANDPACKAGEVERSION" . "\t" . $allvariables->{'BRANDPACKAGEVERSION'} . "\n"; + push(@{$propertyfile}, $onepropertyline); + } + + if ( $allvariables->{'BASISROOTNAME'} ) + { + my $onepropertyline = "BASISROOTNAME" . "\t" . $allvariables->{'BASISROOTNAME'} . "\n"; + push(@{$propertyfile}, $onepropertyline); + } + + if ( $allvariables->{'PREREQUIREDPATCH'} ) + { + my $onepropertyline = "PREREQUIREDPATCH" . "\t" . $allvariables->{'PREREQUIREDPATCH'} . "\n"; + push(@{$propertyfile}, $onepropertyline); + } + + my $onepropertyline = "IGNOREPREREQUIREDPATCH" . "\t" . "1" . "\n"; + push(@{$propertyfile}, $onepropertyline); + + $onepropertyline = "DONTOPTIMIZELIBS" . "\t" . "0" . "\n"; + push(@{$propertyfile}, $onepropertyline); + + if ( $installer::globals::sundirexists ) + { + my $onepropertyline = "SUNDIREXISTS" . "\t" . "1" . "\n"; + push(@{$propertyfile}, $onepropertyline); + } + + if ( $installer::globals::officedirhostname ) + { + my $onepropertyline = "OFFICEDIRHOSTNAME" . "\t" . $installer::globals::officedirhostname . "\n"; + push(@{$propertyfile}, $onepropertyline); + } + + if ( $installer::globals::basisdirhostname ) + { + my $onepropertyline = "BASISDIRHOSTNAME" . "\t" . $installer::globals::basisdirhostname . "\n"; + push(@{$propertyfile}, $onepropertyline); + } + + if ( $installer::globals::uredirhostname ) + { + my $onepropertyline = "UREDIRHOSTNAME" . "\t" . $installer::globals::uredirhostname . "\n"; + push(@{$propertyfile}, $onepropertyline); + } + + if ( $installer::globals::sundirhostname ) + { + my $onepropertyline = "SUNDIRHOSTNAME" . "\t" . $installer::globals::sundirhostname . "\n"; + push(@{$propertyfile}, $onepropertyline); + } + + if ( $installer::globals::desktoplinkexists ) + { + my $onepropertyline = "DESKTOPLINKEXISTS" . "\t" . "1" . "\n"; + push(@{$propertyfile}, $onepropertyline); + + $onepropertyline = "CREATEDESKTOPLINK" . "\t" . "1" . "\n"; # Setting the default + push(@{$propertyfile}, $onepropertyline); + } + + if ( $installer::globals::patch ) + { + my $onepropertyline = "ISPATCH" . "\t" . "1" . "\n"; + push(@{$propertyfile}, $onepropertyline); + + $onepropertyline = "SETUP_USED" . "\t" . "0" . "\n"; + push(@{$propertyfile}, $onepropertyline); + } + + if ( $installer::globals::languagepack ) + { + my $onepropertyline = "ISLANGUAGEPACK" . "\t" . "1" . "\n"; + push(@{$propertyfile}, $onepropertyline); + } + + my $languagesline = "PRODUCTALLLANGUAGES" . "\t" . $$languagestringref . "\n"; + push(@{$propertyfile}, $languagesline); + + if (( $allvariables->{'PRODUCTEXTENSION'} ) && ( $allvariables->{'PRODUCTEXTENSION'} eq "Beta" )) + { + my $registryline = "WRITE_REGISTRY" . "\t" . "0" . "\n"; + push(@{$propertyfile}, $registryline); + my $betainfoline = "BETAPRODUCT" . "\t" . "1" . "\n"; + push(@{$propertyfile}, $betainfoline); + } + elsif ( $allvariables->{'DEVELOPMENTPRODUCT'} ) + { + my $registryline = "WRITE_REGISTRY" . "\t" . "0" . "\n"; + push(@{$propertyfile}, $registryline); + } + else + { + my $registryline = "WRITE_REGISTRY" . "\t" . "1" . "\n"; # Default: Write complete registry + push(@{$propertyfile}, $registryline); + } + + # Adding also used tree conditions for multilayer products. + # These are saved in %installer::globals::usedtreeconditions + foreach my $treecondition (keys %installer::globals::usedtreeconditions) + { + my $onepropertyline = $treecondition . "\t" . "1" . "\n"; + push(@{$propertyfile}, $onepropertyline); + } + + # No more license dialog for selected products + if ( $allvariables->{'HIDELICENSEDIALOG'} ) + { + my $onepropertyline = "HIDEEULA" . "\t" . "1" . "\n"; + push(@{$propertyfile}, $onepropertyline); + } + + # Setting .NET requirements + if ( $installer::globals::required_dotnet_version ne "" ) + { + my $onepropertyline = "REQUIRED_DOTNET_VERSION" . "\t" . $installer::globals::required_dotnet_version . "\n"; + push(@{$propertyfile}, $onepropertyline); + + $onepropertyline = "DOTNET_SUFFICIENT" . "\t" . "1" . "\n"; # default value for found .NET + push(@{$propertyfile}, $onepropertyline); + } + +} + +####################################################### +# Setting properties needed for ms file type registration +####################################################### + +sub set_ms_file_types_properties +{ + my ($propertyfile) = @_; + + push(@{$propertyfile}, "REGISTER_PPS" . "\t" . "0" . "\n"); + push(@{$propertyfile}, "REGISTER_PPSX" . "\t" . "0" . "\n"); + push(@{$propertyfile}, "REGISTER_PPSM" . "\t" . "0" . "\n"); + push(@{$propertyfile}, "REGISTER_PPAM" . "\t" . "0" . "\n"); + push(@{$propertyfile}, "REGISTER_PPT" . "\t" . "0" . "\n"); + push(@{$propertyfile}, "REGISTER_PPTX" . "\t" . "0" . "\n"); + push(@{$propertyfile}, "REGISTER_PPTM" . "\t" . "0" . "\n"); + push(@{$propertyfile}, "REGISTER_POT" . "\t" . "0" . "\n"); + push(@{$propertyfile}, "REGISTER_POTX" . "\t" . "0" . "\n"); + push(@{$propertyfile}, "REGISTER_POTM" . "\t" . "0" . "\n"); + + push(@{$propertyfile}, "REGISTER_DOC" . "\t" . "0" . "\n"); + push(@{$propertyfile}, "REGISTER_DOCX" . "\t" . "0" . "\n"); + push(@{$propertyfile}, "REGISTER_DOCM" . "\t" . "0" . "\n"); + push(@{$propertyfile}, "REGISTER_DOT" . "\t" . "0" . "\n"); + push(@{$propertyfile}, "REGISTER_DOTX" . "\t" . "0" . "\n"); + push(@{$propertyfile}, "REGISTER_DOTM" . "\t" . "0" . "\n"); + push(@{$propertyfile}, "REGISTER_RTF" . "\t" . "0" . "\n"); + + push(@{$propertyfile}, "REGISTER_XLS" . "\t" . "0" . "\n"); + push(@{$propertyfile}, "REGISTER_XLSX" . "\t" . "0" . "\n"); + push(@{$propertyfile}, "REGISTER_XLSM" . "\t" . "0" . "\n"); + push(@{$propertyfile}, "REGISTER_XLSB" . "\t" . "0" . "\n"); + push(@{$propertyfile}, "REGISTER_XLAM" . "\t" . "0" . "\n"); + push(@{$propertyfile}, "REGISTER_XLT" . "\t" . "0" . "\n"); + push(@{$propertyfile}, "REGISTER_XLTX" . "\t" . "0" . "\n"); + push(@{$propertyfile}, "REGISTER_XLTM" . "\t" . "0" . "\n"); + + push(@{$propertyfile}, "REGISTER_NO_MSO_TYPES" . "\t" . "0" . "\n"); + push(@{$propertyfile}, "REGISTER_ALL_MSO_TYPES" . "\t" . "0" . "\n"); +} + +#################################################################################### +# Updating the file Property.idt dynamically +# Content: +# Property Value +#################################################################################### + +sub update_property_table +{ + my ($basedir, $language, $allvariables, $languagestringref) = @_; + + my $properyfilename = $basedir . $installer::globals::separator . "Property.idt"; + + my $propertyfile = installer::files::read_file($properyfilename); + + # Getting the new values + # Some values (arpcomments, arpcontacts, ...) are inserted from the Property.mlf + + my $arpcomments = get_arpcomments_for_property_table($allvariables, $languagestringref); + my $installlevel = get_installlevel_for_property_table(); + my $ischeckforproductupdates = get_ischeckforproductupdates_for_property_table(); + my $manufacturer = get_manufacturer_for_property_table(); + my $productlanguage = get_productlanguage_for_property_table($language); + my $productname = get_productname_for_property_table($allvariables); + my $productversion = get_productversion_for_property_table(); + my $quickstarterlinkname = get_quickstarterlinkname_for_property_table($allvariables); + + # Updating the values + + for ( my $i = 0; $i <= $#{$propertyfile}; $i++ ) + { + ${$propertyfile}[$i] =~ s/\bARPCOMMENTSTEMPLATE\b/$arpcomments/; + ${$propertyfile}[$i] =~ s/\bINSTALLLEVELTEMPLATE\b/$installlevel/; + ${$propertyfile}[$i] =~ s/\bISCHECKFORPRODUCTUPDATESTEMPLATE\b/$ischeckforproductupdates/; + ${$propertyfile}[$i] =~ s/\bMANUFACTURERTEMPLATE\b/$manufacturer/; + ${$propertyfile}[$i] =~ s/\bPRODUCTLANGUAGETEMPLATE\b/$productlanguage/; + ${$propertyfile}[$i] =~ s/\bPRODUCTNAMETEMPLATE\b/$productname/; + ${$propertyfile}[$i] =~ s/\bPRODUCTVERSIONTEMPLATE\b/$productversion/; + ${$propertyfile}[$i] =~ s/\bQUICKSTARTERLINKNAMETEMPLATE\b/$quickstarterlinkname/; + } + + # Setting variables into propertytable + set_important_properties($propertyfile, $allvariables, $languagestringref); + + # Setting feature names as properties for Windows patch mechanism + if ( $installer::globals::patch ) { set_featurename_properties_for_patch($propertyfile); } + + # Setting variables for register for ms file types + set_ms_file_types_properties($propertyfile); + + # Saving the file + + installer::files::save_file($properyfilename ,$propertyfile); + my $infoline = "Updated idt file: $properyfilename\n"; + push(@installer::globals::logfileinfo, $infoline); + +} + +#################################################################################### +# Setting language specific Properties in file Property.idt dynamically +# Adding: +# is1033 = 1 +# isMulti = 1 +#################################################################################### + +sub set_languages_in_property_table +{ + my ($basedir, $languagesarrayref) = @_; + + my $properyfilename = $basedir . $installer::globals::separator . "Property.idt"; + my $propertyfile = installer::files::read_file($properyfilename); + + # Setting the component properties saved in %installer::globals::languageproperties + foreach my $localproperty ( keys %installer::globals::languageproperties ) + { + $onepropertyline = $localproperty . "\t" . $installer::globals::languageproperties{$localproperty} . "\n"; + push(@{$propertyfile}, $onepropertyline); + } + + # Setting the info about multilingual installation in property "isMulti" + + my $propertyname = "isMulti"; + my $ismultivalue = 0; + + if ( $installer::globals::ismultilingual ) { $ismultivalue = 1; } + + my $onepropertyline = $propertyname . "\t" . $ismultivalue . "\n"; + push(@{$propertyfile}, $onepropertyline); + + # setting the ARPPRODUCTICON + + if ($installer::globals::sofficeiconadded) # set in shortcut.pm + { + $onepropertyline = "ARPPRODUCTICON" . "\t" . "soffice.exe" . "\n"; + push(@{$propertyfile}, $onepropertyline); + } + + # Saving the file + + installer::files::save_file($properyfilename ,$propertyfile); + my $infoline = "Added language content into idt file: $properyfilename\n"; + push(@installer::globals::logfileinfo, $infoline); + +} + +############################################################ +# Setting the ProductCode and the UpgradeCode +# into the Property table. Both have to be stored +# in the global file $installer::globals::codefilename +############################################################ + +sub set_codes_in_property_table +{ + my ($basedir) = @_; + + # Reading the property file + + my $properyfilename = $basedir . $installer::globals::separator . "Property.idt"; + my $propertyfile = installer::files::read_file($properyfilename); + + # Updating the values + + for ( my $i = 0; $i <= $#{$propertyfile}; $i++ ) + { + ${$propertyfile}[$i] =~ s/\bPRODUCTCODETEMPLATE\b/$installer::globals::productcode/; + ${$propertyfile}[$i] =~ s/\bUPGRADECODETEMPLATE\b/$installer::globals::upgradecode/; + } + + # Saving the property file + + installer::files::save_file($properyfilename ,$propertyfile); + my $infoline = "Added language content into idt file: $properyfilename\n"; + push(@installer::globals::logfileinfo, $infoline); + +} + +############################################################ +# Setting the variable REGKEYPRODPATH, that is used +# by the language packs. +############################################################ + +sub set_regkeyprodpath_in_property_table +{ + my ($basedir, , $allvariables) = @_; + + # Reading the property file + + my $properyfilename = $basedir . $installer::globals::separator . "Property.idt"; + my $propertyfile = installer::files::read_file($properyfilename); + + my $name = $allvariables->{'PRODUCTNAME'}; + my $version = $allvariables->{'PRODUCTVERSION'}; + + my $onepropertyline = "REGKEYPRODPATH" . "\t" . "Software" . "\\" . $installer::globals::manufacturer . "\\". $name; + + push(@{$propertyfile}, $onepropertyline); + + # Saving the property file + + installer::files::save_file($properyfilename ,$propertyfile); + my $infoline = "Added language content into idt file: $properyfilename\n"; + push(@installer::globals::logfileinfo, $infoline); + +} + +############################################################ +# Changing default for MS file type registration +# in Beta products. +############################################################ + +sub update_checkbox_table +{ + my ($basedir, $allvariables) = @_; + + if (( $allvariables->{'PRODUCTEXTENSION'} ) && ( $allvariables->{'PRODUCTEXTENSION'} eq "Beta" )) + { + my $checkboxfilename = $basedir . $installer::globals::separator . "CheckBox.idt"; + + if ( -f $checkboxfilename ) + { + my $checkboxfile = installer::files::read_file($checkboxfilename); + + my $checkboxline = "SELECT_WORD" . "\t" . "0" . "\n"; + push(@{$checkboxfile}, $checkboxline); + $checkboxline = "SELECT_EXCEL" . "\t" . "0" . "\n"; + push(@{$checkboxfile}, $checkboxline); + $checkboxline = "SELECT_POWERPOINT" . "\t" . "0" . "\n"; + push(@{$checkboxfile}, $checkboxline); + + # Saving the property file + installer::files::save_file($checkboxfilename ,$checkboxfile); + my $infoline = "Added ms file type defaults into idt file: $checkboxfilename\n"; + push(@installer::globals::logfileinfo, $infoline); + } + } +} + +1; diff --git a/solenv/bin/modules/installer/windows/registry.pm b/solenv/bin/modules/installer/windows/registry.pm new file mode 100644 index 000000000000..9f9da9485677 --- /dev/null +++ b/solenv/bin/modules/installer/windows/registry.pm @@ -0,0 +1,399 @@ +#************************************************************************* +# +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# Copyright 2008 by Sun Microsystems, Inc. +# +# OpenOffice.org - a multi-platform office productivity suite +# +# $RCSfile: registry.pm,v $ +# +# $Revision: 1.18 $ +# +# This file is part of OpenOffice.org. +# +# OpenOffice.org is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License version 3 +# only, as published by the Free Software Foundation. +# +# OpenOffice.org is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License version 3 for more details +# (a copy is included in the LICENSE file that accompanied this code). +# +# You should have received a copy of the GNU Lesser General Public License +# version 3 along with OpenOffice.org. If not, see +# <http://www.openoffice.org/license.html> +# for a copy of the LGPLv3 License. +# +#************************************************************************* + +package installer::windows::registry; + +use installer::files; +use installer::globals; +use installer::worker; +use installer::windows::idtglobal; + +##################################################### +# Generating the component name from a registryitem +##################################################### + +sub get_registry_component_name +{ + my ($registryref, $allvariables) = @_; + + # In this function exists the rule to create components from registryitems + # Rule: + # The componentname can be directly taken from the ModuleID. + # All registryitems belonging to one module can get the same component. + + my $componentname = ""; + my $isrootmodule = 0; + + if ( $registryref->{'ModuleID'} ) { $componentname = $registryref->{'ModuleID'}; } + + $componentname =~ s/\\/\_/g; + $componentname =~ s/\//\_/g; + $componentname =~ s/\-/\_/g; + $componentname =~ s/\_\s*$//g; + + $componentname = lc($componentname); # componentnames always lowercase + + if ( $componentname eq "gid_module_root" ) { $isrootmodule = 1; } + + # Attention: Maximum length for the componentname is 72 + + $componentname =~ s/gid_module_/g_m_/g; + $componentname =~ s/_optional_/_o_/g; + $componentname =~ s/_javafilter_/_jf_/g; + + $componentname = $componentname . "_registry"; # identifying this component as registryitem component + + # This componentname must be more specific + my $addon = "_"; + if ( $allvariables->{'PRODUCTNAME'} ) { $addon = $addon . $allvariables->{'PRODUCTNAME'}; } + if ( $allvariables->{'PRODUCTVERSION'} ) { $addon = $addon . $allvariables->{'PRODUCTVERSION'}; } + $addon = lc($addon); + $addon =~ s/ //g; + $addon =~ s/-//g; + $addon =~ s/\.//g; + + my $styles = ""; + if ( $registryref->{'Styles'} ) { $styles = $registryref->{'Styles'}; } + + # Layer links must have unique Component GUID for all products. This is necessary, because only the + # uninstallation of the last product has to delete registry keys. + if ( $styles =~ /\bLAYER_REGISTRY\b/ ) + { + $componentname = "g_m_root_registry_layer_ooo_reglayer"; + # Styles USE_URELAYERVERSION, USE_OOOBASEVERSION + if ( $styles =~ /\bUSE_URELAYERVERSION\b/ ) { $addon = "_ure_" . $allvariables->{'URELAYERVERSION'}; } + if ( $styles =~ /\bUSE_OOOBASEVERSION\b/ ) { $addon = "_basis_" . $allvariables->{'OOOBASEVERSION'}; } + $addon =~ s/\.//g; + } + + $componentname = $componentname . $addon; + + if (( $styles =~ /\bLANGUAGEPACK\b/ ) && ( $installer::globals::languagepack )) { $componentname = $componentname . "_lang"; } + if ( $styles =~ /\bALWAYS_REQUIRED\b/ ) { $componentname = $componentname . "_forced"; } + + if ( $isrootmodule ) { $installer::globals::registryrootcomponent = $componentname; } + + return $componentname; +} + +############################################################## +# Returning identifier for registry table. +############################################################## + +sub get_registry_identifier +{ + my ($registry) = @_; + + my $identifier = ""; + + if ( $registry->{'gid'} ) { $identifier = $registry->{'gid'}; } + + $identifier = lc($identifier); # always lower case + + # Attention: Maximum length is 72 + + $identifier =~ s/gid_regitem_/g_r_/; + $identifier =~ s/_soffice_/_s_/; + $identifier =~ s/_clsid_/_c_/; + $identifier =~ s/_currentversion_/_cv_/; + $identifier =~ s/_microsoft_/_ms_/; + $identifier =~ s/_staroffice_/_so_/; + $identifier =~ s/_classpath_/_cp_/; + $identifier =~ s/__/_/g; + + # Saving this in the registry collector + + $registry->{'uniquename'} = $identifier; + + return $identifier; +} + +################################################################## +# Returning root value for registry table. +################################################################## + +sub get_registry_root +{ + my ($registry) = @_; + + my $rootvalue = 0; # Default: Parent is KKEY_CLASSES_ROOT + my $scproot = ""; + + if ( $registry->{'ParentID'} ) { $scproot = $registry->{'ParentID'}; } + + if ( $scproot eq "PREDEFINED_HKEY_LOCAL_MACHINE" ) { $rootvalue = -1; } + + if ( $scproot eq "PREDEFINED_HKEY_CLASSES_ROOT" ) { $rootvalue = 0; } + + if ( $scproot eq "PREDEFINED_HKEY_CURRENT_USER_ONLY" ) { $rootvalue = 1; } + + if ( $scproot eq "PREDEFINED_HKEY_LOCAL_MACHINE_ONLY" ) { $rootvalue = 2; } + + return $rootvalue; +} + +############################################################## +# Returning key for registry table. +############################################################## + +sub get_registry_key +{ + my ($registry, $allvariableshashref) = @_; + + my $key = ""; + + if ( $registry->{'Subkey'} ) { $key = $registry->{'Subkey'}; } + + if ( $key =~ /\%/ ) { $key = installer::worker::replace_variables_in_string($key, $allvariableshashref); } + + return $key; +} + +############################################################## +# Returning name for registry table. +############################################################## + +sub get_registry_name +{ + my ($registry, $allvariableshashref) = @_; + + my $name = ""; + + if ( $registry->{'Name'} ) { $name = $registry->{'Name'}; } + + if ( $name =~ /\%/ ) { $name = installer::worker::replace_variables_in_string($name, $allvariableshashref); } + + return $name; +} + +############################################################## +# Returning value for registry table. +############################################################## + +sub get_registry_value +{ + my ($registry, $allvariableshashref) = @_; + + my $value = ""; + + if ( $registry->{'Value'} ) { $value = $registry->{'Value'}; } + + $value =~ s/\\\"/\"/g; # no more masquerading of '"' + $value =~ s/\<progpath\>/\[OFFICEINSTALLLOCATION\]/; + $value =~ s/\[OFFICEINSTALLLOCATION\]\\/\[OFFICEINSTALLLOCATION\]/; # removing "\" after "[OFFICEINSTALLLOCATION]" + + if ( $value =~ /\%/ ) { $value = installer::worker::replace_variables_in_string($value, $allvariableshashref); } + + return $value; +} + +############################################################## +# Returning 64 bit value for registry table. +############################################################## + +sub get_registry_val64 +{ + my ($registry, $allvariableshashref) = @_; + + my $value = ""; + + if ( $registry->{'Val64'} ) { $value = $registry->{'Val64'}; } + + $value =~ s/\\\"/\"/g; # no more masquerading of '"' + $value =~ s/\<progpath\>/\[OFFICEINSTALLLOCATION\]/; + $value =~ s/\[OFFICEINSTALLLOCATION\]\\/\[OFFICEINSTALLLOCATION\]/; # removing "\" after "[OFFICEINSTALLLOCATION]" + + if ( $value =~ /\%/ ) { $value = installer::worker::replace_variables_in_string($value, $allvariableshashref); } + + return $value; +} + +############################################################## +# Returning component for registry table. +############################################################## + +sub get_registry_component +{ + my ($registry, $allvariables) = @_; + + # All registry items belonging to one module can + # be included into one component + + my $componentname = get_registry_component_name($registry, $allvariables); + + # saving componentname in the registryitem collector + + $registry->{'componentname'} = $componentname; + + return $componentname; +} + +###################################################### +# Adding the content of +# @installer::globals::userregistrycollector +# to the registry table. The content was collected +# in create_files_table() in file.pm. +###################################################### + +sub add_userregs_to_registry_table +{ + my ( $registrytable, $allvariables ) = @_; + + for ( my $i = 0; $i <= $#installer::globals::userregistrycollector; $i++ ) + { + my $onefile = $installer::globals::userregistrycollector[$i]; + + my $styles = ""; + if ( $onefile->{'Styles'} ) { $styles = $onefile->{'Styles'}; } + + my %registry = (); + + $registry{'Registry'} = $onefile->{'userregkeypath'}; + $registry{'Root'} = "1"; # always HKCU + $registry{'Key'} = "Software\\$allvariables->{'MANUFACTURER'}\\$allvariables->{'PRODUCTNAME'} $allvariables->{'PRODUCTVERSION'}\\"; + if ( $onefile->{'needs_user_registry_key'} ) { $registry{'Key'} = $registry{'Key'} . "StartMenu"; } + else { $registry{'Key'} = $registry{'Key'} . "ShellNew"; } + $registry{'Name'} = $onefile->{'Name'}; + $registry{'Value'} = "1"; + $registry{'Component_'} = $onefile->{'componentname'}; + + my $oneline = $registry{'Registry'} . "\t" . $registry{'Root'} . "\t" . $registry{'Key'} . "\t" + . $registry{'Name'} . "\t" . $registry{'Value'} . "\t" . $registry{'Component_'} . "\n"; + + push(@{$registrytable}, $oneline); + } +} + +###################################################### +# Creating the file Registry.idt dynamically +# Content: +# Registry Root Key Name Value Component_ +###################################################### + +sub create_registry_table +{ + my ($registryref, $allregistrycomponentsref, $basedir, $languagesarrayref, $allvariableshashref) = @_; + + for ( my $m = 0; $m <= $#{$languagesarrayref}; $m++ ) + { + my $onelanguage = ${$languagesarrayref}[$m]; + + my @registrytable = (); + my @reg64table = (); + + installer::windows::idtglobal::write_idt_header(\@registrytable, "registry"); + installer::windows::idtglobal::write_idt_header(\@reg64table, "reg64"); + + for ( my $i = 0; $i <= $#{$registryref}; $i++ ) + { + my $oneregistry = ${$registryref}[$i]; + + # Controlling the language! + # Only language independent folderitems or folderitems with the correct language + # will be included into the table + + if (! (!(( $oneregistry->{'ismultilingual'} )) || ( $oneregistry->{'specificlanguage'} eq $onelanguage )) ) { next; } + + my %registry = (); + + $registry{'Registry'} = get_registry_identifier($oneregistry); + $registry{'Root'} = get_registry_root($oneregistry); + $registry{'Key'} = get_registry_key($oneregistry, $allvariableshashref); + $registry{'Name'} = get_registry_name($oneregistry, $allvariableshashref); + $registry{'Value'} = get_registry_value($oneregistry, $allvariableshashref); + $registry{'Val64'} = get_registry_val64($oneregistry, $allvariableshashref); + $registry{'Component_'} = get_registry_component($oneregistry, $allvariableshashref); + + # Collecting all components + if (!(installer::existence::exists_in_array($registry{'Component_'}, $allregistrycomponentsref))) + { + push(@{$allregistrycomponentsref}, $registry{'Component_'}); + } + + # Collecting all components with DONT_DELETE style + my $style = ""; + if ( $oneregistry->{'Styles'} ) { $style = $oneregistry->{'Styles'}; } + if ( $style =~ /\bDONT_DELETE\b/ ) { $installer::globals::dontdeletecomponents{$registry{'Component_'}} = 1; } + + # Saving upgradekey to write this into setup.ini for minor upgrades + if ( $style =~ /\bUPGRADEKEY\b/ ) { $installer::globals::minorupgradekey = $registry{'Key'}; } + + # Collecting all registry components with ALWAYS_REQUIRED style + if ( ! ( $style =~ /\bALWAYS_REQUIRED\b/ )) + { + # Setting a component condition for unforced registry components! + # Only write into registry, if WRITE_REGISTRY is set. + if ( $oneregistry->{'ComponentCondition'} ) { $oneregistry->{'ComponentCondition'} = "(" . $oneregistry->{'ComponentCondition'} . ") AND (WRITE_REGISTRY=1)"; } + else { $oneregistry->{'ComponentCondition'} = "WRITE_REGISTRY=1"; } + } + + # Collecting all component conditions + if ( $oneregistry->{'ComponentCondition'} ) + { + if ( ! exists($installer::globals::componentcondition{$registry{'Component_'}})) + { + $installer::globals::componentcondition{$registry{'Component_'}} = $oneregistry->{'ComponentCondition'}; + } + } + + my $oneline = $registry{'Registry'} . "\t" . $registry{'Root'} . "\t" . $registry{'Key'} . "\t" + . $registry{'Name'} . "\t" . $registry{'Value'} . "\t" . $registry{'Component_'} . "\n"; + + my $oneline64 = $registry{'Registry'} . "\t" . $registry{'Root'} . "\t" . $registry{'Key'} . "\t" + . $registry{'Name'} . "\t" . $registry{'Val64'} . "\t" . $registry{'Component_'} . "\n"; + + if ( ! ( $style =~ /\bX64_ONLY\b/ )) { push(@registrytable, $oneline); } # standard registry table for 32 Bit + if (( $style =~ /\bX64\b/ ) || ( $style =~ /\bX64_ONLY\b/ )) { push(@reg64table , $oneline64); } + } + + # If there are added user registry keys for files collected in + # @installer::globals::userregistrycollector (file.pm), then + # this registry keys have to be added now. This is necessary for + # files in PREDEFINED_OSSHELLNEWDIR, because their component + # needs as KeyPath a RegistryItem in HKCU. + + if ( $installer::globals::addeduserregitrykeys ) { add_userregs_to_registry_table(\@registrytable, $allvariableshashref); } + + # Saving the file + + my $registrytablename = $basedir . $installer::globals::separator . "Registry.idt" . "." . $onelanguage; + installer::files::save_file($registrytablename ,\@registrytable); + my $infoline = "Created idt file: $registrytablename\n"; + push(@installer::globals::logfileinfo, $infoline); + + $registrytablename = $basedir . $installer::globals::separator . "Reg64.idt" . "." . $onelanguage; + installer::files::save_file($registrytablename ,\@reg64table ); + my $infoline = "Created idt file: $registrytablename\n"; + push(@installer::globals::logfileinfo, $infoline); + } +} + +1; diff --git a/solenv/bin/modules/installer/windows/removefile.pm b/solenv/bin/modules/installer/windows/removefile.pm new file mode 100644 index 000000000000..73c276a44310 --- /dev/null +++ b/solenv/bin/modules/installer/windows/removefile.pm @@ -0,0 +1,156 @@ +#************************************************************************* +# +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# Copyright 2008 by Sun Microsystems, Inc. +# +# OpenOffice.org - a multi-platform office productivity suite +# +# $RCSfile: removefile.pm,v $ +# +# $Revision: 1.4 $ +# +# This file is part of OpenOffice.org. +# +# OpenOffice.org is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License version 3 +# only, as published by the Free Software Foundation. +# +# OpenOffice.org is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License version 3 for more details +# (a copy is included in the LICENSE file that accompanied this code). +# +# You should have received a copy of the GNU Lesser General Public License +# version 3 along with OpenOffice.org. If not, see +# <http://www.openoffice.org/license.html> +# for a copy of the LGPLv3 License. +# +#************************************************************************* + +package installer::windows::removefile; + +use installer::files; +use installer::globals; +use installer::windows::idtglobal; + +######################################################################## +# Returning the FileKey for a folderitem for removefile table. +######################################################################## + +sub get_removefile_filekey +{ + my ($folderitem) = @_; + + # returning the unique identifier + + my $identifier = "remove_" . $folderitem->{'directory'}; + + $identifier = lc($identifier); + + return $identifier; +} + +######################################################################## +# Returning the Component for a folderitem for removefile table. +######################################################################## + +sub get_removefile_component +{ + my ($folderitem) = @_; + + return $folderitem->{'component'}; +} + +######################################################################## +# Returning the FileName for a folderitem for removefile table. +######################################################################## + +sub get_removefile_filename +{ + my ($folderitem) = @_; + + # return nothing: The assigned directory will be removed + + return ""; +} + +######################################################################## +# Returning the DirProperty for a folderitem for removefile table. +######################################################################## + +sub get_removefile_dirproperty +{ + my ($folderitem) = @_; + + return $folderitem->{'directory'}; +} + +######################################################################## +# Returning the InstallMode for a folderitem for removefile table. +######################################################################## + +sub get_removefile_installmode +{ + my ($folderitem) = @_; + + # always returning "2": The file is only removed, if the assigned + # component is removed. Name: msidbRemoveFileInstallModeOnRemove + + return 2; +} + +########################################################################################################### +# Creating the file RemoveFi.idt dynamically +# Content: +# FileKey Component_ FileName DirProperty InstallMode +########################################################################################################### + +sub create_removefile_table +{ + my ($folderitemsref, $basedir) = @_; + + my @removefiletable = (); + + installer::windows::idtglobal::write_idt_header(\@removefiletable, "removefile"); + + # Only the directories created for the FolderItems have to be deleted + # with the information in the table RemoveFile + + my @directorycollector = (); + + for ( my $i = 0; $i <= $#{$folderitemsref}; $i++ ) + { + my $onelink = ${$folderitemsref}[$i]; + + if ( $onelink->{'used'} == 0 ) { next; } + + if ( installer::existence::exists_in_array($onelink->{'directory'}, \@directorycollector)) { next; } + + push(@directorycollector, $onelink->{'directory'}); + + my %removefile = (); + + $removefile{'FileKey'} = get_removefile_filekey($onelink); + $removefile{'Component_'} = get_removefile_component($onelink); + $removefile{'FileName'} = get_removefile_filename($onelink); + $removefile{'DirProperty'} = get_removefile_dirproperty($onelink); + $removefile{'InstallMode'} = get_removefile_installmode($onelink); + + my $oneline = $removefile{'FileKey'} . "\t" . $removefile{'Component_'} . "\t" . $removefile{'FileName'} . "\t" + . $removefile{'DirProperty'} . "\t" . $removefile{'InstallMode'} . "\n"; + + push(@removefiletable, $oneline); + } + + # Saving the file + + my $removefiletablename = $basedir . $installer::globals::separator . "RemoveFi.idt"; + installer::files::save_file($removefiletablename ,\@removefiletable); + my $infoline = "Created idt file: $removefiletablename\n"; + push(@installer::globals::logfileinfo, $infoline); + +} + +1;
\ No newline at end of file diff --git a/solenv/bin/modules/installer/windows/selfreg.pm b/solenv/bin/modules/installer/windows/selfreg.pm new file mode 100644 index 000000000000..821eae4dceb4 --- /dev/null +++ b/solenv/bin/modules/installer/windows/selfreg.pm @@ -0,0 +1,92 @@ +#************************************************************************* +# +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# Copyright 2008 by Sun Microsystems, Inc. +# +# OpenOffice.org - a multi-platform office productivity suite +# +# $RCSfile: selfreg.pm,v $ +# +# $Revision: 1.5 $ +# +# This file is part of OpenOffice.org. +# +# OpenOffice.org is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License version 3 +# only, as published by the Free Software Foundation. +# +# OpenOffice.org is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License version 3 for more details +# (a copy is included in the LICENSE file that accompanied this code). +# +# You should have received a copy of the GNU Lesser General Public License +# version 3 along with OpenOffice.org. If not, see +# <http://www.openoffice.org/license.html> +# for a copy of the LGPLv3 License. +# +#************************************************************************* + +package installer::windows::selfreg; + +use installer::exiter; +use installer::files; +use installer::globals; +use installer::worker; +use installer::windows::idtglobal; + +############################################################## +# Returning the cost for the selfreg table. +############################################################## + +sub get_selfreg_cost +{ + my ( $onefile ) = @_; + + return "0"; +} + +#################################################################################### +# Creating the file SelfReg.idt dynamically +# Content: +# File_ Cost +# UpgradeCode VersionMin VersionMax Language Attributes Remove ActionProperty +#################################################################################### + +sub create_selfreg_table +{ + my ($filesref, $basedir) = @_; + + my @selfregtable = (); + + installer::windows::idtglobal::write_idt_header(\@selfregtable, "selfreg"); + + # Registering all libraries with flag "SELFREG" + + my $selfregfiles = installer::worker::collect_all_items_with_special_flag($filesref, "SELFREG"); + + for ( my $i = 0; $i <= $#{$selfregfiles}; $i++ ) + { + my $onefile = ${$selfregfiles}[$i]; + + my %selfreg = (); + + $selfreg{'File_'} = $onefile->{'uniquename'}; + $selfreg{'Cost'} = get_selfreg_cost($onefile); + + my $oneline = $selfreg{'File_'} . "\t" . $selfreg{'Cost'} . "\n"; + + push(@selfregtable, $oneline); + } + + # Saving the file + + my $selfregtablename = $basedir . $installer::globals::separator . "SelfReg.idt"; + installer::files::save_file($selfregtablename ,\@selfregtable); + my $infoline = "Created idt file: $selfregtablename\n"; + push(@installer::globals::logfileinfo, $infoline); +} + +1;
\ No newline at end of file diff --git a/solenv/bin/modules/installer/windows/shortcut.pm b/solenv/bin/modules/installer/windows/shortcut.pm new file mode 100644 index 000000000000..acc0f7484910 --- /dev/null +++ b/solenv/bin/modules/installer/windows/shortcut.pm @@ -0,0 +1,716 @@ +#************************************************************************* +# +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# Copyright 2008 by Sun Microsystems, Inc. +# +# OpenOffice.org - a multi-platform office productivity suite +# +# $RCSfile: shortcut.pm,v $ +# +# $Revision: 1.15 $ +# +# This file is part of OpenOffice.org. +# +# OpenOffice.org is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License version 3 +# only, as published by the Free Software Foundation. +# +# OpenOffice.org is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License version 3 for more details +# (a copy is included in the LICENSE file that accompanied this code). +# +# You should have received a copy of the GNU Lesser General Public License +# version 3 along with OpenOffice.org. If not, see +# <http://www.openoffice.org/license.html> +# for a copy of the LGPLv3 License. +# +#************************************************************************* + +package installer::windows::shortcut; + +use installer::existence; +use installer::exiter; +use installer::files; +use installer::globals; +use installer::windows::idtglobal; + +############################################################## +# Returning the file object for the msiassembly table. +############################################################## + +sub get_file_by_name +{ + my ( $filesref, $filename ) = @_; + + my $foundfile = 0; + my $onefile; + + for ( my $i = 0; $i <= $#{$filesref}; $i++ ) + { + $onefile = ${$filesref}[$i]; + my $name = $onefile->{'Name'}; + + if ( $name eq $filename ) + { + $foundfile = 1; + last; + } + } + + if (! $foundfile ) { $onefile = ""; } + + return $onefile; +} + +############################################################## +# Returning identifier for shortcut table. +############################################################## + +sub get_shortcut_identifier +{ + my ($shortcut) = @_; + + my $identifier = $shortcut->{'gid'}; + + return $identifier; +} + +############################################################## +# Returning directory for shortcut table. +############################################################## + +sub get_shortcut_directory +{ + my ($shortcut, $dirref) = @_; + + # For shortcuts it is easy to convert the gid_Dir_Abc into the unique name in + # the directory table, for instance help_en_simpressidx. + # For files (components) this is not so easy, because files can be included + # in zip files with subdirectories that are not defined in scp. + + my $onedir; + my $shortcutdirectory = $shortcut->{'Dir'}; + my $directory = ""; + my $found = 0; + + for ( my $i = 0; $i <= $#{$dirref}; $i++ ) + { + $onedir = ${$dirref}[$i]; + my $directorygid = $onedir->{'Dir'}; + + if ( $directorygid eq $shortcutdirectory ) + { + $found = 1; + last; + } + } + + if (!($found)) + { + installer::exiter::exit_program("ERROR: Did not find DirectoryID $shortcutdirectory in directory collection for shortcut", "get_shortcut_directory"); + } + + $directory = $onedir->{'uniquename'}; + + if ($directory eq "") { $directory = "OFFICEINSTALLLOCATION"; } # Shortcuts in the root directory + + return $directory; +} + +############################################################## +# Returning name for shortcut table. +############################################################## + +sub get_shortcut_name +{ + my ($shortcut, $shortnamesref, $onelanguage) = @_; + + my $returnstring; + + my $name = $shortcut->{'Name'}; + + my $shortstring = installer::windows::idtglobal::make_eight_three_conform($name, "shortcut", $shortnamesref); + $shortstring =~ s/\s/\_/g; # replacing white spaces with underline + + if ( $shortstring eq $name ) { $returnstring = $name; } # nothing changed + else {$returnstring = $shortstring . "\|" . $name; } + + return $returnstring; +} + +############################################################## +# Returning component for shortcut table. +############################################################## + +sub get_shortcut_component +{ + my ($shortcut, $filesref) = @_; + + my $onefile; + my $component = ""; + my $found = 0; + my $shortcut_fileid = $shortcut->{'FileID'}; + + my $absolute_filename = 0; + if ( $shortcut->{'Styles'} ) { $styles = $shortcut->{'Styles'}; } + if ( $styles =~ /\bABSOLUTE_FILENAME\b/ ) { $absolute_filename = 1; } # FileID contains an absolute filename + if ( $styles =~ /\bUSE_HELPER_FILENAME\b/ ) { $absolute_filename = 1; } # ComponentIDFile contains id of a helper file + + # if the FileID contains an absolute filename, therefore the entry for "ComponentIDFile" has to be used. + if ( $absolute_filename ) { $shortcut_fileid = $shortcut->{'ComponentIDFile'}; } + + for ( my $i = 0; $i <= $#{$filesref}; $i++ ) + { + $onefile = ${$filesref}[$i]; + my $filegid = $onefile->{'gid'}; + + if ( $filegid eq $shortcut_fileid ) + { + $found = 1; + last; + } + } + + if (!($found)) + { + installer::exiter::exit_program("ERROR: Did not find FileID $shortcut_fileid in file collection for shortcut", "get_shortcut_component"); + } + + $component = $onefile->{'componentname'}; + + # finally saving the componentname in the folderitem collector + + $shortcut->{'component'} = $component; + + return $component; +} + +############################################################## +# Returning target for shortcut table. +############################################################## + +sub get_shortcut_target +{ + my ($shortcut, $filesref) = @_; + + my $target = ""; + my $found = 0; + my $shortcut_fileid = $shortcut->{'FileID'}; + my $onefile; + + for ( my $i = 0; $i <= $#{$filesref}; $i++ ) + { + $onefile = ${$filesref}[$i]; + my $filegid = $onefile->{'gid'}; + + if ( $filegid eq $shortcut_fileid ) + { + $found = 1; + last; + } + } + + if (!($found)) + { + installer::exiter::exit_program("ERROR: Did not find FileID $shortcut_fileid in file collection for shortcut", "get_shortcut_target"); + } + + if ( $onefile->{'Name'} ) + { + $target = $onefile->{'Name'}; + } + + $target = "\[\#" . $target . "\]"; # format for Non-Advertised shortcuts + + return $target; +} + +############################################################## +# Returning arguments for shortcut table. +############################################################## + +sub get_shortcut_arguments +{ + my ($shortcut) = @_; + + return ""; +} + +############################################################## +# Returning the localized description for shortcut table. +############################################################## + +sub get_shortcut_description +{ + my ($shortcut, $onelanguage) = @_; + + my $description = ""; + if ( $shortcut->{'Tooltip'} ) { $description = $shortcut->{'Tooltip'}; } + + return $description; +} + +############################################################## +# Returning hotkey for shortcut table. +############################################################## + +sub get_shortcut_hotkey +{ + my ($shortcut) = @_; + + return ""; +} + +############################################################## +# Returning icon for shortcut table. +############################################################## + +sub get_shortcut_icon +{ + my ($shortcut) = @_; + + return ""; +} + +############################################################## +# Returning iconindex for shortcut table. +############################################################## + +sub get_shortcut_iconindex +{ + my ($shortcut) = @_; + + return ""; +} + +############################################################## +# Returning show command for shortcut table. +############################################################## + +sub get_shortcut_showcmd +{ + my ($shortcut) = @_; + + return ""; +} + +############################################################## +# Returning working directory for shortcut table. +############################################################## + +sub get_shortcut_wkdir +{ + my ($shortcut) = @_; + + return ""; +} + +#################################################################### +# Returning working directory for shortcut table for FolderItems. +#################################################################### + +sub get_folderitem_wkdir +{ + my ($onelink, $dirref) = @_; + + # For shortcuts it is easy to convert the gid_Dir_Abc into the unique name in + # the directory table, for instance help_en_simpressidx. + + my $onedir; + my $workingdirectory = ""; + if ( $onelink->{'WkDir'} ) { $workingdirectory = $onelink->{'WkDir'}; } + my $directory = ""; + + if ( $workingdirectory ) + { + my $found = 0; + + for ( my $i = 0; $i <= $#{$dirref}; $i++ ) + { + $onedir = ${$dirref}[$i]; + my $directorygid = $onedir->{'Dir'}; + + if ( $directorygid eq $workingdirectory ) + { + $found = 1; + last; + } + } + + if (!($found)) + { + installer::exiter::exit_program("ERROR: Did not find DirectoryID $workingdirectory in directory collection for FolderItem", "get_folderitem_wkdir"); + } + + $directory = $onedir->{'uniquename'}; + + if ($directory eq "") { $directory = "OFFICEINSTALLLOCATION"; } + } + + return $directory; +} + +################################################################### +# Returning the directory for a folderitem for shortcut table. +################################################################### + +sub get_folderitem_directory +{ + my ($shortcut) = @_; + + # my $directory = "$installer::globals::programmenufolder"; # default + my $directory = "$installer::globals::officemenufolder"; # default + + # The value $installer::globals::programmenufolder is not correct for the + # PREDEFINED folders, like PREDEFINED_AUTOSTART + + if ( $shortcut->{'FolderID'} eq "PREDEFINED_AUTOSTART" ) + { + $directory = $installer::globals::startupfolder; + } + + if ( $shortcut->{'FolderID'} eq "PREDEFINED_DESKTOP" ) + { + $directory = $installer::globals::desktopfolder; + $installer::globals::desktoplinkexists = 1; + } + + if ( $shortcut->{'FolderID'} eq "PREDEFINED_STARTMENU" ) + { + $directory = $installer::globals::startmenufolder; + } + + # saving the directory in the folderitems collector + + $shortcut->{'directory'} = $directory; + + return $directory; +} + +######################################################################## +# Returning the target (feature) for a folderitem for shortcut table. +# For non-advertised shortcuts this is a formatted string. +######################################################################## + +sub get_folderitem_target +{ + my ($shortcut, $filesref) = @_; + + my $onefile; + my $target = ""; + my $found = 0; + my $shortcut_fileid = $shortcut->{'FileID'}; + + my $styles = ""; + my $nonadvertised = 0; + my $absolute_filename = 0; + if ( $shortcut->{'Styles'} ) { $styles = $shortcut->{'Styles'}; } + if ( $styles =~ /\bNON_ADVERTISED\b/ ) { $nonadvertised = 1; } # this is a non-advertised shortcut + if ( $styles =~ /\bABSOLUTE_FILENAME\b/ ) { $absolute_filename = 1; } # FileID contains an absolute filename + + # if the FileID contains an absolute filename this can simply be returned as target for the shortcut table. + if ( $absolute_filename ) + { + $shortcut->{'target'} = $shortcut_fileid; + return $shortcut_fileid; + } + + for ( my $i = 0; $i <= $#{$filesref}; $i++ ) + { + $onefile = ${$filesref}[$i]; + my $filegid = $onefile->{'gid'}; + + if ( $filegid eq $shortcut_fileid ) + { + $found = 1; + last; + } + } + + if (!($found)) + { + installer::exiter::exit_program("ERROR: Did not find FileID $shortcut_fileid in file collection for folderitem", "get_folderitem_target"); + } + + # Non advertised shortcuts do not return the feature, but the path to the file + if ( $nonadvertised ) + { + $target = "\[" . $onefile->{'uniquedirname'} . "\]" . "\\" . $onefile->{'Name'}; + $shortcut->{'target'} = $target; + return $target; + } + + # the rest only for advertised shortcuts, which contain the feature in the shortcut table. + + if ( $onefile->{'modules'} ) { $target = $onefile->{'modules'}; } + + # If modules contains a list of modules, only taking the first one. + # But this should never be needed + + if ( $target =~ /^\s*(.*?)\,/ ) { $target = $1; } + + # Attention: Maximum feature length is 38! + installer::windows::idtglobal::shorten_feature_gid(\$target); + + # and finally saving the target in the folderitems collector + + $shortcut->{'target'} = $target; + + return $target; +} + +######################################################################## +# Returning the arguments for a folderitem for shortcut table. +######################################################################## + +sub get_folderitem_arguments +{ + my ($shortcut) = @_; + + my $parameter = ""; + + if ( $shortcut->{'Parameter'} ) { $parameter = $shortcut->{'Parameter'}; } + + return $parameter; +} + +######################################################################## +# Returning the icon for a folderitem for shortcut table. +# The returned value has to be defined in the icon table. +######################################################################## + +sub get_folderitem_icon +{ + my ($shortcut, $filesref, $iconfilecollector) = @_; + + my $styles = ""; + if ( $shortcut->{'Styles'} ) { $styles = $shortcut->{'Styles'}; } + if ( $styles =~ /\bNON_ADVERTISED\b/ ) { return ""; } # no icon for non-advertised shortcuts + + my $iconfilegid = ""; + + if ( $shortcut->{'IconFile'} ) { $iconfilegid = $shortcut->{'IconFile'}; } + else { $iconfilegid = $shortcut->{'FileID'}; } + + my $onefile; + my $found = 0; + + for ( my $i = 0; $i <= $#{$filesref}; $i++ ) + { + $onefile = ${$filesref}[$i]; + my $filegid = $onefile->{'gid'}; + + if ( $filegid eq $iconfilegid ) + { + $found = 1; + last; + } + } + + if (!($found)) + { + installer::exiter::exit_program("ERROR: Did not find FileID $iconfilegid in file collection", "get_folderitem_icon"); + } + + $iconfile = $onefile->{'Name'}; + + # collecting all icon files to copy them into the icon directory + + my $sourcepath = $onefile->{'sourcepath'}; + + if (! installer::existence::exists_in_array($sourcepath, $iconfilecollector)) + { + push(@{$iconfilecollector}, $sourcepath); + } + + return $iconfile; +} + +######################################################################## +# Returning the iconindex for a folderitem for shortcut table. +######################################################################## + +sub get_folderitem_iconindex +{ + my ($shortcut) = @_; + + my $styles = ""; + if ( $shortcut->{'Styles'} ) { $styles = $shortcut->{'Styles'}; } + if ( $styles =~ /\bNON_ADVERTISED\b/ ) { return ""; } # no iconindex for non-advertised shortcuts + + my $iconid = 0; + + if ( $shortcut->{'IconID'} ) { $iconid = $shortcut->{'IconID'}; } + + return $iconid; +} + +######################################################################## +# Returning the show command for a folderitem for shortcut table. +######################################################################## + +sub get_folderitem_showcmd +{ + my ($shortcut) = @_; + + return "1"; +} + +########################################################################################################### +# Creating the file Shortcut.idt dynamically +# Content: +# Shortcut Directory_ Name Component_ Target Arguments Description Hotkey Icon_ IconIndex ShowCmd WkDir +########################################################################################################### + +sub create_shortcut_table +{ + my ($filesref, $linksref, $folderref, $folderitemsref, $dirref, $basedir, $languagesarrayref, $includepatharrayref, $iconfilecollector) = @_; + + for ( my $m = 0; $m <= $#{$languagesarrayref}; $m++ ) + { + my $onelanguage = ${$languagesarrayref}[$m]; + + my @shortcuttable = (); + + my @shortnames = (); # to collect all short names + + installer::windows::idtglobal::write_idt_header(\@shortcuttable, "shortcut"); + + # First the links, defined in scp as ShortCut + + for ( my $i = 0; $i <= $#{$linksref}; $i++ ) + { + my $onelink = ${$linksref}[$i]; + + # Controlling the language! + # Only language independent folderitems or folderitems with the correct language + # will be included into the table + + if (! (!(( $onelink->{'ismultilingual'} )) || ( $onelink->{'specificlanguage'} eq $onelanguage )) ) { next; } + + my %shortcut = (); + + $shortcut{'Shortcut'} = get_shortcut_identifier($onelink); + $shortcut{'Directory_'} = get_shortcut_directory($onelink, $dirref); + $shortcut{'Name'} = get_shortcut_name($onelink, \@shortnames, $onelanguage); # localized name + $shortcut{'Component_'} = get_shortcut_component($onelink, $filesref); + $shortcut{'Target'} = get_shortcut_target($onelink, $filesref); + $shortcut{'Arguments'} = get_shortcut_arguments($onelink); + $shortcut{'Description'} = get_shortcut_description($onelink, $onelanguage); # localized description + $shortcut{'Hotkey'} = get_shortcut_hotkey($onelink); + $shortcut{'Icon_'} = get_shortcut_icon($onelink); + $shortcut{'IconIndex'} = get_shortcut_iconindex($onelink); + $shortcut{'ShowCmd'} = get_shortcut_showcmd($onelink); + $shortcut{'WkDir'} = get_shortcut_wkdir($onelink); + + my $oneline = $shortcut{'Shortcut'} . "\t" . $shortcut{'Directory_'} . "\t" . $shortcut{'Name'} . "\t" + . $shortcut{'Component_'} . "\t" . $shortcut{'Target'} . "\t" . $shortcut{'Arguments'} . "\t" + . $shortcut{'Description'} . "\t" . $shortcut{'Hotkey'} . "\t" . $shortcut{'Icon_'} . "\t" + . $shortcut{'IconIndex'} . "\t" . $shortcut{'ShowCmd'} . "\t" . $shortcut{'WkDir'} . "\n"; + + push(@shortcuttable, $oneline); + } + + # Second the entries into the start menu, defined in scp as Folder and Folderitem + # These shortcuts will fill the icons table. + + for ( my $i = 0; $i <= $#{$folderref}; $i++ ) + { + my $foldergid = ${$folderref}[$i]->{'gid'}; + + # iterating over all folderitems for this folder + + for ( my $j = 0; $j <= $#{$folderitemsref}; $j++ ) + { + my $onelink = ${$folderitemsref}[$j]; + + # Controlling the language! + # Only language independent folderitems or folderitems with the correct language + # will be included into the table + + if (! (!(( $onelink->{'ismultilingual'} )) || ( $onelink->{'specificlanguage'} eq $onelanguage )) ) { next; } + + # controlling the folder + + my $localused = 0; + + if ( $onelink->{'used'} ) { $localused = $onelink->{'used'}; } + + if (!($localused == 1)) { $onelink->{'used'} = "0"; } # no resetting + + if (!( $onelink->{'FolderID'} eq $foldergid )) { next; } + + $onelink->{'used'} = "1"; + + my %shortcut = (); + + $shortcut{'Shortcut'} = get_shortcut_identifier($onelink); + $shortcut{'Directory_'} = get_folderitem_directory($onelink); + $shortcut{'Name'} = get_shortcut_name($onelink, \@shortnames, $onelanguage); # localized name + $shortcut{'Component_'} = get_shortcut_component($onelink, $filesref); + $shortcut{'Target'} = get_folderitem_target($onelink, $filesref); + $shortcut{'Arguments'} = get_folderitem_arguments($onelink); + $shortcut{'Description'} = get_shortcut_description($onelink, $onelanguage); # localized description + $shortcut{'Hotkey'} = get_shortcut_hotkey($onelink); + $shortcut{'Icon_'} = get_folderitem_icon($onelink, $filesref, $iconfilecollector); + $shortcut{'IconIndex'} = get_folderitem_iconindex($onelink); + $shortcut{'ShowCmd'} = get_folderitem_showcmd($onelink); + $shortcut{'WkDir'} = get_folderitem_wkdir($onelink, $dirref); + + my $oneline = $shortcut{'Shortcut'} . "\t" . $shortcut{'Directory_'} . "\t" . $shortcut{'Name'} . "\t" + . $shortcut{'Component_'} . "\t" . $shortcut{'Target'} . "\t" . $shortcut{'Arguments'} . "\t" + . $shortcut{'Description'} . "\t" . $shortcut{'Hotkey'} . "\t" . $shortcut{'Icon_'} . "\t" + . $shortcut{'IconIndex'} . "\t" . $shortcut{'ShowCmd'} . "\t" . $shortcut{'WkDir'} . "\n"; + + push(@shortcuttable, $oneline); + } + } + + # if it is part of the product, the soffice.exe has to be included into the icon table + # as icon for the ARP applet + + my $sofficefile = "soffice.exe"; + my $onefile = get_file_by_name($filesref, $sofficefile); + + if ( $onefile ne "" ) + { + my $sourcepath = $onefile->{'sourcepath'}; + if (! installer::existence::exists_in_array($sourcepath, $iconfilecollector)) + { + unshift(@{$iconfilecollector}, $sourcepath); + $installer::globals::sofficeiconadded = 1; + } + } + + # For language packs and patches the soffice.exe has to be included, even if it is not part of the product. + # Also as part of the ARP applet (no substitution needed for ProductName, because the file is not installed!) + + if (( $onefile eq "" ) && (( $installer::globals::languagepack ) || ( $installer::globals::patch ))) + { + my $sourcepathref = installer::scriptitems::get_sourcepath_from_filename_and_includepath(\$sofficefile, $includepatharrayref, 1); + if ($$sourcepathref eq "") { installer::exiter::exit_program("ERROR: Could not find $sofficefile as icon in language pack!", "create_shortcut_table"); } + + if (! installer::existence::exists_in_array($$sourcepathref, $iconfilecollector)) + { + unshift(@{$iconfilecollector}, $$sourcepathref); + $installer::globals::sofficeiconadded = 1; + } + + my $localinfoline = "Added icon file $$sourcepathref for language pack into icon file collector.\n"; + push(@installer::globals::logfileinfo, $localinfoline); + } + + # Saving the file + + my $shortcuttablename = $basedir . $installer::globals::separator . "Shortcut.idt" . "." . $onelanguage; + installer::files::save_file($shortcuttablename ,\@shortcuttable); + my $infoline = "Created idt file: $shortcuttablename\n"; + push(@installer::globals::logfileinfo, $infoline); + } +} + + +1;
\ No newline at end of file diff --git a/solenv/bin/modules/installer/windows/sign.pm b/solenv/bin/modules/installer/windows/sign.pm new file mode 100644 index 000000000000..ecefbd9fd1f8 --- /dev/null +++ b/solenv/bin/modules/installer/windows/sign.pm @@ -0,0 +1,1002 @@ +#************************************************************************* +# +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# Copyright 2008 by Sun Microsystems, Inc. +# +# OpenOffice.org - a multi-platform office productivity suite +# +# $RCSfile: binary.pm,v $ +# +# This file is part of OpenOffice.org. +# +# OpenOffice.org is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License version 3 +# only, as published by the Free Software Foundation. +# +# OpenOffice.org is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License version 3 for more details +# (a copy is included in the LICENSE file that accompanied this code). +# +# You should have received a copy of the GNU Lesser General Public License +# version 3 along with OpenOffice.org. If not, see +# <http://www.openoffice.org/license.html> +# for a copy of the LGPLv3 License. +# +#************************************************************************* + +package installer::windows::sign; + +use Cwd; +use installer::converter; +use installer::existence; +use installer::files; +use installer::globals; +use installer::scriptitems; +use installer::worker; +use installer::windows::admin; + +######################################################## +# Copying an existing Windows installation set. +######################################################## + +sub copy_install_set +{ + my ( $installsetpath ) = @_; + + installer::logger::include_header_into_logfile("Start: Copying installation set $installsetpath"); + + my $infoline = ""; + + my $dirname = $installsetpath; + installer::pathanalyzer::make_absolute_filename_to_relative_filename(\$dirname); + + my $path = $installsetpath; + installer::pathanalyzer::get_path_from_fullqualifiedname(\$path); + + $path =~ s/\Q$installer::globals::separator\E\s*$//; + + if ( $dirname =~ /\./ ) { $dirname =~ s/\./_signed_inprogress./; } + else { $dirname = $dirname . "_signed_inprogress"; } + + my $newpath = $path . $installer::globals::separator . $dirname; + my $removepath = $newpath; + $removepath =~ s/_inprogress/_witherror/; + + if ( -d $newpath ) { installer::systemactions::remove_complete_directory($newpath, 1); } + if ( -d $removepath ) { installer::systemactions::remove_complete_directory($removepath, 1); } + + $infoline = "Copy installation set from $installsetpath to $newpath\n"; + push( @installer::globals::logfileinfo, $infoline); + + $installsetpath = installer::systemactions::copy_complete_directory($installsetpath, $newpath); + + installer::logger::include_header_into_logfile("End: Copying installation set $installsetpath"); + + return $newpath; +} + +######################################################## +# Renaming an existing Windows installation set. +######################################################## + +sub rename_install_set +{ + my ( $installsetpath ) = @_; + + my $infoline = ""; + + my $dirname = $installsetpath; + installer::pathanalyzer::make_absolute_filename_to_relative_filename(\$dirname); + + my $path = $installsetpath; + installer::pathanalyzer::get_path_from_fullqualifiedname(\$path); + + $path =~ s/\Q$installer::globals::separator\E\s*$//; + + if ( $dirname =~ /\./ ) { $dirname =~ s/\./_inprogress./; } + else { $dirname = $dirname . "_inprogress"; } + + my $newpath = $path . $installer::globals::separator . $dirname; + my $removepath = $newpath; + $removepath =~ s/_inprogress/_witherror/; + + if ( -d $newpath ) { installer::systemactions::remove_complete_directory($newpath, 1); } + if ( -d $removepath ) { installer::systemactions::remove_complete_directory($removepath, 1); } + + $installsetpath = installer::systemactions::rename_directory($installsetpath, $newpath); + + return $newpath; +} + +######################################################### +# Checking the local system +# Checking existence of needed files in include path +######################################################### + +sub check_system_path +{ + # The following files have to be found in the environment variable PATH + # Only, if \"-sign\" is used. + # Windows : "msicert.exe", "msidb.exe", "signtool.exe" + + my @needed_files_in_path = ("msicert.exe", "msidb.exe", "signtool.exe"); + if ( $installer::globals::internal_cabinet_signing ) + { + push(@needed_files_in_path, "cabarc.exe"); + push(@needed_files_in_path, "makecab.exe"); + } + + my $onefile; + my $error = 0; + my $pathvariable = $ENV{'PATH'}; + my $local_pathseparator = $installer::globals::pathseparator; + + if( $^O =~ /cygwin/i ) + { # When using cygwin's perl the PATH variable is POSIX style and ... + $pathvariable = qx{cygpath -mp "$pathvariable"} ; + # has to be converted to DOS style for further use. + $local_pathseparator = ';'; + } + + my $patharrayref = installer::converter::convert_stringlist_into_array(\$pathvariable, $local_pathseparator); + + $installer::globals::patharray = $patharrayref; + + foreach my $onefile ( @needed_files_in_path ) + { + installer::logger::print_message( "...... searching $onefile ..." ); + + my $fileref = installer::scriptitems::get_sourcepath_from_filename_and_includepath_classic(\$onefile, $patharrayref , 0); + + if ( $$fileref eq "" ) + { + $error = 1; + installer::logger::print_error( "$onefile not found\n" ); + } + else + { + installer::logger::print_message( "\tFound: $$fileref\n" ); + } + } + + $installer::globals::signfiles_checked = 1; + + if ( $error ) { installer::exiter::exit_program("ERROR: Could not find all needed files in path!", "check_system_path"); } +} + +###################################################### +# Making systemcall +###################################################### + +sub make_systemcall +{ + my ($systemcall, $displaysystemcall) = @_; + + installer::logger::print_message( "... $displaysystemcall ...\n" ); + + my $success = 1; + my $returnvalue = system($systemcall); + + my $infoline = "Systemcall: $displaysystemcall\n"; + push( @installer::globals::logfileinfo, $infoline); + + if ($returnvalue) + { + $infoline = "ERROR: Could not execute \"$displaysystemcall\"!\n"; + push( @installer::globals::logfileinfo, $infoline); + $success = 0; + } + else + { + $infoline = "Success: Executed \"$displaysystemcall\" successfully!\n"; + push( @installer::globals::logfileinfo, $infoline); + } + + return $success; +} + +###################################################### +# Making systemcall with warning +###################################################### + +sub make_systemcall_with_warning +{ + my ($systemcall, $displaysystemcall) = @_; + + installer::logger::print_message( "... $displaysystemcall ...\n" ); + + my $success = 1; + my $returnvalue = system($systemcall); + + my $infoline = "Systemcall: $displaysystemcall\n"; + push( @installer::globals::logfileinfo, $infoline); + + if ($returnvalue) + { + $infoline = "WARNING: Could not execute \"$displaysystemcall\"!\n"; + push( @installer::globals::logfileinfo, $infoline); + $success = 0; + } + else + { + $infoline = "Success: Executed \"$displaysystemcall\" successfully!\n"; + push( @installer::globals::logfileinfo, $infoline); + } + + return $success; +} + +###################################################### +# Making systemcall with more return data +###################################################### + +sub execute_open_system_call +{ + my ( $systemcall ) = @_; + + my @openoutput = (); + my $success = 1; + + my $comspec = $ENV{COMSPEC}; + $comspec = $comspec . " -c "; + + if( $^O =~ /cygwin/i ) + { + # $comspec =~ s/\\/\\\\/g; + # $comspec = qx{cygpath -u "$comspec"}; + # $comspec =~ s/\s*$//g; + $comspec = ""; + } + + my $localsystemcall = "$comspec $systemcall 2>&1 |"; + + open( OPN, "$localsystemcall") or warn "Can't execute $localsystemcall\n"; + while (<OPN>) { push(@openoutput, $_); } + close (OPN); + + my $returnvalue = $?; # $? contains the return value of the systemcall + + if ($returnvalue) + { + $infoline = "ERROR: Could not execute \"$systemcall\"!\n"; + push( @installer::globals::logfileinfo, $infoline); + $success = 0; + } + else + { + $infoline = "Success: Executed \"$systemcall\" successfully!\n"; + push( @installer::globals::logfileinfo, $infoline); + } + + return ($success, \@openoutput); +} + +######################################################## +# Reading first line of pw file. +######################################################## + +sub get_pw +{ + my ( $file ) = @_; + + my $filecontent = installer::files::read_file($file); + + my $pw = ${$filecontent}[0]; + $pw =~ s/^\s*//; + $pw =~ s/\s*$//; + + return $pw; +} + +######################################################## +# Counting the keys of a hash. +######################################################## + +sub get_hash_count +{ + my ($hashref) = @_; + + my $counter = 0; + + foreach my $key ( keys %{$hashref} ) { $counter++; } + + return $counter; +} + +############################################################ +# Collect all DiskIds to the corresponding cabinet files. +############################################################ + +sub analyze_media_file +{ + my ($filecontent) = @_; + + my %diskidhash = (); + + for ( my $i = 0; $i <= $#{$filecontent}; $i++ ) + { + if ( $i < 3 ) { next; } + + if ( ${$filecontent}[$i] =~ /^\s*(.*?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\s*$/ ) + { + my $diskid = $1; + my $cabfile = $4; + + $diskidhash{$cabfile} = $diskid; + } + } + + return \%diskidhash; +} + +######################################################## +# Collect all DiskIds from database table "Media". +######################################################## + +sub collect_diskid_from_media_table +{ + my ($msidatabase, $languagestring) = @_; + + # creating working directory + my $workdir = installer::systemactions::create_directories("media", \$languagestring); + installer::windows::admin::extract_tables_from_pcpfile($msidatabase, $workdir, "Media"); + + # Reading tables + my $filename = $workdir . $installer::globals::separator . "Media.idt"; + if ( ! -f $filename ) { installer::exiter::exit_program("ERROR: Could not find required file: $filename !", "collect_diskid_from_media_table"); } + my $filecontent = installer::files::read_file($filename); + my $diskidhash = analyze_media_file($filecontent); + + return $diskidhash; +} + +######################################################## +# Check, if this installation set contains +# internal cabinet files included into the msi +# database. +######################################################## + +sub check_for_internal_cabfiles +{ + my ($cabfilehash) = @_; + + my $contains_internal_cabfiles = 0; + my %allcabfileshash = (); + + foreach my $filename ( keys %{$cabfilehash} ) + { + if ( $filename =~ /^\s*\#/ ) # starting with a hash + { + $contains_internal_cabfiles = 1; + # setting real filename without hash as key and name with hash as value + my $realfilename = $filename; + $realfilename =~ s/^\s*\#//; + $allcabfileshash{$realfilename} = $filename; + } + } + + return ( $contains_internal_cabfiles, \%allcabfileshash ); +} + +######################################################## +# Collecting all files in an installation set. +######################################################## + +sub analyze_installset_content +{ + my ( $installsetpath ) = @_; + + my @sourcefiles = (); + my $pathstring = ""; + installer::systemactions::read_complete_directory($installsetpath, $pathstring, \@sourcefiles); + + if ( ! ( $#sourcefiles > -1 )) { installer::exiter::exit_program("ERROR: No file in installation set. Path: $installsetpath !", "analyze_installset_content"); } + + my %allcabfileshash = (); + my %allmsidatabaseshash = (); + my %allfileshash = (); + my $contains_external_cabfiles = 0; + my $msidatabase = ""; + my $contains_msidatabase = 0; + + for ( my $j = 0; $j <= $#sourcefiles; $j++ ) + { + if ( $sourcefiles[$j] =~ /\.cab\s*$/ ) { $allcabfileshash{$sourcefiles[$j]} = 1; } + else + { + if ( $sourcefiles[$j] =~ /\.txt\s*$/ ) { next; } + if ( $sourcefiles[$j] =~ /\.html\s*$/ ) { next; } + if ( $sourcefiles[$j] =~ /\.ini\s*$/ ) { next; } + if ( $sourcefiles[$j] =~ /\.bmp\s*$/ ) { next; } + if ( $sourcefiles[$j] =~ /\.msi\s*$/ ) + { + if ( $msidatabase eq "" ) { $msidatabase = $sourcefiles[$j]; } + else { installer::exiter::exit_program("ERROR: There is more than one msi database in installation set. Path: $installsetpath !", "analyze_installset_content"); } + } + $allfileshash{$sourcefiles[$j]} = 1; + } + } + + # Is there at least one cab file in the installation set? + my $cabcounter = get_hash_count(\%allcabfileshash); + if ( $cabcounter > 0 ) { $contains_external_cabfiles = 1; } + + # How about a cab file without a msi database? + if (( $cabcounter > 0 ) && ( $msidatabase eq "" )) { installer::exiter::exit_program("ERROR: There is no msi database in the installation set, but an external cabinet file. Path: $installsetpath !", "collect_installset_content"); } + + if ( $msidatabase ne "" ) { $contains_msidatabase = 1; } + + return (\%allcabfileshash, \%allfileshash, $msidatabase, $contains_external_cabfiles, $contains_msidatabase); +} + +######################################################## +# Adding content of external cabinet files into the +# msi database +######################################################## + +sub msicert_database +{ + my ($msidatabase, $allcabfiles, $cabfilehash, $internalcabfile) = @_; + + my $fullsuccess = 1; + + foreach my $cabfile ( keys %{$allcabfiles} ) + { + my $mediacabfilename = $cabfile; + if ( $internalcabfile ) { $mediacabfilename = "\#" . $mediacabfilename; } + if ( ! exists($cabfilehash->{$mediacabfilename}) ) { installer::exiter::exit_program("ERROR: Could not determine DiskId from media table for cabinet file \"$cabfile\" !", "msicert_database"); } + my $diskid = $cabfilehash->{$mediacabfilename}; + + my $systemcall = "msicert.exe -d $msidatabase -m $diskid -c $cabfile -h"; + $success = make_systemcall($systemcall, $systemcall); + if ( ! $success ) { $fullsuccess = 0; } + } + + return $fullsuccess; +} + +######################################################## +# Signing a list of files +######################################################## + +sub sign_files +{ + my ( $followmeinfohash, $allfiles, $pw, $cabinternal ) = @_; + + my $infoline = ""; + my $fullsuccess = 1; + my $maxcounter = 3; + + my $productname = ""; + if ( $followmeinfohash->{'allvariableshash'}->{'PRODUCTNAME'} ) { $productname = "/d " . "\"$followmeinfohash->{'allvariableshash'}->{'PRODUCTNAME'}\""; } + my $url = ""; + if (( ! exists($followmeinfohash->{'allvariableshash'}->{'OPENSOURCE'}) ) || ( $followmeinfohash->{'allvariableshash'}->{'OPENSOURCE'} == 0 )) { $url = "/du " . "\"http://www.sun.com\""; } + else { $url = "/du " . "\"http://www.openoffice.org\""; } + my $timestampurl = "http://timestamp.verisign.com/scripts/timestamp.dll"; + + my $pfxfilepath = $installer::globals::pfxfile; + + if( $^O =~ /cygwin/i ) + { + $pfxfilepath = qx{cygpath -w "$pfxfilepath"}; + $pfxfilepath =~ s/\\/\\\\/g; + $pfxfilepath =~ s/\s*$//g; + } + + foreach my $onefile ( reverse sort keys %{$allfiles} ) + { + if ( already_certified($onefile) ) + { + $infoline = "Already certified: Skipping file $onefile\n"; + push( @installer::globals::logfileinfo, $infoline); + next; + } + + my $counter = 1; + my $success = 0; + + while (( $counter <= $maxcounter ) && ( ! $success )) + { + if ( $counter > 1 ) { installer::logger::print_message( "\n\n... repeating file $onefile ...\n" ); } + if ( $cabinternal ) { installer::logger::print_message(" Signing: $onefile\n"); } + my $systemcall = "signtool.exe sign /f \"$pfxfilepath\" /p $pw $productname $url /t \"$timestampurl\" \"$onefile\""; + my $displaysystemcall = "signtool.exe sign /f \"$pfxfilepath\" /p ***** $productname $url /t \"$timestampurl\" \"$onefile\""; + $success = make_systemcall_with_warning($systemcall, $displaysystemcall); + $counter++; + } + + if ( ! $success ) + { + $fullsuccess = 0; + installer::exiter::exit_program("ERROR: Could not sign file: $onefile!", "sign_files"); + } + } + + return $fullsuccess; +} + +########################################################################## +# 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); +} + +################################################################# +# Setting the path, where the cab files are unpacked. +################################################################# + +sub get_cab_path +{ + my ($temppath) = @_; + + my $cabpath = "cabs_" . $$; + $cabpath = $temppath . $installer::globals::separator . $cabpath; + if ( ! -d $cabpath ) { installer::systemactions::create_directory($cabpath); } + + return $cabpath; +} + +################################################################# +# Exclude all cab files from the msi database. +################################################################# + +sub extract_cabs_from_database +{ + my ($msidatabase, $allcabfiles) = @_; + + installer::logger::include_header_into_logfile("Extracting cabs from msi database"); + + my $infoline = ""; + my $fullsuccess = 1; + my $msidb = "msidb.exe"; # Has to be in the path + + # msidb.exe really wants backslashes. (And double escaping because system() expands the string.) + $msidatabase =~ s/\//\\\\/g; + + foreach my $onefile ( keys %{$allcabfiles} ) + { + my $systemcall = $msidb . " -d " . $msidatabase . " -x " . $onefile; + my $success = make_systemcall($systemcall, $systemcall); + if ( ! $success ) { $fullsuccess = 0; } + + # and removing the stream from the database + $systemcall = $msidb . " -d " . $msidatabase . " -k " . $onefile; + $success = make_systemcall($systemcall, $systemcall); + if ( ! $success ) { $fullsuccess = 0; } + } + + return $fullsuccess; +} + +################################################################# +# Include cab files into the msi database. +################################################################# + +sub include_cabs_into_database +{ + my ($msidatabase, $allcabfiles) = @_; + + installer::logger::include_header_into_logfile("Including cabs into msi database"); + + my $infoline = ""; + my $fullsuccess = 1; + my $msidb = "msidb.exe"; # Has to be in the path + + # msidb.exe really wants backslashes. (And double escaping because system() expands the string.) + $msidatabase =~ s/\//\\\\/g; + + foreach my $onefile ( keys %{$allcabfiles} ) + { + my $systemcall = $msidb . " -d " . $msidatabase . " -a " . $onefile; + my $success = make_systemcall($systemcall, $systemcall); + if ( ! $success ) { $fullsuccess = 0; } + } + + return $fullsuccess; +} + +######################################################## +# Reading the order of the files inside the +# cabinet files. +######################################################## + +sub read_cab_file +{ + my ($cabfilename) = @_; + + installer::logger::print_message( "\n... reading cabinet file $cabfilename ...\n" ); + my $infoline = "Reading cabinet file $cabfilename\n"; + push( @installer::globals::logfileinfo, $infoline); + + my $systemcall = "cabarc.exe" . " L " . $cabfilename; + push(@logfile, "$systemcall\n"); + + my ($success, $fileorder) = execute_open_system_call($systemcall); + + my @allfiles = (); + + for ( my $i = 0; $i <= $#{$fileorder}; $i++ ) + { + my $line = ${$fileorder}[$i]; + if ( $line =~ /^\s*(.*?)\s+\d+\s+\d+\/\d+\/\d+\s+\d+\:\d+\:\d+\s+[\w-]+\s*$/ ) + { + my $filename = $1; + push(@allfiles, $filename); + } + } + + return \@allfiles; +} + +######################################################## +# Unpacking a cabinet file. +######################################################## + +sub unpack_cab_file +{ + my ($cabfilename, $temppath) = @_; + + installer::logger::print_message( "\n... unpacking cabinet file $cabfilename ...\n" ); + my $infoline = "Unpacking cabinet file $cabfilename\n"; + push( @installer::globals::logfileinfo, $infoline); + + my $dirname = $cabfilename; + $dirname =~ s/\.cab\s*$//; + my $workingpath = $temppath . $installer::globals::separator . "unpack_". $dirname . "_" . $$; + if ( ! -d $workingpath ) { installer::systemactions::create_directory($workingpath); } + + # changing into unpack directory + my $from = cwd(); + chdir($workingpath); + + my $fullcabfilename = $from . $installer::globals::separator . $cabfilename; + + if( $^O =~ /cygwin/i ) + { + $fullcabfilename = qx{cygpath -w "$fullcabfilename"}; + $fullcabfilename =~ s/\\/\\\\/g; + $fullcabfilename =~ s/\s*$//g; + } + + my $systemcall = "cabarc.exe" . " -p X " . $fullcabfilename; + $success = make_systemcall($systemcall, $systemcall); + if ( ! $success ) { installer::exiter::exit_program("ERROR: Could not unpack cabinet file: $fullcabfilename!", "unpack_cab_file"); } + + # returning to directory + chdir($from); + + return $workingpath; +} + +######################################################## +# Returning the header of a ddf file. +######################################################## + +sub get_ddf_file_header +{ + my ($ddffileref, $cabinetfile, $installdir) = @_; + + my $oneline; + my $compressionlevel = 2; + + if( $^O =~ /cygwin/i ) + { + $installdir = qx{cygpath -w "$installdir"}; + $installdir =~ s/\s*$//g; + } + + $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=CDROM\n"; # This allows the .cab file to be as large as needed. + push(@{$ddffileref} ,$oneline); + $oneline = ".Set CompressionType=LZX\n"; + push(@{$ddffileref} ,$oneline); + $oneline = ".Set Compress=ON\n"; + push(@{$ddffileref} ,$oneline); + $oneline = ".Set CompressionLevel=$compressionlevel\n"; + push(@{$ddffileref} ,$oneline); + $oneline = ".Set Cabinet=ON\n"; + push(@{$ddffileref} ,$oneline); + $oneline = ".Set DiskDirectoryTemplate=" . $installdir . "\n"; + push(@{$ddffileref} ,$oneline); +} + +######################################################## +# Writing content into ddf file. +######################################################## + +sub put_all_files_into_ddffile +{ + my ($ddffile, $allfiles, $workingpath) = @_; + + $workingpath =~ s/\//\\/g; + + for ( my $i = 0; $i <= $#{$allfiles}; $i++ ) + { + my $filename = ${$allfiles}[$i]; + if( $^O =~ /cygwin/i ) { $filename =~ s/\//\\/g; } # Backslash for Cygwin! + if ( ! -f $filename ) { installer::exiter::exit_program("ERROR: Could not find file: $filename!", "put_all_files_into_ddffile"); } + my $infoline = "\"" . $filename . "\"" . " " . ${$allfiles}[$i] . "\n"; + push( @{$ddffile}, $infoline); + } +} + +######################################################## +# Packing a cabinet file. +######################################################## + +sub do_pack_cab_file +{ + my ($cabfilename, $allfiles, $workingpath, $temppath) = @_; + + installer::logger::print_message( "\n... packing cabinet file $cabfilename ...\n" ); + my $infoline = "Packing cabinet file $cabfilename\n"; + push( @installer::globals::logfileinfo, $infoline); + + if ( -f $cabfilename ) { unlink($cabfilename); } # removing cab file + if ( -f $cabfilename ) { installer::exiter::exit_program("ERROR: Failed to remove file: $cabfilename!", "do_pack_cab_file"); } + + # generate ddf file for makecab.exe + my @ddffile = (); + + my $dirname = $cabfilename; + $dirname =~ s/\.cab\s*$//; + my $ddfpath = $temppath . $installer::globals::separator . "ddf_". $dirname . "_" . $$; + + my $ddffilename = $cabfilename; + $ddffilename =~ s/.cab/.ddf/; + $ddffilename = $ddfpath . $installer::globals::separator . $ddffilename; + + if ( ! -d $ddfpath ) { installer::systemactions::create_directory($ddfpath); } + + my $from = cwd(); + + chdir($workingpath); # changing into the directory with the unpacked files + + get_ddf_file_header(\@ddffile, $cabfilename, $from); + put_all_files_into_ddffile(\@ddffile, $allfiles, $workingpath); + # lines in ddf files must not be longer than 256 characters + check_ddf_file(\@ddffile, $ddffilename); + + installer::files::save_file($ddffilename, \@ddffile); + + if( $^O =~ /cygwin/i ) + { + $ddffilename = qx{cygpath -w "$ddffilename"}; + $ddffilename =~ s/\\/\\\\/g; + $ddffilename =~ s/\s*$//g; + } + + my $systemcall = "makecab.exe /V1 /F " . $ddffilename; + my $success = make_systemcall($systemcall, $systemcall); + if ( ! $success ) { installer::exiter::exit_program("ERROR: Could not pack cabinet file!", "do_pack_cab_file"); } + + chdir($from); + + return ($success); +} + +######################################################## +# Extraction the file extension from a file +######################################################## + +sub get_extension +{ + my ( $file ) = @_; + + my $extension = ""; + + if ( $file =~ /^\s*(.*)\.(\w+?)\s*$/ ) { $extension = $2; } + + return $extension; +} + +######################################################## +# Checking, if a file already contains a certificate. +# This must not be overwritten. +######################################################## + +sub already_certified +{ + my ( $filename ) = @_; + + my $success = 1; + my $is_certified = 0; + + my $systemcall = "signtool.exe verify /q /pa \"$filename\""; + my $returnvalue = system($systemcall); + + if ( $returnvalue ) { $success = 0; } + + # my $success = make_systemcall($systemcall, $systemcall); + + if ( $success ) + { + $is_certified = 1; + installer::logger::print_message( "... already certified -> skipping $filename ...\n" ); + } + + return $is_certified; +} + +######################################################## +# Signing the files, that are included into +# cabinet files. +######################################################## + +sub sign_files_in_cabinet_files +{ + my ( $followmeinfohash, $allcabfiles, $pw, $temppath ) = @_; + + my $complete_success = 1; + my $from = cwd(); + + foreach my $cabfilename ( keys %{$allcabfiles} ) + { + my $success = 1; + + # saving order of files in cab file + my $fileorder = read_cab_file($cabfilename); + + # unpack into $working path + my $workingpath = unpack_cab_file($cabfilename, $temppath); + + chdir($workingpath); + + # sign files + my %allfileshash = (); + foreach my $onefile ( @{$fileorder} ) + { + my $extension = get_extension($onefile); + if ( exists( $installer::globals::sign_extensions{$extension} ) ) + { + $allfileshash{$onefile} = 1; + } + } + $success = sign_files($followmeinfohash, \%allfileshash, $pw, 1); + if ( ! $success ) { $complete_success = 0; } + + chdir($from); + + # pack into new directory + do_pack_cab_file($cabfilename, $fileorder, $workingpath, $temppath); + } + + return $complete_success; +} + +######################################################## +# Signing an existing Windows installation set. +######################################################## + +sub sign_install_set +{ + my ($followmeinfohash, $make_copy, $temppath) = @_; + + my $installsetpath = $followmeinfohash->{'finalinstalldir'}; + + installer::logger::include_header_into_logfile("Start: Signing installation set $installsetpath"); + + my $complete_success = 1; + my $success = 1; + + my $infoline = "Signing installation set in $installsetpath\n"; + push( @installer::globals::logfileinfo, $infoline); + + # check required files. + if ( ! $installer::globals::signfiles_checked ) { check_system_path(); } + + # get cerficate information + my $pw = get_pw($installer::globals::pwfile); + + # making a copy of the installation set, if required + if ( $make_copy ) { $installsetpath = copy_install_set($installsetpath); } + else { $installsetpath = rename_install_set($installsetpath); } + + # collecting all files in the installation set + my ($allcabfiles, $allfiles, $msidatabase, $contains_external_cabfiles, $contains_msidatabase) = analyze_installset_content($installsetpath); + + # changing into installation set + my $from = cwd(); + my $fullmsidatabase = $installsetpath . $installer::globals::separator . $msidatabase; + + if( $^O =~ /cygwin/i ) + { + $fullmsidatabase = qx{cygpath -w "$fullmsidatabase"}; + $fullmsidatabase =~ s/\\/\\\\/g; + $fullmsidatabase =~ s/\s*$//g; + } + + chdir($installsetpath); + + if ( $contains_msidatabase ) + { + # exclude media table from msi database and get all diskids. + my $cabfilehash = collect_diskid_from_media_table($msidatabase, $followmeinfohash->{'languagestring'}); + + # Check, if there are internal cab files + my ( $contains_internal_cabfiles, $all_internal_cab_files) = check_for_internal_cabfiles($cabfilehash); + + if ( $contains_internal_cabfiles ) + { + my $cabpath = get_cab_path($temppath); + chdir($cabpath); + + # Exclude all cabinet files from database + $success = extract_cabs_from_database($fullmsidatabase, $all_internal_cab_files); + if ( ! $success ) { $complete_success = 0; } + + if ( $installer::globals::internal_cabinet_signing ) { sign_files_in_cabinet_files($followmeinfohash, $all_internal_cab_files, $pw, $temppath); } + + $success = sign_files($followmeinfohash, $all_internal_cab_files, $pw, 0); + if ( ! $success ) { $complete_success = 0; } + $success = msicert_database($fullmsidatabase, $all_internal_cab_files, $cabfilehash, 1); + if ( ! $success ) { $complete_success = 0; } + + # Include all cabinet files into database + $success = include_cabs_into_database($fullmsidatabase, $all_internal_cab_files); + if ( ! $success ) { $complete_success = 0; } + chdir($installsetpath); + } + + # Warning: There might be a problem with very big cabinet files + # signing all external cab files first + if ( $contains_external_cabfiles ) + { + if ( $installer::globals::internal_cabinet_signing ) { sign_files_in_cabinet_files($followmeinfohash, $allcabfiles, $pw, $temppath); } + + $success = sign_files($followmeinfohash, $allcabfiles, $pw, 0); + if ( ! $success ) { $complete_success = 0; } + $success = msicert_database($msidatabase, $allcabfiles, $cabfilehash, 0); + if ( ! $success ) { $complete_success = 0; } + } + } + + # finally all other files can be signed + $success = sign_files($followmeinfohash, $allfiles, $pw, 0); + if ( ! $success ) { $complete_success = 0; } + + # and changing back + chdir($from); + + installer::logger::include_header_into_logfile("End: Signing installation set $installsetpath"); + + return ($installsetpath); +} + +1; diff --git a/solenv/bin/modules/installer/windows/strip.pm b/solenv/bin/modules/installer/windows/strip.pm new file mode 100644 index 000000000000..c7e5e883596c --- /dev/null +++ b/solenv/bin/modules/installer/windows/strip.pm @@ -0,0 +1,163 @@ +#************************************************************************* +# +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# Copyright 2008 by Sun Microsystems, Inc. +# +# OpenOffice.org - a multi-platform office productivity suite +# +# $RCSfile: strip.pm,v $ +# +# $Revision: 1.5 $ +# +# This file is part of OpenOffice.org. +# +# OpenOffice.org is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License version 3 +# only, as published by the Free Software Foundation. +# +# OpenOffice.org is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License version 3 for more details +# (a copy is included in the LICENSE file that accompanied this code). +# +# You should have received a copy of the GNU Lesser General Public License +# version 3 along with OpenOffice.org. If not, see +# <http://www.openoffice.org/license.html> +# for a copy of the LGPLv3 License. +# +#************************************************************************* + +package installer::windows::strip; + +use File::Temp qw(tmpnam); +use installer::converter; +use installer::existence; +use installer::globals; +use installer::logger; +use installer::pathanalyzer; +use installer::systemactions; + +##################################################################### +# Checking whether a file has to be stripped +##################################################################### + +sub need_to_strip +{ + my ( $filename ) = @_; + + my $strip = 0; + + # Check using the "nm" command + + $filename =~ s/\\/\\\\/g; + + open (FILE, "nm $filename 2>&1 |"); + my $nmoutput = <FILE>; + close (FILE); + + if ( $nmoutput && !( $nmoutput =~ /no symbols/i || $nmoutput =~ /not recognized/i )) { $strip = 1; } + + return $strip +} + +##################################################################### +# Checking whether a file has to be stripped +##################################################################### + +sub do_strip +{ + my ( $filename ) = @_; + + my $systemcall = "strip" . " " . $filename; + + my $returnvalue = system($systemcall); + + my $infoline = "Systemcall: $systemcall\n"; + push( @installer::globals::logfileinfo, $infoline); + + if ($returnvalue) + { + $infoline = "ERROR: Could not strip $filename!\n"; + push( @installer::globals::logfileinfo, $infoline); + } + else + { + $infoline = "SUCCESS: Stripped library $filename!\n"; + push( @installer::globals::logfileinfo, $infoline); + } +} + +##################################################################### +# Resolving all variables in the packagename. +##################################################################### + +sub strip_binaries +{ + my ( $filelist, $languagestringref ) = @_; + + installer::logger::include_header_into_logfile("Stripping files:"); + + my $strippeddirbase = installer::systemactions::create_directories("stripped", $languagestringref); + + if (! installer::existence::exists_in_array($strippeddirbase, \@installer::globals::removedirs)) + { + push(@installer::globals::removedirs, $strippeddirbase); + } + + my ($tmpfilehandle, $tmpfilename) = tmpnam(); + open SOURCEPATHLIST, ">$tmpfilename" or die "oops...\n"; + for ( my $i = 0; $i <= $#{$filelist}; $i++ ) + { + print SOURCEPATHLIST "${$filelist}[$i]->{'sourcepath'}\n"; + } + close SOURCEPATHLIST; + my @filetypelist = qx{file -f "$tmpfilename"}; + chomp @filetypelist; + unlink "$tmpfilename" or die "oops\n"; + for ( my $i = 0; $i <= $#{$filelist}; $i++ ) + { + ${$filelist}[$i]->{'is_executable'} = ( $filetypelist[$i] =~ /:.*PE executable/ ); + } + + if ( $^O =~ /cygwin/i ) { installer::worker::generate_cygwin_pathes($filelist); } + + for ( my $i = 0; $i <= $#{$filelist}; $i++ ) + { + my $sourcefilename = ${$filelist}[$i]->{'cyg_sourcepath'}; + + if ( ${$filelist}[$i]->{'is_executable'} && need_to_strip($sourcefilename) ) + { + my $shortfilename = $sourcefilename; + installer::pathanalyzer::make_absolute_filename_to_relative_filename(\$shortfilename); + + $infoline = "Strip: $shortfilename\n"; + push( @installer::globals::logfileinfo, $infoline); + + # copy file into directory for stripped libraries + + my $onelanguage = ${$filelist}[$i]->{'specificlanguage'}; + + # files without language into directory "00" + + if ($onelanguage eq "") { $onelanguage = "00"; } + + my $strippeddir = $strippeddirbase . $installer::globals::separator . $onelanguage; + installer::systemactions::create_directory($strippeddir); # creating language specific subdirectories + + my $destfilename = $strippeddir . $installer::globals::separator . $shortfilename; + installer::systemactions::copy_one_file($sourcefilename, $destfilename); + + # change sourcepath in files collector + + ${$filelist}[$i]->{'sourcepath'} = $destfilename; + + # strip file + + do_strip($destfilename); + } + } +} + +1; diff --git a/solenv/bin/modules/installer/windows/update.pm b/solenv/bin/modules/installer/windows/update.pm new file mode 100644 index 000000000000..4c1081fa8458 --- /dev/null +++ b/solenv/bin/modules/installer/windows/update.pm @@ -0,0 +1,604 @@ +#************************************************************************* +# +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# Copyright 2008 by Sun Microsystems, Inc. +# +# OpenOffice.org - a multi-platform office productivity suite +# +# $RCSfile: update.pm,v $ +# +# $Revision: 1.2 $ +# +# This file is part of OpenOffice.org. +# +# OpenOffice.org is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License version 3 +# only, as published by the Free Software Foundation. +# +# OpenOffice.org is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License version 3 for more details +# (a copy is included in the LICENSE file that accompanied this code). +# +# You should have received a copy of the GNU Lesser General Public License +# version 3 along with OpenOffice.org. If not, see +# <http://www.openoffice.org/license.html> +# for a copy of the LGPLv3 License. +# +#************************************************************************* + +package installer::windows::update; + +use installer::converter; +use installer::exiter; +use installer::files; +use installer::globals; +use installer::pathanalyzer; +use installer::systemactions; + +################################################################################# +# Extracting all tables from an msi database +################################################################################# + +sub extract_all_tables_from_msidatabase +{ + my ($fulldatabasepath, $workdir) = @_; + + my $msidb = "msidb.exe"; # Has to be in the path + my $infoline = ""; + my $systemcall = ""; + my $returnvalue = ""; + my $extraslash = ""; # Has to be set for non-ActiveState perl + + # Export of all tables by using "*" + + if ( $^O =~ /cygwin/i ) { + # msidb.exe really wants backslashes. (And double escaping because system() expands the string.) + $fulldatabasepath =~ s/\//\\\\/g; + $workdir =~ s/\//\\\\/g; + $extraslash = "\\"; + } + + $systemcall = $msidb . " -d " . $fulldatabasepath . " -f " . $workdir . " -e " . $extraslash . "*"; + $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); + installer::exiter::exit_program("ERROR: Could not exclude tables from msi database: $fulldatabasepath !", "extract_all_tables_from_msidatabase"); + } + else + { + $infoline = "Success: Executed $systemcall successfully!\n"; + push( @installer::globals::logfileinfo, $infoline); + } +} + +################################################################################# +# Collecting the keys from the first line of the idt file +################################################################################# + +sub collect_all_keys +{ + my ($line) = @_; + + my @allkeys = (); + my $rownumber = 0; + my $onekey = ""; + + while ( $line =~ /^\s*(\S+?)\t(.*)$/ ) + { + $onekey = $1; + $line = $2; + $rownumber++; + push(@allkeys, $onekey); + } + + # and the last key + + $onekey = $line; + $onekey =~ s/^\s*//g; + $onekey =~ s/\s*$//g; + + $rownumber++; + push(@allkeys, $onekey); + + return (\@allkeys, $rownumber); +} + +################################################################################# +# Analyzing the content of one line of an idt file +################################################################################# + +sub get_oneline_hash +{ + my ($line, $allkeys, $rownumber) = @_; + + my $counter = 0; + my %linehash = (); + + $line =~ s/^\s*//; + $line =~ s/\s*$//; + + my $value = ""; + my $onekey = ""; + + while ( $line =~ /^(.*?)\t(.*)$/ ) + { + $value = $1; + $line = $2; + $onekey = ${$allkeys}[$counter]; + $linehash{$onekey} = $value; + $counter++; + } + + # the last column + + $value = $line; + $onekey = ${$allkeys}[$counter]; + + $linehash{$onekey} = $value; + + return \%linehash; +} + +################################################################################# +# Analyzing the content of an idt file +################################################################################# + +sub analyze_idt_file +{ + my ($filecontent) = @_; + + my %table = (); + # keys are written in first line + my ($allkeys, $rownumber) = collect_all_keys(${$filecontent}[0]); + + for ( my $i = 0; $i <= $#{$filecontent}; $i++ ) + { + if (( $i == 0 ) || ( $i == 1 ) || ( $i == 2 )) { next; } + + my $onelinehash = get_oneline_hash(${$filecontent}[$i], $allkeys, $rownumber); + my $linekey = $i - 2; # ! : The linenumber is the unique key !? Always decrease by two, because of removed first three lines. + $table{$linekey} = $onelinehash; + } + + return \%table; +} + +################################################################################# +# Reading all idt files in a specified directory +################################################################################# + +sub read_all_tables_from_msidatabase +{ + my ($workdir) = @_; + + my %database = (); + + my $ext = "idt"; + + my $allidtfiles = installer::systemactions::find_file_with_file_extension($ext, $workdir); + + for ( my $i = 0; $i <= $#{$allidtfiles}; $i++ ) + { + my $onefilename = ${$allidtfiles}[$i]; + my $longonefilename = $workdir . $installer::globals::separator . $onefilename; + if ( ! -f $longonefilename ) { installer::exiter::exit_program("ERROR: Could not find idt file: $longonefilename!", "read_all_tables_from_msidatabase"); } + my $filecontent = installer::files::read_file($longonefilename); + my $idtcontent = analyze_idt_file($filecontent); + my $key = $onefilename; + $key =~ s/\.idt\s*$//; + $database{$key} = $idtcontent; + } + + return \%database; +} + +################################################################################# +# Checking, if this is the correct database. +################################################################################# + +sub correct_database +{ + my ($product, $pro, $langs, $languagestringref) = @_; + + my $correct_database = 0; + + # Comparing $product with $installer::globals::product and + # $pro with $installer::globals::pro and + # $langs with $languagestringref + + my $product_is_good = 0; + + my $localproduct = $installer::globals::product; + if ( $installer::globals::languagepack ) { $localproduct = $localproduct . "LanguagePack"; } + + if ( $product eq $localproduct ) { $product_is_good = 1; } + + if ( $product_is_good ) + { + my $pro_is_good = 0; + + if ((( $pro eq "pro" ) && ( $installer::globals::pro )) || (( $pro eq "nonpro" ) && ( ! $installer::globals::pro ))) { $pro_is_good = 1; } + + if ( $pro_is_good ) + { + my $langlisthash = installer::converter::convert_stringlist_into_hash(\$langs, ","); + my $langstringhash = installer::converter::convert_stringlist_into_hash($languagestringref, "_"); + + my $not_included = 0; + foreach my $onelang ( keys %{$langlisthash} ) + { + if ( ! exists($langstringhash->{$onelang}) ) + { + $not_included = 1; + last; + } + } + + if ( ! $not_included ) + { + foreach my $onelanguage ( keys %{$langstringhash} ) + { + if ( ! exists($langlisthash->{$onelanguage}) ) + { + $not_included = 1; + last; + } + } + + if ( ! $not_included ) { $correct_database = 1; } + } + } + } + + return $correct_database; +} + +################################################################################# +# Searching for the path to the reference database for this special product. +################################################################################# + +sub get_databasename_from_list +{ + my ($filecontent, $languagestringref, $filename) = @_; + + my $databasepath = ""; + + for ( my $i = 0; $i <= $#{$filecontent}; $i++ ) + { + my $line = ${$filecontent}[$i]; + if ( $line =~ /^\s*$/ ) { next; } # empty line + if ( $line =~ /^\s*\#/ ) { next; } # comment line + + if ( $line =~ /^\s*(.+?)\s*\t+\s*(.+?)\s*\t+\s*(.+?)\s*\t+\s*(.+?)\s*$/ ) + { + my $product = $1; + my $pro = $2; + my $langs = $3; + my $path = $4; + + if (( $pro ne "pro" ) && ( $pro ne "nonpro" )) { installer::exiter::exit_program("ERROR: Wrong syntax in file: $filename. Only \"pro\" or \"nonpro\" allowed in column 1! Line: \"$line\"", "get_databasename_from_list"); } + + if ( correct_database($product, $pro, $langs, $languagestringref) ) + { + $databasepath = $path; + last; + } + } + else + { + installer::exiter::exit_program("ERROR: Wrong syntax in file: $filename! Line: \"$line\"", "get_databasename_from_list"); + } + } + + return $databasepath; +} + +################################################################################# +# Reading an existing database completely +################################################################################# + +sub readdatabase +{ + my ($allvariables, $languagestringref, $includepatharrayref) = @_; + + my $database = ""; + my $infoline = ""; + + if ( ! $allvariables->{'UPDATE_DATABASE_LISTNAME'} ) { installer::exiter::exit_program("ERROR: If \"UPDATE_DATABASE\" is set, \"UPDATE_DATABASE_LISTNAME\" is required.", "Main"); } + my $listfilename = $allvariables->{'UPDATE_DATABASE_LISTNAME'}; + + # Searching the list in the include pathes + my $listname = installer::scriptitems::get_sourcepath_from_filename_and_includepath(\$listfilename, $includepatharrayref, 1); + if ( $$listname eq "" ) { installer::exiter::exit_program("ERROR: List file not found: $listfilename !", "readdatabase"); } + my $completelistname = $$listname; + + # Reading list file + my $listfile = installer::files::read_file($completelistname); + + # Get name and path of reference database + my $databasename = get_databasename_from_list($listfile, $languagestringref, $completelistname); + + # If the correct database was not found, this is not necessarily an error. But in this case, this is not an update packaging process! + if (( $databasename ) && ( $databasename ne "" )) # This is an update packaging process! + { + $installer::globals::updatedatabase = 1; + installer::logger::print_message( "... update process, using database $databasename ...\n" ); + $infoline = "\nDatabase found in $completelistname: \"$databasename\"\n\n"; + # Saving in global variable + $installer::globals::updatedatabasepath = $databasename; + } + else + { + # installer::logger::print_message( "... no update process, no database found ...\n" ); + $infoline = "\nNo database found in $completelistname. This is no update process!\n\n"; + } + push( @installer::globals::logfileinfo, $infoline); + + if ( $installer::globals::updatedatabase ) + { + if ( ! -f $databasename ) { installer::exiter::exit_program("ERROR: Could not find reference database: $databasename!", "readdatabase"); } + + my $msifilename = $databasename; + installer::pathanalyzer::make_absolute_filename_to_relative_filename(\$msifilename); + + installer::logger::include_timestamp_into_logfile("Performance Info: readdatabase start"); + + # create directory for unpacking + my $databasedir = installer::systemactions::create_directories("database", $languagestringref); + + # copy database + my $fulldatabasepath = $databasedir . $installer::globals::separator . $msifilename; + installer::systemactions::copy_one_file($databasename, $fulldatabasepath); + + installer::logger::include_timestamp_into_logfile("Performance Info: readdatabase: before extracting tables"); + + # extract all tables from database + extract_all_tables_from_msidatabase($fulldatabasepath, $databasedir); + + installer::logger::include_timestamp_into_logfile("Performance Info: readdatabase: before reading tables"); + + # read all tables + $database = read_all_tables_from_msidatabase($databasedir); + + # Test output: + + # foreach my $key1 ( keys %{$database} ) + # { + # print "Test1: $key1\n"; + # foreach my $key2 ( keys %{$database->{$key1}} ) + # { + # print "\tTest2: $key2\n"; + # foreach my $key3 ( keys %{$database->{$key1}->{$key2}} ) + # { + # print "\t\tTest3: $key3: $database->{$key1}->{$key2}->{$key3}\n"; + # } + # } + # } + + # Example: File table + + # my $filetable = $database->{'File'}; + # foreach my $linenumber ( keys %{$filetable} ) + # { + # print "Test Filenumber: $linenumber\n"; + # foreach my $key ( keys %{$filetable->{$linenumber}} ) + # { + # print "\t\tTest: $key: $filetable->{$linenumber}->{$key}\n"; + # } + # } + + # Example: Searching for ProductCode in table Property + + # my $column1 = "Property"; + # my $column2 = "Value"; + # my $searchkey = "ProductCode"; + # my $propertytable = $database->{'Property'}; + # foreach my $linenumber ( keys %{$propertytable} ) + # { + # if ( $propertytable->{$linenumber}->{$column1} eq $searchkey ) + # { + # print("Test: $searchkey : $propertytable->{$linenumber}->{$column2}\n"); + # } + # } + + installer::logger::include_timestamp_into_logfile("Performance Info: readdatabase end"); + } + + return $database; +} + +################################################################################# +# Files can be included in merge modules. This is also important for update. +################################################################################# + +sub readmergedatabase +{ + my ( $mergemodules, $languagestringref, $includepatharrayref ) = @_; + + installer::logger::include_timestamp_into_logfile("Performance Info: readmergedatabase start"); + + my $mergemoduledir = installer::systemactions::create_directories("mergedatabase", $languagestringref); + + my %allmergefiles = (); + + $installer::globals::mergemodulenumber = $#{$mergemodules} + 1; + + foreach my $mergemodule ( @{$mergemodules} ) + { + my $filename = $mergemodule->{'Name'}; + my $mergefile = installer::scriptitems::get_sourcepath_from_filename_and_includepath(\$filename, $includepatharrayref, 1); + + if ( $$mergefile eq "" ) { installer::exiter::exit_program("ERROR: msm file not found: $filename !", "readmergedatabase"); } + my $completesource = $$mergefile; + + my $mergegid = $mergemodule->{'gid'}; + my $workdir = $mergemoduledir . $installer::globals::separator . $mergegid; + if ( ! -d $workdir ) { installer::systemactions::create_directory($workdir); } + + my $completedest = $workdir . $installer::globals::separator . $filename; + installer::systemactions::copy_one_file($completesource, $completedest); + if ( ! -f $completedest ) { installer::exiter::exit_program("ERROR: msm file not found: $completedest !", "readmergedatabase"); } + + # extract all tables from database + extract_all_tables_from_msidatabase($completedest, $workdir); + + # read all tables + my $onemergefile = read_all_tables_from_msidatabase($workdir); + + $allmergefiles{$mergegid} = $onemergefile; + } + + foreach my $mergefilegid ( keys %allmergefiles ) + { + my $onemergefile = $allmergefiles{$mergefilegid}; + my $filetable = $onemergefile->{'File'}; + + foreach my $linenumber ( keys %{$filetable} ) + { + # Collecting all files from merge modules in global hash + $installer::globals::mergemodulefiles{$filetable->{$linenumber}->{'File'}} = 1; + } + } + + installer::logger::include_timestamp_into_logfile("Performance Info: readmergedatabase end"); +} + +################################################################################# +# Creating several useful hashes from old database +################################################################################# + +sub create_database_hashes +{ + my ( $database ) = @_; + + # 1. Hash ( Component -> UniqueFileName ), required in File table. + # Read from File table. + + my %uniquefilename = (); + my %allupdatesequences = (); + my %allupdatecomponents = (); + my %allupdatefileorder = (); + my %allupdatecomponentorder = (); + my %revuniquefilename = (); + my %revshortfilename = (); + my %shortdirname = (); + my %componentid = (); + my %componentidkeypath = (); + my %alloldproperties = (); + my %allupdatelastsequences = (); + my %allupdatediskids = (); + + my $filetable = $database->{'File'}; + + foreach my $linenumber ( keys %{$filetable} ) + { + my $comp = $filetable->{$linenumber}->{'Component_'}; + my $uniquename = $filetable->{$linenumber}->{'File'}; + my $filename = $filetable->{$linenumber}->{'FileName'}; + my $sequence = $filetable->{$linenumber}->{'Sequence'}; + + my $shortname = ""; + if ( $filename =~ /^\s*(.*?)\|\s*(.*?)\s*$/ ) + { + $shortname = $1; + $filename = $2; + } + + # unique is the combination of $component and $filename + my $key = "$comp/$filename"; + + if ( exists($uniquefilename{$key}) ) { installer::exiter::exit_program("ERROR: Component/FileName \"$key\" is not unique in table \"File\" !", "create_database_hashes"); } + + my $value = $uniquename; + if ( $shortname ne "" ) { $value = "$uniquename;$shortname"; } + $uniquefilename{$key} = $value; # saving the unique keys and short names in hash + + # Saving reverse keys too + $revuniquefilename{$uniquename} = $key; + if ( $shortname ne "" ) { $revshortfilename{$shortname} = $key; } + + # Saving Sequences for unique names (and also components) + $allupdatesequences{$uniquename} = $sequence; + $allupdatecomponents{$uniquename} = $comp; + + # Saving unique names and components for sequences + $allupdatefileorder{$sequence} = $uniquename; + $allupdatecomponentorder{$sequence} = $comp; + } + + # 2. Hash, required in Directory table. + + my $dirtable = $database->{'Directory'}; + + foreach my $linenumber ( keys %{$dirtable} ) + { + my $dir = $dirtable->{$linenumber}->{'Directory'}; # this is a unique name + my $defaultdir = $dirtable->{$linenumber}->{'DefaultDir'}; + + my $shortname = ""; + if ( $defaultdir =~ /^\s*(.*?)\|\s*(.*?)\s*$/ ) + { + $shortname = $1; + $shortdirname{$dir} = $shortname; # collecting only the short names + } + } + + # 3. Hash, collecting info from Component table. + # ComponentID and KeyPath have to be reused. + + my $comptable = $database->{'Component'}; + + foreach my $linenumber ( keys %{$comptable} ) + { + my $comp = $comptable->{$linenumber}->{'Component'}; + my $compid = $comptable->{$linenumber}->{'ComponentId'}; + my $keypath = $comptable->{$linenumber}->{'KeyPath'}; + + $componentid{$comp} = $compid; + $componentidkeypath{$comp} = $keypath; + } + + # 4. Hash, property table, required for ProductCode and Installlocation. + + my $proptable = $database->{'Property'}; + + foreach my $linenumber ( keys %{$proptable} ) + { + my $prop = $proptable->{$linenumber}->{'Property'}; + my $value = $proptable->{$linenumber}->{'Value'}; + + $alloldproperties{$prop} = $value; + } + + # 5. Media table, getting last sequence + + my $mediatable = $database->{'Media'}; + $installer::globals::updatelastsequence = 0; + + foreach my $linenumber ( keys %{$mediatable} ) + { + my $cabname = $mediatable->{$linenumber}->{'Cabinet'}; + my $lastsequence = $mediatable->{$linenumber}->{'LastSequence'}; + my $diskid = $mediatable->{$linenumber}->{'DiskId'}; + $allupdatelastsequences{$cabname} = $lastsequence; + $allupdatediskids{$cabname} = $diskid; + + if ( $lastsequence > $installer::globals::updatelastsequence ) { $installer::globals::updatelastsequence = $lastsequence; } + } + + $installer::globals::updatesequencecounter = $installer::globals::updatelastsequence; + + return (\%uniquefilename, \%revuniquefilename, \%revshortfilename, \%allupdatesequences, \%allupdatecomponents, \%allupdatefileorder, \%allupdatecomponentorder, \%shortdirname, \%componentid, \%componentidkeypath, \%alloldproperties, \%allupdatelastsequences, \%allupdatediskids); +} + + +1;
\ No newline at end of file diff --git a/solenv/bin/modules/installer/windows/upgrade.pm b/solenv/bin/modules/installer/windows/upgrade.pm new file mode 100644 index 000000000000..5e3d5379579a --- /dev/null +++ b/solenv/bin/modules/installer/windows/upgrade.pm @@ -0,0 +1,171 @@ +#************************************************************************* +# +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# Copyright 2008 by Sun Microsystems, Inc. +# +# OpenOffice.org - a multi-platform office productivity suite +# +# $RCSfile: upgrade.pm,v $ +# +# $Revision: 1.16 $ +# +# This file is part of OpenOffice.org. +# +# OpenOffice.org is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License version 3 +# only, as published by the Free Software Foundation. +# +# OpenOffice.org is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License version 3 for more details +# (a copy is included in the LICENSE file that accompanied this code). +# +# You should have received a copy of the GNU Lesser General Public License +# version 3 along with OpenOffice.org. If not, see +# <http://www.openoffice.org/license.html> +# for a copy of the LGPLv3 License. +# +#************************************************************************* + +package installer::windows::upgrade; + +use installer::exiter; +use installer::files; +use installer::globals; +use installer::windows::idtglobal; + +#################################################################################### +# Creating the file Upgrade.idt dynamically +# Content: +# UpgradeCode VersionMin VersionMax Language Attributes Remove ActionProperty +#################################################################################### + +sub create_upgrade_table +{ + my ($basedir, $allvariableshashref) = @_; + + my @upgradetable = (); + + # fix for problematic OOo 1.9 versions + my $include_ooo_fix = 0; + my $ooomaxnew = ""; + if (($installer::globals::product =~ /OpenOffice/i ) && ( ! ( $installer::globals::product =~ /SDK/i )) && ( ! $installer::globals::languagepack )) + { + $include_ooo_fix = 1; + $ooomaxnew = "34.0.0"; + } + + installer::windows::idtglobal::write_idt_header(\@upgradetable, "upgrade"); + + # Setting also $installer::globals::msimajorproductversion, that is for example "3.0.0", to differ between old products for OOo 2.x and + # older products from OOo 3.x. The latter must be removed always, the removal of the first is controlled with a checkbox. + my $newline = $installer::globals::upgradecode . "\t" . "\t" . $installer::globals::msimajorproductversion . "\t" . "\t" . "0" . "\t" . "\t" . "OLDPRODUCTS" . "\n"; + push(@upgradetable, $newline); + + # Setting all products, that must be removed. + $newline = $installer::globals::upgradecode . "\t" . $installer::globals::msimajorproductversion . "\t" . $installer::globals::msiproductversion . "\t" . "\t" . "257" . "\t" . "\t" . "OLDPRODUCTSSAMEMAJOR" . "\n"; + push(@upgradetable, $newline); + + if ( ! $installer::globals::patch ) + { + # preventing downgrading + $newline = $installer::globals::upgradecode . "\t" . $installer::globals::msiproductversion . "\t" . $ooomaxnew . "\t" . "\t" . "2" . "\t" . "\t" . "NEWPRODUCTS" . "\n"; + push(@upgradetable, $newline); + + $newline = $installer::globals::upgradecode . "\t" . $installer::globals::msiproductversion . "\t" . $ooomaxnew . "\t" . "\t" . "258" . "\t" . "\t" . "SAMEPRODUCTS" . "\n"; + push(@upgradetable, $newline); + + if ( $include_ooo_fix ) + { + $newline = $installer::globals::upgradecode . "\t" . "35.0.0" . "\t" . "36.0.0" . "\t" . "\t" . "1" . "\t" . "\t" . "OLDPRODUCTS2" . "\n"; + push(@upgradetable, $newline); + } + + # if (( $allvariableshashref->{'PATCHUPGRADECODE'} ) && ( ! $installer::globals::languagepack )) + # { + # $newline = $allvariableshashref->{'PATCHUPGRADECODE'} . "\t" . "\t" . $installer::globals::msiproductversion . "\t" . "\t" . "1" . "\t" . "\t" . "OLDPRODUCTSPATCH" . "\n"; + # push(@upgradetable, $newline); + # + # $newline = $allvariableshashref->{'PATCHUPGRADECODE'} . "\t" . $installer::globals::msiproductversion . "\t" . "\t" . "\t" . "2" . "\t" . "\t" . "NEWPRODUCTSPATCH" . "\n"; + # push(@upgradetable, $newline); + # + # $newline = $allvariableshashref->{'PATCHUPGRADECODE'} . "\t" . $installer::globals::msiproductversion . "\t" . "\t" . "\t" . "258" . "\t" . "\t" . "SAMEPRODUCTSPATCH" . "\n"; + # push(@upgradetable, $newline); + # } + + # also searching for the beta + + if (( $allvariableshashref->{'BETAUPGRADECODE'} ) && ( ! $installer::globals::languagepack )) + { + $newline = $allvariableshashref->{'BETAUPGRADECODE'} . "\t" . "1.0" . "\t" . "\t" . "\t" . "1" . "\t" . "\t" . "BETAPRODUCTS" . "\n"; + push(@upgradetable, $newline); + } + + # also searching for the stub + + if (( $allvariableshashref->{'STUBUPGRADECODE'} ) && ( ! $installer::globals::languagepack )) + { + $newline = $allvariableshashref->{'STUBUPGRADECODE'} . "\t" . "1.0" . "\t" . "\t" . "\t" . "1" . "\t" . "\t" . "STUBPRODUCTS" . "\n"; + push(@upgradetable, $newline); + } + + # searching for all older patches and languagepacks (defined in a extra file) + + if (( $allvariableshashref->{'REMOVE_UPGRADE_CODE_FILE'} ) && ( ! $installer::globals::languagepack )) + { + my $filename = $allvariableshashref->{'REMOVE_UPGRADE_CODE_FILE'}; + my $langpackcodefilename = $installer::globals::idttemplatepath . $installer::globals::separator . $filename; + if ( ! -f $langpackcodefilename ) { installer::exiter::exit_program("ERROR: Could not find file \"$langpackcodefilename\".", "create_upgrade_table"); } + + my $filecontent = installer::files::read_file($langpackcodefilename); + my $newlines = analyze_file_for_upgrade_table($filecontent); + + for ( my $i = 0; $i <= $#{$newlines}; $i++ ) { push(@upgradetable, ${$newlines}[$i]); } + } + } + + # No upgrade for Beta versions! + + if (( $allvariableshashref->{'PRODUCTEXTENSION'} eq "Beta" ) && ( ! $installer::globals::patch ) && ( ! $installer::globals::languagepack )) + { + @upgradetable = (); + installer::windows::idtglobal::write_idt_header(\@upgradetable, "upgrade"); + my $infoline = "Beta product -> empty Upgrade table\n"; + push(@installer::globals::logfileinfo, $infoline); + } + + # Saving the file + + my $upgradetablename = $basedir . $installer::globals::separator . "Upgrade.idt"; + installer::files::save_file($upgradetablename ,\@upgradetable); + my $infoline = "Created idt file: $upgradetablename\n"; + push(@installer::globals::logfileinfo, $infoline); +} + +############################################################## +# Reading the file with UpgradeCodes of old products, +# that can be removed, if the user wants to remove them. +############################################################## + +sub analyze_file_for_upgrade_table +{ + my ($filecontent) = @_; + + my @allnewlines = (); + + for ( my $i = 0; $i <= $#{$filecontent}; $i++ ) + { + my $line = ${$filecontent}[$i]; + if ( $line =~ /^\s*$/ ) { next; } # empty lines can be ignored + if ( $line =~ /^\s*\#/ ) { next; } # comment lines starting with a hash + + if ( $line =~ /^(.*)\t(.*)\t(.*)\t(.*)\t(.*)\t(.*)\t(.*)$/ ) { push(@allnewlines, $line); } + else { installer::exiter::exit_program("ERROR: Wrong syntax in file for upgrade table", "analyze_file_for_upgrade_table"); } + } + + return \@allnewlines; +} + +1; |