diff options
Diffstat (limited to 'solenv/bin/modules/installer/windows/admin.pm')
-rw-r--r-- | solenv/bin/modules/installer/windows/admin.pm | 916 |
1 files changed, 916 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..47a71b059b2a --- /dev/null +++ b/solenv/bin/modules/installer/windows/admin.pm @@ -0,0 +1,916 @@ +#************************************************************************* +# +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# Copyright 2000, 2010 Oracle and/or its affiliates. +# +# OpenOffice.org - a multi-platform office productivity suite +# +# 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); } + + # Also adding the pathes of the startparents + foreach $dir (@startparents) + { + if ( ! exists($fullpathhash{$dir}) ) { $fullpathhash{$dir} = $targetdir; } + } + + return \%fullpathhash; +} + +#################################################################################### +# Copying files into installation set +#################################################################################### + +sub copy_files_into_directory_structure +{ + my ($fileorder, $filehash, $componenthash, $fullpathhash, $maxsequence, $unpackdir, $installdir, $dirhash) = @_; + + my $unopkgfile = ""; + + 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"); + } + + if ( $destfile =~ /unopkg\.exe\s*$/ ) { $unopkgfile = $destfile; } + + # 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"); + # } + } + + return $unopkgfile; +} + + +############################################################### +# 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"); + } +} + +#################################################### +# Detecting the directory with extensions +#################################################### + +sub get_extensions_dir +{ + my ( $unopkgfile ) = @_; + + my $localbranddir = $unopkgfile; + installer::pathanalyzer::get_path_from_fullqualifiedname(\$localbranddir); # "program" dir in brand layer + installer::pathanalyzer::get_path_from_fullqualifiedname(\$localbranddir); # root dir in brand layer + $localbranddir =~ s/\Q$installer::globals::separator\E\s*$//; + my $extensiondir = $localbranddir . $installer::globals::separator . "share" . $installer::globals::separator . "extensions"; + + return $extensiondir; +} + +############################################################## +# Removing all empty directories below a specified directory +############################################################## + +sub remove_empty_dirs_in_folder +{ + my ( $dir, $firstrun ) = @_; + + if ( $firstrun ) + { + print "Removing superfluous directories\n"; + } + + my @content = (); + + $dir =~ s/\Q$installer::globals::separator\E\s*$//; + + if ( -d $dir ) + { + opendir(DIR, $dir); + @content = readdir(DIR); + closedir(DIR); + + my $oneitem; + + foreach $oneitem (@content) + { + if ((!($oneitem eq ".")) && (!($oneitem eq ".."))) + { + my $item = $dir . $installer::globals::separator . $oneitem; + + if ( -d $item ) # recursive + { + remove_empty_dirs_in_folder($item, 0); + } + } + } + + # try to remove empty directory + my $returnvalue = rmdir $dir; + + # if ( $returnvalue ) { print "Successfully removed empty dir $dir\n"; } + } +} + +#################################################################################### +# 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 + my $unopkgfile = 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); + + if ( $unopkgfile ne "" ) + { + # Removing empty dirs in extension folder + my $extensionfolder = get_extensions_dir($unopkgfile); + if ( -d $extensionfolder ) { remove_empty_dirs_in_folder($extensionfolder, 1); } + } + + # 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; |