diff options
Diffstat (limited to 'solenv/bin/modules')
28 files changed, 2249 insertions, 4469 deletions
diff --git a/solenv/bin/modules/installer/control.pm b/solenv/bin/modules/installer/control.pm index 2974b949e5f1..51edf8d3b8e8 100644 --- a/solenv/bin/modules/installer/control.pm +++ b/solenv/bin/modules/installer/control.pm @@ -321,7 +321,7 @@ sub filter_log_error ($$$$) # Remove all filenames that contain the word "Error". my $work_string = $message; - $work_string =~ s/Error\.(idt|mlf|ulf|html|hpp|ipp)//g; + $work_string =~ s/Error\.(idt|mlf|ulf|idl|html|hpp|ipp)//g; if ($work_string =~ /\bError\b/i) { @@ -497,7 +497,7 @@ sub check_updatepack # try to write into $shipdrive - my $directory = $installer::globals::product . "_" . $installer::globals::compiler . "_" . $installer::globals::buildid . "_" . $installer::globals::languageproducts[0] . "_test_$$"; + my $directory = $installer::globals::product . "_" . $installer::globals::compiler . "_" . $installer::globals::buildid . "_" . $installer::globals::languageproduct . "_test_$$"; $directory =~ s/\,/\_/g; # for the list of languages $directory =~ s/\-/\_/g; # for en-US, pt-BR, ... $directory = $shipdrive . $installer::globals::separator . $directory; diff --git a/solenv/bin/modules/installer/epmfile.pm b/solenv/bin/modules/installer/epmfile.pm index e877595b35ac..38cff5d3569c 100644 --- a/solenv/bin/modules/installer/epmfile.pm +++ b/solenv/bin/modules/installer/epmfile.pm @@ -2312,7 +2312,7 @@ sub log_rpm_info $infoline = "$rpmout\n"; $infoline =~ s/error/e_r_r_o_r/gi; # avoiding log problems - $installer::logger::Lang->printf($infoline); + $installer::logger::Lang->print($infoline); } } else diff --git a/solenv/bin/modules/installer/globals.pm b/solenv/bin/modules/installer/globals.pm index f4dcd0ff382f..04d14ea6d735 100644 --- a/solenv/bin/modules/installer/globals.pm +++ b/solenv/bin/modules/installer/globals.pm @@ -113,7 +113,7 @@ BEGIN $required_dotnet_version = "2.0.0.0"; $productextension = ""; - @languageproducts = (); + $languageproduct = undef; $build = ""; $minor = ""; $lastminor = ""; @@ -226,24 +226,14 @@ BEGIN @linkrpms = (); $archiveformat = ""; $minorupgradekey = ""; - $updatelastsequence = 0; - $updatesequencecounter = 0; - $updatedatabase = 0; - $updatedatabasepath = ""; $pfffileexists = 0; $pffcabfilename = "ooobasis3.0_pff.cab"; - $mergemodulenumber = 0; - %allmergemodulefilesequences = (); - %newupdatefiles = (); - %allusedupdatesequences = (); - %mergemodulefiles = (); $mergefiles_added_into_collector = 0; $creating_windows_installer_patch = 0; $strip = 1; $globallogging = 0; - $globalloggingform21 = 1; $logfilename = "logfile.log"; # the default logfile name for global errors # @logfileinfo = (); # @errorlogfileinfo = (); @@ -273,7 +263,6 @@ BEGIN $isopensourceproduct = 1; $manufacturer = ""; $longmanufacturer = ""; - $sundirname = "Oracle"; $codefilename = "codes.txt"; $componentfilename = "components.txt"; $productcode = ""; @@ -292,7 +281,6 @@ BEGIN $pwfile = ""; $pfxfile = ""; - %mergemodules = (); %merge_media_line = (); %merge_allfeature_hash = (); %merge_alldirectory_hash = (); @@ -375,7 +363,6 @@ BEGIN @pcfdiffcomment = (); @epmdifflist = (); $desktoplinkexists = 0; - $sundirexists = 0; $analyze_spellcheckerlanguage = 0; %spellcheckerlanguagehash = (); %spellcheckerfilehash = (); @@ -403,11 +390,8 @@ BEGIN $officedirhostname = ""; $basisdirhostname = ""; $uredirhostname = ""; - $sundirhostname = ""; $officedirgid = ""; $basisdirgid = ""; - $uredirgid = ""; - $sundirgid = ""; %sign_extensions = ("dll" => "1", "exe" => "1", "cab" => "1"); %treestyles = (); @@ -418,13 +402,10 @@ BEGIN %usedtreeconditions = (); %moduledestination = (); - $one_cab_file = 0; $fix_number_of_cab_files = 1; - $cab_file_per_component = 0; $cabfilecompressionlevel = 2; $number_of_cabfiles = 1; # only for $fix_number_of_cab_files = 1 $include_cab_in_msi = 0; - $use_packages_for_cabs = 0; $msidatabasename = ""; $prepare_winpatch = 0; $previous_idt_dir = ""; @@ -451,7 +432,6 @@ BEGIN $postprocess_specialepm = 0; $postprocess_standardepm = 0; - $mergemodules_analyzed = 0; $starttime = ""; diff --git a/solenv/bin/modules/installer/languages.pm b/solenv/bin/modules/installer/languages.pm index d184ff7f66ee..030956d0c529 100644 --- a/solenv/bin/modules/installer/languages.pm +++ b/solenv/bin/modules/installer/languages.pm @@ -30,31 +30,46 @@ use installer::globals; use installer::remover; use installer::ziplist; -############################################################################# -# Analyzing the laguage list parameter and language list from zip list file -############################################################################# +=head2 analyze_languagelist() -sub analyze_languagelist -{ - my $first = $installer::globals::languagelist; + Convert $installer::globals::languagelist into $installer::globals::languageproduct. + + That is now just a replacement of '_' with ','. + + $installer::globals::languageproduct (specified by the -l option + on the command line) can contain multiple languages separated by + '_' to specify multilingual builds. - $first =~ s/\_/\,/g; # substituting "_" by ",", in case of dmake definition 01_49 + Separation by '#' to build multiple languages (single or + multilingual) in one make_installer.pl run is not supported + anymore. Call make_installer.pl with all languages separately instead: + make_installer.pl -l L1#L2 + -> + make_installer.pl -l L1 + make_installer.pl -l L2 - # Products are separated by a "#", if defined in zip-list by a "|". But "get_info_about_languages" - # substitutes already "|" to "#". This procedure only knows "#" as product separator. - # Different languages for one product are separated by ",". But on the command line the "_" is used. - # Therefore "_" is replaced by "," at the beginning of this procedure. +=cut +sub analyze_languagelist() +{ + my $languageproduct = $installer::globals::languagelist; + + $languageproduct =~ s/\_/\,/g; # substituting "_" by ",", in case of dmake definition 01_49 - while ($first =~ /^(\S+)\#(\S+?)$/) # Minimal matching, to keep the order of languages + if ($languageproduct =~ /\#/) { - $first = $1; - my $last = $2; - unshift(@installer::globals::languageproducts, $last); + installer::exiter::exit_program( + "building more than one language (or language set) is not supported anymore\n" + ."please replace one call of 'make_installer.pl -l language1#language2'\n" + ."with two calls 'make_installer.pl -l language1' and 'make_installer.pl -l language2'", + "installer::language::analyze_languagelist"); } - unshift(@installer::globals::languageproducts, $first); + $installer::globals::languageproduct = $languageproduct; } + + + #################################################### # Reading languages from zip list file #################################################### diff --git a/solenv/bin/modules/installer/parameter.pm b/solenv/bin/modules/installer/parameter.pm index faa3cc1fde2a..eb4209b82c7d 100644 --- a/solenv/bin/modules/installer/parameter.pm +++ b/solenv/bin/modules/installer/parameter.pm @@ -618,8 +618,7 @@ sub outputparameter () else { push(@output, "Not unzipping ARCHIVE files\n"); } if (!($installer::globals::languages_defined_in_productlist)) { - push(@output, "Languages:\n"); - foreach my $element (@installer::globals::languageproducts) { push(@output, "\t$element\n"); } + push(@output, sprintf("Languages: %s\n", $installer::globals::languageproduct)); } else { diff --git a/solenv/bin/modules/installer/patch/FileOperations.pm b/solenv/bin/modules/installer/patch/FileOperations.pm new file mode 100644 index 000000000000..931db2eca654 --- /dev/null +++ b/solenv/bin/modules/installer/patch/FileOperations.pm @@ -0,0 +1,333 @@ +#************************************************************** +# +# 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::patch::FileOperations; + +use File::Basename; +use File::Copy; +use IO::Compress::Bzip2; +use IO::Uncompress::Bunzip2; + +my $CompressionMethod = "bzip2"; + + +=head1 NAME + + package installer::patch::FileOperations - Class for collecting, checking and executing file operations. + +=cut + + +sub new ($) +{ + my ($class) = (@_); + + my $self = { + 'operations' => [] + }; + bless($self, $class); + + return $self; +} + + + + +sub AddCopyOperation ($$$) +{ + my ($self, $source_name, $target_name) = @_; + + push + @{$self->{'operations'}}, + [ + 'copy', + $source_name, + $target_name + ]; +} + + + + +sub AddMakeDirectoryOperation ($$) +{ + my ($self, $path) = @_; + + push + @{$self->{'operations'}}, + [ + 'mkdir', + $path + ]; +} + + + + +sub AddCompressOperation ($$) +{ + my ($self, $filename) = @_; + + push + @{$self->{'operations'}}, + [ + 'compress', + $filename + ]; +} + + + + +sub AddUncompressOperation ($$$) +{ + my ($self, $source_name, $target_name) = @_; + + push + @{$self->{'operations'}}, + [ + 'uncompress', + $source_name, + $target_name + ]; +} + + + + +sub Check ($) +{ + my ($self) = @_; + + # Keep track of which directories or files would be created to check if + # operations that depend on these files will succeed. + my %files = (); + my %directories = (); + + my @error_messages = (); + foreach my $operation (@{$self->{'operations'}}) + { + my $command = $operation->[0]; + + if ($command eq "copy") + { + my ($source_name, $destination_name) = ($operation->[1], $operation->[2]); + if ( ! -f $source_name) + { + push @error_messages, sprintf("%s is not a regular file and can not be copied", $source_name); + } + my $destination_path = dirname($destination_name); + if ( ! -d $destination_path && ! defined $directories{$destination_path}) + { + push @error_messages, sprintf("destination path %s does not exist", $destination_path); + } + if ( -f $destination_name) + { + # The destination file already exists. We have to overwrite it. + if ( ! -w $destination_name) + { + push @error_messges, sprintf("destination file %s exists but can not be overwritten", $destination_name); + } + } + $files{$destination_name} = 1; + } + elsif ($command eq "mkdir") + { + my $path = $operation->[1]; + if ( -d $path) + { + # Directory already exists. That is OK, the mkdir command will be silently ignored. + } + else + { + $directories{$path} = 1; + } + } + elsif ($command eq "compress") + { + my $filename = $operation->[1]; + if ( ! -f $filename && ! defined $files{$filename}) + { + # File does not exist and will not be created by an earlier operation. + push @error_messages, sprintf("file %s does not exist and can not be compressed", $filename); + } + } + elsif ($command eq "uncompress") + { + my ($source_filename, $destination_filename) = ($operation->[1], $operation->[2]); + if ($CompressionMethod eq "bzip2") + { + $source_filename .= ".bz2"; + } + if ( ! -f $source_filename && ! defined $files{$source_filename}) + { + # File does not exist and will not be created by an earlier operation. + push @error_messages, sprintf("file %s does not exist and can not be decompressed", $source_filename); + } + if ( -f $destination_filename && ! -w $destination_filename) + { + # Destination file aleady exists but can not be replaced. + push @error_messages, sprintf("compress destination file %s exists but can not be replaced", $destination_filename); + } + } + else + { + push @error_messages, sprintf("unknown operation %s", $command); + } + } + + return @error_messages; +} + + + + +sub CheckAndExecute ($) +{ + my ($self) = @_; + + my @error_messages = $self->Check(); + if (scalar @error_messages > 0) + { + $installer::logger::Lang->printf("can not execute all operations:\n"); + for my $message (@error_messages) + { + $installer::logger::Lang->printf("ERROR: %s\n", $message); + } + return 0; + } + else + { + return $self->Execute(); + } +} + + + + +sub Execute ($) +{ + my ($self) = @_; + + foreach my $operation (@{$self->{'operations'}}) + { + my $command = $operation->[0]; + + if ($command eq "copy") + { + my ($source_name, $destination_name) = ($operation->[1], $operation->[2]); + $installer::logger::Lang->printf("copy from %s\n to %s\n", $source_name, $destination_name); + if ( ! $DryRun) + { + my $result = copy($source_name, $destination_name); + if ( ! $result) + { + $installer::logger::Lang->printf("ERROR: copying from %s to %s failed", + $source_name, $destination_name); + } + } + } + elsif ($command eq "mkdir") + { + my $path = $operation->[1]; + if ( -d $path) + { + # Path exists already. Do nothing. + } + else + { + $installer::logger::Lang->printf("creating directory %s\n", $path); + if ( ! $DryRun) + { + if (File::Path::make_path($path, {'mode' => 0775}) == 0) + { + $installer::logger::Lang->printf("could not create directory %s\n", $path); + } + } + } + } + elsif ($command eq "compress") + { + my $filename = $operation->[1]; + $installer::logger::Lang->printf("compressing %s\n", $filename); + if ( ! $DryRun) + { + my $result = 0; + if ($CompressionMethod eq "bzip2") + { + $result = IO::Compress::Bzip2::bzip2($filename => $filename.".bz2"); + } + if ($result == 0) + { + $installer::logger::Lang->printf("ERROR: could not compress %s\n", $filename); + } + else + { + unlink($filename); + } + } + } + elsif ($command eq "uncompress") + { + my ($source_name, $destination_name) = ($operation->[1], $operation->[2]); + if ($CompressionMethod eq "bzip2") + { + $source_name .= ".bz2"; + } + $installer::logger::Lang->printf("uncompressing %s to %s\n", $source_name, $destination_name); + + my $destination_base_name = basename($destination_name); + + if ( ! $DryRun) + { + my $result = 0; + if ($CompressionMethod eq "bzip2") + { + $result = IO::Uncompress::Bunzip2::bunzip2($source_name => $destination_name); + } + if ($result == 0) + { + $installer::logger::Lang->printf("ERROR: failed to extract content of '%s' from '%s'\n", + $destination_name, $source_name); + return 0; + } + } + } + + else + { + die "unknown operation $command\n"; + } + } + + return 1; +} + + + +sub GetOperationCount ($) +{ + my ($self) = @_; + return scalar @{$self->{'operations'}}; +} + + +1; diff --git a/solenv/bin/modules/installer/patch/FileSequenceList.pm b/solenv/bin/modules/installer/patch/FileSequenceList.pm new file mode 100644 index 000000000000..6c607d8314e8 --- /dev/null +++ b/solenv/bin/modules/installer/patch/FileSequenceList.pm @@ -0,0 +1,159 @@ +#************************************************************** +# +# 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::patch::FileSequenceList; + +use XML::LibXML; +use strict; + +=head1 NAME + + FileSequenceList.pm - Class for retrieving and processing the 'Sequence' values of the MSI 'File' table. + +=cut + +=head2 new($class) + + Create a new FileSequenceList object. + +=cut +sub new ($) +{ + my ($class) = @_; + + my $self = { + 'data' => undef + }; + bless($self, $class); + + return $self; +} + + + + +sub SetFromFileList ($$) +{ + my ($self, $files) = @_; + + my %data = map {$_->{'uniquename'} => $_->{'sequencenumber'}} @$files; + $self->{'data'} = \%data; +} + + + + +sub SetFromMap ($$) +{ + my ($self, $map) = @_; + + $self->{'data'} = $map; +} + + + + +sub GetFileCount ($) +{ + my ($self) = @_; + + return scalar keys %{$self->{'data'}}; +} + + + + +=head2 GetSequenceNumbers ($files) + + $files is a hash that maps unique file names (File->File) to sequence + numbers (File->Sequence). The later is (expected to be) initially unset and + is set in this method. + + For new files -- entries in the given $files that do not exist in the 'data' + member -- no sequence numbers are defined. + + When there are removed files -- entries in the 'data' member that do not + exist in the given $files -- then a list of these files is returned. In + that case the given $files remain unmodified. + + The returned list is empty when everyting is OK. + +=cut +sub GetSequenceNumbers ($$) +{ + my ($self, $files) = @_; + + # Check if files have been removed. + my @missing = (); + foreach my $name (keys %{$self->{'data'}}) + { + if ( ! defined $files->{$name}) + { + push @missing, $name; + } + } + if (scalar @missing > 0) + { + # Yes. Return the names of the removed files. + return @missing; + } + + # No files where removed. Set the sequence numbers. + foreach my $name (keys %$files) + { + $files->{$name} = $self->{'data'}->{$name}; + } + return (); +} + + + + +sub GetDifference ($$) +{ + my ($self, $other) = @_; + + # Create maps for easy reference. + my (@files_in_both, @files_in_self, @files_in_other); + foreach my $name (keys %{$self->{'data'}}) + { + if (defined $other->{'data'}->{$name}) + { + push @files_in_both, $name; + } + else + { + push @files_in_self, $name; + } + } + foreach my $name (keys %{$self->{'data'}}) + { + if ( ! defined $self->{'data'}->{$name}) + { + push @files_in_other, $name; + } + } + + return (\@files_in_both, \@files_in_self, \@files_in_other); +} + + +1; diff --git a/solenv/bin/modules/installer/patch/InstallationSet.pm b/solenv/bin/modules/installer/patch/InstallationSet.pm new file mode 100644 index 000000000000..67ff1fe4e0b7 --- /dev/null +++ b/solenv/bin/modules/installer/patch/InstallationSet.pm @@ -0,0 +1,467 @@ +#************************************************************** +# +# 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::patch::InstallationSet; + +use installer::patch::Tools; +use installer::patch::Version; +use installer::logger; + + +my $Unpacker = "/c/Program\\ Files/7-Zip/7z.exe"; + +=head1 NAME + + package installer::patch::InstallationSet - Functions for handling installation sets + +=head1 DESCRIPTION + + This package contains functions for unpacking the .exe files that + are created by the NSIS installer creator and the .cab files in + the installation sets. + +=cut + +sub UnpackExe ($$) +{ + my ($filename, $destination_path) = @_; + + $installer::logger::Info->printf("unpacking installation set to '%s'\n", $destination_path); + + # Unpack to a temporary path and change its name to the destination path + # only when the unpacking has completed successfully. + my $temporary_destination_path = $destination_path . ".tmp"; + File::Path::make_path($temporary_destination_path); + + my $windows_filename = installer::patch::Tools::CygpathToWindows($filename); + my $windows_destination_path = installer::patch::Tools::CygpathToWindows($temporary_destination_path); + my $command = join(" ", + $Unpacker, + "x", "-o".$windows_destination_path, + $windows_filename); + my $result = qx($command); + + # Check the existence of the .cab files. + my $cab_filename = File::Spec->catfile($temporary_destination_path, "openoffice1.cab"); + if ( ! -f $cab_filename) + { + installer::logger::PrintError("cab file '%s' was not extracted from installation set\n", $cab_filename); + return 0; + } + if (rename($temporary_destination_path, $destination_path) == 0) + { + installer::logger::PrintError("can not rename temporary extraction directory\n"); + return 0; + } + return 1; +} + + + + +=head2 UnpackCab($cab_filename, $destination_path) + + Unpacking the cabinet file inside an .exe installation set is a + three step process because there is no directory information stored + inside the cab file. This has to be taken from the 'File' and + 'Directory' tables in the .msi file. + + 1. Setup the directory structure of all files in the cab from the 'File' and 'Directory' tables in the msi. + + 2. Unpack the cab file. + + 3. Move the files to their destination directories. + +=cut +sub UnpackCab ($$$) +{ + my ($cab_filename, $msi, $destination_path) = @_; + + # Step 1 + # Extract the directory structure from the 'File' and 'Directory' tables in the given msi. + $installer::logger::Info->printf("setting up directory tree\n"); + my $file_table = $msi->GetTable("File"); + my $file_to_directory_map = $msi->GetFileToDirectoryMap(); + + # Step 2 + # Unpack the .cab file to a temporary path. + my $temporary_destination_path = $destination_path . ".tmp"; + if ( -d $temporary_destination_path) + { + # Temporary directory already exists => cab file has already been unpacked (flat), nothing to do. + $installer::logger::Info->printf("cab file has already been unpacked to flat structure\n"); + } + else + { + UnpackCabFlat($cab_filename, $temporary_destination_path, $file_table); + } + + # Step 3 + # Move the files to their destinations. + File::Path::make_path($destination_path); + $installer::logger::Info->printf("moving files to their directories\n"); + my $count = 0; + foreach my $file_row (@{$file_table->GetAllRows()}) + { + my $unique_name = $file_row->GetValue('File'); + my $directory_full_names = $file_to_directory_map->{$unique_name}; + my ($source_full_name, $target_full_name) = @$directory_full_names; + + my $flat_filename = File::Spec->catfile($temporary_destination_path, $unique_name); + my $dir_path = File::Spec->catfile($destination_path, $source_full_name); + my $dir_filename = File::Spec->catfile($dir_path, $unique_name); + + printf("%d: making path %s and copying %s to %s\n", + $count, + $dir_path, + $unique_name, + $dir_filename); + File::Path::make_path($dir_path); + File::Copy::move($flat_filename, $dir_filename); + + ++$count; + } + + # Cleanup. Remove the temporary directory. It should be empty by now. + rmdir($temporary_destination_path); +} + + + + +=head2 UnpackCabFlat ($cab_filename, $destination_path, $file_table) + + Unpack the flat file structure of the $cab_filename to $destination_path. + + In order to detect and handle an incomplete (arborted) previous + extraction, the cab file is unpacked to a temprorary directory + that after successful extraction is renamed to $destination_path. + +=cut +sub UnpackCabFlat ($$$) +{ + my ($cab_filename, $destination_path, $file_table) = @_; + + # Unpack the .cab file to a temporary path (note that + # $destination_path may alreay bee a temporary path). Using a + # second one prevents the lengthy flat unpacking to be repeated + # when another step fails. + + $installer::logger::Info->printf("unpacking cab file\n"); + my $temporary_destination_path = $destination_path . ".tmp"; + File::Path::make_path($temporary_destination_path); + my $windows_cab_filename = installer::patch::Tools::CygpathToWindows($cab_filename); + my $windows_destination_path = installer::patch::Tools::CygpathToWindows($temporary_destination_path); + my $command = join(" ", + $Unpacker, + "x", "-o".$windows_destination_path, + $windows_cab_filename, + "-y"); + printf("running command '%s'\n", $command); + open my $cmd, $command."|"; + my $extraction_count = 0; + my $file_count = $file_table->GetRowCount(); + while (<$cmd>) + { + my $message = $_; + chomp($message); + ++$extraction_count; + printf("%4d/%4d %3.2f%% \r", + $extraction_count, + $file_count, + $extraction_count*100/$file_count); + } + close $cmd; + printf("extraction done \n"); + + rename($temporary_destination_path, $destination_path) + || installer::logger::PrintError( + "can not rename the temporary directory '%s' to '%s'\n", + $temporary_destination_path, + $destination_path); +} + + + + +=head GetUnpackedMsiPath ($version, $language, $package_format, $product) + + Convenience function that returns where a downloadable installation set is extracted to. + +=cut +sub GetUnpackedMsiPath ($$$$) +{ + my ($version, $language, $package_format, $product) = @_; + + return File::Spec->catfile( + GetUnpackedPath($version, $language, $package_format, $product), + "unpacked_msi"); +} + + + + +=head GetUnpackedCabPath ($version, $language, $package_format, $product) + + Convenience function that returns where a cab file is extracted + (with injected directory structure from the msi file) to. + +=cut +sub GetUnpackedCabPath ($$$$) +{ + my ($version, $language, $package_format, $product) = @_; + + return File::Spec->catfile( + GetUnpackedPath($version, $language, $package_format, $product), + "unpacked_cab"); +} + + + + +=head2 GetUnpackedPath($version, $language, $package_format, $product) + + Internal function for creating paths to where archives are unpacked. + +=cut +sub GetUnpackedPath ($$$$) +{ + my ($version, $language, $package_format, $product) = @_; + + return File::Spec->catfile( + $ENV{'SRC_ROOT'}, + "instsetoo_native", + $ENV{'INPATH'}, + $product, + $package_format, + installer::patch::Version::ArrayToDirectoryName(installer::patch::Version::StringToNumberArray($version)), + $language); +} + + + + +=head2 Download($language, $release_data, $filename) + + Download an installation set to $filename. The URL for the + download is taken from $release_data, a snippet from the + instsetoo_native/data/releases.xml file. + +=cut +sub Download ($$$) +{ + my ($language, $release_data, $filename) = @_; + + my $url = $release_data->{'URL'}; + $release_data->{'URL'} =~ /^(.*)\/([^\/]+)$/; + my ($location, $basename) = ($1,$2); + + $installer::logger::Info->printf("downloading %s\n", $basename); + $installer::logger::Info->printf(" from '%s'\n", $location); + my $filesize = $release_data->{'file-size'}; + $installer::logger::Info->printf(" expected size is %d\n", $filesize); + my $temporary_filename = $filename . ".part"; + my $resume_size = 0; + if ( -f $temporary_filename) + { + $resume_size = -s $temporary_filename; + $installer::logger::Info->printf(" trying to resume at %d/%d bytes\n", $resume_size, $filesize); + } + + # Prepare checksum. + my $checksum = undef; + my $checksum_type = $release_data->{'checksum-type'}; + my $checksum_value = $release_data->{'checksum-value'}; + my $digest = undef; + if ($checksum_type eq "sha256") + { + $digest = Digest->new("SHA-256"); + } + elsif ($checksum_type eq "md5") + { + $digest = Digest->new("md5"); + } + else + { + installer::logger::PrintError( + "checksum type %s is not supported. Supported checksum types are: sha256,md5\n", + $checksum_type); + return 0; + } + + # Download the extension. + open my $out, ">>$temporary_filename"; + binmode($out); + + my $mode = $|; + my $handle = select STDOUT; + $| = 1; + select $handle; + + my $agent = LWP::UserAgent->new(); + $agent->timeout(120); + $agent->show_progress(0); + my $last_was_redirect = 0; + my $bytes_read = 0; + $agent->add_handler('response_redirect' + => sub{ + $last_was_redirect = 1; + return; + }); + $agent->add_handler('response_data' + => sub{ + if ($last_was_redirect) + { + $last_was_redirect = 0; + # Throw away the data we got so far. + $digest->reset(); + close $out; + open $out, ">$temporary_filename"; + binmode($out); + } + my($response,$agent,$h,$data)=@_; + print $out $data; + $digest->add($data); + $bytes_read += length($data); + printf("read %*d / %d %d%% \r", + length($filesize), + $bytes_read, + $filesize, + $bytes_read*100/$filesize); + }); + my $response; + if ($resume_size > 0) + { + $response = $agent->get($url, 'Range' => "bytes=$resume_size-"); + } + else + { + $response = $agent->get($url); + } + close $out; + + $handle = select STDOUT; + $| = $mode; + select $handle; + + $installer::logger::Info->print(" \r"); + + if ($response->is_success()) + { + if ($digest->hexdigest() eq $checksum_value) + { + $installer::logger::Info->PrintInfo("download was successfull\n"); + if ( ! rename($temporary_filename, $filename)) + { + installer::logger::PrintError("can not rename '%s' to '%s'\n", $temporary_filename, $filename); + return 0; + } + else + { + return 1; + } + } + else + { + installer::logger::PrintError("%s checksum is wrong\n", $checksum_type); + return 0; + } + } + else + { + installer::logger::PrintError("there was a download error\n"); + return 0; + } +} + + + + +=head2 ProvideDownloadSet ($version, $language, $package_format) + + Download an installation set when it is not yet present to + $ENV{'TARFILE_LOCATION'}. Verify the downloaded file with the + checksum that is extracted from the + instsetoo_native/data/releases.xml file. + +=cut +sub ProvideDownloadSet ($$$) +{ + my ($version, $language, $package_format) = @_; + + my $release_item = installer::patch::ReleasesList::Instance()->{$version}->{$package_format}->{$language}; + + # Get basename of installation set from URL. + $release_item->{'URL'} =~ /^(.*)\/([^\/]+)$/; + my ($location, $basename) = ($1,$2); + + # Is the installation set already present in ext_sources/ ? + my $need_download = 0; + my $ext_sources_filename = File::Spec->catfile( + $ENV{'TARFILE_LOCATION'}, + $basename); + if ( ! -f $ext_sources_filename) + { + $installer::logger::Info->printf("download set is not in ext_sources/ (%s)\n", $ext_sources_filename); + $need_download = 1; + } + else + { + $installer::logger::Info->printf("download set exists at '%s'\n", $ext_sources_filename); + if ($release_item->{'checksum-type'} eq 'sha256') + { + $installer::logger::Info->printf("checking SHA256 checksum\n"); + my $digest = Digest->new("SHA-256"); + open my $in, "<", $ext_sources_filename; + $digest->addfile($in); + close $in; + if ($digest->hexdigest() ne $release_item->{'checksum-value'}) + { + $installer::logger::Info->printf(" mismatch\n", $ext_sources_filename); + $need_download = 1; + } + else + { + $installer::logger::Info->printf(" match\n"); + } + } + } + + if ($need_download) + { + if ( ! installer::patch::InstallationSet::Download( + $language, + $release_item, + $ext_sources_filename)) + { + return 0; + } + if ( ! -f $ext_sources_filename) + { + $installer::logger::Info->printf("download set could not be downloaded\n"); + return 0; + } + } + + return $ext_sources_filename; +} + +1; diff --git a/solenv/bin/modules/installer/patch/Msi.pm b/solenv/bin/modules/installer/patch/Msi.pm new file mode 100644 index 000000000000..c5c650a82c47 --- /dev/null +++ b/solenv/bin/modules/installer/patch/Msi.pm @@ -0,0 +1,342 @@ +#************************************************************** +# +# 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::patch::Msi; + +use installer::patch::MsiTable; +use installer::patch::Tools; +use strict; + + +=head1 NAME + + package installer::patch::Msi - Class represents a single MSI file and gives access to its tables. + +=cut + + + +=head2 new($class, $version, $language, $product_name) + + Create a new object of the Msi class. The values of $version, $language, and $product_name define + where to look for the msi file. + + If construction fails then IsValid() will return false. + +=cut +sub new ($$$$) +{ + my ($class, $version, $language, $product_name) = @_; + + my $path = installer::patch::InstallationSet::GetUnpackedMsiPath( + $version, + $language, + "msi", + $product_name); + + # Find the msi in the path. + my $filename = undef; + if ( -d $path) + { + my @msi_files = glob(File::Spec->catfile($path, "*.msi")); + if (scalar @msi_files != 1) + { + printf STDERR ("there are %d msi files in %s, should be 1", scalar @msi_files, $filename); + $filename = ""; + } + else + { + $filename = $msi_files[0]; + } + } + else + { + installer::logger::PrintError("can not access path '%s' to find msi\n", $path); + return undef; + } + + if ( ! -f $filename) + { + installer::logger::PrintError("can not access MSI file at '%s'\n", $filename); + return undef; + } + + my $self = { + 'filename' => $filename, + 'path' => $path, + 'version' => $version, + 'language' => $language, + 'package_format' => "msi", + 'product_name' => $product_name, + 'tmpdir' => File::Temp->newdir(CLEANUP => 1), + 'is_valid' => -f $filename + }; + bless($self, $class); + + return $self; +} + + + + +sub IsValid ($) +{ + my ($self) = @_; + + return $self->{'is_valid'}; +} + + + + +=head2 GetTable($seld, $table_name) + + Return an MsiTable object for $table_name. Table objects are kept + alive for the life time of the Msi object. Therefore the second + call for the same table is very cheap. + +=cut +sub GetTable ($$) +{ + my ($self, $table_name) = @_; + + my $table = $self->{'tables'}->{$table_name}; + if ( ! defined $table) + { + my $table_filename = File::Spec->catfile($self->{'tmpdir'}, $table_name .".idt"); + if ( ! -f $table_filename + || ! EnsureAYoungerThanB($table_filename, $self->{'fullname'})) + { + # Extract table from database to text file on disk. + my $truncated_table_name = length($table_name)>8 ? substr($table_name,0,8) : $table_name; + my $command = join(" ", + "msidb.exe", + "-d", installer::patch::Tools::CygpathToWindows($self->{'filename'}), + "-f", installer::patch::Tools::CygpathToWindows($self->{'tmpdir'}), + "-e", $table_name); + my $result = qx($command); + print $result; + } + + # Read table into memory. + $table = new installer::patch::MsiTable($table_filename, $table_name); + $self->{'tables'}->{$table_name} = $table; + } + + return $table; +} + + + + +=head2 EnsureAYoungerThanB ($filename_a, $filename_b) + + Internal function (not a method) that compares to files according + to their last modification times (mtime). + +=cut +sub EnsureAYoungerThanB ($$) +{ + my ($filename_a, $filename_b) = @_; + + die("file $filename_a does not exist") unless -f $filename_a; + die("file $filename_b does not exist") unless -f $filename_b; + + my @stat_a = stat($filename_a); + my @stat_b = stat($filename_b); + + if ($stat_a[9] <= $stat_b[9]) + { + return 0; + } + else + { + return 1; + } +} + + + + +=head2 SplitLongShortName($name) + + Split $name (typically from the 'FileName' column in the 'File' + table or 'DefaultDir' column in the 'Directory' table) at the '|' + into short (8.3) and long names. If there is no '|' in $name then + $name is returned as both short and long name. + + Returns long and short name (in this order) as array. + +=cut +sub SplitLongShortName ($) +{ + my ($name) = @_; + + if ($name =~ /^([^\|]*)\|(.*)$/) + { + return ($2,$1); + } + else + { + return ($name,$name); + } +} + + + +=head2 SplitTargetSourceLongShortName ($name) + + Split $name first at the ':' into target and source parts and each + of those at the '|'s into long and short parts. Names that follow + this pattern come from the 'DefaultDir' column in the 'Directory' + table. + +=cut +sub SplitTargetSourceLongShortName ($) +{ + my ($name) = @_; + + if ($name =~ /^([^:]*):(.*)$/) + { + return (installer::patch::Msi::SplitLongShortName($1), installer::patch::Msi::SplitLongShortName($2)); + } + else + { + my ($long,$short) = installer::patch::Msi::SplitLongShortName($name); + return ($long,$short,$long,$short); + } +} + + + + +=head2 GetFileToDirectoryMap ($) + + Return a map (hash) that maps the unique name (column 'File' in + the 'File' table) to its directory names. Each value is a + reference to an array of two elements: the source path and the + target path. + + The map is kept alive for the lifetime of the Msi object. All + calls but the first are cheap. + +=cut +sub GetFileToDirectoryMap ($) +{ + my ($self) = @_; + + if (defined $self->{'FileToDirectoryMap'}) + { + return $self->{'FileToDirectoryMap'}; + } + + my $file_table = $self->GetTable("File"); + my $directory_table = $self->GetTable("Directory"); + my $component_table = $self->GetTable("Component"); + $installer::logger::Info->printf("got access to tables File, Directory, Component\n"); + + my %dir_map = (); + foreach my $row (@{$directory_table->GetAllRows()}) + { + my ($target_name, undef, $source_name, undef) + = installer::patch::Msi::SplitTargetSourceLongShortName($row->GetValue("DefaultDir")); + $dir_map{$row->GetValue("Directory")} = { + 'parent' => $row->GetValue("Directory_Parent"), + 'source_name' => $source_name, + 'target_name' => $target_name}; + } + + # Set up full names for all directories. + my @todo = map {$_} (keys %dir_map); + my $process_count = 0; + my $push_count = 0; + while (scalar @todo > 0) + { + ++$process_count; + + my $key = shift @todo; + my $item = $dir_map{$key}; + next if defined $item->{'full_source_name'}; + + if ($item->{'parent'} eq "") + { + # Directory has no parent => full names are the same as the name. + $item->{'full_source_name'} = $item->{'source_name'}; + $item->{'full_target_name'} = $item->{'target_name'}; + } + else + { + my $parent = $dir_map{$item->{'parent'}}; + if ( defined $parent->{'full_source_name'}) + { + # Parent aleady has full names => we can create the full name of the current item. + $item->{'full_source_name'} = $parent->{'full_source_name'} . "/" . $item->{'source_name'}; + $item->{'full_target_name'} = $parent->{'full_target_name'} . "/" . $item->{'target_name'}; + } + else + { + # Parent has to be processed before the current item can be processed. + # Push both to the head of the list. + unshift @todo, $key; + unshift @todo, $item->{'parent'}; + + ++$push_count; + } + } + } + + foreach my $key (keys %dir_map) + { + $dir_map{$key}->{'full_source_name'} =~ s/\/(\.\/)+/\//g; + $dir_map{$key}->{'full_source_name'} =~ s/^SourceDir\///; + $dir_map{$key}->{'full_target_name'} =~ s/\/(\.\/)+/\//g; + $dir_map{$key}->{'full_target_name'} =~ s/^SourceDir\///; + } + $installer::logger::Info->printf("for %d directories there where %d processing steps and %d pushes\n", + $directory_table->GetRowCount(), + $process_count, + $push_count); + + # Setup a map from component names to directory items. + my %component_to_directory_map = map {$_->GetValue('Component') => $_->GetValue('Directory_')} @{$component_table->GetAllRows()}; + + # Finally, create the map from files to directories. + my $map = {}; + my $file_component_index = $file_table->GetColumnIndex("Component_"); + my $file_file_index = $file_table->GetColumnIndex("File"); + foreach my $file_row (@{$file_table->GetAllRows()}) + { + my $component_name = $file_row->GetValue($file_component_index); + my $directory_name = $component_to_directory_map{$component_name}; + my $dir_item = $dir_map{$directory_name}; + my $unique_name = $file_row->GetValue($file_file_index); + $map->{$unique_name} = [$dir_item->{'full_source_name'},$dir_item->{'full_target_name'}]; + } + + $installer::logger::Info->printf("got full paths for %d files\n", + $file_table->GetRowCount()); + + $self->{'FileToDirectoryMap'} = $map; + return $map; +} + + +1; diff --git a/solenv/bin/modules/installer/patch/MsiRow.pm b/solenv/bin/modules/installer/patch/MsiRow.pm new file mode 100644 index 000000000000..24a6fd22bc68 --- /dev/null +++ b/solenv/bin/modules/installer/patch/MsiRow.pm @@ -0,0 +1,160 @@ +#************************************************************** +# +# 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::patch::MsiRow; + +=head1 NAME + + package installer::patch::MsiRow - Class that represents a single row of an Msi table. + +=cut + + +=head2 new ($class, $table, @data) + + Create a new MsiRow object for the given table row data. Each row + stores a reference to its $table so that it can access global + values like column names. + +=cut +sub new ($$@) +{ + my ($class, $table, @data) = @_; + + my $self = { + 'table' => $table, + 'values' => [@data] + }; + bless($self, $class); + + my $column_count = $table->GetColumnCount(); + while (scalar @{$self->{'values'}} < $column_count) + { + push @{$self->{'values'}}, ""; + } + + return $self; +} + + + +=head2 GetValue($self, $column) + + Return the value in the column specified by $column, which can be + either the column name or the index of the column. + +=cut +sub GetValue ($$) +{ + my ($self, $column) = @_; + + if ($column =~ /^\d+$/) + { + return $self->{'values'}->[$column]; + } + else + { + my $column_index = $self->{'table'}->GetColumnIndex($column); + return $self->{'values'}->[$column_index]; + } +} + + + + +sub SetValue ($$$) +{ + my ($self, $column, $value) = @_; + + if ($column =~ /^\d+$/) + { + $self->{'values'}->[$column] = $value; + } + else + { + my $column_index = $self->{'table'}->GetColumnIndex($column); + $self->{'values'}->[$column_index] = $value; + } + $self->{'table'}->MarkAsModified(); +} + + + + +sub Format ($$) +{ + my $self = shift; + my $concatenation = shift; + + my $result = ""; + my $first = 1; + my $index = 0; + my $column_count = $self->{'table'}->GetColumnCount(); + foreach my $item (@{$self->{'values'}}) + { + ++$index; + + if ( ! $first) + { + $result .= $concatenation; + } + else + { + $first = 0; + } + $result .= $item; + } + return $result; +} + + + + +sub Clone ($$) +{ + my ($self, $new_table) = @_; + + my $clone = { %$self }; + $clone->{'values'} = [ @{$self->{'values'}} ]; + $clone->{'table'} = $new_table; + bless($clone, "MsiRow"); + + return $clone; +} + + + + +sub SetTable ($$) +{ + my ($self, $new_table) = @_; + + if (defined $self->{'table'} && $self->{'table'} != $new_table) + { + MsiTools::Die("can not reset table of row"); + } + else + { + $self->{'table'} = $new_table; + } +} + +1; diff --git a/solenv/bin/modules/installer/patch/MsiTable.pm b/solenv/bin/modules/installer/patch/MsiTable.pm new file mode 100644 index 000000000000..a95b94af17bc --- /dev/null +++ b/solenv/bin/modules/installer/patch/MsiTable.pm @@ -0,0 +1,274 @@ +#************************************************************** +# +# 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::patch::MsiTable; + +=head1 NAME + + package installer::patch::MsiTable - Class that represents one table of an Msi file. + +=cut + +use installer::patch::MsiRow; + +use strict; + +=head new ($class, $filename, $table_name) + + Create a new MsiTable object from the output of a previous + msidb.exe run. The table is named $table_name, its data is read + from $filename. + +=cut +sub new ($$$) +{ + my ($class, $filename, $table_name) = @_; + + my $self = { + 'name' => $table_name, + 'is_valid' => 1 + }; + bless($self, $class); + + if ( -f $filename) + { + $self->ReadFile($filename); + } + return $self; +} + + + + +sub IsValid ($) +{ + my ($self) = @_; + return $self->{'is_valid'}; +} + + + + +sub Trim ($) +{ + my $line = shift; + + $line =~ s/(^\s+|\s+$)//g; + + return $line; +} + + + +=head2 ReadFile($self, $filename) + + Read the content of the table from the specified .idt file. + For each row a MsiRow object is appended to $self->{'rows'}. + +=cut +sub ReadFile ($$) +{ + my ($self, $filename) = @_; + + if ( ! (-f $filename && -r $filename)) + { + printf STDERR ("can not open idt file %s for reading\n", $filename); + $self->{'is_valid'} = 0; + return; + } + + open my $in, "<", $filename; + + my $columns = Trim(<$in>); + $self->{'columns'} = [split(/\t/, $columns)]; + + my $column_specs = Trim(<$in>); + $self->{'column_specs'} = [split(/\t/, $column_specs)]; + + # Table name, index columns. + my $line = Trim(<$in>); + my @items = split(/\t/, $line); + if (scalar @items == 3) + { + $self->{'codepage'} = shift @items; + } + my $table_name = shift @items; + if ($table_name ne $self->{'name'}) + { + printf STDERR ("reading wrong table data for table '%s' (got %s)\n", $self->{'name'}, $table_name); + $self->{'is_valid'} = 0; + return; + } + $self->{'index_columns'} = [@items]; + $self->{'index_column_index'} = $self->GetColumnIndex($items[0]); + + my $rows = []; + while (<$in>) + { + # Remove all trailing returns and newlines. Keep trailing spaces and tabs. + s/[\r\n]+$//g; + + my @items = split(/\t/, $_); + push @$rows, new installer::patch::MsiRow($self, @items); + } + $self->{'rows'} = $rows; + + return $self; +} + + + +=head2 GetColumnCount($self) + + Return the number of columns in the table. + +=cut +sub GetColumnCount ($) +{ + my ($self) = @_; + + return scalar @{$self->{'columns'}}; +} + + + + +=head2 GetRowCount($self) + + Return the number of rows in the table. + +=cut +sub GetRowCount ($) +{ + my ($self) = @_; + + return scalar @{$self->{'rows'}}; +} + + + + +=head2 GetColumnIndx($self, $column_name) + + Return the 0 based index of the column named $column_name. Use + this to speed up (slightly) access to column values when accessing + many or all rows of a table. + +=cut +sub GetColumnIndex ($$) +{ + my ($self, $column_name) = @_; + + my $index = 0; + foreach my $name (@{$self->{'columns'}}) + { + if ($name eq $column_name) + { + return $index; + } + ++$index; + } + + printf STDERR ("did not find column %s in %s\n", $column_name, join(" and ", @{$self->{'columns'}})); + return -1; +} + + + + +=head2 GetValue($self, $selector_column, $selector_column_value, $value_column) + + Find the row in which the $selector_column has value + $selector_column_value and return its value in the $value_column. + +=cut + +sub GetValue ($$$$) +{ + my ($self, $selector_column, $selector_column_value, $value_column) = @_; + + my $row = $self->GetRow($selector_column, $selector_column_value); + if (defined $row) + { + return $row->GetValue($value_column); + } + else + { + return undef; + } +} + + + + +=head2 GetRow($self, $column, $value) + + Return the (first) row which has $value in $column. + +=cut +sub GetRow ($$$) +{ + my ($self, $column, $value) = @_; + + my $column_index = $self->GetColumnIndex($column); + if ($column_index<0) + { + printf STDERR "ERROR: unknown column $column in table $self->{'name'}\n"; + return undef; + } + + foreach my $row (@{$self->{'rows'}}) + { + if ($row->GetValue($column_index) eq $value) + { + return $row; + } + } + + printf STDERR ("ERROR: did not find row for %s->%s in %s\n", + $column, + $value, + table $self->{'name'}); + + return undef; +} + + + + +=head2 GetAllRows ($self) + + Return the reference to an array that contains all rows of the table. + +=cut + +sub GetAllRows ($) +{ + my $self = shift; + + return $self->{'rows'}; +} + + + + + +1; diff --git a/solenv/bin/modules/installer/patch/ReleasesList.pm b/solenv/bin/modules/installer/patch/ReleasesList.pm new file mode 100644 index 000000000000..320e86400998 --- /dev/null +++ b/solenv/bin/modules/installer/patch/ReleasesList.pm @@ -0,0 +1,210 @@ +#************************************************************** +# +# 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::patch::ReleasesList; + +use XML::LibXML; +use File::Spec; +use strict; + +=head1 NAME + + package installer::patch::ReleasesList - Functions for accessing the instsetoo_native/data/releases.xml file + +=cut + + +my $Instance = undef; + +=head2 Instance() + + Return the singleton instance. + +=cut +sub Instance() +{ + if ( ! defined $Instance) + { + $Instance = new installer::patch::ReleasesList(); + } + return $Instance; +} + + + + +=head2 new($class) + + Internal constructor. Don't call. + +=cut +sub new ($) +{ + my ($class) = @_; + + my $self = {}; + bless($self, $class); + + $self->Read(); + + return $self; +} + + + + +=head2 GetFirstChild ($node, $child_name) + + Internal function that returns the first child. Use only when the + first child is the (expected) only child in a list. + +=cut +sub GetFirstChild ($$) +{ + my ($node, $child_name) = @_; + + if ( ! defined $node) + { + return undef; + } + else + { + my @child_nodes = $node->getElementsByTagName($child_name); + if (scalar @child_nodes == 0) + { + return undef; + } + else + { + return $child_nodes[0]; + } + } +} + + + + +=head2 GetText ($node) + + Internal function that returns the trimmed text content of a node. + +=cut +sub GetText ($) +{ + my ($node) = @_; + + if ( ! defined $node) + { + return ""; + } + else + { + my $text = $node->textContent(); + $text =~ s/(^\s+|\s+$)//g; + return $text; + } +} + + + + +=head2 Read($self) + + Read the releases.xml file as doctree and parse its content. + +=cut +sub Read ($) +{ + my ($self) = @_; + + my $filename = File::Spec->catfile($ENV{'SRC_ROOT'}, "instsetoo_native", "data", "releases.xml"); + my $parser = XML::LibXML->new(); + my $document = $parser->parse_file($filename); + foreach my $release_node ($document->getElementsByTagName("release")) + { + my $version_node = GetFirstChild($release_node, "version"); + my $version = GetText($version_node); + next if $version eq ""; + + foreach my $download_node (GetFirstChild($release_node, "download")) + { + my $package_node = GetFirstChild($download_node, "package-format"); + my $package_format = GetText($package_node); + next if $package_format eq ""; + + my $download_data = ParseDownloadData($download_node); + if (defined $download_data) + { + $self->{$version}->{$package_format} = $download_data; + } + } + } + +} + + + + +=head2 ParseDownloadData ($download_node) + + Parse the data for one set of download data (there is one per release and package format). + +=cut +sub ParseDownloadData ($) +{ + my ($download_node) = @_; + + my $url_node = GetFirstChild($download_node, "url-template"); + my $url_template = GetText($url_node); + if ($url_template eq "") + { + print STDERR "releases data file corrupt (no URL template)\n"; + return undef; + } + + my $download_data = {}; + foreach my $item_node (@{$download_node->getElementsByTagName("item")}) + { + my $language = GetText(GetFirstChild($item_node, "language")); + my $checksum_node = GetFirstChild($item_node, "checksum"); + if ( ! defined $checksum_node) + { + print STDERR "releases data file corrupt (item has no 'checksum' node)\n"; + return undef; + } + my $checksum_type = $checksum_node->getAttribute("type"); + my $checksum_value = GetText($checksum_node); + my $file_size = GetText(GetFirstChild($item_node, "size")); + + my $url = $url_template; + $url =~ s/\%L/$language/g; + $download_data->{$language} = { + 'URL' => $url, + 'checksum-type' => $checksum_type, + 'checksum-value' => $checksum_value, + 'file-size' => $file_size + }; + } + + return $download_data; +} + +1; diff --git a/solenv/bin/modules/installer/patch/Tools.pm b/solenv/bin/modules/installer/patch/Tools.pm new file mode 100644 index 000000000000..b29b5596b381 --- /dev/null +++ b/solenv/bin/modules/installer/patch/Tools.pm @@ -0,0 +1,47 @@ +#************************************************************** +# +# 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::patch::Tools; + +=head1 NAME + + package installer::patch::Tools - Collection of functions that don't fit anywhere else + +=cut + + + + +=head2 CygpathToWindows ($path) + + Convert the given path with the 'cygpath' command into Windows format. Quote backslashes. + +=cut +sub CygpathToWindows($) +{ + my ($path) = @_; + my $windows_path = qx(cygpath -w "$path"); + $windows_path =~ s/(^\s+|\s+$)//g; + $windows_path =~ s/\\/\\\\/g; + return $windows_path; +} + +1; diff --git a/solenv/bin/modules/installer/patch/Version.pm b/solenv/bin/modules/installer/patch/Version.pm new file mode 100644 index 000000000000..685df6dc5c58 --- /dev/null +++ b/solenv/bin/modules/installer/patch/Version.pm @@ -0,0 +1,74 @@ +#************************************************************** +# +# 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::patch::Version; + + +=head1 NAME + + package installer::patch::Version - Functions for handling version numbers. + +=cut + + + +# We handle version numbers that consist of three parts: major, minor and micro version number. +my $VersionPartCount = 3; + + + +=head StringToNumberArray($version_string) + + Convert a version string (where the individual parts are separated by '.') into an array of three numbers. + Missing numbers are filled with 0. + + Returns an array with three elements (major, minor, micro). +=cut +sub StringToNumberArray ($) +{ + my ($version_string) = @_; + + my @version_parts = split(/\./, $version_string); + while (scalar @version_parts < $VersionPartCount) + { + push @version_parts, "0"; + } + return @version_parts; +} + + + + +=head ArrayToDirectoryName (@) + + Return a directory name (without any path) for the given array of version numbers. + +=cut +sub ArrayToDirectoryName (@) +{ + return "v-".join("-", @_); +} + + + + + +1; diff --git a/solenv/bin/modules/installer/scriptitems.pm b/solenv/bin/modules/installer/scriptitems.pm index d07498bdbafc..127bcff2c876 100644 --- a/solenv/bin/modules/installer/scriptitems.pm +++ b/solenv/bin/modules/installer/scriptitems.pm @@ -534,7 +534,7 @@ sub add_bundled_extension_blobs { # Add the default extensions for the current language set. # http:// extensions are taken from ext_sources/. - for my $name (ExtensionsLst::GetExtensionList("http|https", @installer::globals::languageproducts)) + for my $name (ExtensionsLst::GetExtensionList("http|https", ($installer::globals::languageproduct))) { push @bundle_files, $bundlehttpsrc . $name; } @@ -542,11 +542,10 @@ sub add_bundled_extension_blobs } $installer::logger::Info->printf( - "preparing %d extension blob%s for language%s %s:\n", + "preparing %d extension blob%s for language %s:\n", $#bundle_files + 1, $#bundle_files!=0 ? "s" : "", - $#installer::globals::languageproducts!=0 ? "s" : "", - join(" ", @installer::globals::languageproducts)); + $installer::globals::languageproduct); foreach my $filename ( @bundle_files) { @@ -602,18 +601,17 @@ sub add_bundled_prereg_extensions else { # Add extensions from file:// URLs. - for my $name (ExtensionsLst::GetExtensionList("file", @installer::globals::languageproducts)) + for my $name (ExtensionsLst::GetExtensionList("file", ($installer::globals::languageproduct))) { push @bundle_files, $name; } } $installer::logger::Info->printf( - "preparing %d bundled extension%s for language%s %s:\n", + "preparing %d bundled extension%s for language %s:\n", $#bundle_files + 1, $#bundle_files!=0 ? "s" : "", - $#installer::globals::languageproducts!=0 ? "s" : "", - join(" ", @installer::globals::languageproducts)); + $installer::globals::languageproduct); foreach my $filename (@bundle_files) { $installer::logger::Info->printf(" %s\n", $filename); @@ -717,12 +715,6 @@ sub set_global_directory_hostnames $installer::globals::officedirgid = $onedir->{'gid'}; $allvariables->{'OFFICEDIRECTORYHOSTNAME'} = $installer::globals::officedirhostname; } - if ( $styles =~ /\bSUNDIRECTORY\b/ ) - { - $installer::globals::sundirhostname = $onedir->{'HostName'}; - $installer::globals::sundirgid = $onedir->{'gid'}; - $allvariables->{'SUNDIRECTORYHOSTNAME'} = $installer::globals::sundirhostname; - } } } diff --git a/solenv/bin/modules/installer/windows/component.pm b/solenv/bin/modules/installer/windows/component.pm index 2804cb5a31a1..f285197cd7cd 100644 --- a/solenv/bin/modules/installer/windows/component.pm +++ b/solenv/bin/modules/installer/windows/component.pm @@ -46,11 +46,6 @@ sub get_component_guid # 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}) ) { diff --git a/solenv/bin/modules/installer/windows/directory.pm b/solenv/bin/modules/installer/windows/directory.pm index 86be8a214d70..5c0368fb8aa4 100644 --- a/solenv/bin/modules/installer/windows/directory.pm +++ b/solenv/bin/modules/installer/windows/directory.pm @@ -269,7 +269,6 @@ sub create_unique_directorynames if ( $installer::globals::installlocationdirectoryset ) { installer::exiter::exit_program("ERROR: Directory with flag ISINSTALLLOCATION alread set: \"$installer::globals::installlocationdirectory\".", "create_unique_directorynames"); } $installer::globals::installlocationdirectory = $uniquename; $installer::globals::installlocationdirectoryset = 1; - if ( $installer::globals::installlocationdirectory =~ /oracle_/i ) { $installer::globals::sundirexists = 1; } } # setting the sundirectory @@ -358,8 +357,7 @@ 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); } + if ( $installer::globals::prepare_winpatch ) { @shortnames = values(%installer::globals::saved83dirmapping); } for ( my $i = 0; $i <= $#{$directoryref}; $i++ ) { @@ -371,11 +369,7 @@ sub create_defaultdir_directorynames # 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}) )) + if (( $installer::globals::prepare_winpatch ) && ( exists($installer::globals::saved83dirmapping{$uniquename}) )) { $shortstring = $installer::globals::saved83dirmapping{$uniquename}; } @@ -473,11 +467,6 @@ sub add_root_directories $productkey = $productkey . " " . $allvariableshashref->{'POSTVERSIONEXTENSION'}; $realproductkey = $realproductkey . " " . $allvariableshashref->{'POSTVERSIONEXTENSION'}; } - if ( $allvariableshashref->{'NOVERSIONINDIRNAME'} ) - { - $productkey = $productname; - $realproductkey = $realproductname; - } if ( $allvariableshashref->{'NOSPACEINDIRECTORYNAME'} ) { $productkey =~ s/\ /\_/g; diff --git a/solenv/bin/modules/installer/windows/file.pm b/solenv/bin/modules/installer/windows/file.pm index 00a23d12a2cd..9e0169d052b8 100644 --- a/solenv/bin/modules/installer/windows/file.pm +++ b/solenv/bin/modules/installer/windows/file.pm @@ -377,16 +377,7 @@ sub generate_unique_filename_for_filetable 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 (( $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/^(.*);/; @@ -473,13 +464,7 @@ sub generate_filename_for_filetable 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"}) )) + if (( $installer::globals::prepare_winpatch ) && ( exists($installer::globals::savedmapping{"$fileref->{'componentname'}/$filename"}) )) { $installer::globals::savedmapping{"$fileref->{'componentname'}/$filename"} =~ m/.*;(.*)/; if ($1 ne '') @@ -567,89 +552,6 @@ sub get_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"; - $installer::logger::Lang->print($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 ############################################# @@ -693,91 +595,6 @@ sub generate_registry_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"; - $installer::logger::Lang->print($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 ) - { - $installer::logger::Lang->print("\n"); - $installer::logger::Lang->print("New files compared to the update database:\n"); - } - - $installer::logger::Lang->printf("%s (%s) Sequence: %s\n", - $onefile->{'Name'}, - $onefile->{'gid'}, - $onefile->{'assignedsequencenumber'}); - } - - if ( $counter == 0 ) - { - $infoline = "Info: No new file compared with update database!\n"; - $installer::logger::Lang->print($infoline); - } - -} - ################################################################### # Collecting further conditions for the component table. # This is used by multilayer products, to enable installation @@ -844,7 +661,7 @@ sub collect_shortnames_from_old_database sub create_files_table { - my ($filesref, $allfilecomponentsref, $basedir, $allvariables, $uniquefilenamehashref, $allupdatesequenceshashref, $allupdatecomponentshashref, $allupdatefileorderhashref) = @_; + my ($filesref, $allfilecomponentsref, $basedir, $allvariables, $uniquefilenamehashref) = @_; $installer::logger::Lang->add_timestamp("Performance Info: File Table start"); @@ -869,8 +686,6 @@ sub create_files_table # 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"); @@ -911,7 +726,7 @@ sub create_files_table $installer::globals::insert_file_at_end = 0; $counter++; - $file{'Sequence'} = get_sequence_for_file($counter, $onefile, \%file, $allupdatesequenceshashref, $allupdatecomponentshashref, $allupdatefileorderhashref, \%allfilecomponents); + $file{'Sequence'} = $counter; $onefile->{'sequencenumber'} = $file{'Sequence'}; diff --git a/solenv/bin/modules/installer/windows/media.pm b/solenv/bin/modules/installer/windows/media.pm index 0dc57b39485e..42c42ae0d6dc 100644 --- a/solenv/bin/modules/installer/windows/media.pm +++ b/solenv/bin/modules/installer/windows/media.pm @@ -210,16 +210,7 @@ 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}; - } + my $sequence = $installer::globals::lastsequence{$cabfilename}; return $sequence; } @@ -233,7 +224,7 @@ sub get_last_sequence sub create_media_table { - my ($filesref, $basedir, $allvariables, $alludpatelastsequences, $allupdatediskids) = @_; + my ($filesref, $basedir, $allvariables) = @_; my @mediatable = (); @@ -243,105 +234,7 @@ sub create_media_table 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 - { - $installer::logger::Lang->printf( - "Warning: Could not find cabinet file \"%s}\" in update database. This seems to be an new cabinet file!?\n", - $media{'Cabinet'}); - } - } - } - - # 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 ) + if ( $installer::globals::fix_number_of_cab_files ) { # number of cabfiles my $maxcabfilenumber = $installer::globals::number_of_cabfiles; @@ -409,36 +302,6 @@ sub create_media_table } } } - 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"); diff --git a/solenv/bin/modules/installer/windows/mergemodule.pm b/solenv/bin/modules/installer/windows/mergemodule.pm deleted file mode 100644 index 2ed2781d9747..000000000000 --- a/solenv/bin/modules/installer/windows/mergemodule.pm +++ /dev/null @@ -1,1668 +0,0 @@ -#************************************************************** -# -# 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::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::Info->printf("... merging msm files into msi database ... \n"); - $installer::logger::Lang->print("\n"); - $installer::logger::Lang->add_timestamp("Performance 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::Lang->print("\n"); - $installer::logger::Lang->add_timestamp("Performance Info: Analyzing MergeModules, start"); - $installer::logger::Lang->print("Analyzing all Merge Modules\n"); - $installer::logger::Lang->print("\n"); - - %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"; - $installer::logger::Lang->print($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"; - $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 from merge file: $completedest !", "merge_mergemodules_into_msi_database"); - } - else - { - $infoline = "Success: Executed $systemcall successfully!\n"; - $installer::logger::Lang->print($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"; - $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 merge file: $completedest !", "merge_mergemodules_into_msi_database"); - } - else - { - $infoline = "Success: Executed $systemcall successfully!\n"; - $installer::logger::Lang->print($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"; - $installer::logger::Lang->print($infoline); - - $installer::globals::mergemodules_analyzed = 1; - $installer::logger::Lang->print("\n"); - $installer::logger::Lang->add_timestamp("Performance Info: Analyzing MergeModules, stop"); - - $infoline = "\n"; - $installer::logger::Lang->print($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::Info->printf("\t... %s ... \n", $mergemodulehash->{'name'}); - - $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::Lang->print("\n"); - $installer::logger::Lang->add_timestamp("Performance 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"; - $installer::logger::Lang->print($infoline); - - if ($returnvalue) - { - $infoline = "ERROR: Could not execute $systemcall . Returnvalue: $returnvalue!\n"; - $installer::logger::Lang->print($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"; - $installer::logger::Lang->print($infoline); - } - - $installer::logger::Lang->print("\n"); - $installer::logger::Lang->add_timestamp("Performance 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::Lang->print("\n"); - $installer::logger::Lang->add_timestamp("Performance 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"; - $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 msi database: $msifilename !", "merge_mergemodules_into_msi_database"); - } - else - { - $infoline = "Success: Executed $systemcall successfully!\n"; - $installer::logger::Lang->print($infoline); - } - - $installer::logger::Lang->print("\n"); - $installer::logger::Lang->add_timestamp("Performance 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::Lang->print("\n"); - $installer::logger::Lang->add_timestamp("Performance Info: Changing Media table"); - change_media_table($mergemodulehash, $workdir, $mergemodulegid, $allupdatelastsequences, $allupdatediskids); - $installer::logger::Lang->print("\n"); - $installer::logger::Lang->add_timestamp("Performance Info: Changing File table"); - $filesref = change_file_table($mergemodulehash, $workdir, $allupdatesequences, $includepatharrayref, $filesref, $mergemodulegid); - $installer::logger::Lang->print("\n"); - $installer::logger::Lang->add_timestamp("Performance Info: Changing FeatureComponent table"); - change_featurecomponent_table($mergemodulehash, $workdir); - $installer::logger::Lang->print("\n"); - $installer::logger::Lang->add_timestamp("Performance Info: Changing Directory table"); - change_directory_table($mergemodulehash, $workdir); - if ( $mergemodulehash->{'hasmsiassemblies'} ) - { - $installer::logger::Lang->print("\n"); - $installer::logger::Lang->add_timestamp("Performance 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::Lang->print("\n"); - $installer::logger::Lang->add_timestamp("Performance Info: Changing InstallExecuteSequence table"); - change_executesequence_table($mergemodulehash, $workdir, "InstallE.idt", "ModuleInstallExecuteSequence.idt"); - $installer::logger::Lang->print("\n"); - $installer::logger::Lang->add_timestamp("Performance Info: Changing InstallUISequence table"); - change_executesequence_table($mergemodulehash, $workdir, "InstallU.idt", "ModuleInstallExecuteSequence.idt"); - } - - if ( -f "ModuleAdminExecuteSequence.idt" ) - { - $installer::logger::Lang->print("\n"); - $installer::logger::Lang->add_timestamp("Performance Info: Changing AdminExecuteSequence table"); - change_executesequence_table($mergemodulehash, $workdir, "AdminExe.idt", "ModuleAdminExecuteSequence.idt"); - } - - if ( -f "ModuleAdvtExecuteSequence.idt" ) - { - $installer::logger::Lang->print("\n"); - $installer::logger::Lang->add_timestamp("Performance Info: Changing AdvtExecuteSequence table"); - change_executesequence_table($mergemodulehash, $workdir, "AdvtExec.idt", "ModuleAdvtExecuteSequence.idt"); - } - - $installer::logger::Lang->print("\n"); - $installer::logger::Lang->add_timestamp("Performance Info: All tables edited"); - - # Including tables into msi database - - $installer::logger::Lang->print("\n"); - $installer::logger::Lang->add_timestamp("Performance 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"; - $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: $msifilename !", "merge_mergemodules_into_msi_database"); - } - else - { - $infoline = "Success: Executed $systemcall successfully!\n"; - $installer::logger::Lang->print($infoline); - } - - $installer::logger::Lang->print("\n"); - $installer::logger::Lang->add_timestamp("Performance 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::Lang->print("\n"); - $installer::logger::Lang->add_timestamp("Performance 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"; - $installer::logger::Lang->print($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"; - $installer::logger::Lang->print($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"; - $installer::logger::Lang->print($infoline); - my $newline = $start . $newmaxsequencenumber . $final . "\n"; - ${$filecontent}[$i] = $newline; - $infoline = "Merge: Changed line in media table: ${$filecontent}[$i]\n"; - $installer::logger::Lang->print($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"; - $installer::logger::Lang->print($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"; - $installer::logger::Lang->print($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"; - $installer::logger::Lang->print($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"; - $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); - } - - 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"; - $installer::logger::Lang->print($infoline); - $infoline = "Old: $oldline\n"; - $installer::logger::Lang->print($infoline); - $infoline = "New: $newline\n"; - $installer::logger::Lang->print($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"; - $installer::logger::Lang->print($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"; - $installer::logger::Lang->print($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"; - $installer::logger::Lang->print($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"; - $installer::logger::Lang->print($infoline); - $infoline = "Old: $oldline\n"; - $installer::logger::Lang->print($infoline); - $infoline = "New: $newline\n"; - $installer::logger::Lang->print($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"; - $installer::logger::Lang->print($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"; - $installer::logger::Lang->print($infoline); - $infoline = "Old: $oldline\n"; - $installer::logger::Lang->print($infoline); - $infoline = "New: $newline\n"; - $installer::logger::Lang->print($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"; - $installer::logger::Lang->print($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 index 24342935643f..a02f20b3a982 100644 --- a/solenv/bin/modules/installer/windows/msiglobal.pm +++ b/solenv/bin/modules/installer/windows/msiglobal.pm @@ -166,193 +166,7 @@ sub generate_cab_file_list 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"; - $installer::logger::Lang->print($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"; - $installer::logger::Lang->print($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 )) + if ( $installer::globals::fix_number_of_cab_files ) { for ( my $i = 0; $i <= $#{$filesref}; $i++ ) { @@ -428,69 +242,6 @@ sub generate_cab_file_list 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"; - $installer::logger::Lang->print($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 = (); @@ -1405,28 +1156,6 @@ sub copy_windows_installer_files_into_installset } ################################################################# -# 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 ################################################################# @@ -2016,11 +1745,7 @@ sub set_global_code_variables } # ProductCode must not change, if Windows patches shall be applied - if ( $installer::globals::updatedatabase ) - { - $installer::globals::productcode = $alloldproperties->{'ProductCode'}; - } - elsif ( $installer::globals::prepare_winpatch ) + if ( $installer::globals::prepare_winpatch ) { # ProductCode has to be specified in each language my $searchstring = "PRODUCTCODE"; diff --git a/solenv/bin/modules/installer/windows/msp.pm b/solenv/bin/modules/installer/windows/msp.pm deleted file mode 100644 index 735377439fc5..000000000000 --- a/solenv/bin/modules/installer/windows/msp.pm +++ /dev/null @@ -1,1493 +0,0 @@ -#************************************************************** -# -# 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::msp; - -use File::Copy; -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); -} - -################################################################################# -# Collecting the destinations of all files with flag PATCH in a hash. -################################################################################# - -sub collect_patch_file_destinations -{ - my ( $filesarray ) = @_; - - my %patchfiledestinations = (); - my %nopatchfiledestinations = (); - my $patchcounter = 0; - my $nopatchcounter = 0; - - for ( my $i = 0; $i <= $#{$filesarray}; $i++ ) - { - my $onefile = ${$filesarray}[$i]; - my $styles = ""; - - if ( $onefile->{'Styles'} ) { $styles = $onefile->{'Styles'} }; - - if ( $styles =~ /\bPATCH\b/ ) - { - $patchfiledestinations{$onefile->{'destination'}} = 1; - $patchcounter++; - } - else - { - $nopatchfiledestinations{$onefile->{'destination'}} = 1; - $nopatchcounter++; - } - } - - return (\%patchfiledestinations, \%nopatchfiledestinations, $patchcounter, $nopatchcounter); -} - -################################################################################# -# Returning the first path segment of a path -################################################################################# - -sub get_first_path_segment -{ - my ( $path ) = @_; - - my $firstsegment = ""; - my $remainder = $path; - - if ( $path =~ /^\s*(.*?)[\/\\](.*)\s*$/ ) - { - $firstsegment = $1; - $remainder = $2; - } - - return ($firstsegment, $remainder); -} - -################################################################################# -# Finding the flexible path in the destinations, that are saved in -# the hash $nopatchfiledestinations. -################################################################################# - -sub prepare_path_in_nopatchfilehash -{ - my ($nopatchfiledestinations, $newpath) = @_; - - my $infoline = ""; - my $flexiblepath = ""; - my $found = 0; - my %checked_destinations = (); - - foreach my $onedestination ( keys %{$nopatchfiledestinations} ) - { - $flexiblepath = ""; - $found = 0; - - my $found_first_segement = 1; - my $firstsegement = ""; - my $fixedpath = $onedestination; - my $testfile = $newpath . $installer::globals::separator . $fixedpath; - - while (( ! -f $testfile ) && ( $found_first_segement )) - { - $firstsegement = ""; - ( $firstsegement, $fixedpath ) = get_first_path_segment($fixedpath); - - if ( $firstsegement ne "" ) - { - $found_first_segement = 1; - $flexiblepath = $flexiblepath . $firstsegement . $installer::globals::separator; - } - else - { - $found_first_segement = 0; - } - - $testfile = $newpath . $installer::globals::separator . $fixedpath; - } - - if ( -f $testfile ) { $found = 1; } - - if ( $found ) { last; } - } - - if ( ! $found ) { installer::exiter::exit_program("ERROR: Could not determine flexible destination path for msp patch creation!", "prepare_path_in_nopatchfilehash"); } - - $infoline = "Setting flexible path for msp creation: $flexiblepath\n"; - $installer::logger::Lang->print($infoline); - - foreach my $onedestination ( keys %{$nopatchfiledestinations} ) - { - $onedestination =~ s/^\s*\Q$flexiblepath\E//; - $checked_destinations{$onedestination} = 1; - } - - return \%checked_destinations; -} - -################################################################################# -# Synchronizing the two installed products in that way, that only -# files with flag PATCH are different. -################################################################################# - -sub synchronize_installation_sets -{ - my ($olddatabase, $newdatabase, $filesarray) = @_; - - $installer::logger::Lang->print("\n"); - $installer::logger::Lang->print("Synchronizing installed products because of PATCH flag\n"); - $infoline = "Old product: $olddatabase\n"; - $installer::logger::Lang->print($infoline); - $infoline = "New product: $newdatabase\n"; - $installer::logger::Lang->print($infoline); - - my ( $patchfiledestinations, $nopatchfiledestinations, $patchfilecounter, $nopatchfilecounter ) = collect_patch_file_destinations($filesarray); - - $infoline = "Number of files with PATCH flag: $patchfilecounter\n"; - $installer::logger::Lang->print($infoline); - - $infoline = "Number of files without PATCH flag: $nopatchfilecounter\n"; - $installer::logger::Lang->print($infoline); - - foreach my $localfile ( sort keys %{$patchfiledestinations} ) - { - $infoline = "\tPATCH file: $localfile\n"; - $installer::logger::Lang->print($infoline); - } - - my $oldpath = $olddatabase; - if ( $^O =~ /cygwin/i ) { $oldpath =~ s/\\/\//g; } - installer::pathanalyzer::get_path_from_fullqualifiedname(\$oldpath); - $oldpath =~ s/\\\s*$//; - $oldpath =~ s/\/\s*$//; - - my $newpath = $newdatabase; - if ( $^O =~ /cygwin/i ) { $newpath =~ s/\\/\//g; } - installer::pathanalyzer::get_path_from_fullqualifiedname(\$newpath); - $newpath =~ s/\\\s*$//; - $newpath =~ s/\/\s*$//; - - # The destination path is not correct. destinations in the hash contain - # the flexible installation path, that is not part in the administrative installation - $nopatchfiledestinations = prepare_path_in_nopatchfilehash($nopatchfiledestinations, $newpath); - - foreach my $onedestination ( keys %{$nopatchfiledestinations} ) - { - my $source = $oldpath . $installer::globals::separator . $onedestination; - my $dest = $newpath . $installer::globals::separator . $onedestination; - - if ( -f $source ) - { - if ( -f $dest ) - { - my $copyreturn = copy($source, $dest); - # installer::systemactions::copy_one_file($source, $dest); - # $infoline = "Synchronizing file: $source to $dest\n"; - # $installer::logger::Lang->print($infoline); - } - else - { - $infoline = "Not synchronizing. Destination file \"$dest\" does not exist.\n"; - $installer::logger::Lang->print($infoline); - } - } - else - { - $infoline = "Not synchronizing. Source file \"$source\" does not exist.\n"; - $installer::logger::Lang->print($infoline); - } - } -} - -################################################################################# -# 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"; - $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: $fullpcpfilepath !", "extract_all_tables_from_msidatabase"); - } - else - { - $infoline = "Success: Executed $systemcall successfully!\n"; - $installer::logger::Lang->print($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"; - $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 pcp file: $fullpcpfilepath !", "include_tables_into_pcpfile"); - } - else - { - $infoline = "Success: Executed $systemcall successfully!\n"; - $installer::logger::Lang->print($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::Info->printf("... %s ...\n", $systemcall); - - $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 execute $systemcall !", "execute_msimsp"); - } - else - { - $infoline = "Success: Executed $systemcall successfully!\n"; - $installer::logger::Lang->print($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"; - $installer::logger::Lang->print($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"; - $installer::logger::Lang->print($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"; - $installer::logger::Lang->print($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"; - $installer::logger::Lang->print($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"; - $installer::logger::Lang->print($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 = "Apache OpenOffice"; - 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"; - $installer::logger::Lang->print($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) = @_; - - # print this message even in 'quiet' mode - $installer::logger::Info->print("\n"); - $installer::logger::Info->print("******************************************\n"); - $installer::logger::Info->print("... creating msp installation set ...\n", 1); - $installer::logger::Info->print("******************************************\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::Lang->print("\n"); - $installer::logger::Lang->add_timestamp("Performance Info: Starting product installation"); - - # Installing both installation sets - $installer::logger::Info->printf("... installing products ...\n"); - my ($olddatabase, $newdatabase) = install_installation_sets($installationdir); - - $installer::logger::Lang->print("\n"); - $installer::logger::Lang->add_timestamp("Performance Info: Starting synchronization of installation sets"); - - # Synchronizing installed products, allowing only different files with PATCH flag - $installer::logger::Info->printf("... synchronizing installation sets ...\n"); - synchronize_installation_sets($olddatabase, $newdatabase, $filesarray); - - $installer::logger::Lang->print("\n"); - $installer::logger::Lang->add_timestamp("Performance Info: Starting pcp file creation"); - - # Create pcp file - $installer::logger::Info->printf("... 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::Lang->print("\n"); - $installer::logger::Lang->add_timestamp("Performance Info: Starting msimsp.exe"); - my $msimsplogfile = execute_msimsp($fullpcpfilename, $mspfilename, $localmspdir); - - # Copy final installation set next to msp file - $installer::logger::Lang->print("\n"); - $installer::logger::Lang->add_timestamp("Performance Info: Copying installation set"); - $installer::logger::Info->printf("... 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"; - # $installer::logger::Lang->print($infoline); - } - - # Find all files included into the patch - # Analyzing the msimsp log file $msimsplogfile - analyze_msimsp_logfile($msimsplogfile, $filesarray); - - # Done - $installer::logger::Lang->print("\n"); - $installer::logger::Lang->add_timestamp("Performance Info: msp creation done"); - - return $mspdir; -} - -1; diff --git a/solenv/bin/modules/installer/windows/property.pm b/solenv/bin/modules/installer/windows/property.pm index eddce824633b..e93ad0491950 100644 --- a/solenv/bin/modules/installer/windows/property.pm +++ b/solenv/bin/modules/installer/windows/property.pm @@ -308,12 +308,6 @@ sub set_important_properties $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"; @@ -325,12 +319,6 @@ sub set_important_properties 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"; diff --git a/solenv/bin/modules/installer/windows/registry.pm b/solenv/bin/modules/installer/windows/registry.pm index 3e26b03810d6..6469ee1b536a 100644 --- a/solenv/bin/modules/installer/windows/registry.pm +++ b/solenv/bin/modules/installer/windows/registry.pm @@ -77,17 +77,6 @@ sub get_registry_component_name 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"; } diff --git a/solenv/bin/modules/installer/windows/update.pm b/solenv/bin/modules/installer/windows/update.pm deleted file mode 100644 index 974ac6b760f8..000000000000 --- a/solenv/bin/modules/installer/windows/update.pm +++ /dev/null @@ -1,595 +0,0 @@ -#************************************************************** -# -# 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::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); - - $installer::logger::Lang->printf("Systemcall: %s\n", $systemcall); - - if ($returnvalue) - { - $installer::logger::Lang->printf("ERROR: Could not execute %s !\n", $systemcall); - installer::exiter::exit_program("ERROR: Could not exclude tables from msi database: $fulldatabasepath !", "extract_all_tables_from_msidatabase"); - } - else - { - $installer::logger::Lang->printf("Success: Executed %s successfully!\n", $systemcall); - } -} - -################################################################################# -# 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::Info->printf("... update process, using database %s ...\n", $databasename); - $installer::logger::Lang->printf("\n"); - $installer::logger::Lang->printf("Database found in %s: \"%s\"\n", $completelistname, $databasename); - $installer::logger::Lang->printf("\n"); - # Saving in global variable - $installer::globals::updatedatabasepath = $databasename; - } - else - { - $installer::logger::Lang->printf("\n"); - $installer::logger::Lang->printf("No database found in %s. This is no update process!\n", $completelistname); - $installer::logger::Lang->printf("\n"); - } - - 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::Lang->add_timestamp("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::Lang->add_timestamp("Performance Info: readdatabase: before extracting tables"); - - # extract all tables from database - extract_all_tables_from_msidatabase($fulldatabasepath, $databasedir); - - $installer::logger::Lang->add_timestamp("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::Lang->add_timestamp("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::Lang->add_timestamp("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::Lang->add_timestamp("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; diff --git a/solenv/bin/modules/installer/ziplist.pm b/solenv/bin/modules/installer/ziplist.pm index 6673c3236feb..7c66cdd6851c 100644 --- a/solenv/bin/modules/installer/ziplist.pm +++ b/solenv/bin/modules/installer/ziplist.pm @@ -30,6 +30,121 @@ use installer::logger; use installer::parameter; use installer::remover; use installer::systemactions; +use strict; + +=head2 read_openoffice_lst_file (#loggingdir) + Read the settings and variables from the settings file (typically 'openoffice.lst'). +=cut +sub read_openoffice_lst_file ($$;$) +{ + my ($filename, $product_name, $loggingdir) = @_; + + # Read all lines from the settings file. + my $ziplistref = installer::files::read_file($filename); + + # Extract the lines of the settings block for the current product. + my ($productblockref, $parent) = installer::ziplist::getproductblock( + $ziplistref, $product_name, 1); + my ($settingsblockref, undef) = installer::ziplist::getproductblock($productblockref, "Settings", 0); + $settingsblockref = installer::ziplist::analyze_settings_block($settingsblockref); + + # Split into settings and variables. + my $allsettingsarrayref = installer::ziplist::get_settings_from_ziplist($settingsblockref); + my $allvariablesarrayref = installer::ziplist::get_variables_from_ziplist($settingsblockref); + + # global product block from zip.lst + my ($globalproductblockref, undef) = installer::ziplist::getproductblock( + $ziplistref, $installer::globals::globalblock, 0); + + if ($installer::globals::globallogging && defined $loggingdir) + { + installer::files::save_file($loggingdir . "productblock.log", $productblockref); + installer::files::save_file($loggingdir . "settingsblock.log", $settingsblockref); + installer::files::save_file($loggingdir . "allsettings1.log", $allsettingsarrayref); + installer::files::save_file($loggingdir . "allvariables1.log", $allvariablesarrayref); + installer::files::save_file($loggingdir . "globalproductblock.log", $globalproductblockref); + } + + # Integrate parent values. + while (defined $parent) + { + my $parentproductblockref; + ($parentproductblockref, $parent) = installer::ziplist::getproductblock($ziplistref, $parent, 1); + my ($parentsettingsblockref, undef) = installer::ziplist::getproductblock( + $parentproductblockref, "Settings", 0); + $parentsettingsblockref = installer::ziplist::analyze_settings_block($parentsettingsblockref); + my $allparentsettingsarrayref = installer::ziplist::get_settings_from_ziplist($parentsettingsblockref); + my $allparentvariablesarrayref = installer::ziplist::get_variables_from_ziplist($parentsettingsblockref); + if (scalar @$allparentsettingsarrayref > 0) + { + $allsettingsarrayref = installer::converter::combine_arrays_from_references_first_win( + $allsettingsarrayref, + $allparentsettingsarrayref) + } + if (scalar @$allparentvariablesarrayref) + { + $allvariablesarrayref = installer::converter::combine_arrays_from_references_first_win( + $allvariablesarrayref, + $allparentvariablesarrayref) + } + } + + # Integrate global values. + if (scalar @$globalproductblockref) + { + # settings block from zip.lst + my ($globalsettingsblockref, undef) = installer::ziplist::getproductblock( + $globalproductblockref, "Settings", 0); + + # select data from settings block in zip.lst + $globalsettingsblockref = installer::ziplist::analyze_settings_block($globalsettingsblockref); + + my $allglobalsettingsarrayref = installer::ziplist::get_settings_from_ziplist($globalsettingsblockref); + my $allglobalvariablesarrayref = installer::ziplist::get_variables_from_ziplist($globalsettingsblockref); + + if ($installer::globals::globallogging && defined $loggingdir) + { + installer::files::save_file($loggingdir . "globalsettingsblock1.log", $globalsettingsblockref); + installer::files::save_file($loggingdir . "globalsettingsblock2.log", $globalsettingsblockref); + installer::files::save_file($loggingdir . "allglobalsettings1.log", $allglobalsettingsarrayref); + installer::files::save_file($loggingdir . "allglobalvariables1.log", $allglobalvariablesarrayref); + } + + if (scalar @$allglobalsettingsarrayref > 0) + { + $allsettingsarrayref = installer::converter::combine_arrays_from_references_first_win( + $allsettingsarrayref, $allglobalsettingsarrayref); + } + if (scalar @$allglobalvariablesarrayref > 0) + { + $allvariablesarrayref = installer::converter::combine_arrays_from_references_first_win( + $allvariablesarrayref, $allglobalvariablesarrayref); + } + } + + # Remove multiples (and the trailing ##%##). + $allsettingsarrayref = installer::ziplist::remove_multiples_from_ziplist($allsettingsarrayref); + $allvariablesarrayref = installer::ziplist::remove_multiples_from_ziplist($allvariablesarrayref); + installer::ziplist::replace_variables_in_ziplist_variables($allvariablesarrayref); + + # Transform array into hash. + my $allvariableshashref = installer::converter::convert_array_to_hash($allvariablesarrayref); + + # Postprocess the variables. + installer::ziplist::set_default_productversion_if_required($allvariableshashref); + installer::ziplist::add_variables_to_allvariableshashref($allvariableshashref); + installer::ziplist::overwrite_ooovendor($allvariableshashref); + + if ($installer::globals::globallogging && defined $loggingdir) + { + installer::files::save_file($loggingdir . "allsettings2.log" ,$allsettingsarrayref); + installer::files::save_file($loggingdir . "allvariables2.log" ,$allvariablesarrayref); + } + + # Eventually we should fix this so that we don't have to return the raw arrays, only the resulting hashes. + return ($allvariableshashref, $allsettingsarrayref); +} + ################################################# # Getting data from path file and zip list file @@ -571,7 +686,7 @@ sub replace_languages_in_pathes my $language = ${$languagesref}[$j]; $line =~ s/\$\(LANG\)/$language/g; push(@patharray ,$line); - $newdir = $line; + my $newdir = $line; $line = $originalline; installer::remover::remove_leading_and_ending_whitespaces(\$newline); @@ -627,8 +742,6 @@ sub list_all_files_from_include_path } $installer::logger::Lang->print("\n"); - - return \@filesarray; } ##################################################### @@ -780,9 +893,18 @@ sub add_variables_to_allvariableshashref $variableshashref->{'LCPRODUCTEXTENSION'} = ""; } - if ( $installer::globals::patch ) { $variableshashref->{'PRODUCTADDON'} = $installer::globals::patchaddon; } - elsif ( $installer::globals::languagepack ) { $variableshashref->{'PRODUCTADDON'} = $installer::globals::languagepackaddon; } - else { $variableshashref->{'PRODUCTADDON'} = ""; } + if ($installer::globals::patch) + { + $variableshashref->{'PRODUCTADDON'} = $installer::globals::patchaddon; + } + elsif ($installer::globals::languagepack) + { + $variableshashref->{'PRODUCTADDON'} = $installer::globals::languagepackaddon; + } + else + { + $variableshashref->{'PRODUCTADDON'} = ""; + } my $localbuild = $installer::globals::build; if ( $localbuild =~ /^\s*(\w+?)(\d+)\s*$/ ) { $localbuild = $2; } # using "680" instead of "src680" diff --git a/solenv/bin/modules/par2script/globals.pm b/solenv/bin/modules/par2script/globals.pm index dc52b98d03a5..ecc2976f65f3 100644 --- a/solenv/bin/modules/par2script/globals.pm +++ b/solenv/bin/modules/par2script/globals.pm @@ -39,8 +39,7 @@ BEGIN @allitems = ("Installation", "ScpAction", "Directory", "File", "Shortcut", "Unixlink", "Module", "Profile", "ProfileItem", - "Folder", "FolderItem", "RegistryItem", "WindowsCustomAction", - "MergeModule"); + "Folder", "FolderItem", "RegistryItem", "WindowsCustomAction"); @items_assigned_at_modules = ("File", "Directory", "Unixlink"); @items_with_directories = ("File", "Profile", "Shortcut", "Unixlink"); diff --git a/solenv/bin/modules/pre2par/globals.pm b/solenv/bin/modules/pre2par/globals.pm index 6eb5b3d1146e..11e4544c1321 100644 --- a/solenv/bin/modules/pre2par/globals.pm +++ b/solenv/bin/modules/pre2par/globals.pm @@ -38,8 +38,7 @@ BEGIN @allitems = ("Installation", "ScpAction", "HelpText", "Directory", "DataCarrier", "StarRegistry", "File", "Shortcut", "Custom", "Unixlink", "Procedure", "Module", "Profile", "ProfileItem", - "Folder", "FolderItem", "RegistryItem", "StarRegistryItem", "WindowsCustomAction", - "MergeModule"); + "Folder", "FolderItem", "RegistryItem", "StarRegistryItem", "WindowsCustomAction"); $logging = 0; $logfilename = "logfile.log"; # the default logfile name for global errors |