| #************************************************************** |
| # |
| # Licensed to the Apache Software Foundation (ASF) under one |
| # or more contributor license agreements. See the NOTICE file |
| # distributed with this work for additional information |
| # regarding copyright ownership. The ASF licenses this file |
| # to you under the Apache License, Version 2.0 (the |
| # "License"); you may not use this file except in compliance |
| # with the License. You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, |
| # software distributed under the License is distributed on an |
| # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| # KIND, either express or implied. See the License for the |
| # specific language governing permissions and limitations |
| # under the 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"; |
| $installer::logger::Lang->print($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"; |
| $installer::logger::Lang->print($infoline); |
| |
| if ($returnvalue) |
| { |
| $infoline = "ERROR: Could not execute $systemcall !\n"; |
| $installer::logger::Lang->print($infoline); |
| installer::exiter::exit_program("ERROR: Could not extract cabinet file: $mergemodulehash->{'cabinetfile'} !", "change_file_table"); |
| } |
| else |
| { |
| $infoline = "Success: Executed $systemcall successfully!\n"; |
| $installer::logger::Lang->print($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"; |
| $installer::logger::Lang->print($infoline); |
| |
| if ($returnvalue) |
| { |
| $infoline = "ERROR: Could not execute $systemcall !\n"; |
| $installer::logger::Lang->print($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"; |
| $installer::logger::Lang->print($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"; |
| $installer::logger::Lang->print($infoline); |
| |
| if ($returnvalue) |
| { |
| $infoline = "ERROR: Could not execute $systemcall !\n"; |
| $installer::logger::Lang->print($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"; |
| $installer::logger::Lang->print($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; |
| $installer::logger::Lang->print($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::Lang->print("\n"); |
| $installer::logger::Lang->add_timestamp("Performance 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"; |
| $installer::logger::Lang->print($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"; |
| $installer::logger::Lang->print($infoline); |
| } |
| } |
| } |
| } |
| |
| $infoline = "Number of removed registry items: $removecounter\n"; |
| $installer::logger::Lang->print($infoline); |
| $infoline = "Number of changed registry items: $renamecounter\n"; |
| $installer::logger::Lang->print($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"; |
| $installer::logger::Lang->print($infoline); |
| |
| $installer::logger::Lang->print("\n"); |
| $installer::logger::Lang->add_timestamp("Performance 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"; |
| $installer::logger::Lang->printf($systemcall); |
| $returnvalue = system($systemcall); |
| |
| if ($returnvalue) |
| { |
| $infoline = "ERROR: Could not execute $systemcall !\n"; |
| $installer::logger::Lang->print($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::Info->printf("... installing %s in directory %s ...\n", |
| $databasepath, |
| $targetdir); |
| |
| 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; |