summaryrefslogtreecommitdiff
path: root/solenv/bin/modules/installer/windows
diff options
context:
space:
mode:
Diffstat (limited to 'solenv/bin/modules/installer/windows')
-rw-r--r--solenv/bin/modules/installer/windows/admin.pm916
-rw-r--r--solenv/bin/modules/installer/windows/assembly.pm371
-rw-r--r--solenv/bin/modules/installer/windows/binary.pm77
-rw-r--r--solenv/bin/modules/installer/windows/component.pm524
-rw-r--r--solenv/bin/modules/installer/windows/createfolder.pm153
-rw-r--r--solenv/bin/modules/installer/windows/directory.pm456
-rw-r--r--solenv/bin/modules/installer/windows/feature.pm445
-rw-r--r--solenv/bin/modules/installer/windows/featurecomponent.pm244
-rw-r--r--solenv/bin/modules/installer/windows/file.pm971
-rw-r--r--solenv/bin/modules/installer/windows/font.pm110
-rw-r--r--solenv/bin/modules/installer/windows/icon.pm77
-rw-r--r--solenv/bin/modules/installer/windows/idtglobal.pm2423
-rw-r--r--solenv/bin/modules/installer/windows/inifile.pm146
-rw-r--r--solenv/bin/modules/installer/windows/java.pm120
-rw-r--r--solenv/bin/modules/installer/windows/language.pm74
-rw-r--r--solenv/bin/modules/installer/windows/media.pm458
-rwxr-xr-xsolenv/bin/modules/installer/windows/mergemodule.pm1652
-rw-r--r--solenv/bin/modules/installer/windows/msiglobal.pm2265
-rw-r--r--solenv/bin/modules/installer/windows/msp.pm1293
-rw-r--r--solenv/bin/modules/installer/windows/patch.pm155
-rw-r--r--solenv/bin/modules/installer/windows/property.pm656
-rw-r--r--solenv/bin/modules/installer/windows/registry.pm397
-rw-r--r--solenv/bin/modules/installer/windows/removefile.pm152
-rw-r--r--solenv/bin/modules/installer/windows/selfreg.pm88
-rw-r--r--solenv/bin/modules/installer/windows/shortcut.pm697
-rw-r--r--solenv/bin/modules/installer/windows/sign.pm1249
-rw-r--r--solenv/bin/modules/installer/windows/strip.pm159
-rw-r--r--solenv/bin/modules/installer/windows/update.pm600
-rw-r--r--solenv/bin/modules/installer/windows/upgrade.pm167
29 files changed, 17095 insertions, 0 deletions
diff --git a/solenv/bin/modules/installer/windows/admin.pm b/solenv/bin/modules/installer/windows/admin.pm
new file mode 100644
index 000000000000..47a71b059b2a
--- /dev/null
+++ b/solenv/bin/modules/installer/windows/admin.pm
@@ -0,0 +1,916 @@
+#*************************************************************************
+#
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+#
+# Copyright 2000, 2010 Oracle and/or its affiliates.
+#
+# OpenOffice.org - a multi-platform office productivity suite
+#
+# This file is part of OpenOffice.org.
+#
+# OpenOffice.org is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License version 3
+# only, as published by the Free Software Foundation.
+#
+# OpenOffice.org is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License version 3 for more details
+# (a copy is included in the LICENSE file that accompanied this code).
+#
+# You should have received a copy of the GNU Lesser General Public License
+# version 3 along with OpenOffice.org. If not, see
+# <http://www.openoffice.org/license.html>
+# for a copy of the LGPLv3 License.
+#
+#*************************************************************************
+
+package installer::windows::admin;
+
+use File::Copy;
+use installer::exiter;
+use installer::files;
+use installer::globals;
+use installer::pathanalyzer;
+use installer::systemactions;
+use installer::worker;
+use installer::windows::idtglobal;
+
+#################################################################################
+# Unpacking cabinet files with expand
+#################################################################################
+
+sub unpack_cabinet_file
+{
+ my ($cabfilename, $unpackdir) = @_;
+
+ my $infoline = "Unpacking cabinet file: $cabfilename\n";
+ push( @installer::globals::logfileinfo, $infoline);
+
+ my $expandfile = "expand.exe"; # Has to be in the path
+
+ # expand.exe has to be located in the system directory.
+ # Cygwin has another tool expand.exe, that converts tabs to spaces. This cannot be used of course.
+ # But this wrong expand.exe is typically in the PATH before this expand.exe, to unpack
+ # cabinet files.
+
+# if ( $^O =~ /cygwin/i )
+# {
+# $expandfile = $ENV{'SYSTEMROOT'} . "/system32/expand.exe"; # Has to be located in the systemdirectory
+# $expandfile =~ s/\\/\//;
+# if ( ! -f $expandfile ) { exit_program("ERROR: Did not find file $expandfile in the Windows system folder!"); }
+# }
+
+ if ( $^O =~ /cygwin/i )
+ {
+ $expandfile = qx(cygpath -u "$ENV{WINDIR}"/System32/expand.exe);
+ chomp $expandfile;
+ }
+
+ my $expandlogfile = $unpackdir . $installer::globals::separator . "expand.log";
+
+ # exclude cabinet file
+ # my $systemcall = $cabarc . " -o X " . $mergemodulehash->{'cabinetfile'};
+
+ my $systemcall = "";
+ if ( $^O =~ /cygwin/i ) {
+ my $localunpackdir = qx{cygpath -w "$unpackdir"};
+ chomp ($localunpackdir);
+ $localunpackdir =~ s/\\/\\\\/g;
+ $cabfilename =~ s/\\/\\\\/g;
+ $cabfilename =~ s/\s*$//g;
+ $systemcall = $expandfile . " " . $cabfilename . " -F:\* " . $localunpackdir . " \> " . $expandlogfile;
+ }
+ else
+ {
+ $systemcall = $expandfile . " " . $cabfilename . " -F:\* " . $unpackdir . " \> " . $expandlogfile;
+ }
+
+ my $returnvalue = system($systemcall);
+ $infoline = "Systemcall: $systemcall\n";
+ push( @installer::globals::logfileinfo, $infoline);
+
+ if ($returnvalue)
+ {
+ $infoline = "ERROR: Could not execute $systemcall !\n";
+ push( @installer::globals::logfileinfo, $infoline);
+ installer::exiter::exit_program("ERROR: Could not extract cabinet file: $mergemodulehash->{'cabinetfile'} !", "change_file_table");
+ }
+ else
+ {
+ $infoline = "Success: Executed $systemcall successfully!\n";
+ push( @installer::globals::logfileinfo, $infoline);
+ }
+}
+
+#################################################################################
+# Include tables into a msi database
+#################################################################################
+
+sub include_tables_into_pcpfile
+{
+ my ($fullmsidatabasepath, $workdir, $tables) = @_;
+
+ my $msidb = "msidb.exe"; # Has to be in the path
+ my $infoline = "";
+ my $systemcall = "";
+ my $returnvalue = "";
+
+ # Make all table 8+3 conform
+ my $alltables = installer::converter::convert_stringlist_into_array(\$tables, " ");
+
+ for ( my $i = 0; $i <= $#{$alltables}; $i++ )
+ {
+ my $tablename = ${$alltables}[$i];
+ $tablename =~ s/\s*$//;
+ my $namelength = length($tablename);
+ if ( $namelength > 8 )
+ {
+ my $newtablename = substr($tablename, 0, 8); # name, offset, length
+ my $oldfile = $workdir . $installer::globals::separator . $tablename . ".idt";
+ my $newfile = $workdir . $installer::globals::separator . $newtablename . ".idt";
+ if ( -f $newfile ) { unlink $newfile; }
+ installer::systemactions::copy_one_file($oldfile, $newfile);
+ my $savfile = $oldfile . ".orig";
+ installer::systemactions::copy_one_file($oldfile, $savfile);
+ }
+ }
+
+ # Import of tables
+
+ $systemcall = $msidb . " -d " . $fullmsidatabasepath . " -f " . $workdir . " -i " . $tables;
+
+ $returnvalue = system($systemcall);
+
+ $infoline = "Systemcall: $systemcall\n";
+ push( @installer::globals::logfileinfo, $infoline);
+
+ if ($returnvalue)
+ {
+ $infoline = "ERROR: Could not execute $systemcall !\n";
+ push( @installer::globals::logfileinfo, $infoline);
+ installer::exiter::exit_program("ERROR: Could not include tables into msi database: $fullmsidatabasepath !", "include_tables_into_pcpfile");
+ }
+ else
+ {
+ $infoline = "Success: Executed $systemcall successfully!\n";
+ push( @installer::globals::logfileinfo, $infoline);
+ }
+}
+
+#################################################################################
+# Extracting tables from msi database
+#################################################################################
+
+sub extract_tables_from_pcpfile
+{
+ my ($fullmsidatabasepath, $workdir, $tablelist) = @_;
+
+ my $msidb = "msidb.exe"; # Has to be in the path
+ my $infoline = "";
+ my $systemcall = "";
+ my $returnvalue = "";
+
+ my $localfullmsidatabasepath = $fullmsidatabasepath;
+
+ # Export of all tables by using "*"
+
+ if ( $^O =~ /cygwin/i ) {
+ # Copying the msi database locally guarantees the format of the directory.
+ # Otherwise it is defined in the file of UPDATE_DATABASE_LISTNAME
+
+ my $msifilename = $localfullmsidatabasepath;
+ installer::pathanalyzer::make_absolute_filename_to_relative_filename(\$msifilename);
+ my $destdatabasename = $workdir . $installer::globals::separator . $msifilename;
+ installer::systemactions::copy_one_file($localfullmsidatabasepath, $destdatabasename);
+ $localfullmsidatabasepath = $destdatabasename;
+
+ chomp( $localfullmsidatabasepath = qx{cygpath -w "$localfullmsidatabasepath"} );
+ chomp( $workdir = qx{cygpath -w "$workdir"} );
+
+ # msidb.exe really wants backslashes. (And double escaping because system() expands the string.)
+ $localfullmsidatabasepath =~ s/\\/\\\\/g;
+ $workdir =~ s/\\/\\\\/g;
+
+ # and if there are still slashes, they also need to be double backslash
+ $localfullmsidatabasepath =~ s/\//\\\\/g;
+ $workdir =~ s/\//\\\\/g;
+ }
+
+ $systemcall = $msidb . " -d " . $localfullmsidatabasepath . " -f " . $workdir . " -e $tablelist";
+ $returnvalue = system($systemcall);
+
+ $infoline = "Systemcall: $systemcall\n";
+ push( @installer::globals::logfileinfo, $infoline);
+
+ if ($returnvalue)
+ {
+ $infoline = "ERROR: Could not execute $systemcall !\n";
+ push( @installer::globals::logfileinfo, $infoline);
+ installer::exiter::exit_program("ERROR: Could not exclude tables from pcp file: $localfullmsidatabasepath !", "extract_tables_from_pcpfile");
+ }
+ else
+ {
+ $infoline = "Success: Executed $systemcall successfully!\n";
+ push( @installer::globals::logfileinfo, $infoline);
+ }
+}
+
+################################################################################
+# Analyzing the content of Directory.idt
+#################################################################################
+
+sub analyze_directory_file
+{
+ my ($filecontent) = @_;
+
+ my %table = ();
+
+ for ( my $i = 0; $i <= $#{$filecontent}; $i++ )
+ {
+ if (( $i == 0 ) || ( $i == 1 ) || ( $i == 2 )) { next; }
+
+ if ( ${$filecontent}[$i] =~ /^\s*(.*?)\t(.*?)\t(.*?)\s*$/ )
+ {
+ my $dir = $1;
+ my $parent = $2;
+ my $name = $3;
+
+ if ( $name =~ /^\s*(.*?)\s*\:\s*(.*?)\s*$/ ) { $name = $2; }
+ if ( $name =~ /^\s*(.*?)\s*\|\s*(.*?)\s*$/ ) { $name = $2; }
+
+ my %helphash = ();
+ $helphash{'Directory_Parent'} = $parent;
+ $helphash{'DefaultDir'} = $name;
+ $table{$dir} = \%helphash;
+ }
+ }
+
+ return \%table;
+}
+
+#################################################################################
+# Analyzing the content of Component.idt
+#################################################################################
+
+sub analyze_component_file
+{
+ my ($filecontent) = @_;
+
+ my %table = ();
+
+ for ( my $i = 0; $i <= $#{$filecontent}; $i++ )
+ {
+ if (( $i == 0 ) || ( $i == 1 ) || ( $i == 2 )) { next; }
+
+ if ( ${$filecontent}[$i] =~ /^\s*(.*?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\s*$/ )
+ {
+ my $component = $1;
+ my $dir = $3;
+
+ $table{$component} = $dir;
+ }
+ }
+
+ return \%table;
+}
+
+#################################################################################
+# Analyzing the full content of Component.idt
+#################################################################################
+
+sub analyze_keypath_component_file
+{
+ my ($filecontent) = @_;
+
+ my %keypathtable = ();
+
+ for ( my $i = 0; $i <= $#{$filecontent}; $i++ )
+ {
+ if (( $i == 0 ) || ( $i == 1 ) || ( $i == 2 )) { next; }
+
+ if ( ${$filecontent}[$i] =~ /^\s*(.*?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\s*$/ )
+ {
+ my $component = $1;
+ my $keypath = $6;
+
+ $keypathtable{$keypath} = $component;
+ }
+ }
+
+ return (\%keypathtable);
+
+}
+
+#################################################################################
+# Analyzing the content of Registry.idt
+#################################################################################
+
+sub analyze_registry_file
+{
+ my ($filecontent) = @_;
+
+ my %table = ();
+
+ for ( my $i = 0; $i <= $#{$filecontent}; $i++ )
+ {
+ if (( $i == 0 ) || ( $i == 1 ) || ( $i == 2 )) { next; }
+
+ if ( ${$filecontent}[$i] =~ /^\s*(.*?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\s*$/ )
+ {
+ my $registry = $1;
+ my $root = $2;
+ my $key = $3;
+ my $name = $4;
+ my $value = $5;
+ my $component = $6;
+
+ my %helphash = ();
+ # $helphash{'Registry'} = $registry;
+ $helphash{'Root'} = $root;
+ $helphash{'Key'} = $key;
+ $helphash{'Name'} = $name;
+ $helphash{'Value'} = $value;
+ $helphash{'Component'} = $component;
+
+ $table{$registry} = \%helphash;
+ }
+ }
+
+ return \%table;
+}
+
+#################################################################################
+# Analyzing the content of File.idt
+#################################################################################
+
+sub analyze_file_file
+{
+ my ($filecontent) = @_;
+
+ my %table = ();
+ my %fileorder = ();
+ my $maxsequence = 0;
+
+ for ( my $i = 0; $i <= $#{$filecontent}; $i++ )
+ {
+ if (( $i == 0 ) || ( $i == 1 ) || ( $i == 2 )) { next; }
+
+ if ( ${$filecontent}[$i] =~ /^\s*(.*?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\s*$/ )
+ {
+ my $file = $1;
+ my $comp = $2;
+ my $filename = $3;
+ my $sequence = $8;
+
+ if ( $filename =~ /^\s*(.*?)\s*\|\s*(.*?)\s*$/ ) { $filename = $2; }
+
+ my %helphash = ();
+ $helphash{'Component'} = $comp;
+ $helphash{'FileName'} = $filename;
+ $helphash{'Sequence'} = $sequence;
+
+ $table{$file} = \%helphash;
+
+ $fileorder{$sequence} = $file;
+
+ if ( $sequence > $maxsequence ) { $maxsequence = $sequence; }
+ }
+ }
+
+ return (\%table, \%fileorder, $maxsequence);
+}
+
+####################################################################################
+# Recursively creating the directory tree
+####################################################################################
+
+sub create_directory_tree
+{
+ my ($parent, $pathcollector, $fulldir, $dirhash) = @_;
+
+ foreach my $dir ( keys %{$dirhash} )
+ {
+ if (( $dirhash->{$dir}->{'Directory_Parent'} eq $parent ) && ( $dirhash->{$dir}->{'DefaultDir'} ne "." ))
+ {
+ my $dirname = $dirhash->{$dir}->{'DefaultDir'};
+ # Create the directory
+ my $newdir = $fulldir . $installer::globals::separator . $dirname;
+ if ( ! -f $newdir ) { mkdir $newdir; }
+ # Saving in collector
+ $pathcollector->{$dir} = $newdir;
+ # Iteration
+ create_directory_tree($dir, $pathcollector, $newdir, $dirhash);
+ }
+ }
+}
+
+####################################################################################
+# Creating the directory tree
+####################################################################################
+
+sub create_directory_structure
+{
+ my ($dirhash, $targetdir) = @_;
+
+ my %fullpathhash = ();
+
+ my @startparents = ("TARGETDIR", "INSTALLLOCATION");
+
+ foreach $dir (@startparents) { create_directory_tree($dir, \%fullpathhash, $targetdir, $dirhash); }
+
+ # Also adding the pathes of the startparents
+ foreach $dir (@startparents)
+ {
+ if ( ! exists($fullpathhash{$dir}) ) { $fullpathhash{$dir} = $targetdir; }
+ }
+
+ return \%fullpathhash;
+}
+
+####################################################################################
+# Copying files into installation set
+####################################################################################
+
+sub copy_files_into_directory_structure
+{
+ my ($fileorder, $filehash, $componenthash, $fullpathhash, $maxsequence, $unpackdir, $installdir, $dirhash) = @_;
+
+ my $unopkgfile = "";
+
+ for ( my $i = 1; $i <= $maxsequence; $i++ )
+ {
+ if ( exists($fileorder->{$i}) )
+ {
+ my $file = $fileorder->{$i};
+ if ( ! exists($filehash->{$file}->{'Component'}) ) { installer::exiter::exit_program("ERROR: Did not find component for file: \"$file\".", "copy_files_into_directory_structure"); }
+ my $component = $filehash->{$file}->{'Component'};
+ if ( ! exists($componenthash->{$component}) ) { installer::exiter::exit_program("ERROR: Did not find directory for component: \"$component\".", "copy_files_into_directory_structure"); }
+ my $dirname = $componenthash->{$component};
+ if ( ! exists($fullpathhash->{$dirname}) ) { installer::exiter::exit_program("ERROR: Did not find full directory path for dir: \"$dirname\".", "copy_files_into_directory_structure"); }
+ my $destdir = $fullpathhash->{$dirname};
+ if ( ! exists($filehash->{$file}->{'FileName'}) ) { installer::exiter::exit_program("ERROR: Did not find \"FileName\" for file: \"$file\".", "copy_files_into_directory_structure"); }
+ my $destfile = $filehash->{$file}->{'FileName'};
+
+ $destfile = $destdir . $installer::globals::separator . $destfile;
+ my $sourcefile = $unpackdir . $installer::globals::separator . $file;
+
+ if ( ! -f $sourcefile )
+ {
+ # It is possible, that this was an unpacked file
+ # Looking in the dirhash, to find the subdirectory in the installation set (the id is $dirname)
+ # subdir is not recursively analyzed, only one directory.
+
+ my $oldsourcefile = $sourcefile;
+ my $subdir = "";
+ if ( exists($dirhash->{$dirname}->{'DefaultDir'}) ) { $subdir = $dirhash->{$dirname}->{'DefaultDir'} . $installer::globals::separator; }
+ my $realfilename = $filehash->{$file}->{'FileName'};
+ my $localinstalldir = $installdir;
+
+ $localinstalldir =~ s/\\\s*$//;
+ $localinstalldir =~ s/\/\s*$//;
+
+ $sourcefile = $localinstalldir . $installer::globals::separator . $subdir . $realfilename;
+
+ if ( ! -f $sourcefile )
+ {
+ installer::exiter::exit_program("ERROR: File not found: \"$oldsourcefile\" (or \"$sourcefile\").", "copy_files_into_directory_structure");
+ }
+ }
+
+ my $copyreturn = copy($sourcefile, $destfile);
+
+ if ( ! $copyreturn) # only logging problems
+ {
+ my $infoline = "ERROR: Could not copy $sourcefile to $destfile (insufficient disc space for $destfile ?)\n";
+ $returnvalue = 0;
+ push(@installer::globals::logfileinfo, $infoline);
+ installer::exiter::exit_program($infoline, "copy_files_into_directory_structure");
+ }
+
+ if ( $destfile =~ /unopkg\.exe\s*$/ ) { $unopkgfile = $destfile; }
+
+ # installer::systemactions::copy_one_file($sourcefile, $destfile);
+ }
+ # else # allowing missing sequence numbers ?
+ # {
+ # installer::exiter::exit_program("ERROR: No file assigned to sequence $i", "copy_files_into_directory_structure");
+ # }
+ }
+
+ return $unopkgfile;
+}
+
+
+###############################################################
+# Setting the time string for the
+# Summary Information stream in the
+# msi database of the admin installations.
+###############################################################
+
+sub get_sis_time_string
+{
+ # Syntax: <yyyy/mm/dd hh:mm:ss>
+ my $second = (localtime())[0];
+ my $minute = (localtime())[1];
+ my $hour = (localtime())[2];
+ my $day = (localtime())[3];
+ my $month = (localtime())[4];
+ my $year = 1900 + (localtime())[5];
+
+ $month++; # zero based month
+
+ if ( $second < 10 ) { $second = "0" . $second; }
+ if ( $minute < 10 ) { $minute = "0" . $minute; }
+ if ( $hour < 10 ) { $hour = "0" . $hour; }
+ if ( $day < 10 ) { $day = "0" . $day; }
+ if ( $month < 10 ) { $month = "0" . $month; }
+
+ my $timestring = $year . "/" . $month . "/" . $day . " " . $hour . ":" . $minute . ":" . $second;
+
+ return $timestring;
+}
+
+###############################################################
+# Windows registry entries containing properties are not set
+# correctly during msp patch process. The properties are
+# empty or do get their default values. This destroys the
+# values of many entries in Windows registry.
+# This can be fixed by removing all entries in Registry table,
+# containing a property before starting msimsp.exe.
+###############################################################
+
+sub remove_properties_from_registry_table
+{
+ my ($registryhash, $componentkeypathhash, $registryfilecontent) = @_;
+
+ installer::logger::include_timestamp_into_logfile("\nPerformance Info: Start remove_properties_from_registry_table");
+
+ my @registrytable = ();
+
+ # Registry hash
+ # Collecting all RegistryItems with values containing a property: [...]
+ # To which component do they belong
+ # Is this after removal an empty component? Create a replacement, so that
+ # no Component has to be removed.
+ # Is this RegistryItem a KeyPath of a component. Then it cannot be removed.
+
+ my %problemitems = ();
+ my %problemcomponents = ();
+ my %securecomponents = ();
+ my $changevalue = "";
+ my $changeroot = "";
+ my $infoline = "";
+
+ my $newitemcounter = 0;
+ my $olditemcounter = 0;
+
+ foreach my $regitem ( keys %{$registryhash} )
+ {
+ my $value = "";
+ if ( exists($registryhash->{$regitem}->{'Value'}) ) { $value = $registryhash->{$regitem}->{'Value'}; }
+
+ if ( $value =~ /^.*(\[.*?\]).*$/ )
+ {
+ my $property = $1;
+
+ # Collecting registry item
+ $problemitems{$regitem} = 1; # "1" -> can be removed
+ if ( exists($componentkeypathhash->{$regitem}) ) { $problemitems{$regitem} = 2; } # "2" -> cannot be removed, KeyPath
+
+ # Collecting component (and number of problematic registry items
+ # my $component = $registryhash->{$regitem}->{'Component'};
+ # if ( exists($problemcomponents{$regitem}) ) { $problemcomponents{$regitem} = $problemcomponents{$regitem} + 1; }
+ # else { $problemcomponents{$regitem} = 1; }
+ }
+ else
+ {
+ # Collecting all components with secure regisry items
+ my $component = "";
+ if ( exists($registryhash->{$regitem}->{'Component'}) ) { $component = $registryhash->{$regitem}->{'Component'}; }
+ if ( $component eq "" ) { installer::exiter::exit_program("ERROR: Did not find component for registry item \"$regitem\".", "remove_properties_from_registry_table"); }
+ $securecomponents{$component} = 1;
+ }
+
+ # Searching for change value
+ my $localkey = "";
+ if ( exists($registryhash->{$regitem}->{'Key'}) ) { $localkey = $registryhash->{$regitem}->{'Key'}; }
+ if (( $localkey =~ /^\s*(Software\\.*\\)StartMenu\s*$/ ) && ( $changevalue eq "" ))
+ {
+ $changevalue = $1;
+ $changeroot = $registryhash->{$regitem}->{'Root'};
+ }
+
+ $olditemcounter++;
+ }
+
+ my $removecounter = 0;
+ my $renamecounter = 0;
+
+ foreach my $regitem ( keys %{$registryhash} )
+ {
+ my $value = "";
+ if ( exists($registryhash->{$regitem}->{'Value'}) ) { $value = $registryhash->{$regitem}->{'Value'}; }
+
+ if ( $value =~ /^.*(\[.*?\]).*$/ )
+ {
+ # Removing registry items, that are no KeyPath and that belong to components,
+ # that have other secure registry items.
+
+ my $component = "";
+ if ( exists($registryhash->{$regitem}->{'Component'}) ) { $component = $registryhash->{$regitem}->{'Component'}; }
+ if ( $component eq "" ) { installer::exiter::exit_program("ERROR: Did not find component for registry item (2) \"$regitem\".", "remove_properties_from_registry_table"); }
+
+ if (( $problemitems{$regitem} == 1 ) && ( exists($securecomponents{$component}) ))
+ {
+ # remove complete registry item
+ delete($registryhash->{$regitem});
+ $removecounter++;
+ $infoline = "Removing registry item: $regitem : $value\n";
+ push( @installer::globals::logfileinfo, $infoline);
+ }
+ else
+ {
+ # Changing values of registry items, that are KeyPath or that contain to
+ # components with only unsecure registry items.
+
+ if (( $problemitems{$regitem} == 2 ) || ( ! exists($securecomponents{$component}) ))
+ {
+ # change value of registry item
+ if ( $changevalue eq "" ) { installer::exiter::exit_program("ERROR: Did not find good change value for registry items", "remove_properties_from_registry_table"); }
+
+ my $oldkey = "";
+ if ( exists($registryhash->{$regitem}->{'Key'}) ) { $oldkey = $registryhash->{$regitem}->{'Key'}; };
+ my $oldname = "";
+ if ( exists($registryhash->{$regitem}->{'Name'}) ) { $oldname = $registryhash->{$regitem}->{'Name'}; }
+ my $oldvalue = "";
+ if ( exists($registryhash->{$regitem}->{'Value'}) ) { $oldvalue = $registryhash->{$regitem}->{'Value'}; }
+
+ $registryhash->{$regitem}->{'Key'} = $changevalue . "RegistryItem";
+ $registryhash->{$regitem}->{'Root'} = $changeroot;
+ $registryhash->{$regitem}->{'Name'} = $regitem;
+ $registryhash->{$regitem}->{'Value'} = 1;
+ $renamecounter++;
+
+ $infoline = "Changing registry item: $regitem\n";
+ $infoline = "Old: $oldkey : $oldname : $oldvalue\n";
+ $infoline = "New: $registryhash->{$regitem}->{'Key'} : $registryhash->{$regitem}->{'Name'} : $registryhash->{$regitem}->{'Value'}\n";
+ push( @installer::globals::logfileinfo, $infoline);
+ }
+ }
+ }
+ }
+
+ $infoline = "Number of removed registry items: $removecounter\n";
+ push( @installer::globals::logfileinfo, $infoline);
+ $infoline = "Number of changed registry items: $renamecounter\n";
+ push( @installer::globals::logfileinfo, $infoline);
+
+ # Creating the new content of Registry table
+ # First three lines from $registryfilecontent
+ # All further files from changed $registryhash
+
+ for ( my $i = 0; $i <= 2; $i++ ) { push(@registrytable, ${$registryfilecontent}[$i]); }
+
+ foreach my $regitem ( keys %{$registryhash} )
+ {
+ my $root = "";
+ if ( exists($registryhash->{$regitem}->{'Root'}) ) { $root = $registryhash->{$regitem}->{'Root'}; }
+ else { installer::exiter::exit_program("ERROR: Did not find root in registry table for item: \"$regitem\".", "remove_properties_from_registry_table"); }
+ my $localkey = "";
+ if ( exists($registryhash->{$regitem}->{'Key'}) ) { $localkey = $registryhash->{$regitem}->{'Key'}; }
+ my $name = "";
+ if ( exists($registryhash->{$regitem}->{'Name'}) ) { $name = $registryhash->{$regitem}->{'Name'}; }
+ my $value = "";
+ if ( exists($registryhash->{$regitem}->{'Value'}) ) { $value = $registryhash->{$regitem}->{'Value'}; }
+ my $comp = "";
+ if ( exists($registryhash->{$regitem}->{'Component'}) ) { $comp = $registryhash->{$regitem}->{'Component'}; }
+
+ my $oneline = $regitem . "\t" . $root . "\t" . $localkey . "\t" . $name . "\t" . $value . "\t" . $comp . "\n";
+ push(@registrytable, $oneline);
+
+ $newitemcounter++;
+ }
+
+ $infoline = "Number of registry items: $newitemcounter. Old value: $olditemcounter.\n";
+ push( @installer::globals::logfileinfo, $infoline);
+
+ installer::logger::include_timestamp_into_logfile("\nPerformance Info: End remove_properties_from_registry_table");
+
+ return (\@registrytable);
+}
+
+###############################################################
+# Writing content of administrative installations into
+# Summary Information Stream of msi database.
+# This is required for example for following
+# patch processes using Windows Installer service.
+###############################################################
+
+sub write_sis_info
+{
+ my ($msidatabase) = @_ ;
+
+ if ( ! -f $msidatabase ) { installer::exiter::exit_program("ERROR: Cannot find file $msidatabase", "write_sis_info"); }
+
+ my $msiinfo = "msiinfo.exe"; # Has to be in the path
+ my $infoline = "";
+ my $systemcall = "";
+ my $returnvalue = "";
+
+ # Required setting for administrative installations:
+ # -w 4 (source files are unpacked), wordcount
+ # -s <date of admin installation>, LastPrinted, Syntax: <yyyy/mm/dd hh:mm:ss>
+ # -l <person_making_admin_installation>, LastSavedBy
+
+ my $wordcount = 4; # Unpacked files
+ my $lastprinted = get_sis_time_string();
+ my $lastsavedby = "Installer";
+
+ my $localmsidatabase = $msidatabase;
+
+ if( $^O =~ /cygwin/i )
+ {
+ $localmsidatabase = qx{cygpath -w "$localmsidatabase"};
+ $localmsidatabase =~ s/\\/\\\\/g;
+ $localmsidatabase =~ s/\s*$//g;
+ }
+
+ $systemcall = $msiinfo . " " . "\"" . $localmsidatabase . "\"" . " -w " . $wordcount . " -s " . "\"" . $lastprinted . "\"" . " -l $lastsavedby";
+ push(@installer::globals::logfileinfo, $systemcall);
+ $returnvalue = system($systemcall);
+
+ if ($returnvalue)
+ {
+ $infoline = "ERROR: Could not execute $systemcall !\n";
+ push(@installer::globals::logfileinfo, $infoline);
+ installer::exiter::exit_program($infoline, "write_sis_info");
+ }
+}
+
+####################################################
+# Detecting the directory with extensions
+####################################################
+
+sub get_extensions_dir
+{
+ my ( $unopkgfile ) = @_;
+
+ my $localbranddir = $unopkgfile;
+ installer::pathanalyzer::get_path_from_fullqualifiedname(\$localbranddir); # "program" dir in brand layer
+ installer::pathanalyzer::get_path_from_fullqualifiedname(\$localbranddir); # root dir in brand layer
+ $localbranddir =~ s/\Q$installer::globals::separator\E\s*$//;
+ my $extensiondir = $localbranddir . $installer::globals::separator . "share" . $installer::globals::separator . "extensions";
+
+ return $extensiondir;
+}
+
+##############################################################
+# Removing all empty directories below a specified directory
+##############################################################
+
+sub remove_empty_dirs_in_folder
+{
+ my ( $dir, $firstrun ) = @_;
+
+ if ( $firstrun )
+ {
+ print "Removing superfluous directories\n";
+ }
+
+ my @content = ();
+
+ $dir =~ s/\Q$installer::globals::separator\E\s*$//;
+
+ if ( -d $dir )
+ {
+ opendir(DIR, $dir);
+ @content = readdir(DIR);
+ closedir(DIR);
+
+ my $oneitem;
+
+ foreach $oneitem (@content)
+ {
+ if ((!($oneitem eq ".")) && (!($oneitem eq "..")))
+ {
+ my $item = $dir . $installer::globals::separator . $oneitem;
+
+ if ( -d $item ) # recursive
+ {
+ remove_empty_dirs_in_folder($item, 0);
+ }
+ }
+ }
+
+ # try to remove empty directory
+ my $returnvalue = rmdir $dir;
+
+ # if ( $returnvalue ) { print "Successfully removed empty dir $dir\n"; }
+ }
+}
+
+####################################################################################
+# Simulating an administrative installation
+####################################################################################
+
+sub make_admin_install
+{
+ my ($databasepath, $targetdir) = @_;
+
+ # Create helper directory
+
+ installer::logger::print_message( "... installing $databasepath in directory $targetdir ...\n" );
+
+ my $helperdir = $targetdir . $installer::globals::separator . "installhelper";
+ installer::systemactions::create_directory($helperdir);
+
+ # Get File.idt, Component.idt and Directory.idt from database
+
+ my $tablelist = "File Directory Component Registry";
+ extract_tables_from_pcpfile($databasepath, $helperdir, $tablelist);
+
+ # Unpack all cab files into $helperdir, cab files must be located next to msi database
+ my $installdir = $databasepath;
+
+ if ( $^O =~ /cygwin/i ) { $installdir =~ s/\\/\//g; } # backslash to slash
+
+ installer::pathanalyzer::get_path_from_fullqualifiedname(\$installdir);
+
+ if ( $^O =~ /cygwin/i ) { $installdir =~ s/\//\\/g; } # slash to backslash
+
+ my $databasefilename = $databasepath;
+ installer::pathanalyzer::make_absolute_filename_to_relative_filename(\$databasefilename);
+
+ my $cabfiles = installer::systemactions::find_file_with_file_extension("cab", $installdir);
+
+ if ( $#{$cabfiles} < 0 ) { installer::exiter::exit_program("ERROR: Did not find any cab file in directory $installdir", "make_admin_install"); }
+
+ # Set unpackdir
+ my $unpackdir = $helperdir . $installer::globals::separator . "unpack";
+ installer::systemactions::create_directory($unpackdir);
+
+ for ( my $i = 0; $i <= $#{$cabfiles}; $i++ )
+ {
+ my $cabfile = "";
+ if ( $^O =~ /cygwin/i )
+ {
+ $cabfile = $installdir . ${$cabfiles}[$i];
+ }
+ else
+ {
+ $cabfile = $installdir . $installer::globals::separator . ${$cabfiles}[$i];
+ }
+ unpack_cabinet_file($cabfile, $unpackdir);
+ }
+
+ # Reading tables
+ my $filename = $helperdir . $installer::globals::separator . "Directory.idt";
+ my $filecontent = installer::files::read_file($filename);
+ my $dirhash = analyze_directory_file($filecontent);
+
+ $filename = $helperdir . $installer::globals::separator . "Component.idt";
+ my $componentfilecontent = installer::files::read_file($filename);
+ my $componenthash = analyze_component_file($componentfilecontent);
+
+ $filename = $helperdir . $installer::globals::separator . "File.idt";
+ $filecontent = installer::files::read_file($filename);
+ my ( $filehash, $fileorder, $maxsequence ) = analyze_file_file($filecontent);
+
+ # Creating the directory structure
+ my $fullpathhash = create_directory_structure($dirhash, $targetdir);
+
+ # Copying files
+ my $unopkgfile = copy_files_into_directory_structure($fileorder, $filehash, $componenthash, $fullpathhash, $maxsequence, $unpackdir, $installdir, $dirhash);
+
+ my $msidatabase = $targetdir . $installer::globals::separator . $databasefilename;
+ installer::systemactions::copy_one_file($databasepath, $msidatabase);
+
+ if ( $unopkgfile ne "" )
+ {
+ # Removing empty dirs in extension folder
+ my $extensionfolder = get_extensions_dir($unopkgfile);
+ if ( -d $extensionfolder ) { remove_empty_dirs_in_folder($extensionfolder, 1); }
+ }
+
+ # Editing registry table because of wrong Property value
+ # my $registryfilename = $helperdir . $installer::globals::separator . "Registry.idt";
+ # my $componentfilename = $helperdir . $installer::globals::separator . "Component.idt";
+ # my $componentkeypathhash = analyze_keypath_component_file($componentfilecontent);
+
+ # my $registryfilecontent = installer::files::read_file($registryfilename);
+ # my $registryhash = analyze_registry_file($registryfilecontent);
+
+ # $registryfilecontent = remove_properties_from_registry_table($registryhash, $componentkeypathhash, $registryfilecontent);
+
+ # installer::files::save_file($registryfilename, $registryfilecontent);
+ # $tablelist = "Registry";
+ # include_tables_into_pcpfile($msidatabase, $helperdir, $tablelist);
+
+ # Saving info in Summary Information Stream of msi database (required for following patches)
+ write_sis_info($msidatabase);
+
+ return $msidatabase;
+}
+
+1;
diff --git a/solenv/bin/modules/installer/windows/assembly.pm b/solenv/bin/modules/installer/windows/assembly.pm
new file mode 100644
index 000000000000..bb5efe2207d5
--- /dev/null
+++ b/solenv/bin/modules/installer/windows/assembly.pm
@@ -0,0 +1,371 @@
+#*************************************************************************
+#
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+#
+# Copyright 2000, 2010 Oracle and/or its affiliates.
+#
+# OpenOffice.org - a multi-platform office productivity suite
+#
+# This file is part of OpenOffice.org.
+#
+# OpenOffice.org is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License version 3
+# only, as published by the Free Software Foundation.
+#
+# OpenOffice.org is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License version 3 for more details
+# (a copy is included in the LICENSE file that accompanied this code).
+#
+# You should have received a copy of the GNU Lesser General Public License
+# version 3 along with OpenOffice.org. If not, see
+# <http://www.openoffice.org/license.html>
+# for a copy of the LGPLv3 License.
+#
+#*************************************************************************
+
+package installer::windows::assembly;
+
+use installer::files;
+use installer::globals;
+use installer::worker;
+use installer::windows::idtglobal;
+
+##############################################################
+# Returning the first module of a file from the
+# comma separated list of modules.
+##############################################################
+
+sub get_msiassembly_feature
+{
+ my ( $onefile ) = @_;
+
+ my $module = "";
+
+ if ( $onefile->{'modules'} ) { $module = $onefile->{'modules'}; }
+
+ # If modules contains a list of modules, only taking the first one.
+
+ if ( $module =~ /^\s*(.*?)\,/ ) { $module = $1; }
+
+ # Attention: Maximum feature length is 38!
+ installer::windows::idtglobal::shorten_feature_gid(\$module);
+
+ return $module;
+}
+
+##############################################################
+# Returning the component of a file.
+##############################################################
+
+sub get_msiassembly_component
+{
+ my ( $onefile ) = @_;
+
+ my $component = "";
+
+ $component = $onefile->{'componentname'};
+
+ return $component;
+}
+
+##############################################################
+# Returning the file name as manifest file
+##############################################################
+
+sub get_msiassembly_filemanifest
+{
+ my ( $onefile ) = @_;
+
+ my $filemanifest = "";
+
+ $filemanifest = $onefile->{'uniquename'};
+ # $filemanifest = $onefile->{'Name'};
+
+ return $filemanifest;
+}
+
+
+##############################################################
+# Returning the file application
+##############################################################
+
+sub get_msiassembly_fileapplication
+{
+ my ( $onefile ) = @_;
+
+ my $fileapplication = "";
+
+ return $fileapplication;
+}
+
+##############################################################
+# Returning the file attributes
+##############################################################
+
+sub get_msiassembly_attributes
+{
+ my ( $onefile ) = @_;
+
+ my $fileattributes = "";
+
+ if ( $onefile->{'Attributes'} ne "" ) { $fileattributes = $onefile->{'Attributes'}; }
+
+ return $fileattributes;
+}
+
+##############################################################
+# Returning the file object for the msiassembly table.
+##############################################################
+
+sub get_msiassembly_file
+{
+ my ( $filesref, $filename ) = @_;
+
+ my $foundfile = 0;
+ my $onefile;
+
+ for ( my $i = 0; $i <= $#{$filesref}; $i++ )
+ {
+ $onefile = ${$filesref}[$i];
+ my $name = $onefile->{'Name'};
+
+ if ( $name eq $filename )
+ {
+ $foundfile = 1;
+ last;
+ }
+ }
+
+ # It does not need to exist. For example products that do not contain the libraries.
+ # if (! $foundfile ) { installer::exiter::exit_program("ERROR: No unique file name found for $filename !", "get_selfreg_file"); }
+
+ if (! $foundfile ) { $onefile = ""; }
+
+ return $onefile;
+}
+
+##############################################################
+# Returning the file object for the msiassembly table.
+##############################################################
+
+sub get_msiassembly_file_by_gid
+{
+ my ( $filesref, $gid ) = @_;
+
+ my $foundfile = 0;
+ my $onefile;
+
+ for ( my $i = 0; $i <= $#{$filesref}; $i++ )
+ {
+ $onefile = ${$filesref}[$i];
+ my $filegid = $onefile->{'gid'};
+
+ if ( $filegid eq $gid )
+ {
+ $foundfile = 1;
+ last;
+ }
+ }
+
+ # It does not need to exist. For example products that do not contain the libraries.
+ # if (! $foundfile ) { installer::exiter::exit_program("ERROR: No unique file name found for $filename !", "get_selfreg_file"); }
+
+ if (! $foundfile ) { $onefile = ""; }
+
+ return $onefile;
+}
+
+####################################################################################
+# Creating the file MsiAssembly.idt dynamically
+# Content:
+# Component_ Feature_ File_Manifest File_Application Attributes
+# s72 s38 S72 S72 I2
+# MsiAssembly Component_
+####################################################################################
+
+sub create_msiassembly_table
+{
+ my ($filesref, $basedir) = @_;
+
+ $installer::globals::msiassemblyfiles = installer::worker::collect_all_items_with_special_flag($filesref, "ASSEMBLY");
+
+ my @msiassemblytable = ();
+
+ installer::windows::idtglobal::write_idt_header(\@msiassemblytable, "msiassembly");
+
+ # Registering all libraries listed in $installer::globals::msiassemblyfiles
+
+ for ( my $i = 0; $i <= $#{$installer::globals::msiassemblyfiles}; $i++ )
+ {
+ my $onefile = ${$installer::globals::msiassemblyfiles}[$i];
+
+ my %msiassembly = ();
+
+ $msiassembly{'Component_'} = get_msiassembly_component($onefile);
+ $msiassembly{'Feature_'} = get_msiassembly_feature($onefile);
+ $msiassembly{'File_Manifest'} = get_msiassembly_filemanifest($onefile);
+ $msiassembly{'File_Application'} = get_msiassembly_fileapplication($onefile);
+ $msiassembly{'Attributes'} = get_msiassembly_attributes($onefile);
+
+ my $oneline = $msiassembly{'Component_'} . "\t" . $msiassembly{'Feature_'} . "\t" .
+ $msiassembly{'File_Manifest'} . "\t" . $msiassembly{'File_Application'} . "\t" .
+ $msiassembly{'Attributes'} . "\n";
+
+ push(@msiassemblytable, $oneline);
+ }
+
+ # Saving the file
+
+ my $msiassemblytablename = $basedir . $installer::globals::separator . "MsiAssem.idt";
+ installer::files::save_file($msiassemblytablename ,\@msiassemblytable);
+ my $infoline = "Created idt file: $msiassemblytablename\n";
+ push(@installer::globals::logfileinfo, $infoline);
+}
+
+####################################################################################
+# Returning the name for the table MsiAssemblyName
+####################################################################################
+
+sub get_msiassemblyname_name
+{
+ ( $number ) = @_;
+
+ my $name = "";
+
+ if ( $number == 1 ) { $name = "name"; }
+ elsif ( $number == 2 ) { $name = "publicKeyToken"; }
+ elsif ( $number == 3 ) { $name = "version"; }
+ elsif ( $number == 4 ) { $name = "culture"; }
+
+ return $name;
+}
+
+####################################################################################
+# Creating the file MsiAssemblyName.idt dynamically
+# Content:
+# Component_ Name Value
+# s72 s255 s255
+# MsiAssemblyName Component_ Name
+####################################################################################
+
+sub create_msiassemblyname_table
+{
+ my ($filesref, $basedir) = @_;
+
+ my @msiassemblynametable = ();
+
+ installer::windows::idtglobal::write_idt_header(\@msiassemblynametable, "msiassemblyname");
+
+ for ( my $i = 0; $i <= $#{$installer::globals::msiassemblyfiles}; $i++ )
+ {
+ my $onefile = ${$installer::globals::msiassemblyfiles}[$i];
+
+ my $component = get_msiassembly_component($onefile);
+ my $oneline = "";
+
+ # Order: (Assembly)name, publicKeyToken, version, culture.
+
+ if ( $onefile->{'Assemblyname'} )
+ {
+ $oneline = $component . "\t" . "name" . "\t" . $onefile->{'Assemblyname'} . "\n";
+ push(@msiassemblynametable, $oneline);
+ }
+
+ if ( $onefile->{'PublicKeyToken'} )
+ {
+ $oneline = $component . "\t" . "publicKeyToken" . "\t" . $onefile->{'PublicKeyToken'} . "\n";
+ push(@msiassemblynametable, $oneline);
+ }
+
+ if ( $onefile->{'Version'} )
+ {
+ $oneline = $component . "\t" . "version" . "\t" . $onefile->{'Version'} . "\n";
+ push(@msiassemblynametable, $oneline);
+ }
+
+ if ( $onefile->{'Culture'} )
+ {
+ $oneline = $component . "\t" . "culture" . "\t" . $onefile->{'Culture'} . "\n";
+ push(@msiassemblynametable, $oneline);
+ }
+
+ if ( $onefile->{'ProcessorArchitecture'} )
+ {
+ $oneline = $component . "\t" . "processorArchitecture" . "\t" . $onefile->{'ProcessorArchitecture'} . "\n";
+ push(@msiassemblynametable, $oneline);
+ }
+ }
+
+ # Saving the file
+
+ my $msiassemblynametablename = $basedir . $installer::globals::separator . "MsiAsseN.idt";
+ installer::files::save_file($msiassemblynametablename ,\@msiassemblynametable);
+ my $infoline = "Created idt file: $msiassemblynametablename\n";
+ push(@installer::globals::logfileinfo, $infoline);
+
+}
+
+####################################################################################
+# setting an installation condition for the assembly libraries saved in
+# @installer::globals::msiassemblynamecontent
+####################################################################################
+
+sub add_assembly_condition_into_component_table
+{
+ my ($filesref, $basedir) = @_;
+
+ my $componenttablename = $basedir . $installer::globals::separator . "Componen.idt";
+ my $componenttable = installer::files::read_file($componenttablename);
+ my $changed = 0;
+ my $infoline = "";
+
+ for ( my $i = 0; $i <= $#{$installer::globals::msiassemblyfiles}; $i++ )
+ {
+ my $onefile = ${$installer::globals::msiassemblyfiles}[$i];
+
+ my $filecomponent = get_msiassembly_component($onefile);
+
+ for ( my $j = 0; $j <= $#{$componenttable}; $j++ )
+ {
+ my $oneline = ${$componenttable}[$j];
+
+ if ( $oneline =~ /(.*)\t(.*)\t(.*)\t(.*)\t(.*)\t(.*)/ )
+ {
+ my $component = $1;
+ my $componentid = $2;
+ my $directory = $3;
+ my $attributes = $4;
+ my $condition = $5;
+ my $keypath = $6;
+
+ if ( $component eq $filecomponent )
+ {
+ # setting the condition
+
+ # $condition = "MsiNetAssemblySupport";
+ $condition = "DOTNET_SUFFICIENT=1";
+ $oneline = $component . "\t" . $componentid . "\t" . $directory . "\t" . $attributes . "\t" . $condition . "\t" . $keypath . "\n";
+ ${$componenttable}[$j] = $oneline;
+ $changed = 1;
+ $infoline = "Changing $componenttablename :\n";
+ push(@installer::globals::logfileinfo, $infoline);
+ $infoline = $oneline;
+ push(@installer::globals::logfileinfo, $infoline);
+ last;
+ }
+ }
+ }
+ }
+
+ if ( $changed )
+ {
+ # Saving the file
+ installer::files::save_file($componenttablename ,$componenttable);
+ $infoline = "Saved idt file: $componenttablename\n";
+ push(@installer::globals::logfileinfo, $infoline);
+ }
+}
+
+1; \ No newline at end of file
diff --git a/solenv/bin/modules/installer/windows/binary.pm b/solenv/bin/modules/installer/windows/binary.pm
new file mode 100644
index 000000000000..96ee50eb4b19
--- /dev/null
+++ b/solenv/bin/modules/installer/windows/binary.pm
@@ -0,0 +1,77 @@
+#*************************************************************************
+#
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+#
+# Copyright 2000, 2010 Oracle and/or its affiliates.
+#
+# OpenOffice.org - a multi-platform office productivity suite
+#
+# This file is part of OpenOffice.org.
+#
+# OpenOffice.org is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License version 3
+# only, as published by the Free Software Foundation.
+#
+# OpenOffice.org is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License version 3 for more details
+# (a copy is included in the LICENSE file that accompanied this code).
+#
+# You should have received a copy of the GNU Lesser General Public License
+# version 3 along with OpenOffice.org. If not, see
+# <http://www.openoffice.org/license.html>
+# for a copy of the LGPLv3 License.
+#
+#*************************************************************************
+
+package installer::windows::binary;
+
+use installer::existence;
+use installer::files;
+use installer::globals;
+
+###########################################################################################################
+# Updating the table Binary dynamically with all files from $binarytablefiles
+# Content:
+# Name Data
+# s72 v0
+# Binary Name
+###########################################################################################################
+
+sub update_binary_table
+{
+ my ($languageidtdir, $filesref, $binarytablefiles) = @_;
+
+ my $binaryidttablename = $languageidtdir . $installer::globals::separator . "Binary.idt";
+ my $binaryidttable = installer::files::read_file($binaryidttablename);
+
+ # Only the iconfiles, that are used in the shortcut table for the
+ # FolderItems (entries in Windows startmenu) are added into the icon table.
+
+ for ( my $i = 0; $i <= $#{$binarytablefiles}; $i++ )
+ {
+ my $binaryfile = ${$binarytablefiles}[$i];
+ my $binaryfilename = $binaryfile->{'Name'};
+ my $binaryfiledata = $binaryfilename;
+
+ $binaryfilename =~ s/\.//g; # removing "." in filename: "abc.dll" to "abcdll" in name column
+
+ my %binary = ();
+
+ $binary{'Name'} = $binaryfilename;
+ $binary{'Data'} = $binaryfiledata;
+
+ my $oneline = $binary{'Name'} . "\t" . $binary{'Data'} . "\n";
+
+ push(@{$binaryidttable}, $oneline);
+ }
+
+ # Saving the file
+
+ installer::files::save_file($binaryidttablename ,$binaryidttable);
+ my $infoline = "Updated idt file: $binaryidttablename\n";
+ push(@installer::globals::logfileinfo, $infoline);
+}
+
+1;
diff --git a/solenv/bin/modules/installer/windows/component.pm b/solenv/bin/modules/installer/windows/component.pm
new file mode 100644
index 000000000000..38989858a4b6
--- /dev/null
+++ b/solenv/bin/modules/installer/windows/component.pm
@@ -0,0 +1,524 @@
+#*************************************************************************
+#
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+#
+# Copyright 2000, 2010 Oracle and/or its affiliates.
+#
+# OpenOffice.org - a multi-platform office productivity suite
+#
+# This file is part of OpenOffice.org.
+#
+# OpenOffice.org is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License version 3
+# only, as published by the Free Software Foundation.
+#
+# OpenOffice.org is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License version 3 for more details
+# (a copy is included in the LICENSE file that accompanied this code).
+#
+# You should have received a copy of the GNU Lesser General Public License
+# version 3 along with OpenOffice.org. If not, see
+# <http://www.openoffice.org/license.html>
+# for a copy of the LGPLv3 License.
+#
+#*************************************************************************
+
+package installer::windows::component;
+
+use installer::converter;
+use installer::existence;
+use installer::exiter;
+use installer::files;
+use installer::globals;
+use installer::windows::idtglobal;
+use installer::windows::language;
+
+##############################################################
+# Returning a globally unique ID (GUID) for a component
+# If the component is new, a unique guid has to be created.
+# If the component already exists, the guid has to be
+# taken from a list component <-> guid
+# Sample for a guid: {B68FD953-3CEF-4489-8269-8726848056E8}
+##############################################################
+
+sub get_component_guid
+{
+ my ( $componentname, $componentidhashref ) = @_;
+
+ # At this time only a template
+ my $returnvalue = "\{COMPONENTGUID\}";
+
+ if (( $installer::globals::updatedatabase ) && ( exists($componentidhashref->{$componentname}) ))
+ {
+ $returnvalue = $componentidhashref->{$componentname};
+ }
+
+ # Returning a ComponentID, that is assigned in scp project
+ if ( exists($installer::globals::componentid{$componentname}) )
+ {
+ $returnvalue = "\{" . $installer::globals::componentid{$componentname} . "\}";
+ }
+
+ return $returnvalue;
+}
+
+##############################################################
+# Returning the directory for a file component.
+##############################################################
+
+sub get_file_component_directory
+{
+ my ($componentname, $filesref, $dirref) = @_;
+
+ my ($onefile, $component, $onedir, $hostname, $uniquedir);
+ my $found = 0;
+
+ for ( my $i = 0; $i <= $#{$filesref}; $i++ )
+ {
+ $onefile = ${$filesref}[$i];
+ $component = $onefile->{'componentname'};
+
+ if ( $component eq $componentname )
+ {
+ $found = 1;
+ last;
+ }
+ }
+
+ if (!($found))
+ {
+ # This component can be ignored, if it exists in a version with extension "_pff" (this was renamed in file::get_sequence_for_file() )
+ my $ignore_this_component = 0;
+ my $origcomponentname = $componentname;
+ my $componentname = $componentname . "_pff";
+
+ for ( my $j = 0; $j <= $#{$filesref}; $j++ )
+ {
+ $onefile = ${$filesref}[$j];
+ $component = $onefile->{'componentname'};
+
+ if ( $component eq $componentname )
+ {
+ $ignore_this_component = 1;
+ last;
+ }
+ }
+
+ if ( $ignore_this_component ) { return "IGNORE_COMP"; }
+ else { installer::exiter::exit_program("ERROR: Did not find component \"$origcomponentname\" in file collection", "get_file_component_directory"); }
+ }
+
+ my $localstyles = "";
+
+ if ( $onefile->{'Styles'} ) { $localstyles = $onefile->{'Styles'}; }
+
+ if ( $localstyles =~ /\bFONT\b/ ) # special handling for font files
+ {
+ return $installer::globals::fontsfolder;
+ }
+
+ my $destdir = "";
+
+ if ( $onefile->{'Dir'} ) { $destdir = $onefile->{'Dir'}; }
+
+ if ( $destdir =~ /\bPREDEFINED_OSSHELLNEWDIR\b/ ) # special handling for shellnew files
+ {
+ return $installer::globals::templatefolder;
+ }
+
+ my $destination = $onefile->{'destination'};
+
+ installer::pathanalyzer::get_path_from_fullqualifiedname(\$destination);
+
+ $destination =~ s/\Q$installer::globals::separator\E\s*$//;
+
+ # This path has to be defined in the directory collection at "HostName"
+
+ if ($destination eq "") # files in the installation root
+ {
+ $uniquedir = "INSTALLLOCATION";
+ }
+ else
+ {
+ $found = 0;
+
+ for ( my $i = 0; $i <= $#{$dirref}; $i++ )
+ {
+ $onedir = ${$dirref}[$i];
+ $hostname = $onedir->{'HostName'};
+
+ if ( $hostname eq $destination )
+ {
+ $found = 1;
+ last;
+ }
+ }
+
+ if (!($found))
+ {
+ installer::exiter::exit_program("ERROR: Did not find destination $destination in directory collection", "get_file_component_directory");
+ }
+
+ $uniquedir = $onedir->{'uniquename'};
+
+ if ( $uniquedir eq $installer::globals::officeinstalldirectory )
+ {
+ $uniquedir = "INSTALLLOCATION";
+ }
+ }
+
+ $onefile->{'uniquedirname'} = $uniquedir; # saving it in the file collection
+
+ return $uniquedir
+}
+
+##############################################################
+# Returning the directory for a registry component.
+# This cannot be a useful value
+##############################################################
+
+sub get_registry_component_directory
+{
+ my $componentdir = "INSTALLLOCATION";
+
+ return $componentdir;
+}
+
+##############################################################
+# Returning the attributes for a file component.
+# Always 8 in this first try?
+##############################################################
+
+sub get_file_component_attributes
+{
+ my ($componentname, $filesref) = @_;
+
+ my $attributes;
+
+ $attributes = 2;
+
+ # special handling for font files
+
+ my $onefile;
+ my $found = 0;
+
+ for ( my $i = 0; $i <= $#{$filesref}; $i++ )
+ {
+ $onefile = ${$filesref}[$i];
+ my $component = $onefile->{'componentname'};
+
+ if ( $component eq $componentname )
+ {
+ $found = 1;
+ last;
+ }
+ }
+
+ if (!($found))
+ {
+ installer::exiter::exit_program("ERROR: Did not find component in file collection", "get_file_component_attributes");
+ }
+
+ my $localstyles = "";
+
+ if ( $onefile->{'Styles'} ) { $localstyles = $onefile->{'Styles'}; }
+
+ if ( $localstyles =~ /\bFONT\b/ )
+ {
+ $attributes = 16; # font files will not be deinstalled
+ }
+
+ if ( $localstyles =~ /\bASSEMBLY\b/ )
+ {
+ $attributes = 0; # Assembly files cannot run from source
+ }
+
+ if (( $onefile->{'Dir'} =~ /\bPREDEFINED_OSSHELLNEWDIR\b/ ) || ( $onefile->{'needs_user_registry_key'} ))
+ {
+ $attributes = 4; # Files in shellnew dir and in non advertised startmenu entries must have user registry key as KeyPath
+ }
+
+ return $attributes
+}
+
+##############################################################
+# Returning the attributes for a registry component.
+# Always 4, indicating, the keypath is a defined in
+# table registry
+##############################################################
+
+sub get_registry_component_attributes
+{
+ my ($componentname) = @_;
+
+ my $attributes;
+
+ $attributes = 4;
+
+ if ( exists($installer::globals::dontdeletecomponents{$componentname}) ) { $attributes = $attributes + 16; }
+
+ return $attributes
+}
+
+##############################################################
+# Returning the conditions for a component.
+# This is important for language dependent components
+# in multilingual installation sets.
+##############################################################
+
+sub get_file_component_condition
+{
+ my ($componentname, $filesref) = @_;
+
+ my $condition = "";
+
+ if (exists($installer::globals::componentcondition{$componentname}))
+ {
+ $condition = $installer::globals::componentcondition{$componentname};
+ }
+
+ # there can be also tree conditions for multilayer products
+ if (exists($installer::globals::treeconditions{$componentname}))
+ {
+ if ( $condition eq "" )
+ {
+ $condition = $installer::globals::treeconditions{$componentname};
+ }
+ else
+ {
+ $condition = "($condition) And ($installer::globals::treeconditions{$componentname})";
+ }
+ }
+
+ return $condition
+}
+
+##############################################################
+# Returning the conditions for a registry component.
+##############################################################
+
+sub get_component_condition
+{
+ my ($componentname) = @_;
+
+ my $condition;
+
+ $condition = ""; # Always ?
+
+ if (exists($installer::globals::componentcondition{$componentname}))
+ {
+ $condition = $installer::globals::componentcondition{$componentname};
+ }
+
+ return $condition
+}
+
+####################################################################
+# Returning the keypath for a component.
+# This will be the name of the first file/registry, found in the
+# collection $itemsref
+# Attention: This has to be the unique (file)name, not the
+# real filename!
+####################################################################
+
+sub get_component_keypath
+{
+ my ($componentname, $itemsref, $componentidkeypathhashref) = @_;
+
+ my $oneitem;
+ my $found = 0;
+ my $infoline = "";
+
+ for ( my $i = 0; $i <= $#{$itemsref}; $i++ )
+ {
+ $oneitem = ${$itemsref}[$i];
+ my $component = $oneitem->{'componentname'};
+
+ if ( $component eq $componentname )
+ {
+ $found = 1;
+ last;
+ }
+ }
+
+ if (!($found))
+ {
+ installer::exiter::exit_program("ERROR: Did not find component in file/registry collection, function get_component_keypath", "get_component_keypath");
+ }
+
+ my $keypath = $oneitem->{'uniquename'}; # "uniquename", not "Name"
+
+ # Special handling for updates from existing databases, because KeyPath must not change
+ if (( $installer::globals::updatedatabase ) && ( exists($componentidkeypathhashref->{$componentname}) ))
+ {
+ $keypath = $componentidkeypathhashref->{$componentname};
+ # -> check, if this is a valid key path?!
+ if ( $keypath ne $oneitem->{'uniquename'} )
+ {
+ # Warning: This keypath was changed because of info from old database
+ $infoline = "WARNING: The KeyPath for component \"$componentname\" was changed from \"$oneitem->{'uniquename'}\" to \"$keypath\" because of information from update database";
+ push(@installer::globals::logfileinfo, $infoline);
+ }
+ }
+
+ # Special handling for components in PREDEFINED_OSSHELLNEWDIR. These components
+ # need as KeyPath a RegistryItem in HKCU
+ if ( $oneitem->{'userregkeypath'} ) { $keypath = $oneitem->{'userregkeypath'}; }
+
+ # saving it in the file and registry collection
+ $oneitem->{'keypath'} = $keypath;
+
+ return $keypath
+}
+
+###################################################################
+# Creating the file Componen.idt dynamically
+# Content:
+# Component ComponentId Directory_ Attributes Condition KeyPath
+###################################################################
+
+sub create_component_table
+{
+ my ($filesref, $registryref, $dirref, $allfilecomponentsref, $allregistrycomponents, $basedir, $componentidhashref, $componentidkeypathhashref) = @_;
+
+ my @componenttable = ();
+
+ my ($oneline, $infoline);
+
+ installer::windows::idtglobal::write_idt_header(\@componenttable, "component");
+
+ # collect_layer_conditions();
+
+
+ # File components
+
+ for ( my $i = 0; $i <= $#{$allfilecomponentsref}; $i++ )
+ {
+ my %onecomponent = ();
+
+ $onecomponent{'name'} = ${$allfilecomponentsref}[$i];
+ $onecomponent{'guid'} = get_component_guid($onecomponent{'name'}, $componentidhashref);
+ $onecomponent{'directory'} = get_file_component_directory($onecomponent{'name'}, $filesref, $dirref);
+ if ( $onecomponent{'directory'} eq "IGNORE_COMP" ) { next; }
+ $onecomponent{'attributes'} = get_file_component_attributes($onecomponent{'name'}, $filesref);
+ $onecomponent{'condition'} = get_file_component_condition($onecomponent{'name'}, $filesref);
+ $onecomponent{'keypath'} = get_component_keypath($onecomponent{'name'}, $filesref, $componentidkeypathhashref);
+
+ $oneline = $onecomponent{'name'} . "\t" . $onecomponent{'guid'} . "\t" . $onecomponent{'directory'} . "\t"
+ . $onecomponent{'attributes'} . "\t" . $onecomponent{'condition'} . "\t" . $onecomponent{'keypath'} . "\n";
+
+ push(@componenttable, $oneline);
+ }
+
+ # Registry components
+
+ for ( my $i = 0; $i <= $#{$allregistrycomponents}; $i++ )
+ {
+ my %onecomponent = ();
+
+ $onecomponent{'name'} = ${$allregistrycomponents}[$i];
+ $onecomponent{'guid'} = get_component_guid($onecomponent{'name'}, $componentidhashref);
+ $onecomponent{'directory'} = get_registry_component_directory();
+ $onecomponent{'attributes'} = get_registry_component_attributes($onecomponent{'name'});
+ $onecomponent{'condition'} = get_component_condition($onecomponent{'name'});
+ $onecomponent{'keypath'} = get_component_keypath($onecomponent{'name'}, $registryref, $componentidkeypathhashref);
+
+ $oneline = $onecomponent{'name'} . "\t" . $onecomponent{'guid'} . "\t" . $onecomponent{'directory'} . "\t"
+ . $onecomponent{'attributes'} . "\t" . $onecomponent{'condition'} . "\t" . $onecomponent{'keypath'} . "\n";
+
+ push(@componenttable, $oneline);
+ }
+
+ # Saving the file
+
+ my $componenttablename = $basedir . $installer::globals::separator . "Componen.idt";
+ installer::files::save_file($componenttablename ,\@componenttable);
+ $infoline = "Created idt file: $componenttablename\n";
+ push(@installer::globals::logfileinfo, $infoline);
+}
+
+####################################################################################
+# Returning a component for a scp module gid.
+# Pairs are saved in the files collector.
+####################################################################################
+
+sub get_component_name_from_modulegid
+{
+ my ($modulegid, $filesref) = @_;
+
+ my $componentname = "";
+
+ for ( my $i = 0; $i <= $#{$filesref}; $i++ )
+ {
+ my $onefile = ${$filesref}[$i];
+
+ if ( $onefile->{'modules'} )
+ {
+ my $filemodules = $onefile->{'modules'};
+ my $filemodulesarrayref = installer::converter::convert_stringlist_into_array_without_newline(\$filemodules, ",");
+
+ if (installer::existence::exists_in_array($modulegid, $filemodulesarrayref))
+ {
+ $componentname = $onefile->{'componentname'};
+ last;
+ }
+ }
+ }
+
+ return $componentname;
+}
+
+####################################################################################
+# Updating the file Environm.idt dynamically
+# Content:
+# Environment Name Value Component_
+####################################################################################
+
+sub set_component_in_environment_table
+{
+ my ($basedir, $filesref) = @_;
+
+ my $infoline = "";
+
+ my $environmentfilename = $basedir . $installer::globals::separator . "Environm.idt";
+
+ if ( -f $environmentfilename ) # only do something, if file exists
+ {
+ my $environmentfile = installer::files::read_file($environmentfilename);
+
+ for ( my $i = 3; $i <= $#{$environmentfile}; $i++ ) # starting in line 4 of Environm.idt
+ {
+ if ( ${$environmentfile}[$i] =~ /^\s*(.*?)\t(.*?)\t(.*?)\t(.*?)\s*$/ )
+ {
+ my $modulegid = $4; # in Environment table a scp module gid can be used as component replacement
+
+ my $componentname = get_component_name_from_modulegid($modulegid, $filesref);
+
+ if ( $componentname ) # only do something if a component could be found
+ {
+ $infoline = "Updated Environment table:\n";
+ push(@installer::globals::logfileinfo, $infoline);
+ $infoline = "Old line: ${$environmentfile}[$i]\n";
+ push(@installer::globals::logfileinfo, $infoline);
+
+ ${$environmentfile}[$i] =~ s/$modulegid/$componentname/;
+
+ $infoline = "New line: ${$environmentfile}[$i]\n";
+ push(@installer::globals::logfileinfo, $infoline);
+
+ }
+ }
+ }
+
+ # Saving the file
+
+ installer::files::save_file($environmentfilename ,$environmentfile);
+ $infoline = "Updated idt file: $environmentfilename\n";
+ push(@installer::globals::logfileinfo, $infoline);
+
+ }
+}
+
+1; \ No newline at end of file
diff --git a/solenv/bin/modules/installer/windows/createfolder.pm b/solenv/bin/modules/installer/windows/createfolder.pm
new file mode 100644
index 000000000000..7ade8866c6c5
--- /dev/null
+++ b/solenv/bin/modules/installer/windows/createfolder.pm
@@ -0,0 +1,153 @@
+#*************************************************************************
+#
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+#
+# Copyright 2000, 2010 Oracle and/or its affiliates.
+#
+# OpenOffice.org - a multi-platform office productivity suite
+#
+# This file is part of OpenOffice.org.
+#
+# OpenOffice.org is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License version 3
+# only, as published by the Free Software Foundation.
+#
+# OpenOffice.org is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License version 3 for more details
+# (a copy is included in the LICENSE file that accompanied this code).
+#
+# You should have received a copy of the GNU Lesser General Public License
+# version 3 along with OpenOffice.org. If not, see
+# <http://www.openoffice.org/license.html>
+# for a copy of the LGPLv3 License.
+#
+#*************************************************************************
+
+package installer::windows::createfolder;
+
+use installer::existence;
+use installer::exiter;
+use installer::files;
+use installer::globals;
+use installer::windows::idtglobal;
+
+##############################################################
+# Returning directory for createfolder table.
+##############################################################
+
+sub get_createfolder_directory
+{
+ my ($onedir) = @_;
+
+ my $uniquename = $onedir->{'uniquename'};
+
+ return $uniquename;
+}
+
+##############################################################
+# Searching the correct file for language pack directories.
+##############################################################
+
+sub get_languagepack_file
+{
+ my ($filesref, $onedir) = @_;
+
+ my $language = $onedir->{'specificlanguage'};
+ my $foundfile = 0;
+ my $onefile = "";
+
+ for ( my $i = 0; $i <= $#{$filesref}; $i++ )
+ {
+ $onefile = ${$filesref}[$i];
+
+ if ( $onefile->{'specificlanguage'} eq $onedir->{'specificlanguage'} )
+ {
+ $foundfile = 1;
+ last;
+ }
+ }
+
+ if ( ! $foundfile ) { installer::exiter::exit_program("ERROR: No file with correct language found (language pack build)!", "get_languagepack_file"); }
+
+ return $onefile;
+}
+
+##############################################################
+# Returning component for createfolder table.
+##############################################################
+
+sub get_createfolder_component
+{
+ my ($onedir, $filesref, $allvariableshashref) = @_;
+
+ # Directories do not belong to a module.
+ # Therefore they can only belong to the root module and
+ # will be added to a component at the root module.
+ # All directories will be added to the component
+ # containing the file $allvariableshashref->{'GLOBALFILEGID'}
+
+ if ( ! $allvariableshashref->{'GLOBALFILEGID'} ) { installer::exiter::exit_program("ERROR: GLOBALFILEGID must be defined in list file!", "get_createfolder_component"); }
+ if (( $installer::globals::patch ) && ( ! $allvariableshashref->{'GLOBALFILEGID'} )) { installer::exiter::exit_program("ERROR: GLOBALPATCHFILEGID must be defined in list file!", "get_createfolder_component"); }
+
+ my $globalfilegid = $allvariableshashref->{'GLOBALFILEGID'};
+ if ( $installer::globals::patch ) { $globalfilegid = $allvariableshashref->{'GLOBALPATCHFILEGID'}; }
+
+ my $onefile = "";
+ if ( $installer::globals::languagepack ) { $onefile = get_languagepack_file($filesref, $onedir); }
+ else { $onefile = installer::existence::get_specified_file($filesref, $globalfilegid); }
+
+ return $onefile->{'componentname'};
+}
+
+####################################################################################
+# Creating the file CreateFo.idt dynamically for creation of empty directories
+# Content:
+# Directory_ Component_
+####################################################################################
+
+sub create_createfolder_table
+{
+ my ($dirref, $filesref, $basedir, $allvariableshashref) = @_;
+
+ my @createfoldertable = ();
+
+ my $infoline;
+
+ installer::windows::idtglobal::write_idt_header(\@createfoldertable, "createfolder");
+
+ for ( my $i = 0; $i <= $#{$dirref}; $i++ )
+ {
+ my $onedir = ${$dirref}[$i];
+
+ # language packs get only language dependent directories
+ if (( $installer::globals::languagepack ) && ( $onedir->{'specificlanguage'} eq "" )) { next };
+
+ my $styles = "";
+
+ if ( $onedir->{'Styles'} ) { $styles = $onedir->{'Styles'}; }
+
+ if ( $styles =~ /\bCREATE\b/ )
+ {
+ my %directory = ();
+
+ $directory{'Directory_'} = get_createfolder_directory($onedir);
+ $directory{'Component_'} = get_createfolder_component($onedir, $filesref, $allvariableshashref);
+
+ my $oneline = $directory{'Directory_'} . "\t" . $directory{'Component_'} . "\n";
+
+ push(@createfoldertable, $oneline);
+ }
+ }
+
+ # Saving the file
+
+ my $createfoldertablename = $basedir . $installer::globals::separator . "CreateFo.idt";
+ installer::files::save_file($createfoldertablename ,\@createfoldertable);
+ $infoline = "Created idt file: $createfoldertablename\n";
+ push(@installer::globals::logfileinfo, $infoline);
+
+}
+
+1; \ No newline at end of file
diff --git a/solenv/bin/modules/installer/windows/directory.pm b/solenv/bin/modules/installer/windows/directory.pm
new file mode 100644
index 000000000000..ba6f9a3b75bd
--- /dev/null
+++ b/solenv/bin/modules/installer/windows/directory.pm
@@ -0,0 +1,456 @@
+#*************************************************************************
+#
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+#
+# Copyright 2000, 2010 Oracle and/or its affiliates.
+#
+# OpenOffice.org - a multi-platform office productivity suite
+#
+# This file is part of OpenOffice.org.
+#
+# OpenOffice.org is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License version 3
+# only, as published by the Free Software Foundation.
+#
+# OpenOffice.org is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License version 3 for more details
+# (a copy is included in the LICENSE file that accompanied this code).
+#
+# You should have received a copy of the GNU Lesser General Public License
+# version 3 along with OpenOffice.org. If not, see
+# <http://www.openoffice.org/license.html>
+# for a copy of the LGPLv3 License.
+#
+#*************************************************************************
+
+package installer::windows::directory;
+
+use installer::exiter;
+use installer::files;
+use installer::globals;
+use installer::pathanalyzer;
+use installer::windows::idtglobal;
+
+##############################################################
+# Collecting all directory trees in global hash
+##############################################################
+
+sub collectdirectorytrees
+{
+ my ( $directoryref ) = @_;
+
+ for ( my $i = 0; $i <= $#{$directoryref}; $i++ )
+ {
+ my $onedir = ${$directoryref}[$i];
+ my $styles = "";
+ if ( $onedir->{'Styles'} ) { $styles = $onedir->{'Styles'}; }
+
+ if ( $styles ne "" )
+ {
+ foreach my $treestyle ( keys %installer::globals::treestyles )
+ {
+ if ( $styles =~ /\b$treestyle\b/ )
+ {
+ my $hostname = $onedir->{'HostName'};
+ # -> hostname is the key, the style the value!
+ $installer::globals::hostnametreestyles{$hostname} = $treestyle;
+ }
+ }
+ }
+ }
+}
+
+##############################################################
+# Overwriting global programfilesfolder, if required
+##############################################################
+
+sub overwrite_programfilesfolder
+{
+ my ( $allvariables ) = @_;
+
+ if ( $allvariables->{'PROGRAMFILESFOLDERNAME'} )
+ {
+ $installer::globals::programfilesfolder = $allvariables->{'PROGRAMFILESFOLDERNAME'};
+ }
+}
+
+##############################################################
+# Adding unique directory names to the directory collection
+##############################################################
+
+sub create_unique_directorynames
+{
+ my ($directoryref) = @_;
+
+ $installer::globals::officeinstalldirectoryset = 0;
+
+ for ( my $i = 0; $i <= $#{$directoryref}; $i++ )
+ {
+ my $onedir = ${$directoryref}[$i];
+ my $uniquename = $onedir->{'HostName'};
+ my $styles = "";
+ if ( $onedir->{'Styles'} ) { $styles = $onedir->{'Styles'}; }
+ # get_path_from_fullqualifiedname(\$uniqueparentname);
+ # making /registry/schema/org/openoffice/VCL.xcs to VCL.xcs
+
+ $uniquename =~ s/^\s*//g; # removing beginning white spaces
+ $uniquename =~ s/\s*$//g; # removing ending white spaces
+ $uniquename =~ s/\s//g; # removing white spaces
+ $uniquename =~ s/\_//g; # removing existing underlines
+ $uniquename =~ s/\.//g; # removing dots in directoryname
+ $uniquename =~ s/\Q$installer::globals::separator\E/\_/g; # replacing slash and backslash with underline
+
+ my $uniqueparentname = $uniquename;
+
+ if ( $uniqueparentname =~ /^\s*(.*)\_(.*?)\s*$/ ) # the underline is now the separator
+ {
+ $uniqueparentname = $1;
+ }
+ else
+ {
+ $uniqueparentname = $installer::globals::programfilesfolder;
+ }
+
+ if ( $styles =~ /\bPROGRAMFILESFOLDER\b/ ) { $uniqueparentname = $installer::globals::programfilesfolder; }
+ if ( $styles =~ /\bCOMMONFILESFOLDER\b/ ) { $uniqueparentname = $installer::globals::commonfilesfolder; }
+ if ( $styles =~ /\bCOMMONAPPDATAFOLDER\b/ ) { $uniqueparentname = $installer::globals::commonappdatafolder; }
+ if ( $styles =~ /\bLOCALAPPDATAFOLDER\b/ ) { $uniqueparentname = $installer::globals::localappdatafolder; }
+
+ if ( $styles =~ /\bSHAREPOINTPATH\b/ )
+ {
+ $uniqueparentname = "SHAREPOINTPATH";
+ $installer::globals::usesharepointpath = 1;
+ }
+
+ $uniquename =~ s/\-/\_/g; # making "-" to "_"
+ $uniqueparentname =~ s/\-/\_/g; # making "-" to "_"
+
+ $onedir->{'uniquename'} = $uniquename;
+ $onedir->{'uniqueparentname'} = $uniqueparentname;
+
+ # setting the installlocation directory
+ if ( $styles =~ /\bISINSTALLLOCATION\b/ )
+ {
+ 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
+ if ( $styles =~ /\bSUNDIRECTORY\b/ )
+ {
+ if ( $installer::globals::vendordirectoryset ) { installer::exiter::exit_program("ERROR: Directory with flag SUNDIRECTORY alread set: \"$installer::globals::vendordirectory\".", "create_unique_directorynames"); }
+ $installer::globals::vendordirectory = $uniquename;
+ $installer::globals::vendordirectoryset = 1;
+ }
+ }
+}
+
+#####################################################
+# Adding ":." to selected default directory names
+#####################################################
+
+sub check_sourcedir_addon
+{
+ my ( $onedir, $allvariableshashref ) = @_;
+
+ if (($installer::globals::addchildprojects) ||
+ ($installer::globals::patch) ||
+ ($installer::globals::languagepack) ||
+ ($allvariableshashref->{'CHANGETARGETDIR'}))
+ {
+ my $sourcediraddon = "\:\.";
+ $onedir->{'defaultdir'} = $onedir->{'defaultdir'} . $sourcediraddon;
+ }
+
+}
+
+#####################################################
+# The directory with the style ISINSTALLLOCATION
+# will be replaced by INSTALLLOCATION
+#####################################################
+
+sub set_installlocation_directory
+{
+ my ( $directoryref, $allvariableshashref ) = @_;
+
+ if ( ! $installer::globals::installlocationdirectoryset ) { installer::exiter::exit_program("ERROR: Directory with flag ISINSTALLLOCATION not set!", "set_installlocation_directory"); }
+
+ for ( my $i = 0; $i <= $#{$directoryref}; $i++ )
+ {
+ my $onedir = ${$directoryref}[$i];
+
+ if ( $onedir->{'uniquename'} eq $installer::globals::installlocationdirectory )
+ {
+ $onedir->{'uniquename'} = "INSTALLLOCATION";
+ check_sourcedir_addon($onedir, $allvariableshashref);
+ }
+
+ if ( $onedir->{'uniquename'} eq $installer::globals::vendordirectory )
+ {
+ check_sourcedir_addon($onedir, $allvariableshashref);
+ }
+
+ if ( $onedir->{'uniqueparentname'} eq $installer::globals::installlocationdirectory )
+ {
+ $onedir->{'uniqueparentname'} = "INSTALLLOCATION";
+ }
+ }
+}
+
+#####################################################
+# Getting the name of the top level directory. This
+# can have only one letter
+#####################################################
+
+sub get_last_directory_name
+{
+ my ($completepathref) = @_;
+
+ if ( $$completepathref =~ /^.*[\/\\](.+?)\s*$/ )
+ {
+ $$completepathref = $1;
+ }
+}
+
+#####################################################
+# Creating the defaultdir for the file Director.idt
+#####################################################
+
+sub create_defaultdir_directorynames
+{
+ my ($directoryref, $shortdirnamehashref) = @_;
+
+ my @shortnames = ();
+ if ( $installer::globals::updatedatabase ) { @shortnames = values(%{$shortdirnamehashref}); }
+ elsif ( $installer::globals::prepare_winpatch ) { @shortnames = values(%installer::globals::saved83dirmapping); }
+
+ for ( my $i = 0; $i <= $#{$directoryref}; $i++ )
+ {
+ my $onedir = ${$directoryref}[$i];
+ my $hostname = $onedir->{'HostName'};
+
+ $hostname =~ s/\Q$installer::globals::separator\E\s*$//;
+ get_last_directory_name(\$hostname);
+ # installer::pathanalyzer::make_absolute_filename_to_relative_filename(\$hostname); # making program/classes to classes
+ my $uniquename = $onedir->{'uniquename'};
+ my $shortstring;
+ if (( $installer::globals::updatedatabase ) && ( exists($shortdirnamehashref->{$uniquename}) ))
+ {
+ $shortstring = $shortdirnamehashref->{$uniquename};
+ }
+ elsif (( $installer::globals::prepare_winpatch ) && ( exists($installer::globals::saved83dirmapping{$uniquename}) ))
+ {
+ $shortstring = $installer::globals::saved83dirmapping{$uniquename};
+ }
+ else
+ {
+ $shortstring = installer::windows::idtglobal::make_eight_three_conform($hostname, "dir", \@shortnames);
+ }
+
+ my $defaultdir;
+
+ if ( $shortstring eq $hostname )
+ {
+ $defaultdir = $hostname;
+ }
+ else
+ {
+ $defaultdir = $shortstring . "|" . $hostname;
+ }
+
+ $onedir->{'defaultdir'} = $defaultdir;
+
+ my $fontdir = "";
+ if ( $onedir->{'Dir'} ) { $fontdir = $onedir->{'Dir'}; }
+
+ my $fontdefaultdir = "";
+ if ( $onedir->{'defaultdir'} ) { $fontdefaultdir = $onedir->{'defaultdir'}; }
+
+ if (( $fontdir eq "PREDEFINED_OSSYSTEMFONTDIR" ) && ( $fontdefaultdir eq $installer::globals::fontsdirhostname ))
+ {
+ $installer::globals::fontsdirname = $onedir->{'defaultdir'};
+ $installer::globals::fontsdirparent = $onedir->{'uniqueparentname'};
+ }
+ }
+}
+
+###############################################
+# Fill content into the directory table
+###############################################
+
+sub create_directorytable_from_collection
+{
+ my ($directorytableref, $directoryref) = @_;
+
+ for ( my $i = 0; $i <= $#{$directoryref}; $i++ )
+ {
+ my $onedir = ${$directoryref}[$i];
+ my $hostname = $onedir->{'HostName'};
+ my $dir = "";
+
+ if ( $onedir->{'Dir'} ) { $dir = $onedir->{'Dir'}; }
+
+ if (( $dir eq "PREDEFINED_PROGDIR" ) && ( $hostname eq "" )) { next; } # removing files from root directory
+
+ my $oneline = $onedir->{'uniquename'} . "\t" . $onedir->{'uniqueparentname'} . "\t" . $onedir->{'defaultdir'} . "\n";
+
+ push(@{$directorytableref}, $oneline);
+ }
+}
+
+###############################################
+# Defining the root installation structure
+###############################################
+
+sub add_root_directories
+{
+ my ($directorytableref, $allvariableshashref) = @_;
+
+# my $sourcediraddon = "";
+# if (($installer::globals::addchildprojects) ||
+# ($installer::globals::patch) ||
+# ($installer::globals::languagepack) ||
+# ($allvariableshashref->{'CHANGETARGETDIR'}))
+# {
+# $sourcediraddon = "\:\.";
+# }
+
+ my $oneline = "";
+
+ if (( ! $installer::globals::patch ) && ( ! $installer::globals::languagepack ) && ( ! $allvariableshashref->{'DONTUSESTARTMENUFOLDER'} ))
+ {
+ my $productname = $allvariableshashref->{'PRODUCTNAME'};
+ my $productversion = $allvariableshashref->{'PRODUCTVERSION'};
+ my $baseproductversion = $productversion;
+
+ if (( $installer::globals::prepare_winpatch ) && ( $allvariableshashref->{'BASEPRODUCTVERSION'} ))
+ {
+ $baseproductversion = $allvariableshashref->{'BASEPRODUCTVERSION'}; # for example "2.0" for OOo
+ }
+
+ my $realproductkey = $productname . " " . $productversion;
+ my $productkey = $productname . " " . $baseproductversion;
+
+ if (( $allvariableshashref->{'POSTVERSIONEXTENSION'} ) && ( ! $allvariableshashref->{'DONTUSEEXTENSIONINDEFAULTDIR'} ))
+ {
+ $productkey = $productkey . " " . $allvariableshashref->{'POSTVERSIONEXTENSION'};
+ $realproductkey = $realproductkey . " " . $allvariableshashref->{'POSTVERSIONEXTENSION'};
+ }
+ if ( $allvariableshashref->{'NOVERSIONINDIRNAME'} )
+ {
+ $productkey = $productname;
+ $realproductkey = $realproductname;
+ }
+ if ( $allvariableshashref->{'NOSPACEINDIRECTORYNAME'} )
+ {
+ $productkey =~ s/\ /\_/g;
+ $realproductkey =~ s/\ /\_/g;
+ }
+
+ my $shortproductkey = installer::windows::idtglobal::make_eight_three_conform($productkey, "dir"); # third parameter not used
+ $shortproductkey =~ s/\s/\_/g; # changing empty space to underline
+
+ $oneline = "$installer::globals::officemenufolder\t$installer::globals::programmenufolder\t$shortproductkey|$realproductkey\n";
+ push(@{$directorytableref}, $oneline);
+ }
+
+ $oneline = "TARGETDIR\t\tSourceDir\n";
+ push(@{$directorytableref}, $oneline);
+
+ $oneline = "$installer::globals::programfilesfolder\tTARGETDIR\t.\n";
+ push(@{$directorytableref}, $oneline);
+
+ $oneline = "$installer::globals::programmenufolder\tTARGETDIR\t.\n";
+ push(@{$directorytableref}, $oneline);
+
+ $oneline = "$installer::globals::startupfolder\tTARGETDIR\t.\n";
+ push(@{$directorytableref}, $oneline);
+
+ $oneline = "$installer::globals::desktopfolder\tTARGETDIR\t.\n";
+ push(@{$directorytableref}, $oneline);
+
+ $oneline = "$installer::globals::startmenufolder\tTARGETDIR\t.\n";
+ push(@{$directorytableref}, $oneline);
+
+ $oneline = "$installer::globals::commonfilesfolder\tTARGETDIR\t.\n";
+ push(@{$directorytableref}, $oneline);
+
+ $oneline = "$installer::globals::commonappdatafolder\tTARGETDIR\t.\n";
+ push(@{$directorytableref}, $oneline);
+
+ $oneline = "$installer::globals::localappdatafolder\tTARGETDIR\t.\n";
+ push(@{$directorytableref}, $oneline);
+
+ if ( $installer::globals::usesharepointpath )
+ {
+ $oneline = "SHAREPOINTPATH\tTARGETDIR\t.\n";
+ push(@{$directorytableref}, $oneline);
+ }
+
+ $oneline = "$installer::globals::systemfolder\tTARGETDIR\t.\n";
+ push(@{$directorytableref}, $oneline);
+
+ my $localtemplatefoldername = $installer::globals::templatefoldername;
+ my $directorytableentry = $localtemplatefoldername;
+ my $shorttemplatefoldername = installer::windows::idtglobal::make_eight_three_conform($localtemplatefoldername, "dir");
+ if ( $shorttemplatefoldername ne $localtemplatefoldername ) { $directorytableentry = "$shorttemplatefoldername|$localtemplatefoldername"; }
+ $oneline = "$installer::globals::templatefolder\tTARGETDIR\t$directorytableentry\n";
+ push(@{$directorytableref}, $oneline);
+
+ if ( $installer::globals::fontsdirname )
+ {
+ $oneline = "$installer::globals::fontsfolder\t$installer::globals::fontsdirparent\t$installer::globals::fontsfoldername\:$installer::globals::fontsdirname\n";
+ }
+ else
+ {
+ $oneline = "$installer::globals::fontsfolder\tTARGETDIR\t$installer::globals::fontsfoldername\n";
+ }
+
+ push(@{$directorytableref}, $oneline);
+
+}
+
+###############################################
+# Creating the file Director.idt dynamically
+###############################################
+
+sub create_directory_table
+{
+ my ($directoryref, $basedir, $allvariableshashref, $shortdirnamehashref, $loggingdir) = @_;
+
+ # Structure of the directory table:
+ # Directory Directory_Parent DefaultDir
+ # Directory is a unique identifier
+ # Directory_Parent is the unique identifier of the parent
+ # DefaultDir is .:APPLIC~1|Application Data with
+ # Before ":" : [sourcedir]:[destdir] (not programmed yet)
+ # After ":" : 8+3 and not 8+3 the destination directory name
+
+ my @directorytable = ();
+ my $infoline;
+
+ overwrite_programfilesfolder($allvariableshashref);
+ create_unique_directorynames($directoryref);
+ if ( $installer::globals::globallogging ) { installer::files::save_array_of_hashes($loggingdir . "directoriesforidt_local_1.log", $directoryref); }
+ create_defaultdir_directorynames($directoryref, $shortdirnamehashref); # only destdir!
+ if ( $installer::globals::globallogging ) { installer::files::save_array_of_hashes($loggingdir . "directoriesforidt_local_2.log", $directoryref); }
+ set_installlocation_directory($directoryref, $allvariableshashref);
+ if ( $installer::globals::globallogging ) { installer::files::save_array_of_hashes($loggingdir . "directoriesforidt_local_3.log", $directoryref); }
+ installer::windows::idtglobal::write_idt_header(\@directorytable, "directory");
+ add_root_directories(\@directorytable, $allvariableshashref);
+ create_directorytable_from_collection(\@directorytable, $directoryref);
+
+ # Saving the file
+
+ my $directorytablename = $basedir . $installer::globals::separator . "Director.idt";
+ installer::files::save_file($directorytablename ,\@directorytable);
+ $infoline = "Created idt file: $directorytablename\n";
+ push(@installer::globals::logfileinfo, $infoline);
+
+}
+
+1;
diff --git a/solenv/bin/modules/installer/windows/feature.pm b/solenv/bin/modules/installer/windows/feature.pm
new file mode 100644
index 000000000000..39521baecdfe
--- /dev/null
+++ b/solenv/bin/modules/installer/windows/feature.pm
@@ -0,0 +1,445 @@
+#*************************************************************************
+#
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+#
+# Copyright 2000, 2010 Oracle and/or its affiliates.
+#
+# OpenOffice.org - a multi-platform office productivity suite
+#
+# This file is part of OpenOffice.org.
+#
+# OpenOffice.org is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License version 3
+# only, as published by the Free Software Foundation.
+#
+# OpenOffice.org is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License version 3 for more details
+# (a copy is included in the LICENSE file that accompanied this code).
+#
+# You should have received a copy of the GNU Lesser General Public License
+# version 3 along with OpenOffice.org. If not, see
+# <http://www.openoffice.org/license.html>
+# for a copy of the LGPLv3 License.
+#
+#*************************************************************************
+
+package installer::windows::feature;
+
+use installer::existence;
+use installer::exiter;
+use installer::files;
+use installer::globals;
+use installer::sorter;
+use installer::worker;
+use installer::windows::idtglobal;
+use installer::windows::language;
+
+##############################################################
+# Returning the gid for a feature.
+# Attention: Maximum length
+##############################################################
+
+sub get_feature_gid
+{
+ my ($onefeature) = @_;
+
+ my $gid = "";
+
+ if ( $onefeature->{'gid'} ) { $gid = $onefeature->{'gid'}; }
+
+ # Attention: Maximum feature length is 38!
+ installer::windows::idtglobal::shorten_feature_gid(\$gid);
+
+ return $gid
+}
+
+##############################################################
+# Returning the gid of the parent.
+# Attention: Maximum length
+##############################################################
+
+sub get_feature_parent
+{
+ my ($onefeature) = @_;
+
+ my $parentgid = "";
+
+ if ( $onefeature->{'ParentID'} ) { $parentgid = $onefeature->{'ParentID'}; }
+
+ # The modules, hanging directly below the root, have to be root modules.
+ # Only then it is possible to make the "real" root module invisible by
+ # setting the display to "0".
+
+ if ( $parentgid eq $installer::globals::rootmodulegid ) { $parentgid = ""; }
+
+ # Attention: Maximum feature length is 38!
+ installer::windows::idtglobal::shorten_feature_gid(\$parentgid);
+
+ return $parentgid
+}
+
+##############################################################
+# Returning the display for a feature.
+# 0: Feature is not shown
+# odd: subfeatures are shown
+# even: subfeatures are not shown
+##############################################################
+
+sub get_feature_display
+{
+ my ($onefeature) = @_;
+
+ my $display;
+ my $parentid = "";
+
+ if ( $onefeature->{'ParentID'} ) { $parentid = $onefeature->{'ParentID'}; }
+
+ if ( $parentid eq "" )
+ {
+ $display = "0"; # root module is not visible
+ }
+ elsif ( $onefeature->{'gid'} eq "gid_Module_Prg") # program module shows subfeatures
+ {
+ $display = "1"; # root module shows subfeatures
+ }
+ else
+ {
+ $display = "2"; # all other modules do not show subfeatures
+ }
+
+ # special case: Feature has flag "HIDDEN_ROOT" -> $display is 0
+ my $styles = "";
+ if ( $onefeature->{'Styles'} ) { $styles = $onefeature->{'Styles'}; }
+ if ( $styles =~ /\bHIDDEN_ROOT\b/ ) { $display = "0"; }
+
+ # Special handling for language modules. Only visible in multilingual installation set
+ if (( $styles =~ /\bSHOW_MULTILINGUAL_ONLY\b/ ) && ( ! $installer::globals::ismultilingual )) { $display = "0"; }
+
+ # Special handling for c05office. No program module visible.
+ if (( $onefeature->{'gid'} eq "gid_Module_Prg" ) && ( $installer::globals::product =~ /c05office/i )) { $display = "0"; }
+
+ # making all feature invisible in Language packs!
+ if ( $installer::globals::languagepack ) { $display = "0"; }
+
+ return $display
+}
+
+##############################################################
+# Returning the level for a feature.
+##############################################################
+
+sub get_feature_level
+{
+ my ($onefeature) = @_;
+
+ my $level = "20"; # the default
+
+ my $localdefault = "";
+
+ if ( $onefeature->{'Default'} ) { $localdefault = $onefeature->{'Default'}; }
+
+ if ( $localdefault eq "NO" ) # explicitely set Default = "NO"
+ {
+ $level = "200"; # deselected in default installation, base is 100
+ if ( $installer::globals::patch ) { $level = "20"; }
+ }
+
+ # special handling for Java and Ada
+ if ( $onefeature->{'Name'} )
+ {
+ if ( $onefeature->{'Name'} =~ /java/i ) { $level = $level + 40; }
+ }
+
+ # if FeatureLevel is defined in scp, this will be used
+
+ if ( $onefeature->{'FeatureLevel'} ) { $level = $onefeature->{'FeatureLevel'}; }
+
+ return $level
+}
+
+##############################################################
+# Returning the directory for a feature.
+##############################################################
+
+sub get_feature_directory
+{
+ my ($onefeature) = @_;
+
+ my $directory;
+
+ $directory = "INSTALLLOCATION";
+
+ return $directory
+}
+
+##############################################################
+# Returning the directory for a feature.
+##############################################################
+
+sub get_feature_attributes
+{
+ my ($onefeature) = @_;
+
+ my $attributes;
+
+ # No advertising of features and no leaving on network.
+ # Feature without parent must not have the "2"
+
+ my $parentgid = "";
+ if ( $onefeature->{'ParentID'} ) { $parentgid = $onefeature->{'ParentID'}; }
+
+ if (( $parentgid eq "" ) || ( $parentgid eq $installer::globals::rootmodulegid )) { $attributes = "8"; }
+ else { $attributes = "10"; }
+
+ return $attributes
+}
+
+#################################################################################
+# Replacing one variable in one files
+#################################################################################
+
+sub replace_one_variable
+{
+ my ($translationfile, $variable, $searchstring) = @_;
+
+ for ( my $i = 0; $i <= $#{$translationfile}; $i++ )
+ {
+ ${$translationfile}[$i] =~ s/\%$searchstring/$variable/g;
+ }
+}
+
+#################################################################################
+# Replacing the variables in the feature names and descriptions
+#################################################################################
+
+sub replace_variables
+{
+ my ($translationfile, $variableshashref) = @_;
+
+ foreach $key (keys %{$variableshashref})
+ {
+ my $value = $variableshashref->{$key};
+ replace_one_variable($translationfile, $value, $key);
+ }
+}
+
+#################################################################################
+# Collecting the feature recursively.
+#################################################################################
+
+sub collect_modules_recursive
+{
+ my ($modulesref, $parentid, $feature, $directaccess, $directgid, $directparent, $directsortkey, $sorted) = @_;
+
+ my @allchildren = ();
+ my $childrenexist = 0;
+
+ # Collecting children from Module $parentid
+
+ my $modulegid;
+ foreach $modulegid ( keys %{$directparent})
+ {
+ if ( $directparent->{$modulegid} eq $parentid )
+ {
+ my %childhash = ( "gid" => "$modulegid", "Sortkey" => "$directsortkey->{$modulegid}");
+ push(@allchildren, \%childhash);
+ $childrenexist = 1;
+ }
+ }
+
+ # Sorting children
+
+ if ( $childrenexist )
+ {
+ # Sort children
+ installer::sorter::sort_array_of_hashes_numerically(\@allchildren, "Sortkey");
+
+ # Adding children to new array
+ my $childhashref;
+ foreach $childhashref ( @allchildren )
+ {
+ my $gid = $childhashref->{'gid'};
+
+ # Saving all lines, that have this 'gid'
+
+ my $unique;
+ foreach $unique ( keys %{$directgid} )
+ {
+ if ( $directgid->{$unique} eq $gid )
+ {
+ push(@{$feature}, ${$modulesref}[$directaccess->{$unique}]);
+ if ( $sorted->{$unique} == 1 ) { installer::exiter::exit_program("ERROR: Sorting feature failed! \"$unique\" already sorted.", "sort_feature"); }
+ $sorted->{$unique} = 1;
+ }
+ }
+
+ collect_modules_recursive($modulesref, $gid, $feature, $directaccess, $directgid, $directparent, $directsortkey, $sorted);
+ }
+ }
+}
+
+#################################################################################
+# Sorting the feature in specified order. Evaluated is the key "Sortkey", that
+# is set in scp2 projects.
+# The display order of modules in Windows Installer is dependent from the order
+# in the idt file. Therefore the order of the modules array has to be adapted
+# to the Sortkey order, before the idt file is created.
+#################################################################################
+
+sub sort_feature
+{
+ my ($modulesref) = @_;
+
+ my @feature = ();
+
+ my %directaccess = ();
+ my %directparent = ();
+ my %directgid = ();
+ my %directsortkey = ();
+ my %sorted = ();
+
+ for ( my $i = 0; $i <= $#{$modulesref}; $i++ )
+ {
+ my $onefeature = ${$modulesref}[$i];
+
+ my $uniquekey = $onefeature->{'uniquekey'};
+ my $modulegid = $onefeature->{'gid'};
+
+ $directaccess{$uniquekey} = $i;
+
+ $directgid{$uniquekey} = $onefeature->{'gid'};
+
+ # ParentID and Sortkey are not saved for the 'uniquekey', but only for the 'gid'
+
+ if ( $onefeature->{'ParentID'} ) { $directparent{$modulegid} = $onefeature->{'ParentID'}; }
+ else { $directparent{$modulegid} = ""; }
+
+ if ( $onefeature->{'Sortkey'} ) { $directsortkey{$modulegid} = $onefeature->{'Sortkey'}; }
+ else { $directsortkey{$modulegid} = "9999"; }
+
+ # Bookkeeping:
+ $sorted{$uniquekey} = 0;
+ }
+
+ # Searching all feature recursively, beginning with ParentID = ""
+ my $parentid = "";
+ collect_modules_recursive($modulesref, $parentid, \@feature, \%directaccess, \%directgid, \%directparent, \%directsortkey, \%sorted);
+
+ # Bookkeeping
+ my $modulekey;
+ foreach $modulekey ( keys %sorted )
+ {
+ if ( $sorted{$modulekey} == 0 )
+ {
+ my $infoline = "Warning: Module \"$modulekey\" could not be sorted. Added to the end of the module array.\n";
+ push(@installer::globals::logfileinfo, $infoline);
+ push(@feature, ${$modulesref}[$directaccess{$modulekey}]);
+ }
+ }
+
+ return \@feature;
+}
+
+#################################################################################
+# Adding a unique key to the modules array. The gid is not unique for
+# multilingual modules. Only the combination from gid and specific language
+# is unique. Uniqueness is required for sorting mechanism.
+#################################################################################
+
+sub add_uniquekey
+{
+ my ( $modulesref ) = @_;
+
+ for ( my $i = 0; $i <= $#{$modulesref}; $i++ )
+ {
+ my $uniquekey = ${$modulesref}[$i]->{'gid'};
+ if ( ${$modulesref}[$i]->{'specificlanguage'} ) { $uniquekey = $uniquekey . "_" . ${$modulesref}[$i]->{'specificlanguage'}; }
+ ${$modulesref}[$i]->{'uniquekey'} = $uniquekey;
+ }
+}
+
+#################################################################################
+# Creating the file Feature.idt dynamically
+# Content:
+# Feature Feature_Parent Title Description Display Level Directory_ Attributes
+#################################################################################
+
+sub create_feature_table
+{
+ my ($modulesref, $basedir, $languagesarrayref, $allvariableshashref) = @_;
+
+ for ( my $m = 0; $m <= $#{$languagesarrayref}; $m++ )
+ {
+ my $onelanguage = ${$languagesarrayref}[$m];
+
+ my $infoline;
+
+ my @featuretable = ();
+
+ installer::windows::idtglobal::write_idt_header(\@featuretable, "feature");
+
+ for ( my $i = 0; $i <= $#{$modulesref}; $i++ )
+ {
+ my $onefeature = ${$modulesref}[$i];
+
+ # Java and Ada only, if the correct settings are set
+ my $styles = "";
+ if ( $onefeature->{'Styles'} ) { $styles = $onefeature->{'Styles'}; }
+ if (( $styles =~ /\bJAVAMODULE\b/ ) && ( ! ($allvariableshashref->{'JAVAPRODUCT'} ))) { next; }
+ if (( $styles =~ /\bADAMODULE\b/ ) && ( ! ($allvariableshashref->{'ADAPRODUCT'} ))) { next; }
+
+ # Controlling the language!
+ # Only language independent feature or feature with the correct language will be included into the table
+
+ if (! (!(( $onefeature->{'ismultilingual'} )) || ( $onefeature->{'specificlanguage'} eq $onelanguage )) ) { next; }
+
+ my %feature = ();
+
+ $feature{'feature'} = get_feature_gid($onefeature);
+ $feature{'feature_parent'} = get_feature_parent($onefeature);
+ # if ( $onefeature->{'ParentID'} eq "" ) { $feature{'feature_parent'} = ""; } # Root has no parent
+ $feature{'Title'} = $onefeature->{'Name'};
+ $feature{'Description'} = $onefeature->{'Description'};
+ $feature{'Display'} = get_feature_display($onefeature);
+ $feature{'Level'} = get_feature_level($onefeature);
+ $feature{'Directory_'} = get_feature_directory($onefeature);
+ $feature{'Attributes'} = get_feature_attributes($onefeature);
+
+ my $oneline = $feature{'feature'} . "\t" . $feature{'feature_parent'} . "\t" . $feature{'Title'} . "\t"
+ . $feature{'Description'} . "\t" . $feature{'Display'} . "\t" . $feature{'Level'} . "\t"
+ . $feature{'Directory_'} . "\t" . $feature{'Attributes'} . "\n";
+
+ push(@featuretable, $oneline);
+
+ # collecting all feature in global feature collector (so that properties can be set in property table)
+ if ( ! installer::existence::exists_in_array($feature{'feature'}, \@installer::globals::featurecollector) )
+ {
+ push(@installer::globals::featurecollector, $feature{'feature'});
+ }
+
+ # collecting all language feature in feature collector for check of language selection
+ if (( $styles =~ /\bSHOW_MULTILINGUAL_ONLY\b/ ) && ( $onefeature->{'ParentID'} ne $installer::globals::rootmodulegid ))
+ {
+ $installer::globals::multilingual_only_modules{$feature{'feature'}} = 1;
+ }
+
+ # collecting all application feature in global feature collector for check of application selection
+ if ( $styles =~ /\bAPPLICATIONMODULE\b/ )
+ {
+ $installer::globals::application_modules{$feature{'feature'}} = 1;
+ }
+ }
+
+ # Saving the file
+
+ my $featuretablename = $basedir . $installer::globals::separator . "Feature.idt" . "." . $onelanguage;
+ installer::files::save_file($featuretablename ,\@featuretable);
+ $infoline = "Created idt file: $featuretablename\n";
+ push(@installer::globals::logfileinfo, $infoline);
+ }
+
+}
+
+1;
diff --git a/solenv/bin/modules/installer/windows/featurecomponent.pm b/solenv/bin/modules/installer/windows/featurecomponent.pm
new file mode 100644
index 000000000000..c5053212aad3
--- /dev/null
+++ b/solenv/bin/modules/installer/windows/featurecomponent.pm
@@ -0,0 +1,244 @@
+#*************************************************************************
+#
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+#
+# Copyright 2000, 2010 Oracle and/or its affiliates.
+#
+# OpenOffice.org - a multi-platform office productivity suite
+#
+# This file is part of OpenOffice.org.
+#
+# OpenOffice.org is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License version 3
+# only, as published by the Free Software Foundation.
+#
+# OpenOffice.org is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License version 3 for more details
+# (a copy is included in the LICENSE file that accompanied this code).
+#
+# You should have received a copy of the GNU Lesser General Public License
+# version 3 along with OpenOffice.org. If not, see
+# <http://www.openoffice.org/license.html>
+# for a copy of the LGPLv3 License.
+#
+#*************************************************************************
+
+package installer::windows::featurecomponent;
+
+use installer::converter;
+use installer::existence;
+use installer::exiter;
+use installer::files;
+use installer::globals;
+use installer::windows::idtglobal;
+
+#################################################################################
+# Collecting all pairs of features and components from the files collector
+#################################################################################
+
+sub create_featurecomponent_table_from_files_collector
+{
+ my ($featurecomponenttableref, $filesref) = @_;
+
+ for ( my $i = 0; $i <= $#{$filesref}; $i++ )
+ {
+ my $onefile = ${$filesref}[$i];
+
+ my $filecomponent = $onefile->{'componentname'};
+ my $filemodules = $onefile->{'modules'};
+
+ if ( $filecomponent eq "" )
+ {
+ installer::exiter::exit_program("ERROR: No component defined for file $onefile->{'Name'}", "create_featurecomponent_table_from_files_collector");
+ }
+ if ( $filemodules eq "" )
+ {
+ installer::exiter::exit_program("ERROR: No modules found for file $onefile->{'Name'}", "create_featurecomponent_table_from_files_collector");
+ }
+
+ my $filemodulesarrayref = installer::converter::convert_stringlist_into_array(\$filemodules, ",");
+
+ for ( my $j = 0; $j <= $#{$filemodulesarrayref}; $j++ )
+ {
+ my %featurecomponent = ();
+
+ my $onemodule = ${$filemodulesarrayref}[$j];
+ $onemodule =~ s/\s*$//;
+ $featurecomponent{'Feature'} = $onemodule;
+ $featurecomponent{'Component'} = $filecomponent;
+
+ # Attention: Features are renamed, because the maximum length is 38.
+ # But in the files collector ($filesref), the original names are saved.
+
+ installer::windows::idtglobal::shorten_feature_gid(\$featurecomponent{'Feature'});
+
+ $oneline = "$featurecomponent{'Feature'}\t$featurecomponent{'Component'}\n";
+
+ # control of uniqueness
+
+ if (! installer::existence::exists_in_array($oneline, $featurecomponenttableref))
+ {
+ push(@{$featurecomponenttableref}, $oneline);
+ }
+ }
+ }
+}
+
+#################################################################################
+# Collecting all pairs of features and components from the registry collector
+#################################################################################
+
+sub create_featurecomponent_table_from_registry_collector
+{
+ my ($featurecomponenttableref, $registryref) = @_;
+
+ for ( my $i = 0; $i <= $#{$registryref}; $i++ )
+ {
+ my $oneregistry = ${$registryref}[$i];
+
+ my $registrycomponent = $oneregistry->{'componentname'};
+ my $registrymodule = $oneregistry->{'ModuleID'};
+
+ if ( $registrycomponent eq "" )
+ {
+ installer::exiter::exit_program("ERROR: No component defined for registry $oneregistry->{'gid'}", "create_featurecomponent_table_from_registry_collector");
+ }
+ if ( $registrymodule eq "" )
+ {
+ installer::exiter::exit_program("ERROR: No modules found for registry $oneregistry->{'gid'}", "create_featurecomponent_table_from_registry_collector");
+ }
+
+ my %featurecomponent = ();
+
+ $featurecomponent{'Feature'} = $registrymodule;
+ $featurecomponent{'Component'} = $registrycomponent;
+
+ # Attention: Features are renamed, because the maximum length is 38.
+ # But in the files collector ($filesref), the original names are saved.
+
+ installer::windows::idtglobal::shorten_feature_gid(\$featurecomponent{'Feature'});
+
+ $oneline = "$featurecomponent{'Feature'}\t$featurecomponent{'Component'}\n";
+
+ # control of uniqueness
+
+ if (! installer::existence::exists_in_array($oneline, $featurecomponenttableref))
+ {
+ push(@{$featurecomponenttableref}, $oneline);
+ }
+ }
+}
+
+#################################################################################
+# Collecting all feature that are listed in the featurecomponent table.
+#################################################################################
+
+sub collect_all_feature
+{
+ my ($featurecomponenttable) = @_;
+
+ my @allfeature = ();
+
+ for ( my $i = 3; $i <= $#{$featurecomponenttable}; $i++ ) # beginning in line 4
+ {
+ my $oneline = ${$featurecomponenttable}[$i];
+
+ if ( $oneline =~ /^\s*(\S+)\s+(\S+)\s*$/ )
+ {
+ my $feature = $1;
+
+ if (! installer::existence::exists_in_array($feature, \@allfeature)) { push(@allfeature, $feature); }
+ }
+ }
+
+ return \@allfeature;
+}
+
+#################################################################################
+# On Win98 and Win Me there seems to be the problem, that maximum 817
+# components can be added to a feature. Even if Windows Installer 2.0
+# is used.
+#################################################################################
+
+sub check_number_of_components_at_feature
+{
+ my ($featurecomponenttable) = @_;
+
+ my $infoline = "\nChecking number of components at features. Maximum is 817 (for Win 98 and Win Me)\n";
+ push(@installer::globals::logfileinfo, $infoline);
+
+ my $allfeature = collect_all_feature($featurecomponenttable);
+
+ for ( my $i = 0; $i <= $#{$allfeature}; $i++ )
+ {
+ my $onefeature = ${$allfeature}[$i];
+ my $featurecomponents = 0;
+
+ for ( my $j = 0; $j <= $#{$featurecomponenttable}; $j++ )
+ {
+ if ( ${$featurecomponenttable}[$j] =~ /^\s*\Q$onefeature\E\s+(\S+)\s*$/ ) { $featurecomponents++; }
+ }
+
+ if ( $featurecomponents > 816 )
+ {
+ installer::exiter::exit_program("ERROR: More than 816 components ($featurecomponents) at feature $onefeature. This causes problems on Win 98 and Win Me!", "check_number_of_components_at_feature");
+ }
+
+ # Logging the result
+
+ $infoline = "Number of components at feature $onefeature : $featurecomponents\n";
+ push(@installer::globals::logfileinfo, $infoline);
+ }
+
+ $infoline = "\n";
+ push(@installer::globals::logfileinfo, $infoline);
+}
+
+#################################################################################
+# Creating the file FeatureC.idt dynamically
+# Content:
+# Feature Component
+#################################################################################
+
+sub create_featurecomponent_table
+{
+ my ($filesref, $registryref, $basedir) = @_;
+
+ my @featurecomponenttable = ();
+ my $infoline;
+
+ installer::windows::idtglobal::write_idt_header(\@featurecomponenttable, "featurecomponent");
+
+ # This is the first time, that features and componentes are related
+ # Problem: How about created profiles, configurationfiles, services.rdb
+ # -> simple solution: putting them all to the root module
+ # Otherwise profiles and configurationfiles cannot be created the way, they are now created
+ # -> especially a problem for the configurationfiles! # ToDo
+ # Very good: All ProfileItems belong to the root
+ # services.rdb belongs to the root anyway.
+
+ # At the moment only the files are related to components (and the files know their modules).
+ # The component for each file is written into the files collector $filesinproductlanguageresolvedarrayref
+
+ create_featurecomponent_table_from_files_collector(\@featurecomponenttable, $filesref);
+
+ create_featurecomponent_table_from_registry_collector(\@featurecomponenttable, $registryref);
+
+ # Additional components have to be added here
+
+ # Checking, whether there are more than 817 components at a feature
+
+ check_number_of_components_at_feature(\@featurecomponenttable);
+
+ # Saving the file
+
+ my $featurecomponenttablename = $basedir . $installer::globals::separator . "FeatureC.idt";
+ installer::files::save_file($featurecomponenttablename ,\@featurecomponenttable);
+ $infoline = "Created idt file: $featurecomponenttablename\n";
+ push(@installer::globals::logfileinfo, $infoline);
+
+}
+
+1; \ No newline at end of file
diff --git a/solenv/bin/modules/installer/windows/file.pm b/solenv/bin/modules/installer/windows/file.pm
new file mode 100644
index 000000000000..10666be7460c
--- /dev/null
+++ b/solenv/bin/modules/installer/windows/file.pm
@@ -0,0 +1,971 @@
+#*************************************************************************
+#
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+#
+# Copyright 2000, 2010 Oracle and/or its affiliates.
+#
+# OpenOffice.org - a multi-platform office productivity suite
+#
+# This file is part of OpenOffice.org.
+#
+# OpenOffice.org is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License version 3
+# only, as published by the Free Software Foundation.
+#
+# OpenOffice.org is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License version 3 for more details
+# (a copy is included in the LICENSE file that accompanied this code).
+#
+# You should have received a copy of the GNU Lesser General Public License
+# version 3 along with OpenOffice.org. If not, see
+# <http://www.openoffice.org/license.html>
+# for a copy of the LGPLv3 License.
+#
+#*************************************************************************
+
+package installer::windows::file;
+
+use Digest::MD5;
+use installer::existence;
+use installer::exiter;
+use installer::files;
+use installer::globals;
+use installer::logger;
+use installer::pathanalyzer;
+use installer::worker;
+use installer::windows::font;
+use installer::windows::idtglobal;
+use installer::windows::language;
+
+##########################################################################
+# Assigning one cabinet file to each file. This is requrired,
+# if cabinet files shall be equivalent to packages.
+##########################################################################
+
+sub assign_cab_to_files
+{
+ my ( $filesref ) = @_;
+
+ my $infoline = "";
+
+ for ( my $i = 0; $i <= $#{$filesref}; $i++ )
+ {
+ if ( ! exists(${$filesref}[$i]->{'modules'}) ) { installer::exiter::exit_program("ERROR: No module assignment found for ${$filesref}[$i]->{'gid'} !", "assign_cab_to_files"); }
+ my $module = ${$filesref}[$i]->{'modules'};
+ # If modules contains a list of modules, only taking the first one.
+ if ( $module =~ /^\s*(.*?)\,/ ) { $module = $1; }
+
+ if ( ! exists($installer::globals::allcabinetassigns{$module}) ) { installer::exiter::exit_program("ERROR: No cabinet file assigned to module \"$module\" (${$filesref}[$i]->{'gid'}) !", "assign_cab_to_files"); }
+ ${$filesref}[$i]->{'assignedcabinetfile'} = $installer::globals::allcabinetassigns{$module};
+
+ # Counting the files in each cabinet file
+ if ( ! exists($installer::globals::cabfilecounter{${$filesref}[$i]->{'assignedcabinetfile'}}) )
+ {
+ $installer::globals::cabfilecounter{${$filesref}[$i]->{'assignedcabinetfile'}} = 1;
+ }
+ else
+ {
+ $installer::globals::cabfilecounter{${$filesref}[$i]->{'assignedcabinetfile'}}++;
+ }
+ }
+
+ # logging the number of files in each cabinet file
+
+ $infoline = "\nCabinet file content:\n";
+ push(@installer::globals::logfileinfo, $infoline);
+ my $cabfile;
+ foreach $cabfile ( sort keys %installer::globals::cabfilecounter )
+ {
+ $infoline = "$cabfile : $installer::globals::cabfilecounter{$cabfile} files\n";
+ push(@installer::globals::logfileinfo, $infoline);
+ }
+
+ # assigning startsequencenumbers for each cab file
+
+ my $offset = 1;
+ foreach $cabfile ( sort keys %installer::globals::cabfilecounter )
+ {
+ my $filecount = $installer::globals::cabfilecounter{$cabfile};
+ $installer::globals::cabfilecounter{$cabfile} = $offset;
+ $offset = $offset + $filecount;
+
+ $installer::globals::lastsequence{$cabfile} = $offset - 1;
+ }
+
+ # logging the start sequence numbers
+
+ $infoline = "\nCabinet file start sequences:\n";
+ push(@installer::globals::logfileinfo, $infoline);
+ foreach $cabfile ( sort keys %installer::globals::cabfilecounter )
+ {
+ $infoline = "$cabfile : $installer::globals::cabfilecounter{$cabfile}\n";
+ push(@installer::globals::logfileinfo, $infoline);
+ }
+
+ # logging the last sequence numbers
+
+ $infoline = "\nCabinet file last sequences:\n";
+ push(@installer::globals::logfileinfo, $infoline);
+ foreach $cabfile ( sort keys %installer::globals::lastsequence )
+ {
+ $infoline = "$cabfile : $installer::globals::lastsequence{$cabfile}\n";
+ push(@installer::globals::logfileinfo, $infoline);
+ }
+}
+
+##########################################################################
+# Assigning sequencenumbers to files. This is requrired,
+# if cabinet files shall be equivalent to packages.
+##########################################################################
+
+sub assign_sequencenumbers_to_files
+{
+ my ( $filesref ) = @_;
+
+ my %directaccess = ();
+ my %allassigns = ();
+
+ for ( my $i = 0; $i <= $#{$filesref}; $i++ )
+ {
+ my $onefile = ${$filesref}[$i];
+
+ # Keeping order in cabinet files
+ # -> collecting all files in one cabinet file
+ # -> sorting files and assigning numbers
+
+ # Saving counter $i for direct access into files array
+ # "destination" of the file is a unique identifier ('Name' is not unique!)
+ if ( exists($directaccess{$onefile->{'destination'}}) ) { installer::exiter::exit_program("ERROR: 'destination' at file not unique: $onefile->{'destination'}", "assign_sequencenumbers_to_files"); }
+ $directaccess{$onefile->{'destination'}} = $i;
+
+ my $cabfilename = $onefile->{'assignedcabinetfile'};
+ # collecting files in cabinet files
+ if ( ! exists($allassigns{$cabfilename}) )
+ {
+ my %onecabfile = ();
+ $onecabfile{$onefile->{'destination'}} = 1;
+ $allassigns{$cabfilename} = \%onecabfile;
+ }
+ else
+ {
+ $allassigns{$cabfilename}->{$onefile->{'destination'}} = 1;
+ }
+ }
+
+ # Sorting each hash and assigning numbers
+ # The destination of the file determines the sort order, not the filename!
+ my $cabfile;
+ foreach $cabfile ( sort keys %allassigns )
+ {
+ my $counter = $installer::globals::cabfilecounter{$cabfile};
+ my $dest;
+ foreach $dest ( sort keys %{$allassigns{$cabfile}} ) # <- sorting the destination!
+ {
+ my $directaccessnumber = $directaccess{$dest};
+ ${$filesref}[$directaccessnumber]->{'assignedsequencenumber'} = $counter;
+ $counter++;
+ }
+ }
+}
+
+###############################################
+# Generating the component name from a file
+###############################################
+
+sub get_file_component_name
+{
+ my ($fileref, $filesref) = @_;
+
+ # In this function exists the rule to create components from files
+ # Rule:
+ # Two files get the same componentid, if:
+ # both have the same destination directory.
+ # both have the same "gid" -> both were packed in the same zip file
+ # All other files are included into different components!
+
+ # my $componentname = $fileref->{'gid'} . "_" . $fileref->{'Dir'};
+
+ # $fileref->{'Dir'} is not sufficient! All files in a zip file have the same $fileref->{'Dir'},
+ # but can be in different subdirectories.
+ # Solution: destination=share\Scripts\beanshell\Capitalise\capitalise.bsh
+ # in which the filename (capitalise.bsh) has to be removed and all backslashes (slashes) are
+ # converted into underline.
+
+ my $destination = $fileref->{'destination'};
+ installer::pathanalyzer::get_path_from_fullqualifiedname(\$destination);
+ $destination =~ s/\s//g;
+ $destination =~ s/\\/\_/g;
+ $destination =~ s/\//\_/g;
+ $destination =~ s/\_\s*$//g; # removing ending underline
+
+ my $componentname = $fileref->{'gid'} . "__" . $destination;
+
+ # Files with different languages, need to be packed into different components.
+ # Then the installation of the language specific component is determined by a language condition.
+
+ if ( $fileref->{'ismultilingual'} )
+ {
+ my $officelanguage = $fileref->{'specificlanguage'};
+ $componentname = $componentname . "_" . $officelanguage;
+ }
+
+ $componentname = lc($componentname); # componentnames always lowercase
+
+ $componentname =~ s/\-/\_/g; # converting "-" to "_"
+ $componentname =~ s/\./\_/g; # converting "-" to "_"
+
+ # Attention: Maximum length for the componentname is 72
+
+ $componentname =~ s/gid_file_/g_f_/g;
+ $componentname =~ s/_extra_/_e_/g;
+ $componentname =~ s/_config_/_c_/g;
+ $componentname =~ s/_org_openoffice_/_o_o_/g;
+ $componentname =~ s/_program_/_p_/g;
+ $componentname =~ s/_typedetection_/_td_/g;
+ $componentname =~ s/_linguistic_/_l_/g;
+ $componentname =~ s/_module_/_m_/g;
+ $componentname =~ s/_optional_/_opt_/g;
+ $componentname =~ s/_packages/_pack/g;
+ $componentname =~ s/_menubar/_mb/g;
+ $componentname =~ s/_common_/_cm_/g;
+ $componentname =~ s/_export_/_exp_/g;
+ $componentname =~ s/_table_/_tb_/g;
+ $componentname =~ s/_sofficecfg_/_sc_/g;
+ $componentname =~ s/_startmodulecommands_/_smc_/g;
+ $componentname =~ s/_drawimpresscommands_/_dic_/g;
+ $componentname =~ s/_basiccommands_/_bac_/g;
+ $componentname =~ s/_basicidecommands_/_baic_/g;
+ $componentname =~ s/_genericcommands_/_genc_/g;
+ $componentname =~ s/_bibliographycommands_/_bibc_/g;
+ $componentname =~ s/_share_/_s_/g;
+ $componentname =~ s/_modules_/_ms_/g;
+ $componentname =~ s/_uiconfig_zip_/_ucz_/g;
+ $componentname =~ s/_soffice_cfg_/_sc_/g;
+
+ # All this is not necessary for files, which have the flag ASSIGNCOMPOMENT
+
+ my $styles = "";
+ if ( $fileref->{'Styles'} ) { $styles = $fileref->{'Styles'}; }
+ if ( $styles =~ /\bASSIGNCOMPOMENT\b/ ) { $componentname = get_component_from_assigned_file($fileref->{'AssignComponent'}, $filesref); }
+
+ return $componentname;
+}
+
+####################################################################
+# Returning the component name for a defined file gid.
+# This is necessary for files with flag ASSIGNCOMPOMENT
+####################################################################
+
+sub get_component_from_assigned_file
+{
+ my ($gid, $filesref) = @_;
+
+ my $onefile = installer::existence::get_specified_file($filesref, $gid);
+ my $componentname = "";
+ if ( $onefile->{'componentname'} ) { $componentname = $onefile->{'componentname'}; }
+ else { installer::exiter::exit_program("ERROR: No component defined for file: $gid", "get_component_from_assigned_file"); }
+
+ return $componentname;
+}
+
+####################################################################
+# Generating the special filename for the database file File.idt
+# Sample: CONTEXTS, CONTEXTS1
+# This name has to be unique.
+# In most cases this is simply the filename.
+####################################################################
+
+sub generate_unique_filename_for_filetable
+{
+ my ($fileref, $component, $uniquefilenamehashref) = @_;
+
+ # This new filename has to be saved into $fileref, because this is needed to find the source.
+ # The filename sbasic.idx/OFFSETS is changed to OFFSETS, but OFFSETS is not unique.
+ # In this procedure names like OFFSETS5 are produced. And exactly this string has to be added to
+ # the array of all files.
+
+ my $uniquefilename = "";
+ my $counter = 0;
+
+ if ( $fileref->{'Name'} ) { $uniquefilename = $fileref->{'Name'}; }
+
+ installer::pathanalyzer::make_absolute_filename_to_relative_filename(\$uniquefilename); # making /registry/schema/org/openoffice/VCL.xcs to VCL.xcs
+
+ # Reading unique filename with help of "Component_" in File table from old database
+ if (( $installer::globals::updatedatabase ) && ( exists($uniquefilenamehashref->{"$component/$uniquefilename"}) ))
+ {
+ $uniquefilename = $uniquefilenamehashref->{"$component/$uniquefilename"}; # syntax of $value: ($uniquename;$shortname)
+ if ( $uniquefilename =~ /^\s*(.*?)\;\s*(.*?)\s*$/ ) { $uniquefilename = $1; }
+ $lcuniquefilename = lc($uniquefilename);
+ $installer::globals::alluniquefilenames{$uniquefilename} = 1;
+ $installer::globals::alllcuniquefilenames{$lcuniquefilename} = 1;
+ return $uniquefilename;
+ }
+ elsif (( $installer::globals::prepare_winpatch ) && ( exists($installer::globals::savedmapping{"$component/$uniquefilename"}) ))
+ {
+ # If we have a FTK mapping for this component/file, use it.
+ $installer::globals::savedmapping{"$component/$uniquefilename"} =~ m/^(.*);/;
+ $uniquefilename = $1;
+ $lcuniquefilename = lc($uniquefilename);
+ $installer::globals::alluniquefilenames{$uniquefilename} = 1;
+ $installer::globals::alllcuniquefilenames{$lcuniquefilename} = 1;
+ return $uniquefilename;
+ }
+
+ $uniquefilename =~ s/\-/\_/g; # no "-" allowed
+ $uniquefilename =~ s/\@/\_/g; # no "@" allowed
+ $uniquefilename =~ s/\$/\_/g; # no "$" allowed
+ $uniquefilename =~ s/^\s*\./\_/g; # no "." at the beginning allowed allowed
+ $uniquefilename =~ s/^\s*\d/\_d/g; # no number at the beginning allowed allowed (even file "0.gif", replacing to "_d.gif")
+ $uniquefilename =~ s/org_openoffice_/ooo_/g; # shorten the unique file name
+
+ my $lcuniquefilename = lc($uniquefilename); # only lowercase names
+
+ my $newname = 0;
+
+ if ( ! exists($installer::globals::alllcuniquefilenames{$lcuniquefilename}) &&
+ ! exists($installer::globals::savedrevmapping{$lcuniquefilename}) )
+ {
+ $installer::globals::alluniquefilenames{$uniquefilename} = 1;
+ $installer::globals::alllcuniquefilenames{$lcuniquefilename} = 1;
+ $newname = 1;
+ }
+
+ if ( ! $newname )
+ {
+ # adding a number until the name is really unique: OFFSETS, OFFSETS1, OFFSETS2, ...
+ # But attention: Making "abc.xcu" to "abc1.xcu"
+
+ my $uniquefilenamebase = $uniquefilename;
+
+ do
+ {
+ $counter++;
+
+ if ( $uniquefilenamebase =~ /\./ )
+ {
+ $uniquefilename = $uniquefilenamebase;
+ $uniquefilename =~ s/\./$counter\./;
+ }
+ else
+ {
+ $uniquefilename = $uniquefilenamebase . $counter;
+ }
+
+ $newname = 0;
+ $lcuniquefilename = lc($uniquefilename); # only lowercase names
+
+ if ( ! exists($installer::globals::alllcuniquefilenames{$lcuniquefilename}) &&
+ ! exists($installer::globals::savedrevmapping{$lcuniquefilename}) )
+ {
+ $installer::globals::alluniquefilenames{$uniquefilename} = 1;
+ $installer::globals::alllcuniquefilenames{$lcuniquefilename} = 1;
+ $newname = 1;
+ }
+ }
+ until ( $newname )
+ }
+
+ return $uniquefilename;
+}
+
+####################################################################
+# Generating the special file column for the database file File.idt
+# Sample: NAMETR~1.TAB|.nametranslation.table
+# The first part has to be 8.3 conform.
+####################################################################
+
+sub generate_filename_for_filetable
+{
+ my ($fileref, $shortnamesref, $uniquefilenamehashref) = @_;
+
+ my $returnstring = "";
+
+ my $filename = $fileref->{'Name'};
+
+ installer::pathanalyzer::make_absolute_filename_to_relative_filename(\$filename); # making /registry/schema/org/openoffice/VCL.xcs to VCL.xcs
+
+ my $shortstring;
+
+ # Reading short string with help of "FileName" in File table from old database
+ if (( $installer::globals::updatedatabase ) && ( exists($uniquefilenamehashref->{"$fileref->{'componentname'}/$filename"}) ))
+ {
+ my $value = $uniquefilenamehashref->{"$fileref->{'componentname'}/$filename"}; # syntax of $value: ($uniquename;$shortname)
+ if ( $value =~ /^\s*(.*?)\;\s*(.*?)\s*$/ ) { $shortstring = $2; } # already collected in function "collect_shortnames_from_old_database"
+ else { $shortstring = $filename; }
+ }
+ elsif (( $installer::globals::prepare_winpatch ) && ( exists($installer::globals::savedmapping{"$fileref->{'componentname'}/$filename"}) ))
+ {
+ $installer::globals::savedmapping{"$fileref->{'componentname'}/$filename"} =~ m/.*;(.*)/;
+ if ($1 ne '')
+ {
+ $shortstring = $1;
+ }
+ else
+ {
+ $shortstring = installer::windows::idtglobal::make_eight_three_conform_with_hash($filename, "file", $shortnamesref);
+ }
+ }
+ else
+ {
+ $shortstring = installer::windows::idtglobal::make_eight_three_conform_with_hash($filename, "file", $shortnamesref);
+ }
+
+ if ( $shortstring eq $filename ) { $returnstring = $filename; } # nothing changed
+ else {$returnstring = $shortstring . "\|" . $filename; }
+
+ return $returnstring;
+}
+
+#########################################
+# Returning the filesize of a file
+#########################################
+
+sub get_filesize
+{
+ my ($fileref) = @_;
+
+ my $file = $fileref->{'sourcepath'};
+
+ my $filesize;
+
+ if ( -f $file ) # test of existence. For instance services.rdb does not always exist
+ {
+ $filesize = ( -s $file ); # file size can be "0"
+ }
+ else
+ {
+ $filesize = -1;
+ }
+
+ return $filesize;
+}
+
+#############################################
+# Returning the file version, if required
+# Sample: "8.0.1.8976";
+#############################################
+
+sub get_fileversion
+{
+ my ($onefile, $allvariables, $styles) = @_;
+
+ my $fileversion = "";
+
+ if ( $allvariables->{'USE_FILEVERSION'} )
+ {
+ if ( ! $allvariables->{'LIBRARYVERSION'} ) { installer::exiter::exit_program("ERROR: USE_FILEVERSION is set, but not LIBRARYVERSION", "get_fileversion"); }
+ my $libraryversion = $allvariables->{'LIBRARYVERSION'};
+ if ( $libraryversion =~ /^\s*(\d+)\.(\d+)\.(\d+)\s*$/ )
+ {
+ my $major = $1;
+ my $minor = $2;
+ my $micro = $3;
+ my $concat = 100 * $minor + $micro;
+ $libraryversion = $major . "\." . $concat;
+ }
+ my $vendornumber = 0;
+ if ( $allvariables->{'VENDORPATCHVERSION'} ) { $vendornumber = $allvariables->{'VENDORPATCHVERSION'}; }
+ $fileversion = $libraryversion . "\." . $installer::globals::buildid . "\." . $vendornumber;
+ if ( $onefile->{'FileVersion'} ) { $fileversion = $onefile->{'FileVersion'}; } # overriding FileVersion in scp
+
+ # if ( $styles =~ /\bFONT\b/ )
+ # {
+ # my $newfileversion = installer::windows::font::get_font_version($onefile->{'sourcepath'});
+ # if ( $newfileversion != 0 ) { $fileversion = $newfileversion; }
+ # }
+ }
+
+ if ( $installer::globals::prepare_winpatch ) { $fileversion = ""; } # Windows patches do not allow this version # -> who says so?
+
+ return $fileversion;
+}
+
+#############################################
+# Returning the sequence for a file
+#############################################
+
+sub get_sequence_for_file
+{
+ my ($number, $onefile, $fileentry, $allupdatesequenceshashref, $allupdatecomponentshashref, $allupdatefileorderhashref, $allfilecomponents) = @_;
+
+ my $sequence = "";
+ my $infoline = "";
+ my $pffcomponentname = $onefile->{'componentname'} . "_pff";
+
+ if ( $installer::globals::updatedatabase )
+ {
+ if (( exists($allupdatesequenceshashref->{$onefile->{'uniquename'}}) ) &&
+ (( $onefile->{'componentname'} eq $allupdatecomponentshashref->{$onefile->{'uniquename'}} ) ||
+ ( $pffcomponentname eq $allupdatecomponentshashref->{$onefile->{'uniquename'}} )))
+ {
+ # The second condition is necessary to find shifted files, that have same "uniquename", but are now
+ # located in another directory. This can be seen at the component name.
+ $sequence = $allupdatesequenceshashref->{$onefile->{'uniquename'}};
+ $onefile->{'assignedsequencenumber'} = $sequence;
+ # Collecting all used sequences, to guarantee, that no number is unused
+ $installer::globals::allusedupdatesequences{$sequence} = 1;
+ # Special help for files, that already have a "pff" component name (for example after ServicePack 1)
+ if ( $pffcomponentname eq $allupdatecomponentshashref->{$onefile->{'uniquename'}} )
+ {
+ $infoline = "Warning: Special handling for component \"$pffcomponentname\". This file was added after the final, but before this ServicePack.\n";
+ push(@installer::globals::logfileinfo, $infoline);
+ $onefile->{'componentname'} = $pffcomponentname; # pff for "post final file"
+ $fileentry->{'Component_'} = $onefile->{'componentname'};
+ if ( ! exists($allfilecomponents->{$fileentry->{'Component_'}}) ) { $allfilecomponents->{$fileentry->{'Component_'}} = 1; }
+ }
+ }
+ else
+ {
+ $installer::globals::updatesequencecounter++;
+ $sequence = $installer::globals::updatesequencecounter;
+ $onefile->{'assignedsequencenumber'} = $sequence;
+ # $onefile->{'assignedcabinetfile'} = $installer::globals::pffcabfilename; # assigning to cabinet file for "post final files"
+ # Collecting all new files
+ $installer::globals::newupdatefiles{$sequence} = $onefile;
+ # Saving in sequence hash
+ $allupdatefileorderhashref->{$sequence} = $onefile->{'uniquename'};
+
+ # If the new file is part of an existing component, this must be changed now. All files
+ # of one component have to be included in one cabinet file. But because the order must
+ # not change, all new files have to be added to new components.
+ # $onefile->{'componentname'} = $file{'Component_'};
+
+ $onefile->{'componentname'} = $onefile->{'componentname'} . "_pff"; # pff for "post final file"
+ $fileentry->{'Component_'} = $onefile->{'componentname'};
+ if ( ! exists($allfilecomponents->{$fileentry->{'Component_'}}) ) { $allfilecomponents->{$fileentry->{'Component_'}} = 1; }
+ $onefile->{'PostFinalFile'} = 1;
+ # $installer::globals::pfffileexists = 1;
+ # The sequence for this file has changed. It has to be inserted at the end of the files collector.
+ $installer::globals::insert_file_at_end = 1;
+ $installer::globals::newfilescollector{$sequence} = $onefile; # Adding new files to the end of the filescollector
+ $installer::globals::newfilesexist = 1;
+ }
+ }
+ elsif (( $onefile->{'assignedsequencenumber'} ) && ( $installer::globals::use_packages_for_cabs ))
+ {
+ $sequence = $onefile->{'assignedsequencenumber'};
+ }
+ else
+ {
+ $sequence = $number;
+ # my $sequence = $number + 1;
+
+ # Idea: Each component is packed into a cab file.
+ # This requires that all files in one cab file have sequences directly follwing each other,
+ # for instance from 1456 to 1466. Then in the media table the LastSequence for this cab file
+ # is 1466.
+ # Because all files belonging to one component are directly behind each other in the file
+ # collector, it is possible to use simply an increasing number as sequence value.
+ # If files belonging to one component are not directly behind each other in the files collector
+ # this mechanism will no longer work.
+ }
+
+ return $sequence;
+}
+
+#############################################
+# Returning the Windows language of a file
+#############################################
+
+sub get_language_for_file
+{
+ my ($fileref) = @_;
+
+ my $language = "";
+
+ if ( $fileref->{'specificlanguage'} ) { $language = $fileref->{'specificlanguage'}; }
+
+ if (!($language eq ""))
+ {
+ $language = installer::windows::language::get_windows_language($language);
+ }
+
+ return $language;
+}
+
+####################################################################
+# Creating a new KeyPath for components in TemplatesFolder.
+####################################################################
+
+sub generate_registry_keypath
+{
+ my ($onefile) = @_;
+
+ my $keypath = $onefile->{'Name'};
+ $keypath =~ s/\.//g;
+ $keypath = lc($keypath);
+ $keypath = "userreg_" . $keypath;
+
+ return $keypath;
+}
+
+####################################################################
+# Check, if in an update process files are missing. No removal
+# of files allowed for Windows Patch creation.
+# Also logging all new files, that have to be included in extra
+# components and cab files.
+####################################################################
+
+sub check_file_sequences
+{
+ my ($allupdatefileorderhashref, $allupdatecomponentorderhashref) = @_;
+
+ # All used sequences stored in %installer::globals::allusedupdatesequences
+ # Maximum sequence number of old database stored in $installer::globals::updatelastsequence
+ # All new files stored in %installer::globals::newupdatefiles
+
+ my $infoline = "";
+
+ my @missing_sequences = ();
+ my @really_missing_sequences = ();
+
+ for ( my $i = 1; $i <= $installer::globals::updatelastsequence; $i++ )
+ {
+ if ( ! exists($installer::globals::allusedupdatesequences{$i}) ) { push(@missing_sequences, $i); }
+ }
+
+ if ( $#missing_sequences > -1 )
+ {
+ # Missing sequences can also be caused by files included in merge modules. This files are added later into the file table.
+ # Therefore now it is time to check the content of the merge modules.
+
+ for ( my $j = 0; $j <= $#missing_sequences; $j++ )
+ {
+ my $filename = $allupdatefileorderhashref->{$missing_sequences[$j]};
+
+ # Is this a file from a merge module? Then this is no error.
+ if ( ! exists($installer::globals::mergemodulefiles{$filename}) )
+ {
+ push(@really_missing_sequences, $missing_sequences[$j]);
+ }
+ }
+ }
+
+ if ( $#really_missing_sequences > -1 )
+ {
+ my $errorstring = "";
+ for ( my $j = 0; $j <= $#really_missing_sequences; $j++ )
+ {
+ my $filename = $allupdatefileorderhashref->{$really_missing_sequences[$j]};
+ my $comp = $allupdatecomponentorderhashref->{$really_missing_sequences[$j]};
+ $errorstring = "$errorstring$filename (Sequence: $really_missing_sequences[$j], Component: \"$comp\")\n";
+ }
+
+ $infoline = "ERROR: Files are removed compared with update database.\nThe following files are missing:\n$errorstring";
+ push(@installer::globals::logfileinfo, $infoline);
+ installer::exiter::exit_program($infoline, "check_file_sequences");
+ }
+
+ # Searching for new files
+
+ my $counter = 0;
+
+ foreach my $key ( keys %installer::globals::newupdatefiles )
+ {
+ my $onefile = $installer::globals::newupdatefiles{$key};
+ $counter++;
+ if ( $counter == 1 )
+ {
+ $infoline = "\nNew files compared to the update database:\n";
+ push(@installer::globals::logfileinfo, $infoline);
+ }
+
+ $infoline = "$onefile->{'Name'} ($onefile->{'gid'}) Sequence: $onefile->{'assignedsequencenumber'}\n";
+ push(@installer::globals::logfileinfo, $infoline);
+ }
+
+ if ( $counter == 0 )
+ {
+ $infoline = "Info: No new file compared with update database!\n";
+ push(@installer::globals::logfileinfo, $infoline);
+ }
+
+}
+
+###################################################################
+# Collecting further conditions for the component table.
+# This is used by multilayer products, to enable installation
+# of separate layers.
+###################################################################
+
+sub get_tree_condition_for_component
+{
+ my ($onefile, $componentname) = @_;
+
+ if ( $onefile->{'destination'} )
+ {
+ my $dest = $onefile->{'destination'};
+
+ # Comparing the destination path with
+ # $installer::globals::hostnametreestyles{$hostname} = $treestyle;
+ # (-> hostname is the key, the style the value!)
+
+ foreach my $hostname ( keys %installer::globals::hostnametreestyles )
+ {
+ if (( $dest eq $hostname ) || ( $dest =~ /^\s*\Q$hostname\E\\/ ))
+ {
+ # the value is the style
+ my $style = $installer::globals::hostnametreestyles{$hostname};
+ # the condition is saved in %installer::globals::treestyles
+ my $condition = $installer::globals::treestyles{$style};
+ # Saving condition to be added in table Property
+ $installer::globals::usedtreeconditions{$condition} = 1;
+ $condition = $condition . "=1";
+ # saving this condition
+ $installer::globals::treeconditions{$componentname} = $condition;
+
+ # saving also at the file, for usage in fileinfo
+ $onefile->{'layer'} = $installer::globals::treelayername{$style};
+ }
+ }
+ }
+}
+
+############################################
+# Collecting all short names, that are
+# already used by the old database
+############################################
+
+sub collect_shortnames_from_old_database
+{
+ my ($uniquefilenamehashref, $shortnameshashref) = @_;
+
+ foreach my $key ( keys %{$uniquefilenamehashref} )
+ {
+ my $value = $uniquefilenamehashref->{$key}; # syntax of $value: ($uniquename;$shortname)
+
+ if ( $value =~ /^\s*(.*?)\;\s*(.*?)\s*$/ )
+ {
+ my $shortstring = $2;
+ $shortnameshashref->{$shortstring} = 1; # adding the shortname to the array of all shortnames
+ }
+ }
+}
+
+############################################
+# Creating the file File.idt dynamically
+############################################
+
+sub create_files_table
+{
+ my ($filesref, $allfilecomponentsref, $basedir, $allvariables, $uniquefilenamehashref, $allupdatesequenceshashref, $allupdatecomponentshashref, $allupdatefileorderhashref) = @_;
+
+ installer::logger::include_timestamp_into_logfile("Performance Info: File Table start");
+
+ # Structure of the files table:
+ # File Component_ FileName FileSize Version Language Attributes Sequence
+ # In this function, all components are created.
+ #
+ # $allfilecomponentsref is empty at the beginning
+
+ my $infoline;
+
+ my @allfiles = ();
+ my @filetable = ();
+ my @filehashtable = ();
+ my %allfilecomponents = ();
+ my $counter = 0;
+
+ if ( $^O =~ /cygwin/i ) { installer::worker::generate_cygwin_pathes($filesref); }
+
+ # The filenames must be collected because of uniqueness
+ # 01-44-~1.DAT, 01-44-~2.DAT, ...
+ # my @shortnames = ();
+ my %shortnames = ();
+
+ if ( $installer::globals::updatedatabase ) { collect_shortnames_from_old_database($uniquefilenamehashref, \%shortnames); }
+
+ installer::windows::idtglobal::write_idt_header(\@filetable, "file");
+ installer::windows::idtglobal::write_idt_header(\@filehashtable, "filehash");
+
+ for ( my $i = 0; $i <= $#{$filesref}; $i++ )
+ {
+ my %file = ();
+
+ my $onefile = ${$filesref}[$i];
+
+ my $styles = "";
+ if ( $onefile->{'Styles'} ) { $styles = $onefile->{'Styles'}; }
+ if (( $styles =~ /\bJAVAFILE\b/ ) && ( ! ($allvariables->{'JAVAPRODUCT'} ))) { next; }
+
+ $file{'Component_'} = get_file_component_name($onefile, $filesref);
+ $file{'File'} = generate_unique_filename_for_filetable($onefile, $file{'Component_'}, $uniquefilenamehashref);
+
+ $onefile->{'uniquename'} = $file{'File'};
+ $onefile->{'componentname'} = $file{'Component_'};
+
+ # Collecting all components
+ # if (!(installer::existence::exists_in_array($file{'Component_'}, $allfilecomponentsref))) { push(@{$allfilecomponentsref}, $file{'Component_'}); }
+
+ if ( ! exists($allfilecomponents{$file{'Component_'}}) ) { $allfilecomponents{$file{'Component_'}} = 1; }
+
+ $file{'FileName'} = generate_filename_for_filetable($onefile, \%shortnames, $uniquefilenamehashref);
+
+ $file{'FileSize'} = get_filesize($onefile);
+
+ $file{'Version'} = get_fileversion($onefile, $allvariables, $styles);
+
+ $file{'Language'} = get_language_for_file($onefile);
+
+ if ( $styles =~ /\bDONT_PACK\b/ ) { $file{'Attributes'} = "8192"; }
+ else { $file{'Attributes'} = "16384"; }
+
+ # $file{'Attributes'} = "16384"; # Sourcefile is packed
+ # $file{'Attributes'} = "8192"; # Sourcefile is unpacked
+
+ $installer::globals::insert_file_at_end = 0;
+ $counter++;
+ $file{'Sequence'} = get_sequence_for_file($counter, $onefile, \%file, $allupdatesequenceshashref, $allupdatecomponentshashref, $allupdatefileorderhashref, \%allfilecomponents);
+
+ $onefile->{'sequencenumber'} = $file{'Sequence'};
+
+ my $oneline = $file{'File'} . "\t" . $file{'Component_'} . "\t" . $file{'FileName'} . "\t"
+ . $file{'FileSize'} . "\t" . $file{'Version'} . "\t" . $file{'Language'} . "\t"
+ . $file{'Attributes'} . "\t" . $file{'Sequence'} . "\n";
+
+ push(@filetable, $oneline);
+
+ if ( ! $installer::globals::insert_file_at_end ) { push(@allfiles, $onefile); }
+
+ # Collecting all component conditions
+ if ( $onefile->{'ComponentCondition'} )
+ {
+ if ( ! exists($installer::globals::componentcondition{$file{'Component_'}}))
+ {
+ $installer::globals::componentcondition{$file{'Component_'}} = $onefile->{'ComponentCondition'};
+ }
+ }
+
+ # Collecting also all tree conditions for multilayer products
+ get_tree_condition_for_component($onefile, $file{'Component_'});
+
+ # Collecting all component names, that have flag VERSION_INDEPENDENT_COMP_ID
+ # This should be all components with constant API, for example URE
+ if ( $styles =~ /\bVERSION_INDEPENDENT_COMP_ID\b/ )
+ {
+ $installer::globals::base_independent_components{$onefile->{'componentname'}} = 1;
+ }
+
+ # Collecting all component ids, that are defined at files in scp project (should not be used anymore)
+ if ( $onefile->{'CompID'} )
+ {
+ if ( ! exists($installer::globals::componentid{$onefile->{'componentname'}}))
+ {
+ $installer::globals::componentid{$onefile->{'componentname'}} = $onefile->{'CompID'};
+ }
+ else
+ {
+ if ( $installer::globals::componentid{$onefile->{'componentname'}} ne $onefile->{'CompID'} )
+ {
+ installer::exiter::exit_program("ERROR: There is already a ComponentID for component \"$onefile->{'componentname'}\" : \"$installer::globals::componentid{$onefile->{'componentname'}}\" . File \"$onefile->{'gid'}\" uses \"$onefile->{'CompID'}\" !", "create_files_table");
+ }
+ }
+
+ # Also checking vice versa. Is this ComponentID already used? If yes, is the componentname the same?
+
+ if ( ! exists($installer::globals::comparecomponentname{$onefile->{'CompID'}}))
+ {
+ $installer::globals::comparecomponentname{$onefile->{'CompID'}} = $onefile->{'componentname'};
+ }
+ else
+ {
+ if ( $installer::globals::comparecomponentname{$onefile->{'CompID'}} ne $onefile->{'componentname'} )
+ {
+ installer::exiter::exit_program("ERROR: There is already a component for ComponentID \"$onefile->{'CompID'}\" : \"$installer::globals::comparecomponentname{$onefile->{'CompID'}}\" . File \"$onefile->{'gid'}\" has same component id but is included in component \"$onefile->{'componentname'}\" !", "create_files_table");
+ }
+ }
+ }
+
+ # Collecting all language specific conditions
+ # if ( $onefile->{'haslanguagemodule'} )
+ if ( $onefile->{'ismultilingual'} )
+ {
+ if ( $onefile->{'ComponentCondition'} ) { installer::exiter::exit_program("ERROR: Cannot set language condition. There is already another component condition for file $onefile->{'gid'}: \"$onefile->{'ComponentCondition'}\" !", "create_files_table"); }
+
+ if ( $onefile->{'specificlanguage'} eq "" ) { installer::exiter::exit_program("ERROR: There is no specific language for file at language module: $onefile->{'gid'} !", "create_files_table"); }
+ my $locallanguage = $onefile->{'specificlanguage'};
+ my $property = "IS" . $file{'Language'};
+ my $value = 1;
+ my $condition = $property . "=" . $value;
+
+ $onefile->{'ComponentCondition'} = $condition;
+
+ if ( exists($installer::globals::componentcondition{$file{'Component_'}}))
+ {
+ if ( $installer::globals::componentcondition{$file{'Component_'}} ne $condition ) { installer::exiter::exit_program("ERROR: There is already another component condition for file $onefile->{'gid'}: \"$installer::globals::componentcondition{$file{'Component_'}}\" and \"$condition\" !", "create_files_table"); }
+ }
+ else
+ {
+ $installer::globals::componentcondition{$file{'Component_'}} = $condition;
+ }
+
+ # collecting all properties for table Property
+ if ( ! exists($installer::globals::languageproperties{$property}) ) { $installer::globals::languageproperties{$property} = $value; }
+ }
+
+ if ( $installer::globals::prepare_winpatch )
+ {
+ my $path = $onefile->{'sourcepath'};
+ if ( $^O =~ /cygwin/i ) { $path = $onefile->{'cyg_sourcepath'}; }
+
+ open(FILE, $path) or die "ERROR: Can't open $path for creating file hash";
+ binmode(FILE);
+ my $hashinfo = pack("l", 20);
+ $hashinfo .= Digest::MD5->new->addfile(*FILE)->digest;
+
+ my @i = unpack ('x[l]l4', $hashinfo);
+ $oneline = $file{'File'} . "\t" .
+ "0" . "\t" .
+ $i[0] . "\t" .
+ $i[1] . "\t" .
+ $i[2] . "\t" .
+ $i[3] . "\n";
+ push (@filehashtable, $oneline);
+ }
+
+ # Saving the sequence number in a hash with uniquefilename as key.
+ # This is used for better performance in "save_packorder"
+ $installer::globals::uniquefilenamesequence{$onefile->{'uniquename'}} = $onefile->{'sequencenumber'};
+
+ # Special handling for files in PREDEFINED_OSSHELLNEWDIR. These components
+ # need as KeyPath a RegistryItem in HKCU
+ my $destdir = "";
+ if ( $onefile->{'Dir'} ) { $destdir = $onefile->{'Dir'}; }
+
+ if (( $destdir =~ /\bPREDEFINED_OSSHELLNEWDIR\b/ ) || ( $onefile->{'needs_user_registry_key'} ))
+ {
+ my $keypath = generate_registry_keypath($onefile);
+ $onefile->{'userregkeypath'} = $keypath;
+ push(@installer::globals::userregistrycollector, $onefile);
+ $installer::globals::addeduserregitrykeys = 1;
+ }
+ }
+
+ # putting content from %allfilecomponents to $allfilecomponentsref for later usage
+ foreach $localkey (keys %allfilecomponents ) { push( @{$allfilecomponentsref}, $localkey); }
+
+ my $filetablename = $basedir . $installer::globals::separator . "File.idt";
+ installer::files::save_file($filetablename ,\@filetable);
+ $infoline = "\nCreated idt file: $filetablename\n";
+ push(@installer::globals::logfileinfo, $infoline);
+
+ installer::logger::include_timestamp_into_logfile("Performance Info: File Table end");
+
+ my $filehashtablename = $basedir . $installer::globals::separator . "MsiFileHash.idt";
+ installer::files::save_file($filehashtablename ,\@filehashtable);
+ $infoline = "\nCreated idt file: $filehashtablename\n";
+ push(@installer::globals::logfileinfo, $infoline);
+
+ # Now the new files can be added to the files collector (only in update packaging processes)
+ if ( $installer::globals::newfilesexist )
+ {
+ foreach my $seq (sort keys %installer::globals::newfilescollector) { push(@allfiles, $installer::globals::newfilescollector{$seq}) }
+ }
+
+ return \@allfiles;
+}
+
+1;
diff --git a/solenv/bin/modules/installer/windows/font.pm b/solenv/bin/modules/installer/windows/font.pm
new file mode 100644
index 000000000000..9dab78caf5f7
--- /dev/null
+++ b/solenv/bin/modules/installer/windows/font.pm
@@ -0,0 +1,110 @@
+#*************************************************************************
+#
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+#
+# Copyright 2000, 2010 Oracle and/or its affiliates.
+#
+# OpenOffice.org - a multi-platform office productivity suite
+#
+# This file is part of OpenOffice.org.
+#
+# OpenOffice.org is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License version 3
+# only, as published by the Free Software Foundation.
+#
+# OpenOffice.org is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License version 3 for more details
+# (a copy is included in the LICENSE file that accompanied this code).
+#
+# You should have received a copy of the GNU Lesser General Public License
+# version 3 along with OpenOffice.org. If not, see
+# <http://www.openoffice.org/license.html>
+# for a copy of the LGPLv3 License.
+#
+#*************************************************************************
+
+package installer::windows::font;
+
+use installer::files;
+use installer::globals;
+use installer::windows::idtglobal;
+
+
+#################################################################################
+# Creating the file Font.idt dynamically
+# Content:
+# File_ FontTitle
+#################################################################################
+
+sub create_font_table
+{
+ my ($filesref, $basedir) = @_;
+
+ my @fonttable = ();
+
+ installer::windows::idtglobal::write_idt_header(\@fonttable, "font");
+
+ for ( my $i = 0; $i <= $#{$filesref}; $i++ )
+ {
+ my $onefile = ${$filesref}[$i];
+ my $styles = "";
+
+ if ( $onefile->{'Styles'} ) { $styles = $onefile->{'Styles'}; }
+
+ if ( $styles =~ /\bFONT\b/ )
+ {
+ my %font = ();
+
+ $font{'File_'} = $onefile->{'uniquename'};
+ # $font{'FontTitle'} = $onefile->{'FontName'}; # results in a warning during validation
+ $font{'FontTitle'} = "";
+
+ my $oneline = $font{'File_'} . "\t" . $font{'FontTitle'} . "\n";
+
+ push(@fonttable, $oneline);
+ }
+ }
+
+ # Saving the file
+
+ my $fonttablename = $basedir . $installer::globals::separator . "Font.idt";
+ installer::files::save_file($fonttablename ,\@fonttable);
+ my $infoline = "Created idt file: $fonttablename\n";
+ push(@installer::globals::logfileinfo, $infoline);
+
+}
+
+#################################################################################
+# Reading the Font version from the ttf file, to avoid installation
+# of older files over newer files.
+#################################################################################
+
+sub get_font_version
+{
+ my ( $fontfile ) = @_;
+
+ if ( ! -f $fontfile ) { installer::exiter::exit_program("ERROR: Font file does not exist: \"$fontfile\"", "get_font_version"); }
+
+ my $fontversion = 0;
+ my $infoline = "";
+
+ my $onefile = installer::files::read_binary_file($fontfile);
+
+ if ( $onefile =~ /Version\s+(\d+\.\d+\.*\d*)/ )
+ {
+ $fontversion = $1;
+ $infoline = "FONT: Font \"$fontfile\" version: $fontversion\n";
+ push(@installer::globals::logfileinfo, $infoline);
+ }
+ else
+ {
+ $infoline = "FONT: Could not determine font version: \"$fontfile\"\n";
+ push(@installer::globals::logfileinfo, $infoline);
+ }
+
+ return $fontversion;
+}
+
+1;
diff --git a/solenv/bin/modules/installer/windows/icon.pm b/solenv/bin/modules/installer/windows/icon.pm
new file mode 100644
index 000000000000..ac2fa9ea8cfa
--- /dev/null
+++ b/solenv/bin/modules/installer/windows/icon.pm
@@ -0,0 +1,77 @@
+#*************************************************************************
+#
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+#
+# Copyright 2000, 2010 Oracle and/or its affiliates.
+#
+# OpenOffice.org - a multi-platform office productivity suite
+#
+# This file is part of OpenOffice.org.
+#
+# OpenOffice.org is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License version 3
+# only, as published by the Free Software Foundation.
+#
+# OpenOffice.org is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License version 3 for more details
+# (a copy is included in the LICENSE file that accompanied this code).
+#
+# You should have received a copy of the GNU Lesser General Public License
+# version 3 along with OpenOffice.org. If not, see
+# <http://www.openoffice.org/license.html>
+# for a copy of the LGPLv3 License.
+#
+#*************************************************************************
+
+package installer::windows::icon;
+
+use installer::files;
+use installer::globals;
+use installer::pathanalyzer;
+use installer::windows::idtglobal;
+
+###########################################################################################################
+# Creating the file Icon.idt dynamically
+# Content:
+# Name Data
+###########################################################################################################
+
+sub create_icon_table
+{
+ my ($iconfilecollector, $basedir) = @_;
+
+ my @icontable = ();
+
+ installer::windows::idtglobal::write_idt_header(\@icontable, "icon");
+
+ # Only the iconfiles, that are used in the shortcut table for the
+ # FolderItems (entries in Windows startmenu) are added into the icon table.
+
+ for ( my $i = 0; $i <= $#{$iconfilecollector}; $i++ )
+ {
+ my $iconfile = ${$iconfilecollector}[$i];
+
+ installer::pathanalyzer::make_absolute_filename_to_relative_filename(\$iconfile);
+
+ my %icon = ();
+
+ $icon{'Name'} = $iconfile; # simply soffice.exe
+ $icon{'Data'} = $iconfile; # simply soffice.exe
+
+ my $oneline = $icon{'Name'} . "\t" . $icon{'Data'} . "\n";
+
+ push(@icontable, $oneline);
+ }
+
+ # Saving the file
+
+ my $icontablename = $basedir . $installer::globals::separator . "Icon.idt";
+ installer::files::save_file($icontablename ,\@icontable);
+ my $infoline = "Created idt file: $icontablename\n";
+ push(@installer::globals::logfileinfo, $infoline);
+
+}
+
+1;
diff --git a/solenv/bin/modules/installer/windows/idtglobal.pm b/solenv/bin/modules/installer/windows/idtglobal.pm
new file mode 100644
index 000000000000..64dc2f34a12a
--- /dev/null
+++ b/solenv/bin/modules/installer/windows/idtglobal.pm
@@ -0,0 +1,2423 @@
+#*************************************************************************
+#
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+#
+# Copyright 2000, 2010 Oracle and/or its affiliates.
+#
+# OpenOffice.org - a multi-platform office productivity suite
+#
+# This file is part of OpenOffice.org.
+#
+# OpenOffice.org is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License version 3
+# only, as published by the Free Software Foundation.
+#
+# OpenOffice.org is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License version 3 for more details
+# (a copy is included in the LICENSE file that accompanied this code).
+#
+# You should have received a copy of the GNU Lesser General Public License
+# version 3 along with OpenOffice.org. If not, see
+# <http://www.openoffice.org/license.html>
+# for a copy of the LGPLv3 License.
+#
+#*************************************************************************
+
+package installer::windows::idtglobal;
+
+use Cwd;
+use installer::converter;
+use installer::existence;
+use installer::exiter;
+use installer::files;
+use installer::globals;
+use installer::pathanalyzer;
+use installer::remover;
+use installer::scriptitems;
+use installer::systemactions;
+use installer::windows::language;
+
+##############################################################
+# Shorten the gid for a feature.
+# Attention: Maximum length is 38
+##############################################################
+
+sub shorten_feature_gid
+{
+ my ($stringref) = @_;
+
+ $$stringref =~ s/gid_Module_/gm_/;
+ $$stringref =~ s/_Root_/_r_/;
+ $$stringref =~ s/_Prg_/_p_/;
+ $$stringref =~ s/_Optional_/_o_/;
+ $$stringref =~ s/_Wrt_Flt_/_w_f_/;
+ $$stringref =~ s/_Javafilter_/_jf_/;
+}
+
+############################################
+# Getting the next free number, that
+# can be added.
+# Sample: 01-44-~1.DAT, 01-44-~2.DAT, ...
+############################################
+
+sub get_next_free_number
+{
+ my ($name, $shortnamesref) = @_;
+
+ my $counter = 0;
+ my $dontsave = 0;
+ my $alreadyexists;
+ my ($newname, $shortname);
+
+ do
+ {
+ $alreadyexists = 0;
+ $counter++;
+ $newname = $name . $counter;
+
+ for ( my $i = 0; $i <= $#{$shortnamesref}; $i++ )
+ {
+ $shortname = ${$shortnamesref}[$i];
+
+ if ( uc($shortname) eq uc($newname) ) # case insensitive
+ {
+ $alreadyexists = 1;
+ last;
+ }
+ }
+ }
+ until (!($alreadyexists));
+
+ if (( $counter > 9 ) && ( length($name) > 6 ))
+ {
+ $dontsave = 1;
+ }
+
+ if (!($dontsave))
+ {
+ push(@{$shortnamesref}, $newname); # adding the new shortname to the array of shortnames
+ }
+
+ return $counter
+}
+
+############################################
+# Getting the next free number, that
+# can be added.
+# Sample: 01-44-~1.DAT, 01-44-~2.DAT, ...
+############################################
+
+sub get_next_free_number_with_hash
+{
+ my ($name, $shortnamesref, $ext) = @_;
+
+ my $counter = 0;
+ my $dontsave = 0;
+ my $saved = 0;
+ my $alreadyexists;
+ my ($newname, $shortname);
+
+ do
+ {
+ $alreadyexists = 0;
+ $counter++;
+ $newname = $name . $counter;
+ $newname = uc($newname); # case insensitive, always upper case
+ if ( exists($shortnamesref->{$newname}) ||
+ exists($installer::globals::savedrev83mapping{$newname.$ext}) )
+ {
+ $alreadyexists = 1;
+ }
+ }
+ until (!($alreadyexists));
+
+ if (( $counter > 9 ) && ( length($name) > 6 )) { $dontsave = 1; }
+ if (( $counter > 99 ) && ( length($name) > 5 )) { $dontsave = 1; }
+
+ if (!($dontsave))
+ {
+ # push(@{$shortnamesref}, $newname); # adding the new shortname to the array of shortnames
+ $shortnamesref->{$newname} = 1; # adding the new shortname to the array of shortnames, always uppercase
+ $saved = 1;
+ }
+
+ return ( $counter, $saved )
+}
+
+#########################################
+# 8.3 for filenames and directories
+#########################################
+
+sub make_eight_three_conform
+{
+ my ($inputstring, $pattern, $shortnamesref) = @_;
+
+ # all shortnames are collected in $shortnamesref, because of uniqueness
+
+ my ($name, $namelength, $number);
+ my $conformstring = "";
+ my $changed = 0;
+
+ if (( $inputstring =~ /^\s*(.*?)\.(.*?)\s*$/ ) && ( $pattern eq "file" )) # files with a dot
+ {
+ $name = $1;
+ my $extension = $2;
+
+ $namelength = length($name);
+ my $extensionlength = length($extension);
+
+ if ( $extensionlength > 3 )
+ {
+ # simply taking the first three letters
+ $extension = substr($extension, 0, 3); # name, offset, length
+ }
+
+ # Attention: readme.html -> README~1.HTM
+
+ if (( $namelength > 8 ) || ( $extensionlength > 3 ))
+ {
+ # taking the first six letters
+ $name = substr($name, 0, 6); # name, offset, length
+ $name =~ s/\s*$//; # removing ending whitespaces
+ $name = $name . "\~";
+ $number = get_next_free_number($name, $shortnamesref);
+
+ # if $number>9 the new name would be "abcdef~10.xyz", which is 9+3, and therefore not allowed
+
+ if ( $number > 9 )
+ {
+ $name = substr($name, 0, 5); # name, offset, length
+ $name =~ s/\s*$//; # removing ending whitespaces
+ $name = $name . "\~";
+ $number = get_next_free_number($name, $shortnamesref);
+ }
+
+ $name = $name . "$number";
+
+ $changed = 1;
+ }
+
+ $conformstring = $name . "\." . $extension;
+
+ if ( $changed ) { $conformstring= uc($conformstring); }
+ }
+ else # no dot in filename or directory (also used for shortcuts)
+ {
+ $name = $inputstring;
+ $namelength = length($name);
+
+ if ( $namelength > 8 )
+ {
+ # taking the first six letters
+ $name = substr($name, 0, 6); # name, offset, length
+ $name =~ s/\s*$//; # removing ending whitespaces
+ $name = $name . "\~";
+ $number = get_next_free_number($name, $shortnamesref);
+
+ # if $number>9 the new name would be "abcdef~10.xyz", which is 9+3, and therefore not allowed
+
+ if ( $number > 9 )
+ {
+ $name = substr($name, 0, 5); # name, offset, length
+ $name =~ s/\s*$//; # removing ending whitespaces
+ $name = $name . "\~";
+ $number = get_next_free_number($name, $shortnamesref);
+ }
+
+ $name = $name . "$number";
+ $changed = 1;
+ if ( $pattern eq "dir" ) { $name =~ s/\./\_/g; } # in directories replacing "." with "_"
+ }
+
+ $conformstring = $name;
+
+ if ( $changed ) { $conformstring = uc($name); }
+ }
+
+ return $conformstring;
+}
+
+#########################################
+# 8.3 for filenames and directories
+# $shortnamesref is a hash in this case
+# -> performance reasons
+#########################################
+
+sub make_eight_three_conform_with_hash
+{
+ my ($inputstring, $pattern, $shortnamesref) = @_;
+
+ # all shortnames are collected in $shortnamesref, because of uniqueness (a hash!)
+
+ my ($name, $namelength, $number);
+ my $conformstring = "";
+ my $changed = 0;
+ my $saved;
+
+ # if (( $inputstring =~ /^\s*(.*?)\.(.*?)\s*$/ ) && ( $pattern eq "file" )) # files with a dot
+ if (( $inputstring =~ /^\s*(.*)\.(.*?)\s*$/ ) && ( $pattern eq "file" )) # files with a dot
+ {
+ # extension has to be non-greedy, but name is. This is important to find the last dot in the filename
+ $name = $1;
+ my $extension = $2;
+
+ if ( $name =~ /^\s*(.*?)\s*$/ ) { $name = $1; } # now the name is also non-greedy
+ $name =~ s/\.//g; # no dots in 8+3 conform filename
+
+ $namelength = length($name);
+ my $extensionlength = length($extension);
+
+ if ( $extensionlength > 3 )
+ {
+ # simply taking the first three letters
+ $extension = substr($extension, 0, 3); # name, offset, length
+ $changed = 1;
+ }
+
+ # Attention: readme.html -> README~1.HTM
+
+ if (( $namelength > 8 ) || ( $extensionlength > 3 ))
+ {
+ # taking the first six letters, if filename is longer than 6 characters
+ if ( $namelength > 6 )
+ {
+ $name = substr($name, 0, 6); # name, offset, length
+ $name =~ s/\s*$//; # removing ending whitespaces
+ $name = $name . "\~";
+ ($number, $saved) = get_next_free_number_with_hash($name, $shortnamesref, '.'.uc($extension));
+
+ # if $number>9 the new name would be "abcdef~10.xyz", which is 9+3, and therefore not allowed
+
+ if ( ! $saved )
+ {
+ $name = substr($name, 0, 5); # name, offset, length
+ $name =~ s/\s*$//; # removing ending whitespaces
+ $name = $name . "\~";
+ ($number, $saved) = get_next_free_number_with_hash($name, $shortnamesref, '.'.uc($extension));
+
+ # if $number>99 the new name would be "abcde~100.xyz", which is 9+3, and therefore not allowed
+
+ if ( ! $saved )
+ {
+ $name = substr($name, 0, 4); # name, offset, length
+ $name =~ s/\s*$//; # removing ending whitespaces
+ $name = $name . "\~";
+ ($number, $saved) = get_next_free_number_with_hash($name, $shortnamesref, '.'.uc($extension));
+
+ if ( ! $saved )
+ {
+ installer::exiter::exit_program("ERROR: Could not set 8+3 conform name for $inputstring !", "make_eight_three_conform_with_hash");
+ }
+ }
+ }
+
+ $name = $name . "$number";
+ $changed = 1;
+ }
+ }
+
+ $conformstring = $name . "\." . $extension;
+
+ if ( $changed ) { $conformstring= uc($conformstring); }
+ }
+ else # no dot in filename or directory (also used for shortcuts)
+ {
+ $name = $inputstring;
+ $namelength = length($name);
+
+ if ( $namelength > 8 )
+ {
+ # taking the first six letters
+ $name = substr($name, 0, 6); # name, offset, length
+ $name =~ s/\s*$//; # removing ending whitespaces
+ $name = $name . "\~";
+ ( $number, $saved ) = get_next_free_number_with_hash($name, $shortnamesref, '');
+
+ # if $number>9 the new name would be "abcdef~10", which is 9+0, and therefore not allowed
+
+ if ( ! $saved )
+ {
+ $name = substr($name, 0, 5); # name, offset, length
+ $name =~ s/\s*$//; # removing ending whitespaces
+ $name = $name . "\~";
+ ( $number, $saved ) = get_next_free_number_with_hash($name, $shortnamesref, '');
+
+ # if $number>99 the new name would be "abcde~100", which is 9+0, and therefore not allowed
+
+ if ( ! $saved )
+ {
+ $name = substr($name, 0, 4); # name, offset, length
+ $name =~ s/\s*$//; # removing ending whitespaces
+ $name = $name . "\~";
+ ( $number, $saved ) = get_next_free_number_with_hash($name, $shortnamesref, '');
+
+ if ( ! $saved ) { installer::exiter::exit_program("ERROR: Could not set 8+3 conform name for $inputstring !", "make_eight_three_conform_with_hash"); }
+ }
+ }
+
+ $name = $name . "$number";
+ $changed = 1;
+ if ( $pattern eq "dir" ) { $name =~ s/\./\_/g; } # in directories replacing "." with "_"
+ }
+
+ $conformstring = $name;
+
+ if ( $changed ) { $conformstring = uc($name); }
+ }
+
+ return $conformstring;
+}
+
+#########################################
+# Writing the header for idt files
+#########################################
+
+sub write_idt_header
+{
+ my ($idtref, $definestring) = @_;
+
+ my $oneline;
+
+ if ( $definestring eq "file" )
+ {
+ $oneline = "File\tComponent_\tFileName\tFileSize\tVersion\tLanguage\tAttributes\tSequence\n";
+ push(@{$idtref}, $oneline);
+ $oneline = "s72\ts72\tl255\ti4\tS72\tS20\tI2\ti2\n";
+ push(@{$idtref}, $oneline);
+ $oneline = "File\tFile\n";
+ push(@{$idtref}, $oneline);
+ }
+
+ if ( $definestring eq "filehash" )
+ {
+ $oneline = "File_\tOptions\tHashPart1\tHashPart2\tHashPart3\tHashPart4\n";
+ push(@{$idtref}, $oneline);
+ $oneline = "s72\ti2\ti4\ti4\ti4\ti4\n";
+ push(@{$idtref}, $oneline);
+ $oneline = "MsiFileHash\tFile_\n";
+ push(@{$idtref}, $oneline);
+ }
+
+ if ( $definestring eq "directory" )
+ {
+ $oneline = "Directory\tDirectory_Parent\tDefaultDir\n";
+ push(@{$idtref}, $oneline);
+ $oneline = "s72\tS72\tl255\n";
+ push(@{$idtref}, $oneline);
+ $oneline = "Directory\tDirectory\n";
+ push(@{$idtref}, $oneline);
+ }
+
+ if ( $definestring eq "component" )
+ {
+ $oneline = "Component\tComponentId\tDirectory_\tAttributes\tCondition\tKeyPath\n";
+ push(@{$idtref}, $oneline);
+ $oneline = "s72\tS38\ts72\ti2\tS255\tS72\n";
+ push(@{$idtref}, $oneline);
+ $oneline = "Component\tComponent\n";
+ push(@{$idtref}, $oneline);
+ }
+
+ if ( $definestring eq "feature" )
+ {
+ $oneline = "Feature\tFeature_Parent\tTitle\tDescription\tDisplay\tLevel\tDirectory_\tAttributes\n";
+ push(@{$idtref}, $oneline);
+ $oneline = "s38\tS38\tL64\tL255\tI2\ti2\tS72\ti2\n";
+ push(@{$idtref}, $oneline);
+ $oneline = "WINDOWSENCODINGTEMPLATE\tFeature\tFeature\n";
+ push(@{$idtref}, $oneline);
+ }
+
+ if ( $definestring eq "featurecomponent" )
+ {
+ $oneline = "Feature_\tComponent_\n";
+ push(@{$idtref}, $oneline);
+ $oneline = "s38\ts72\n";
+ push(@{$idtref}, $oneline);
+ $oneline = "FeatureComponents\tFeature_\tComponent_\n";
+ push(@{$idtref}, $oneline);
+ }
+
+ if ( $definestring eq "media" )
+ {
+ $oneline = "DiskId\tLastSequence\tDiskPrompt\tCabinet\tVolumeLabel\tSource\n";
+ push(@{$idtref}, $oneline);
+ $oneline = "i2\ti2\tL64\tS255\tS32\tS72\n";
+ push(@{$idtref}, $oneline);
+ $oneline = "Media\tDiskId\n";
+ push(@{$idtref}, $oneline);
+ }
+
+ if ( $definestring eq "font" )
+ {
+ $oneline = "File_\tFontTitle\n";
+ push(@{$idtref}, $oneline);
+ $oneline = "s72\tS128\n";
+ push(@{$idtref}, $oneline);
+ $oneline = "Font\tFile_\n";
+ push(@{$idtref}, $oneline);
+ }
+
+ if ( $definestring eq "shortcut" )
+ {
+ $oneline = "Shortcut\tDirectory_\tName\tComponent_\tTarget\tArguments\tDescription\tHotkey\tIcon_\tIconIndex\tShowCmd\tWkDir\n";
+ push(@{$idtref}, $oneline);
+ $oneline = "s72\ts72\tl128\ts72\ts72\tS255\tL255\tI2\tS72\tI2\tI2\tS72\n";
+ push(@{$idtref}, $oneline);
+ $oneline = "WINDOWSENCODINGTEMPLATE\tShortcut\tShortcut\n";
+ push(@{$idtref}, $oneline);
+ }
+
+ if ( $definestring eq "registry" )
+ {
+ $oneline = "Registry\tRoot\tKey\tName\tValue\tComponent_\n";
+ push(@{$idtref}, $oneline);
+ $oneline = "s72\ti2\tl255\tL255\tL0\ts72\n";
+ push(@{$idtref}, $oneline);
+ $oneline = "Registry\tRegistry\n";
+ push(@{$idtref}, $oneline);
+ }
+
+ if ( $definestring eq "reg64" )
+ {
+ $oneline = "Registry\tRoot\tKey\tName\tValue\tComponent_\n";
+ push(@{$idtref}, $oneline);
+ $oneline = "s72\ti2\tl255\tL255\tL0\ts72\n";
+ push(@{$idtref}, $oneline);
+ $oneline = "Reg64\tRegistry\n";
+ push(@{$idtref}, $oneline);
+ }
+
+ if ( $definestring eq "createfolder" )
+ {
+ $oneline = "Directory_\tComponent_\n";
+ push(@{$idtref}, $oneline);
+ $oneline = "s72\ts72\n";
+ push(@{$idtref}, $oneline);
+ $oneline = "CreateFolder\tDirectory_\tComponent_\n";
+ push(@{$idtref}, $oneline);
+ }
+
+ if ( $definestring eq "removefile" )
+ {
+ $oneline = "FileKey\tComponent_\tFileName\tDirProperty\tInstallMode\n";
+ push(@{$idtref}, $oneline);
+ $oneline = "s72\ts72\tL255\ts72\ti2\n";
+ push(@{$idtref}, $oneline);
+ $oneline = "RemoveFile\tFileKey\n";
+ push(@{$idtref}, $oneline);
+ }
+
+ if ( $definestring eq "upgrade" )
+ {
+ $oneline = "UpgradeCode\tVersionMin\tVersionMax\tLanguage\tAttributes\tRemove\tActionProperty\n";
+ push(@{$idtref}, $oneline);
+ $oneline = "s38\tS20\tS20\tS255\ti4\tS255\ts72\n";
+ push(@{$idtref}, $oneline);
+ $oneline = "Upgrade\tUpgradeCode\tVersionMin\tVersionMax\tLanguage\tAttributes\n";
+ push(@{$idtref}, $oneline);
+ }
+
+ if ( $definestring eq "icon" )
+ {
+ $oneline = "Name\tData\n";
+ push(@{$idtref}, $oneline);
+ $oneline = "s72\tv0\n";
+ push(@{$idtref}, $oneline);
+ $oneline = "Icon\tName\n";
+ push(@{$idtref}, $oneline);
+ }
+
+ if ( $definestring eq "inifile" )
+ {
+ $oneline = "IniFile\tFileName\tDirProperty\tSection\tKey\tValue\tAction\tComponent_\n";
+ push(@{$idtref}, $oneline);
+ $oneline = "s72\tl255\tS72\tl96\tl128\tl255\ti2\ts72\n";
+ push(@{$idtref}, $oneline);
+ $oneline = "IniFile\tIniFile\n";
+ push(@{$idtref}, $oneline);
+ }
+
+ if ( $definestring eq "selfreg" )
+ {
+ $oneline = "File_\tCost\n";
+ push(@{$idtref}, $oneline);
+ $oneline = "s72\tI2\n";
+ push(@{$idtref}, $oneline);
+ $oneline = "SelfReg\tFile_\n";
+ push(@{$idtref}, $oneline);
+ }
+
+ if ( $definestring eq "msiassembly" )
+ {
+ $oneline = "Component_\tFeature_\tFile_Manifest\tFile_Application\tAttributes\n";
+ push(@{$idtref}, $oneline);
+ $oneline = "s72\ts38\tS72\tS72\tI2\n";
+ push(@{$idtref}, $oneline);
+ $oneline = "MsiAssembly\tComponent_\n";
+ push(@{$idtref}, $oneline);
+ }
+
+ if ( $definestring eq "msiassemblyname" )
+ {
+ $oneline = "Component_\tName\tValue\n";
+ push(@{$idtref}, $oneline);
+ $oneline = "s72\ts255\ts255\n";
+ push(@{$idtref}, $oneline);
+ $oneline = "MsiAssemblyName\tComponent_\tName\n";
+ push(@{$idtref}, $oneline);
+ }
+
+ if ( $definestring eq "appsearch" )
+ {
+ $oneline = "Property\tSignature_\n";
+ push(@{$idtref}, $oneline);
+ $oneline = "s72\ts72\n";
+ push(@{$idtref}, $oneline);
+ $oneline = "AppSearch\tProperty\tSignature_\n";
+ push(@{$idtref}, $oneline);
+ }
+
+ if ( $definestring eq "reglocat" )
+ {
+ $oneline = "Signature_\tRoot\tKey\tName\tType\n";
+ push(@{$idtref}, $oneline);
+ $oneline = "s72\ti2\ts255\tS255\tI2\n";
+ push(@{$idtref}, $oneline);
+ $oneline = "RegLocator\tSignature_\n";
+ push(@{$idtref}, $oneline);
+ }
+
+ if ( $definestring eq "signatur" )
+ {
+ $oneline = "Signature\tFileName\tMinVersion\tMaxVersion\tMinSize\tMaxSize\tMinDate\tMaxDate\tLanguages\n";
+ push(@{$idtref}, $oneline);
+ $oneline = "s72\ts255\tS20\tS20\tI4\tI4\tI4\tI4\tS255\n";
+ push(@{$idtref}, $oneline);
+ $oneline = "Signature\tSignature\n";
+ push(@{$idtref}, $oneline);
+ }
+
+}
+
+##############################################################
+# Returning the name of the rranslation file for a
+# given language.
+# Sample: "01" oder "en-US" -> "1033.txt"
+##############################################################
+
+sub get_languagefilename
+{
+ my ($idtfilename, $basedir) = @_;
+
+ # $idtfilename =~ s/\.idt/\.ulf/;
+ $idtfilename =~ s/\.idt/\.mlf/;
+
+ my $languagefilename = $basedir . $installer::globals::separator . $idtfilename;
+
+ return $languagefilename;
+}
+
+##############################################################
+# Returning the complete block in all languages
+# for a specified string
+##############################################################
+
+sub get_language_block_from_language_file
+{
+ my ($searchstring, $languagefile) = @_;
+
+ my @language_block = ();
+
+ for ( my $i = 0; $i <= $#{$languagefile}; $i++ )
+ {
+ if ( ${$languagefile}[$i] =~ /^\s*\[\s*$searchstring\s*\]\s*$/ )
+ {
+ my $counter = $i;
+
+ push(@language_block, ${$languagefile}[$counter]);
+ $counter++;
+
+ while (( $counter <= $#{$languagefile} ) && (!( ${$languagefile}[$counter] =~ /^\s*\[/ )))
+ {
+ push(@language_block, ${$languagefile}[$counter]);
+ $counter++;
+ }
+
+ last;
+ }
+ }
+
+ return \@language_block;
+}
+
+##############################################################
+# Returning a specific language string from the block
+# of all translations
+##############################################################
+
+sub get_language_string_from_language_block
+{
+ my ($language_block, $language, $oldstring) = @_;
+
+ my $newstring = "";
+
+ for ( my $i = 0; $i <= $#{$language_block}; $i++ )
+ {
+ if ( ${$language_block}[$i] =~ /^\s*$language\s*\=\s*\"(.*)\"\s*$/ )
+ {
+ $newstring = $1;
+ last;
+ }
+ }
+
+ if ( $newstring eq "" )
+ {
+ $language = "en-US"; # defaulting to english
+
+ for ( my $i = 0; $i <= $#{$language_block}; $i++ )
+ {
+ if ( ${$language_block}[$i] =~ /^\s*$language\s*\=\s*\"(.*)\"\s*$/ )
+ {
+ $newstring = $1;
+ last;
+ }
+ }
+ }
+
+ return $newstring;
+}
+
+##############################################################
+# Returning a specific code from the block
+# of all codes. No defaulting to english!
+##############################################################
+
+sub get_code_from_code_block
+{
+ my ($codeblock, $language) = @_;
+
+ my $newstring = "";
+
+ for ( my $i = 0; $i <= $#{$codeblock}; $i++ )
+ {
+ if ( ${$codeblock}[$i] =~ /^\s*$language\s*\=\s*\"(.*)\"\s*$/ )
+ {
+ $newstring = $1;
+ last;
+ }
+ }
+
+ return $newstring;
+}
+
+##############################################################
+# Translating an idt file
+##############################################################
+
+sub translate_idtfile
+{
+ my ($idtfile, $languagefile, $onelanguage) = @_;
+
+ for ( my $i = 0; $i <= $#{$idtfile}; $i++ )
+ {
+ my @allstrings = ();
+
+ my $oneline = ${$idtfile}[$i];
+
+ while ( $oneline =~ /\b(OOO_\w+)\b/ )
+ {
+ my $replacestring = $1;
+ push(@allstrings, $replacestring);
+ $oneline =~ s/$replacestring//;
+ }
+
+ my $oldstring;
+
+ foreach $oldstring (@allstrings)
+ {
+ my $language_block = get_language_block_from_language_file($oldstring, $languagefile);
+ my $newstring = get_language_string_from_language_block($language_block, $onelanguage, $oldstring);
+
+ # if (!( $newstring eq "" )) { ${$idtfile}[$i] =~ s/$oldstring/$newstring/; }
+ ${$idtfile}[$i] =~ s/$oldstring/$newstring/; # always substitute, even if $newstring eq "" (there are empty strings for control.idt)
+ }
+ }
+}
+
+##############################################################
+# Copying all needed files to create a msi database
+# into one language specific directory
+##############################################################
+
+sub prepare_language_idt_directory
+{
+ my ($destinationdir, $newidtdir, $onelanguage, $filesref, $iconfilecollector, $binarytablefiles, $allvariables) = @_;
+
+ # Copying all idt-files from the source $installer::globals::idttemplatepath to the destination $destinationdir
+ # Copying all files in the subdirectory "Binary"
+ # Copying all files in the subdirectory "Icon"
+
+ my $infoline = "";
+
+ installer::systemactions::copy_directory($installer::globals::idttemplatepath, $destinationdir);
+
+ if ( -d $installer::globals::idttemplatepath . $installer::globals::separator . "Binary")
+ {
+ installer::systemactions::create_directory($destinationdir . $installer::globals::separator . "Binary");
+ installer::systemactions::copy_directory($installer::globals::idttemplatepath . $installer::globals::separator . "Binary", $destinationdir . $installer::globals::separator . "Binary");
+
+ if ((( $installer::globals::patch ) && ( $allvariables->{'WINDOWSPATCHBITMAPDIRECTORY'} )) || ( $allvariables->{'WINDOWSBITMAPDIRECTORY'} ))
+ {
+ my $bitmapdir = "";
+ if ( $allvariables->{'WINDOWSPATCHBITMAPDIRECTORY'} ) { $bitmapdir = $allvariables->{'WINDOWSPATCHBITMAPDIRECTORY'}; }
+ if ( $allvariables->{'WINDOWSBITMAPDIRECTORY'} ) { $bitmapdir = $allvariables->{'WINDOWSBITMAPDIRECTORY'}; }
+
+ my $newsourcedir = $installer::globals::unpackpath . $installer::globals::separator . $bitmapdir; # path setting in list file dependent from unpackpath !?
+ $infoline = "\nOverwriting files in directory \"" . $destinationdir . $installer::globals::separator . "Binary" . "\" with files from directory \"" . $newsourcedir . "\".\n";
+ push( @installer::globals::logfileinfo, $infoline);
+ if ( ! -d $newsourcedir )
+ {
+ my $currentdir = cwd();
+ installer::exiter::exit_program("ERROR: Directory $newsourcedir does not exist! Current directory is: $currentdir", "prepare_language_idt_directory");
+ }
+ installer::systemactions::copy_directory($newsourcedir, $destinationdir . $installer::globals::separator . "Binary");
+ }
+ }
+
+ installer::systemactions::create_directory($destinationdir . $installer::globals::separator . "Icon");
+
+ if ( -d $installer::globals::idttemplatepath . $installer::globals::separator . "Icon")
+ {
+ installer::systemactions::copy_directory($installer::globals::idttemplatepath . $installer::globals::separator . "Icon", $destinationdir . $installer::globals::separator . "Icon");
+ }
+
+ # Copying all files in $iconfilecollector, that describe icons of folderitems
+
+ for ( my $i = 0; $i <= $#{$iconfilecollector}; $i++ )
+ {
+ my $iconfilename = ${$iconfilecollector}[$i];
+ installer::pathanalyzer::make_absolute_filename_to_relative_filename(\$iconfilename);
+ installer::systemactions::copy_one_file(${$iconfilecollector}[$i], $destinationdir . $installer::globals::separator . "Icon" . $installer::globals::separator . $iconfilename);
+ }
+
+ # Copying all files in $binarytablefiles in the binary directory
+
+ for ( my $i = 0; $i <= $#{$binarytablefiles}; $i++ )
+ {
+ my $binaryfile = ${$binarytablefiles}[$i];
+ my $binaryfilepath = $binaryfile->{'sourcepath'};
+ my $binaryfilename = $binaryfilepath;
+ installer::pathanalyzer::make_absolute_filename_to_relative_filename(\$binaryfilename);
+ installer::systemactions::copy_one_file($binaryfilepath, $destinationdir . $installer::globals::separator . "Binary" . $installer::globals::separator . $binaryfilename);
+ }
+
+ # Copying all new created and language independent idt-files to the destination $destinationdir.
+ # Example: "File.idt"
+
+ installer::systemactions::copy_directory_with_fileextension($newidtdir, $destinationdir, "idt");
+
+ # Copying all new created and language dependent idt-files to the destination $destinationdir.
+ # Example: "Feature.idt.01"
+
+ installer::systemactions::copy_directory_with_fileextension($newidtdir, $destinationdir, $onelanguage);
+ installer::systemactions::rename_files_with_fileextension($destinationdir, $onelanguage);
+
+}
+
+##############################################################
+# Returning the source path of the rtf licensefile for
+# a specified language
+##############################################################
+
+sub get_rtflicensefilesource
+{
+ my ($language, $includepatharrayref) = @_;
+
+ my $licensefilename = "license_" . $language . ".rtf";
+
+ my $sourcefileref = installer::scriptitems::get_sourcepath_from_filename_and_includepath(\$licensefilename, $includepatharrayref, 1);
+
+ if ($$sourcefileref eq "") { installer::exiter::exit_program("ERROR: Could not find $licensefilename!", "get_rtflicensefilesource"); }
+
+ my $infoline = "Using licensefile: $$sourcefileref\n";
+ push( @installer::globals::logfileinfo, $infoline);
+
+ return $$sourcefileref;
+}
+
+##############################################################
+# Returning the source path of the licensefile for
+# a specified language
+##############################################################
+
+sub get_licensefilesource
+{
+ my ($language, $filesref) = @_;
+
+ my $licensefilename = "license_" . $language . ".txt";
+ my $sourcepath = "";
+ my $foundlicensefile = 0;
+
+ for ( my $i = 0; $i <= $#{$filesref}; $i++ )
+ {
+ my $onefile = ${$filesref}[$i];
+ my $filename = $onefile->{'Name'};
+
+ if ($filename eq $licensefilename)
+ {
+ $sourcepath = $onefile->{'sourcepath'};
+ $foundlicensefile = 1;
+ last;
+ }
+ }
+
+ if ( ! $foundlicensefile ) { installer::exiter::exit_program("ERROR: Did not find file $licensefilename in file collector!", "get_licensefilesource"); }
+
+ return $sourcepath;
+}
+
+##############################################################
+# A simple converter to create the license text
+# in rtf format
+##############################################################
+
+sub get_rtf_licensetext
+{
+ my ($licensefile) = @_;
+
+ # A very simple rtf converter
+
+ # The static header
+
+ my $rtf_licensetext = '{\rtf1\ansi\deff0';
+ $rtf_licensetext = $rtf_licensetext . '{\fonttbl{\f0\froman\fprq2\fcharset0 Times New Roman;}}';
+ $rtf_licensetext = $rtf_licensetext . '{\colortbl\red0\green0\blue0;\red255\green255\blue255;\red128\green128\blue128;}';
+ $rtf_licensetext = $rtf_licensetext . '{\stylesheet{\s1\snext1 Standard;}}';
+ $rtf_licensetext = $rtf_licensetext . '{\info{\comment StarWriter}{\vern5690}}\deftab709';
+ $rtf_licensetext = $rtf_licensetext . '{\*\pgdsctbl';
+ $rtf_licensetext = $rtf_licensetext . '{\pgdsc0\pgdscuse195\pgwsxn11905\pghsxn16837\marglsxn1134\margrsxn1134\margtsxn1134\margbsxn1134\pgdscnxt0 Standard;}}';
+ $rtf_licensetext = $rtf_licensetext . '\paperh16837\paperw11905\margl1134\margr1134\margt1134\margb1134\sectd\sbknone\pgwsxn11905\pghsxn16837\marglsxn1134\margrsxn1134\margtsxn1134\margbsxn1134\ftnbj\ftnstart1\ftnrstcont\ftnnar\aenddoc\aftnrstcont\aftnstart1\aftnnrlc';
+ $rtf_licensetext = $rtf_licensetext . '\pard\plain \s1';
+
+ for ( my $i = 0; $i <= $#{$licensefile}; $i++ )
+ {
+ my $oneline = ${$licensefile}[$i];
+ # if ( $oneline =~ /^\s*$/ ) { $oneline = '\par'; } # empty lines
+
+ if ( $i == 0 ) { $oneline =~ s/^\W*//; }
+
+ $oneline =~ s/\t/ /g; # no tabs allowed, converting to four spaces
+ $oneline =~ s/\n$//g; # no newline at line end
+
+# $oneline =~ s/ä/\\\'e4/g; # converting "ä"
+# $oneline =~ s/ö/\\\'f6/g; # converting "ö"
+# $oneline =~ s/ü/\\\'fc/g; # converting "ü"
+# $oneline =~ s/ß/\\\'df/g; # converting "ß"
+
+ # german replacements
+
+ $oneline =~ s/\Ã\„/\\\'c4/g; # converting "Ä"
+ $oneline =~ s/\Ã\–/\\\'d6/g; # converting "Ö"
+ $oneline =~ s/\Ã\œ/\\\'dc/g; # converting "Ü"
+ $oneline =~ s/\Ã\¤/\\\'e4/g; # converting "ä"
+ $oneline =~ s/\Ã\¶/\\\'f6/g; # converting "ö"
+ $oneline =~ s/\Ã\¼/\\\'fc/g; # converting "ü"
+ $oneline =~ s/\Ã\Ÿ/\\\'df/g; # converting "ß"
+
+ # french replacements
+
+ $oneline =~ s/\Ã\‰/\\\'c9/g;
+ $oneline =~ s/\Ã\€/\\\'c0/g;
+ $oneline =~ s/\Â\«/\\\'ab/g;
+ $oneline =~ s/\Â\»/\\\'bb/g;
+ $oneline =~ s/\Ã\©/\\\'e9/g;
+ $oneline =~ s/\Ã\¨/\\\'e8/g;
+ $oneline =~ s/\Ã\ /\\\'e0/g;
+ $oneline =~ s/\Ã\´/\\\'f4/g;
+ $oneline =~ s/\Ã\§/\\\'e7/g;
+ $oneline =~ s/\Ã\ª/\\\'ea/g;
+ $oneline =~ s/\Ã\Š/\\\'ca/g;
+ $oneline =~ s/\Ã\»/\\\'fb/g;
+ $oneline =~ s/\Ã\¹/\\\'f9/g;
+ $oneline =~ s/\Ã\®/\\\'ee/g;
+
+ # quotation marks
+
+ $oneline =~ s/\â\€\ž/\\\'84/g;
+ $oneline =~ s/\â\€\œ/\\ldblquote/g;
+ $oneline =~ s/\â\€\™/\\rquote/g;
+
+
+ $oneline =~ s/\Â\ /\\\~/g;
+
+ $oneline = '\par ' . $oneline;
+
+ $rtf_licensetext = $rtf_licensetext . $oneline;
+ }
+
+ # and the end
+
+ $rtf_licensetext = $rtf_licensetext . '\par \par }';
+
+ return $rtf_licensetext;
+}
+
+##############################################################
+# A simple converter to create a license txt string from
+# the rtf format
+##############################################################
+
+sub make_string_licensetext
+{
+ my ($licensefile) = @_;
+
+ my $rtf_licensetext = "";
+
+ for ( my $i = 0; $i <= $#{$licensefile}; $i++ )
+ {
+ my $oneline = ${$licensefile}[$i];
+ $oneline =~ s/\s*$//g; # no whitespace at line end
+
+ $rtf_licensetext = $rtf_licensetext . $oneline . " ";
+ }
+
+ return $rtf_licensetext;
+}
+
+##############################################################
+# Setting the path, where the soffice.exe is installed, into
+# the CustomAction table
+##############################################################
+
+sub add_officedir_to_database
+{
+ my ($basedir, $allvariables) = @_;
+
+ my $customactionfilename = $basedir . $installer::globals::separator . "CustomAc.idt";
+
+ my $customacfile = installer::files::read_file($customactionfilename);
+
+ my $found = 0;
+
+ # Updating the values
+
+ if ( $installer::globals::officeinstalldirectoryset )
+ {
+ $found = 0;
+
+ for ( my $i = 0; $i <= $#{$customacfile}; $i++ )
+ {
+ if ( ${$customacfile}[$i] =~ /\bOFFICEDIRECTORYGID\b/ )
+ {
+ ${$customacfile}[$i] =~ s/\bOFFICEDIRECTORYGID\b/$installer::globals::officeinstalldirectory/;
+ $found = 1;
+ }
+ }
+
+ if (( ! $found ) && ( ! $allvariables->{'IGNOREDIRECTORYLAYER'} ))
+ {
+ installer::exiter::exit_program("ERROR: \"OFFICEDIRECTORYGID\" not found in \"$customactionfilename\" !", "add_officedir_to_database");
+ }
+ }
+
+ # Saving the file
+
+ installer::files::save_file($customactionfilename ,$customacfile);
+ my $infoline = "Updated idt file: $customactionfilename\n";
+ push(@installer::globals::logfileinfo, $infoline);
+
+}
+
+##############################################################
+# Including the license text into the table control.idt
+##############################################################
+
+sub add_licensefile_to_database
+{
+ my ($licensefile, $controltable) = @_;
+
+ # Nine tabs before the license text and two tabs after it
+ # The license text has to be included into the dialog
+ # LicenseAgreement into the control Memo.
+
+ my $foundlicenseline = 0;
+ my ($number, $line);
+
+ for ( my $i = 0; $i <= $#{$controltable}; $i++ )
+ {
+ $line = ${$controltable}[$i];
+
+ if ( $line =~ /^\s*\bLicenseAgreement\b\t\bMemo\t/ )
+ {
+ $foundlicenseline = 1;
+ $number = $i;
+ last;
+ }
+ }
+
+ if (!($foundlicenseline))
+ {
+ installer::exiter::exit_program("ERROR: Line for license file in Control.idt not found!", "add_licensefile_to_database");
+ }
+ else
+ {
+ my %control = ();
+
+ if ( $line =~ /^\s*(.*?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\s*$/ )
+ {
+ $control{'Dialog_'} = $1;
+ $control{'Control'} = $2;
+ $control{'Type'} = $3;
+ $control{'X'} = $4;
+ $control{'Y'} = $5;
+ $control{'Width'} = $6;
+ $control{'Height'} = $7;
+ $control{'Attributes'} = $8;
+ $control{'Property'} = $9;
+ $control{'Text'} = $10;
+ $control{'Control_Next'} = $11;
+ $control{'Help'} = $12;
+ }
+ else
+ {
+ installer::exiter::exit_program("ERROR: Could not split line correctly!", "add_licensefile_to_database");
+ }
+
+ # my $licensetext = get_rtf_licensetext($licensefile);
+ my $licensetext = make_string_licensetext($licensefile);
+
+ $control{'Text'} = $licensetext;
+
+ my $newline = $control{'Dialog_'} . "\t" . $control{'Control'} . "\t" . $control{'Type'} . "\t" .
+ $control{'X'} . "\t" . $control{'Y'} . "\t" . $control{'Width'} . "\t" .
+ $control{'Height'} . "\t" . $control{'Attributes'} . "\t" . $control{'Property'} . "\t" .
+ $control{'Text'} . "\t" . $control{'Control_Next'} . "\t" . $control{'Help'} . "\n";
+
+ ${$controltable}[$number] = $newline
+ }
+}
+
+################################################################################################
+# Including the checkboxes for the language selection dialog
+# into the table control.idt . This is only relevant for
+# multilingual installation sets.
+#
+# old:
+# LanguageSelection CheckBox1 CheckBox 22 60 15 24 3 IS1033 CheckBox2
+# LanguageSelection Text1 Text 40 60 70 15 65539 OOO_CONTROL_LANG_1033
+# LanguageSelection CheckBox2 CheckBox 22 90 15 24 3 IS1031 Next
+# LanguageSelection Text2 Text 40 90 70 15 65539 OOO_CONTROL_LANG_1031
+# new:
+# LanguageSelection CheckBox1 CheckBox 22 60 15 24 3 IS1033 Text CheckBox2
+# LanguageSelection CheckBox2 CheckBox 22 90 15 24 3 IS1031 Text Next
+################################################################################################
+
+sub add_language_checkboxes_to_database
+{
+ my ($controltable, $languagesarrayref) = @_;
+
+ # for each language, two lines have to be inserted
+
+ for ( my $i = 0; $i <= $#{$languagesarrayref}; $i++ )
+ {
+ my $last = 0;
+ if ( $i == $#{$languagesarrayref} ) { $last = 1; } # special handling for the last
+
+ my $onelanguage = ${$languagesarrayref}[$i];
+ my $windowslanguage = installer::windows::language::get_windows_language($onelanguage);
+
+ # my $is_english = 0;
+ # if ( $windowslanguage eq "1033" ) { $is_english = 1; }
+
+ my $checkboxattribute = "3";
+ # if ( $is_english ) { $checkboxattribute = "1"; } # english is not deselectable
+
+ my $count = $i + 1;
+ my $nextcount = $i + 2;
+ my $checkboxcount = "CheckBox" . $count;
+
+ my $multiplier = 20;
+ my $offset = 60;
+ if ( $#{$languagesarrayref} > 7 )
+ {
+ $multiplier = 15; # smaller differences for more than 7 languages
+ $offset = 50; # smaller offset for more than 7 languages
+ }
+
+ my $yvalue = $offset + $i * $multiplier;
+
+ my $property = "IS" . $windowslanguage;
+ # if ( ! exists($installer::globals::languageproperties{$property}) ) { installer::exiter::exit_program("ERROR: Could not find property \"$property\" in the list of language properties!", "add_language_checkboxes_to_database"); }
+
+ my $controlnext = "";
+ if ( $last ) { $controlnext = "Next"; }
+ else { $controlnext = "CheckBox" . $nextcount; }
+
+ my $stringname = "OOO_CONTROL_LANG_" . $windowslanguage;
+
+ my $line1 = "LanguageSelection" . "\t" . $checkboxcount . "\t" . "CheckBox" . "\t" .
+ "22" . "\t" . $yvalue . "\t" . "200" . "\t" . "15" . "\t" . $checkboxattribute . "\t" .
+ $property . "\t" . $stringname . "\t" . $controlnext . "\t" . "\n";
+
+ push(@{$controltable}, $line1);
+
+ # my $textcount = "Text" . $count;
+ # my $stringname = "OOO_CONTROL_LANG_" . $windowslanguage;
+ #
+ # $yvalue = $yvalue + 2; # text 2 pixel lower than checkbox
+ #
+ # my $line2 = "LanguageSelection" . "\t" . $textcount . "\t" . "Text" . "\t" .
+ # "40" . "\t" . $yvalue . "\t" . "70" . "\t" . "15" . "\t" . "65539" . "\t" .
+ # "\t" . $stringname . "\t" . "\t" . "\n";
+ #
+ # push(@{$controltable}, $line2);
+ }
+}
+
+###################################################################
+# Determining the last position in a sequencetable
+# into the tables CustomAc.idt and InstallE.idt.
+###################################################################
+
+sub get_last_position_in_sequencetable
+{
+ my ($sequencetable) = @_;
+
+ my $position = 0;
+
+ for ( my $i = 0; $i <= $#{$sequencetable}; $i++ )
+ {
+ my $line = ${$sequencetable}[$i];
+
+ if ( $line =~ /^\s*\w+\t.*\t\s*(\d+)\s$/ )
+ {
+ my $newposition = $1;
+ if ( $newposition > $position ) { $position = $newposition; }
+ }
+ }
+
+ return $position;
+}
+
+#########################################################################
+# Determining the position of a specified Action in the sequencetable
+#########################################################################
+
+sub get_position_in_sequencetable
+{
+ my ($action, $sequencetable) = @_;
+
+ my $position = 0;
+
+ $action =~ s/^\s*behind_//;
+
+ for ( my $i = 0; $i <= $#{$sequencetable}; $i++ )
+ {
+ my $line = ${$sequencetable}[$i];
+
+ if ( $line =~ /^\s*(\w+)\t.*\t\s*(\d+)\s$/ )
+ {
+ my $compareaction = $1;
+ $position = $2;
+ if ( $compareaction eq $action ) { last; }
+ }
+ }
+
+ return $position;
+}
+
+################################################################################################
+# Including the CustomAction for the configuration
+# into the tables CustomAc.idt and InstallE.idt.
+#
+# CustomAc.idt: ExecutePkgchk 82 pkgchk.exe -s
+# InstallE.idt: ExecutePkgchk Not REMOVE="ALL" 3175
+#
+# CustomAc.idt: ExecuteQuickstart 82 install_quickstart.exe
+# InstallE.idt: ExecuteQuickstart &gm_o_Quickstart=3 3200
+#
+# CustomAc.idt: ExecuteInstallRegsvrex 82 regsvrex.exe shlxthdl.dll
+# InstallE.idt: ExecuteInstallRegsvrex Not REMOVE="ALL" 3225
+#
+# CustomAc.idt: ExecuteUninstallRegsvrex 82 regsvrex.exe /u shlxthdl.dll
+# InstallE.idt: ExecuteUninstallRegsvrex REMOVE="ALL" 690
+#
+# CustomAc.idt: Regmsdocmsidll1 1 reg4msdocmsidll Reg4MsDocEntry
+# InstallU.idt: Regmsdocmsidll1 Not REMOVE="ALL" 610
+#
+# CustomAc.idt: Regmsdocmsidll2 1 reg4msdocmsidll Reg4MsDocEntry
+# InstallE.idt: Regmsdocmsidll2 Not REMOVE="ALL" 3160
+################################################################################################
+
+sub set_custom_action
+{
+ my ($customactionidttable, $actionname, $actionflags, $exefilename, $actionparameter, $inbinarytable, $filesref, $customactionidttablename, $styles) = @_;
+
+ my $included_customaction = 0;
+ my $infoline = "";
+ my $customaction_exefilename = $exefilename;
+ my $uniquename = "";
+
+ # when the style NO_FILE is set, no searching for the file is needed, no filtering is done, we can add that custom action
+ if ( $styles =~ /\bNO_FILE\b/ )
+ {
+ my $line = $actionname . "\t" . $actionflags . "\t" . $customaction_exefilename . "\t" . $actionparameter . "\n";
+ push(@{$customactionidttable}, $line);
+
+ $infoline = "Added $actionname CustomAction into table $customactionidttablename (NO_FILE has been set)\n";
+ push(@installer::globals::logfileinfo, $infoline);
+
+ $included_customaction = 1;
+ return $included_customaction;
+ }
+
+ # is the $exefilename a library that is included into the binary table
+
+ if ( $inbinarytable ) { $customaction_exefilename =~ s/\.//; } # this is the entry in the binary table ("abc.dll" -> "abcdll")
+
+ # is the $exefilename included into the product?
+
+ my $contains_file = 0;
+
+ # All files are located in $filesref and in @installer::globals::binarytableonlyfiles.
+ # Both must be added together
+ my $localfilesref = installer::converter::combine_arrays_from_references(\@installer::globals::binarytableonlyfiles, $filesref);
+
+ for ( my $i = 0; $i <= $#{$localfilesref}; $i++ )
+ {
+ my $onefile = ${$localfilesref}[$i];
+ my $filename = "";
+ if ( exists($onefile->{'Name'}) )
+ {
+ $filename = $onefile->{'Name'};
+
+ if ( $filename eq $exefilename )
+ {
+ $contains_file = 1;
+ $uniquename = ${$localfilesref}[$i]->{'uniquename'};
+ last;
+ }
+ }
+ else
+ {
+ installer::exiter::exit_program("ERROR: Did not find \"Name\" for file \"$onefile->{'uniquename'}\" ($onefile->{'gid'})!", "set_custom_action");
+ }
+ }
+
+ if ( $contains_file )
+ {
+ # Now the CustomAction can be included into the CustomAc.idt
+
+ if ( ! $inbinarytable ) { $customaction_exefilename = $uniquename; } # the unique file name has to be added to the custom action table
+
+ my $line = $actionname . "\t" . $actionflags . "\t" . $customaction_exefilename . "\t" . $actionparameter . "\n";
+ push(@{$customactionidttable}, $line);
+
+ $included_customaction = 1;
+ }
+
+ if ( $included_customaction ) { $infoline = "Added $actionname CustomAction into table $customactionidttablename\n"; }
+ else { $infoline = "Did not add $actionname CustomAction into table $customactionidttablename\n"; }
+ push(@installer::globals::logfileinfo, $infoline);
+
+ return $included_customaction;
+}
+
+####################################################################
+# Adding a Custom Action to InstallExecuteTable or InstallUITable
+####################################################################
+
+sub add_custom_action_to_install_table
+{
+ my ($installtable, $exefilename, $actionname, $actioncondition, $position, $filesref, $installtablename, $styles) = @_;
+
+ my $included_customaction = 0;
+ my $feature = "";
+ my $infoline = "";
+
+ # when the style NO_FILE is set, no searching for the file is needed, no filtering is done, we can add that custom action
+ if ( $styles =~ /\bNO_FILE\b/ )
+ {
+ # then the InstallE.idt.idt or InstallU.idt.idt
+ $actioncondition =~ s/FEATURETEMPLATE/$feature/g; # only execute Custom Action, if feature of the file is installed
+
+ my $actionposition = 0;
+
+ if ( $position eq "end" ) { $actionposition = get_last_position_in_sequencetable($installtable) + 25; }
+ elsif ( $position =~ /^\s*behind_/ ) { $actionposition = get_position_in_sequencetable($position, $installtable) + 2; }
+ else { $actionposition = get_position_in_sequencetable($position, $installtable) - 2; }
+
+ my $line = $actionname . "\t" . $actioncondition . "\t" . $actionposition . "\n";
+ push(@{$installtable}, $line);
+
+ $infoline = "Added $actionname CustomAction into table $installtablename (NO_FILE has been set)\n";
+ push(@installer::globals::logfileinfo, $infoline);
+ return;
+ }
+
+ my $contains_file = 0;
+
+ # All files are located in $filesref and in @installer::globals::binarytableonlyfiles.
+ # Both must be added together
+ my $localfilesref = installer::converter::combine_arrays_from_references(\@installer::globals::binarytableonlyfiles, $filesref);
+
+ for ( my $i = 0; $i <= $#{$localfilesref}; $i++ )
+ {
+ my $filename = ${$localfilesref}[$i]->{'Name'};
+
+ if ( $filename eq $exefilename )
+ {
+ $contains_file = 1;
+
+ # Determining the feature of the file
+
+ if ( ${$localfilesref}[$i] ) { $feature = ${$localfilesref}[$i]->{'modules'}; }
+
+ # If modules contains a list of modules, only taking the first one.
+ if ( $feature =~ /^\s*(.*?)\,/ ) { $feature = $1; }
+ # Attention: Maximum feature length is 38!
+ shorten_feature_gid(\$feature);
+
+ last;
+ }
+ }
+
+ if ( $contains_file )
+ {
+ # then the InstallE.idt.idt or InstallU.idt.idt
+
+ $actioncondition =~ s/FEATURETEMPLATE/$feature/g; # only execute Custom Action, if feature of the file is installed
+
+# my $actionposition = 0;
+# if ( $position eq "end" ) { $actionposition = get_last_position_in_sequencetable($installtable) + 25; }
+# elsif ( $position =~ /^\s*behind_/ ) { $actionposition = get_position_in_sequencetable($position, $installtable) + 2; }
+# else { $actionposition = get_position_in_sequencetable($position, $installtable) - 2; }
+# my $line = $actionname . "\t" . $actioncondition . "\t" . $actionposition . "\n";
+
+ my $positiontemplate = "";
+ if ( $position =~ /^\s*\d+\s*$/ ) { $positiontemplate = $position; } # setting the position directly, number defined in scp2
+ else { $positiontemplate = "POSITIONTEMPLATE_" . $position; }
+
+ my $line = $actionname . "\t" . $actioncondition . "\t" . $positiontemplate . "\n";
+ push(@{$installtable}, $line);
+
+ $included_customaction = 1;
+ }
+
+ if ( $included_customaction ) { $infoline = "Added $actionname CustomAction into table $installtablename\n"; }
+ else { $infoline = "Did not add $actionname CustomAction into table $installtablename\n"; }
+ push(@installer::globals::logfileinfo, $infoline);
+
+}
+
+##################################################################
+# A line in the table ControlEvent connects a Control
+# with a Custom Action
+#################################################################
+
+sub connect_custom_action_to_control
+{
+ my ( $table, $tablename, $dialog, $control, $event, $argument, $condition, $ordering) = @_;
+
+ my $line = $dialog . "\t" . $control. "\t" . $event. "\t" . $argument. "\t" . $condition. "\t" . $ordering . "\n";
+
+ push(@{$table}, $line);
+
+ $line =~ s/\s*$//g;
+
+ $infoline = "Added line \"$line\" into table $tablename\n";
+ push(@installer::globals::logfileinfo, $infoline);
+}
+
+##################################################################
+# A line in the table ControlCondition connects a Control state
+# with a condition
+##################################################################
+
+sub connect_condition_to_control
+{
+ my ( $table, $tablename, $dialog, $control, $event, $condition) = @_;
+
+ my $line = $dialog . "\t" . $control. "\t" . $event. "\t" . $condition. "\n";
+
+ push(@{$table}, $line);
+
+ $line =~ s/\s*$//g;
+
+ $infoline = "Added line \"$line\" into table $tablename\n";
+ push(@installer::globals::logfileinfo, $infoline);
+}
+
+##################################################################
+# Searching for a sequencenumber in InstallUISequence table
+# "ExecuteAction" must be the last action
+##################################################################
+
+sub get_free_number_in_uisequence_table
+{
+ my ( $installuitable ) = @_;
+
+ # determining the sequence of "ExecuteAction"
+
+ my $executeactionnumber = 0;
+
+ for ( my $i = 0; $i <= $#{$installuitable}; $i++ )
+ {
+ if ( ${$installuitable}[$i] =~ /^\s*(\w+)\t\w*\t(\d+)\s*$/ )
+ {
+ my $actionname = $1;
+ my $actionnumber = $2;
+
+ if ( $actionname eq "ExecuteAction" )
+ {
+ $executeactionnumber = $actionnumber;
+ last;
+ }
+ }
+ }
+
+ if ( $executeactionnumber == 0 ) { installer::exiter::exit_program("ERROR: Did not find \"ExecuteAction\" in InstallUISequence table!", "get_free_number_in_uisequence_table"); }
+
+ # determining the sequence of the action before "ExecuteAction"
+
+ my $lastactionnumber = 0;
+
+ for ( my $i = 0; $i <= $#{$installuitable}; $i++ )
+ {
+ if ( ${$installuitable}[$i] =~ /^\s*\w+\t\w*\t(\d+)\s*$/ )
+ {
+ my $actionnumber = $1;
+
+ if (( $actionnumber > $lastactionnumber ) && ( $actionnumber != $executeactionnumber ))
+ {
+ $lastactionnumber = $actionnumber;
+ }
+ }
+ }
+
+ # the new number can now be calculated
+
+ my $newnumber = 0;
+
+ if ((( $lastactionnumber + $executeactionnumber ) % 2 ) == 0 ) { $newnumber = ( $lastactionnumber + $executeactionnumber ) / 2; }
+ else { $newnumber = ( $lastactionnumber + $executeactionnumber -1 ) / 2; }
+
+ return $newnumber;
+}
+
+##################################################################
+# Searching for a specified string in the feature table
+##################################################################
+
+sub get_feature_name
+{
+ my ( $string, $featuretable ) = @_;
+
+ my $featurename = "";
+
+ for ( my $i = 0; $i <= $#{$featuretable}; $i++ )
+ {
+ if ( ${$featuretable}[$i] =~ /^\s*(\w+$string)\t/ )
+ {
+ $featurename = $1;
+ last;
+ }
+ }
+
+ return $featurename;
+}
+
+######################################################################
+# Returning the toplevel directory name of one specific file
+######################################################################
+
+sub get_directory_name_from_file
+{
+ my ($onefile) = @_;
+
+ my $destination = $onefile->{'destination'};
+ my $name = $onefile->{'Name'};
+
+ $destination =~ s/\Q$name\E\s*$//;
+ $destination =~ s/\Q$installer::globals::separator\E\s*$//;
+
+ my $path = "";
+
+ if ( $destination =~ /\Q$installer::globals::separator\E/ )
+ {
+ if ( $destination =~ /^\s*(\S.*\S\Q$installer::globals::separator\E)(\S.+\S?)/ )
+ {
+ $path = $2;
+ }
+ }
+ else
+ {
+ $path = $destination;
+ }
+
+ return $path;
+}
+
+#############################################################
+# Including the new subdir into the directory table
+#############################################################
+
+sub include_subdirname_into_directory_table
+{
+ my ($dirname, $directorytable, $directorytablename, $onefile) = @_;
+
+ my $subdir = "";
+ if ( $onefile->{'Subdir'} ) { $subdir = $onefile->{'Subdir'}; }
+ if ( $subdir eq "" ) { installer::exiter::exit_program("ERROR: No \"Subdir\" defined for $onefile->{'Name'}", "include_subdirname_into_directory_table"); }
+
+ # program INSTALLLOCATION program -> subjava INSTALLLOCATION program:java
+
+ my $uniquename = "";
+ my $parent = "";
+ my $name = "";
+
+ my $includedline = 0;
+
+ my $newdir = "";
+
+ for ( my $i = 0; $i <= $#{$directorytable}; $i++ )
+ {
+
+ if ( ${$directorytable}[$i] =~ /^\s*(.*?)\t(.*?)\t(.*?)\s*$/ )
+ {
+ $uniquename = $1;
+ $parent = $2;
+ $name = $3;
+
+ if ( $dirname eq $name )
+ {
+ my $newuniquename = "sub" . $subdir;
+ $newdir = $newuniquename;
+ # my $newparent = $parent;
+ my $newparent = "INSTALLLOCATION";
+ my $newname = $name . "\:" . $subdir;
+ my $newline =
+ $line = "$newuniquename\t$newparent\t$newname\n";
+ push(@{$directorytable}, $line);
+ installer::remover::remove_leading_and_ending_whitespaces(\$line);
+ $infoline = "Added $line into directory table $directorytablename\n";
+ push(@installer::globals::logfileinfo, $infoline);
+
+ $includedline = 1;
+ last;
+ }
+ }
+ }
+
+ if ( ! $includedline ) { installer::exiter::exit_program("ERROR: Could not include new subdirectory into directory table for file $onefile->{'Name'}!", "include_subdirname_into_directory_table"); }
+
+ return $newdir;
+}
+
+##################################################################
+# Including the new sub directory into the component table
+##################################################################
+
+sub include_subdir_into_componenttable
+{
+ my ($subdir, $onefile, $componenttable) = @_;
+
+ my $componentname = $onefile->{'componentname'};
+
+ my $changeddirectory = 0;
+
+ for ( my $i = 0; $i <= $#{$componenttable}; $i++ )
+ {
+ if ( ${$componenttable}[$i] =~ /^\s*(.*?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\s*$/ )
+ {
+ my $localcomponentname = $1;
+ my $directory = $3;
+
+ if ( $componentname eq $localcomponentname )
+ {
+ my $oldvalue = ${$componenttable}[$i];
+ ${$componenttable}[$i] =~ s/\b\Q$directory\E\b/$subdir/;
+ my $newvalue = ${$componenttable}[$i];
+
+ installer::remover::remove_leading_and_ending_whitespaces(\$oldvalue);
+ installer::remover::remove_leading_and_ending_whitespaces(\$newvalue);
+ $infoline = "Change in Component table: From \"$oldvalue\" to \"$newvalue\"\n";
+ push(@installer::globals::logfileinfo, $infoline);
+
+ $changeddirectory = 1;
+ last;
+ }
+ }
+ }
+
+ if ( ! $changeddirectory ) { installer::exiter::exit_program("ERROR: Could not change directory for component: $onefile->{'Name'}!", "include_subdir_into_componenttable"); }
+
+}
+
+################################################################################################
+# Including the content for the child installations
+# into the tables:
+# CustomAc.idt, InstallU.idt, Feature.idt
+################################################################################################
+
+sub add_childprojects
+{
+ my ($languageidtdir, $filesref, $allvariables) = @_;
+
+ my $customactiontablename = $languageidtdir . $installer::globals::separator . "CustomAc.idt";
+ my $customactiontable = installer::files::read_file($customactiontablename);
+ my $installuitablename = $languageidtdir . $installer::globals::separator . "InstallU.idt";
+ my $installuitable = installer::files::read_file($installuitablename);
+ my $featuretablename = $languageidtdir . $installer::globals::separator . "Feature.idt";
+ my $featuretable = installer::files::read_file($featuretablename);
+ my $directorytablename = $languageidtdir . $installer::globals::separator . "Director.idt";
+ my $directorytable = installer::files::read_file($directorytablename);
+ my $componenttablename = $languageidtdir . $installer::globals::separator . "Componen.idt";
+ my $componenttable = installer::files::read_file($componenttablename);
+
+ my $infoline = "";
+ my $line = "";
+
+ $installer::globals::javafile = installer::worker::return_first_item_with_special_flag($filesref ,"JAVAFILE");
+ $installer::globals::urefile = installer::worker::return_first_item_with_special_flag($filesref ,"UREFILE");
+
+ if (( $installer::globals::javafile eq "" ) && ( $allvariables->{'JAVAPRODUCT'} )) { installer::exiter::exit_program("ERROR: No JAVAFILE found in files collector!", "add_childprojects"); }
+ if (( $installer::globals::urefile eq "" ) && ( $allvariables->{'UREPRODUCT'} )) { installer::exiter::exit_program("ERROR: No UREFILE found in files collector!", "add_childprojects"); }
+
+ # Content for Directory table
+ # SystemFolder TARGETDIR .
+
+ my $contains_systemfolder = 0;
+
+ for ( my $i = 0; $i <= $#{$directorytable}; $i++ )
+ {
+ if ( ${$directorytable}[$i] =~ /^\s*SystemFolder\t/ )
+ {
+ $contains_systemfolder = 1;
+ last;
+ }
+ }
+
+ if ( ! $contains_systemfolder )
+ {
+ $line = "SystemFolder\tTARGETDIR\t\.\n";
+ push(@{$directorytable}, $line);
+ installer::remover::remove_leading_and_ending_whitespaces(\$line);
+ $infoline = "Added $line into table $directorytablename\n";
+ }
+ else
+ {
+ $infoline = "SystemFolder already exists in table $directorytablename\n";
+ }
+
+ push(@installer::globals::logfileinfo, $infoline);
+
+ # Additional content for the directory table
+ # subjava INSTALLLOCATION program:java
+ # subure INSTALLLOCATION program:ure
+
+ my $dirname = "";
+ my $subjavadir = "";
+ my $suburedir = "";
+
+ if ( $allvariables->{'JAVAPRODUCT'} )
+ {
+ $dirname = get_directory_name_from_file($installer::globals::javafile);
+ $subjavadir = include_subdirname_into_directory_table($dirname, $directorytable, $directorytablename, $installer::globals::javafile);
+ }
+
+ if ( $allvariables->{'UREPRODUCT'} )
+ {
+ $dirname = get_directory_name_from_file($installer::globals::urefile);
+ $suburedir = include_subdirname_into_directory_table($dirname, $directorytable, $directorytablename, $installer::globals::urefile);
+ }
+
+ # Content for the Component table
+ # The Java and Ada components have new directories
+
+ if ( $allvariables->{'JAVAPRODUCT'} ) { include_subdir_into_componenttable($subjavadir, $installer::globals::javafile, $componenttable); }
+ if ( $allvariables->{'UREPRODUCT'} ) { include_subdir_into_componenttable($suburedir, $installer::globals::urefile, $componenttable); }
+
+ # Content for CustomAction table
+
+ if ( $allvariables->{'JAVAPRODUCT'} )
+ {
+ $line = "InstallJava\t98\tSystemFolder\t[SourceDir]$installer::globals::javafile->{'Subdir'}\\$installer::globals::javafile->{'Name'} \/qb REBOOT=Suppress SPONSORS=0 DISABLEAD=1\n";
+ push(@{$customactiontable} ,$line);
+ installer::remover::remove_leading_and_ending_whitespaces(\$line);
+ $infoline = "Added $line into table $customactiontablename\n";
+ push(@installer::globals::logfileinfo, $infoline);
+ }
+
+ if ( $allvariables->{'UREPRODUCT'} )
+ {
+ $line = "InstallUre\t98\tSystemFolder\t$installer::globals::urefile->{'Subdir'}\\$installer::globals::urefile->{'Name'} /S\n";
+ push(@{$customactiontable} ,$line);
+ installer::remover::remove_leading_and_ending_whitespaces(\$line);
+ $infoline = "Added $line into table $customactiontablename\n";
+ push(@installer::globals::logfileinfo, $infoline);
+ }
+
+ if ( $allvariables->{'JAVAPRODUCT'} )
+ {
+ $line = "MaintenanceJava\t82\t$installer::globals::javafile->{'uniquename'}\t\/qb REBOOT=Suppress SPONSORS=0 DISABLEAD=1\n";
+ push(@{$customactiontable} ,$line);
+ installer::remover::remove_leading_and_ending_whitespaces(\$line);
+ $infoline = "Added $line into table $customactiontablename\n";
+ push(@installer::globals::logfileinfo, $infoline);
+ }
+
+ if ( $allvariables->{'UREPRODUCT'} )
+ {
+ $line = "MaintenanceUre\t82\t$installer::globals::urefile->{'uniquename'}\t\/S\n";
+ push(@{$customactiontable} ,$line);
+ installer::remover::remove_leading_and_ending_whitespaces(\$line);
+ $infoline = "Added $line into table $customactiontablename\n";
+ push(@installer::globals::logfileinfo, $infoline);
+ }
+
+ # Content for InstallUISequence table
+ # InstallAdabas &gm_o_Adabas=3 825
+ # InstallJava &gm_o_Java=3 827
+
+ my $number = "";
+ my $featurename = "";
+
+ if ( $allvariables->{'ADAPRODUCT'} )
+ {
+ $number = get_free_number_in_uisequence_table($installuitable);
+ $featurename = get_feature_name("_Adabas", $featuretable);
+ $line = "InstallAdabas\t\&$featurename\=3 And Not Installed And Not PATCH\t$number\n";
+ push(@{$installuitable} ,$line);
+ installer::remover::remove_leading_and_ending_whitespaces(\$line);
+ $infoline = "Added $line into table $installuitablename\n";
+ push(@installer::globals::logfileinfo, $infoline);
+ }
+
+ if ( $allvariables->{'JAVAPRODUCT'} )
+ {
+ $number = get_free_number_in_uisequence_table($installuitable) + 2;
+ $featurename = get_feature_name("_Java", $featuretable);
+ if ( $featurename ) { $line = "InstallJava\t\&$featurename\=3 And Not Installed And JAVAPATH\=\"\" And Not PATCH\t$number\n"; }
+ else { $line = "InstallJava\tNot Installed And JAVAPATH\=\"\" And Not PATCH\t$number\n"; } # feature belongs to root
+ push(@{$installuitable} ,$line);
+ installer::remover::remove_leading_and_ending_whitespaces(\$line);
+ $infoline = "Added $line into table $installuitablename\n";
+ push(@installer::globals::logfileinfo, $infoline);
+ }
+
+ if ( $allvariables->{'ADAPRODUCT'} )
+ {
+ $number = get_free_number_in_uisequence_table($installuitable) + 4;
+ $featurename = get_feature_name("_Adabas", $featuretable);
+ $line = "MaintenanceAdabas\t\&$featurename\=3 And Installed And Not PATCH\t$number\n";
+ push(@{$installuitable} ,$line);
+ installer::remover::remove_leading_and_ending_whitespaces(\$line);
+ $infoline = "Added $line into table $installuitablename\n";
+ push(@installer::globals::logfileinfo, $infoline);
+ }
+
+ if ( $allvariables->{'JAVAPRODUCT'} )
+ {
+ $number = get_free_number_in_uisequence_table($installuitable) + 6;
+ $featurename = get_feature_name("_Java", $featuretable);
+ if ( $featurename ) { $line = "MaintenanceJava\t\&$featurename\=3 And Installed And JAVAPATH\=\"\" And Not PATCH\t$number\n"; }
+ else { $line = "MaintenanceJava\tInstalled And JAVAPATH\=\"\" And Not PATCH\t$number\n"; } # feature belongs to root
+ push(@{$installuitable} ,$line);
+ installer::remover::remove_leading_and_ending_whitespaces(\$line);
+ $infoline = "Added $line into table $installuitablename\n";
+ push(@installer::globals::logfileinfo, $infoline);
+ }
+
+ if ( $allvariables->{'UREPRODUCT'} )
+ {
+ $number = get_free_number_in_uisequence_table($installuitable) + 8;
+ $featurename = get_feature_name("_Ure", $featuretable);
+ if ( $featurename ) { $line = "InstallUre\t\&$featurename\=3 And Not Installed\t$number\n"; }
+ else { $line = "InstallUre\tNot Installed\t$number\n"; } # feature belongs to root
+ push(@{$installuitable} ,$line);
+ installer::remover::remove_leading_and_ending_whitespaces(\$line);
+ $infoline = "Added $line into table $installuitablename\n";
+ push(@installer::globals::logfileinfo, $infoline);
+ }
+
+ if ( $allvariables->{'UREPRODUCT'} )
+ {
+ $number = get_free_number_in_uisequence_table($installuitable) + 10;
+ $featurename = get_feature_name("_Ure", $featuretable);
+ if ( $featurename ) { $line = "MaintenanceUre\t\&$featurename\=3 And Installed\t$number\n"; }
+ else { $line = "MaintenanceUre\tInstalled\t$number\n"; } # feature belongs to root
+ push(@{$installuitable} ,$line);
+ installer::remover::remove_leading_and_ending_whitespaces(\$line);
+ $infoline = "Added $line into table $installuitablename\n";
+ push(@installer::globals::logfileinfo, $infoline);
+ }
+
+ # Content for Feature table, better from scp (translation)
+ # gm_o_java gm_optional Java 1.4.2 Description 2 200
+
+ installer::files::save_file($customactiontablename, $customactiontable);
+ installer::files::save_file($installuitablename, $installuitable);
+ installer::files::save_file($featuretablename, $featuretable);
+ installer::files::save_file($directorytablename, $directorytable);
+ installer::files::save_file($componenttablename, $componenttable);
+}
+
+##################################################################
+# Setting the encoding in all idt files. Replacing the
+# variable WINDOWSENCODINGTEMPLATE
+##################################################################
+
+sub setencoding
+{
+ my ( $languageidtdir, $onelanguage ) = @_;
+
+ my $encoding = installer::windows::language::get_windows_encoding($onelanguage);
+
+ # collecting all idt files in the directory $languageidtdir and substituting the string
+
+ my $idtfiles = installer::systemactions::find_file_with_file_extension("idt", $languageidtdir);
+
+ for ( my $i = 0; $i <= $#{$idtfiles}; $i++ )
+ {
+ my $onefilename = $languageidtdir . $installer::globals::separator . ${$idtfiles}[$i];
+ my $onefile = installer::files::read_file($onefilename);
+
+ for ( my $j = 0; $j <= $#{$onefile}; $j++ )
+ {
+ ${$onefile}[$j] =~ s/WINDOWSENCODINGTEMPLATE/$encoding/g;
+ }
+
+ installer::files::save_file($onefilename, $onefile);
+ }
+}
+
+##################################################################
+# Setting the condition, that at least one module is selected.
+# All modules with flag SHOW_MULTILINGUAL_ONLY were already
+# collected. In table ControlE.idt, the string
+# LANGUAGECONDITIONINSTALL needs to be replaced.
+# Also for APPLICATIONCONDITIONINSTALL for the applications
+# with flag APPLICATIONMODULE.
+##################################################################
+
+sub set_multilanguageonly_condition
+{
+ my ( $languageidtdir ) = @_;
+
+ my $onefilename = $languageidtdir . $installer::globals::separator . "ControlE.idt";
+ my $onefile = installer::files::read_file($onefilename);
+
+ # Language modules
+
+ my $condition = "";
+
+ foreach my $module ( sort keys %installer::globals::multilingual_only_modules )
+ {
+ $condition = $condition . " &$module=3 Or";
+ }
+
+ $condition =~ s/^\s*//;
+ $condition =~ s/\s*Or\s*$//; # removing the ending "Or"
+
+ if ( $condition eq "" ) { $condition = "1"; }
+
+ for ( my $j = 0; $j <= $#{$onefile}; $j++ )
+ {
+ ${$onefile}[$j] =~ s/LANGUAGECONDITIONINSTALL/$condition/;
+ }
+
+ # Application modules
+
+ $condition = "";
+
+ foreach my $module ( sort keys %installer::globals::application_modules )
+ {
+ $condition = $condition . " &$module=3 Or";
+ }
+
+ $condition =~ s/^\s*//;
+ $condition =~ s/\s*Or\s*$//; # removing the ending "Or"
+
+ if ( $condition eq "" ) { $condition = "1"; }
+
+ for ( my $j = 0; $j <= $#{$onefile}; $j++ )
+ {
+ ${$onefile}[$j] =~ s/APPLICATIONCONDITIONINSTALL/$condition/;
+ }
+
+ installer::files::save_file($onefilename, $onefile);
+}
+
+#############################################
+# Putting array values into hash
+#############################################
+
+sub fill_assignment_hash
+{
+ my ($gid, $name, $key, $assignmenthashref, $parameter, $tablename, $assignmentarray) = @_;
+
+ my $max = $parameter - 1;
+
+ if ( $max != $#{$assignmentarray} )
+ {
+ my $definedparameter = $#{$assignmentarray} + 1;
+ installer::exiter::exit_program("ERROR: gid: $gid, key: $key ! Wrong parameter in scp. For table $tablename $parameter parameter are required ! You defined: $definedparameter", "fill_assignment_hash");
+ }
+
+ for ( my $i = 0; $i <= $#{$assignmentarray}; $i++ )
+ {
+ my $counter = $i + 1;
+ my $key = "parameter". $counter;
+
+ my $localvalue = ${$assignmentarray}[$i];
+ installer::remover::remove_leading_and_ending_quotationmarks(\$localvalue);
+ $localvalue =~ s/\\\"/\"/g;
+ $localvalue =~ s/\\\!/\!/g;
+ $localvalue =~ s/\\\&/\&/g;
+ $localvalue =~ s/\\\</\</g;
+ $localvalue =~ s/\\\>/\>/g;
+ $assignmenthashref->{$key} = $localvalue;
+ }
+}
+
+##########################################################################
+# Checking the assignment of a Windows CustomAction and putting it
+# into a hash
+##########################################################################
+
+sub create_customaction_assignment_hash
+{
+ my ($gid, $name, $key, $assignmentarray) = @_;
+
+ my %assignment = ();
+ my $assignmenthashref = \%assignment;
+
+ my $tablename = ${$assignmentarray}[0];
+ installer::remover::remove_leading_and_ending_quotationmarks(\$tablename);
+
+ my $tablename_defined = 0;
+ my $parameter = 0;
+
+ if ( $tablename eq "InstallUISequence" )
+ {
+ $tablename_defined = 1;
+ $parameter = 3;
+ fill_assignment_hash($gid, $name, $key, $assignmenthashref, $parameter, $tablename, $assignmentarray);
+ }
+
+ if ( $tablename eq "InstallExecuteSequence" )
+ {
+ $tablename_defined = 1;
+ $parameter = 3;
+ fill_assignment_hash($gid, $name, $key, $assignmenthashref, $parameter, $tablename, $assignmentarray);
+ }
+
+ if ( $tablename eq "AdminExecuteSequence" )
+ {
+ $tablename_defined = 1;
+ $parameter = 3;
+ fill_assignment_hash($gid, $name, $key, $assignmenthashref, $parameter, $tablename, $assignmentarray);
+ }
+
+ if ( $tablename eq "ControlEvent" )
+ {
+ $tablename_defined = 1;
+ $parameter = 7;
+ fill_assignment_hash($gid, $name, $key, $assignmenthashref, $parameter, $tablename, $assignmentarray);
+ }
+
+ if ( $tablename eq "ControlCondition" )
+ {
+ $tablename_defined = 1;
+ $parameter = 5;
+ fill_assignment_hash($gid, $name, $key, $assignmenthashref, $parameter, $tablename, $assignmentarray);
+ }
+
+ if ( ! $tablename_defined )
+ {
+ installer::exiter::exit_program("ERROR: gid: $gid, key: $key ! Unknown Windows CustomAction table: $tablename ! Currently supported: InstallUISequence, InstallExecuteSequence, ControlEvent, ControlCondition", "create_customaction_assignment_hash");
+ }
+
+ return $assignmenthashref;
+}
+
+##########################################################################
+# Finding the position of a specified CustomAction.
+# If the CustomAction is not found, the return value is "-1".
+# If the CustomAction position is not defined yet,
+# the return value is also "-1".
+##########################################################################
+
+sub get_customaction_position
+{
+ my ($action, $sequencetable) = @_;
+
+ my $position = -1;
+
+ for ( my $i = 0; $i <= $#{$sequencetable}; $i++ )
+ {
+ my $line = ${$sequencetable}[$i];
+
+ if ( $line =~ /^\s*([\w\.]+)\t.*\t\s*(\d+)\s$/ ) # matching only, if position is a number!
+ {
+ my $compareaction = $1;
+ my $localposition = $2;
+
+ if ( $compareaction eq $action )
+ {
+ $position = $localposition;
+ last;
+ }
+ }
+ }
+
+ return $position;
+}
+
+##########################################################################
+# Setting the position of CustomActions in sequence tables.
+# Replacing all occurences of "POSITIONTEMPLATE_"
+##########################################################################
+
+sub set_positions_in_table
+{
+ my ( $sequencetable, $tablename ) = @_;
+
+ my $infoline = "\nSetting positions in table \"$tablename\".\n";
+ push(@installer::globals::logfileinfo, $infoline);
+
+ # Step 1: Resolving all occurences of "POSITIONTEMPLATE_end"
+
+ my $lastposition = get_last_position_in_sequencetable($sequencetable);
+
+ for ( my $i = 0; $i <= $#{$sequencetable}; $i++ )
+ {
+ if ( ${$sequencetable}[$i] =~ /^\s*([\w\.]+)\t.*\t\s*POSITIONTEMPLATE_end\s*$/ )
+ {
+ my $customaction = $1;
+ $lastposition = $lastposition + 25;
+ ${$sequencetable}[$i] =~ s/POSITIONTEMPLATE_end/$lastposition/;
+ $infoline = "Setting position \"$lastposition\" for custom action \"$customaction\".\n";
+ push(@installer::globals::logfileinfo, $infoline);
+ }
+ }
+
+ # Step 2: Resolving all occurences of "POSITIONTEMPLATE_abc" or "POSITIONTEMPLATE_behind_abc"
+ # where abc is the name of the reference Custom Action.
+ # This has to be done, until there is no more occurence of POSITIONTEMPLATE (success)
+ # or there is no replacement in one circle (failure).
+
+ my $template_exists = 0;
+ my $template_replaced = 0;
+ my $counter = 0;
+
+ do
+ {
+ $template_exists = 0;
+ $template_replaced = 0;
+ $counter++;
+
+ for ( my $i = 0; $i <= $#{$sequencetable}; $i++ )
+ {
+ if ( ${$sequencetable}[$i] =~ /^\s*([\w\.]+)\t.*\t\s*(POSITIONTEMPLATE_.*?)\s*$/ )
+ {
+ my $onename = $1;
+ my $templatename = $2;
+ my $positionname = $templatename;
+ my $customaction = $templatename;
+ $customaction =~ s/POSITIONTEMPLATE_//;
+ $template_exists = 1;
+
+ # Trying to find the correct number.
+ # This can fail, if the custom action has no number
+
+ my $setbehind = 0;
+ if ( $customaction =~ /^\s*behind_(.*?)\s*$/ )
+ {
+ $customaction = $1;
+ $setbehind = 1;
+ }
+
+ my $position = get_customaction_position($customaction, $sequencetable);
+
+ if ( $position >= 0 ) # Found CustomAction and is has a position. Otherwise return value is "-1".
+ {
+ my $newposition = 0;
+ if ( $setbehind ) { $newposition = $position + 2; }
+ else { $newposition = $position - 2; }
+ ${$sequencetable}[$i] =~ s/$templatename/$newposition/;
+ $template_replaced = 1;
+ $infoline = "Setting position \"$newposition\" for custom action \"$onename\" (scp: \"$positionname\" at position $position).\n";
+ push(@installer::globals::logfileinfo, $infoline);
+ }
+ else
+ {
+ $infoline = "Could not assign position for custom action \"$onename\" yet (scp: \"$positionname\").\n";
+ push(@installer::globals::logfileinfo, $infoline);
+ }
+ }
+ }
+ } while (( $template_exists ) && ( $template_replaced ));
+
+ # An error occured, because templates still exist, but could not be replaced.
+ # Reason:
+ # 1. Wrong name of CustomAction in scp2 (typo?)
+ # 2. Circular dependencies of CustomActions (A after B and B after A)
+
+ # Problem: It is allowed, that a CustomAction is defined in scp2 in a library that is
+ # part of product ABC, but this CustomAction is not used in this product
+ # and the reference CustomAction is not part of this product.
+ # Therefore this cannot be an error, but only produce a warning. The assigned number
+ # must be the last sequence number.
+
+ if (( $template_exists ) && ( ! $template_replaced ))
+ {
+ # Giving a precise error message, collecting all unresolved templates
+ # my $templatestring = "";
+
+ for ( my $i = 0; $i <= $#{$sequencetable}; $i++ )
+ {
+ if ( ${$sequencetable}[$i] =~ /^\s*([\w\.]+)\t.*\t\s*(POSITIONTEMPLATE_.*?)\s*$/ )
+ {
+ my $customactionname = $1;
+ my $fulltemplate = $2;
+ my $template = $fulltemplate;
+ $template =~ s/POSITIONTEMPLATE_//;
+ # my $newstring = $customactionname . " (" . $template . ")";
+ # $templatestring = $templatestring . $newstring . ", ";
+ # Setting at the end!
+ $lastposition = $lastposition + 25;
+ ${$sequencetable}[$i] =~ s/$fulltemplate/$lastposition/;
+ $infoline = "WARNING: Setting position \"$lastposition\" for custom action \"$customactionname\". Could not find CustomAction \"$template\".\n";
+ push(@installer::globals::logfileinfo, $infoline);
+ }
+ }
+ # $templatestring =~ s/,\s*$//;
+
+ # $infoline = "Error: Saving table \"$tablename\"\n";
+ # push(@installer::globals::logfileinfo, $infoline);
+ # print $infoline;
+ # installer::files::save_file($tablename, $sequencetable);
+ # installer::exiter::exit_program("ERROR: Unresolved positions in CustomActions in scp2: $templatestring", "set_positions_in_table");
+ }
+}
+
+##########################################################################
+# Setting the Windows custom actions into different tables
+# CustomAc.idt, InstallE.idt, InstallU.idt, ControlE.idt, ControlC.idt
+##########################################################################
+
+sub addcustomactions
+{
+ my ($languageidtdir, $customactions, $filesarray) = @_;
+
+ installer::logger::include_timestamp_into_logfile("\nPerformance Info: addcustomactions start\n");
+
+ my $customactionidttablename = $languageidtdir . $installer::globals::separator . "CustomAc.idt";
+ my $customactionidttable = installer::files::read_file($customactionidttablename);
+ my $installexecutetablename = $languageidtdir . $installer::globals::separator . "InstallE.idt";
+ my $installexecutetable = installer::files::read_file($installexecutetablename);
+ my $adminexecutetablename = $languageidtdir . $installer::globals::separator . "AdminExe.idt";
+ my $adminexecutetable = installer::files::read_file($adminexecutetablename);
+ my $installuitablename = $languageidtdir . $installer::globals::separator . "InstallU.idt";
+ my $installuitable = installer::files::read_file($installuitablename);
+ my $controleventtablename = $languageidtdir . $installer::globals::separator . "ControlE.idt";
+ my $controleventtable = installer::files::read_file($controleventtablename);
+ my $controlconditiontablename = $languageidtdir . $installer::globals::separator . "ControlC.idt";
+ my $controlconditiontable = installer::files::read_file($controlconditiontablename);
+
+ # Iterating over all Windows custom actions
+
+ for ( my $i = 0; $i <= $#{$customactions}; $i++ )
+ {
+ my $customaction = ${$customactions}[$i];
+ my $name = $customaction->{'Name'};
+ my $typ = $customaction->{'Typ'};
+ my $source = $customaction->{'Source'};
+ my $target = $customaction->{'Target'};
+ my $inbinarytable = $customaction->{'Inbinarytable'};
+ my $gid = $customaction->{'gid'};
+
+ my $styles = "";
+ if ( $customaction->{'Styles'} ) { $styles = $customaction->{'Styles'}; }
+
+ my $added_customaction = set_custom_action($customactionidttable, $name, $typ, $source, $target, $inbinarytable, $filesarray, $customactionidttablename, $styles);
+
+ if ( $added_customaction )
+ {
+ # If the CustomAction was added into the CustomAc.idt, it can be connected to the installation.
+ # There are currently two different ways for doing this:
+ # 1. Using "add_custom_action_to_install_table", which adds the CustomAction to the install sequences,
+ # which are saved in InstallE.idt and InstallU.idt
+ # 2. Using "connect_custom_action_to_control" and "connect_custom_action_to_control". The first method
+ # connects a CustomAction to a control in ControlE.idt. The second method sets a condition for a control,
+ # which might be influenced by the CustomAction. This happens in ControlC.idt.
+
+ # Any Windows CustomAction can have a lot of different assignments.
+
+ for ( my $j = 1; $j <= 50; $j++ )
+ {
+ my $key = "Assignment" . $j;
+ my $value = "";
+ if ( $customaction->{$key} )
+ {
+ $value = $customaction->{$key};
+
+ # in a patch the Assignment can be overwritten by a PatchAssignment
+ if ( $installer::globals::patch )
+ {
+ $patchkey = "PatchAssignment" . $j;
+ if ( $customaction->{$patchkey} )
+ {
+ $value = $customaction->{$patchkey};
+ $key = $patchkey;
+ }
+ }
+
+ }
+ else { last; }
+
+ # $value is now a comma separated list
+ if ( $value =~ /^\s*\(\s*(.*)\s*\);?\s*$/ ) { $value = $1; }
+ my $assignmentarray = installer::converter::convert_stringlist_into_array(\$value, ",");
+ my $assignment = create_customaction_assignment_hash($gid, $name, $key, $assignmentarray);
+
+ if ( $assignment->{'parameter1'} eq "InstallExecuteSequence" )
+ {
+ add_custom_action_to_install_table($installexecutetable, $source, $name, $assignment->{'parameter2'}, $assignment->{'parameter3'}, $filesarray, $installexecutetablename, $styles);
+ }
+ elsif ( $assignment->{'parameter1'} eq "AdminExecuteSequence" )
+ {
+ add_custom_action_to_install_table($adminexecutetable, $source, $name, $assignment->{'parameter2'}, $assignment->{'parameter3'}, $filesarray, $adminexecutetablename, $styles);
+ }
+ elsif ( $assignment->{'parameter1'} eq "InstallUISequence" )
+ {
+ add_custom_action_to_install_table($installuitable, $source, $name, $assignment->{'parameter2'}, $assignment->{'parameter3'}, $filesarray, $installuitablename, $styles);
+ }
+ elsif ( $assignment->{'parameter1'} eq "ControlEvent" )
+ {
+ connect_custom_action_to_control($controleventtable, $controleventtablename, $assignment->{'parameter2'}, $assignment->{'parameter3'}, $assignment->{'parameter4'}, $assignment->{'parameter5'}, $assignment->{'parameter6'}, $assignment->{'parameter7'});
+ }
+ elsif ( $assignment->{'parameter1'} eq "ControlCondition" )
+ {
+ connect_condition_to_control($controlconditiontable, $controlconditiontablename, $assignment->{'parameter2'}, $assignment->{'parameter3'}, $assignment->{'parameter4'}, $assignment->{'parameter5'});
+ }
+ else
+ {
+ installer::exiter::exit_program("ERROR: gid: $gid, key: $key ! Unknown Windows CustomAction table: $assignmenthashref->{'parameter1'} ! Currently supported: InstallUISequence, InstallESequence, ControlEvent, ControlCondition", "addcustomactions");
+ }
+ }
+ }
+ }
+
+ # Setting the positions in the tables
+
+ set_positions_in_table($installexecutetable, $installexecutetablename);
+ set_positions_in_table($installuitable, $installuitablename);
+ set_positions_in_table($adminexecutetable, $adminexecutetablename);
+
+ # Saving the files
+
+ installer::files::save_file($customactionidttablename, $customactionidttable);
+ installer::files::save_file($installexecutetablename, $installexecutetable);
+ installer::files::save_file($adminexecutetablename, $adminexecutetable);
+ installer::files::save_file($installuitablename, $installuitable);
+ installer::files::save_file($controleventtablename, $controleventtable);
+ installer::files::save_file($controlconditiontablename, $controlconditiontable);
+
+ my $infoline = "Updated idt file: $customactionidttablename\n";
+ push(@installer::globals::logfileinfo, $infoline);
+ $infoline = "Updated idt file: $installexecutetablename\n";
+ push(@installer::globals::logfileinfo, $infoline);
+ $infoline = "Updated idt file: $adminexecutetablename\n";
+ push(@installer::globals::logfileinfo, $infoline);
+ $infoline = "Updated idt file: $installuitablename\n";
+ push(@installer::globals::logfileinfo, $infoline);
+ $infoline = "Updated idt file: $controleventtablename\n";
+ push(@installer::globals::logfileinfo, $infoline);
+ $infoline = "Updated idt file: $controlconditiontablename\n";
+ push(@installer::globals::logfileinfo, $infoline);
+
+ installer::logger::include_timestamp_into_logfile("\nPerformance Info: addcustomactions end\n");
+}
+
+##########################################################################
+# Setting bidi attributes in idt tables
+##########################################################################
+
+sub setbidiattributes
+{
+ my ($languageidtdir, $onelanguage) = @_;
+
+ # Editing the files Dialog.idt and Control.idt
+
+ my $dialogfilename = $languageidtdir . $installer::globals::separator . "Dialog.idt";
+ my $controlfilename = $languageidtdir . $installer::globals::separator . "Control.idt";
+
+ my $dialogfile = installer::files::read_file($dialogfilename);
+ my $controlfile = installer::files::read_file($controlfilename);
+
+ # Searching attributes in Dialog.idt and adding "896".
+ # Attributes are in column 6 (from 10).
+
+ my $bidiattribute = 896;
+ for ( my $i = 0; $i <= $#{$dialogfile}; $i++ )
+ {
+ if ( $i < 3 ) { next; }
+ if ( ${$dialogfile}[$i] =~ /^\s*(.*?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\s*$/ )
+ {
+ my $one = $1;
+ my $two = $2;
+ my $three = $3;
+ my $four = $4;
+ my $five = $5;
+ my $attribute = $6;
+ my $seven = $7;
+ my $eight = $8;
+ $attribute = $attribute + $bidiattribute;
+ ${$dialogfile}[$i] = "$one\t$two\t$three\t$four\t$five\t$attribute\t$seven\t$eight\n";
+ }
+ }
+
+ # Searching attributes in Control.idt and adding "224".
+ # Attributes are in column 8 (from 12).
+
+ $bidiattribute = 224;
+ for ( my $i = 0; $i <= $#{$controlfile}; $i++ )
+ {
+ if ( $i < 3 ) { next; }
+ if ( ${$controlfile}[$i] =~ /^\s*(.*?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\s*$/ )
+ {
+ my $one = $1;
+ my $two = $2;
+ my $three = $3;
+ my $four = $4;
+ my $five = $5;
+ my $six = $6;
+ my $seven = $7;
+ my $attribute = $8;
+ my $nine = $9;
+ my $ten = $10;
+ my $eleven = $11;
+ my $twelve = $12;
+ $attribute = $attribute + $bidiattribute;
+ ${$controlfile}[$i] = "$one\t$two\t$three\t$four\t$five\t$six\t$seven\t$attribute\t$nine\t$ten\t$eleven\t$twelve\n";
+ }
+ }
+
+ # Saving the file
+
+ installer::files::save_file($dialogfilename, $dialogfile);
+ $infoline = "Set bidi support in idt file \"$dialogfilename\" for language $onelanguage\n";
+ push(@installer::globals::logfileinfo, $infoline);
+
+ installer::files::save_file($controlfilename, $controlfile);
+ $infoline = "Set bidi support in idt file \"$controlfilename\" for language $onelanguage\n";
+ push(@installer::globals::logfileinfo, $infoline);
+}
+
+1;
diff --git a/solenv/bin/modules/installer/windows/inifile.pm b/solenv/bin/modules/installer/windows/inifile.pm
new file mode 100644
index 000000000000..de279da8d48b
--- /dev/null
+++ b/solenv/bin/modules/installer/windows/inifile.pm
@@ -0,0 +1,146 @@
+#*************************************************************************
+#
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+#
+# Copyright 2000, 2010 Oracle and/or its affiliates.
+#
+# OpenOffice.org - a multi-platform office productivity suite
+#
+# This file is part of OpenOffice.org.
+#
+# OpenOffice.org is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License version 3
+# only, as published by the Free Software Foundation.
+#
+# OpenOffice.org is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License version 3 for more details
+# (a copy is included in the LICENSE file that accompanied this code).
+#
+# You should have received a copy of the GNU Lesser General Public License
+# version 3 along with OpenOffice.org. If not, see
+# <http://www.openoffice.org/license.html>
+# for a copy of the LGPLv3 License.
+#
+#*************************************************************************
+
+package installer::windows::inifile;
+
+use installer::existence;
+use installer::files;
+use installer::globals;
+use installer::windows::idtglobal;
+
+####################################################
+# Setting the profile for a special profileitem
+####################################################
+
+sub get_profile_for_profileitem
+{
+ my ($profileid, $filesref) = @_;
+
+ my $profile = installer::existence::get_specified_file($filesref, $profileid);
+
+ return $profile;
+}
+
+####################################################
+# Checking whether profile is included in patch
+####################################################
+
+sub profile_has_patch_flag
+{
+ my ($profile) = @_;
+
+ my $in_patch = 0;
+
+ my $styles = "";
+ if ( $profile->{'Styles'} ) { $styles = $profile->{'Styles'}; }
+ if ( $styles =~ /\bPATCH\b/ ) { $in_patch = 1; }
+
+ return $in_patch;
+}
+
+####################################################
+# Checking whether profile is part of product
+####################################################
+
+sub file_is_part_of_product
+{
+ my ($profilegid, $filesref) = @_;
+
+ my $part_of_product = 0;
+
+ for ( my $i = 0; $i <= $#{$filesref}; $i++ )
+ {
+ $onefile = ${$filesref}[$i];
+ my $filegid = $onefile->{'gid'};
+
+ if ( $filegid eq $profilegid )
+ {
+ $part_of_product = 1;
+ last;
+ }
+ }
+
+ return $part_of_product;
+}
+
+###########################################################################################################
+# Creating the file IniFile.idt dynamically
+# Content:
+# IniFile\tFileName\tDirProperty\tSection\tKey\tValue\tAction\tComponent_
+###########################################################################################################
+
+sub create_inifile_table
+{
+ my ($inifiletableentries, $filesref, $basedir) = @_;
+
+ my @inifiletable = ();
+
+ installer::windows::idtglobal::write_idt_header(\@inifiletable, "inifile");
+
+ for ( my $i = 0; $i <= $#{$inifiletableentries}; $i++ )
+ {
+ my $profileitem = ${$inifiletableentries}[$i];
+
+ my $profileid = $profileitem->{'ProfileID'};
+
+ # Is this profile part of the product? This is not sure, for example in patch process.
+ # If the profile is not part of the product, this ProfileItem must be ignored.
+
+ if ( ! file_is_part_of_product($profileid, $filesref) ) { next; }
+
+ my $profile = get_profile_for_profileitem($profileid, $filesref);
+
+ if (( $installer::globals::patch ) && ( ! profile_has_patch_flag($profile) )) { next; }
+
+ my %inifile = ();
+
+ $inifile{'IniFile'} = $profileitem->{'Inifiletablekey'};
+ $inifile{'FileName'} = $profile->{'Name'};
+ $inifile{'DirProperty'} = $profile->{'uniquedirname'};
+ $inifile{'Section'} = $profileitem->{'Section'};
+ $inifile{'Key'} = $profileitem->{'Key'};
+ $inifile{'Value'} = $profileitem->{'Value'};
+ $inifile{'Action'} = $profileitem->{'Inifiletableaction'};
+ $inifile{'Component_'} = $profile->{'componentname'};
+
+ my $oneline = $inifile{'IniFile'} . "\t" . $inifile{'FileName'} . "\t" . $inifile{'DirProperty'} . "\t"
+ . $inifile{'Section'} . "\t" . $inifile{'Key'} . "\t" . $inifile{'Value'} . "\t"
+ . $inifile{'Action'} . "\t" . $inifile{'Component_'} . "\n";
+
+ push(@inifiletable, $oneline);
+ }
+
+ # Saving the file
+
+ my $inifiletablename = $basedir . $installer::globals::separator . "IniFile.idt";
+ installer::files::save_file($inifiletablename ,\@inifiletable);
+ my $infoline = "Created idt file: $inifiletablename\n";
+ push(@installer::globals::logfileinfo, $infoline);
+
+}
+
+1;
diff --git a/solenv/bin/modules/installer/windows/java.pm b/solenv/bin/modules/installer/windows/java.pm
new file mode 100644
index 000000000000..11ccd7076f71
--- /dev/null
+++ b/solenv/bin/modules/installer/windows/java.pm
@@ -0,0 +1,120 @@
+#*************************************************************************
+#
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+#
+# Copyright 2000, 2010 Oracle and/or its affiliates.
+#
+# OpenOffice.org - a multi-platform office productivity suite
+#
+# This file is part of OpenOffice.org.
+#
+# OpenOffice.org is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License version 3
+# only, as published by the Free Software Foundation.
+#
+# OpenOffice.org is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License version 3 for more details
+# (a copy is included in the LICENSE file that accompanied this code).
+#
+# You should have received a copy of the GNU Lesser General Public License
+# version 3 along with OpenOffice.org. If not, see
+# <http://www.openoffice.org/license.html>
+# for a copy of the LGPLv3 License.
+#
+#*************************************************************************
+
+package installer::windows::java;
+
+use installer::exiter;
+use installer::files;
+use installer::globals;
+use installer::windows::idtglobal;
+
+####################################################################################
+# Writing content into RegLocat.idt and AppSearc.idt to find Java on system
+####################################################################################
+
+sub update_java_tables
+{
+ my ($basedir, $allvariables) = @_;
+
+ my $reglocatfile = "";
+ my $appsearchfile = "";
+
+ my $reglocatfilename = $basedir . $installer::globals::separator . "RegLocat.idt";
+ my $appsearchfilename = $basedir . $installer::globals::separator . "AppSearc.idt";
+ my $signaturefilename = $basedir . $installer::globals::separator . "Signatur.idt";
+
+ if ( -f $reglocatfilename )
+ {
+ $reglocatfile = installer::files::read_file($reglocatfilename);
+ }
+ else
+ {
+ my @reglocattable = ();
+ $reglocatfile = \@reglocattable;
+ installer::windows::idtglobal::write_idt_header($reglocatfile, "reglocat");
+ }
+
+ if ( -f $appsearchfilename )
+ {
+ $appsearchfile = installer::files::read_file($appsearchfilename);
+ }
+ else
+ {
+ my @appsearchtable = ();
+ $appsearchfile = \@appsearchtable;
+ installer::windows::idtglobal::write_idt_header($appsearchfile, "appsearch");
+ }
+
+ if ( -f $signaturefilename )
+ {
+ $signaturefile = installer::files::read_file($signaturefilename);
+ }
+ else
+ {
+ my @signaturetable = ();
+ $signaturefile = \@signaturetable;
+ installer::windows::idtglobal::write_idt_header($signaturefile, "signatur");
+ }
+
+ # Writing content into this tables
+ # Java version is saved in scp project
+ # $installer::globals::javafile was defined in installer::windows::idtglobal::add_childprojects
+
+ if ( ! $installer::globals::javafile->{'Javaversion'} ) { installer::exiter::exit_program("ERROR: \"Javaversion\" has to be defined in $installer::globals::javafile->{'gid'} in scp project!", "update_java_tables"); }
+
+ my $javastring = $installer::globals::javafile->{'Javaversion'};
+
+ my $signature = "JavaReg";
+ my $rootvalue = "2";
+ my $key = "Software\\JavaSoft\\Java Runtime Environment\\" . $javastring;
+ my $name = "JavaHome";
+ my $type = 2;
+ my $property = "JAVAPATH";
+
+ my $oneline = $signature . "\t" . $rootvalue . "\t" . $key . "\t" . $name . "\t" . $type . "\n";
+ push(@{$reglocatfile}, $oneline);
+
+ $oneline = $property . "\t" . $signature . "\n";
+ push(@{$appsearchfile}, $oneline);
+
+ # Saving the files
+
+ installer::files::save_file($reglocatfilename ,$reglocatfile);
+ my $infoline = "Updated idt file for Java: $reglocatfilename\n";
+ push(@installer::globals::logfileinfo, $infoline);
+
+ installer::files::save_file($appsearchfilename ,$appsearchfile);
+ $infoline = "Updated idt file for Java: $appsearchfilename\n";
+ push(@installer::globals::logfileinfo, $infoline);
+
+ installer::files::save_file($signaturefilename ,$signaturefile);
+ $infoline = "Updated idt file: $signaturefilename\n";
+ push(@installer::globals::logfileinfo, $infoline);
+
+}
+
+1;
diff --git a/solenv/bin/modules/installer/windows/language.pm b/solenv/bin/modules/installer/windows/language.pm
new file mode 100644
index 000000000000..c0dca543eb56
--- /dev/null
+++ b/solenv/bin/modules/installer/windows/language.pm
@@ -0,0 +1,74 @@
+#*************************************************************************
+#
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+#
+# Copyright 2000, 2010 Oracle and/or its affiliates.
+#
+# OpenOffice.org - a multi-platform office productivity suite
+#
+# This file is part of OpenOffice.org.
+#
+# OpenOffice.org is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License version 3
+# only, as published by the Free Software Foundation.
+#
+# OpenOffice.org is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License version 3 for more details
+# (a copy is included in the LICENSE file that accompanied this code).
+#
+# You should have received a copy of the GNU Lesser General Public License
+# version 3 along with OpenOffice.org. If not, see
+# <http://www.openoffice.org/license.html>
+# for a copy of the LGPLv3 License.
+#
+#*************************************************************************
+
+package installer::windows::language;
+
+use installer::exiter;
+
+####################################################
+# Determining the Windows language (LCID)
+# English: 1033
+####################################################
+
+sub get_windows_language
+{
+ my ($language) = @_;
+
+ my $windowslanguage = "";
+
+ if ( $installer::globals::msilanguage->{$language} ) { $windowslanguage = $installer::globals::msilanguage->{$language}; }
+
+ if ( $windowslanguage eq "" ) { installer::exiter::exit_program("ERROR: Unknown language $language in function get_windows_language", "get_windows_language"); }
+
+ return $windowslanguage;
+}
+
+####################################################
+# Determining the Windows language ANSI-Codepage
+# English: 1252
+####################################################
+
+sub get_windows_encoding
+{
+ my ($language) = @_;
+
+ my $windowsencoding = "";
+
+ if ( $installer::globals::msiencoding->{$language} ) { $windowsencoding = $installer::globals::msiencoding->{$language}; }
+
+ # if ( $windowsencoding eq "" ) { installer::exiter::exit_program("ERROR: Unknown language $language in function get_windows_encoding", "get_windows_encoding"); }
+ if ( $windowsencoding eq "" ) { $windowsencoding = "0"; } # setting value, if the language is not listed in the encodinglist
+
+ if ( $windowsencoding eq "0" ) { $windowsencoding = "65001"; } # languages with "0" have to be available in UTF-8 (65001)
+
+ # Asian multilingual installation sets need a code neutral Windows Installer database -> $windowsencoding = 0
+ if (( $language eq "en-US" ) && (( $installer::globals::product =~ /suitemulti/i ) || ( $installer::globals::product =~ /officemulti/i ) || ( $installer::globals::product =~ /c05office/i ) || ( $installer::globals::added_english ))) { $windowsencoding = "0"; }
+
+ return $windowsencoding;
+}
+
+1; \ No newline at end of file
diff --git a/solenv/bin/modules/installer/windows/media.pm b/solenv/bin/modules/installer/windows/media.pm
new file mode 100644
index 000000000000..35e90f6a3856
--- /dev/null
+++ b/solenv/bin/modules/installer/windows/media.pm
@@ -0,0 +1,458 @@
+#*************************************************************************
+#
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+#
+# Copyright 2000, 2010 Oracle and/or its affiliates.
+#
+# OpenOffice.org - a multi-platform office productivity suite
+#
+# This file is part of OpenOffice.org.
+#
+# OpenOffice.org is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License version 3
+# only, as published by the Free Software Foundation.
+#
+# OpenOffice.org is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License version 3 for more details
+# (a copy is included in the LICENSE file that accompanied this code).
+#
+# You should have received a copy of the GNU Lesser General Public License
+# version 3 along with OpenOffice.org. If not, see
+# <http://www.openoffice.org/license.html>
+# for a copy of the LGPLv3 License.
+#
+#*************************************************************************
+
+package installer::windows::media;
+
+use installer::exiter;
+use installer::files;
+use installer::globals;
+use installer::windows::idtglobal;
+
+##############################################################
+# Returning the diskid for the media table.
+##############################################################
+
+sub get_media_diskid
+{
+ my ($id) = @_;
+
+ return $id;
+}
+
+##############################################################
+# Returning the lastsequence for the media table.
+##############################################################
+
+sub get_media_lastsequence
+{
+ my ($fileref) = @_;
+
+ return $fileref->{'sequencenumber'};
+}
+
+##############################################################
+# Returning the diskprompt for the media table.
+##############################################################
+
+sub get_media_diskprompt
+{
+ return 1;
+}
+
+##############################################################
+# Returning the cabinet file name for the media table.
+##############################################################
+
+sub get_media_cabinet
+{
+ my ($id) = @_;
+
+ my $number = 1000 + $id;
+ my $filename = "f_" . $number . ".cab";
+
+ if ( $installer::globals::include_cab_in_msi ) { $filename = "\#" . $filename; }
+
+ return $filename;
+}
+
+##############################################################
+# Returning the volumelabel for the media table.
+##############################################################
+
+sub get_media_volumelabel
+{
+ return "DISK1";
+}
+
+##############################################################
+# Returning the source for the media table.
+##############################################################
+
+sub get_media_source
+{
+ return "";
+}
+
+##############################################################
+# Saving the cabinet file name in the files collector.
+# This is useful for making a list to connect the
+# source of each file with the destination cabinet file.
+##############################################################
+
+sub set_cabinetfilename_for_component_in_file_collector
+{
+ my ($cabinetfilename, $filesref, $componentname, $max) = @_;
+
+ for ( my $i = 0; $i <= $max; $i++ )
+ {
+ my $onefile = ${$filesref}[$i];
+ my $component = $onefile->{'componentname'};
+
+ if ( $component eq $componentname )
+ {
+ my $cabinet = "";
+
+ if ( $onefile->{'cabinet'} ) { $cabinet = $onefile->{'cabinet'}; }
+
+ if ( $cabinet eq "" )
+ {
+ $onefile->{'cabinet'} = $cabinetfilename;
+ }
+ }
+ }
+}
+
+#################################################
+# Creating the cab file name dynamically
+#################################################
+
+sub generate_cab_filename_for_some_cabs
+{
+ my ( $allvariables, $id ) = @_;
+
+ my $name = $allvariables->{'PRODUCTNAME'};
+
+ $name = lc($name);
+ $name =~ s/\.//g;
+ $name =~ s/\s//g;
+
+ # possibility to overwrite the name with variable CABFILENAME
+ if ( $allvariables->{'CABFILENAME'} ) { $name = $allvariables->{'CABFILENAME'}; }
+
+ $name = $name . $id . ".cab";
+
+ if ( $installer::globals::include_cab_in_msi ) { $name = "\#" . $name; }
+
+ return $name;
+}
+
+#################################################
+# Creating the cab file name for cab files
+# defined in packages.
+#################################################
+
+sub get_cabfilename
+{
+ my ($name) = @_;
+
+ if ( $installer::globals::include_cab_in_msi ) { $name = "\#" . $name; }
+
+ return $name;
+}
+
+#################################################
+# Creating the cab file name dynamically
+#################################################
+
+sub generate_cab_filename
+{
+ my ( $allvariables ) = @_;
+
+ my $name = $allvariables->{'PRODUCTNAME'};
+
+ $name = lc($name);
+ $name =~ s/\.//g;
+ $name =~ s/\s//g;
+
+ # possibility to overwrite the name with variable CABFILENAME
+ if ( $allvariables->{'CABFILENAME'} ) { $name = $allvariables->{'CABFILENAME'}; }
+
+ $name = $name . ".cab";
+
+ if ( $installer::globals::include_cab_in_msi ) { $name = "\#" . $name; }
+
+ return $name;
+}
+
+sub get_maximum_filenumber
+{
+ my ($allfiles, $maxcabfilenumber) = @_;
+
+ my $maxfile = 0;
+
+ while ( ! ( $allfiles%$maxcabfilenumber == 0 ))
+ {
+ $allfiles++;
+ }
+
+ $maxfile = $allfiles / $maxcabfilenumber;
+
+ $maxfile++; # for securitry
+
+ return $maxfile;
+}
+
+#################################################################################
+# Setting the last sequence for the cabinet files
+#################################################################################
+
+sub get_last_sequence
+{
+ my ( $cabfilename, $alludpatelastsequences ) = @_;
+
+ my $sequence = 0;
+
+ if (( $installer::globals::updatedatabase ) && ( exists($alludpatelastsequences->{$cabfilename}) ))
+ {
+ $sequence = $alludpatelastsequences->{$cabfilename};
+ }
+ else
+ {
+ $sequence = $installer::globals::lastsequence{$cabfilename};
+ }
+
+ return $sequence;
+}
+
+#################################################################################
+# Creating the file Media.idt dynamically
+# Content:
+# DiskId LastSequence DiskPrompt Cabinet VolumeLabel Source
+# Idea: Every component is packed into each own cab file
+#################################################################################
+
+sub create_media_table
+{
+ my ($filesref, $basedir, $allvariables, $alludpatelastsequences, $allupdatediskids) = @_;
+
+ my @mediatable = ();
+
+ my $diskid = 0;
+
+ installer::windows::idtglobal::write_idt_header(\@mediatable, "media");
+
+ if ( $allvariables->{'INCLUDE_CAB_IN_MSI'} ) { $installer::globals::include_cab_in_msi = 1; }
+
+ if ( $installer::globals::use_packages_for_cabs )
+ {
+ my $cabfile;
+ foreach $cabfile ( sort keys %installer::globals::lastsequence )
+ {
+ my %media = ();
+ $diskid++;
+
+ $media{'DiskId'} = get_media_diskid($diskid);
+ $media{'LastSequence'} = get_last_sequence($cabfile, $alludpatelastsequences);
+ $media{'DiskPrompt'} = get_media_diskprompt();
+ $media{'Cabinet'} = get_cabfilename($cabfile);
+ $media{'VolumeLabel'} = get_media_volumelabel();
+ $media{'Source'} = get_media_source();
+
+ my $oneline = $media{'DiskId'} . "\t" . $media{'LastSequence'} . "\t" . $media{'DiskPrompt'} . "\t"
+ . $media{'Cabinet'} . "\t" . $media{'VolumeLabel'} . "\t" . $media{'Source'} . "\n";
+
+ push(@mediatable, $oneline);
+
+ # Comparing the disk id with the disk id from update database. Both have to be identical. New files have to be added
+ # to the new pff cabinet file. And existing cab files must not be removed.
+ if ( $installer::globals::updatedatabase )
+ {
+ # Comparing lines in new media table with line from media table in udpate database.
+ if ( exists($allupdatediskids->{$media{'Cabinet'}}) )
+ {
+ if ( $media{'DiskId'} != $allupdatediskids->{$media{'Cabinet'}} )
+ {
+ installer::exiter::exit_program("ERROR: Different DiskIDs for cab file \"$media{'Cabinet'}\".\nCurrent installation set: \"$media{'DiskId'}\", but update database used \"$allupdatediskids->{$media{'Cabinet'}}\".\nWere cabinet files removed or added?", "create_media_table");
+ }
+ }
+ else
+ {
+ my $localinfoline = "Warning: Could not find cabinet file \"$media{'Cabinet'}}\" in update database. This seems to be an new cabinet file!?\n";
+ push(@installer::globals::logfileinfo, $localinfoline);
+ }
+ }
+ }
+
+ # one new cabinet file for all files added after the final release
+ if (( $installer::globals::updatedatabase ) && ( $installer::globals::pfffileexists ))
+ {
+ my %media = ();
+ $diskid++;
+
+ $media{'DiskId'} = get_media_diskid($diskid) + $installer::globals::mergemodulenumber; # Adding mergemodulenumber, because this files are included later
+ $media{'LastSequence'} = $installer::globals::updatesequencecounter;
+ $media{'DiskPrompt'} = get_media_diskprompt();
+ $media{'Cabinet'} = get_cabfilename($installer::globals::pffcabfilename);
+ $media{'VolumeLabel'} = get_media_volumelabel();
+ $media{'Source'} = get_media_source();
+
+ my $oneline = $media{'DiskId'} . "\t" . $media{'LastSequence'} . "\t" . $media{'DiskPrompt'} . "\t"
+ . $media{'Cabinet'} . "\t" . $media{'VolumeLabel'} . "\t" . $media{'Source'} . "\n";
+
+ push(@mediatable, $oneline);
+ }
+
+ }
+ elsif ( $installer::globals::cab_file_per_component )
+ {
+ for ( my $i = 0; $i <= $#{$filesref}; $i++ )
+ {
+ my $onefile = ${$filesref}[$i];
+ my $nextfile = ${$filesref}[$i+1];
+
+ my $filecomponent = "";
+ my $nextcomponent = "";
+
+ if ( $onefile->{'componentname'} ) { $filecomponent = $onefile->{'componentname'}; }
+ if ( $nextfile->{'componentname'} ) { $nextcomponent = $nextfile->{'componentname'}; }
+
+ if ( $filecomponent eq $nextcomponent )
+ {
+ next; # nothing to do, this is not the last file of a component
+ }
+
+ my %media = ();
+ $diskid++;
+
+ $media{'DiskId'} = get_media_diskid($diskid);
+ $media{'LastSequence'} = get_media_lastsequence($onefile);
+ $media{'DiskPrompt'} = get_media_diskprompt();
+ $media{'Cabinet'} = get_media_cabinet($diskid);
+ $media{'VolumeLabel'} = get_media_volumelabel();
+ $media{'Source'} = get_media_source();
+
+ my $oneline = $media{'DiskId'} . "\t" . $media{'LastSequence'} . "\t" . $media{'DiskPrompt'} . "\t"
+ . $media{'Cabinet'} . "\t" . $media{'VolumeLabel'} . "\t" . $media{'Source'} . "\n";
+
+ push(@mediatable, $oneline);
+
+ $media{'Cabinet'} =~ s/^\s*\#//; # removing leading hash
+ set_cabinetfilename_for_component_in_file_collector($media{'Cabinet'}, $filesref, $filecomponent, $i);
+ }
+ }
+ elsif ( $installer::globals::fix_number_of_cab_files )
+ {
+ # number of cabfiles
+ my $maxcabfilenumber = $installer::globals::number_of_cabfiles;
+ if ( $allvariables->{'CABFILENUMBER'} ) { $maxcabfilenumber = $allvariables->{'CABFILENUMBER'}; }
+ my $allfiles = $#{$filesref} + 1;
+ my $maxfilenumber = get_maximum_filenumber($allfiles, $maxcabfilenumber);
+ # my $maxfilenumber = 1000; # maximum 1000 files in each cabinet file
+ my $cabfilenumber = 0;
+ my $cabfull = 0;
+ my $counter = 0;
+
+ # Sorting of files collector files required !
+ # Attention: The order in the cab file is not guaranteed (especially in udpate process)
+
+ for ( my $i = 0; $i <= $#{$filesref}; $i++ )
+ {
+ if (( $counter >= $maxfilenumber ) || ( $i == $#{$filesref} )) { $cabfull = 1; }
+
+ $counter++; # counting the files in the cab file
+
+ my $onefile = ${$filesref}[$i];
+ my $nextfile = ${$filesref}[$i+1];
+
+ my $filecomponent = "";
+ my $nextcomponent = "";
+
+ if ( $onefile->{'componentname'} ) { $filecomponent = $onefile->{'componentname'}; }
+ if ( $nextfile->{'componentname'} ) { $nextcomponent = $nextfile->{'componentname'}; }
+
+ if ( $filecomponent eq $nextcomponent ) # all files of one component have to be in one cab file
+ {
+ next; # nothing to do, this is not the last file of a component
+ }
+
+ if ( $cabfull )
+ {
+ my %media = ();
+ $cabfilenumber++;
+
+ $media{'DiskId'} = get_media_diskid($cabfilenumber);
+ # $media{'LastSequence'} = get_media_lastsequence($onefile);
+ $media{'LastSequence'} = $i + 1; # This should be correct, also for unsorted files collectors
+ $media{'DiskPrompt'} = get_media_diskprompt();
+ $media{'Cabinet'} = generate_cab_filename_for_some_cabs($allvariables, $cabfilenumber);
+ $media{'VolumeLabel'} = get_media_volumelabel();
+ $media{'Source'} = get_media_source();
+
+ my $oneline = $media{'DiskId'} . "\t" . $media{'LastSequence'} . "\t" . $media{'DiskPrompt'} . "\t"
+ . $media{'Cabinet'} . "\t" . $media{'VolumeLabel'} . "\t" . $media{'Source'} . "\n";
+
+ push(@mediatable, $oneline);
+
+ # Saving the cabinet file name in the file collector
+
+ $media{'Cabinet'} =~ s/^\s*\#//; # removing leading hash
+
+ for ( my $j = 0; $j <= $i; $j++ )
+ {
+ my $onefile = ${$filesref}[$j];
+ if ( ! $onefile->{'cabinet'} ) { $onefile->{'cabinet'} = $media{'Cabinet'}; }
+ }
+
+ $cabfull = 0;
+ $counter = 0;
+ }
+ }
+ }
+ elsif ( $installer::globals::one_cab_file )
+ {
+ my %media = ();
+ $diskid++;
+
+ my $maximumfile = $#{$filesref};
+
+ $media{'DiskId'} = get_media_diskid($diskid);
+ # $media{'LastSequence'} = ${$filesref}[$maximumfile]->{'sequencenumber'}; # sequence number of the last file
+ $media{'LastSequence'} = $maximumfile + 1; # This works also for unsorted file collector
+ $media{'DiskPrompt'} = get_media_diskprompt();
+ $media{'Cabinet'} = generate_cab_filename($allvariables);
+ $media{'VolumeLabel'} = get_media_volumelabel();
+ $media{'Source'} = get_media_source();
+
+ my $oneline = $media{'DiskId'} . "\t" . $media{'LastSequence'} . "\t" . $media{'DiskPrompt'} . "\t"
+ . $media{'Cabinet'} . "\t" . $media{'VolumeLabel'} . "\t" . $media{'Source'} . "\n";
+
+ push(@mediatable, $oneline);
+
+ # Saving the cabinet file name in the file collector
+
+ $media{'Cabinet'} =~ s/^\s*\#//; # removing leading hash
+
+ for ( my $i = 0; $i <= $#{$filesref}; $i++ )
+ {
+ my $onefile = ${$filesref}[$i];
+ $onefile->{'cabinet'} = $media{'Cabinet'};
+ }
+ }
+ else
+ {
+ installer::exiter::exit_program("ERROR: No cab file specification in globals.pm !", "create_media_table");
+ }
+
+ # Saving the file
+
+ my $mediatablename = $basedir . $installer::globals::separator . "Media.idt";
+ installer::files::save_file($mediatablename ,\@mediatable);
+ my $infoline = "Created idt file: $mediatablename\n";
+ push(@installer::globals::logfileinfo, $infoline);
+}
+
+1;
diff --git a/solenv/bin/modules/installer/windows/mergemodule.pm b/solenv/bin/modules/installer/windows/mergemodule.pm
new file mode 100755
index 000000000000..eb524fdafc3e
--- /dev/null
+++ b/solenv/bin/modules/installer/windows/mergemodule.pm
@@ -0,0 +1,1652 @@
+#*************************************************************************
+#
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+#
+# Copyright 2000, 2010 Oracle and/or its affiliates.
+#
+# OpenOffice.org - a multi-platform office productivity suite
+#
+# This file is part of OpenOffice.org.
+#
+# OpenOffice.org is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License version 3
+# only, as published by the Free Software Foundation.
+#
+# OpenOffice.org is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License version 3 for more details
+# (a copy is included in the LICENSE file that accompanied this code).
+#
+# You should have received a copy of the GNU Lesser General Public License
+# version 3 along with OpenOffice.org. If not, see
+# <http://www.openoffice.org/license.html>
+# for a copy of the LGPLv3 License.
+#
+#*************************************************************************
+
+package installer::windows::mergemodule;
+
+use Cwd;
+use Digest::MD5;
+use installer::converter;
+use installer::exiter;
+use installer::files;
+use installer::globals;
+use installer::logger;
+use installer::pathanalyzer;
+use installer::remover;
+use installer::scriptitems;
+use installer::systemactions;
+use installer::worker;
+use installer::windows::idtglobal;
+use installer::windows::language;
+
+#################################################################
+# Merging the Windows MergeModules into the msi database.
+#################################################################
+
+sub merge_mergemodules_into_msi_database
+{
+ my ($mergemodules, $filesref, $msifilename, $languagestringref, $language, $languagefile, $allvariables, $includepatharrayref, $allupdatesequences, $allupdatelastsequences, $allupdatediskids) = @_;
+
+ my $domerge = 0;
+ if (( $#{$mergemodules} > -1 ) && ( ! $installer::globals::patch ) && ( ! $installer::globals::languagepack )) { $domerge = 1; }
+
+ if ( $domerge )
+ {
+ installer::logger::include_header_into_logfile("Merging merge modules into msi database");
+ installer::logger::print_message( "... merging msm files into msi database ... \n" );
+ installer::logger::include_timestamp_into_logfile("\nPerformance Info: MergeModule into msi database, start");
+
+ my $msidb = "msidb.exe"; # Has to be in the path
+ my $cabinetfile = "MergeModule.CABinet"; # the name of each cabinet file in a merge file
+ my $infoline = "";
+ my $systemcall = "";
+ my $returnvalue = "";
+
+ # 1. Analyzing the MergeModule (has only to be done once)
+ # a. -> Extracting cabinet file: msidb.exe -d <msmfile> -x MergeModule.CABinet
+ # b. -> Number of files in cabinet file: msidb.exe -d <msmfile> -f <directory> -e File
+ # c. -> List of components: msidb.exe -d <msmfile> -f <directory> -e Component
+
+ if ( ! $installer::globals::mergemodules_analyzed )
+ {
+ installer::logger::include_timestamp_into_logfile("\nPerformance Info: Analyzing MergeModules, start");
+ $infoline = "Analyzing all Merge Modules\n\n";
+ push( @installer::globals::logfileinfo, $infoline);
+
+ %installer::globals::mergemodules = ();
+
+ my $mergemoduledir = installer::systemactions::create_directories("mergefiles", $languagestringref);
+ # push(@installer::globals::removedirs, $mergemoduledir);
+
+ my $mergemodule;
+ foreach $mergemodule ( @{$mergemodules} )
+ {
+ my $filename = $mergemodule->{'Name'};
+ my $mergefile = installer::scriptitems::get_sourcepath_from_filename_and_includepath(\$filename, $includepatharrayref, 1);
+
+ if ( ! -f $$mergefile ) { installer::exiter::exit_program("ERROR: msm file not found: $filename !", "merge_mergemodules_into_msi_database"); }
+ my $completesource = $$mergefile;
+
+ my $mergegid = $mergemodule->{'gid'};
+ my $workdir = $mergemoduledir . $installer::globals::separator . $mergegid;
+ if ( ! -d $workdir ) { installer::systemactions::create_directory($workdir); }
+
+ $infoline = "Analyzing Merge Module: $filename\n";
+ push( @installer::globals::logfileinfo, $infoline);
+
+ # copy msm file into working directory
+ my $completedest = $workdir . $installer::globals::separator . $filename;
+ installer::systemactions::copy_one_file($completesource, $completedest);
+ if ( ! -f $completedest ) { installer::exiter::exit_program("ERROR: msm file not found: $completedest !", "merge_mergemodules_into_msi_database"); }
+
+ # changing directory
+ my $from = cwd();
+ my $to = $workdir;
+ chdir($to);
+
+ # remove an existing cabinet file
+ if ( -f $cabinetfile ) { unlink($cabinetfile); }
+
+ # exclude cabinet file
+ $systemcall = $msidb . " -d " . $filename . " -x " . $cabinetfile;
+ $returnvalue = system($systemcall);
+
+ $infoline = "Systemcall: $systemcall\n";
+ push( @installer::globals::logfileinfo, $infoline);
+
+ if ($returnvalue)
+ {
+ $infoline = "ERROR: Could not execute $systemcall !\n";
+ push( @installer::globals::logfileinfo, $infoline);
+ installer::exiter::exit_program("ERROR: Could not extract cabinet file from merge file: $completedest !", "merge_mergemodules_into_msi_database");
+ }
+ else
+ {
+ $infoline = "Success: Executed $systemcall successfully!\n";
+ push( @installer::globals::logfileinfo, $infoline);
+ }
+
+ # exclude tables from mergefile
+ # Attention: All listed tables have to exist in the database. If they not exist, an error window pops up
+ # and the return value of msidb.exe is not zero. The error window makes it impossible to check the existence
+ # of a table with the help of the return value.
+ # Solution: Export of all tables by using "*" . Some tables must exist (File Component Directory), other
+ # tables do not need to exist (MsiAssembly).
+
+ if ( $^O =~ /cygwin/i ) {
+ # msidb.exe really wants backslashes. (And double escaping because system() expands the string.)
+ my $localworkdir = $workdir;
+ $localworkdir =~ s/\//\\\\/g;
+ $systemcall = $msidb . " -d " . $filename . " -f " . $localworkdir . " -e \\\*";
+ }
+ else
+ {
+ # $systemcall = $msidb . " -d " . $filename . " -f " . $workdir . " -e File Component MsiAssembly Directory";
+ $systemcall = $msidb . " -d " . $filename . " -f " . $workdir . " -e \*";
+ }
+
+ $returnvalue = system($systemcall);
+
+ $infoline = "Systemcall: $systemcall\n";
+ push( @installer::globals::logfileinfo, $infoline);
+
+ if ($returnvalue)
+ {
+ $infoline = "ERROR: Could not execute $systemcall !\n";
+ push( @installer::globals::logfileinfo, $infoline);
+ installer::exiter::exit_program("ERROR: Could not exclude tables from merge file: $completedest !", "merge_mergemodules_into_msi_database");
+ }
+ else
+ {
+ $infoline = "Success: Executed $systemcall successfully!\n";
+ push( @installer::globals::logfileinfo, $infoline);
+ }
+
+ # Determining files
+ my $idtfilename = "File.idt"; # must exist
+ if ( ! -f $idtfilename ) { installer::exiter::exit_program("ERROR: File \"$idtfilename\" not found in directory \"$workdir\" !", "merge_mergemodules_into_msi_database"); }
+ my $filecontent = installer::files::read_file($idtfilename);
+ my @file_idt_content = ();
+ my $filecounter = 0;
+ my %mergefilesequence = ();
+ for ( my $i = 0; $i <= $#{$filecontent}; $i++ )
+ {
+ if ( $i <= 2 ) { next; } # ignoring first three lines
+ if ( ${$filecontent}[$i] =~ /^\s*$/ ) { next; } # ignoring empty lines
+ $filecounter++;
+ push(@file_idt_content, ${$filecontent}[$i]);
+ if ( ${$filecontent}[$i] =~ /^\s*(.+?)\t(.+?)\t(.+?)\t(.+?)\t(.*?)\t(.*?)\t(.*?)\t(\d+?)\s*$/ )
+ {
+ my $filename = $1;
+ my $filesequence = $8;
+ $mergefilesequence{$filename} = $filesequence;
+ }
+ else
+ {
+ my $linecount = $i + 1;
+ installer::exiter::exit_program("ERROR: Unknown line format in table \"$idtfilename\" (line $linecount) !", "merge_mergemodules_into_msi_database");
+ }
+ }
+
+ # Determining components
+ $idtfilename = "Component.idt"; # must exist
+ if ( ! -f $idtfilename ) { installer::exiter::exit_program("ERROR: File \"$idtfilename\" not found in directory \"$workdir\" !", "merge_mergemodules_into_msi_database"); }
+ $filecontent = installer::files::read_file($idtfilename);
+ my %componentnames = ();
+ for ( my $i = 0; $i <= $#{$filecontent}; $i++ )
+ {
+ if ( $i <= 2 ) { next; } # ignoring first three lines
+ if ( ${$filecontent}[$i] =~ /^\s*$/ ) { next; } # ignoring empty lines
+ if ( ${$filecontent}[$i] =~ /^\s*(\S+)\s+/ ) { $componentnames{$1} = 1; }
+ }
+
+ # Determining directories
+ $idtfilename = "Directory.idt"; # must exist
+ if ( ! -f $idtfilename ) { installer::exiter::exit_program("ERROR: File \"$idtfilename\" not found in directory \"$workdir\" !", "merge_mergemodules_into_msi_database"); }
+ $filecontent = installer::files::read_file($idtfilename);
+ my %mergedirectories = ();
+ for ( my $i = 0; $i <= $#{$filecontent}; $i++ )
+ {
+ if ( $i <= 2 ) { next; } # ignoring first three lines
+ if ( ${$filecontent}[$i] =~ /^\s*$/ ) { next; } # ignoring empty lines
+ if ( ${$filecontent}[$i] =~ /^\s*(\S+)\s+/ ) { $mergedirectories{$1} = 1; }
+ }
+
+ # Determining assemblies
+ $idtfilename = "MsiAssembly.idt"; # does not need to exist
+ my $hasmsiassemblies = 0;
+ my %mergeassemblies = ();
+ if ( -f $idtfilename )
+ {
+ $filecontent = installer::files::read_file($idtfilename);
+ $hasmsiassemblies = 1;
+ for ( my $i = 0; $i <= $#{$filecontent}; $i++ )
+ {
+ if ( $i <= 2 ) { next; } # ignoring first three lines
+ if ( ${$filecontent}[$i] =~ /^\s*$/ ) { next; } # ignoring empty lines
+ if ( ${$filecontent}[$i] =~ /^\s*(\S+)\s+/ ) { $mergeassemblies{$1} = 1; }
+ }
+ }
+
+ # It is possible, that other tables have to be checked here. This happens, if tables in the
+ # merge module have to know the "Feature" or the "Directory", under which the content of the
+ # msm file is integrated into the msi database.
+
+ # Determining name of cabinet file in installation set
+ my $cabfilename = $mergemodule->{'Cabfilename'};
+ installer::packagelist::resolve_packagevariables(\$cabfilename, $allvariables, 0);
+
+ # Analyzing styles
+ # Flag REMOVE_FILE_TABLE is required for msvc9 Merge-Module, because otherwise msidb.exe
+ # fails during integration of msm file into msi database.
+
+ my $styles = "";
+ my $removefiletable = 0;
+ if ( $mergemodule->{'Styles'} ) { $styles = $mergemodule->{'Styles'}; }
+ if ( $styles =~ /\bREMOVE_FILE_TABLE\b/ ) { $removefiletable = 1; }
+
+ if ( $removefiletable )
+ {
+ my $removeworkdir = $workdir . $installer::globals::separator . "remove_file_idt";
+ if ( ! -d $removeworkdir ) { installer::systemactions::create_directory($removeworkdir); }
+ my $completeremovedest = $removeworkdir . $installer::globals::separator . $filename;
+ installer::systemactions::copy_one_file($completedest, $completeremovedest);
+ if ( ! -f $completeremovedest ) { installer::exiter::exit_program("ERROR: msm file not found: $completeremovedest !", "merge_mergemodules_into_msi_database"); }
+
+ # Unpacking msm file
+ if ( $^O =~ /cygwin/i ) {
+ # msidb.exe really wants backslashes. (And double escaping because system() expands the string.)
+ my $localcompleteremovedest = $completeremovedest;
+ my $localremoveworkdir = $removeworkdir;
+ $localcompleteremovedest =~ s/\//\\\\/g;
+ $localremoveworkdir =~ s/\//\\\\/g;
+ $systemcall = $msidb . " -d " . $localcompleteremovedest . " -f " . $localremoveworkdir . " -e \\\*";
+ }
+ else
+ {
+ $systemcall = $msidb . " -d " . $completeremovedest . " -f " . $removeworkdir . " -e \*";
+ }
+
+ $returnvalue = system($systemcall);
+
+ my $idtfilename = $removeworkdir . $installer::globals::separator . "File.idt";
+ if ( -f $idtfilename ) { unlink $idtfilename; }
+ unlink $completeremovedest;
+
+ # Packing msm file without "File.idt"
+ if ( $^O =~ /cygwin/i ) {
+ # msidb.exe really wants backslashes. (And double escaping because system() expands the string.)
+ my $localcompleteremovedest = $completeremovedest;
+ my $localremoveworkdir = $removeworkdir;
+ $localcompleteremovedest =~ s/\//\\\\/g;
+ $localremoveworkdir =~ s/\//\\\\/g;
+ $systemcall = $msidb . " -c -d " . $localcompleteremovedest . " -f " . $localremoveworkdir . " -i \\\*";
+ }
+ else
+ {
+ $systemcall = $msidb . " -c -d " . $completeremovedest . " -f " . $removeworkdir . " -i \*";
+ }
+ $returnvalue = system($systemcall);
+
+ # Using this msm file for merging
+ if ( -f $completeremovedest ) { $completedest = $completeremovedest; }
+ else { installer::exiter::exit_program("ERROR: Could not find msm file without File.idt: $completeremovedest !", "merge_mergemodules_into_msi_database"); }
+ }
+
+ # Saving MergeModule info
+
+ my %onemergemodulehash = ();
+ $onemergemodulehash{'mergefilepath'} = $completedest;
+ $onemergemodulehash{'workdir'} = $workdir;
+ $onemergemodulehash{'cabinetfile'} = $workdir . $installer::globals::separator . $cabinetfile;
+ $onemergemodulehash{'filenumber'} = $filecounter;
+ $onemergemodulehash{'componentnames'} = \%componentnames;
+ $onemergemodulehash{'cabfilename'} = $cabfilename;
+ $onemergemodulehash{'feature'} = $mergemodule->{'Feature'};
+ $onemergemodulehash{'rootdir'} = $mergemodule->{'RootDir'};
+ $onemergemodulehash{'name'} = $mergemodule->{'Name'};
+ $onemergemodulehash{'mergefilesequence'} = \%mergefilesequence;
+ $onemergemodulehash{'mergeassemblies'} = \%mergeassemblies;
+ $onemergemodulehash{'mergedirectories'} = \%mergedirectories;
+ $onemergemodulehash{'hasmsiassemblies'} = $hasmsiassemblies;
+ $onemergemodulehash{'removefiletable'} = $removefiletable;
+ $onemergemodulehash{'fileidtcontent'} = \@file_idt_content;
+
+ $installer::globals::mergemodules{$mergegid} = \%onemergemodulehash;
+
+ # Collecting all cab files, to copy them into installation set
+ $installer::globals::copy_msm_files{$cabfilename} = $onemergemodulehash{'cabinetfile'};
+
+ chdir($from);
+ }
+
+ $infoline = "All Merge Modules successfully analyzed\n";
+ push( @installer::globals::logfileinfo, $infoline);
+
+ $installer::globals::mergemodules_analyzed = 1;
+ installer::logger::include_timestamp_into_logfile("\nPerformance Info: Analyzing MergeModules, stop");
+
+ $infoline = "\n";
+ push( @installer::globals::logfileinfo, $infoline);
+ }
+
+ # 2. Change msi database (has to be done for every msi database -> for every language)
+ # a. Merge msm file into msi database: msidb.exe -d <msifile> -m <mergefile>
+ # b. Extracting tables from msi database: msidb.exe -d <msifile> -f <directory> -e File Media, ...
+ # c. Changing content of msi database in tables: File, Media, Directory, FeatureComponent
+ # d. Including tables into msi database: msidb.exe -d <msifile> -f <directory> -i File Media, ...
+ # e. Copying cabinet file into installation set (later)
+
+ my $counter = 0;
+ my $mergemodulegid;
+ foreach $mergemodulegid (keys %installer::globals::mergemodules)
+ {
+ my $mergemodulehash = $installer::globals::mergemodules{$mergemodulegid};
+ $counter++;
+
+ installer::logger::include_header_into_logfile("Merging Module: $mergemodulehash->{'name'}");
+ installer::logger::print_message( "\t... $mergemodulehash->{'name'} ... \n" );
+
+ $msifilename = installer::converter::make_path_conform($msifilename);
+ my $workdir = $msifilename;
+ installer::pathanalyzer::get_path_from_fullqualifiedname(\$workdir);
+
+ # changing directory
+ my $from = cwd();
+ my $to = $workdir;
+ chdir($to);
+
+ # Saving original msi database
+ installer::systemactions::copy_one_file($msifilename, "$msifilename\.$counter");
+
+ # Merging msm file, this is the "real" merge command
+
+ installer::logger::include_timestamp_into_logfile("\nPerformance Info: Before merging database");
+
+ if ( $^O =~ /cygwin/i ) {
+ # msidb.exe really wants backslashes. (And double escaping because system() expands the string.)
+ my $localmergemodulepath = $mergemodulehash->{'mergefilepath'};
+ my $localmsifilename = $msifilename;
+ $localmergemodulepath =~ s/\//\\\\/g;
+ $localmsifilename =~ s/\//\\\\/g;
+ $systemcall = $msidb . " -d " . $localmsifilename . " -m " . $localmergemodulepath;
+ }
+ else
+ {
+ $systemcall = $msidb . " -d " . $msifilename . " -m " . $mergemodulehash->{'mergefilepath'};
+ }
+ $returnvalue = system($systemcall);
+
+ $infoline = "Systemcall: $systemcall\n";
+ push( @installer::globals::logfileinfo, $infoline);
+
+ if ($returnvalue)
+ {
+ $infoline = "ERROR: Could not execute $systemcall . Returnvalue: $returnvalue!\n";
+ push( @installer::globals::logfileinfo, $infoline);
+ installer::exiter::exit_program("ERROR: Could not merge msm file into database: $mergemodulehash->{'mergefilepath'} !", "merge_mergemodules_into_msi_database");
+ }
+ else
+ {
+ $infoline = "Success: Executed $systemcall successfully!\n";
+ push( @installer::globals::logfileinfo, $infoline);
+ }
+
+ installer::logger::include_timestamp_into_logfile("\nPerformance Info: After merging database");
+
+ # Saving original idt files
+ if ( -f "File.idt" ) { installer::systemactions::rename_one_file("File.idt", "File.idt.$counter"); }
+ if ( -f "Media.idt" ) { installer::systemactions::rename_one_file("Media.idt", "Media.idt.$counter"); }
+ if ( -f "Directory.idt" ) { installer::systemactions::rename_one_file("Directory.idt", "Directory.idt.$counter"); }
+ if ( -f "Director.idt" ) { installer::systemactions::rename_one_file("Director.idt", "Director.idt.$counter"); }
+ if ( -f "FeatureComponents.idt" ) { installer::systemactions::rename_one_file("FeatureComponents.idt", "FeatureComponents.idt.$counter"); }
+ if ( -f "FeatureC.idt" ) { installer::systemactions::rename_one_file("FeatureC.idt", "FeatureC.idt.$counter"); }
+ if ( -f "MsiAssembly.idt" ) { installer::systemactions::rename_one_file("MsiAssembly.idt", "MsiAssembly.idt.$counter"); }
+ if ( -f "MsiAssem.idt" ) { installer::systemactions::rename_one_file("MsiAssem.idt", "MsiAssem.idt.$counter"); }
+
+ # Extracting tables
+
+ installer::logger::include_timestamp_into_logfile("\nPerformance Info: Before extracting tables");
+
+ my $workingtables = "File Media Directory FeatureComponents"; # required tables
+ # Optional tables can be added now
+ if ( $mergemodulehash->{'hasmsiassemblies'} ) { $workingtables = $workingtables . " MsiAssembly"; }
+
+ # Table "Feature" has to be exported, but it is not necessary to import it.
+ if ( $^O =~ /cygwin/i ) {
+ # msidb.exe really wants backslashes. (And double escaping because system() expands the string.)
+ my $localmsifilename = $msifilename;
+ my $localworkdir = $workdir;
+ $localmsifilename =~ s/\//\\\\/g;
+ $localworkdir =~ s/\//\\\\/g;
+ $systemcall = $msidb . " -d " . $localmsifilename . " -f " . $localworkdir . " -e " . "Feature " . $workingtables;
+ }
+ else
+ {
+ $systemcall = $msidb . " -d " . $msifilename . " -f " . $workdir . " -e " . "Feature " . $workingtables;
+ }
+ $returnvalue = system($systemcall);
+
+ $infoline = "Systemcall: $systemcall\n";
+ push( @installer::globals::logfileinfo, $infoline);
+
+ if ($returnvalue)
+ {
+ $infoline = "ERROR: Could not execute $systemcall !\n";
+ push( @installer::globals::logfileinfo, $infoline);
+ installer::exiter::exit_program("ERROR: Could not exclude tables from msi database: $msifilename !", "merge_mergemodules_into_msi_database");
+ }
+ else
+ {
+ $infoline = "Success: Executed $systemcall successfully!\n";
+ push( @installer::globals::logfileinfo, $infoline);
+ }
+
+ installer::logger::include_timestamp_into_logfile("\nPerformance Info: After extracting tables");
+
+ # Using 8+3 table names, that are used, when tables are integrated into database. The export of tables
+ # creates idt-files, that have long names.
+
+ if ( -f "Directory.idt" ) { installer::systemactions::rename_one_file("Directory.idt", "Director.idt"); }
+ if ( -f "FeatureComponents.idt" ) { installer::systemactions::rename_one_file("FeatureComponents.idt", "FeatureC.idt"); }
+ if ( -f "MsiAssembly.idt" ) { installer::systemactions::rename_one_file("MsiAssembly.idt", "MsiAssem.idt"); }
+
+ # Changing content of tables: File, Media, Directory, FeatureComponent, MsiAssembly
+ installer::logger::include_timestamp_into_logfile("\nPerformance Info: Changing Media table");
+ change_media_table($mergemodulehash, $workdir, $mergemodulegid, $allupdatelastsequences, $allupdatediskids);
+ installer::logger::include_timestamp_into_logfile("\nPerformance Info: Changing File table");
+ $filesref = change_file_table($mergemodulehash, $workdir, $allupdatesequences, $includepatharrayref, $filesref, $mergemodulegid);
+ installer::logger::include_timestamp_into_logfile("\nPerformance Info: Changing FeatureComponent table");
+ change_featurecomponent_table($mergemodulehash, $workdir);
+ installer::logger::include_timestamp_into_logfile("\nPerformance Info: Changing Directory table");
+ change_directory_table($mergemodulehash, $workdir);
+ if ( $mergemodulehash->{'hasmsiassemblies'} )
+ {
+ installer::logger::include_timestamp_into_logfile("\nPerformance Info: Changing MsiAssembly table");
+ change_msiassembly_table($mergemodulehash, $workdir);
+ }
+
+ # msidb.exe does not merge InstallExecuteSequence, AdminExecuteSequence and AdvtExecuteSequence. Instead it creates
+ # new tables ModuleInstallExecuteSequence, ModuleAdminExecuteSequence and ModuleAdvtExecuteSequence that need to be
+ # merged into the three ExecuteSequences with the following process (also into InstallUISequence.idt).
+
+ # Saving original idt files
+ if ( -f "InstallE.idt" ) { installer::systemactions::rename_one_file("InstallE.idt", "InstallE.idt.$counter"); }
+ if ( -f "InstallU.idt" ) { installer::systemactions::rename_one_file("InstallU.idt", "InstallU.idt.$counter"); }
+ if ( -f "AdminExe.idt" ) { installer::systemactions::rename_one_file("AdminExe.idt", "AdminExe.idt.$counter"); }
+ if ( -f "AdvtExec.idt" ) { installer::systemactions::rename_one_file("AdvtExec.idt", "AdvtExec.idt.$counter"); }
+ if ( -f "ModuleInstallExecuteSequence.idt" ) { installer::systemactions::rename_one_file("ModuleInstallExecuteSequence.idt", "ModuleInstallExecuteSequence.idt.$counter"); }
+ if ( -f "ModuleAdminExecuteSequence.idt" ) { installer::systemactions::rename_one_file("ModuleAdminExecuteSequence.idt", "ModuleAdminExecuteSequence.idt.$counter"); }
+ if ( -f "ModuleAdvtExecuteSequence.idt" ) { installer::systemactions::rename_one_file("ModuleAdvtExecuteSequence.idt", "ModuleAdvtExecuteSequence.idt.$counter"); }
+
+ # Extracting tables
+ my $moduleexecutetables = "ModuleInstallExecuteSequence ModuleAdminExecuteSequence ModuleAdvtExecuteSequence"; # new tables
+ my $executetables = "InstallExecuteSequence InstallUISequence AdminExecuteSequence AdvtExecuteSequence"; # tables to be merged
+
+
+ if ( $^O =~ /cygwin/i ) {
+ # msidb.exe really wants backslashes. (And double escaping because system() expands the string.)
+ my $localmsifilename = $msifilename;
+ my $localworkdir = $workdir;
+ $localmsifilename =~ s/\//\\\\/g;
+ $localworkdir =~ s/\//\\\\/g;
+ $systemcall = $msidb . " -d " . $localmsifilename . " -f " . $localworkdir . " -e " . "Feature " . $moduleexecutetables;
+ }
+ else
+ {
+ $systemcall = $msidb . " -d " . $msifilename . " -f " . $workdir . " -e " . "Feature " . $moduleexecutetables;
+ }
+ $returnvalue = system($systemcall);
+
+ if ( $^O =~ /cygwin/i ) {
+ # msidb.exe really wants backslashes. (And double escaping because system() expands the string.)
+ my $localmsifilename = $msifilename;
+ my $localworkdir = $workdir;
+ $localmsifilename =~ s/\//\\\\/g;
+ $localworkdir =~ s/\//\\\\/g;
+ $systemcall = $msidb . " -d " . $localmsifilename . " -f " . $localworkdir . " -e " . "Feature " . $executetables;
+ }
+ else
+ {
+ $systemcall = $msidb . " -d " . $msifilename . " -f " . $workdir . " -e " . "Feature " . $executetables;
+ }
+ $returnvalue = system($systemcall);
+
+ # Using 8+3 table names, that are used, when tables are integrated into database. The export of tables
+ # creates idt-files, that have long names.
+
+ if ( -f "InstallExecuteSequence.idt" ) { installer::systemactions::rename_one_file("InstallExecuteSequence.idt", "InstallE.idt"); }
+ if ( -f "InstallUISequence.idt" ) { installer::systemactions::rename_one_file("InstallUISequence.idt", "InstallU.idt"); }
+ if ( -f "AdminExecuteSequence.idt" ) { installer::systemactions::rename_one_file("AdminExecuteSequence.idt", "AdminExe.idt"); }
+ if ( -f "AdvtExecuteSequence.idt" ) { installer::systemactions::rename_one_file("AdvtExecuteSequence.idt", "AdvtExec.idt"); }
+
+ # Merging content of tables ModuleInstallExecuteSequence, ModuleAdminExecuteSequence and ModuleAdvtExecuteSequence
+ # into tables InstallExecuteSequence, AdminExecuteSequence and AdvtExecuteSequence
+ if ( -f "ModuleInstallExecuteSequence.idt" )
+ {
+ installer::logger::include_timestamp_into_logfile("\nPerformance Info: Changing InstallExecuteSequence table");
+ change_executesequence_table($mergemodulehash, $workdir, "InstallE.idt", "ModuleInstallExecuteSequence.idt");
+ installer::logger::include_timestamp_into_logfile("\nPerformance Info: Changing InstallUISequence table");
+ change_executesequence_table($mergemodulehash, $workdir, "InstallU.idt", "ModuleInstallExecuteSequence.idt");
+ }
+
+ if ( -f "ModuleAdminExecuteSequence.idt" )
+ {
+ installer::logger::include_timestamp_into_logfile("\nPerformance Info: Changing AdminExecuteSequence table");
+ change_executesequence_table($mergemodulehash, $workdir, "AdminExe.idt", "ModuleAdminExecuteSequence.idt");
+ }
+
+ if ( -f "ModuleAdvtExecuteSequence.idt" )
+ {
+ installer::logger::include_timestamp_into_logfile("\nPerformance Info: Changing AdvtExecuteSequence table");
+ change_executesequence_table($mergemodulehash, $workdir, "AdvtExec.idt", "ModuleAdvtExecuteSequence.idt");
+ }
+
+ installer::logger::include_timestamp_into_logfile("\nPerformance Info: All tables edited");
+
+ # Including tables into msi database
+
+ installer::logger::include_timestamp_into_logfile("\nPerformance Info: Before including tables");
+
+ if ( $^O =~ /cygwin/i ) {
+ # msidb.exe really wants backslashes. (And double escaping because system() expands the string.)
+ my $localmsifilename = $msifilename;
+ my $localworkdir = $workdir;
+ $localmsifilename =~ s/\//\\\\/g;
+ $localworkdir =~ s/\//\\\\/g;
+ $systemcall = $msidb . " -d " . $localmsifilename . " -f " . $localworkdir . " -i " . $workingtables. " " . $executetables;
+ }
+ else
+ {
+ $systemcall = $msidb . " -d " . $msifilename . " -f " . $workdir . " -i " . $workingtables. " " . $executetables;
+ }
+ $returnvalue = system($systemcall);
+
+ $infoline = "Systemcall: $systemcall\n";
+ push( @installer::globals::logfileinfo, $infoline);
+
+ if ($returnvalue)
+ {
+ $infoline = "ERROR: Could not execute $systemcall !\n";
+ push( @installer::globals::logfileinfo, $infoline);
+ installer::exiter::exit_program("ERROR: Could not include tables into msi database: $msifilename !", "merge_mergemodules_into_msi_database");
+ }
+ else
+ {
+ $infoline = "Success: Executed $systemcall successfully!\n";
+ push( @installer::globals::logfileinfo, $infoline);
+ }
+
+ installer::logger::include_timestamp_into_logfile("\nPerformance Info: After including tables");
+
+ chdir($from);
+ }
+
+ if ( ! $installer::globals::mergefiles_added_into_collector ) { $installer::globals::mergefiles_added_into_collector = 1; } # Now all mergemodules are merged for one language.
+
+ installer::logger::include_timestamp_into_logfile("\nPerformance Info: MergeModule into msi database, stop");
+ }
+
+ return $filesref;
+}
+
+#########################################################################
+# Analyzing the content of the media table.
+#########################################################################
+
+sub analyze_media_file
+{
+ my ($filecontent, $workdir) = @_;
+
+ my %filehash = ();
+ my $linecount = 0;
+ my $counter = 0;
+ my $filename = "Media.idt";
+
+ for ( my $i = 0; $i <= $#{$filecontent}; $i++ )
+ {
+ if ( $i <= 2 ) { next; } # ignoring first three lines
+ if ( ${$filecontent}[$i] =~ /^\s*$/ ) { next; } # ignoring empty lines
+ if ( ${$filecontent}[$i] =~ /^\s*(.+?)\t(.+?)\t(.+?)\t(.+?)\t(.+?)\t(.*?)\s*$/ )
+ {
+ my %line = ();
+ # Format: DiskId LastSequence DiskPrompt Cabinet VolumeLabel Source
+ $line{'DiskId'} = $1;
+ $line{'LastSequence'} = $2;
+ $line{'DiskPrompt'} = $3;
+ $line{'Cabinet'} = $4;
+ $line{'VolumeLabel'} = $5;
+ $line{'Source'} = $6;
+
+ $counter++;
+ $filehash{$counter} = \%line;
+ }
+ else
+ {
+ $linecount = $i + 1;
+ installer::exiter::exit_program("ERROR: Unknown line format in table \"$filename\" in \"$workdir\" (line $linecount) !", "analyze_media_file");
+ }
+ }
+
+ return \%filehash;
+}
+
+#########################################################################
+# Setting the DiskID for the new cabinet file
+#########################################################################
+
+sub get_diskid
+{
+ my ($mediafile, $allupdatediskids, $cabfilename) = @_;
+
+ my $diskid = 0;
+ my $line;
+
+ if (( $installer::globals::updatedatabase ) && ( exists($allupdatediskids->{$cabfilename}) ))
+ {
+ $diskid = $allupdatediskids->{$cabfilename};
+ }
+ else
+ {
+ foreach $line ( keys %{$mediafile} )
+ {
+ if ( $mediafile->{$line}->{'DiskId'} > $diskid ) { $diskid = $mediafile->{$line}->{'DiskId'}; }
+ }
+
+ $diskid++;
+ }
+
+ return $diskid;
+}
+
+#########################################################################
+# Setting the global LastSequence variable
+#########################################################################
+
+sub set_current_last_sequence
+{
+ my ($mediafile) = @_;
+
+ my $lastsequence = 0;
+ my $line;
+ foreach $line ( keys %{$mediafile} )
+ {
+ if ( $mediafile->{$line}->{'LastSequence'} > $lastsequence ) { $lastsequence = $mediafile->{$line}->{'LastSequence'}; }
+ }
+
+ $installer::globals::lastsequence_before_merge = $lastsequence;
+}
+
+#########################################################################
+# Setting the LastSequence for the new cabinet file
+#########################################################################
+
+sub get_lastsequence
+{
+ my ($mergemodulehash, $allupdatelastsequences) = @_;
+
+ my $lastsequence = 0;
+
+ if (( $installer::globals::updatedatabase ) && ( exists($allupdatelastsequences->{$mergemodulehash->{'cabfilename'}}) ))
+ {
+ $lastsequence = $allupdatelastsequences->{$mergemodulehash->{'cabfilename'}};
+ }
+ else
+ {
+ $lastsequence = $installer::globals::lastsequence_before_merge + $mergemodulehash->{'filenumber'};
+ }
+
+ return $lastsequence;
+}
+
+#########################################################################
+# Setting the DiskPrompt for the new cabinet file
+#########################################################################
+
+sub get_diskprompt
+{
+ my ($mediafile) = @_;
+
+ my $diskprompt = "";
+ my $line;
+ foreach $line ( keys %{$mediafile} )
+ {
+ if ( exists($mediafile->{$line}->{'DiskPrompt'}) )
+ {
+ $diskprompt = $mediafile->{$line}->{'DiskPrompt'};
+ last;
+ }
+ }
+
+ return $diskprompt;
+}
+
+#########################################################################
+# Setting the VolumeLabel for the new cabinet file
+#########################################################################
+
+sub get_volumelabel
+{
+ my ($mediafile) = @_;
+
+ my $volumelabel = "";
+ my $line;
+ foreach $line ( keys %{$mediafile} )
+ {
+ if ( exists($mediafile->{$line}->{'VolumeLabel'}) )
+ {
+ $volumelabel = $mediafile->{$line}->{'VolumeLabel'};
+ last;
+ }
+ }
+
+ return $volumelabel;
+}
+
+#########################################################################
+# Setting the Source for the new cabinet file
+#########################################################################
+
+sub get_source
+{
+ my ($mediafile) = @_;
+
+ my $source = "";
+ my $line;
+ foreach $line ( keys %{$mediafile} )
+ {
+ if ( exists($mediafile->{$line}->{'Source'}) )
+ {
+ $diskprompt = $mediafile->{$line}->{'Source'};
+ last;
+ }
+ }
+
+ return $source;
+}
+
+#########################################################################
+# For each Merge Module one new line has to be included into the
+# media table.
+#########################################################################
+
+sub create_new_media_line
+{
+ my ($mergemodulehash, $mediafile, $allupdatelastsequences, $allupdatediskids) = @_;
+
+ my $diskid = get_diskid($mediafile, $allupdatediskids, $mergemodulehash->{'cabfilename'});
+ my $lastsequence = get_lastsequence($mergemodulehash, $allupdatelastsequences);
+ my $diskprompt = get_diskprompt($mediafile);
+ my $cabinet = $mergemodulehash->{'cabfilename'};
+ my $volumelabel = get_volumelabel($mediafile);
+ my $source = get_source($mediafile);
+
+ if ( $installer::globals::include_cab_in_msi ) { $cabinet = "\#" . $cabinet; }
+
+ my $newline = "$diskid\t$lastsequence\t$diskprompt\t$cabinet\t$volumelabel\t$source\n";
+
+ return $newline;
+}
+
+#########################################################################
+# Setting the last diskid in media table.
+#########################################################################
+
+sub get_last_diskid
+{
+ my ($mediafile) = @_;
+
+ my $lastdiskid = 0;
+ my $line;
+ foreach $line ( keys %{$mediafile} )
+ {
+ if ( $mediafile->{$line}->{'DiskId'} > $lastdiskid ) { $lastdiskid = $mediafile->{$line}->{'DiskId'}; }
+ }
+
+ return $lastdiskid;
+}
+
+#########################################################################
+# Setting global variable for last cab file name.
+#########################################################################
+
+sub set_last_cabfile_name
+{
+ my ($mediafile, $lastdiskid) = @_;
+
+ my $line;
+ foreach $line ( keys %{$mediafile} )
+ {
+ if ( $mediafile->{$line}->{'DiskId'} == $lastdiskid ) { $installer::globals::lastcabfilename = $mediafile->{$line}->{'Cabinet'}; }
+ }
+ my $infoline = "Setting last cabinet file: $installer::globals::lastcabfilename\n";
+ push( @installer::globals::logfileinfo, $infoline);
+}
+
+#########################################################################
+# In the media table the new cabinet file has to be added or the
+# number of the last cabinet file has to be increased.
+#########################################################################
+
+sub change_media_table
+{
+ my ( $mergemodulehash, $workdir, $mergemodulegid, $allupdatelastsequences, $allupdatediskids ) = @_;
+
+ my $infoline = "Changing content of table \"Media\"\n";
+ push( @installer::globals::logfileinfo, $infoline);
+
+ my $filename = "Media.idt";
+ if ( ! -f $filename ) { installer::exiter::exit_program("ERROR: Could not find file \"$filename\" in \"$workdir\" !", "change_media_table"); }
+
+ my $filecontent = installer::files::read_file($filename);
+ my $mediafile = analyze_media_file($filecontent, $workdir);
+ set_current_last_sequence($mediafile);
+
+ if ( $installer::globals::fix_number_of_cab_files )
+ {
+ # Determining the line with the highest sequencenumber. That file needs to be updated.
+ my $lastdiskid = get_last_diskid($mediafile);
+ if ( $installer::globals::lastcabfilename eq "" ) { set_last_cabfile_name($mediafile, $lastdiskid); }
+ my $newmaxsequencenumber = $installer::globals::lastsequence_before_merge + $mergemodulehash->{'filenumber'};
+
+ for ( my $i = 0; $i <= $#{$filecontent}; $i++ )
+ {
+ if ( $i <= 2 ) { next; } # ignoring first three lines
+ if ( ${$filecontent}[$i] =~ /^\s*$/ ) { next; } # ignoring empty lines
+ if ( ${$filecontent}[$i] =~ /^\s*(\Q$lastdiskid\E\t)\Q$installer::globals::lastsequence_before_merge\E(\t.*)$/ )
+ {
+ my $start = $1;
+ my $final = $2;
+ $infoline = "Merge: Old line in media table: ${$filecontent}[$i]\n";
+ push( @installer::globals::logfileinfo, $infoline);
+ my $newline = $start . $newmaxsequencenumber . $final . "\n";
+ ${$filecontent}[$i] = $newline;
+ $infoline = "Merge: Changed line in media table: ${$filecontent}[$i]\n";
+ push( @installer::globals::logfileinfo, $infoline);
+ }
+ }
+ }
+ else
+ {
+ # the new line is identical for all localized databases, but has to be created for each MergeModule ($mergemodulegid)
+ if ( ! exists($installer::globals::merge_media_line{$mergemodulegid}) )
+ {
+ $installer::globals::merge_media_line{$mergemodulegid} = create_new_media_line($mergemodulehash, $mediafile, $allupdatelastsequences, $allupdatediskids);
+ }
+
+ $infoline = "Adding line: $installer::globals::merge_media_line{$mergemodulegid}\n";
+ push( @installer::globals::logfileinfo, $infoline);
+
+ # adding new line
+ push(@{$filecontent}, $installer::globals::merge_media_line{$mergemodulegid});
+ }
+
+ # saving file
+ installer::files::save_file($filename, $filecontent);
+}
+
+#########################################################################
+# Putting the directory table content into a hash.
+#########################################################################
+
+sub analyze_directorytable_file
+{
+ my ($filecontent, $idtfilename) = @_;
+
+ my %dirhash = ();
+ # Iterating over the file content
+ for ( my $i = 0; $i <= $#{$filecontent}; $i++ )
+ {
+ if ( $i <= 2 ) { next; } # ignoring first three lines
+ if ( ${$filecontent}[$i] =~ /^\s*$/ ) { next; } # ignoring empty lines
+ if ( ${$filecontent}[$i] =~ /^\s*(.+?)\t(.*?)\t(.*?)\s*$/ )
+ {
+ my %line = ();
+ # Format: Directory Directory_Parent DefaultDir
+ $line{'Directory'} = $1;
+ $line{'Directory_Parent'} = $2;
+ $line{'DefaultDir'} = $3;
+ $line{'linenumber'} = $i; # saving also the line number for direct access
+
+ my $uniquekey = $line{'Directory'};
+ $dirhash{$uniquekey} = \%line;
+ }
+ else
+ {
+ my $linecount = $i + 1;
+ installer::exiter::exit_program("ERROR: Unknown line format in table \"$idtfilename\" (line $linecount) !", "analyze_directorytable_file");
+ }
+ }
+
+ return \%dirhash;
+}
+
+#########################################################################
+# Putting the msi assembly table content into a hash.
+#########################################################################
+
+sub analyze_msiassemblytable_file
+{
+ my ($filecontent, $idtfilename) = @_;
+
+ my %assemblyhash = ();
+ # Iterating over the file content
+ for ( my $i = 0; $i <= $#{$filecontent}; $i++ )
+ {
+ if ( $i <= 2 ) { next; } # ignoring first three lines
+ if ( ${$filecontent}[$i] =~ /^\s*$/ ) { next; } # ignoring empty lines
+ if ( ${$filecontent}[$i] =~ /^\s*(.+?)\t(.+?)\t(.+?)\t(.*?)\t(.*?)\s*$/ )
+ {
+ my %line = ();
+ # Format: Component_ Feature_ File_Manifest File_Application Attributes
+ $line{'Component'} = $1;
+ $line{'Feature'} = $2;
+ $line{'File_Manifest'} = $3;
+ $line{'File_Application'} = $4;
+ $line{'Attributes'} = $5;
+ $line{'linenumber'} = $i; # saving also the line number for direct access
+
+ my $uniquekey = $line{'Component'};
+ $assemblyhash{$uniquekey} = \%line;
+ }
+ else
+ {
+ my $linecount = $i + 1;
+ installer::exiter::exit_program("ERROR: Unknown line format in table \"$idtfilename\" (line $linecount) !", "analyze_msiassemblytable_file");
+ }
+ }
+
+ return \%assemblyhash;
+}
+
+#########################################################################
+# Putting the file table content into a hash.
+#########################################################################
+
+sub analyze_filetable_file
+{
+ my ( $filecontent, $idtfilename ) = @_;
+
+ my %filehash = ();
+ # Iterating over the file content
+ for ( my $i = 0; $i <= $#{$filecontent}; $i++ )
+ {
+ if ( $i <= 2 ) { next; } # ignoring first three lines
+ if ( ${$filecontent}[$i] =~ /^\s*$/ ) { next; } # ignoring empty lines
+ if ( ${$filecontent}[$i] =~ /^\s*(.+?)\t(.+?)\t(.+?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\t(.+?)\s*$/ )
+ {
+ my %line = ();
+ # Format: File Component_ FileName FileSize Version Language Attributes Sequence
+ $line{'File'} = $1;
+ $line{'Component'} = $2;
+ $line{'FileName'} = $3;
+ $line{'FileSize'} = $4;
+ $line{'Version'} = $5;
+ $line{'Language'} = $6;
+ $line{'Attributes'} = $7;
+ $line{'Sequence'} = $8;
+ $line{'linenumber'} = $i; # saving also the line number for direct access
+
+ my $uniquekey = $line{'File'};
+ $filehash{$uniquekey} = \%line;
+ }
+ else
+ {
+ my $linecount = $i + 1;
+ installer::exiter::exit_program("ERROR: Unknown line format in table \"$idtfilename\" (line $linecount) !", "analyze_filetable_file");
+ }
+ }
+
+ return \%filehash;
+}
+
+#########################################################################
+# Creating a new line for the directory table.
+#########################################################################
+
+sub get_new_line_for_directory_table
+{
+ my ($dir) = @_;
+
+ my $newline = "$dir->{'Directory'}\t$dir->{'Directory_Parent'}\t$dir->{'DefaultDir'}\n";
+
+ return $newline;
+}
+
+#########################################################################
+# Creating a new line for the file table.
+#########################################################################
+
+sub get_new_line_for_file_table
+{
+ my ($file) = @_;
+
+ my $newline = "$file->{'File'}\t$file->{'Component'}\t$file->{'FileName'}\t$file->{'FileSize'}\t$file->{'Version'}\t$file->{'Language'}\t$file->{'Attributes'}\t$file->{'Sequence'}\n";
+
+ return $newline;
+}
+
+#########################################################################
+# Creating a new line for the msiassembly table.
+#########################################################################
+
+sub get_new_line_for_msiassembly_table
+{
+ my ($assembly) = @_;
+
+ my $newline = "$assembly->{'Component'}\t$assembly->{'Feature'}\t$assembly->{'File_Manifest'}\t$assembly->{'File_Application'}\t$assembly->{'Attributes'}\n";
+
+ return $newline;
+}
+
+#########################################################################
+# Sorting the files collector, if there are files, following
+# the merge module files.
+#########################################################################
+
+sub sort_files_collector_for_sequence
+{
+ my ($filesref) = @_;
+
+ my @sortarray = ();
+ my %helphash = ();
+
+ for ( my $i = 0; $i <= $#{$filesref}; $i++ )
+ {
+ my $onefile = ${$filesref}[$i];
+ if ( ! exists($onefile->{'sequencenumber'}) ) { installer::exiter::exit_program("ERROR: Could not find sequencenumber for file: $onefile->{'uniquename'} !", "sort_files_collector_for_sequence"); }
+ my $sequence = $onefile->{'sequencenumber'};
+ $helphash{$sequence} = $onefile;
+ }
+
+ foreach my $seq ( sort { $a <=> $b } keys %helphash ) { push(@sortarray, $helphash{$seq}); }
+
+ return \@sortarray;
+}
+
+#########################################################################
+# In the file table "Sequence" and "Attributes" have to be changed.
+#########################################################################
+
+sub change_file_table
+{
+ my ($mergemodulehash, $workdir, $allupdatesequenceshashref, $includepatharrayref, $filesref, $mergemodulegid) = @_;
+
+ my $infoline = "Changing content of table \"File\"\n";
+ push( @installer::globals::logfileinfo, $infoline);
+
+ my $idtfilename = "File.idt";
+ if ( ! -f $idtfilename ) { installer::exiter::exit_program("ERROR: Could not find file \"$idtfilename\" in \"$workdir\" !", "change_file_table"); }
+
+ my $filecontent = installer::files::read_file($idtfilename);
+
+ # If File.idt needed to be removed before the msm database was merged into the msi database,
+ # now it is time to add the content into File.idt
+ if ( $mergemodulehash->{'removefiletable'} )
+ {
+ for ( my $i = 0; $i <= $#{$mergemodulehash->{'fileidtcontent'}}; $i++ )
+ {
+ push(@{$filecontent}, ${$mergemodulehash->{'fileidtcontent'}}[$i]);
+ }
+ }
+
+ # Unpacking the MergeModule.CABinet (only once)
+ # Unpacking into temp directory. Warning: expand.exe has problems with very long unpack directories.
+
+ my $unpackdir = installer::systemactions::create_directories("cab", "");
+ push(@installer::globals::removedirs, $unpackdir);
+ $unpackdir = $unpackdir . $installer::globals::separator . $mergemodulegid;
+
+ my %newfileshash = ();
+ if (( $installer::globals::fix_number_of_cab_files ) && ( ! $installer::globals::mergefiles_added_into_collector ))
+ {
+ if ( ! -d $unpackdir ) { installer::systemactions::create_directory($unpackdir); }
+
+ # Unpack the cab file, so that in can be included into the last office cabinet file. Attention: cararc.exe from cabsdk required.
+ # cabarc.exe -o X <fullcabfilepath>
+
+ # my $cabarcfilename = "cabarc.exe";
+ # my $cabarcfile = installer::scriptitems::get_sourcepath_from_filename_and_includepath(\$cabarcfilename, $includepatharrayref, 1);
+
+ # if ( ! -f $$cabarcfile )
+ # {
+ # $cabarcfilename = "CABARC.EXE";
+ # $cabarcfile = installer::scriptitems::get_sourcepath_from_filename_and_includepath(\$cabarcfilename, $includepatharrayref, 1);
+ # if ( ! -f $$cabarcfile )
+ # {
+ # installer::exiter::exit_program("ERROR: cabarc.exe not found !", "change_file_table");
+ # }
+ # }
+ # my $cabarc = $$cabarcfile;
+
+ # changing directory
+ my $from = cwd();
+ my $to = $mergemodulehash->{'workdir'};
+ if ( $^O =~ /cygwin/i ) {
+ $to = qx(cygpath -u "$to");
+ chomp $to;
+ }
+
+ chdir($to) || die "Could not chdir to \"$to\"\n";
+
+ # Unpack the cab file, so that in can be included into the last office cabinet file.
+ # Not using cabarc.exe from cabsdk for unpacking cabinet files, but "expand.exe" that
+ # should be available on every Windows system.
+
+ $infoline = "Unpacking cabinet file: $mergemodulehash->{'cabinetfile'}\n";
+ push( @installer::globals::logfileinfo, $infoline);
+
+ # Avoid the Cygwin expand command
+ my $expandfile = "expand.exe"; # Has to be in the path
+ if ( $^O =~ /cygwin/i ) {
+ $expandfile = qx(cygpath -u "$ENV{WINDIR}"/System32/expand.exe);
+ chomp $expandfile;
+ }
+
+ my $cabfilename = "MergeModule.CABinet";
+
+ # exclude cabinet file
+ # my $systemcall = $cabarc . " -o X " . $mergemodulehash->{'cabinetfile'};
+
+ my $systemcall = "";
+ if ( $^O =~ /cygwin/i ) {
+ my $localunpackdir = qx(cygpath -m "$unpackdir");
+ chomp $localunpackdir;
+ $systemcall = $expandfile . " " . $cabfilename . " -F:\\\* " . $localunpackdir;
+ }
+ else
+ {
+ $systemcall = $expandfile . " " . $cabfilename . " -F:\* " . $unpackdir . " 2\>\&1";
+ }
+
+ my $returnvalue = system($systemcall);
+
+ $infoline = "Systemcall: $systemcall\n";
+ push( @installer::globals::logfileinfo, $infoline);
+
+ if ($returnvalue)
+ {
+ $infoline = "ERROR: Could not execute $systemcall !\n";
+ push( @installer::globals::logfileinfo, $infoline);
+ installer::exiter::exit_program("ERROR: Could not extract cabinet file: $mergemodulehash->{'cabinetfile'} !", "change_file_table");
+ }
+ else
+ {
+ $infoline = "Success: Executed $systemcall successfully!\n";
+ push( @installer::globals::logfileinfo, $infoline);
+ }
+
+ chdir($from);
+ }
+
+ # For performance reasons creating a hash with file names and rows
+ # The content of File.idt is changed after every merge -> content cannot be saved in global hash
+ $merge_filetablehashref = analyze_filetable_file($filecontent, $idtfilename);
+
+ my $attributes = "16384"; # Always
+
+ my $filename;
+ foreach $filename (keys %{$mergemodulehash->{'mergefilesequence'}} )
+ {
+ my $mergefilesequence = $mergemodulehash->{'mergefilesequence'}->{$filename};
+
+ if ( ! exists($merge_filetablehashref->{$filename}) ) { installer::exiter::exit_program("ERROR: Could not find file \"$filename\" in \"$idtfilename\" !", "change_file_table"); }
+ my $filehash = $merge_filetablehashref->{$filename};
+ my $linenumber = $filehash->{'linenumber'};
+
+ # <- this line has to be changed concerning "Sequence" and "Attributes"
+ $filehash->{'Attributes'} = $attributes;
+
+ # If this is an update process, the sequence numbers have to be reused.
+ if ( $installer::globals::updatedatabase )
+ {
+ if ( ! exists($allupdatesequenceshashref->{$filehash->{'File'}}) ) { installer::exiter::exit_program("ERROR: Sequence not defined for file \"$filehash->{'File'}\" !", "change_file_table"); }
+ $filehash->{'Sequence'} = $allupdatesequenceshashref->{$filehash->{'File'}};
+ # Saving all mergemodule sequence numbers. This is important for creating ddf files
+ $installer::globals::allmergemodulefilesequences{$filehash->{'Sequence'}} = 1;
+ }
+ else
+ {
+ # Important saved data: $installer::globals::lastsequence_before_merge.
+ # This mechanism keeps the correct order inside the new cabinet file.
+ $filehash->{'Sequence'} = $filehash->{'Sequence'} + $installer::globals::lastsequence_before_merge;
+ }
+
+ my $oldline = ${$filecontent}[$linenumber];
+ my $newline = get_new_line_for_file_table($filehash);
+ ${$filecontent}[$linenumber] = $newline;
+
+ $infoline = "Merge, replacing line:\n";
+ push( @installer::globals::logfileinfo, $infoline);
+ $infoline = "Old: $oldline\n";
+ push( @installer::globals::logfileinfo, $infoline);
+ $infoline = "New: $newline\n";
+ push( @installer::globals::logfileinfo, $infoline);
+
+ # Adding files to the files collector (but only once)
+ if (( $installer::globals::fix_number_of_cab_files ) && ( ! $installer::globals::mergefiles_added_into_collector ))
+ {
+ # If the number of cabinet files is kept constant,
+ # all files from the mergemodule cabinet files will
+ # be integrated into the last office cabinet file
+ # (installer::globals::lastcabfilename).
+ # Therefore the files must now be added to the filescollector,
+ # so that they will be integrated into the ddf files.
+
+ # Problem with very long filenames -> copying to shorter filenames
+ my $newfilename = "f" . $filehash->{'Sequence'};
+ my $completesource = $unpackdir . $installer::globals::separator . $filehash->{'File'};
+ my $completedest = $unpackdir . $installer::globals::separator . $newfilename;
+ installer::systemactions::copy_one_file($completesource, $completedest);
+
+ my $locallastcabfilename = $installer::globals::lastcabfilename;
+ if ( $locallastcabfilename =~ /^\s*\#/ ) { $locallastcabfilename =~ s/^\s*\#//; } # removing beginning hashes
+
+ # Create new file hash for file collector
+ my %newfile = ();
+ $newfile{'sequencenumber'} = $filehash->{'Sequence'};
+ $newfile{'assignedsequencenumber'} = $filehash->{'Sequence'};
+ $newfile{'cabinet'} = $locallastcabfilename;
+ $newfile{'sourcepath'} = $completedest;
+ $newfile{'componentname'} = $filehash->{'Component'};
+ $newfile{'uniquename'} = $filehash->{'File'};
+ $newfile{'Name'} = $filehash->{'File'};
+
+ # Saving in globals sequence hash
+ $installer::globals::uniquefilenamesequence{$filehash->{'File'}} = $filehash->{'Sequence'};
+
+ if ( ! -f $newfile{'sourcepath'} ) { installer::exiter::exit_program("ERROR: File \"$newfile{'sourcepath'}\" must exist!", "change_file_table"); }
+
+ # Collecting all new files. Attention: This files must be included into files collector in correct order!
+ $newfileshash{$filehash->{'Sequence'}} = \%newfile;
+ # push(@{$filesref}, \%newfile); -> this is not the correct order
+ }
+ }
+
+ # Now the files can be added to the files collector
+ # In the case of an update process, there can be new files, that have to be added after the merge module files.
+ # Warning: In multilingual installation sets, the files only have to be added once to the files collector!
+
+ if ( ! $installer::globals::mergefiles_added_into_collector )
+ {
+ foreach my $localsequence ( sort { $a <=> $b } keys %newfileshash ) { push(@{$filesref}, $newfileshash{$localsequence}); }
+ if ( $installer::globals::newfilesexist ) { $filesref = sort_files_collector_for_sequence($filesref); }
+ # $installer::globals::mergefiles_added_into_collector = 1; -> Not yet. Only if all mergemodules are merged for one language.
+ }
+
+ # Saving the idt file (for every language)
+ installer::files::save_file($idtfilename, $filecontent);
+
+ return $filesref;
+}
+
+#########################################################################
+# Reading the file "Director.idt". The Directory, that is defined in scp
+# has to be defined in this table.
+#########################################################################
+
+sub collect_directories
+{
+ my $idtfilename = "Director.idt";
+ my $filecontent = installer::files::read_file($idtfilename);
+
+ for ( my $i = 0; $i <= $#{$filecontent}; $i++ )
+ {
+ if ( $i <= 2 ) { next; } # ignoring first three lines
+ if ( ${$filecontent}[$i] =~ /^\s*$/ ) { next; } # ignoring empty lines
+ # Format: Directory Directory_Parent DefaultDir
+ if ( ${$filecontent}[$i] =~ /^\s*(.+?)\t(.*?)\t(.*?)\s*$/ )
+ {
+ $installer::globals::merge_alldirectory_hash{$1} = 1;
+ }
+ else
+ {
+ my $linecount = $i + 1;
+ installer::exiter::exit_program("ERROR: Unknown line format in table \"$idtfilename\" (line $linecount) !", "collect_directories");
+ }
+ }
+}
+
+#########################################################################
+# Reading the file "Feature.idt". The Feature, that is defined in scp
+# has to be defined in this table.
+#########################################################################
+
+sub collect_feature
+{
+ my $idtfilename = "Feature.idt";
+ if ( ! -f $idtfilename ) { installer::exiter::exit_program("ERROR: Could not find file \"$idtfilename\" in \"$workdir\" !", "collect_feature"); }
+ my $filecontent = installer::files::read_file($idtfilename);
+
+ for ( my $i = 0; $i <= $#{$filecontent}; $i++ )
+ {
+ if ( $i <= 2 ) { next; } # ignoring first three lines
+ if ( ${$filecontent}[$i] =~ /^\s*$/ ) { next; } # ignoring empty lines
+ # Format: Feature Feature_Parent Title Description Display Level Directory_ Attributes
+ if ( ${$filecontent}[$i] =~ /^\s*(.+?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\s*$/ )
+ {
+ $installer::globals::merge_allfeature_hash{$1} = 1;
+ }
+ else
+ {
+ my $linecount = $i + 1;
+ installer::exiter::exit_program("ERROR: Unknown line format in table \"$idtfilename\" (line $linecount) !", "collect_feature");
+ }
+ }
+}
+
+#########################################################################
+# In the featurecomponent table, the new connections have to be added.
+#########################################################################
+
+sub change_featurecomponent_table
+{
+ my ($mergemodulehash, $workdir) = @_;
+
+ my $infoline = "Changing content of table \"FeatureComponents\"\n";
+ push( @installer::globals::logfileinfo, $infoline);
+
+ my $idtfilename = "FeatureC.idt";
+ if ( ! -f $idtfilename ) { installer::exiter::exit_program("ERROR: Could not find file \"$idtfilename\" in \"$workdir\" !", "change_featurecomponent_table"); }
+
+ my $filecontent = installer::files::read_file($idtfilename);
+
+ # Simply adding for each new component one line. The Feature has to be defined in scp project.
+ my $feature = $mergemodulehash->{'feature'};
+
+ if ( ! $installer::globals::mergefeaturecollected )
+ {
+ collect_feature(); # putting content into hash %installer::globals::merge_allfeature_hash
+ $installer::globals::mergefeaturecollected = 1;
+ }
+
+ if ( ! exists($installer::globals::merge_allfeature_hash{$feature}) )
+ {
+ installer::exiter::exit_program("ERROR: Unknown feature defined in scp: \"$feature\" . Not defined in table \"Feature\" !", "change_featurecomponent_table");
+ }
+
+ my $component;
+ foreach $component ( keys %{$mergemodulehash->{'componentnames'}} )
+ {
+ my $line = "$feature\t$component\n";
+ push(@{$filecontent}, $line);
+ $infoline = "Adding line: $line\n";
+ push( @installer::globals::logfileinfo, $infoline);
+ }
+
+ # saving file
+ installer::files::save_file($idtfilename, $filecontent);
+}
+
+#########################################################################
+# In the directory table, the directory parent has to be changed,
+# if it is not TARGETDIR.
+#########################################################################
+
+sub change_directory_table
+{
+ my ($mergemodulehash, $workdir) = @_;
+
+ # directory for MergeModule has to be defined in scp project
+ my $scpdirectory = $mergemodulehash->{'rootdir'};
+
+ if ( $scpdirectory ne "TARGETDIR" ) # TARGETDIR works fine, when using msidb.exe
+ {
+ my $infoline = "Changing content of table \"Directory\"\n";
+ push( @installer::globals::logfileinfo, $infoline);
+
+ my $idtfilename = "Director.idt";
+ if ( ! -f $idtfilename ) { installer::exiter::exit_program("ERROR: Could not find file \"$idtfilename\" in \"$workdir\" !", "change_directory_table"); }
+
+ my $filecontent = installer::files::read_file($idtfilename);
+
+ if ( ! $installer::globals::mergedirectoriescollected )
+ {
+ collect_directories(); # putting content into %installer::globals::merge_alldirectory_hash, only first column!
+ $installer::globals::mergedirectoriescollected = 1;
+ }
+
+ if ( ! exists($installer::globals::merge_alldirectory_hash{$scpdirectory}) )
+ {
+ installer::exiter::exit_program("ERROR: Unknown directory defined in scp: \"$scpdirectory\" . Not defined in table \"Directory\" !", "change_directory_table");
+ }
+
+ # If the definition in scp is okay, now the complete content of "Director.idt" can be analyzed
+ my $merge_directorytablehashref = analyze_directorytable_file($filecontent, $idtfilename);
+
+ my $directory;
+ foreach $directory (keys %{$mergemodulehash->{'mergedirectories'}} )
+ {
+ if ( ! exists($merge_directorytablehashref->{$directory}) ) { installer::exiter::exit_program("ERROR: Could not find directory \"$directory\" in \"$idtfilename\" !", "change_directory_table"); }
+ my $dirhash = $merge_directorytablehashref->{$directory};
+ my $linenumber = $dirhash->{'linenumber'};
+
+ # <- this line has to be changed concerning "Directory_Parent",
+ # if the current value is "TARGETDIR", which is the default value from msidb.exe
+
+ if ( $dirhash->{'Directory_Parent'} eq "TARGETDIR" )
+ {
+ $dirhash->{'Directory_Parent'} = $scpdirectory;
+
+ my $oldline = ${$filecontent}[$linenumber];
+ my $newline = get_new_line_for_directory_table($dirhash);
+ ${$filecontent}[$linenumber] = $newline;
+
+ $infoline = "Merge, replacing line:\n";
+ push( @installer::globals::logfileinfo, $infoline);
+ $infoline = "Old: $oldline\n";
+ push( @installer::globals::logfileinfo, $infoline);
+ $infoline = "New: $newline\n";
+ push( @installer::globals::logfileinfo, $infoline);
+ }
+ }
+
+ # saving file
+ installer::files::save_file($idtfilename, $filecontent);
+ }
+}
+
+#########################################################################
+# In the msiassembly table, the feature has to be changed.
+#########################################################################
+
+sub change_msiassembly_table
+{
+ my ($mergemodulehash, $workdir) = @_;
+
+ my $infoline = "Changing content of table \"MsiAssembly\"\n";
+ push( @installer::globals::logfileinfo, $infoline);
+
+ my $idtfilename = "MsiAssem.idt";
+ if ( ! -f $idtfilename ) { installer::exiter::exit_program("ERROR: Could not find file \"$idtfilename\" in \"$workdir\" !", "change_msiassembly_table"); }
+
+ my $filecontent = installer::files::read_file($idtfilename);
+
+ # feature has to be defined in scp project
+ my $feature = $mergemodulehash->{'feature'};
+
+ if ( ! $installer::globals::mergefeaturecollected )
+ {
+ collect_feature(); # putting content into hash %installer::globals::merge_allfeature_hash
+ $installer::globals::mergefeaturecollected = 1;
+ }
+
+ if ( ! exists($installer::globals::merge_allfeature_hash{$feature}) )
+ {
+ installer::exiter::exit_program("ERROR: Unknown feature defined in scp: \"$feature\" . Not defined in table \"Feature\" !", "change_msiassembly_table");
+ }
+
+ my $merge_msiassemblytablehashref = analyze_msiassemblytable_file($filecontent, $idtfilename);
+
+ my $component;
+ foreach $component (keys %{$mergemodulehash->{'mergeassemblies'}} )
+ {
+ if ( ! exists($merge_msiassemblytablehashref->{$component}) ) { installer::exiter::exit_program("ERROR: Could not find component \"$component\" in \"$idtfilename\" !", "change_msiassembly_table"); }
+ my $assemblyhash = $merge_msiassemblytablehashref->{$component};
+ my $linenumber = $assemblyhash->{'linenumber'};
+
+ # <- this line has to be changed concerning "Feature"
+ $assemblyhash->{'Feature'} = $feature;
+
+ my $oldline = ${$filecontent}[$linenumber];
+ my $newline = get_new_line_for_msiassembly_table($assemblyhash);
+ ${$filecontent}[$linenumber] = $newline;
+
+ $infoline = "Merge, replacing line:\n";
+ push( @installer::globals::logfileinfo, $infoline);
+ $infoline = "Old: $oldline\n";
+ push( @installer::globals::logfileinfo, $infoline);
+ $infoline = "New: $newline\n";
+ push( @installer::globals::logfileinfo, $infoline);
+ }
+
+ # saving file
+ installer::files::save_file($idtfilename, $filecontent);
+}
+
+#########################################################################
+# Creating file content hash
+#########################################################################
+
+sub make_executeidtcontent_hash
+{
+ my ($filecontent, $idtfilename) = @_;
+
+ my %newhash = ();
+
+ for ( my $i = 0; $i <= $#{$filecontent}; $i++ )
+ {
+ if ( $i <= 2 ) { next; } # ignoring first three lines
+ if ( ${$filecontent}[$i] =~ /^\s*$/ ) { next; } # ignoring empty lines
+ # Format for all sequence tables: Action Condition Sequence
+ if ( ${$filecontent}[$i] =~ /^\s*(.+?)\t(.*?)\t(.*?)\s*$/ )
+ {
+ my %onehash = ();
+ $onehash{'Action'} = $1;
+ $onehash{'Condition'} = $2;
+ $onehash{'Sequence'} = $3;
+ $newhash{$onehash{'Action'}} = \%onehash;
+ }
+ else
+ {
+ my $linecount = $i + 1;
+ installer::exiter::exit_program("ERROR: Unknown line format in table \"$idtfilename\" (line $linecount) !", "make_executeidtcontent_hash");
+ }
+ }
+
+ return \%newhash;
+}
+
+#########################################################################
+# Creating file content hash
+#########################################################################
+
+sub make_moduleexecuteidtcontent_hash
+{
+ my ($filecontent, $idtfilename) = @_;
+
+ my %newhash = ();
+
+ for ( my $i = 0; $i <= $#{$filecontent}; $i++ )
+ {
+ if ( $i <= 2 ) { next; } # ignoring first three lines
+ if ( ${$filecontent}[$i] =~ /^\s*$/ ) { next; } # ignoring empty lines
+ # Format for all module sequence tables: Action Sequence BaseAction After Condition
+ if ( ${$filecontent}[$i] =~ /^\s*(.+?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\s*$/ )
+ {
+ my %onehash = ();
+ $onehash{'Action'} = $1;
+ $onehash{'Sequence'} = $2;
+ $onehash{'BaseAction'} = $3;
+ $onehash{'After'} = $4;
+ $onehash{'Condition'} = $5;
+ $newhash{$onehash{'Action'}} = \%onehash;
+ }
+ else
+ {
+ my $linecount = $i + 1;
+ installer::exiter::exit_program("ERROR: Unknown line format in table \"$idtfilename\" (line $linecount) !", "make_executeidtcontent_hash");
+ }
+ }
+
+ return \%newhash;
+}
+
+#########################################################################
+# ExecuteSequence tables need to be merged with
+# ModuleExecuteSequence tables created by msidb.exe.
+#########################################################################
+
+sub change_executesequence_table
+{
+ my ($mergemodulehash, $workdir, $idtfilename, $moduleidtfilename) = @_;
+
+ my $infoline = "Changing content of table \"$idtfilename\"\n";
+ push( @installer::globals::logfileinfo, $infoline);
+
+ if ( ! -f $idtfilename ) { installer::exiter::exit_program("ERROR: Could not find file \"$idtfilename\" in \"$workdir\" !", "change_executesequence_table"); }
+ if ( ! -f $moduleidtfilename ) { installer::exiter::exit_program("ERROR: Could not find file \"$moduleidtfilename\" in \"$workdir\" !", "change_executesequence_table"); }
+
+ # Reading file content
+ my $idtfilecontent = installer::files::read_file($idtfilename);
+ my $moduleidtfilecontent = installer::files::read_file($moduleidtfilename);
+
+ # Converting to hash
+ my $idtcontenthash = make_executeidtcontent_hash($idtfilecontent, $idtfilename);
+ my $moduleidtcontenthash = make_moduleexecuteidtcontent_hash($moduleidtfilecontent, $moduleidtfilename);
+
+ # Merging
+ foreach my $action ( keys %{$moduleidtcontenthash} )
+ {
+ if ( exists($idtcontenthash->{$action}) ) { next; } # Action already exists, can be ignored
+
+ if (( $idtfilename eq "InstallU.idt" ) && ( ! ( $action =~ /^\s*WindowsFolder\./ ))) { next; } # Only "WindowsFolder.*" CustomActions for UI Sequence table
+
+ my $actionhashref = $moduleidtcontenthash->{$action};
+ if ( $actionhashref->{'Sequence'} ne "" )
+ {
+ # Format for all sequence tables: Action Condition Sequence
+ my $newline = $actionhashref->{'Action'} . "\t" . $actionhashref->{'Condition'} . "\t" . $actionhashref->{'Sequence'} . "\n";
+ # Adding to table
+ push(@{$idtfilecontent}, $newline);
+ # Also adding to hash
+ my %idttablehash = ();
+ $idttablehash{'Action'} = $actionhashref->{'Action'};
+ $idttablehash{'Condition'} = $actionhashref->{'Condition'};
+ $idttablehash{'Sequence'} = $actionhashref->{'Sequence'};
+ $idtcontenthash->{$action} = \%idttablehash;
+
+ }
+ else # no sequence defined, using syntax "BaseAction" and "After"
+ {
+ my $baseactionname = $actionhashref->{'BaseAction'};
+ # If this baseactionname is not defined in execute idt file, it is not possible to merge
+ if ( ! exists($idtcontenthash->{$baseactionname}) ) { installer::exiter::exit_program("ERROR: Merge problem: Could not find action \"$baseactionname\" in file \"$idtfilename\" !", "change_executesequence_table"); }
+
+ my $baseaction = $idtcontenthash->{$baseactionname};
+ my $sequencenumber = $baseaction->{'Sequence'};
+ if ( $actionhashref->{'After'} == 1 ) { $sequencenumber = $sequencenumber + 1; }
+ else { $sequencenumber = $sequencenumber - 1; }
+
+ # Format for all sequence tables: Action Condition Sequence
+ my $newline = $actionhashref->{'Action'} . "\t" . $actionhashref->{'Condition'} . "\t" . $sequencenumber . "\n";
+ # Adding to table
+ push(@{$idtfilecontent}, $newline);
+ # Also adding to hash
+ my %idttablehash = ();
+ $idttablehash{'Action'} = $actionhashref->{'Action'};
+ $idttablehash{'Condition'} = $actionhashref->{'Condition'};
+ $idttablehash{'Sequence'} = $sequencenumber;
+ $idtcontenthash->{$action} = \%idttablehash;
+ }
+ }
+
+ # saving file
+ installer::files::save_file($idtfilename, $idtfilecontent);
+}
+
+
+1;
diff --git a/solenv/bin/modules/installer/windows/msiglobal.pm b/solenv/bin/modules/installer/windows/msiglobal.pm
new file mode 100644
index 000000000000..9f2d21f8b7f8
--- /dev/null
+++ b/solenv/bin/modules/installer/windows/msiglobal.pm
@@ -0,0 +1,2265 @@
+#*************************************************************************
+#
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+#
+# Copyright 2000, 2010 Oracle and/or its affiliates.
+#
+# OpenOffice.org - a multi-platform office productivity suite
+#
+# This file is part of OpenOffice.org.
+#
+# OpenOffice.org is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License version 3
+# only, as published by the Free Software Foundation.
+#
+# OpenOffice.org is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License version 3 for more details
+# (a copy is included in the LICENSE file that accompanied this code).
+#
+# You should have received a copy of the GNU Lesser General Public License
+# version 3 along with OpenOffice.org. If not, see
+# <http://www.openoffice.org/license.html>
+# for a copy of the LGPLv3 License.
+#
+#*************************************************************************
+
+package installer::windows::msiglobal;
+
+use Cwd;
+use Digest::MD5;
+use installer::converter;
+use installer::exiter;
+use installer::files;
+use installer::globals;
+use installer::logger;
+use installer::pathanalyzer;
+use installer::remover;
+use installer::scriptitems;
+use installer::systemactions;
+use installer::worker;
+use installer::windows::idtglobal;
+use installer::windows::language;
+
+###########################################################################
+# Generating the header of the ddf file.
+# The usage of ddf files is needed, because makecab.exe can only include
+# one sourcefile into a cab file
+###########################################################################
+
+sub write_ddf_file_header
+{
+ my ($ddffileref, $cabinetfile, $installdir) = @_;
+
+ my $oneline;
+
+ $oneline = ".Set CabinetName1=" . $cabinetfile . "\n";
+ push(@{$ddffileref} ,$oneline);
+ $oneline = ".Set ReservePerCabinetSize=128\n"; # This reserves space for a digital signature.
+ push(@{$ddffileref} ,$oneline);
+ $oneline = ".Set MaxDiskSize=2147483648\n"; # This allows the .cab file to get a size of 2 GB.
+ push(@{$ddffileref} ,$oneline);
+ $oneline = ".Set CompressionType=LZX\n";
+ push(@{$ddffileref} ,$oneline);
+ $oneline = ".Set Compress=ON\n";
+ push(@{$ddffileref} ,$oneline);
+ $oneline = ".Set CompressionLevel=$installer::globals::cabfilecompressionlevel\n";
+ push(@{$ddffileref} ,$oneline);
+ $oneline = ".Set Cabinet=ON\n";
+ push(@{$ddffileref} ,$oneline);
+ $oneline = ".Set DiskDirectoryTemplate=" . $installdir . "\n";
+ push(@{$ddffileref} ,$oneline);
+}
+
+##########################################################################
+# Lines in ddf files must not contain more than 256 characters
+##########################################################################
+
+sub check_ddf_file
+{
+ my ( $ddffile, $ddffilename ) = @_;
+
+ my $maxlength = 0;
+ my $maxline = 0;
+ my $linelength = 0;
+ my $linenumber = 0;
+
+ for ( my $i = 0; $i <= $#{$ddffile}; $i++ )
+ {
+ my $oneline = ${$ddffile}[$i];
+
+ $linelength = length($oneline);
+ $linenumber = $i + 1;
+
+ if ( $linelength > 256 )
+ {
+ installer::exiter::exit_program("ERROR \"$ddffilename\" line $linenumber: Lines in ddf files must not contain more than 256 characters!", "check_ddf_file");
+ }
+
+ if ( $linelength > $maxlength )
+ {
+ $maxlength = $linelength;
+ $maxline = $linenumber;
+ }
+ }
+
+ my $infoline = "Check of ddf file \"$ddffilename\": Maximum length \"$maxlength\" in line \"$maxline\" (allowed line length: 256 characters)\n";
+ push(@installer::globals::logfileinfo, $infoline);
+}
+
+##########################################################################
+# Lines in ddf files must not be longer than 256 characters.
+# Therefore it can be useful to use relative pathes. Then it is
+# necessary to change into temp directory before calling
+# makecab.exe.
+##########################################################################
+
+sub make_relative_ddf_path
+{
+ my ( $sourcepath ) = @_;
+
+ my $windowstemppath = $installer::globals::temppath;
+
+ if ( $^O =~ /cygwin/i )
+ {
+ $windowstemppath = $installer::globals::cyg_temppath;
+ }
+
+ $sourcepath =~ s/\Q$windowstemppath\E//;
+ $sourcepath =~ s/^\\//;
+
+ return $sourcepath;
+}
+
+##########################################################################
+# Returning the order of the sequences in the files array.
+##########################################################################
+
+sub get_sequenceorder
+{
+ my ($filesref) = @_;
+
+ my %order = ();
+
+ for ( my $i = 0; $i <= $#{$filesref}; $i++ )
+ {
+ my $onefile = ${$filesref}[$i];
+ if ( ! $onefile->{'assignedsequencenumber'} ) { installer::exiter::exit_program("ERROR: No sequence number assigned to $onefile->{'gid'} ($onefile->{'uniquename'})!", "get_sequenceorder"); }
+ $order{$onefile->{'assignedsequencenumber'}} = $i;
+ }
+
+ return \%order;
+}
+
+##########################################################################
+# Generation the list, in which the source of the files is connected
+# with the cabinet destination file. Because more than one file needs
+# to be included into a cab file, this has to be done via ddf files.
+##########################################################################
+
+sub generate_cab_file_list
+{
+ my ($filesref, $installdir, $ddfdir, $allvariables) = @_;
+
+ my @cabfilelist = ();
+
+ installer::logger::include_header_into_logfile("Generating ddf files");
+
+ installer::logger::include_timestamp_into_logfile("Performance Info: ddf file generation start");
+
+ if ( $^O =~ /cygwin/i ) { installer::worker::generate_cygwin_pathes($filesref); }
+
+ if ( $installer::globals::use_packages_for_cabs )
+ {
+ my $sequenceorder = get_sequenceorder($filesref);
+
+ my $counter = 1;
+ my $currentcabfile = "";
+
+ while ( ( exists($sequenceorder->{$counter}) ) || ( exists($installer::globals::allmergemodulefilesequences{$counter}) ) ) # Taking care of files from merge modules
+ {
+ if ( exists($installer::globals::allmergemodulefilesequences{$counter}) )
+ {
+ # Skipping this sequence, it is not included in $filesref, because it is assigned to a file from a merge module.\n";
+ $counter++;
+ next;
+ }
+
+ # Files with increasing sequencerorder are included in one cab file
+ my $onefile = ${$filesref}[$sequenceorder->{$counter}];
+ my $cabinetfile = $onefile->{'assignedcabinetfile'};
+ my $sourcepath = $onefile->{'sourcepath'};
+ if ( $^O =~ /cygwin/i ) { $sourcepath = $onefile->{'cyg_sourcepath'}; }
+ my $uniquename = $onefile->{'uniquename'};
+
+ my $styles = "";
+ my $doinclude = 1;
+ if ( $onefile->{'Styles'} ) { $styles = $onefile->{'Styles'}; };
+ if ( $styles =~ /\bDONT_PACK\b/ ) { $doinclude = 0; }
+
+ # to avoid lines with more than 256 characters, it can be useful to use relative pathes
+ if ( $allvariables->{'RELATIVE_PATHES_IN_DDF'} ) { $sourcepath = make_relative_ddf_path($sourcepath); }
+
+ # all files with the same cabinetfile have increasing sequencenumbers
+
+ my @ddffile = ();
+
+ write_ddf_file_header(\@ddffile, $cabinetfile, $installdir);
+
+ my $ddfline = "\"" . $sourcepath . "\"" . " " . $uniquename . "\n";
+ if ( $doinclude ) { push(@ddffile, $ddfline); }
+
+ $counter++; # increasing the counter
+ my $nextfile = "";
+ my $nextcabinetfile = "";
+ if ( exists($sequenceorder->{$counter}) ) { $nextfile = ${$filesref}[$sequenceorder->{$counter}]; }
+ if ( $nextfile->{'assignedcabinetfile'} ) { $nextcabinetfile = $nextfile->{'assignedcabinetfile'}; }
+
+ while ( $nextcabinetfile eq $cabinetfile )
+ {
+ $sourcepath = $nextfile->{'sourcepath'};
+ if ( $^O =~ /cygwin/i ) { $sourcepath = $nextfile->{'cyg_sourcepath'}; }
+ # to avoid lines with more than 256 characters, it can be useful to use relative pathes
+ if ( $allvariables->{'RELATIVE_PATHES_IN_DDF'} ) { $sourcepath = make_relative_ddf_path($sourcepath); }
+ $uniquename = $nextfile->{'uniquename'};
+ my $localdoinclude = 1;
+ my $nextfilestyles = "";
+ if ( $nextfile->{'Styles'} ) { $nextfilestyles = $nextfile->{'Styles'}; }
+ if ( $nextfilestyles =~ /\bDONT_PACK\b/ ) { $localdoinclude = 0; }
+ $ddfline = "\"" . $sourcepath . "\"" . " " . $uniquename . "\n";
+ if ( $localdoinclude ) { push(@ddffile, $ddfline); }
+
+ $counter++; # increasing the counter!
+ $nextcabinetfile = "_lastfile_";
+ if ( exists($sequenceorder->{$counter}) )
+ {
+ $nextfile = ${$filesref}[$sequenceorder->{$counter}];
+ $nextcabinetfile = $nextfile->{'assignedcabinetfile'};
+ }
+ }
+
+ # creating the DDF file
+
+ my $ddffilename = $cabinetfile;
+ $ddffilename =~ s/.cab/.ddf/;
+ $ddfdir =~ s/\Q$installer::globals::separator\E\s*$//;
+ $ddffilename = $ddfdir . $installer::globals::separator . $ddffilename;
+
+ installer::files::save_file($ddffilename ,\@ddffile);
+ my $infoline = "Created ddf file: $ddffilename\n";
+ push(@installer::globals::logfileinfo, $infoline);
+
+ # lines in ddf files must not be longer than 256 characters
+ check_ddf_file(\@ddffile, $ddffilename);
+
+ # Writing the makecab system call
+
+ my $oneline = "makecab.exe /V3 /F " . $ddffilename . " 2\>\&1 |" . "\n";
+
+ push(@cabfilelist, $oneline);
+
+ # collecting all ddf files
+ push(@installer::globals::allddffiles, $ddffilename);
+ }
+ }
+ elsif ((( $installer::globals::cab_file_per_component ) || ( $installer::globals::fix_number_of_cab_files )) && ( $installer::globals::updatedatabase ))
+ {
+ my $sequenceorder = get_sequenceorder($filesref);
+
+ my $counter = 1;
+ my $currentcabfile = "";
+
+ while ( ( exists($sequenceorder->{$counter}) ) || ( exists($installer::globals::allmergemodulefilesequences{$counter}) ) ) # Taking care of files from merge modules
+ {
+# if ( exists($installer::globals::allmergemodulefilesequences{$counter}) )
+# {
+# # Skipping this sequence, it is not included in $filesref, because it is assigned to a file from a merge module.\n";
+# $counter++;
+# next;
+# }
+
+ my $onefile = ${$filesref}[$sequenceorder->{$counter}];
+ $counter++;
+
+ my $cabinetfile = $onefile->{'cabinet'};
+ my $sourcepath = $onefile->{'sourcepath'};
+ if ( $^O =~ /cygwin/i ) { $sourcepath = $onefile->{'cyg_sourcepath'}; }
+ my $uniquename = $onefile->{'uniquename'};
+
+ my $styles = "";
+ my $doinclude = 1;
+ if ( $onefile->{'Styles'} ) { $styles = $onefile->{'Styles'}; };
+ if ( $styles =~ /\bDONT_PACK\b/ ) { $doinclude = 0; }
+
+ # to avoid lines with more than 256 characters, it can be useful to use relative pathes
+ if ( $allvariables->{'RELATIVE_PATHES_IN_DDF'} ) { $sourcepath = make_relative_ddf_path($sourcepath); }
+
+ my @ddffile = ();
+
+ write_ddf_file_header(\@ddffile, $cabinetfile, $installdir);
+
+ my $ddfline = "\"" . $sourcepath . "\"" . " " . $uniquename . "\n";
+ if ( $doinclude ) { push(@ddffile, $ddfline); }
+
+ my $nextfile = "";
+ if ( ${$filesref}[$sequenceorder->{$counter}] ) { $nextfile = ${$filesref}[$sequenceorder->{$counter}]; }
+
+ my $nextcabinetfile = "";
+
+ if ( $nextfile->{'cabinet'} ) { $nextcabinetfile = $nextfile->{'cabinet'}; }
+
+ while ( $nextcabinetfile eq $cabinetfile )
+ {
+ $sourcepath = $nextfile->{'sourcepath'};
+ if ( $^O =~ /cygwin/i ) { $sourcepath = $nextfile->{'cyg_sourcepath'}; }
+ # to avoid lines with more than 256 characters, it can be useful to use relative pathes
+ if ( $allvariables->{'RELATIVE_PATHES_IN_DDF'} ) { $sourcepath = make_relative_ddf_path($sourcepath); }
+ $uniquename = $nextfile->{'uniquename'};
+ my $localdoinclude = 1;
+ my $nextfilestyles = "";
+ if ( $nextfile->{'Styles'} ) { $nextfilestyles = $nextfile->{'Styles'}; }
+ if ( $nextfilestyles =~ /\bDONT_PACK\b/ ) { $localdoinclude = 0; }
+ $ddfline = "\"" . $sourcepath . "\"" . " " . $uniquename . "\n";
+ if ( $localdoinclude ) { push(@ddffile, $ddfline); }
+ $counter++; # increasing the counter!
+ $nextfile = "";
+ $nextcabinetfile = "_lastfile_";
+ if (( exists($sequenceorder->{$counter}) ) && ( ${$filesref}[$sequenceorder->{$counter}] ))
+ {
+ $nextfile = ${$filesref}[$sequenceorder->{$counter}];
+ $nextcabinetfile = $nextfile->{'cabinet'};
+ }
+ }
+
+ # creating the DDF file
+
+ my $ddffilename = $cabinetfile;
+ $ddffilename =~ s/.cab/.ddf/;
+ $ddfdir =~ s/\Q$installer::globals::separator\E\s*$//;
+ $ddffilename = $ddfdir . $installer::globals::separator . $ddffilename;
+
+ installer::files::save_file($ddffilename ,\@ddffile);
+ my $infoline = "Created ddf file: $ddffilename\n";
+ push(@installer::globals::logfileinfo, $infoline);
+
+ # lines in ddf files must not be longer than 256 characters
+ check_ddf_file(\@ddffile, $ddffilename);
+
+ # Writing the makecab system call
+
+ my $oneline = "makecab.exe /V3 /F " . $ddffilename . " 2\>\&1 |" . "\n";
+
+ push(@cabfilelist, $oneline);
+
+ # collecting all ddf files
+ push(@installer::globals::allddffiles, $ddffilename);
+ }
+ }
+ elsif (( $installer::globals::cab_file_per_component ) || ( $installer::globals::fix_number_of_cab_files ))
+ {
+ for ( my $i = 0; $i <= $#{$filesref}; $i++ )
+ {
+ my $onefile = ${$filesref}[$i];
+ my $cabinetfile = $onefile->{'cabinet'};
+ my $sourcepath = $onefile->{'sourcepath'};
+ if ( $^O =~ /cygwin/i ) { $sourcepath = $onefile->{'cyg_sourcepath'}; }
+ my $uniquename = $onefile->{'uniquename'};
+
+ my $styles = "";
+ my $doinclude = 1;
+ if ( $onefile->{'Styles'} ) { $styles = $onefile->{'Styles'}; };
+ if ( $styles =~ /\bDONT_PACK\b/ ) { $doinclude = 0; }
+
+
+ # to avoid lines with more than 256 characters, it can be useful to use relative pathes
+ if ( $allvariables->{'RELATIVE_PATHES_IN_DDF'} ) { $sourcepath = make_relative_ddf_path($sourcepath); }
+
+ # all files with the same cabinetfile are directly behind each other in the files collector
+
+ my @ddffile = ();
+
+ write_ddf_file_header(\@ddffile, $cabinetfile, $installdir);
+
+ my $ddfline = "\"" . $sourcepath . "\"" . " " . $uniquename . "\n";
+ if ( $doinclude ) { push(@ddffile, $ddfline); }
+
+ my $nextfile = ${$filesref}[$i+1];
+ my $nextcabinetfile = "";
+
+ if ( $nextfile->{'cabinet'} ) { $nextcabinetfile = $nextfile->{'cabinet'}; }
+
+ while ( $nextcabinetfile eq $cabinetfile )
+ {
+ $sourcepath = $nextfile->{'sourcepath'};
+ if ( $^O =~ /cygwin/i ) { $sourcepath = $nextfile->{'cyg_sourcepath'}; }
+ # to avoid lines with more than 256 characters, it can be useful to use relative pathes
+ if ( $allvariables->{'RELATIVE_PATHES_IN_DDF'} ) { $sourcepath = make_relative_ddf_path($sourcepath); }
+ $uniquename = $nextfile->{'uniquename'};
+ my $localdoinclude = 1;
+ my $nextfilestyles = "";
+ if ( $nextfile->{'Styles'} ) { $nextfilestyles = $nextfile->{'Styles'}; }
+ if ( $nextfilestyles =~ /\bDONT_PACK\b/ ) { $localdoinclude = 0; }
+ $ddfline = "\"" . $sourcepath . "\"" . " " . $uniquename . "\n";
+ if ( $localdoinclude ) { push(@ddffile, $ddfline); }
+ $i++; # increasing the counter!
+ $nextfile = ${$filesref}[$i+1];
+ if ( $nextfile ) { $nextcabinetfile = $nextfile->{'cabinet'}; }
+ else { $nextcabinetfile = "_lastfile_"; }
+ }
+
+ # creating the DDF file
+
+ my $ddffilename = $cabinetfile;
+ $ddffilename =~ s/.cab/.ddf/;
+ $ddfdir =~ s/\Q$installer::globals::separator\E\s*$//;
+ $ddffilename = $ddfdir . $installer::globals::separator . $ddffilename;
+
+ installer::files::save_file($ddffilename ,\@ddffile);
+ my $infoline = "Created ddf file: $ddffilename\n";
+ push(@installer::globals::logfileinfo, $infoline);
+
+ # lines in ddf files must not be longer than 256 characters
+ check_ddf_file(\@ddffile, $ddffilename);
+
+ # Writing the makecab system call
+
+ my $oneline = "makecab.exe /V3 /F " . $ddffilename . " 2\>\&1 |" . "\n";
+
+ push(@cabfilelist, $oneline);
+
+ # collecting all ddf files
+ push(@installer::globals::allddffiles, $ddffilename);
+ }
+ }
+ elsif (( $installer::globals::one_cab_file ) && ( $installer::globals::updatedatabase ))
+ {
+ my $sequenceorder = get_sequenceorder($filesref);
+
+ my $counter = 1;
+ my $currentcabfile = "";
+
+ while ( ( exists($sequenceorder->{$counter}) ) || ( exists($installer::globals::allmergemodulefilesequences{$counter}) ) ) # Taking care of files from merge modules
+ {
+ if ( exists($installer::globals::allmergemodulefilesequences{$counter}) )
+ {
+ # Skipping this sequence, it is not included in $filesref, because it is assigned to a file from a merge module.\n";
+ $counter++;
+ next;
+ }
+
+ my $onefile = ${$filesref}[$sequenceorder->{$counter}];
+
+ $cabinetfile = $onefile->{'cabinet'};
+ my $sourcepath = $onefile->{'sourcepath'};
+ if ( $^O =~ /cygwin/i ) { $sourcepath = $onefile->{'cyg_sourcepath'}; }
+ my $uniquename = $onefile->{'uniquename'};
+
+ # to avoid lines with more than 256 characters, it can be useful to use relative pathes
+ if ( $allvariables->{'RELATIVE_PATHES_IN_DDF'} ) { $sourcepath = make_relative_ddf_path($sourcepath); }
+
+ if ( $counter == 1 ) { write_ddf_file_header(\@ddffile, $cabinetfile, $installdir); }
+
+ my $styles = "";
+ my $doinclude = 1;
+ if ( $onefile->{'Styles'} ) { $styles = $onefile->{'Styles'}; };
+ if ( $styles =~ /\bDONT_PACK\b/ ) { $doinclude = 0; }
+
+ my $ddfline = "\"" . $sourcepath . "\"" . " " . $uniquename . "\n";
+ if ( $doinclude ) { push(@ddffile, $ddfline); }
+
+ $counter++; # increasing the counter
+ }
+
+ # creating the DDF file
+
+ my $ddffilename = $cabinetfile;
+ $ddffilename =~ s/.cab/.ddf/;
+ $ddfdir =~ s/[\/\\]\s*$//;
+ $ddffilename = $ddfdir . $installer::globals::separator . $ddffilename;
+
+ installer::files::save_file($ddffilename ,\@ddffile);
+ my $infoline = "Created ddf file: $ddffilename\n";
+ push(@installer::globals::logfileinfo, $infoline);
+
+ # lines in ddf files must not be longer than 256 characters
+ check_ddf_file(\@ddffile, $ddffilename);
+
+ # Writing the makecab system call
+
+ # my $oneline = "makecab.exe /F " . $ddffilename . "\n";
+ my $oneline = "makecab.exe /V3 /F " . $ddffilename . " 2\>\&1 |" . "\n";
+
+ push(@cabfilelist, $oneline);
+
+ # collecting all ddf files
+ push(@installer::globals::allddffiles, $ddffilename);
+ }
+ elsif ( $installer::globals::one_cab_file )
+ {
+ my @ddffile = ();
+
+ my $cabinetfile = "";
+
+ for ( my $i = 0; $i <= $#{$filesref}; $i++ )
+ {
+ my $onefile = ${$filesref}[$i];
+ $cabinetfile = $onefile->{'cabinet'};
+ my $sourcepath = $onefile->{'sourcepath'};
+ if ( $^O =~ /cygwin/i ) { $sourcepath = $onefile->{'cyg_sourcepath'}; }
+ my $uniquename = $onefile->{'uniquename'};
+
+ # to avoid lines with more than 256 characters, it can be useful to use relative pathes
+ if ( $allvariables->{'RELATIVE_PATHES_IN_DDF'} ) { $sourcepath = make_relative_ddf_path($sourcepath); }
+
+ if ( $i == 0 ) { write_ddf_file_header(\@ddffile, $cabinetfile, $installdir); }
+
+ my $styles = "";
+ my $doinclude = 1;
+ if ( $onefile->{'Styles'} ) { $styles = $onefile->{'Styles'}; };
+ if ( $styles =~ /\bDONT_PACK\b/ ) { $doinclude = 0; }
+
+ my $ddfline = "\"" . $sourcepath . "\"" . " " . $uniquename . "\n";
+ if ( $doinclude ) { push(@ddffile, $ddfline); }
+ }
+
+ # creating the DDF file
+
+ my $ddffilename = $cabinetfile;
+ $ddffilename =~ s/.cab/.ddf/;
+ $ddfdir =~ s/[\/\\]\s*$//;
+ $ddffilename = $ddfdir . $installer::globals::separator . $ddffilename;
+
+ installer::files::save_file($ddffilename ,\@ddffile);
+ my $infoline = "Created ddf file: $ddffilename\n";
+ push(@installer::globals::logfileinfo, $infoline);
+
+ # lines in ddf files must not be longer than 256 characters
+ check_ddf_file(\@ddffile, $ddffilename);
+
+ # Writing the makecab system call
+
+ my $oneline = "makecab.exe /F " . $ddffilename . "\n";
+
+ push(@cabfilelist, $oneline);
+
+ # collecting all ddf files
+ push(@installer::globals::allddffiles, $ddffilename);
+ }
+ else
+ {
+ installer::exiter::exit_program("ERROR: No cab file specification in globals.pm !", "create_media_table");
+ }
+
+ installer::logger::include_timestamp_into_logfile("Performance Info: ddf file generation end");
+
+ return \@cabfilelist; # contains all system calls for packaging process
+}
+
+########################################################################
+# Returning the file sequence of a specified file.
+########################################################################
+
+sub get_file_sequence
+{
+ my ($filesref, $uniquefilename) = @_;
+
+ my $sequence = "";
+ my $found_sequence = 0;
+
+ for ( my $i = 0; $i <= $#{$filesref}; $i++ )
+ {
+ my $onefile = ${$filesref}[$i];
+ my $uniquename = $onefile->{'uniquename'};
+
+ if ( $uniquename eq $uniquefilename )
+ {
+ $sequence = $onefile->{'sequencenumber'};
+ $found_sequence = 1;
+ last;
+ }
+ }
+
+ if ( ! $found_sequence ) { installer::exiter::exit_program("ERROR: No sequence found for $uniquefilename !", "get_file_sequence"); }
+
+ return $sequence;
+}
+
+########################################################################
+# For update and patch reasons the pack order needs to be saved.
+# The pack order is saved in the ddf files; the names and locations
+# of the ddf files are saved in @installer::globals::allddffiles.
+# The outputfile "packorder.txt" can be saved in
+# $installer::globals::infodirectory .
+########################################################################
+
+sub save_packorder
+{
+ installer::logger::include_header_into_logfile("Saving pack order");
+
+ installer::logger::include_timestamp_into_logfile("Performance Info: saving pack order start");
+
+ my $packorderfilename = "packorder.txt";
+ $packorderfilename = $installer::globals::infodirectory . $installer::globals::separator . $packorderfilename;
+
+ my @packorder = ();
+
+ my $headerline = "\# Syntax\: Filetable_Sequence Cabinetfilename Physical_FileName Unique_FileName\n\n";
+ push(@packorder, $headerline);
+
+ for ( my $i = 0; $i <= $#installer::globals::allddffiles; $i++ )
+ {
+ my $ddffilename = $installer::globals::allddffiles[$i];
+ my $ddffile = installer::files::read_file($ddffilename);
+ my $cabinetfile = "";
+
+ for ( my $j = 0; $j <= $#{$ddffile}; $j++ )
+ {
+ my $oneline = ${$ddffile}[$j];
+
+ # Getting the Cabinet file name
+
+ if ( $oneline =~ /^\s*\.Set\s+CabinetName.*\=(.*?)\s*$/ ) { $cabinetfile = $1; }
+ if ( $oneline =~ /^\s*\.Set\s+/ ) { next; }
+
+ if ( $oneline =~ /^\s*\"(.*?)\"\s+(.*?)\s*$/ )
+ {
+ my $sourcefile = $1;
+ my $uniquefilename = $2;
+
+ installer::pathanalyzer::make_absolute_filename_to_relative_filename(\$sourcefile);
+
+ # Using the hash created in create_files_table for performance reasons to get the sequence number
+ my $filesequence = "";
+ if ( exists($installer::globals::uniquefilenamesequence{$uniquefilename}) ) { $filesequence = $installer::globals::uniquefilenamesequence{$uniquefilename}; }
+ else { installer::exiter::exit_program("ERROR: No sequence number value for $uniquefilename !", "save_packorder"); }
+
+ my $line = $filesequence . "\t" . $cabinetfile . "\t" . $sourcefile . "\t" . $uniquefilename . "\n";
+ push(@packorder, $line);
+ }
+ }
+ }
+
+ installer::files::save_file($packorderfilename ,\@packorder);
+
+ installer::logger::include_timestamp_into_logfile("Performance Info: saving pack order end");
+}
+
+#################################################################
+# Returning the name of the msi database
+#################################################################
+
+sub get_msidatabasename
+{
+ my ($allvariableshashref, $language) = @_;
+
+ my $databasename = $allvariableshashref->{'PRODUCTNAME'} . $allvariableshashref->{'PRODUCTVERSION'};
+ $databasename = lc($databasename);
+ $databasename =~ s/\.//g;
+ $databasename =~ s/\-//g;
+ $databasename =~ s/\s//g;
+
+ # possibility to overwrite the name with variable DATABASENAME
+ if ( $allvariableshashref->{'DATABASENAME'} )
+ {
+ $databasename = $allvariableshashref->{'DATABASENAME'};
+ }
+
+ if ( $language )
+ {
+ if (!($language eq ""))
+ {
+ $databasename .= "_$language";
+ }
+ }
+
+ $databasename .= ".msi";
+
+ return $databasename;
+}
+
+#################################################################
+# Creating the msi database
+# This works only on Windows
+#################################################################
+
+sub create_msi_database
+{
+ my ($idtdirbase ,$msifilename) = @_;
+
+ # -f : path containing the idt files
+ # -d : msi database, including path
+ # -c : create database
+ # -i : include the following tables ("*" includes all available tables)
+
+ my $msidb = "msidb.exe"; # Has to be in the path
+ my $extraslash = ""; # Has to be set for non-ActiveState perl
+
+ installer::logger::include_header_into_logfile("Creating msi database");
+
+ $idtdirbase = installer::converter::make_path_conform($idtdirbase);
+
+ $msifilename = installer::converter::make_path_conform($msifilename);
+
+ if ( $^O =~ /cygwin/i ) {
+ # msidb.exe really wants backslashes. (And double escaping because system() expands the string.)
+ $idtdirbase =~ s/\//\\\\/g;
+ $msifilename =~ s/\//\\\\/g;
+ $extraslash = "\\";
+ }
+ my $systemcall = $msidb . " -f " . $idtdirbase . " -d " . $msifilename . " -c " . "-i " . $extraslash . "*";
+
+ my $returnvalue = system($systemcall);
+
+ my $infoline = "Systemcall: $systemcall\n";
+ push( @installer::globals::logfileinfo, $infoline);
+
+ if ($returnvalue)
+ {
+ $infoline = "ERROR: Could not execute $msidb!\n";
+ push( @installer::globals::logfileinfo, $infoline);
+ }
+ else
+ {
+ $infoline = "Success: Executed $msidb successfully!\n";
+ push( @installer::globals::logfileinfo, $infoline);
+ }
+}
+
+#####################################################################
+# Returning the value from sis.mlf for Summary Information Stream
+#####################################################################
+
+sub get_value_from_sis_lng
+{
+ my ($language, $languagefile, $searchstring) = @_;
+
+ my $language_block = installer::windows::idtglobal::get_language_block_from_language_file($searchstring, $languagefile);
+ my $newstring = installer::windows::idtglobal::get_language_string_from_language_block($language_block, $language, $searchstring);
+ $newstring = "\"" . $newstring . "\"";
+
+ return $newstring;
+}
+
+#################################################################
+# Returning the msi version for the Summary Information Stream
+#################################################################
+
+sub get_msiversion_for_sis
+{
+ my $msiversion = "200";
+ return $msiversion;
+}
+
+#################################################################
+# Returning the word count for the Summary Information Stream
+#################################################################
+
+sub get_wordcount_for_sis
+{
+ my $wordcount = "0";
+ return $wordcount;
+}
+
+#################################################################
+# Returning the codepage for the Summary Information Stream
+#################################################################
+
+sub get_codepage_for_sis
+{
+ my ( $language ) = @_;
+
+ my $codepage = installer::windows::language::get_windows_encoding($language);
+
+ # Codepage 65001 does not work in Summary Information Stream
+ if ( $codepage == 65001 ) { $codepage = 0; }
+
+ # my $codepage = "1252"; # determine dynamically in a function
+ # my $codepage = "65001"; # UTF-8
+ return $codepage;
+}
+
+#################################################################
+# Returning the template for the Summary Information Stream
+#################################################################
+
+sub get_template_for_sis
+{
+ my ( $language ) = @_;
+
+ my $windowslanguage = installer::windows::language::get_windows_language($language);
+
+ my $value = "\"Intel;" . $windowslanguage; # adding the Windows language
+
+ $value = $value . "\""; # adding ending '"'
+
+ return $value ;
+}
+
+#################################################################
+# Returning the PackageCode for the Summary Information Stream
+#################################################################
+
+sub get_packagecode_for_sis
+{
+ # always generating a new package code for each package
+
+ my $guidref = get_guid_list(1, 1); # only one GUID shall be generated
+
+ ${$guidref}[0] =~ s/\s*$//; # removing ending spaces
+
+ my $guid = "\{" . ${$guidref}[0] . "\}";
+
+ my $infoline = "PackageCode: $guid\n";
+ push( @installer::globals::logfileinfo, $infoline);
+
+ return $guid;
+}
+
+#################################################################
+# Returning the title for the Summary Information Stream
+#################################################################
+
+sub get_title_for_sis
+{
+ my ( $language, $languagefile, $searchstring ) = @_;
+
+ my $title = get_value_from_sis_lng($language, $languagefile, $searchstring );
+
+ return $title;
+}
+
+#################################################################
+# Returning the author for the Summary Information Stream
+#################################################################
+
+sub get_author_for_sis
+{
+ my $author = $installer::globals::longmanufacturer;
+
+ $author = "\"" . $author . "\"";
+
+ return $author;
+}
+
+#################################################################
+# Returning the subject for the Summary Information Stream
+#################################################################
+
+sub get_subject_for_sis
+{
+ my ( $allvariableshashref ) = @_;
+
+ my $subject = $allvariableshashref->{'PRODUCTNAME'} . " " . $allvariableshashref->{'PRODUCTVERSION'};
+
+ $subject = "\"" . $subject . "\"";
+
+ return $subject;
+}
+
+#################################################################
+# Returning the comment for the Summary Information Stream
+#################################################################
+
+sub get_comment_for_sis
+{
+ my ( $language, $languagefile, $searchstring ) = @_;
+
+ my $comment = get_value_from_sis_lng($language, $languagefile, $searchstring );
+
+ return $comment;
+}
+
+#################################################################
+# Returning the keywords for the Summary Information Stream
+#################################################################
+
+sub get_keywords_for_sis
+{
+ my ( $language, $languagefile, $searchstring ) = @_;
+
+ my $keywords = get_value_from_sis_lng($language, $languagefile, $searchstring );
+
+ return $keywords;
+}
+
+######################################################################
+# Returning the application name for the Summary Information Stream
+######################################################################
+
+sub get_appname_for_sis
+{
+ my ( $language, $languagefile, $searchstring ) = @_;
+
+ my $appname = get_value_from_sis_lng($language, $languagefile, $searchstring );
+
+ return $appname;
+}
+
+######################################################################
+# Returning the security for the Summary Information Stream
+######################################################################
+
+sub get_security_for_sis
+{
+ my $security = "0";
+ return $security;
+}
+
+#################################################################
+# Writing the Summary information stream into the msi database
+# This works only on Windows
+#################################################################
+
+sub write_summary_into_msi_database
+{
+ my ($msifilename, $language, $languagefile, $allvariableshashref) = @_;
+
+ # -g : requrired msi version
+ # -c : codepage
+ # -p : template
+
+ installer::logger::include_header_into_logfile("Writing summary information stream");
+
+ my $msiinfo = "msiinfo.exe"; # Has to be in the path
+
+ my $sislanguage = "en-US"; # title, comment, keyword and appname alway in english
+
+ my $msiversion = get_msiversion_for_sis();
+ my $codepage = get_codepage_for_sis($language);
+ my $template = get_template_for_sis($language);
+ my $guid = get_packagecode_for_sis();
+ my $title = get_title_for_sis($sislanguage,$languagefile, "OOO_SIS_TITLE");
+ my $author = get_author_for_sis();
+ my $subject = get_subject_for_sis($allvariableshashref);
+ my $comment = get_comment_for_sis($sislanguage,$languagefile, "OOO_SIS_COMMENT");
+ my $keywords = get_keywords_for_sis($sislanguage,$languagefile, "OOO_SIS_KEYWORDS");
+ my $appname = get_appname_for_sis($sislanguage,$languagefile, "OOO_SIS_APPNAME");
+ my $security = get_security_for_sis();
+ my $wordcount = get_wordcount_for_sis();
+
+ $msifilename = installer::converter::make_path_conform($msifilename);
+
+ my $systemcall = $msiinfo . " " . $msifilename . " -g " . $msiversion . " -c " . $codepage
+ . " -p " . $template . " -v " . $guid . " -t " . $title . " -a " . $author
+ . " -j " . $subject . " -o " . $comment . " -k " . $keywords . " -n " . $appname
+ . " -u " . $security . " -w " . $wordcount;
+
+ my $returnvalue = system($systemcall);
+
+ my $infoline = "Systemcall: $systemcall\n";
+ push( @installer::globals::logfileinfo, $infoline);
+
+ if ($returnvalue)
+ {
+ $infoline = "ERROR: Could not execute $msiinfo!\n";
+ push( @installer::globals::logfileinfo, $infoline);
+ }
+ else
+ {
+ $infoline = "Success: Executed $msiinfo successfully!\n";
+ push( @installer::globals::logfileinfo, $infoline);
+ }
+}
+
+#########################################################################
+# For more than one language in the installation set:
+# Use one database and create Transformations for all other languages
+#########################################################################
+
+sub create_transforms
+{
+ my ($languagesarray, $defaultlanguage, $installdir, $allvariableshashref) = @_;
+
+ installer::logger::include_header_into_logfile("Creating Transforms");
+
+ my $msitran = "msitran.exe"; # Has to be in the path
+
+ $installdir = installer::converter::make_path_conform($installdir);
+
+ # Syntax for creating a transformation
+ # msitran.exe -g <baseDB> <referenceDB> <transformfile> [<errorhandling>}
+
+ my $basedbname = get_msidatabasename($allvariableshashref, $defaultlanguage);
+ $basedbname = $installdir . $installer::globals::separator . $basedbname;
+
+ my $errorhandling = "f"; # Suppress "change codepage" error
+
+ # Iterating over all files
+
+ foreach ( @{$languagesarray} )
+ {
+ my $onelanguage = $_;
+
+ if ( $onelanguage eq $defaultlanguage ) { next; }
+
+ my $referencedbname = get_msidatabasename($allvariableshashref, $onelanguage);
+ $referencedbname = $installdir . $installer::globals::separator . $referencedbname;
+
+ my $transformfile = $installdir . $installer::globals::separator . "trans_" . $onelanguage . ".mst";
+
+ my $systemcall = $msitran . " " . " -g " . $basedbname . " " . $referencedbname . " " . $transformfile . " " . $errorhandling;
+
+ my $returnvalue = system($systemcall);
+
+ my $infoline = "Systemcall: $systemcall\n";
+ push( @installer::globals::logfileinfo, $infoline);
+
+ # Problem: msitran.exe in version 4.0 always returns "1", even if no failure occured.
+ # Therefore it has to be checked, if this is version 4.0. If yes, if the mst file
+ # exists and if it is larger than 0 bytes. If this is true, then no error occured.
+ # File Version of msitran.exe: 4.0.6000.16384 has checksum: "b66190a70145a57773ec769e16777b29".
+ # Same for msitran.exe from wntmsci12: "aa25d3445b94ffde8ef0c1efb77a56b8"
+
+ if ($returnvalue)
+ {
+ $infoline = "WARNING: Returnvalue of $msitran is not 0. Checking version of $msitran!\n";
+ push( @installer::globals::logfileinfo, $infoline);
+
+ open(FILE, "<$installer::globals::msitranpath") or die "ERROR: Can't open $installer::globals::msitranpath for creating file hash";
+ binmode(FILE);
+ my $digest = Digest::MD5->new->addfile(*FILE)->hexdigest;
+ close(FILE);
+
+ my @problemchecksums = ("b66190a70145a57773ec769e16777b29", "aa25d3445b94ffde8ef0c1efb77a56b8");
+ my $isproblemchecksum = 0;
+
+ foreach my $problemchecksum ( @problemchecksums )
+ {
+ $infoline = "Checksum of problematic MsiTran.exe: $problemchecksum\n";
+ push( @installer::globals::logfileinfo, $infoline);
+ $infoline = "Checksum of used MsiTran.exe: $digest\n";
+ push( @installer::globals::logfileinfo, $infoline);
+ if ( $digest eq $problemchecksum ) { $isproblemchecksum = 1; }
+ }
+
+ if ( $isproblemchecksum )
+ {
+ # Check existence of mst
+ if ( -f $transformfile )
+ {
+ $infoline = "File $transformfile exists.\n";
+ push( @installer::globals::logfileinfo, $infoline);
+ my $filesize = ( -s $transformfile );
+ $infoline = "Size of $transformfile: $filesize\n";
+ push( @installer::globals::logfileinfo, $infoline);
+
+ if ( $filesize > 0 )
+ {
+ $infoline = "Info: Returnvalue $returnvalue of $msitran is no problem :-) .\n";
+ push( @installer::globals::logfileinfo, $infoline);
+ $returnvalue = 0; # reset the error
+ }
+ else
+ {
+ $infoline = "Filesize indicates that an error occured.\n";
+ push( @installer::globals::logfileinfo, $infoline);
+ }
+ }
+ else
+ {
+ $infoline = "File $transformfile does not exist -> An error occured.\n";
+ push( @installer::globals::logfileinfo, $infoline);
+ }
+ }
+ else
+ {
+ $infoline = "This is not a problematic version of msitran.exe. Therefore the error is not caused by problematic msitran.exe.\n";
+ push( @installer::globals::logfileinfo, $infoline);
+ }
+ }
+
+ if ($returnvalue)
+ {
+ $infoline = "ERROR: Could not execute $msitran!\n";
+ push( @installer::globals::logfileinfo, $infoline);
+ }
+ else
+ {
+ $infoline = "Success: Executed $msitran successfully!\n";
+ push( @installer::globals::logfileinfo, $infoline);
+ }
+
+ # The reference database can be deleted
+
+ my $result = unlink($referencedbname);
+ # $result contains the number of deleted files
+
+ if ( $result == 0 )
+ {
+ $infoline = "ERROR: Could not remove file $$referencedbname !\n";
+ push( @installer::globals::logfileinfo, $infoline);
+ installer::exiter::exit_program($infoline, "create_transforms");
+ }
+ }
+}
+
+#########################################################################
+# The default language msi database does not need to contain
+# the language in the database name. Therefore the file
+# is renamed. Example: "openofficeorg20_01.msi" to "openofficeorg20.msi"
+#########################################################################
+
+sub rename_msi_database_in_installset
+{
+ my ($defaultlanguage, $installdir, $allvariableshashref) = @_;
+
+ installer::logger::include_header_into_logfile("Renaming msi database");
+
+ my $olddatabasename = get_msidatabasename($allvariableshashref, $defaultlanguage);
+ $olddatabasename = $installdir . $installer::globals::separator . $olddatabasename;
+
+ my $newdatabasename = get_msidatabasename($allvariableshashref);
+
+ $installer::globals::shortmsidatabasename = $newdatabasename;
+
+ $newdatabasename = $installdir . $installer::globals::separator . $newdatabasename;
+
+ installer::systemactions::rename_one_file($olddatabasename, $newdatabasename);
+
+ $installer::globals::msidatabasename = $newdatabasename;
+}
+
+#########################################################################
+# Adding the language to the name of the msi databasename,
+# if this is required (ADDLANGUAGEINDATABASENAME)
+#########################################################################
+
+sub add_language_to_msi_database
+{
+ my ($defaultlanguage, $installdir, $allvariables) = @_;
+
+ my $languagestring = $defaultlanguage;
+ if ( $allvariables->{'USELANGUAGECODE'} ) { $languagestring = installer::windows::language::get_windows_language($defaultlanguage); }
+ my $newdatabasename = $installer::globals::shortmsidatabasename;
+ $newdatabasename =~ s/\.msi\s*$/_$languagestring\.msi/;
+ $installer::globals::shortmsidatabasename = $newdatabasename;
+ $newdatabasename = $installdir . $installer::globals::separator . $newdatabasename;
+
+ my $olddatabasename = $installer::globals::msidatabasename;
+
+ installer::systemactions::rename_one_file($olddatabasename, $newdatabasename);
+
+ $installer::globals::msidatabasename = $newdatabasename;
+}
+
+##########################################################################
+# Writing the databasename into the setup.ini.
+##########################################################################
+
+sub put_databasename_into_setupini
+{
+ my ($setupinifile, $allvariableshashref) = @_;
+
+ my $databasename = get_msidatabasename($allvariableshashref);
+ my $line = "database=" . $databasename . "\n";
+
+ push(@{$setupinifile}, $line);
+}
+
+##########################################################################
+# Writing the required msi version into setup.ini
+##########################################################################
+
+sub put_msiversion_into_setupini
+{
+ my ($setupinifile) = @_;
+
+ my $msiversion = "2.0";
+ my $line = "msiversion=" . $msiversion . "\n";
+
+ push(@{$setupinifile}, $line);
+}
+
+##########################################################################
+# Writing the productname into setup.ini
+##########################################################################
+
+sub put_productname_into_setupini
+{
+ my ($setupinifile, $allvariableshashref) = @_;
+
+ my $productname = $allvariableshashref->{'PRODUCTNAME'};
+ my $line = "productname=" . $productname . "\n";
+
+ push(@{$setupinifile}, $line);
+}
+
+##########################################################################
+# Writing the productcode into setup.ini
+##########################################################################
+
+sub put_productcode_into_setupini
+{
+ my ($setupinifile) = @_;
+
+ my $productcode = $installer::globals::productcode;
+ my $line = "productcode=" . $productcode . "\n";
+
+ push(@{$setupinifile}, $line);
+}
+
+##########################################################################
+# Writing the ProductVersion from Property table into setup.ini
+##########################################################################
+
+sub put_productversion_into_setupini
+{
+ my ($setupinifile) = @_;
+
+ my $line = "productversion=" . $installer::globals::msiproductversion . "\n";
+ push(@{$setupinifile}, $line);
+}
+
+##########################################################################
+# Writing the key for Minor Upgrades into setup.ini
+##########################################################################
+
+sub put_upgradekey_into_setupini
+{
+ my ($setupinifile) = @_;
+
+ if ( $installer::globals::minorupgradekey ne "" )
+ {
+ my $line = "upgradekey=" . $installer::globals::minorupgradekey . "\n";
+ push(@{$setupinifile}, $line);
+ }
+}
+
+##########################################################################
+# Writing the number of languages into setup.ini
+##########################################################################
+
+sub put_languagecount_into_setupini
+{
+ my ($setupinifile, $languagesarray) = @_;
+
+ my $languagecount = $#{$languagesarray} + 1;
+ my $line = "count=" . $languagecount . "\n";
+
+ push(@{$setupinifile}, $line);
+}
+
+##########################################################################
+# Writing the defaultlanguage into setup.ini
+##########################################################################
+
+sub put_defaultlanguage_into_setupini
+{
+ my ($setupinifile, $defaultlanguage) = @_;
+
+ my $windowslanguage = installer::windows::language::get_windows_language($defaultlanguage);
+ my $line = "default=" . $windowslanguage . "\n";
+ push(@{$setupinifile}, $line);
+}
+
+##########################################################################
+# Writing the information about transformations into setup.ini
+##########################################################################
+
+sub put_transforms_into_setupini
+{
+ my ($setupinifile, $onelanguage, $counter) = @_;
+
+ my $windowslanguage = installer::windows::language::get_windows_language($onelanguage);
+ my $transformfilename = "trans_" . $onelanguage . ".mst";
+
+ my $line = "lang" . $counter . "=" . $windowslanguage . "," . $transformfilename . "\n";
+
+ push(@{$setupinifile}, $line);
+}
+
+###################################################
+# Including Windows line ends in ini files
+# Profiles on Windows shall have \r\n line ends
+###################################################
+
+sub include_windows_lineends
+{
+ my ($onefile) = @_;
+
+ for ( my $i = 0; $i <= $#{$onefile}; $i++ )
+ {
+ ${$onefile}[$i] =~ s/\r?\n$/\r\n/;
+ }
+}
+
+##########################################################################
+# Generation the file setup.ini, that is used by the loader setup.exe.
+##########################################################################
+
+sub create_setup_ini
+{
+ my ($languagesarray, $defaultlanguage, $installdir, $allvariableshashref) = @_;
+
+ installer::logger::include_header_into_logfile("Creating setup.ini");
+
+ my $setupinifilename = $installdir . $installer::globals::separator . "setup.ini";
+
+ my @setupinifile = ();
+ my $setupinifile = \@setupinifile;
+
+ my $line = "\[setup\]\n";
+ push(@setupinifile, $line);
+
+ put_databasename_into_setupini($setupinifile, $allvariableshashref);
+ put_msiversion_into_setupini($setupinifile);
+ put_productname_into_setupini($setupinifile, $allvariableshashref);
+ put_productcode_into_setupini($setupinifile);
+ put_productversion_into_setupini($setupinifile);
+ put_upgradekey_into_setupini($setupinifile);
+
+ $line = "\[languages\]\n";
+ push(@setupinifile, $line);
+
+ put_languagecount_into_setupini($setupinifile, $languagesarray);
+ put_defaultlanguage_into_setupini($setupinifile, $defaultlanguage);
+
+ if ( $#{$languagesarray} > 0 ) # writing the transforms information
+ {
+ my $counter = 1;
+
+ for ( my $i = 0; $i <= $#{$languagesarray}; $i++ )
+ {
+ if ( ${$languagesarray}[$i] eq $defaultlanguage ) { next; }
+
+ put_transforms_into_setupini($setupinifile, ${$languagesarray}[$i], $counter);
+ $counter++;
+ }
+ }
+
+ if ( $installer::globals::iswin && $installer::globals::plat =~ /cygwin/i) # Windows line ends only for Cygwin
+ {
+ include_windows_lineends($setupinifile);
+ }
+
+ installer::files::save_file($setupinifilename, $setupinifile);
+
+ $infoline = "Generated file $setupinifilename !\n";
+ push( @installer::globals::logfileinfo, $infoline);
+}
+
+#################################################################
+# Copying the files defined as ScpActions into the
+# installation set.
+#################################################################
+
+sub copy_scpactions_into_installset
+{
+ my ($defaultlanguage, $installdir, $allscpactions) = @_;
+
+ installer::logger::include_header_into_logfile("Copying ScpAction files into installation set");
+
+ for ( my $i = 0; $i <= $#{$allscpactions}; $i++ )
+ {
+ my $onescpaction = ${$allscpactions}[$i];
+
+ if ( $onescpaction->{'Name'} eq "loader.exe" ) { next; } # do not copy this ScpAction loader
+
+ # only copying language independent files or files with the correct language (the defaultlanguage)
+
+ my $filelanguage = $onescpaction->{'specificlanguage'};
+
+ if ( ($filelanguage eq $defaultlanguage) || ($filelanguage eq "") )
+ {
+ my $sourcefile = $onescpaction->{'sourcepath'};
+ my $destfile = $installdir . $installer::globals::separator . $onescpaction->{'DestinationName'};
+
+ installer::systemactions::copy_one_file($sourcefile, $destfile);
+ }
+ }
+}
+
+#################################################################
+# Copying the files for the Windows installer into the
+# installation set (setup.exe).
+#################################################################
+
+sub copy_windows_installer_files_into_installset
+{
+ my ($installdir, $includepatharrayref, $allvariables) = @_;
+
+ installer::logger::include_header_into_logfile("Copying Windows installer files into installation set");
+
+ @copyfile = ();
+ push(@copyfile, "loader2.exe");
+
+ if ( $allvariables->{'NOLOADERREQUIRED'} ) { @copyfile = (); }
+
+ for ( my $i = 0; $i <= $#copyfile; $i++ )
+ {
+ my $filename = $copyfile[$i];
+ my $sourcefileref = installer::scriptitems::get_sourcepath_from_filename_and_includepath(\$filename, $includepatharrayref, 1);
+
+ if ( ! -f $$sourcefileref ) { installer::exiter::exit_program("ERROR: msi file not found: $$sourcefileref !", "copy_windows_installer_files_into_installset"); }
+
+ my $destfile;
+ if ( $copyfile[$i] eq "loader2.exe" ) { $destfile = "setup.exe"; } # renaming the loader
+ else { $destfile = $copyfile[$i]; }
+
+ $destfile = $installdir . $installer::globals::separator . $destfile;
+
+ installer::systemactions::copy_one_file($$sourcefileref, $destfile);
+ }
+}
+
+#################################################################
+# Copying MergeModules for the Windows installer into the
+# installation set. The list of MergeModules is located
+# in %installer::globals::copy_msm_files
+#################################################################
+
+sub copy_merge_modules_into_installset
+{
+ my ($installdir) = @_;
+
+ installer::logger::include_header_into_logfile("Copying Merge files into installation set");
+
+ my $cabfile;
+ foreach $cabfile ( keys %installer::globals::copy_msm_files )
+ {
+ my $sourcefile = $installer::globals::copy_msm_files{$cabfile};
+ my $destfile = $installdir . $installer::globals::separator . $cabfile;
+
+ installer::systemactions::copy_one_file($sourcefile, $destfile);
+ }
+}
+
+#################################################################
+# Copying the child projects into the
+# installation set
+#################################################################
+
+sub copy_child_projects_into_installset
+{
+ my ($installdir, $allvariables) = @_;
+
+ my $sourcefile = "";
+ my $destdir = "";
+
+ # adding Java
+
+ if ( $allvariables->{'JAVAPRODUCT'} )
+ {
+ $sourcefile = $installer::globals::javafile->{'sourcepath'};
+ $destdir = $installdir . $installer::globals::separator . $installer::globals::javafile->{'Subdir'};
+ if ( ! -d $destdir) { installer::systemactions::create_directory($destdir); }
+ installer::systemactions::copy_one_file($sourcefile, $destdir);
+ }
+
+ if ( $allvariables->{'UREPRODUCT'} )
+ {
+ $sourcefile = $installer::globals::urefile->{'sourcepath'};
+ $destdir = $installdir . $installer::globals::separator . $installer::globals::urefile->{'Subdir'};
+ if ( ! -d $destdir) { installer::systemactions::create_directory($destdir); }
+ installer::systemactions::copy_one_file($sourcefile, $destdir);
+ }
+}
+
+#################################################################
+# Getting a list of GUID using uuidgen.exe.
+# This works only on Windows
+#################################################################
+
+sub get_guid_list
+{
+ my ($number, $log) = @_;
+
+ if ( $log ) { installer::logger::include_header_into_logfile("Generating $number GUID"); }
+
+ my $uuidgen = "uuidgen.exe"; # Has to be in the path
+
+ # "-c" for uppercase output
+
+ # my $systemcall = "$uuidgen -n$number -c |";
+ my $systemcall = "$uuidgen -n$number |";
+ open (UUIDGEN, "$systemcall" ) or die("uuidgen is missing.");
+ my @uuidlist = <UUIDGEN>;
+ close (UUIDGEN);
+
+ my $infoline = "Systemcall: $systemcall\n";
+ if ( $log ) { push( @installer::globals::logfileinfo, $infoline); }
+
+ my $comparenumber = $#uuidlist + 1;
+
+ if ( $comparenumber == $number )
+ {
+ $infoline = "Success: Executed $uuidgen successfully!\n";
+ if ( $log ) { push( @installer::globals::logfileinfo, $infoline); }
+ }
+ else
+ {
+ $infoline = "ERROR: Could not execute $uuidgen successfully!\n";
+ if ( $log ) { push( @installer::globals::logfileinfo, $infoline); }
+ }
+
+ # uppercase, no longer "-c", because this is only supported in uuidgen.exe v.1.01
+ for ( my $i = 0; $i <= $#uuidlist; $i++ ) { $uuidlist[$i] = uc($uuidlist[$i]); }
+
+ return \@uuidlist;
+}
+
+#################################################################
+# Calculating a GUID with a string using md5.
+#################################################################
+
+sub calculate_guid
+{
+ my ( $string ) = @_;
+
+ my $guid = "";
+
+ my $md5 = Digest::MD5->new;
+ $md5->add($string);
+ my $digest = $md5->hexdigest;
+ $digest = uc($digest);
+
+ # my $id = pack("A32", $digest);
+ my ($first, $second, $third, $fourth, $fifth) = unpack ('A8 A4 A4 A4 A12', $digest);
+ $guid = "$first-$second-$third-$fourth-$fifth";
+
+ return $guid;
+}
+
+#################################################################
+# Filling the component hash with the values of the
+# component file.
+#################################################################
+
+sub fill_component_hash
+{
+ my ($componentfile) = @_;
+
+ my %components = ();
+
+ for ( my $i = 0; $i <= $#{$componentfile}; $i++ )
+ {
+ my $line = ${$componentfile}[$i];
+
+ if ( $line =~ /^\s*(.*?)\t(.*?)\s*$/ )
+ {
+ my $key = $1;
+ my $value = $2;
+
+ $components{$key} = $value;
+ }
+ }
+
+ return \%components;
+}
+
+#################################################################
+# Creating a new component file, if new guids were generated.
+#################################################################
+
+sub create_new_component_file
+{
+ my ($componenthash) = @_;
+
+ my @componentfile = ();
+
+ my $key;
+
+ foreach $key (keys %{$componenthash})
+ {
+ my $value = $componenthash->{$key};
+ my $input = "$key\t$value\n";
+ push(@componentfile ,$input);
+ }
+
+ return \@componentfile;
+}
+
+#################################################################
+# Filling real component GUID into the component table.
+# This works only on Windows
+#################################################################
+
+sub set_uuid_into_component_table
+{
+ my ($idtdirbase, $allvariables) = @_;
+
+ my $componenttablename = $idtdirbase . $installer::globals::separator . "Componen.idt";
+
+ my $componenttable = installer::files::read_file($componenttablename);
+
+ # For update and patch reasons (small update) the GUID of an existing component must not change!
+ # The collection of component GUIDs is saved in the directory $installer::globals::idttemplatepath in the file "components.txt"
+
+ my $infoline = "";
+ my $counter = 0;
+ # my $componentfile = installer::files::read_file($installer::globals::componentfilename);
+ # my $componenthash = fill_component_hash($componentfile);
+
+ for ( my $i = 3; $i <= $#{$componenttable}; $i++ ) # ignoring the first three lines
+ {
+ my $oneline = ${$componenttable}[$i];
+ my $componentname = "";
+ if ( $oneline =~ /^\s*(\S+?)\t/ ) { $componentname = $1; }
+
+ my $uuid = "";
+
+ # if ( $componenthash->{$componentname} )
+ # {
+ # $uuid = $componenthash->{$componentname};
+ # }
+ # else
+ # {
+
+ if ( exists($installer::globals::calculated_component_guids{$componentname}))
+ {
+ $uuid = $installer::globals::calculated_component_guids{$componentname};
+ }
+ else
+ {
+ # Calculating new GUID with the help of the component name.
+ my $useooobaseversion = 1;
+ if ( exists($installer::globals::base_independent_components{$componentname})) { $useooobaseversion = 0; }
+ my $sourcestring = $componentname;
+
+ if ( $useooobaseversion )
+ {
+ if ( ! exists($allvariables->{'OOOBASEVERSION'}) ) { installer::exiter::exit_program("ERROR: Could not find variable \"OOOBASEVERSION\" (required value for GUID creation)!", "set_uuid_into_component_table"); }
+ $sourcestring = $sourcestring . "_" . $allvariables->{'OOOBASEVERSION'};
+ }
+ $uuid = calculate_guid($sourcestring);
+ $counter++;
+
+ # checking, if there is a conflict with an already created guid
+ if ( exists($installer::globals::allcalculated_guids{$uuid}) ) { installer::exiter::exit_program("ERROR: \"$uuid\" was already created before!", "set_uuid_into_component_table"); }
+ $installer::globals::allcalculated_guids{$uuid} = 1;
+ $installer::globals::calculated_component_guids{$componentname} = $uuid;
+
+ # Setting new uuid
+ # $componenthash->{$componentname} = $uuid;
+
+ # Setting flag
+ # $installer::globals::created_new_component_guid = 1; # this is very important!
+ }
+ # }
+
+ ${$componenttable}[$i] =~ s/COMPONENTGUID/$uuid/;
+ }
+
+ installer::files::save_file($componenttablename, $componenttable);
+
+# if ( $installer::globals::created_new_component_guid )
+# {
+# # create new component file!
+# $componentfile = create_new_component_file($componenthash);
+# installer::worker::sort_array($componentfile);
+#
+# # To avoid conflict the components file cannot be saved at the same place
+# # All important data have to be saved in the directory: $installer::globals::infodirectory
+# my $localcomponentfilename = $installer::globals::componentfilename;
+# installer::pathanalyzer::make_absolute_filename_to_relative_filename(\$localcomponentfilename);
+# $localcomponentfilename = $installer::globals::infodirectory . $installer::globals::separator . $localcomponentfilename;
+# installer::files::save_file($localcomponentfilename, $componentfile);
+#
+# # installer::files::save_file($installer::globals::componentfilename, $componentfile); # version using new file in solver
+#
+# $infoline = "COMPONENTCODES: Created $counter new GUIDs for components ! \n";
+# push( @installer::globals::logfileinfo, $infoline);
+# }
+# else
+# {
+# $infoline = "SUCCESS COMPONENTCODES: All component codes exist! \n";
+# push( @installer::globals::logfileinfo, $infoline);
+# }
+
+}
+
+#################################################################
+# Include all cab files into the msi database.
+# This works only on Windows
+#################################################################
+
+sub include_cabs_into_msi
+{
+ my ($installdir) = @_;
+
+ installer::logger::include_header_into_logfile("Including cabs into msi database");
+
+ my $from = cwd();
+ my $to = $installdir;
+
+ chdir($to);
+
+ my $infoline = "Changing into directory: $to";
+ push( @installer::globals::logfileinfo, $infoline);
+
+ my $msidb = "msidb.exe"; # Has to be in the path
+ my $extraslash = ""; # Has to be set for non-ActiveState perl
+
+ my $msifilename = $installer::globals::msidatabasename;
+
+ $msifilename = installer::converter::make_path_conform($msifilename);
+
+ # msidb.exe really wants backslashes. (And double escaping because system() expands the string.)
+ $idtdirbase =~ s/\//\\\\/g;
+ $msifilename =~ s/\//\\\\/g;
+ $extraslash = "\\";
+
+ my $allcabfiles = installer::systemactions::find_file_with_file_extension("cab", $installdir);
+
+ for ( my $i = 0; $i <= $#{$allcabfiles}; $i++ )
+ {
+ my $systemcall = $msidb . " -d " . $msifilename . " -a " . ${$allcabfiles}[$i];
+
+ my $returnvalue = system($systemcall);
+
+ $infoline = "Systemcall: $systemcall\n";
+ push( @installer::globals::logfileinfo, $infoline);
+
+ if ($returnvalue)
+ {
+ $infoline = "ERROR: Could not execute $systemcall !\n";
+ push( @installer::globals::logfileinfo, $infoline);
+ }
+ else
+ {
+ $infoline = "Success: Executed $systemcall successfully!\n";
+ push( @installer::globals::logfileinfo, $infoline);
+ }
+
+ # deleting the cab file
+
+ unlink(${$allcabfiles}[$i]);
+
+ $infoline = "Deleted cab file: ${$allcabfiles}[$i]\n";
+ push( @installer::globals::logfileinfo, $infoline);
+ }
+
+ $infoline = "Changing back into directory: $from";
+ push( @installer::globals::logfileinfo, $infoline);
+
+ chdir($from);
+}
+
+#################################################################
+# Executing the created batch file to pack all files.
+# This works only on Windows
+#################################################################
+
+sub execute_packaging
+{
+ my ($localpackjobref, $loggingdir, $allvariables) = @_;
+
+ installer::logger::include_header_into_logfile("Packaging process");
+
+ installer::logger::include_timestamp_into_logfile("Performance Info: Execute packaging start");
+
+ my $infoline = "";
+ my $from = cwd();
+ my $to = $loggingdir;
+
+ chdir($to);
+ $infoline = "chdir: $to \n";
+ push( @installer::globals::logfileinfo, $infoline);
+
+ # if the ddf file contains relative pathes, it is necessary to change into the temp directory
+ if ( $allvariables->{'RELATIVE_PATHES_IN_DDF'} )
+ {
+ $to = $installer::globals::temppath;
+ chdir($to);
+ $infoline = "chdir: $to \n";
+ push( @installer::globals::logfileinfo, $infoline);
+ }
+
+ # changing the tmp directory, because makecab.exe generates temporary cab files
+ my $origtemppath = "";
+ if ( $ENV{'TMP'} ) { $origtemppath = $ENV{'TMP'}; }
+ $ENV{'TMP'} = $installer::globals::temppath; # setting TMP to the new unique directory!
+
+ my $maxmakecabcalls = 3;
+ my $allmakecabcalls = $#{$localpackjobref} + 1;
+
+ for ( my $i = 0; $i <= $#{$localpackjobref}; $i++ )
+ {
+ my $systemcall = ${$localpackjobref}[$i];
+
+ my $callscounter = $i + 1;
+
+ installer::logger::print_message( "... makecab.exe ($callscounter/$allmakecabcalls) ... \n" );
+
+ # my $returnvalue = system($systemcall);
+
+ for ( my $n = 1; $n <= $maxmakecabcalls; $n++ )
+ {
+ my @ddfoutput = ();
+
+ $infoline = "Systemcall: $systemcall";
+ push( @installer::globals::logfileinfo, $infoline);
+
+ open (DDF, "$systemcall");
+ while (<DDF>) {push(@ddfoutput, $_); }
+ close (DDF);
+
+ my $returnvalue = $?; # $? contains the return value of the systemcall
+
+ if ($returnvalue)
+ {
+ if ( $n < $maxmakecabcalls )
+ {
+ installer::logger::print_message( "makecab_error (Try $n): Trying again \n" );
+ $infoline = "makecab_error (Try $n): $systemcall !";
+ }
+ else
+ {
+ installer::logger::print_message( "ERROR (Try $n): Abort packing \n" );
+ $infoline = "ERROR (Try $n): $systemcall !";
+ }
+
+ push( @installer::globals::logfileinfo, $infoline);
+ # for ( my $j = 0; $j <= $#ddfoutput; $j++ ) { push( @installer::globals::logfileinfo, "$ddfoutput[$j]"); }
+
+ for ( my $m = 0; $m <= $#ddfoutput; $m++ )
+ {
+ if ( $ddfoutput[$m] =~ /(ERROR\:.*?)\s*$/ )
+ {
+ $infoline = $1 . "\n";
+ if ( $n < $maxmakecabcalls ) { $infoline =~ s/ERROR\:/makecab_error\:/i; }
+ installer::logger::print_message( $infoline );
+ push( @installer::globals::logfileinfo, $infoline);
+ }
+ }
+
+ if ( $n == $maxmakecabcalls ) { installer::exiter::exit_program("ERROR: \"$systemcall\"!", "execute_packaging"); }
+ }
+ else
+ {
+ # installer::logger::print_message( "Success (Try $n): \"$systemcall\"\n" );
+ $infoline = "Success (Try $n): $systemcall";
+ push( @installer::globals::logfileinfo, $infoline);
+ last;
+ }
+ }
+ }
+
+ installer::logger::include_timestamp_into_logfile("Performance Info: Execute packaging end");
+
+ # setting back to the original tmp directory
+ $ENV{'TMP'} = $origtemppath;
+
+ chdir($from);
+ $infoline = "chdir: $from \n";
+ push( @installer::globals::logfileinfo, $infoline);
+}
+
+###############################################################
+# Setting the global variables ProductCode and the UpgradeCode
+###############################################################
+
+sub set_global_code_variables
+{
+ my ( $languagesref, $languagestringref, $allvariableshashref, $alloldproperties ) = @_;
+
+ # In the msi template directory a files "codes.txt" has to exist, in which the ProductCode
+ # and the UpgradeCode for the product are defined.
+ # The name "codes.txt" can be overwritten in Product definition with CODEFILENAME .
+ # Default $installer::globals::codefilename is defined in parameter.pm.
+
+ if ( $allvariableshashref->{'CODEFILENAME'} )
+ {
+ $installer::globals::codefilename = $installer::globals::idttemplatepath . $installer::globals::separator . $allvariableshashref->{'CODEFILENAME'};
+ installer::files::check_file($installer::globals::codefilename);
+ }
+
+ my $infoline = "Using Codes file: $installer::globals::codefilename \n";
+ push( @installer::globals::logfileinfo, $infoline);
+
+ my $codefile = installer::files::read_file($installer::globals::codefilename);
+
+ my $isopensource = 0;
+ if ( $allvariableshashref->{'OPENSOURCE'} ) { $isopensource = $allvariableshashref->{'OPENSOURCE'}; }
+
+ my $onelanguage = "";
+
+ if ( $#{$languagesref} > 0 ) # more than one language
+ {
+ if (( $installer::globals::added_english ) && ( $#{$languagesref} == 1 )) # only multilingual because of added English
+ {
+ $onelanguage = ${$languagesref}[1]; # setting the first language, that is not english
+ }
+ else
+ {
+ if (( ${$languagesref}[1] =~ /jp/ ) ||
+ ( ${$languagesref}[1] =~ /ko/ ) ||
+ ( ${$languagesref}[1] =~ /zh/ ))
+ {
+ $onelanguage = "multiasia";
+ }
+ else
+ {
+ $onelanguage = "multiwestern";
+ }
+ }
+ }
+ else # only one language
+ {
+ $onelanguage = ${$languagesref}[0];
+ }
+
+ # ProductCode must not change, if Windows patches shall be applied
+ if ( $installer::globals::updatedatabase )
+ {
+ $installer::globals::productcode = $alloldproperties->{'ProductCode'};
+ }
+ elsif ( $installer::globals::prepare_winpatch )
+ {
+ # ProductCode has to be specified in each language
+ my $searchstring = "PRODUCTCODE";
+ my $codeblock = installer::windows::idtglobal::get_language_block_from_language_file($searchstring, $codefile);
+ $installer::globals::productcode = installer::windows::idtglobal::get_code_from_code_block($codeblock, $onelanguage);
+ } else {
+ my $guidref = get_guid_list(1, 1); # only one GUID shall be generated
+ ${$guidref}[0] =~ s/\s*$//; # removing ending spaces
+ $installer::globals::productcode = "\{" . ${$guidref}[0] . "\}";
+ }
+
+ if ( $installer::globals::patch ) # patch upgrade codes are defined in soffice.lst
+ {
+ if ( $allvariableshashref->{'PATCHUPGRADECODE'} ) { $installer::globals::upgradecode = $allvariableshashref->{'PATCHUPGRADECODE'}; }
+ else { installer::exiter::exit_program("ERROR: PATCHUPGRADECODE not defined in list file!", "set_global_code_variables"); }
+ }
+ else
+ {
+ # UpgradeCode can take english as default, if not defined in specified language
+
+ $searchstring = "UPGRADECODE"; # searching in the codes.txt file
+ $codeblock = installer::windows::idtglobal::get_language_block_from_language_file($searchstring, $codefile);
+ $installer::globals::upgradecode = installer::windows::idtglobal::get_language_string_from_language_block($codeblock, $onelanguage, "");
+ }
+
+ # if (( $installer::globals::productcode eq "" ) && ( ! $isopensource )) { installer::exiter::exit_program("ERROR: ProductCode for language $onelanguage not defined in $installer::globals::codefilename !", "set_global_code_variables"); }
+ if ( $installer::globals::upgradecode eq "" ) { installer::exiter::exit_program("ERROR: UpgradeCode not defined in $installer::globals::codefilename !", "set_global_code_variables"); }
+
+ $infoline = "Setting ProductCode to: $installer::globals::productcode \n";
+ push( @installer::globals::logfileinfo, $infoline);
+ $infoline = "Setting UpgradeCode to: $installer::globals::upgradecode \n";
+ push( @installer::globals::logfileinfo, $infoline);
+
+ # Adding both variables into the variables array
+
+ $allvariableshashref->{'PRODUCTCODE'} = $installer::globals::productcode;
+ $allvariableshashref->{'UPGRADECODE'} = $installer::globals::upgradecode;
+
+ $infoline = "Defined variable PRODUCTCODE: $installer::globals::productcode \n";
+ push( @installer::globals::logfileinfo, $infoline);
+
+ $infoline = "Defined variable UPGRADECODE: $installer::globals::upgradecode \n";
+ push( @installer::globals::logfileinfo, $infoline);
+
+}
+
+###############################################################
+# Setting the product version used in property table and
+# upgrade table. Saving in global variable $msiproductversion
+###############################################################
+
+sub set_msiproductversion
+{
+ my ( $allvariables ) = @_;
+
+ my $productversion = $allvariables->{'PRODUCTVERSION'};
+
+ if (( $productversion =~ /^\s*\d+\s*$/ ) && ( $productversion > 255 )) { $productversion = $productversion%256; }
+
+ if ( $productversion =~ /^\s*(\d+)\.(\d+)\.(\d+)\s*$/ )
+ {
+ $productversion = $1 . "\." . $2 . $3 . "\." . $installer::globals::buildid;
+ }
+ elsif ( $productversion =~ /^\s*(\d+)\.(\d+)\s*$/ )
+ {
+ $productversion = $1 . "\." . $2 . "\." . $installer::globals::buildid;
+ }
+ else
+ {
+ my $productminor = "00";
+ if (( $allvariables->{'PACKAGEVERSION'} ) && ( $allvariables->{'PACKAGEVERSION'} ne "" ))
+ {
+ if ( $allvariables->{'PACKAGEVERSION'} =~ /^\s*(\d+)\.(\d+)\.(\d+)\s*$/ ) { $productminor = $2; }
+ }
+
+ $productversion = $productversion . "\." . $productminor . "\." . $installer::globals::buildid;
+ }
+
+ $installer::globals::msiproductversion = $productversion;
+
+ # Setting $installer::globals::msimajorproductversion, to differ between old version in upgrade table
+
+ if ( $installer::globals::msiproductversion =~ /^\s*(\d+)\./ )
+ {
+ my $major = $1;
+ $installer::globals::msimajorproductversion = $major . "\.0\.0";
+ }
+}
+
+#################################################################################
+# Including the msi product version into the bootstrap.ini, Windows only
+#################################################################################
+
+sub put_msiproductversion_into_bootstrapfile
+{
+ my ($filesref) = @_;
+
+ for ( my $i = 0; $i <= $#{$filesref}; $i++ )
+ {
+ my $onefile = ${$filesref}[$i];
+
+ if ( $onefile->{'gid'} eq "gid_Profile_Version_Ini" )
+ {
+ my $file = installer::files::read_file($onefile->{'sourcepath'});
+
+ for ( my $j = 0; $j <= $#{$file}; $j++ )
+ {
+ ${$file}[$j] =~ s/\<msiproductversion\>/$installer::globals::msiproductversion/;
+ }
+
+ installer::files::save_file($onefile->{'sourcepath'}, $file);
+
+ last;
+ }
+ }
+}
+
+####################################################################################
+# Updating the file Property.idt dynamically
+# Content:
+# Property Value
+####################################################################################
+
+sub update_reglocat_table
+{
+ my ($basedir, $allvariables) = @_;
+
+ my $reglocatfilename = $basedir . $installer::globals::separator . "RegLocat.idt";
+
+ # Only do something, if this file exists
+
+ if ( -f $reglocatfilename )
+ {
+ my $reglocatfile = installer::files::read_file($reglocatfilename);
+
+ my $layername = "";
+ if ( $allvariables->{'REGISTRYLAYERNAME'} )
+ {
+ $layername = $allvariables->{'REGISTRYLAYERNAME'};
+ }
+ else
+ {
+ for ( my $i = 0; $i <= $#{$reglocatfile}; $i++ )
+ {
+ if ( ${$reglocatfile}[$i] =~ /\bLAYERNAMETEMPLATE\b/ )
+ {
+ installer::exiter::exit_program("ERROR: Variable \"REGISTRYLAYERNAME\" has to be defined", "update_reglocat_table");
+ }
+ }
+ }
+
+ if ( $layername ne "" )
+ {
+ # Updating the layername in
+
+ for ( my $i = 0; $i <= $#{$reglocatfile}; $i++ )
+ {
+ ${$reglocatfile}[$i] =~ s/\bLAYERNAMETEMPLATE\b/$layername/;
+ }
+
+ # Saving the file
+ installer::files::save_file($reglocatfilename ,$reglocatfile);
+ my $infoline = "Updated idt file: $reglocatfilename\n";
+ push(@installer::globals::logfileinfo, $infoline);
+ }
+ }
+}
+
+
+
+####################################################################################
+# Updating the file RemoveRe.idt dynamically (RemoveRegistry.idt)
+# The name of the component has to be replaced.
+####################################################################################
+
+sub update_removere_table
+{
+ my ($basedir) = @_;
+
+ my $removeregistryfilename = $basedir . $installer::globals::separator . "RemoveRe.idt";
+
+ # Only do something, if this file exists
+
+ if ( -f $removeregistryfilename )
+ {
+ my $removeregistryfile = installer::files::read_file($removeregistryfilename);
+
+ for ( my $i = 0; $i <= $#{$removeregistryfile}; $i++ )
+ {
+ for ( my $i = 0; $i <= $#{$removeregistryfile}; $i++ )
+ {
+ ${$removeregistryfile}[$i] =~ s/\bREGISTRYROOTCOMPONENT\b/$installer::globals::registryrootcomponent/;
+ }
+ }
+
+ # Saving the file
+ installer::files::save_file($removeregistryfilename ,$removeregistryfile);
+ my $infoline = "Updated idt file: $removeregistryfilename \n";
+ push(@installer::globals::logfileinfo, $infoline);
+ }
+}
+
+##########################################################################
+# Reading saved mappings in Files.idt and Director.idt.
+# This is required, if installation sets shall be created,
+# that can be used for creation of msp files.
+##########################################################################
+
+sub read_saved_mappings
+{
+ installer::logger::include_header_into_logfile("Reading saved mappings from older installation sets:");
+
+ installer::logger::include_timestamp_into_logfile("Performance Info: Reading saved mappings start");
+
+ if ( $installer::globals::previous_idt_dir )
+ {
+ my @errorlines = ();
+ my $errorstring = "";
+ my $error_occured = 0;
+ my $file_error_occured = 0;
+ my $dir_error = 0;
+
+ my $idtdir = $installer::globals::previous_idt_dir;
+ $idtdir =~ s/\Q$installer::globals::separator\E\s*$//;
+
+ # Reading File.idt
+
+ my $idtfile = $idtdir . $installer::globals::separator . "File.idt";
+ push( @installer::globals::globallogfileinfo, "\nAnalyzing file: $idtfile\n" );
+ if ( ! -f $idtfile ) { push( @installer::globals::globallogfileinfo, "Warning: File $idtfile does not exist!\n" ); }
+
+ my $n = 0;
+ open (F, "<$idtfile") || installer::exiter::exit_program("ERROR: Cannot open file $idtfile for reading", "read_saved_mappings");
+ <F>; <F>; <F>;
+ while (<F>)
+ {
+ m/^([^\t]+)\t([^\t]+)\t((.*)\|)?([^\t]*)/;
+ print "OUT1: \$1: $1, \$2: $2, \$3: $3, \$4: $4, \$5: $5\n";
+ next if ("$1" eq "$5") && (!defined($3));
+ my $lc1 = lc($1);
+
+ if ( exists($installer::globals::savedmapping{"$2/$5"}))
+ {
+ if ( ! $file_error_occured )
+ {
+ $errorstring = "\nErrors in $idtfile: \n";
+ push(@errorlines, $errorstring);
+ }
+ $errorstring = "Duplicate savedmapping{" . "$2/$5}\n";
+ push(@errorlines, $errorstring);
+ $error_occured = 1;
+ $file_error_occured = 1;
+ }
+
+ if ( exists($installer::globals::savedrevmapping{$lc1}))
+ {
+ if ( ! $file_error_occured )
+ {
+ $errorstring = "\nErrors in $idtfile: \n";
+ push(@errorlines, $errorstring);
+ }
+ $errorstring = "Duplicate savedrevmapping{" . "$lc1}\n";
+ push(@errorlines, $errorstring);
+ $error_occured = 1;
+ $file_error_occured = 1;
+ }
+
+ my $shortname = $4 || '';
+
+ # Don't reuse illegal 8.3 mappings that we used to generate in 2.0.4
+ if (index($shortname, '.') > 8 ||
+ (index($shortname, '.') == -1 && length($shortname) > 8))
+ {
+ $shortname = '';
+ }
+
+ if (( $shortname ne '' ) && ( index($shortname, '~') > 0 ) && ( exists($installer::globals::savedrev83mapping{$shortname}) ))
+ {
+ if ( ! $file_error_occured )
+ {
+ $errorstring = "\nErrors in $idtfile: \n";
+ push(@errorlines, $errorstring);
+ }
+ $errorstring = "Duplicate savedrev83mapping{" . "$shortname}\n";
+ push(@errorlines, $errorstring);
+ $error_occured = 1;
+ $file_error_occured = 1;
+ }
+
+ $installer::globals::savedmapping{"$2/$5"} = "$1;$shortname";
+ $installer::globals::savedrevmapping{lc($1)} = "$2/$5";
+ $installer::globals::savedrev83mapping{$shortname} = "$2/$5" if $shortname ne '';
+ $n++;
+ }
+
+ close (F);
+
+ push( @installer::globals::globallogfileinfo, "Read $n old file table key or 8.3 name mappings from $idtfile\n" );
+
+ # Reading Director.idt
+
+ $idtfile = $idtdir . $installer::globals::separator . "Director.idt";
+ push( @installer::globals::globallogfileinfo, "\nAnalyzing file $idtfile\n" );
+ if ( ! -f $idtfile ) { push( @installer::globals::globallogfileinfo, "Warning: File $idtfile does not exist!\n" ); }
+
+ $n = 0;
+ open (F, "<$idtfile") || installer::exiter::exit_program("ERROR: Cannot open file $idtfile for reading", "read_saved_mappings");
+ <F>; <F>; <F>;
+ while (<F>)
+ {
+ m/^([^\t]+)\t([^\t]+)\t(([^~]+~\d.*)\|)?([^\t]*)/;
+ next if (!defined($3));
+ my $lc1 = lc($1);
+
+ print "OUT2: \$1: $1, \$2: $2, \$3: $3\n";
+
+ if ( exists($installer::globals::saved83dirmapping{$1}) )
+ {
+ if ( ! $dir_error_occured )
+ {
+ $errorstring = "\nErrors in $idtfile: \n";
+ push(@errorlines, $errorstring);
+ }
+ $errorstring = "Duplicate saved83dirmapping{" . "$1}\n";
+ push(@errorlines, $errorstring);
+ $error_occured = 1;
+ $dir_error_occured = 1;
+ }
+
+ $installer::globals::saved83dirmapping{$1} = $4;
+ $n++;
+ }
+ close (F);
+
+ push( @installer::globals::globallogfileinfo, "Read $n old directory 8.3 name mappings from $idtfile\n" );
+
+ # Analyzing errors
+
+ if ( $error_occured )
+ {
+ for ( my $i = 0; $i <= $#errorlines; $i++ )
+ {
+ print "$errorlines[$i]";
+ push( @installer::globals::globallogfileinfo, "$errorlines[$i]");
+ }
+ installer::exiter::exit_program("ERROR: Duplicate entries in saved mappings!", "read_saved_mappings");
+ }
+ } else {
+ # push( @installer::globals::globallogfileinfo, "WARNING: Windows patch shall be prepared, but PREVIOUS_IDT_DIR is not set!\n" );
+ installer::exiter::exit_program("ERROR: Windows patch shall be prepared, but environment variable PREVIOUS_IDT_DIR is not set!", "read_saved_mappings");
+ }
+
+ installer::logger::include_timestamp_into_logfile("Performance Info: Reading saved mappings end");
+}
+
+1;
+
diff --git a/solenv/bin/modules/installer/windows/msp.pm b/solenv/bin/modules/installer/windows/msp.pm
new file mode 100644
index 000000000000..a4996945de4d
--- /dev/null
+++ b/solenv/bin/modules/installer/windows/msp.pm
@@ -0,0 +1,1293 @@
+#*************************************************************************
+#
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+#
+# Copyright 2000, 2010 Oracle and/or its affiliates.
+#
+# OpenOffice.org - a multi-platform office productivity suite
+#
+# This file is part of OpenOffice.org.
+#
+# OpenOffice.org is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License version 3
+# only, as published by the Free Software Foundation.
+#
+# OpenOffice.org is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License version 3 for more details
+# (a copy is included in the LICENSE file that accompanied this code).
+#
+# You should have received a copy of the GNU Lesser General Public License
+# version 3 along with OpenOffice.org. If not, see
+# <http://www.openoffice.org/license.html>
+# for a copy of the LGPLv3 License.
+#
+#*************************************************************************
+
+package installer::windows::msp;
+
+use installer::control;
+use installer::converter;
+use installer::exiter;
+use installer::files;
+use installer::globals;
+use installer::logger;
+use installer::pathanalyzer;
+use installer::systemactions;
+use installer::windows::admin;
+use installer::windows::idtglobal;
+use installer::windows::update;
+
+#################################################################################
+# Making all required administrative installations
+#################################################################################
+
+sub install_installation_sets
+{
+ my ($installationdir) = @_;
+
+ # Finding the msi database in the new installation set, that is located in $installationdir
+
+ my $msifiles = installer::systemactions::find_file_with_file_extension("msi", $installationdir);
+
+ if ( $#{$msifiles} < 0 ) { installer::exiter::exit_program("ERROR: Did not find msi database in directory $installationdir", "create_msp_patch"); }
+ if ( $#{$msifiles} > 0 ) { installer::exiter::exit_program("ERROR: Did find more than one msi database in directory $installationdir", "create_msp_patch"); }
+
+ my $newinstallsetdatabasepath = $installationdir . $installer::globals::separator . ${$msifiles}[0];
+ my $oldinstallsetdatabasepath = $installer::globals::updatedatabasepath;
+
+ # Creating temp directory again
+ installer::systemactions::create_directory_structure($installer::globals::temppath);
+
+ # Creating old installation directory
+ my $dirname = "admin";
+ my $installpath = $installer::globals::temppath . $installer::globals::separator . $dirname;
+ if ( ! -d $installpath) { installer::systemactions::create_directory($installpath); }
+
+ my $oldinstallpath = $installpath . $installer::globals::separator . "old";
+ my $newinstallpath = $installpath . $installer::globals::separator . "new";
+
+ if ( ! -d $oldinstallpath) { installer::systemactions::create_directory($oldinstallpath); }
+ if ( ! -d $newinstallpath) { installer::systemactions::create_directory($newinstallpath); }
+
+ my $olddatabase = installer::windows::admin::make_admin_install($oldinstallsetdatabasepath, $oldinstallpath);
+ my $newdatabase = installer::windows::admin::make_admin_install($newinstallsetdatabasepath, $newinstallpath);
+
+ if ( $^O =~ /cygwin/i ) {
+ $olddatabase = qx{cygpath -w "$olddatabase"};
+ $olddatabase =~ s/\s*$//g;
+ $newdatabase = qx{cygpath -w "$newdatabase"};
+ $newdatabase =~ s/\s*$//g;
+ }
+
+ return ($olddatabase, $newdatabase);
+}
+
+#################################################################################
+# Extracting all tables from a pcp file
+#################################################################################
+
+sub extract_all_tables_from_pcpfile
+{
+ my ($fullpcpfilepath, $workdir) = @_;
+
+ my $msidb = "msidb.exe"; # Has to be in the path
+ my $infoline = "";
+ my $systemcall = "";
+ my $returnvalue = "";
+ my $extraslash = ""; # Has to be set for non-ActiveState perl
+
+ my $localfullpcpfile = $fullpcpfilepath;
+ my $localworkdir = $workdir;
+
+ if ( $^O =~ /cygwin/i ) {
+ # msidb.exe really wants backslashes. (And double escaping because system() expands the string.)
+ $localfullpcpfile =~ s/\//\\\\/g;
+ $localworkdir =~ s/\//\\\\/g;
+ $extraslash = "\\";
+ }
+
+ # Export of all tables by using "*"
+
+ $systemcall = $msidb . " -d " . $localfullpcpfile . " -f " . $localworkdir . " -e " . $extraslash . "*";
+ $returnvalue = system($systemcall);
+
+ $infoline = "Systemcall: $systemcall\n";
+ push( @installer::globals::logfileinfo, $infoline);
+
+ if ($returnvalue)
+ {
+ $infoline = "ERROR: Could not execute $systemcall !\n";
+ push( @installer::globals::logfileinfo, $infoline);
+ installer::exiter::exit_program("ERROR: Could not exclude tables from pcp file: $fullpcpfilepath !", "extract_all_tables_from_msidatabase");
+ }
+ else
+ {
+ $infoline = "Success: Executed $systemcall successfully!\n";
+ push( @installer::globals::logfileinfo, $infoline);
+ }
+}
+
+#################################################################################
+# Include tables into a pcp file
+#################################################################################
+
+sub include_tables_into_pcpfile
+{
+ my ($fullpcpfilepath, $workdir, $tables) = @_;
+
+ my $msidb = "msidb.exe"; # Has to be in the path
+ my $infoline = "";
+ my $systemcall = "";
+ my $returnvalue = "";
+
+ # Make all table 8+3 conform
+ my $alltables = installer::converter::convert_stringlist_into_array(\$tables, " ");
+
+ for ( my $i = 0; $i <= $#{$alltables}; $i++ )
+ {
+ my $tablename = ${$alltables}[$i];
+ $tablename =~ s/\s*$//;
+ my $namelength = length($tablename);
+ if ( $namelength > 8 )
+ {
+ my $newtablename = substr($tablename, 0, 8); # name, offset, length
+ my $oldfile = $workdir . $installer::globals::separator . $tablename . ".idt";
+ my $newfile = $workdir . $installer::globals::separator . $newtablename . ".idt";
+ if ( -f $newfile ) { unlink $newfile; }
+ installer::systemactions::copy_one_file($oldfile, $newfile);
+ }
+ }
+
+ # Import of tables
+
+ my $localworkdir = $workdir;
+ my $localfullpcpfilepath = $fullpcpfilepath;
+
+ if ( $^O =~ /cygwin/i ) {
+ # msidb.exe really wants backslashes. (And double escaping because system() expands the string.)
+ $localfullpcpfilepath =~ s/\//\\\\/g;
+ $localworkdir =~ s/\//\\\\/g;
+ }
+
+ $systemcall = $msidb . " -d " . $localfullpcpfilepath . " -f " . $localworkdir . " -i " . $tables;
+
+ $returnvalue = system($systemcall);
+
+ $infoline = "Systemcall: $systemcall\n";
+ push( @installer::globals::logfileinfo, $infoline);
+
+ if ($returnvalue)
+ {
+ $infoline = "ERROR: Could not execute $systemcall !\n";
+ push( @installer::globals::logfileinfo, $infoline);
+ installer::exiter::exit_program("ERROR: Could not include tables into pcp file: $fullpcpfilepath !", "include_tables_into_pcpfile");
+ }
+ else
+ {
+ $infoline = "Success: Executed $systemcall successfully!\n";
+ push( @installer::globals::logfileinfo, $infoline);
+ }
+}
+
+#################################################################################
+# Calling msimsp.exe
+#################################################################################
+
+sub execute_msimsp
+{
+ my ($fullpcpfilename, $mspfilename, $localmspdir) = @_;
+
+ my $msimsp = "msimsp.exe"; # Has to be in the path
+ my $infoline = "";
+ my $systemcall = "";
+ my $returnvalue = "";
+ my $logfilename = $localmspdir . $installer::globals::separator . "msimsp.log";
+
+ # Using a specific temp for each msimsp.exe process
+ # Creating temp directory again (should already have happened)
+ installer::systemactions::create_directory_structure($installer::globals::temppath);
+
+ # Creating old installation directory
+ my $dirname = "msimsptemp";
+ my $msimsptemppath = $installer::globals::temppath . $installer::globals::separator . $dirname;
+ if ( ! -d $msimsptemppath) { installer::systemactions::create_directory($msimsptemppath); }
+
+ # r:\msvc9p\PlatformSDK\v6.1\bin\msimsp.exe -s c:\patch\hotfix_qfe1.pcp -p c:\patch\patch_ooo3_m2_m3.msp -l c:\patch\patch_ooo3_m2_m3.log
+
+ if ( -f $logfilename ) { unlink $logfilename; }
+
+ my $localfullpcpfilename = $fullpcpfilename;
+ my $localmspfilename = $mspfilename;
+ my $locallogfilename = $logfilename;
+ my $localmsimsptemppath = $msimsptemppath;
+
+ if ( $^O =~ /cygwin/i ) {
+ # msimsp.exe really wants backslashes. (And double escaping because system() expands the string.)
+ $localfullpcpfilename =~ s/\//\\\\/g;
+ $locallogfilename =~ s/\//\\\\/g;
+
+ $localmspfilename =~ s/\\/\\\\/g; # path already contains backslash
+ # $localmspfilename =~ s/\//\\\\/g;
+
+ $localmsimsptemppath = qx{cygpath -w "$localmsimsptemppath"};
+ $localmsimsptemppath =~ s/\\/\\\\/g;
+ $localmsimsptemppath =~ s/\s*$//g;
+ }
+
+ $systemcall = $msimsp . " -s " . $localfullpcpfilename . " -p " . $localmspfilename . " -l " . $locallogfilename . " -f " . $localmsimsptemppath;
+ installer::logger::print_message( "... $systemcall ...\n" );
+
+ $returnvalue = system($systemcall);
+
+ $infoline = "Systemcall: $systemcall\n";
+ push( @installer::globals::logfileinfo, $infoline);
+
+ if ($returnvalue)
+ {
+ $infoline = "ERROR: Could not execute $systemcall !\n";
+ push( @installer::globals::logfileinfo, $infoline);
+ installer::exiter::exit_program("ERROR: Could not execute $systemcall !", "execute_msimsp");
+ }
+ else
+ {
+ $infoline = "Success: Executed $systemcall successfully!\n";
+ push( @installer::globals::logfileinfo, $infoline);
+ }
+
+ return $logfilename;
+}
+
+####################################################################
+# Checking existence and saving all tables, that need to be edited
+####################################################################
+
+sub check_and_save_tables
+{
+ my ($tablelist, $workdir) = @_;
+
+ my $tables = installer::converter::convert_stringlist_into_array(\$tablelist, " ");
+
+ for ( my $i = 0; $i <= $#{$tables}; $i++ )
+ {
+ my $filename = ${$tables}[$i];
+ $filename =~ s/\s*$//;
+ my $fullfilename = $workdir . $installer::globals::separator . $filename . ".idt";
+
+ if ( ! -f $fullfilename ) { installer::exiter::exit_program("ERROR: Required idt file could not be found: \"$fullfilename\"!", "check_and_save_tables"); }
+
+ my $savfilename = $fullfilename . ".sav";
+ installer::systemactions::copy_one_file($fullfilename, $savfilename);
+ }
+}
+
+####################################################################
+# Setting the languages for the service packs
+####################################################################
+
+sub create_langstring
+{
+ my ( $languagesarrayref ) = @_;
+
+ my $langstring = "";
+ for ( my $i = 0; $i <= $#{$languagesarrayref}; $i++ ) { $langstring = $langstring . "_" . ${$languagesarrayref}[$i]; }
+
+ return $langstring;
+}
+
+####################################################################
+# Setting the name of the msp database
+####################################################################
+
+sub set_mspfilename
+{
+ my ($allvariables, $mspdir, $languagesarrayref) = @_;
+
+ my $databasename = $allvariables->{'PRODUCTNAME'};
+ $databasename = lc($databasename);
+ $databasename =~ s/\.//g;
+ $databasename =~ s/\-//g;
+ $databasename =~ s/\s//g;
+
+ if ( $allvariables->{'MSPPRODUCTVERSION'} ) { $databasename = $databasename . $allvariables->{'MSPPRODUCTVERSION'}; }
+
+ # possibility to overwrite the name with variable DATABASENAME
+ # if ( $allvariables->{'DATABASENAME'} ) { $databasename = $allvariables->{'DATABASENAME'}; }
+
+ # Adding patch info to database name
+ # if ( $installer::globals::buildid ) { $databasename = $databasename . "_" . $installer::globals::buildid; }
+
+ # if ( $allvariables->{'VENDORPATCHVERSION'} ) { $databasename = $databasename . "_" . $allvariables->{'VENDORPATCHVERSION'}; }
+
+
+ if (( $allvariables->{'SERVICEPACK'} ) && ( $allvariables->{'SERVICEPACK'} == 1 ))
+ {
+ my $windowspatchlevel = 0;
+ if ( $allvariables->{'MSPPATCHLEVEL'} ) { $windowspatchlevel = $allvariables->{'MSPPATCHLEVEL'}; }
+ $databasename = $databasename . "_servicepack_" . $windowspatchlevel;
+ my $languagestring = create_langstring($languagesarrayref);
+ $databasename = $databasename . $languagestring;
+ }
+ else
+ {
+ my $hotfixaddon = "hotfix_";
+ $hotfixaddon = $hotfixaddon . $installer::globals::buildid;
+ my $cwsname = "";
+ if ( $ENV{'CWS_WORK_STAMP'} ) { $hotfixaddon = $ENV{'CWS_WORK_STAMP'}; }
+ if ( $allvariables->{'OVERWRITE_CWSNAME'} ) { $hotfixaddon = $allvariables->{'OVERWRITE_CWSNAME'}; }
+ $databasename = $databasename . "_" . $hotfixaddon;
+ }
+
+ $databasename = $databasename . ".msp";
+
+ my $fullmspname = $mspdir . $installer::globals::separator . $databasename;
+
+ if ( $^O =~ /cygwin/i ) { $fullmspname =~ s/\//\\/g; }
+
+ return $fullmspname;
+}
+
+####################################################################
+# Editing table Properties
+####################################################################
+
+sub change_properties_table
+{
+ my ($localmspdir, $mspfilename) = @_;
+
+ my $infoline = "Changing content of table \"Properties\"\n";
+ push( @installer::globals::logfileinfo, $infoline);
+
+ my $filename = $localmspdir . $installer::globals::separator . "Properties.idt";
+ if ( ! -f $filename ) { installer::exiter::exit_program("ERROR: Could not find file \"$filename\" !", "change_properties_table"); }
+
+ my $filecontent = installer::files::read_file($filename);
+
+
+ my $guidref = installer::windows::msiglobal::get_guid_list(1, 1);
+ ${$guidref}[0] =~ s/\s*$//; # removing ending spaces
+ my $patchcode = "\{" . ${$guidref}[0] . "\}";
+
+ # Setting "PatchOutputPath"
+ my $found_patchoutputpath = 0;
+ my $found_patchguid = 0;
+
+ for ( my $i = 0; $i <= $#{$filecontent}; $i++ )
+ {
+ if ( ${$filecontent}[$i] =~ /^\s*PatchOutputPath\t(.*?)\s*$/ )
+ {
+ my $oldvalue = $1;
+ ${$filecontent}[$i] =~ s/\Q$oldvalue\E/$mspfilename/;
+ $found_patchoutputpath = 1;
+ }
+
+ if ( ${$filecontent}[$i] =~ /^\s*PatchGUID\t(.*?)\s*$/ )
+ {
+ my $oldvalue = $1;
+ ${$filecontent}[$i] =~ s/\Q$oldvalue\E/$patchcode/;
+ $found_patchguid = 1;
+ }
+ }
+
+ if ( ! $found_patchoutputpath )
+ {
+ my $newline = "PatchOutputPath\t$mspfilename\n";
+ push(@{$filecontent}, $newline);
+ }
+
+ if ( ! $found_patchguid )
+ {
+ my $newline = "PatchGUID\t$patchcode\n";
+ push(@{$filecontent}, $newline);
+ }
+
+ # saving file
+ installer::files::save_file($filename, $filecontent);
+}
+
+####################################################################
+# Editing table TargetImages
+####################################################################
+
+sub change_targetimages_table
+{
+ my ($localmspdir, $olddatabase) = @_;
+
+ my $infoline = "Changing content of table \"TargetImages\"\n";
+ push( @installer::globals::logfileinfo, $infoline);
+
+ my $filename = $localmspdir . $installer::globals::separator . "TargetImages.idt";
+ if ( ! -f $filename ) { installer::exiter::exit_program("ERROR: Could not find file \"$filename\" !", "change_targetimages_table"); }
+
+ my $filecontent = installer::files::read_file($filename);
+ my @newcontent = ();
+
+ # Copying the header
+ for ( my $i = 0; $i <= $#{$filecontent}; $i++ ) { if ( $i < 3 ) { push(@newcontent, ${$filecontent}[$i]); } }
+
+ #Adding all targets
+ my $newline = "T1\t$olddatabase\t\tU1\t1\t0x00000922\t1\n";
+ push(@newcontent, $newline);
+
+ # saving file
+ installer::files::save_file($filename, \@newcontent);
+}
+
+####################################################################
+# Editing table UpgradedImages
+####################################################################
+
+sub change_upgradedimages_table
+{
+ my ($localmspdir, $newdatabase) = @_;
+
+ my $infoline = "Changing content of table \"UpgradedImages\"\n";
+ push( @installer::globals::logfileinfo, $infoline);
+
+ my $filename = $localmspdir . $installer::globals::separator . "UpgradedImages.idt";
+ if ( ! -f $filename ) { installer::exiter::exit_program("ERROR: Could not find file \"$filename\" !", "change_upgradedimages_table"); }
+
+ my $filecontent = installer::files::read_file($filename);
+ my @newcontent = ();
+
+ # Copying the header
+ for ( my $i = 0; $i <= $#{$filecontent}; $i++ ) { if ( $i < 3 ) { push(@newcontent, ${$filecontent}[$i]); } }
+
+ # Syntax: Upgraded MsiPath PatchMsiPath SymbolPaths Family
+
+ # default values
+ my $upgraded = "U1";
+ my $msipath = $newdatabase;
+ my $patchmsipath = "";
+ my $symbolpaths = "";
+ my $family = "22334455";
+
+ if ( $#{$filecontent} >= 3 )
+ {
+ my $line = ${$filecontent}[3];
+ if ( $line =~ /^\s*(.*?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\s*$/ )
+ {
+ $upgraded = $1;
+ $patchmsipath = $3;
+ $symbolpaths = $4;
+ $family = $5;
+ }
+ }
+
+ #Adding sequence line, saving PatchFamily
+ my $newline = "$upgraded\t$msipath\t$patchmsipath\t$symbolpaths\t$family\n";
+ push(@newcontent, $newline);
+
+ # saving file
+ installer::files::save_file($filename, \@newcontent);
+}
+
+####################################################################
+# Editing table ImageFamilies
+####################################################################
+
+sub change_imagefamilies_table
+{
+ my ($localmspdir) = @_;
+
+ my $infoline = "Changing content of table \"ImageFamilies\"\n";
+ push( @installer::globals::logfileinfo, $infoline);
+
+ my $filename = $localmspdir . $installer::globals::separator . "ImageFamilies.idt";
+ if ( ! -f $filename ) { installer::exiter::exit_program("ERROR: Could not find file \"$filename\" !", "change_imagefamilies_table"); }
+
+ my $filecontent = installer::files::read_file($filename);
+ my @newcontent = ();
+
+ # Copying the header
+ for ( my $i = 0; $i <= $#{$filecontent}; $i++ ) { if ( $i < 3 ) { push(@newcontent, ${$filecontent}[$i]); } }
+
+ # Syntax: Family MediaSrcPropName MediaDiskId FileSequenceStart DiskPrompt VolumeLabel
+ # "FileSequenceStart has to be set
+
+ # Default values:
+
+ my $family = "22334455";
+ my $mediasrcpropname = "MediaSrcPropName";
+ my $mediadiskid = "2";
+ my $filesequencestart = get_filesequencestart();
+ my $diskprompt = "";
+ my $volumelabel = "";
+
+ if ( $#{$filecontent} >= 3 )
+ {
+ my $line = ${$filecontent}[3];
+ if ( $line =~ /^\s*(.*?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\s*$/ )
+ {
+ $family = $1;
+ $mediasrcpropname = $2;
+ $mediadiskid = $3;
+ $diskprompt = $5;
+ $volumelabel = $6;
+ }
+ }
+
+ #Adding sequence line
+ my $newline = "$family\t$mediasrcpropname\t$mediadiskid\t$filesequencestart\t$diskprompt\t$volumelabel\n";
+ push(@newcontent, $newline);
+
+ # saving file
+ installer::files::save_file($filename, \@newcontent);
+}
+
+####################################################################
+# Setting start sequence for patch
+####################################################################
+
+sub get_filesequencestart
+{
+ my $sequence = 1000; # default
+
+ if ( $installer::globals::updatelastsequence ) { $sequence = $installer::globals::updatelastsequence + 500; }
+
+ return $sequence;
+}
+
+####################################################################
+# Setting time value into pcp file
+# Format mm/dd/yyyy hh:mm
+####################################################################
+
+sub get_patchtime_value
+{
+ # Syntax: 8/8/2008 11:55
+ my $minute = (localtime())[1];
+ my $hour = (localtime())[2];
+ my $day = (localtime())[3];
+ my $month = (localtime())[4];
+ my $year = 1900 + (localtime())[5];
+
+ $month++; # zero based month
+ if ( $minute < 10 ) { $minute = "0" . $minute; }
+ if ( $hour < 10 ) { $hour = "0" . $hour; }
+
+ my $timestring = $month . "/" . $day . "/" . $year . " " . $hour . ":" . $minute;
+
+ return $timestring;
+}
+
+#################################################################################
+# Checking, if this is the correct database.
+#################################################################################
+
+sub correct_langs
+{
+ my ($langs, $languagestringref) = @_;
+
+ my $correct_langs = 0;
+
+ # Comparing $langs with $languagestringref
+
+ my $langlisthash = installer::converter::convert_stringlist_into_hash(\$langs, ",");
+ my $langstringhash = installer::converter::convert_stringlist_into_hash($languagestringref, "_");
+
+ my $not_included = 0;
+ foreach my $onelang ( keys %{$langlisthash} )
+ {
+ if ( ! exists($langstringhash->{$onelang}) )
+ {
+ $not_included = 1;
+ last;
+ }
+ }
+
+ if ( ! $not_included )
+ {
+ foreach my $onelanguage ( keys %{$langstringhash} )
+ {
+ if ( ! exists($langlisthash->{$onelanguage}) )
+ {
+ $not_included = 1;
+ last;
+ }
+ }
+
+ if ( ! $not_included ) { $correct_langs = 1; }
+ }
+
+ return $correct_langs;
+}
+
+#################################################################################
+# Searching for the path to the reference database for this special product.
+#################################################################################
+
+sub get_patchid_from_list
+{
+ my ($filecontent, $languagestringref, $filename) = @_;
+
+ my $patchid = "";
+
+ for ( my $i = 0; $i <= $#{$filecontent}; $i++ )
+ {
+ my $line = ${$filecontent}[$i];
+ if ( $line =~ /^\s*$/ ) { next; } # empty line
+ if ( $line =~ /^\s*\#/ ) { next; } # comment line
+
+ if ( $line =~ /^\s*(.+?)\s*=\s*(.+?)\s*$/ )
+ {
+ my $langs = $1;
+ my $localpatchid = $2;
+
+ if ( correct_langs($langs, $languagestringref) )
+ {
+ $patchid = $localpatchid;
+ last;
+ }
+ }
+ else
+ {
+ installer::exiter::exit_program("ERROR: Wrong syntax in file: $filename! Line: \"$line\"", "get_patchid_from_list");
+ }
+ }
+
+ return $patchid;
+}
+
+####################################################################
+# Editing table PatchMetadata
+####################################################################
+
+sub change_patchmetadata_table
+{
+ my ($localmspdir, $allvariables, $languagestringref) = @_;
+
+ my $infoline = "Changing content of table \"PatchMetadata\"\n";
+ push( @installer::globals::logfileinfo, $infoline);
+
+ my $filename = $localmspdir . $installer::globals::separator . "PatchMetadata.idt";
+ if ( ! -f $filename ) { installer::exiter::exit_program("ERROR: Could not find file \"$filename\" !", "change_patchmetadata_table"); }
+
+ my $filecontent = installer::files::read_file($filename);
+ my @newcontent = ();
+
+ # Syntax: Company Property Value
+ # Interesting properties: "Classification" and "CreationTimeUTC"
+
+ my $classification_set = 0;
+ my $creationtime_set = 0;
+ my $targetproductname_set = 0;
+ my $manufacturer_set = 0;
+ my $displayname_set = 0;
+ my $description_set = 0;
+ my $allowremoval_set = 0;
+
+ my $defaultcompany = "";
+
+ my $classificationstring = "Classification";
+ my $classificationvalue = "Hotfix";
+ if (( $allvariables->{'SERVICEPACK'} ) && ( $allvariables->{'SERVICEPACK'} == 1 )) { $classificationvalue = "ServicePack"; }
+
+ my $allowremovalstring = "AllowRemoval";
+ my $allowremovalvalue = "1";
+ if (( exists($allvariables->{'MSPALLOWREMOVAL'}) ) && ( $allvariables->{'MSPALLOWREMOVAL'} == 0 )) { $allowremovalvalue = 0; }
+
+ my $timestring = "CreationTimeUTC";
+ # Syntax: 8/8/2008 11:55
+ my $timevalue = get_patchtime_value();
+
+ my $targetproductnamestring = "TargetProductName";
+ my $targetproductnamevalue = $allvariables->{'PRODUCTNAME'};
+ if ( $allvariables->{'PROPERTYTABLEPRODUCTNAME'} ) { $targetproductnamevalue = $allvariables->{'PROPERTYTABLEPRODUCTNAME'}; }
+
+ my $manufacturerstring = "ManufacturerName";
+ my $manufacturervalue = "OpenOffice.org";
+ if ( $installer::globals::longmanufacturer ) { $manufacturervalue = $installer::globals::longmanufacturer; }
+
+ my $displaynamestring = "DisplayName";
+ my $descriptionstring = "Description";
+ my $displaynamevalue = "";
+ my $descriptionvalue = "";
+
+ my $base = $allvariables->{'PRODUCTNAME'} . " " . $allvariables->{'PRODUCTVERSION'};
+ if ( $installer::globals::languagepack ) { $base = $targetproductnamevalue; }
+
+ my $windowspatchlevel = 0;
+ if ( $allvariables->{'WINDOWSPATCHLEVEL'} ) { $windowspatchlevel = $allvariables->{'WINDOWSPATCHLEVEL'}; }
+
+ my $displayaddon = "";
+ if ( $allvariables->{'PATCHDISPLAYADDON'} ) { $displayaddon = $allvariables->{'PATCHDISPLAYADDON'}; }
+
+ my $cwsname = "";
+ if ( $ENV{'CWS_WORK_STAMP'} ) { $cwsname = $ENV{'CWS_WORK_STAMP'}; }
+ if (( $cwsname ne "" ) && ( $allvariables->{'OVERWRITE_CWSNAME'} )) { $cwsname = $allvariables->{'OVERWRITE_CWSNAME'}; }
+
+ my $patchsequence = get_patchsequence($allvariables);
+
+ if (( $allvariables->{'SERVICEPACK'} ) && ( $allvariables->{'SERVICEPACK'} == 1 ))
+ {
+ $displaynamevalue = $base . " ServicePack " . $windowspatchlevel . " " . $patchsequence . " Build: " . $installer::globals::buildid;
+ $descriptionvalue = $base . " ServicePack " . $windowspatchlevel . " " . $patchsequence . " Build: " . $installer::globals::buildid;
+ }
+ else
+ {
+ $displaynamevalue = $base . " Hotfix " . $cwsname . " " . $displayaddon . " " . $patchsequence . " Build: " . $installer::globals::buildid;
+ $descriptionvalue = $base . " Hotfix " . $cwsname . " " . $displayaddon . " " . $patchsequence . " Build: " . $installer::globals::buildid;
+ $displaynamevalue =~ s/ / /g;
+ $descriptionvalue =~ s/ / /g;
+ $displaynamevalue =~ s/ / /g;
+ $descriptionvalue =~ s/ / /g;
+ $displaynamevalue =~ s/ / /g;
+ $descriptionvalue =~ s/ / /g;
+ }
+
+ if ( $allvariables->{'MSPPATCHNAMELIST'} )
+ {
+ my $patchnamelistfile = $allvariables->{'MSPPATCHNAMELIST'};
+ $patchnamelistfile = $installer::globals::idttemplatepath . $installer::globals::separator . $patchnamelistfile;
+ if ( ! -f $patchnamelistfile ) { installer::exiter::exit_program("ERROR: Could not find file \"$patchnamelistfile\".", "change_patchmetadata_table"); }
+ my $filecontent = installer::files::read_file($patchnamelistfile);
+
+ # Get name and path of reference database
+ my $patchid = get_patchid_from_list($filecontent, $languagestringref, $patchnamelistfile);
+
+ if ( $patchid eq "" ) { installer::exiter::exit_program("ERROR: Could not find file patchid in file \"$patchnamelistfile\" for language(s) \"$$languagestringref\".", "change_patchmetadata_table"); }
+
+ # Setting language specific patch id
+ }
+
+ for ( my $i = 0; $i <= $#{$filecontent}; $i++ )
+ {
+ if ( ${$filecontent}[$i] =~ /^\s*(.*?)\t(.*?)\t(.*?)\s*$/ )
+ {
+ my $company = $1;
+ my $property = $2;
+ my $value = $3;
+
+ if ( $property eq $classificationstring )
+ {
+ ${$filecontent}[$i] = "$company\t$property\t$classificationvalue\n";
+ $classification_set = 1;
+ }
+
+ if ( $property eq $allowremovalstring )
+ {
+ ${$filecontent}[$i] = "$company\t$property\t$allowremovalvalue\n";
+ $allowremoval_set = 1;
+ }
+
+ if ( $property eq $timestring )
+ {
+ ${$filecontent}[$i] = "$company\t$property\t$timevalue\n";
+ $creationtime_set = 1;
+ }
+
+ if ( $property eq $targetproductnamestring )
+ {
+ ${$filecontent}[$i] = "$company\t$property\t$targetproductnamevalue\n";
+ $targetproductname_set = 1;
+ }
+
+ if ( $property eq $manufacturerstring )
+ {
+ ${$filecontent}[$i] = "$company\t$property\t$manufacturervalue\n";
+ $manufacturer_set = 1;
+ }
+
+ if ( $property eq $displaynamestring )
+ {
+ ${$filecontent}[$i] = "$company\t$property\t$displaynamevalue\n";
+ $displayname_set = 1;
+ }
+
+ if ( $property eq $descriptionstring )
+ {
+ ${$filecontent}[$i] = "$company\t$property\t$descriptionvalue\n";
+ $description_set = 1;
+ }
+ }
+
+ push(@newcontent, ${$filecontent}[$i]);
+ }
+
+ if ( ! $classification_set )
+ {
+ my $line = "$defaultcompany\t$classificationstring\t$classificationvalue\n";
+ push(@newcontent, $line);
+ }
+
+ if ( ! $allowremoval_set )
+ {
+ my $line = "$defaultcompany\t$classificationstring\t$allowremovalvalue\n";
+ push(@newcontent, $line);
+ }
+
+ if ( ! $allowremoval_set )
+ {
+ my $line = "$defaultcompany\t$classificationstring\t$allowremovalvalue\n";
+ push(@newcontent, $line);
+ }
+
+ if ( ! $creationtime_set )
+ {
+ my $line = "$defaultcompany\t$timestring\t$timevalue\n";
+ push(@newcontent, $line);
+ }
+
+ if ( ! $targetproductname_set )
+ {
+ my $line = "$defaultcompany\t$targetproductnamestring\t$targetproductnamevalue\n";
+ push(@newcontent, $line);
+ }
+
+ if ( ! $manufacturer_set )
+ {
+ my $line = "$defaultcompany\t$manufacturerstring\t$manufacturervalue\n";
+ push(@newcontent, $line);
+ }
+
+ if ( ! $displayname_set )
+ {
+ my $line = "$defaultcompany\t$displaynamestring\t$displaynamevalue\n";
+ push(@newcontent, $line);
+ }
+
+ if ( ! $description_set )
+ {
+ my $line = "$defaultcompany\t$descriptionstring\t$descriptionvalue\n";
+ push(@newcontent, $line);
+ }
+
+ # saving file
+ installer::files::save_file($filename, \@newcontent);
+}
+
+####################################################################
+# Editing table PatchSequence
+####################################################################
+
+sub change_patchsequence_table
+{
+ my ($localmspdir, $allvariables) = @_;
+
+ my $infoline = "Changing content of table \"PatchSequence\"\n";
+ push( @installer::globals::logfileinfo, $infoline);
+
+ my $filename = $localmspdir . $installer::globals::separator . "PatchSequence.idt";
+ if ( ! -f $filename ) { installer::exiter::exit_program("ERROR: Could not find file \"$filename\" !", "change_patchsequence_table"); }
+
+ my $filecontent = installer::files::read_file($filename);
+ my @newcontent = ();
+
+ # Copying the header
+ for ( my $i = 0; $i <= $#{$filecontent}; $i++ ) { if ( $i < 3 ) { push(@newcontent, ${$filecontent}[$i]); } }
+
+ # Syntax: PatchFamily Target Sequence Supersede
+
+ my $patchfamily = "SO";
+ my $target = "";
+ my $patchsequence = get_patchsequence($allvariables);
+ my $supersede = get_supersede($allvariables);
+
+ if ( $#{$filecontent} >= 3 )
+ {
+ my $line = ${$filecontent}[3];
+ if ( $line =~ /^\s*(.*?)\t(.*?)\t(.*?)\t(.*?)\s$/ )
+ {
+ $patchfamily = $1;
+ $target = $2;
+ }
+ }
+
+ #Adding sequence line, saving PatchFamily
+ my $newline = "$patchfamily\t$target\t$patchsequence\t$supersede\n";
+ push(@newcontent, $newline);
+
+ # saving file
+ installer::files::save_file($filename, \@newcontent);
+}
+
+####################################################################
+# Setting supersede, "0" for Hotfixes, "1" for ServicePack
+####################################################################
+
+sub get_supersede
+{
+ my ( $allvariables ) = @_;
+
+ my $supersede = 0; # if not defined, this is a Hotfix
+
+ if (( $allvariables->{'SERVICEPACK'} ) && ( $allvariables->{'SERVICEPACK'} == 1 )) { $supersede = 1; }
+
+ return $supersede;
+}
+
+####################################################################
+# Setting the sequence of the patch
+####################################################################
+
+sub get_patchsequence
+{
+ my ( $allvariables ) = @_;
+
+ my $patchsequence = "1.0";
+
+ if ( ! $allvariables->{'PACKAGEVERSION'} ) { installer::exiter::exit_program("ERROR: PACKAGEVERSION must be set for msp patch creation!", "get_patchsequence"); }
+
+ my $packageversion = $allvariables->{'PACKAGEVERSION'};
+
+ if ( $packageversion =~ /^\s*(\d+)\.(\d+)\.(\d+)\s*$/ )
+ {
+ my $major = $1;
+ my $minor = $2;
+ my $micro = $3;
+ my $concat = 100 * $minor + $micro;
+ $packageversion = $major . "\." . $concat;
+ }
+ my $vendornumber = 0;
+ if ( $allvariables->{'VENDORPATCHVERSION'} ) { $vendornumber = $allvariables->{'VENDORPATCHVERSION'}; }
+ $patchsequence = $packageversion . "\." . $installer::globals::buildid . "\." . $vendornumber;
+
+ if ( $allvariables->{'PATCHSEQUENCE'} ) { $patchsequence = $allvariables->{'PATCHSEQUENCE'}; }
+
+ return $patchsequence;
+}
+
+####################################################################
+# Editing all tables from pcp file, that need to be edited
+####################################################################
+
+sub edit_tables
+{
+ my ($tablelist, $localmspdir, $olddatabase, $newdatabase, $mspfilename, $allvariables, $languagestringref) = @_;
+
+ # table list contains: my $tablelist = "Properties TargetImages UpgradedImages ImageFamilies PatchMetadata PatchSequence";
+
+ change_properties_table($localmspdir, $mspfilename);
+ change_targetimages_table($localmspdir, $olddatabase);
+ change_upgradedimages_table($localmspdir, $newdatabase);
+ change_imagefamilies_table($localmspdir);
+ change_patchmetadata_table($localmspdir, $allvariables, $languagestringref);
+ change_patchsequence_table($localmspdir, $allvariables);
+}
+
+#################################################################################
+# Checking, if this is the correct database.
+#################################################################################
+
+sub correct_patch
+{
+ my ($product, $pro, $langs, $languagestringref) = @_;
+
+ my $correct_patch = 0;
+
+ # Comparing $product with $installer::globals::product and
+ # $pro with $installer::globals::pro and
+ # $langs with $languagestringref
+
+ my $product_is_good = 0;
+
+ my $localproduct = $installer::globals::product;
+ if ( $installer::globals::languagepack ) { $localproduct = $localproduct . "LanguagePack"; }
+
+ if ( $product eq $localproduct ) { $product_is_good = 1; }
+
+ if ( $product_is_good )
+ {
+ my $pro_is_good = 0;
+
+ if ((( $pro eq "pro" ) && ( $installer::globals::pro )) || (( $pro eq "nonpro" ) && ( ! $installer::globals::pro ))) { $pro_is_good = 1; }
+
+ if ( $pro_is_good )
+ {
+ my $langlisthash = installer::converter::convert_stringlist_into_hash(\$langs, ",");
+ my $langstringhash = installer::converter::convert_stringlist_into_hash($languagestringref, "_");
+
+ my $not_included = 0;
+ foreach my $onelang ( keys %{$langlisthash} )
+ {
+ if ( ! exists($langstringhash->{$onelang}) )
+ {
+ $not_included = 1;
+ last;
+ }
+ }
+
+ if ( ! $not_included )
+ {
+ foreach my $onelanguage ( keys %{$langstringhash} )
+ {
+ if ( ! exists($langlisthash->{$onelanguage}) )
+ {
+ $not_included = 1;
+ last;
+ }
+ }
+
+ if ( ! $not_included ) { $correct_patch = 1; }
+ }
+ }
+ }
+
+ return $correct_patch;
+}
+
+#################################################################################
+# Searching for the path to the required patch for this special product.
+#################################################################################
+
+sub get_requiredpatchfile_from_list
+{
+ my ($filecontent, $languagestringref, $filename) = @_;
+
+ my $patchpath = "";
+
+ for ( my $i = 0; $i <= $#{$filecontent}; $i++ )
+ {
+ my $line = ${$filecontent}[$i];
+ if ( $line =~ /^\s*$/ ) { next; } # empty line
+ if ( $line =~ /^\s*\#/ ) { next; } # comment line
+
+ if ( $line =~ /^\s*(.+?)\s*\t+\s*(.+?)\s*\t+\s*(.+?)\s*\t+\s*(.+?)\s*$/ )
+ {
+ my $product = $1;
+ my $pro = $2;
+ my $langs = $3;
+ my $path = $4;
+
+ if (( $pro ne "pro" ) && ( $pro ne "nonpro" )) { installer::exiter::exit_program("ERROR: Wrong syntax in file: $filename. Only \"pro\" or \"nonpro\" allowed in column 1! Line: \"$line\"", "get_databasename_from_list"); }
+
+ if ( correct_patch($product, $pro, $langs, $languagestringref) )
+ {
+ $patchpath = $path;
+ last;
+ }
+ }
+ else
+ {
+ installer::exiter::exit_program("ERROR: Wrong syntax in file: $filename! Line: \"$line\"", "get_requiredpatchfile_from_list");
+ }
+ }
+
+ return $patchpath;
+}
+
+##################################################################
+# Converting unicode file to ascii
+# to be more precise: uft-16 little endian to ascii
+##################################################################
+
+sub convert_unicode_to_ascii
+{
+ my ( $filename ) = @_;
+
+ my @localfile = ();
+
+ my $savfilename = $filename . "_before.unicode";
+ installer::systemactions::copy_one_file($filename, $savfilename);
+
+# open( IN, "<:utf16", $filename ) || installer::exiter::exit_program("ERROR: Cannot open file $filename for reading", "convert_unicode_to_ascii");
+# open( IN, "<:para:crlf:uni", $filename ) || installer::exiter::exit_program("ERROR: Cannot open file $filename for reading", "convert_unicode_to_ascii");
+ open( IN, "<:encoding(UTF16-LE)", $filename ) || installer::exiter::exit_program("ERROR: Cannot open file $filename for reading", "convert_unicode_to_ascii");
+# open( IN, "<:encoding(UTF-8)", $filename ) || installer::exiter::exit_program("ERROR: Cannot open file $filename for reading", "convert_unicode_to_ascii");
+ while ( $line = <IN> ) {
+ push @localfile, $line;
+ }
+ close( IN );
+
+ if ( open( OUT, ">", $filename ) )
+ {
+ print OUT @localfile;
+ close(OUT);
+ }
+}
+
+####################################################################
+# Analyzing the log file created by msimsp.exe to find all
+# files included into the patch.
+####################################################################
+
+sub analyze_msimsp_logfile
+{
+ my ($logfile, $filesarray) = @_;
+
+ # Reading log file after converting from utf-16 (LE) to ascii
+ convert_unicode_to_ascii($logfile);
+ my $logfilecontent = installer::files::read_file($logfile);
+
+ # Creating hash from $filesarray: unique file name -> destination of file
+ my %filehash = ();
+ my %destinationcollector = ();
+
+ for ( my $i = 0; $i <= $#{$filesarray}; $i++ )
+ {
+ my $onefile = ${$filesarray}[$i];
+
+ # Only collecting files with "uniquename" and "destination"
+ if (( exists($onefile->{'uniquename'}) ) && ( exists($onefile->{'uniquename'}) ))
+ {
+ my $uniquefilename = $onefile->{'uniquename'};
+ my $destpath = $onefile->{'destination'};
+ $filehash{$uniquefilename} = $destpath;
+ }
+ }
+
+ # Analyzing log file of msimsp.exe, finding all changed files
+ # and searching all destinations of unique file names.
+ # Content in log file: "INFO File Key: <file key> is modified"
+ # Collecting content in @installer::globals::patchfilecollector
+
+ for ( my $i = 0; $i <= $#{$logfilecontent}; $i++ )
+ {
+ if ( ${$logfilecontent}[$i] =~ /Key\:\s*(.*?) is modified\s*$/ )
+ {
+ my $filekey = $1;
+ if ( exists($filehash{$filekey}) ) { $destinationcollector{$filehash{$filekey}} = 1; }
+ else { installer::exiter::exit_program("ERROR: Could not find file key \"$filekey\" in file collector.", "analyze_msimsp_logfile"); }
+ }
+ }
+
+ foreach my $onedest ( sort keys %destinationcollector ) { push(@installer::globals::patchfilecollector, "$onedest\n"); }
+
+}
+
+####################################################################
+# Creating msp patch files for Windows
+####################################################################
+
+sub create_msp_patch
+{
+ my ($installationdir, $includepatharrayref, $allvariables, $languagestringref, $languagesarrayref, $filesarray) = @_;
+
+ my $force = 1; # print this message even in 'quiet' mode
+ installer::logger::print_message( "\n******************************************\n" );
+ installer::logger::print_message( "... creating msp installation set ...\n", $force );
+ installer::logger::print_message( "******************************************\n" );
+
+ $installer::globals::creating_windows_installer_patch = 1;
+
+ my @needed_files = ("msimsp.exe"); # only required for patch creation process
+ installer::control::check_needed_files_in_path(\@needed_files);
+
+ installer::logger::include_header_into_logfile("Creating msp installation sets:");
+
+ my $firstdir = $installationdir;
+ installer::pathanalyzer::get_path_from_fullqualifiedname(\$firstdir);
+
+ my $lastdir = $installationdir;
+ installer::pathanalyzer::make_absolute_filename_to_relative_filename(\$lastdir);
+
+ if ( $lastdir =~ /\./ ) { $lastdir =~ s/\./_msp_inprogress\./ }
+ else { $lastdir = $lastdir . "_msp_inprogress"; }
+
+ # Removing existing directory "_native_packed_inprogress" and "_native_packed_witherror" and "_native_packed"
+
+ my $mspdir = $firstdir . $lastdir;
+ if ( -d $mspdir ) { installer::systemactions::remove_complete_directory($mspdir); }
+
+ my $olddir = $mspdir;
+ $olddir =~ s/_inprogress/_witherror/;
+ if ( -d $olddir ) { installer::systemactions::remove_complete_directory($olddir); }
+
+ $olddir = $mspdir;
+ $olddir =~ s/_inprogress//;
+ if ( -d $olddir ) { installer::systemactions::remove_complete_directory($olddir); }
+
+ # Creating the new directory for new installation set
+ installer::systemactions::create_directory($mspdir);
+
+ $installer::globals::saveinstalldir = $mspdir;
+
+ installer::logger::include_timestamp_into_logfile("\nPerformance Info: Starting product installation");
+
+ # Installing both installation sets
+ installer::logger::print_message( "... installing products ...\n" );
+ my ($olddatabase, $newdatabase) = install_installation_sets($installationdir);
+
+ installer::logger::include_timestamp_into_logfile("\nPerformance Info: Starting pcp file creation");
+
+ # Create pcp file
+ installer::logger::print_message( "... creating pcp file ...\n" );
+
+ my $localmspdir = installer::systemactions::create_directories("msp", $languagestringref);
+
+ if ( ! $allvariables->{'PCPFILENAME'} ) { installer::exiter::exit_program("ERROR: Property \"PCPFILENAME\" has to be defined.", "create_msp_patch"); }
+ my $pcpfilename = $allvariables->{'PCPFILENAME'};
+
+ if ( $installer::globals::languagepack ) { $pcpfilename =~ s/.pcp\s*$/languagepack.pcp/; }
+
+ # Searching the pcp file in the include pathes
+ my $fullpcpfilenameref = installer::scriptitems::get_sourcepath_from_filename_and_includepath(\$pcpfilename, $includepatharrayref, 1);
+ if ( $$fullpcpfilenameref eq "" ) { installer::exiter::exit_program("ERROR: pcp file not found: $pcpfilename !", "create_msp_patch"); }
+ my $fullpcpfilenamesource = $$fullpcpfilenameref;
+
+ # Copying pcp file
+ my $fullpcpfilename = $localmspdir . $installer::globals::separator . $pcpfilename;
+ installer::systemactions::copy_one_file($fullpcpfilenamesource, $fullpcpfilename);
+
+ # a. Extracting tables from msi database: msidb.exe -d <msifile> -f <directory> -e File Media, ...
+ # b. Changing content of msi database in tables: File, Media, Directory, FeatureComponent
+ # c. Including tables into msi database: msidb.exe -d <msifile> -f <directory> -i File Media, ...
+
+ # Unpacking tables from pcp file
+ extract_all_tables_from_pcpfile($fullpcpfilename, $localmspdir);
+
+ # Tables, that need to be edited
+ my $tablelist = "Properties TargetImages UpgradedImages ImageFamilies PatchMetadata PatchSequence"; # required tables
+
+ # Saving all tables
+ check_and_save_tables($tablelist, $localmspdir);
+
+ # Setting the name of the new msp file
+ my $mspfilename = set_mspfilename($allvariables, $mspdir, $languagesarrayref);
+
+ # Editing tables
+ edit_tables($tablelist, $localmspdir, $olddatabase, $newdatabase, $mspfilename, $allvariables, $languagestringref);
+
+ # Adding edited tables into pcp file
+ include_tables_into_pcpfile($fullpcpfilename, $localmspdir, $tablelist);
+
+ # Start msimsp.exe
+ installer::logger::include_timestamp_into_logfile("\nPerformance Info: Starting msimsp.exe");
+ my $msimsplogfile = execute_msimsp($fullpcpfilename, $mspfilename, $localmspdir);
+
+ # Copy final installation set next to msp file
+ installer::logger::include_timestamp_into_logfile("\nPerformance Info: Copying installation set");
+ installer::logger::print_message( "... copying installation set ...\n" );
+
+ my $oldinstallationsetpath = $installer::globals::updatedatabasepath;
+
+ if ( $^O =~ /cygwin/i ) { $oldinstallationsetpath =~ s/\\/\//g; }
+
+ installer::pathanalyzer::get_path_from_fullqualifiedname(\$oldinstallationsetpath);
+ installer::systemactions::copy_complete_directory($oldinstallationsetpath, $mspdir);
+
+ # Copying additional patches into the installation set, if required
+ if (( $allvariables->{'ADDITIONALREQUIREDPATCHES'} ) && ( $allvariables->{'ADDITIONALREQUIREDPATCHES'} ne "" ) && ( ! $installer::globals::languagepack ))
+ {
+ my $filename = $allvariables->{'ADDITIONALREQUIREDPATCHES'};
+
+ my $fullfilenameref = installer::scriptitems::get_sourcepath_from_filename_and_includepath(\$filename, $includepatharrayref, 1);
+ if ( $$fullfilenameref eq "" ) { installer::exiter::exit_program("ERROR: Could not find file with required patches, although it is defined: $filename !", "create_msp_patch"); }
+ my $fullfilename = $$fullfilenameref;
+
+ # Reading list file
+ my $listfile = installer::files::read_file($fullfilename);
+
+ # Get name and path of reference database
+ my $requiredpatchfile = get_requiredpatchfile_from_list($listfile, $languagestringref, $fullfilename);
+ if ( $requiredpatchfile eq "" ) { installer::exiter::exit_program("ERROR: Could not find path to required patch in file $fullfilename for language(s) $$languagestringref!", "create_msp_patch"); }
+
+ # Copying patch file
+ installer::systemactions::copy_one_file($requiredpatchfile, $mspdir);
+ # my $infoline = "Copy $requiredpatchfile to $mspdir\n";
+ # push( @installer::globals::logfileinfo, $infoline);
+ }
+
+ # Find all files included into the patch
+ # Analyzing the msimsp log file $msimsplogfile
+ analyze_msimsp_logfile($msimsplogfile, $filesarray);
+
+ # Done
+ installer::logger::include_timestamp_into_logfile("\nPerformance Info: msp creation done");
+
+ return $mspdir;
+}
+
+1;
diff --git a/solenv/bin/modules/installer/windows/patch.pm b/solenv/bin/modules/installer/windows/patch.pm
new file mode 100644
index 000000000000..536464192a71
--- /dev/null
+++ b/solenv/bin/modules/installer/windows/patch.pm
@@ -0,0 +1,155 @@
+#*************************************************************************
+#
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+#
+# Copyright 2000, 2010 Oracle and/or its affiliates.
+#
+# OpenOffice.org - a multi-platform office productivity suite
+#
+# This file is part of OpenOffice.org.
+#
+# OpenOffice.org is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License version 3
+# only, as published by the Free Software Foundation.
+#
+# OpenOffice.org is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License version 3 for more details
+# (a copy is included in the LICENSE file that accompanied this code).
+#
+# You should have received a copy of the GNU Lesser General Public License
+# version 3 along with OpenOffice.org. If not, see
+# <http://www.openoffice.org/license.html>
+# for a copy of the LGPLv3 License.
+#
+#*************************************************************************
+
+package installer::windows::patch;
+
+use installer::exiter;
+use installer::files;
+use installer::globals;
+use installer::windows::idtglobal;
+
+####################################################################################
+# Creating the file Upgrade.idt dynamically
+# Content:
+# UpgradeCode VersionMin VersionMax Language Attributes Remove ActionProperty
+####################################################################################
+
+sub update_patch_tables
+{
+ my ($basedir, $allvariables) = @_;
+
+ my $reglocatfile = "";
+ my $appsearchfile = "";
+
+ my $reglocatfilename = $basedir . $installer::globals::separator . "RegLocat.idt";
+ my $appsearchfilename = $basedir . $installer::globals::separator . "AppSearc.idt";
+ my $signaturefilename = $basedir . $installer::globals::separator . "Signatur.idt";
+
+ if ( -f $reglocatfilename )
+ {
+ $reglocatfile = installer::files::read_file($reglocatfilename);
+ }
+ else
+ {
+ my @reglocattable = ();
+ $reglocatfile = \@reglocattable;
+ installer::windows::idtglobal::write_idt_header($reglocatfile, "reglocat");
+ }
+
+ if ( -f $appsearchfilename )
+ {
+ $appsearchfile = installer::files::read_file($appsearchfilename);
+ }
+ else
+ {
+ my @appsearchtable = ();
+ $appsearchfile = \@appsearchtable;
+ installer::windows::idtglobal::write_idt_header($appsearchfile, "appsearch");
+ }
+
+ if ( -f $signaturefilename )
+ {
+ $signaturefile = installer::files::read_file($signaturefilename);
+ }
+ else
+ {
+ my @signaturetable = ();
+ $signaturefile = \@signaturetable;
+ installer::windows::idtglobal::write_idt_header($signaturefile, "signatur");
+ }
+
+ # Writing content into this tables
+
+ if ( ! $allvariables->{'PATCHCODEFILE'} ) { installer::exiter::exit_program("ERROR: Variable PATCHCODEFILE must be defined for Windows patches!", "update_patch_tables"); }
+ my $patchcodesfilename = $installer::globals::idttemplatepath . $installer::globals::separator . $allvariables->{'PATCHCODEFILE'};
+ my $patchcodefile = installer::files::read_file($patchcodesfilename);
+
+ my $number = 0;
+
+ for ( my $i = 0; $i <= $#{$patchcodefile}; $i++ )
+ {
+ my $oneline = ${$patchcodefile}[$i];
+
+ if ( $oneline =~ /^\s*\#/ ) { next; } # this is a comment line
+ if ( $oneline =~ /^\s*$/ ) { next; }
+
+ my $code = "";
+ if ( $oneline =~ /^\s*(\S+)\s/ ) { $code = $1; }
+
+ foreach my $name ( sort keys %installer::globals::installlocations )
+ {
+ $number++;
+ my $signature = "dir" . $number . "user";
+ my $rootvalue = "1";
+ my $registryname = "";
+ my $registryversion = "";
+
+ if ( $allvariables->{'SEARCHPRODUCTNAME'} ) { $registryname = $allvariables->{'SEARCHPRODUCTNAME'}; }
+ else { $registryname = $allvariables->{'PRODUCTNAME'}; }
+
+ if ( $allvariables->{'SEARCHPRODUCTVERSION'} ) { $registryversion = $allvariables->{'SEARCHPRODUCTVERSION'}; }
+ else { $registryversion = $allvariables->{'PRODUCTVERSION'}; }
+
+ my $key = "Software\\" . $allvariables->{'MANUFACTURER'} . "\\" . $registryname . "\\" . $registryversion . "\\" . $code;
+
+ my $type = 2;
+ my $property = $name;
+
+ $oneline = $signature . "\t" . $rootvalue . "\t" . $key . "\t" . $name . "\t" . $type . "\n";
+ push(@{$reglocatfile}, $oneline);
+
+ $oneline = $property . "\t" . $signature . "\n";
+ push(@{$appsearchfile}, $oneline);
+
+ $signature = "dir" . $number . "mach";
+ $rootvalue = "2";
+
+ $oneline = $signature . "\t" . $rootvalue . "\t" . $key . "\t" . $name . "\t" . $type . "\n";
+ push(@{$reglocatfile}, $oneline);
+
+ $oneline = $property . "\t" . $signature . "\n";
+ push(@{$appsearchfile}, $oneline);
+ }
+ }
+
+ # Saving the files
+
+ installer::files::save_file($reglocatfilename ,$reglocatfile);
+ my $infoline = "Updated idt file: $reglocatfilename\n";
+ push(@installer::globals::logfileinfo, $infoline);
+
+ installer::files::save_file($appsearchfilename ,$appsearchfile);
+ $infoline = "Updated idt file: $appsearchfilename\n";
+ push(@installer::globals::logfileinfo, $infoline);
+
+ installer::files::save_file($signaturefilename ,$signaturefile);
+ $infoline = "Updated idt file: $signaturefilename\n";
+ push(@installer::globals::logfileinfo, $infoline);
+
+}
+
+1;
diff --git a/solenv/bin/modules/installer/windows/property.pm b/solenv/bin/modules/installer/windows/property.pm
new file mode 100644
index 000000000000..35866103d9fa
--- /dev/null
+++ b/solenv/bin/modules/installer/windows/property.pm
@@ -0,0 +1,656 @@
+#*************************************************************************
+#
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+#
+# Copyright 2000, 2010 Oracle and/or its affiliates.
+#
+# OpenOffice.org - a multi-platform office productivity suite
+#
+# This file is part of OpenOffice.org.
+#
+# OpenOffice.org is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License version 3
+# only, as published by the Free Software Foundation.
+#
+# OpenOffice.org is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License version 3 for more details
+# (a copy is included in the LICENSE file that accompanied this code).
+#
+# You should have received a copy of the GNU Lesser General Public License
+# version 3 along with OpenOffice.org. If not, see
+# <http://www.openoffice.org/license.html>
+# for a copy of the LGPLv3 License.
+#
+#*************************************************************************
+
+package installer::windows::property;
+
+use installer::exiter;
+use installer::files;
+use installer::globals;
+use installer::windows::idtglobal;
+use installer::windows::language;
+
+#############################################
+# Setting the properties dynamically
+# for the table Property.idt
+#############################################
+
+sub get_arpcomments_for_property_table
+{
+ my ( $allvariables, $languagestringref ) = @_;
+
+ my $name = $allvariables->{'PRODUCTNAME'};
+ my $version = $allvariables->{'PRODUCTVERSION'};
+ my $comment = $name . " " . $version;
+
+ my $postversionextension = "";
+ if ( $allvariables->{'POSTVERSIONEXTENSION'} )
+ {
+ $postversionextension = $allvariables->{'POSTVERSIONEXTENSION'};
+ $comment = $comment . " " . $postversionextension;
+ }
+
+ if ( $installer::globals::languagepack ) { $comment = $comment . " " . "Language Pack"; }
+
+ if ( $installer::globals::patch )
+ {
+ if ( ! $allvariables->{'WINDOWSPATCHLEVEL'} ) { installer::exiter::exit_program("ERROR: No Patch level defined for Windows patch: WINDOWSPATCHLEVEL", "get_arpcomments_for_property_table"); }
+ my $patchstring = "Product Update" . " " . $allvariables->{'WINDOWSPATCHLEVEL'};
+ $comment = $comment . " " . $patchstring;
+ }
+
+ my $languagestring = $$languagestringref;
+ $languagestring =~ s/\_/\,/g;
+
+ $comment = $comment . " ($languagestring)";
+
+ my $localminor = "";
+ if ( $installer::globals::updatepack ) { $localminor = $installer::globals::lastminor; }
+ else { $localminor = $installer::globals::minor; }
+
+ my $buildidstring = "(" . $installer::globals::build . $localminor . "(Build:" . $installer::globals::buildid . "))";
+
+ # the environment variable CWS_WORK_STAMP is set only in CWS
+ if ( $ENV{'CWS_WORK_STAMP'} ) { $buildidstring = $buildidstring . "\[CWS\:" . $ENV{'CWS_WORK_STAMP'} . "\]"; }
+
+ $comment = $comment . " " . $buildidstring;
+
+ return $comment;
+}
+
+sub get_installlevel_for_property_table
+{
+ my $installlevel = "100";
+ return $installlevel;
+}
+
+sub get_ischeckforproductupdates_for_property_table
+{
+ my $ischeckforproductupdates = "1";
+ return $ischeckforproductupdates;
+}
+
+sub get_manufacturer_for_property_table
+{
+ return $installer::globals::manufacturer;
+}
+
+sub get_productlanguage_for_property_table
+{
+ my ($language) = @_;
+ my $windowslanguage = installer::windows::language::get_windows_language($language);
+ return $windowslanguage;
+}
+
+sub get_language_string
+{
+ my $langstring = "";
+
+ for ( my $i = 0; $i <= $#installer::globals::languagenames; $i++ )
+ {
+ $langstring = $langstring . $installer::globals::languagenames[$i] . ", ";
+ }
+
+ $langstring =~ s/\,\s*$//;
+ $langstring = "(" . $langstring . ")";
+
+ return $langstring;
+}
+
+sub get_english_language_string
+{
+ my $langstring = "";
+
+ # Sorting value not keys, therefore collecting all values
+ my %helper = ();
+ foreach my $lang ( keys %installer::globals::all_required_english_languagestrings )
+ {
+ $helper{$installer::globals::all_required_english_languagestrings{$lang}} = 1;
+ }
+
+ foreach my $lang ( sort keys %helper )
+ {
+ $langstring = $langstring . $lang . ", ";
+ }
+
+ $langstring =~ s/\,\s*$//;
+ $langstring = "(" . $langstring . ")";
+
+ return $langstring;
+}
+
+sub get_productname_for_property_table
+{
+ my ( $allvariables ) = @_;
+
+ my $name = $allvariables->{'PRODUCTNAME'};
+ my $version = $allvariables->{'PRODUCTVERSION'};
+ my $productname = $name . " " . $version;
+
+ my $postversionextension = "";
+ if ( $allvariables->{'POSTVERSIONEXTENSION'} )
+ {
+ $postversionextension = $allvariables->{'POSTVERSIONEXTENSION'};
+ $productname = $productname . " " . $postversionextension;
+ }
+
+ my $productextension = "";
+ if ( $allvariables->{'PRODUCTEXTENSION'} )
+ {
+ $productextension = $allvariables->{'PRODUCTEXTENSION'};
+ $productname = $productname . " " . $productextension;
+ }
+
+ if ( $installer::globals::languagepack )
+ {
+ # my $langstring = get_language_string(); # Example (English, Deutsch)
+ my $langstring = get_english_language_string(); # New: (English, German)
+ $productname = $name . " " . $version . " Language Pack" . " " . $langstring;
+ }
+
+ if ( $installer::globals::patch )
+ {
+ if ( ! $allvariables->{'WINDOWSPATCHLEVEL'} ) { installer::exiter::exit_program("ERROR: No Patch level defined for Windows patch: WINDOWSPATCHLEVEL", "get_productname_for_property_table"); }
+ my $patchstring = "Product Update" . " " . $allvariables->{'WINDOWSPATCHLEVEL'};
+ $productname = $productname . " " . $patchstring;
+ }
+
+ # Saving this name in hash $allvariables for further usage
+ $allvariables->{'PROPERTYTABLEPRODUCTNAME'} = $productname;
+ my $infoline = "Defined variable PROPERTYTABLEPRODUCTNAME: $productname\n";
+ push(@installer::globals::logfileinfo, $infoline);
+
+ return $productname;
+}
+
+sub get_quickstarterlinkname_for_property_table
+{
+ my ( $allvariables ) = @_;
+
+ # no usage of POSTVERSIONEXTENSION for Quickstarter link name!
+
+ my $name = $allvariables->{'PRODUCTNAME'};
+ my $version = $allvariables->{'PRODUCTVERSION'};
+ my $quickstartername = $name . " " . $version;
+
+ my $infoline = "Defined Quickstarter Link name: $quickstartername\n";
+ push(@installer::globals::logfileinfo, $infoline);
+
+ return $quickstartername;
+}
+
+sub get_productversion_for_property_table
+{
+ return $installer::globals::msiproductversion;
+}
+
+#######################################################
+# Setting all feature names as Properties. This is
+# required for the Windows patch process.
+#######################################################
+
+sub set_featurename_properties_for_patch
+{
+ ($propertyfile) = @_;
+
+ for ( my $i = 0; $i <= $#installer::globals::featurecollector; $i++ )
+ {
+ my $onepropertyline = $installer::globals::featurecollector[$i] . "\t" . "1" . "\n";
+ push(@{$propertyfile}, $onepropertyline);
+ }
+
+}
+
+#######################################################
+# Setting some important properties
+# (for finding the product in deinstallation process)
+#######################################################
+
+sub set_important_properties
+{
+ my ($propertyfile, $allvariables, $languagestringref) = @_;
+
+ # Setting new variables with the content of %PRODUCTNAME and %PRODUCTVERSION
+ if ( $allvariables->{'PRODUCTNAME'} )
+ {
+ my $onepropertyline = "DEFINEDPRODUCT" . "\t" . $allvariables->{'PRODUCTNAME'} . "\n";
+ push(@{$propertyfile}, $onepropertyline);
+ }
+
+ if ( $allvariables->{'PRODUCTVERSION'} )
+ {
+ my $onepropertyline = "DEFINEDVERSION" . "\t" . $allvariables->{'PRODUCTVERSION'} . "\n";
+ push(@{$propertyfile}, $onepropertyline);
+ }
+
+ if (( $allvariables->{'PRODUCTNAME'} ) && ( $allvariables->{'PRODUCTVERSION'} ) && ( $allvariables->{'MANUFACTURER'} ) && ( $allvariables->{'PRODUCTCODE'} ))
+ {
+ my $onepropertyline = "FINDPRODUCT" . "\t" . "Software\\" . $allvariables->{'MANUFACTURER'} . "\\" . $allvariables->{'PRODUCTNAME'} . $allvariables->{'PRODUCTADDON'} . "\\" . $allvariables->{'PRODUCTVERSION'} . "\\" . $allvariables->{'PRODUCTCODE'} . "\n";
+ push(@{$propertyfile}, $onepropertyline);
+ }
+
+ if ( $allvariables->{'PRODUCTMAJOR'} )
+ {
+ my $onepropertyline = "PRODUCTMAJOR" . "\t" . $allvariables->{'PRODUCTMAJOR'} . "\n";
+ push(@{$propertyfile}, $onepropertyline);
+ }
+
+ if ( $allvariables->{'PRODUCTMINOR'} )
+ {
+ my $onepropertyline = "PRODUCTMINOR" . "\t" . $allvariables->{'PRODUCTMINOR'} . "\n";
+ push(@{$propertyfile}, $onepropertyline);
+ }
+
+ if ( $allvariables->{'PRODUCTBUILDID'} )
+ {
+ my $onepropertyline = "PRODUCTBUILDID" . "\t" . $allvariables->{'PRODUCTBUILDID'} . "\n";
+ push(@{$propertyfile}, $onepropertyline);
+ }
+
+ if ( $allvariables->{'OOOBASEVERSION'} )
+ {
+ my $onepropertyline = "OOOBASEVERSION" . "\t" . $allvariables->{'OOOBASEVERSION'} . "\n";
+ push(@{$propertyfile}, $onepropertyline);
+ }
+
+ if ( $allvariables->{'URELAYERVERSION'} )
+ {
+ my $onepropertyline = "URELAYERVERSION" . "\t" . $allvariables->{'URELAYERVERSION'} . "\n";
+ push(@{$propertyfile}, $onepropertyline);
+ }
+
+ if ( $allvariables->{'BRANDPACKAGEVERSION'} )
+ {
+ my $onepropertyline = "BRANDPACKAGEVERSION" . "\t" . $allvariables->{'BRANDPACKAGEVERSION'} . "\n";
+ push(@{$propertyfile}, $onepropertyline);
+ }
+
+ if ( $allvariables->{'BASISROOTNAME'} )
+ {
+ my $onepropertyline = "BASISROOTNAME" . "\t" . $allvariables->{'BASISROOTNAME'} . "\n";
+ push(@{$propertyfile}, $onepropertyline);
+ }
+
+ if ( $allvariables->{'PREREQUIREDPATCH'} )
+ {
+ my $onepropertyline = "PREREQUIREDPATCH" . "\t" . $allvariables->{'PREREQUIREDPATCH'} . "\n";
+ push(@{$propertyfile}, $onepropertyline);
+ }
+
+ my $onepropertyline = "IGNOREPREREQUIREDPATCH" . "\t" . "1" . "\n";
+ push(@{$propertyfile}, $onepropertyline);
+
+ $onepropertyline = "DONTOPTIMIZELIBS" . "\t" . "0" . "\n";
+ push(@{$propertyfile}, $onepropertyline);
+
+ if ( $installer::globals::sundirexists )
+ {
+ my $onepropertyline = "SUNDIREXISTS" . "\t" . "1" . "\n";
+ push(@{$propertyfile}, $onepropertyline);
+ }
+
+ if ( $installer::globals::officedirhostname )
+ {
+ my $onepropertyline = "OFFICEDIRHOSTNAME" . "\t" . $installer::globals::officedirhostname . "\n";
+ push(@{$propertyfile}, $onepropertyline);
+
+ my $localofficedirhostname = $installer::globals::officedirhostname;
+ $localofficedirhostname =~ s/\//\\/g;
+ $onepropertyline = "OFFICEDIRHOSTNAME_" . "\t" . $localofficedirhostname . "\n";
+ push(@{$propertyfile}, $onepropertyline);
+ }
+
+ if ( $installer::globals::sundirhostname )
+ {
+ my $onepropertyline = "SUNDIRHOSTNAME" . "\t" . $installer::globals::sundirhostname . "\n";
+ push(@{$propertyfile}, $onepropertyline);
+ }
+
+ if ( $installer::globals::desktoplinkexists )
+ {
+ my $onepropertyline = "DESKTOPLINKEXISTS" . "\t" . "1" . "\n";
+ push(@{$propertyfile}, $onepropertyline);
+
+ $onepropertyline = "CREATEDESKTOPLINK" . "\t" . "1" . "\n"; # Setting the default
+ push(@{$propertyfile}, $onepropertyline);
+ }
+
+ if ( $installer::globals::patch )
+ {
+ my $onepropertyline = "ISPATCH" . "\t" . "1" . "\n";
+ push(@{$propertyfile}, $onepropertyline);
+
+ $onepropertyline = "SETUP_USED" . "\t" . "0" . "\n";
+ push(@{$propertyfile}, $onepropertyline);
+ }
+
+ if ( $installer::globals::languagepack )
+ {
+ my $onepropertyline = "ISLANGUAGEPACK" . "\t" . "1" . "\n";
+ push(@{$propertyfile}, $onepropertyline);
+ }
+
+ my $languagesline = "PRODUCTALLLANGUAGES" . "\t" . $$languagestringref . "\n";
+ push(@{$propertyfile}, $languagesline);
+
+ if (( $allvariables->{'PRODUCTEXTENSION'} ) && ( $allvariables->{'PRODUCTEXTENSION'} eq "Beta" ))
+ {
+ my $registryline = "WRITE_REGISTRY" . "\t" . "0" . "\n";
+ push(@{$propertyfile}, $registryline);
+ my $betainfoline = "BETAPRODUCT" . "\t" . "1" . "\n";
+ push(@{$propertyfile}, $betainfoline);
+ }
+ elsif ( $allvariables->{'DEVELOPMENTPRODUCT'} )
+ {
+ my $registryline = "WRITE_REGISTRY" . "\t" . "0" . "\n";
+ push(@{$propertyfile}, $registryline);
+ }
+ else
+ {
+ my $registryline = "WRITE_REGISTRY" . "\t" . "1" . "\n"; # Default: Write complete registry
+ push(@{$propertyfile}, $registryline);
+ }
+
+ # Adding also used tree conditions for multilayer products.
+ # These are saved in %installer::globals::usedtreeconditions
+ foreach my $treecondition (keys %installer::globals::usedtreeconditions)
+ {
+ my $onepropertyline = $treecondition . "\t" . "1" . "\n";
+ push(@{$propertyfile}, $onepropertyline);
+ }
+
+ # No more license dialog for selected products
+ if ( $allvariables->{'HIDELICENSEDIALOG'} )
+ {
+ my $onepropertyline = "HIDEEULA" . "\t" . "1" . "\n";
+
+ my $already_defined = 0;
+
+ for ( my $i = 0; $i <= $#{$propertyfile}; $i++ )
+ {
+ if ( ${$propertyfile}[$i] =~ /^\s*HIDEEULA\t/ )
+ {
+ ${$propertyfile}[$i] = $onepropertyline;
+ $already_defined = 1;
+ last;
+ }
+ }
+
+ if ( ! $already_defined )
+ {
+ push(@{$propertyfile}, $onepropertyline);
+ }
+ }
+
+ # Setting .NET requirements
+ if ( $installer::globals::required_dotnet_version ne "" )
+ {
+ my $onepropertyline = "REQUIRED_DOTNET_VERSION" . "\t" . $installer::globals::required_dotnet_version . "\n";
+ push(@{$propertyfile}, $onepropertyline);
+
+ $onepropertyline = "DOTNET_SUFFICIENT" . "\t" . "1" . "\n"; # default value for found .NET
+ push(@{$propertyfile}, $onepropertyline);
+ }
+
+}
+
+#######################################################
+# Setting properties needed for ms file type registration
+#######################################################
+
+sub set_ms_file_types_properties
+{
+ my ($propertyfile) = @_;
+
+ push(@{$propertyfile}, "REGISTER_PPS" . "\t" . "0" . "\n");
+ push(@{$propertyfile}, "REGISTER_PPSX" . "\t" . "0" . "\n");
+ push(@{$propertyfile}, "REGISTER_PPSM" . "\t" . "0" . "\n");
+ push(@{$propertyfile}, "REGISTER_PPAM" . "\t" . "0" . "\n");
+ push(@{$propertyfile}, "REGISTER_PPT" . "\t" . "0" . "\n");
+ push(@{$propertyfile}, "REGISTER_PPTX" . "\t" . "0" . "\n");
+ push(@{$propertyfile}, "REGISTER_PPTM" . "\t" . "0" . "\n");
+ push(@{$propertyfile}, "REGISTER_POT" . "\t" . "0" . "\n");
+ push(@{$propertyfile}, "REGISTER_POTX" . "\t" . "0" . "\n");
+ push(@{$propertyfile}, "REGISTER_POTM" . "\t" . "0" . "\n");
+
+ push(@{$propertyfile}, "REGISTER_DOC" . "\t" . "0" . "\n");
+ push(@{$propertyfile}, "REGISTER_DOCX" . "\t" . "0" . "\n");
+ push(@{$propertyfile}, "REGISTER_DOCM" . "\t" . "0" . "\n");
+ push(@{$propertyfile}, "REGISTER_DOT" . "\t" . "0" . "\n");
+ push(@{$propertyfile}, "REGISTER_DOTX" . "\t" . "0" . "\n");
+ push(@{$propertyfile}, "REGISTER_DOTM" . "\t" . "0" . "\n");
+ push(@{$propertyfile}, "REGISTER_RTF" . "\t" . "0" . "\n");
+
+ push(@{$propertyfile}, "REGISTER_XLS" . "\t" . "0" . "\n");
+ push(@{$propertyfile}, "REGISTER_XLSX" . "\t" . "0" . "\n");
+ push(@{$propertyfile}, "REGISTER_XLSM" . "\t" . "0" . "\n");
+ push(@{$propertyfile}, "REGISTER_XLSB" . "\t" . "0" . "\n");
+ push(@{$propertyfile}, "REGISTER_XLAM" . "\t" . "0" . "\n");
+ push(@{$propertyfile}, "REGISTER_XLT" . "\t" . "0" . "\n");
+ push(@{$propertyfile}, "REGISTER_XLTX" . "\t" . "0" . "\n");
+ push(@{$propertyfile}, "REGISTER_XLTM" . "\t" . "0" . "\n");
+
+ push(@{$propertyfile}, "REGISTER_NO_MSO_TYPES" . "\t" . "0" . "\n");
+ push(@{$propertyfile}, "REGISTER_ALL_MSO_TYPES" . "\t" . "0" . "\n");
+}
+
+####################################################################################
+# Updating the file Property.idt dynamically
+# Content:
+# Property Value
+####################################################################################
+
+sub update_property_table
+{
+ my ($basedir, $language, $allvariables, $languagestringref) = @_;
+
+ my $properyfilename = $basedir . $installer::globals::separator . "Property.idt";
+
+ my $propertyfile = installer::files::read_file($properyfilename);
+
+ # Getting the new values
+ # Some values (arpcomments, arpcontacts, ...) are inserted from the Property.mlf
+
+ my $arpcomments = get_arpcomments_for_property_table($allvariables, $languagestringref);
+ my $installlevel = get_installlevel_for_property_table();
+ my $ischeckforproductupdates = get_ischeckforproductupdates_for_property_table();
+ my $manufacturer = get_manufacturer_for_property_table();
+ my $productlanguage = get_productlanguage_for_property_table($language);
+ my $productname = get_productname_for_property_table($allvariables);
+ my $productversion = get_productversion_for_property_table();
+ my $quickstarterlinkname = get_quickstarterlinkname_for_property_table($allvariables);
+
+ # Updating the values
+
+ for ( my $i = 0; $i <= $#{$propertyfile}; $i++ )
+ {
+ ${$propertyfile}[$i] =~ s/\bARPCOMMENTSTEMPLATE\b/$arpcomments/;
+ ${$propertyfile}[$i] =~ s/\bINSTALLLEVELTEMPLATE\b/$installlevel/;
+ ${$propertyfile}[$i] =~ s/\bISCHECKFORPRODUCTUPDATESTEMPLATE\b/$ischeckforproductupdates/;
+ ${$propertyfile}[$i] =~ s/\bMANUFACTURERTEMPLATE\b/$manufacturer/;
+ ${$propertyfile}[$i] =~ s/\bPRODUCTLANGUAGETEMPLATE\b/$productlanguage/;
+ ${$propertyfile}[$i] =~ s/\bPRODUCTNAMETEMPLATE\b/$productname/;
+ ${$propertyfile}[$i] =~ s/\bPRODUCTVERSIONTEMPLATE\b/$productversion/;
+ ${$propertyfile}[$i] =~ s/\bQUICKSTARTERLINKNAMETEMPLATE\b/$quickstarterlinkname/;
+ }
+
+ # Setting variables into propertytable
+ set_important_properties($propertyfile, $allvariables, $languagestringref);
+
+ # Setting feature names as properties for Windows patch mechanism
+ if ( $installer::globals::patch ) { set_featurename_properties_for_patch($propertyfile); }
+
+ # Setting variables for register for ms file types
+ set_ms_file_types_properties($propertyfile);
+
+ # Saving the file
+
+ installer::files::save_file($properyfilename ,$propertyfile);
+ my $infoline = "Updated idt file: $properyfilename\n";
+ push(@installer::globals::logfileinfo, $infoline);
+
+}
+
+####################################################################################
+# Setting language specific Properties in file Property.idt dynamically
+# Adding:
+# is1033 = 1
+# isMulti = 1
+####################################################################################
+
+sub set_languages_in_property_table
+{
+ my ($basedir, $languagesarrayref) = @_;
+
+ my $properyfilename = $basedir . $installer::globals::separator . "Property.idt";
+ my $propertyfile = installer::files::read_file($properyfilename);
+
+ # Setting the component properties saved in %installer::globals::languageproperties
+ foreach my $localproperty ( keys %installer::globals::languageproperties )
+ {
+ $onepropertyline = $localproperty . "\t" . $installer::globals::languageproperties{$localproperty} . "\n";
+ push(@{$propertyfile}, $onepropertyline);
+ }
+
+ # Setting the info about multilingual installation in property "isMulti"
+
+ my $propertyname = "isMulti";
+ my $ismultivalue = 0;
+
+ if ( $installer::globals::ismultilingual ) { $ismultivalue = 1; }
+
+ my $onepropertyline = $propertyname . "\t" . $ismultivalue . "\n";
+ push(@{$propertyfile}, $onepropertyline);
+
+ # setting the ARPPRODUCTICON
+
+ if ($installer::globals::sofficeiconadded) # set in shortcut.pm
+ {
+ $onepropertyline = "ARPPRODUCTICON" . "\t" . "soffice.ico" . "\n";
+ push(@{$propertyfile}, $onepropertyline);
+ }
+
+ # Saving the file
+
+ installer::files::save_file($properyfilename ,$propertyfile);
+ my $infoline = "Added language content into idt file: $properyfilename\n";
+ push(@installer::globals::logfileinfo, $infoline);
+
+}
+
+############################################################
+# Setting the ProductCode and the UpgradeCode
+# into the Property table. Both have to be stored
+# in the global file $installer::globals::codefilename
+############################################################
+
+sub set_codes_in_property_table
+{
+ my ($basedir) = @_;
+
+ # Reading the property file
+
+ my $properyfilename = $basedir . $installer::globals::separator . "Property.idt";
+ my $propertyfile = installer::files::read_file($properyfilename);
+
+ # Updating the values
+
+ for ( my $i = 0; $i <= $#{$propertyfile}; $i++ )
+ {
+ ${$propertyfile}[$i] =~ s/\bPRODUCTCODETEMPLATE\b/$installer::globals::productcode/;
+ ${$propertyfile}[$i] =~ s/\bUPGRADECODETEMPLATE\b/$installer::globals::upgradecode/;
+ }
+
+ # Saving the property file
+
+ installer::files::save_file($properyfilename ,$propertyfile);
+ my $infoline = "Added language content into idt file: $properyfilename\n";
+ push(@installer::globals::logfileinfo, $infoline);
+
+}
+
+############################################################
+# Setting the variable REGKEYPRODPATH, that is used
+# by the language packs.
+############################################################
+
+sub set_regkeyprodpath_in_property_table
+{
+ my ($basedir, , $allvariables) = @_;
+
+ # Reading the property file
+
+ my $properyfilename = $basedir . $installer::globals::separator . "Property.idt";
+ my $propertyfile = installer::files::read_file($properyfilename);
+
+ my $name = $allvariables->{'PRODUCTNAME'};
+ my $version = $allvariables->{'PRODUCTVERSION'};
+
+ my $onepropertyline = "REGKEYPRODPATH" . "\t" . "Software" . "\\" . $installer::globals::manufacturer . "\\". $name;
+
+ push(@{$propertyfile}, $onepropertyline);
+
+ # Saving the property file
+
+ installer::files::save_file($properyfilename ,$propertyfile);
+ my $infoline = "Added language content into idt file: $properyfilename\n";
+ push(@installer::globals::logfileinfo, $infoline);
+
+}
+
+############################################################
+# Changing default for MS file type registration
+# in Beta products.
+############################################################
+
+sub update_checkbox_table
+{
+ my ($basedir, $allvariables) = @_;
+
+ if (( $allvariables->{'PRODUCTEXTENSION'} ) && ( $allvariables->{'PRODUCTEXTENSION'} eq "Beta" ))
+ {
+ my $checkboxfilename = $basedir . $installer::globals::separator . "CheckBox.idt";
+
+ if ( -f $checkboxfilename )
+ {
+ my $checkboxfile = installer::files::read_file($checkboxfilename);
+
+ my $checkboxline = "SELECT_WORD" . "\t" . "0" . "\n";
+ push(@{$checkboxfile}, $checkboxline);
+ $checkboxline = "SELECT_EXCEL" . "\t" . "0" . "\n";
+ push(@{$checkboxfile}, $checkboxline);
+ $checkboxline = "SELECT_POWERPOINT" . "\t" . "0" . "\n";
+ push(@{$checkboxfile}, $checkboxline);
+
+ # Saving the property file
+ installer::files::save_file($checkboxfilename ,$checkboxfile);
+ my $infoline = "Added ms file type defaults into idt file: $checkboxfilename\n";
+ push(@installer::globals::logfileinfo, $infoline);
+ }
+ }
+}
+
+1;
diff --git a/solenv/bin/modules/installer/windows/registry.pm b/solenv/bin/modules/installer/windows/registry.pm
new file mode 100644
index 000000000000..cf87ba174dd0
--- /dev/null
+++ b/solenv/bin/modules/installer/windows/registry.pm
@@ -0,0 +1,397 @@
+#*************************************************************************
+#
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+#
+# Copyright 2000, 2010 Oracle and/or its affiliates.
+#
+# OpenOffice.org - a multi-platform office productivity suite
+#
+# This file is part of OpenOffice.org.
+#
+# OpenOffice.org is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License version 3
+# only, as published by the Free Software Foundation.
+#
+# OpenOffice.org is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License version 3 for more details
+# (a copy is included in the LICENSE file that accompanied this code).
+#
+# You should have received a copy of the GNU Lesser General Public License
+# version 3 along with OpenOffice.org. If not, see
+# <http://www.openoffice.org/license.html>
+# for a copy of the LGPLv3 License.
+#
+#*************************************************************************
+
+package installer::windows::registry;
+
+use installer::files;
+use installer::globals;
+use installer::worker;
+use installer::windows::idtglobal;
+
+#####################################################
+# Generating the component name from a registryitem
+#####################################################
+
+sub get_registry_component_name
+{
+ my ($registryref, $allvariables) = @_;
+
+ # In this function exists the rule to create components from registryitems
+ # Rule:
+ # The componentname can be directly taken from the ModuleID.
+ # All registryitems belonging to one module can get the same component.
+
+ my $componentname = "";
+ my $isrootmodule = 0;
+
+ if ( $registryref->{'ModuleID'} ) { $componentname = $registryref->{'ModuleID'}; }
+
+ $componentname =~ s/\\/\_/g;
+ $componentname =~ s/\//\_/g;
+ $componentname =~ s/\-/\_/g;
+ $componentname =~ s/\_\s*$//g;
+
+ $componentname = lc($componentname); # componentnames always lowercase
+
+ if ( $componentname eq "gid_module_root" ) { $isrootmodule = 1; }
+
+ # Attention: Maximum length for the componentname is 72
+
+ $componentname =~ s/gid_module_/g_m_/g;
+ $componentname =~ s/_optional_/_o_/g;
+ $componentname =~ s/_javafilter_/_jf_/g;
+
+ $componentname = $componentname . "_registry"; # identifying this component as registryitem component
+
+ # This componentname must be more specific
+ my $addon = "_";
+ if ( $allvariables->{'PRODUCTNAME'} ) { $addon = $addon . $allvariables->{'PRODUCTNAME'}; }
+ if ( $allvariables->{'PRODUCTVERSION'} ) { $addon = $addon . $allvariables->{'PRODUCTVERSION'}; }
+ $addon = lc($addon);
+ $addon =~ s/ //g;
+ $addon =~ s/-//g;
+ $addon =~ s/\.//g;
+
+ my $styles = "";
+ if ( $registryref->{'Styles'} ) { $styles = $registryref->{'Styles'}; }
+
+ # Layer links must have unique Component GUID for all products. This is necessary, because only the
+ # uninstallation of the last product has to delete registry keys.
+ if ( $styles =~ /\bLAYER_REGISTRY\b/ )
+ {
+ $componentname = "g_m_root_registry_layer_ooo_reglayer";
+ # Styles USE_URELAYERVERSION, USE_OOOBASEVERSION
+ if ( $styles =~ /\bUSE_URELAYERVERSION\b/ ) { $addon = "_ure_" . $allvariables->{'URELAYERVERSION'}; }
+ if ( $styles =~ /\bUSE_OOOBASEVERSION\b/ ) { $addon = "_basis_" . $allvariables->{'OOOBASEVERSION'}; }
+ $addon =~ s/\.//g;
+ }
+
+ $componentname = $componentname . $addon;
+
+ if (( $styles =~ /\bLANGUAGEPACK\b/ ) && ( $installer::globals::languagepack )) { $componentname = $componentname . "_lang"; }
+ if ( $styles =~ /\bALWAYS_REQUIRED\b/ ) { $componentname = $componentname . "_forced"; }
+
+ if ( $isrootmodule ) { $installer::globals::registryrootcomponent = $componentname; }
+
+ return $componentname;
+}
+
+##############################################################
+# Returning identifier for registry table.
+##############################################################
+
+sub get_registry_identifier
+{
+ my ($registry) = @_;
+
+ my $identifier = "";
+
+ if ( $registry->{'gid'} ) { $identifier = $registry->{'gid'}; }
+
+ $identifier = lc($identifier); # always lower case
+
+ # Attention: Maximum length is 72
+
+ $identifier =~ s/gid_regitem_/g_r_/;
+ $identifier =~ s/_soffice_/_s_/;
+ $identifier =~ s/_clsid_/_c_/;
+ $identifier =~ s/_currentversion_/_cv_/;
+ $identifier =~ s/_microsoft_/_ms_/;
+ $identifier =~ s/_staroffice_/_so_/;
+ $identifier =~ s/_classpath_/_cp_/;
+ $identifier =~ s/__/_/g;
+
+ # Saving this in the registry collector
+
+ $registry->{'uniquename'} = $identifier;
+
+ return $identifier;
+}
+
+##################################################################
+# Returning root value for registry table.
+##################################################################
+
+sub get_registry_root
+{
+ my ($registry) = @_;
+
+ my $rootvalue = 0; # Default: Parent is KKEY_CLASSES_ROOT
+ my $scproot = "";
+
+ if ( $registry->{'ParentID'} ) { $scproot = $registry->{'ParentID'}; }
+
+ if ( $scproot eq "PREDEFINED_HKEY_LOCAL_MACHINE" ) { $rootvalue = -1; }
+
+ if ( $scproot eq "PREDEFINED_HKEY_CLASSES_ROOT" ) { $rootvalue = 0; }
+
+ if ( $scproot eq "PREDEFINED_HKEY_CURRENT_USER_ONLY" ) { $rootvalue = 1; }
+
+ if ( $scproot eq "PREDEFINED_HKEY_LOCAL_MACHINE_ONLY" ) { $rootvalue = 2; }
+
+ return $rootvalue;
+}
+
+##############################################################
+# Returning key for registry table.
+##############################################################
+
+sub get_registry_key
+{
+ my ($registry, $allvariableshashref) = @_;
+
+ my $key = "";
+
+ if ( $registry->{'Subkey'} ) { $key = $registry->{'Subkey'}; }
+
+ if ( $key =~ /\%/ ) { $key = installer::worker::replace_variables_in_string($key, $allvariableshashref); }
+
+ return $key;
+}
+
+##############################################################
+# Returning name for registry table.
+##############################################################
+
+sub get_registry_name
+{
+ my ($registry, $allvariableshashref) = @_;
+
+ my $name = "";
+
+ if ( $registry->{'Name'} ) { $name = $registry->{'Name'}; }
+
+ if ( $name =~ /\%/ ) { $name = installer::worker::replace_variables_in_string($name, $allvariableshashref); }
+
+ return $name;
+}
+
+##############################################################
+# Returning value for registry table.
+##############################################################
+
+sub get_registry_value
+{
+ my ($registry, $allvariableshashref) = @_;
+
+ my $value = "";
+
+ if ( $registry->{'Value'} ) { $value = $registry->{'Value'}; }
+
+ $value =~ s/\\\"/\"/g; # no more masquerading of '"'
+ $value =~ s/\\\\\s*$/\\/g; # making "\\" at end of value to "\"
+ $value =~ s/\<progpath\>/\[INSTALLLOCATION\]/;
+ $value =~ s/\[INSTALLLOCATION\]\\/\[INSTALLLOCATION\]/; # removing "\" after "[INSTALLLOCATION]"
+
+ if ( $value =~ /\%/ ) { $value = installer::worker::replace_variables_in_string($value, $allvariableshashref); }
+
+ return $value;
+}
+
+##############################################################
+# Returning 64 bit value for registry table.
+##############################################################
+
+sub get_registry_val64
+{
+ my ($registry, $allvariableshashref) = @_;
+
+ my $value = "";
+
+ if ( $registry->{'Val64'} ) { $value = $registry->{'Val64'}; }
+
+ $value =~ s/\\\"/\"/g; # no more masquerading of '"'
+ $value =~ s/\\\\\s*$/\\/g; # making "\\" at end of value to "\"
+ $value =~ s/\<progpath\>/\[INSTALLLOCATION\]/;
+ $value =~ s/\[INSTALLLOCATION\]\\/\[INSTALLLOCATION\]/; # removing "\" after "[INSTALLLOCATION]"
+
+ if ( $value =~ /\%/ ) { $value = installer::worker::replace_variables_in_string($value, $allvariableshashref); }
+
+ return $value;
+}
+
+##############################################################
+# Returning component for registry table.
+##############################################################
+
+sub get_registry_component
+{
+ my ($registry, $allvariables) = @_;
+
+ # All registry items belonging to one module can
+ # be included into one component
+
+ my $componentname = get_registry_component_name($registry, $allvariables);
+
+ # saving componentname in the registryitem collector
+
+ $registry->{'componentname'} = $componentname;
+
+ return $componentname;
+}
+
+######################################################
+# Adding the content of
+# @installer::globals::userregistrycollector
+# to the registry table. The content was collected
+# in create_files_table() in file.pm.
+######################################################
+
+sub add_userregs_to_registry_table
+{
+ my ( $registrytable, $allvariables ) = @_;
+
+ for ( my $i = 0; $i <= $#installer::globals::userregistrycollector; $i++ )
+ {
+ my $onefile = $installer::globals::userregistrycollector[$i];
+
+ my $styles = "";
+ if ( $onefile->{'Styles'} ) { $styles = $onefile->{'Styles'}; }
+
+ my %registry = ();
+
+ $registry{'Registry'} = $onefile->{'userregkeypath'};
+ $registry{'Root'} = "1"; # always HKCU
+ $registry{'Key'} = "Software\\$allvariables->{'MANUFACTURER'}\\$allvariables->{'PRODUCTNAME'} $allvariables->{'PRODUCTVERSION'}\\";
+ if ( $onefile->{'needs_user_registry_key'} ) { $registry{'Key'} = $registry{'Key'} . "StartMenu"; }
+ else { $registry{'Key'} = $registry{'Key'} . "ShellNew"; }
+ $registry{'Name'} = $onefile->{'Name'};
+ $registry{'Value'} = "1";
+ $registry{'Component_'} = $onefile->{'componentname'};
+
+ my $oneline = $registry{'Registry'} . "\t" . $registry{'Root'} . "\t" . $registry{'Key'} . "\t"
+ . $registry{'Name'} . "\t" . $registry{'Value'} . "\t" . $registry{'Component_'} . "\n";
+
+ push(@{$registrytable}, $oneline);
+ }
+}
+
+######################################################
+# Creating the file Registry.idt dynamically
+# Content:
+# Registry Root Key Name Value Component_
+######################################################
+
+sub create_registry_table
+{
+ my ($registryref, $allregistrycomponentsref, $basedir, $languagesarrayref, $allvariableshashref) = @_;
+
+ for ( my $m = 0; $m <= $#{$languagesarrayref}; $m++ )
+ {
+ my $onelanguage = ${$languagesarrayref}[$m];
+
+ my @registrytable = ();
+ my @reg64table = ();
+
+ installer::windows::idtglobal::write_idt_header(\@registrytable, "registry");
+ installer::windows::idtglobal::write_idt_header(\@reg64table, "reg64");
+
+ for ( my $i = 0; $i <= $#{$registryref}; $i++ )
+ {
+ my $oneregistry = ${$registryref}[$i];
+
+ # Controlling the language!
+ # Only language independent folderitems or folderitems with the correct language
+ # will be included into the table
+
+ if (! (!(( $oneregistry->{'ismultilingual'} )) || ( $oneregistry->{'specificlanguage'} eq $onelanguage )) ) { next; }
+
+ my %registry = ();
+
+ $registry{'Registry'} = get_registry_identifier($oneregistry);
+ $registry{'Root'} = get_registry_root($oneregistry);
+ $registry{'Key'} = get_registry_key($oneregistry, $allvariableshashref);
+ $registry{'Name'} = get_registry_name($oneregistry, $allvariableshashref);
+ $registry{'Value'} = get_registry_value($oneregistry, $allvariableshashref);
+ $registry{'Val64'} = get_registry_val64($oneregistry, $allvariableshashref);
+ $registry{'Component_'} = get_registry_component($oneregistry, $allvariableshashref);
+
+ # Collecting all components
+ if (!(installer::existence::exists_in_array($registry{'Component_'}, $allregistrycomponentsref)))
+ {
+ push(@{$allregistrycomponentsref}, $registry{'Component_'});
+ }
+
+ # Collecting all components with DONT_DELETE style
+ my $style = "";
+ if ( $oneregistry->{'Styles'} ) { $style = $oneregistry->{'Styles'}; }
+ if ( $style =~ /\bDONT_DELETE\b/ ) { $installer::globals::dontdeletecomponents{$registry{'Component_'}} = 1; }
+
+ # Saving upgradekey to write this into setup.ini for minor upgrades
+ if ( $style =~ /\bUPGRADEKEY\b/ ) { $installer::globals::minorupgradekey = $registry{'Key'}; }
+
+ # Collecting all registry components with ALWAYS_REQUIRED style
+ if ( ! ( $style =~ /\bALWAYS_REQUIRED\b/ ))
+ {
+ # Setting a component condition for unforced registry components!
+ # Only write into registry, if WRITE_REGISTRY is set.
+ if ( $oneregistry->{'ComponentCondition'} ) { $oneregistry->{'ComponentCondition'} = "(" . $oneregistry->{'ComponentCondition'} . ") AND (WRITE_REGISTRY=1)"; }
+ else { $oneregistry->{'ComponentCondition'} = "WRITE_REGISTRY=1"; }
+ }
+
+ # Collecting all component conditions
+ if ( $oneregistry->{'ComponentCondition'} )
+ {
+ if ( ! exists($installer::globals::componentcondition{$registry{'Component_'}}))
+ {
+ $installer::globals::componentcondition{$registry{'Component_'}} = $oneregistry->{'ComponentCondition'};
+ }
+ }
+
+ my $oneline = $registry{'Registry'} . "\t" . $registry{'Root'} . "\t" . $registry{'Key'} . "\t"
+ . $registry{'Name'} . "\t" . $registry{'Value'} . "\t" . $registry{'Component_'} . "\n";
+
+ my $oneline64 = $registry{'Registry'} . "\t" . $registry{'Root'} . "\t" . $registry{'Key'} . "\t"
+ . $registry{'Name'} . "\t" . $registry{'Val64'} . "\t" . $registry{'Component_'} . "\n";
+
+ if ( ! ( $style =~ /\bX64_ONLY\b/ )) { push(@registrytable, $oneline); } # standard registry table for 32 Bit
+ if (( $style =~ /\bX64\b/ ) || ( $style =~ /\bX64_ONLY\b/ )) { push(@reg64table , $oneline64); }
+ }
+
+ # If there are added user registry keys for files collected in
+ # @installer::globals::userregistrycollector (file.pm), then
+ # this registry keys have to be added now. This is necessary for
+ # files in PREDEFINED_OSSHELLNEWDIR, because their component
+ # needs as KeyPath a RegistryItem in HKCU.
+
+ if ( $installer::globals::addeduserregitrykeys ) { add_userregs_to_registry_table(\@registrytable, $allvariableshashref); }
+
+ # Saving the file
+
+ my $registrytablename = $basedir . $installer::globals::separator . "Registry.idt" . "." . $onelanguage;
+ installer::files::save_file($registrytablename ,\@registrytable);
+ my $infoline = "Created idt file: $registrytablename\n";
+ push(@installer::globals::logfileinfo, $infoline);
+
+ $registrytablename = $basedir . $installer::globals::separator . "Reg64.idt" . "." . $onelanguage;
+ installer::files::save_file($registrytablename ,\@reg64table );
+ $infoline = "Created idt file: $registrytablename\n";
+ push(@installer::globals::logfileinfo, $infoline);
+ }
+}
+
+1;
diff --git a/solenv/bin/modules/installer/windows/removefile.pm b/solenv/bin/modules/installer/windows/removefile.pm
new file mode 100644
index 000000000000..ec2a6142db71
--- /dev/null
+++ b/solenv/bin/modules/installer/windows/removefile.pm
@@ -0,0 +1,152 @@
+#*************************************************************************
+#
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+#
+# Copyright 2000, 2010 Oracle and/or its affiliates.
+#
+# OpenOffice.org - a multi-platform office productivity suite
+#
+# This file is part of OpenOffice.org.
+#
+# OpenOffice.org is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License version 3
+# only, as published by the Free Software Foundation.
+#
+# OpenOffice.org is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License version 3 for more details
+# (a copy is included in the LICENSE file that accompanied this code).
+#
+# You should have received a copy of the GNU Lesser General Public License
+# version 3 along with OpenOffice.org. If not, see
+# <http://www.openoffice.org/license.html>
+# for a copy of the LGPLv3 License.
+#
+#*************************************************************************
+
+package installer::windows::removefile;
+
+use installer::files;
+use installer::globals;
+use installer::windows::idtglobal;
+
+########################################################################
+# Returning the FileKey for a folderitem for removefile table.
+########################################################################
+
+sub get_removefile_filekey
+{
+ my ($folderitem) = @_;
+
+ # returning the unique identifier
+
+ my $identifier = "remove_" . $folderitem->{'directory'};
+
+ $identifier = lc($identifier);
+
+ return $identifier;
+}
+
+########################################################################
+# Returning the Component for a folderitem for removefile table.
+########################################################################
+
+sub get_removefile_component
+{
+ my ($folderitem) = @_;
+
+ return $folderitem->{'component'};
+}
+
+########################################################################
+# Returning the FileName for a folderitem for removefile table.
+########################################################################
+
+sub get_removefile_filename
+{
+ my ($folderitem) = @_;
+
+ # return nothing: The assigned directory will be removed
+
+ return "";
+}
+
+########################################################################
+# Returning the DirProperty for a folderitem for removefile table.
+########################################################################
+
+sub get_removefile_dirproperty
+{
+ my ($folderitem) = @_;
+
+ return $folderitem->{'directory'};
+}
+
+########################################################################
+# Returning the InstallMode for a folderitem for removefile table.
+########################################################################
+
+sub get_removefile_installmode
+{
+ my ($folderitem) = @_;
+
+ # always returning "2": The file is only removed, if the assigned
+ # component is removed. Name: msidbRemoveFileInstallModeOnRemove
+
+ return 2;
+}
+
+###########################################################################################################
+# Creating the file RemoveFi.idt dynamically
+# Content:
+# FileKey Component_ FileName DirProperty InstallMode
+###########################################################################################################
+
+sub create_removefile_table
+{
+ my ($folderitemsref, $basedir) = @_;
+
+ my @removefiletable = ();
+
+ installer::windows::idtglobal::write_idt_header(\@removefiletable, "removefile");
+
+ # Only the directories created for the FolderItems have to be deleted
+ # with the information in the table RemoveFile
+
+ my @directorycollector = ();
+
+ for ( my $i = 0; $i <= $#{$folderitemsref}; $i++ )
+ {
+ my $onelink = ${$folderitemsref}[$i];
+
+ if ( $onelink->{'used'} == 0 ) { next; }
+
+ if ( installer::existence::exists_in_array($onelink->{'directory'}, \@directorycollector)) { next; }
+
+ push(@directorycollector, $onelink->{'directory'});
+
+ my %removefile = ();
+
+ $removefile{'FileKey'} = get_removefile_filekey($onelink);
+ $removefile{'Component_'} = get_removefile_component($onelink);
+ $removefile{'FileName'} = get_removefile_filename($onelink);
+ $removefile{'DirProperty'} = get_removefile_dirproperty($onelink);
+ $removefile{'InstallMode'} = get_removefile_installmode($onelink);
+
+ my $oneline = $removefile{'FileKey'} . "\t" . $removefile{'Component_'} . "\t" . $removefile{'FileName'} . "\t"
+ . $removefile{'DirProperty'} . "\t" . $removefile{'InstallMode'} . "\n";
+
+ push(@removefiletable, $oneline);
+ }
+
+ # Saving the file
+
+ my $removefiletablename = $basedir . $installer::globals::separator . "RemoveFi.idt";
+ installer::files::save_file($removefiletablename ,\@removefiletable);
+ my $infoline = "Created idt file: $removefiletablename\n";
+ push(@installer::globals::logfileinfo, $infoline);
+
+}
+
+1; \ No newline at end of file
diff --git a/solenv/bin/modules/installer/windows/selfreg.pm b/solenv/bin/modules/installer/windows/selfreg.pm
new file mode 100644
index 000000000000..70dffc3ea1af
--- /dev/null
+++ b/solenv/bin/modules/installer/windows/selfreg.pm
@@ -0,0 +1,88 @@
+#*************************************************************************
+#
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+#
+# Copyright 2000, 2010 Oracle and/or its affiliates.
+#
+# OpenOffice.org - a multi-platform office productivity suite
+#
+# This file is part of OpenOffice.org.
+#
+# OpenOffice.org is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License version 3
+# only, as published by the Free Software Foundation.
+#
+# OpenOffice.org is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License version 3 for more details
+# (a copy is included in the LICENSE file that accompanied this code).
+#
+# You should have received a copy of the GNU Lesser General Public License
+# version 3 along with OpenOffice.org. If not, see
+# <http://www.openoffice.org/license.html>
+# for a copy of the LGPLv3 License.
+#
+#*************************************************************************
+
+package installer::windows::selfreg;
+
+use installer::exiter;
+use installer::files;
+use installer::globals;
+use installer::worker;
+use installer::windows::idtglobal;
+
+##############################################################
+# Returning the cost for the selfreg table.
+##############################################################
+
+sub get_selfreg_cost
+{
+ my ( $onefile ) = @_;
+
+ return "0";
+}
+
+####################################################################################
+# Creating the file SelfReg.idt dynamically
+# Content:
+# File_ Cost
+# UpgradeCode VersionMin VersionMax Language Attributes Remove ActionProperty
+####################################################################################
+
+sub create_selfreg_table
+{
+ my ($filesref, $basedir) = @_;
+
+ my @selfregtable = ();
+
+ installer::windows::idtglobal::write_idt_header(\@selfregtable, "selfreg");
+
+ # Registering all libraries with flag "SELFREG"
+
+ my $selfregfiles = installer::worker::collect_all_items_with_special_flag($filesref, "SELFREG");
+
+ for ( my $i = 0; $i <= $#{$selfregfiles}; $i++ )
+ {
+ my $onefile = ${$selfregfiles}[$i];
+
+ my %selfreg = ();
+
+ $selfreg{'File_'} = $onefile->{'uniquename'};
+ $selfreg{'Cost'} = get_selfreg_cost($onefile);
+
+ my $oneline = $selfreg{'File_'} . "\t" . $selfreg{'Cost'} . "\n";
+
+ push(@selfregtable, $oneline);
+ }
+
+ # Saving the file
+
+ my $selfregtablename = $basedir . $installer::globals::separator . "SelfReg.idt";
+ installer::files::save_file($selfregtablename ,\@selfregtable);
+ my $infoline = "Created idt file: $selfregtablename\n";
+ push(@installer::globals::logfileinfo, $infoline);
+}
+
+1; \ No newline at end of file
diff --git a/solenv/bin/modules/installer/windows/shortcut.pm b/solenv/bin/modules/installer/windows/shortcut.pm
new file mode 100644
index 000000000000..76039baf10c9
--- /dev/null
+++ b/solenv/bin/modules/installer/windows/shortcut.pm
@@ -0,0 +1,697 @@
+#*************************************************************************
+#
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+#
+# Copyright 2000, 2010 Oracle and/or its affiliates.
+#
+# OpenOffice.org - a multi-platform office productivity suite
+#
+# This file is part of OpenOffice.org.
+#
+# OpenOffice.org is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License version 3
+# only, as published by the Free Software Foundation.
+#
+# OpenOffice.org is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License version 3 for more details
+# (a copy is included in the LICENSE file that accompanied this code).
+#
+# You should have received a copy of the GNU Lesser General Public License
+# version 3 along with OpenOffice.org. If not, see
+# <http://www.openoffice.org/license.html>
+# for a copy of the LGPLv3 License.
+#
+#*************************************************************************
+
+package installer::windows::shortcut;
+
+use installer::existence;
+use installer::exiter;
+use installer::files;
+use installer::globals;
+use installer::windows::idtglobal;
+
+##############################################################
+# Returning the file object for the msiassembly table.
+##############################################################
+
+sub get_file_by_name
+{
+ my ( $filesref, $filename ) = @_;
+
+ my $foundfile = 0;
+ my $onefile;
+
+ for ( my $i = 0; $i <= $#{$filesref}; $i++ )
+ {
+ $onefile = ${$filesref}[$i];
+ my $name = $onefile->{'Name'};
+
+ if ( $name eq $filename )
+ {
+ $foundfile = 1;
+ last;
+ }
+ }
+
+ if (! $foundfile ) { $onefile = ""; }
+
+ return $onefile;
+}
+
+##############################################################
+# Returning identifier for shortcut table.
+##############################################################
+
+sub get_shortcut_identifier
+{
+ my ($shortcut) = @_;
+
+ my $identifier = $shortcut->{'gid'};
+
+ return $identifier;
+}
+
+##############################################################
+# Returning directory for shortcut table.
+##############################################################
+
+sub get_shortcut_directory
+{
+ my ($shortcut, $dirref) = @_;
+
+ # For shortcuts it is easy to convert the gid_Dir_Abc into the unique name in
+ # the directory table, for instance help_en_simpressidx.
+ # For files (components) this is not so easy, because files can be included
+ # in zip files with subdirectories that are not defined in scp.
+
+ my $onedir;
+ my $shortcutdirectory = $shortcut->{'Dir'};
+ my $directory = "";
+ my $found = 0;
+
+ for ( my $i = 0; $i <= $#{$dirref}; $i++ )
+ {
+ $onedir = ${$dirref}[$i];
+ my $directorygid = $onedir->{'Dir'};
+
+ if ( $directorygid eq $shortcutdirectory )
+ {
+ $found = 1;
+ last;
+ }
+ }
+
+ if (!($found))
+ {
+ installer::exiter::exit_program("ERROR: Did not find DirectoryID $shortcutdirectory in directory collection for shortcut", "get_shortcut_directory");
+ }
+
+ $directory = $onedir->{'uniquename'};
+
+ if ($directory eq "") { $directory = "INSTALLLOCATION"; } # Shortcuts in the root directory
+
+ return $directory;
+}
+
+##############################################################
+# Returning name for shortcut table.
+##############################################################
+
+sub get_shortcut_name
+{
+ my ($shortcut, $shortnamesref, $onelanguage) = @_;
+
+ my $returnstring;
+
+ my $name = $shortcut->{'Name'};
+
+ my $shortstring = installer::windows::idtglobal::make_eight_three_conform($name, "shortcut", $shortnamesref);
+ $shortstring =~ s/\s/\_/g; # replacing white spaces with underline
+
+ if ( $shortstring eq $name ) { $returnstring = $name; } # nothing changed
+ else {$returnstring = $shortstring . "\|" . $name; }
+
+ return $returnstring;
+}
+
+##############################################################
+# Returning component for shortcut table.
+##############################################################
+
+sub get_shortcut_component
+{
+ my ($shortcut, $filesref) = @_;
+
+ my $onefile;
+ my $component = "";
+ my $found = 0;
+ my $shortcut_fileid = $shortcut->{'FileID'};
+
+ my $absolute_filename = 0;
+ if ( $shortcut->{'Styles'} ) { $styles = $shortcut->{'Styles'}; }
+ if ( $styles =~ /\bABSOLUTE_FILENAME\b/ ) { $absolute_filename = 1; } # FileID contains an absolute filename
+ if ( $styles =~ /\bUSE_HELPER_FILENAME\b/ ) { $absolute_filename = 1; } # ComponentIDFile contains id of a helper file
+
+ # if the FileID contains an absolute filename, therefore the entry for "ComponentIDFile" has to be used.
+ if ( $absolute_filename ) { $shortcut_fileid = $shortcut->{'ComponentIDFile'}; }
+
+ for ( my $i = 0; $i <= $#{$filesref}; $i++ )
+ {
+ $onefile = ${$filesref}[$i];
+ my $filegid = $onefile->{'gid'};
+
+ if ( $filegid eq $shortcut_fileid )
+ {
+ $found = 1;
+ last;
+ }
+ }
+
+ if (!($found))
+ {
+ installer::exiter::exit_program("ERROR: Did not find FileID $shortcut_fileid in file collection for shortcut", "get_shortcut_component");
+ }
+
+ $component = $onefile->{'componentname'};
+
+ # finally saving the componentname in the folderitem collector
+
+ $shortcut->{'component'} = $component;
+
+ return $component;
+}
+
+##############################################################
+# Returning target for shortcut table.
+##############################################################
+
+sub get_shortcut_target
+{
+ my ($shortcut, $filesref) = @_;
+
+ my $target = "";
+ my $found = 0;
+ my $shortcut_fileid = $shortcut->{'FileID'};
+ my $onefile;
+
+ for ( my $i = 0; $i <= $#{$filesref}; $i++ )
+ {
+ $onefile = ${$filesref}[$i];
+ my $filegid = $onefile->{'gid'};
+
+ if ( $filegid eq $shortcut_fileid )
+ {
+ $found = 1;
+ last;
+ }
+ }
+
+ if (!($found))
+ {
+ installer::exiter::exit_program("ERROR: Did not find FileID $shortcut_fileid in file collection for shortcut", "get_shortcut_target");
+ }
+
+ if ( $onefile->{'Name'} )
+ {
+ $target = $onefile->{'Name'};
+ }
+
+ $target = "\[\#" . $target . "\]"; # format for Non-Advertised shortcuts
+
+ return $target;
+}
+
+##############################################################
+# Returning arguments for shortcut table.
+##############################################################
+
+sub get_shortcut_arguments
+{
+ my ($shortcut) = @_;
+
+ return "";
+}
+
+##############################################################
+# Returning the localized description for shortcut table.
+##############################################################
+
+sub get_shortcut_description
+{
+ my ($shortcut, $onelanguage) = @_;
+
+ my $description = "";
+ if ( $shortcut->{'Tooltip'} ) { $description = $shortcut->{'Tooltip'}; }
+
+ return $description;
+}
+
+##############################################################
+# Returning hotkey for shortcut table.
+##############################################################
+
+sub get_shortcut_hotkey
+{
+ my ($shortcut) = @_;
+
+ return "";
+}
+
+##############################################################
+# Returning icon for shortcut table.
+##############################################################
+
+sub get_shortcut_icon
+{
+ my ($shortcut) = @_;
+
+ return "";
+}
+
+##############################################################
+# Returning iconindex for shortcut table.
+##############################################################
+
+sub get_shortcut_iconindex
+{
+ my ($shortcut) = @_;
+
+ return "";
+}
+
+##############################################################
+# Returning show command for shortcut table.
+##############################################################
+
+sub get_shortcut_showcmd
+{
+ my ($shortcut) = @_;
+
+ return "";
+}
+
+##############################################################
+# Returning working directory for shortcut table.
+##############################################################
+
+sub get_shortcut_wkdir
+{
+ my ($shortcut) = @_;
+
+ return "";
+}
+
+####################################################################
+# Returning working directory for shortcut table for FolderItems.
+####################################################################
+
+sub get_folderitem_wkdir
+{
+ my ($onelink, $dirref) = @_;
+
+ # For shortcuts it is easy to convert the gid_Dir_Abc into the unique name in
+ # the directory table, for instance help_en_simpressidx.
+
+ my $onedir;
+ my $workingdirectory = "";
+ if ( $onelink->{'WkDir'} ) { $workingdirectory = $onelink->{'WkDir'}; }
+ my $directory = "";
+
+ if ( $workingdirectory )
+ {
+ my $found = 0;
+
+ for ( my $i = 0; $i <= $#{$dirref}; $i++ )
+ {
+ $onedir = ${$dirref}[$i];
+ my $directorygid = $onedir->{'Dir'};
+
+ if ( $directorygid eq $workingdirectory )
+ {
+ $found = 1;
+ last;
+ }
+ }
+
+ if (!($found))
+ {
+ installer::exiter::exit_program("ERROR: Did not find DirectoryID $workingdirectory in directory collection for FolderItem", "get_folderitem_wkdir");
+ }
+
+ $directory = $onedir->{'uniquename'};
+
+ if ($directory eq "") { $directory = "INSTALLLOCATION"; }
+ }
+
+ return $directory;
+}
+
+###################################################################
+# Returning the directory for a folderitem for shortcut table.
+###################################################################
+
+sub get_folderitem_directory
+{
+ my ($shortcut) = @_;
+
+ # my $directory = "$installer::globals::programmenufolder"; # default
+ my $directory = "$installer::globals::officemenufolder"; # default
+
+ # The value $installer::globals::programmenufolder is not correct for the
+ # PREDEFINED folders, like PREDEFINED_AUTOSTART
+
+ if ( $shortcut->{'FolderID'} eq "PREDEFINED_AUTOSTART" )
+ {
+ $directory = $installer::globals::startupfolder;
+ }
+
+ if ( $shortcut->{'FolderID'} eq "PREDEFINED_DESKTOP" )
+ {
+ $directory = $installer::globals::desktopfolder;
+ $installer::globals::desktoplinkexists = 1;
+ }
+
+ if ( $shortcut->{'FolderID'} eq "PREDEFINED_STARTMENU" )
+ {
+ $directory = $installer::globals::startmenufolder;
+ }
+
+ # saving the directory in the folderitems collector
+
+ $shortcut->{'directory'} = $directory;
+
+ return $directory;
+}
+
+########################################################################
+# Returning the target (feature) for a folderitem for shortcut table.
+# For non-advertised shortcuts this is a formatted string.
+########################################################################
+
+sub get_folderitem_target
+{
+ my ($shortcut, $filesref) = @_;
+
+ my $onefile;
+ my $target = "";
+ my $found = 0;
+ my $shortcut_fileid = $shortcut->{'FileID'};
+
+ my $styles = "";
+ my $nonadvertised = 0;
+ my $absolute_filename = 0;
+ if ( $shortcut->{'Styles'} ) { $styles = $shortcut->{'Styles'}; }
+ if ( $styles =~ /\bNON_ADVERTISED\b/ ) { $nonadvertised = 1; } # this is a non-advertised shortcut
+ if ( $styles =~ /\bABSOLUTE_FILENAME\b/ ) { $absolute_filename = 1; } # FileID contains an absolute filename
+
+ # if the FileID contains an absolute filename this can simply be returned as target for the shortcut table.
+ if ( $absolute_filename )
+ {
+ $shortcut->{'target'} = $shortcut_fileid;
+ return $shortcut_fileid;
+ }
+
+ for ( my $i = 0; $i <= $#{$filesref}; $i++ )
+ {
+ $onefile = ${$filesref}[$i];
+ my $filegid = $onefile->{'gid'};
+
+ if ( $filegid eq $shortcut_fileid )
+ {
+ $found = 1;
+ last;
+ }
+ }
+
+ if (!($found))
+ {
+ installer::exiter::exit_program("ERROR: Did not find FileID $shortcut_fileid in file collection for folderitem", "get_folderitem_target");
+ }
+
+ # Non advertised shortcuts do not return the feature, but the path to the file
+ if ( $nonadvertised )
+ {
+ $target = "\[" . $onefile->{'uniquedirname'} . "\]" . "\\" . $onefile->{'Name'};
+ $shortcut->{'target'} = $target;
+ return $target;
+ }
+
+ # the rest only for advertised shortcuts, which contain the feature in the shortcut table.
+
+ if ( $onefile->{'modules'} ) { $target = $onefile->{'modules'}; }
+
+ # If modules contains a list of modules, only taking the first one.
+ # But this should never be needed
+
+ if ( $target =~ /^\s*(.*?)\,/ ) { $target = $1; }
+
+ # Attention: Maximum feature length is 38!
+ installer::windows::idtglobal::shorten_feature_gid(\$target);
+
+ # and finally saving the target in the folderitems collector
+
+ $shortcut->{'target'} = $target;
+
+ return $target;
+}
+
+########################################################################
+# Returning the arguments for a folderitem for shortcut table.
+########################################################################
+
+sub get_folderitem_arguments
+{
+ my ($shortcut) = @_;
+
+ my $parameter = "";
+
+ if ( $shortcut->{'Parameter'} ) { $parameter = $shortcut->{'Parameter'}; }
+
+ return $parameter;
+}
+
+########################################################################
+# Returning the icon for a folderitem for shortcut table.
+# The returned value has to be defined in the icon table.
+########################################################################
+
+sub get_folderitem_icon
+{
+ my ($shortcut, $filesref, $iconfilecollector) = @_;
+
+ my $styles = "";
+ if ( $shortcut->{'Styles'} ) { $styles = $shortcut->{'Styles'}; }
+ if ( $styles =~ /\bNON_ADVERTISED\b/ ) { return ""; } # no icon for non-advertised shortcuts
+
+ my $iconfilegid = "";
+
+ if ( $shortcut->{'IconFile'} ) { $iconfilegid = $shortcut->{'IconFile'}; }
+ else { $iconfilegid = $shortcut->{'FileID'}; }
+
+ my $onefile;
+ my $found = 0;
+
+ for ( my $i = 0; $i <= $#{$filesref}; $i++ )
+ {
+ $onefile = ${$filesref}[$i];
+ my $filegid = $onefile->{'gid'};
+
+ if ( $filegid eq $iconfilegid )
+ {
+ $found = 1;
+ last;
+ }
+ }
+
+ if (!($found))
+ {
+ installer::exiter::exit_program("ERROR: Did not find FileID $iconfilegid in file collection", "get_folderitem_icon");
+ }
+
+ $iconfile = $onefile->{'Name'};
+
+ # collecting all icon files to copy them into the icon directory
+
+ my $sourcepath = $onefile->{'sourcepath'};
+
+ if (! installer::existence::exists_in_array($sourcepath, $iconfilecollector))
+ {
+ push(@{$iconfilecollector}, $sourcepath);
+ }
+
+ return $iconfile;
+}
+
+########################################################################
+# Returning the iconindex for a folderitem for shortcut table.
+########################################################################
+
+sub get_folderitem_iconindex
+{
+ my ($shortcut) = @_;
+
+ my $styles = "";
+ if ( $shortcut->{'Styles'} ) { $styles = $shortcut->{'Styles'}; }
+ if ( $styles =~ /\bNON_ADVERTISED\b/ ) { return ""; } # no iconindex for non-advertised shortcuts
+
+ my $iconid = 0;
+
+ if ( $shortcut->{'IconID'} ) { $iconid = $shortcut->{'IconID'}; }
+
+ return $iconid;
+}
+
+########################################################################
+# Returning the show command for a folderitem for shortcut table.
+########################################################################
+
+sub get_folderitem_showcmd
+{
+ my ($shortcut) = @_;
+
+ return "1";
+}
+
+###########################################################################################################
+# Creating the file Shortcut.idt dynamically
+# Content:
+# Shortcut Directory_ Name Component_ Target Arguments Description Hotkey Icon_ IconIndex ShowCmd WkDir
+###########################################################################################################
+
+sub create_shortcut_table
+{
+ my ($filesref, $linksref, $folderref, $folderitemsref, $dirref, $basedir, $languagesarrayref, $includepatharrayref, $iconfilecollector) = @_;
+
+ for ( my $m = 0; $m <= $#{$languagesarrayref}; $m++ )
+ {
+ my $onelanguage = ${$languagesarrayref}[$m];
+
+ my @shortcuttable = ();
+
+ my @shortnames = (); # to collect all short names
+
+ installer::windows::idtglobal::write_idt_header(\@shortcuttable, "shortcut");
+
+ # First the links, defined in scp as ShortCut
+
+ for ( my $i = 0; $i <= $#{$linksref}; $i++ )
+ {
+ my $onelink = ${$linksref}[$i];
+
+ # Controlling the language!
+ # Only language independent folderitems or folderitems with the correct language
+ # will be included into the table
+
+ if (! (!(( $onelink->{'ismultilingual'} )) || ( $onelink->{'specificlanguage'} eq $onelanguage )) ) { next; }
+
+ my %shortcut = ();
+
+ $shortcut{'Shortcut'} = get_shortcut_identifier($onelink);
+ $shortcut{'Directory_'} = get_shortcut_directory($onelink, $dirref);
+ $shortcut{'Name'} = get_shortcut_name($onelink, \@shortnames, $onelanguage); # localized name
+ $shortcut{'Component_'} = get_shortcut_component($onelink, $filesref);
+ $shortcut{'Target'} = get_shortcut_target($onelink, $filesref);
+ $shortcut{'Arguments'} = get_shortcut_arguments($onelink);
+ $shortcut{'Description'} = get_shortcut_description($onelink, $onelanguage); # localized description
+ $shortcut{'Hotkey'} = get_shortcut_hotkey($onelink);
+ $shortcut{'Icon_'} = get_shortcut_icon($onelink);
+ $shortcut{'IconIndex'} = get_shortcut_iconindex($onelink);
+ $shortcut{'ShowCmd'} = get_shortcut_showcmd($onelink);
+ $shortcut{'WkDir'} = get_shortcut_wkdir($onelink);
+
+ my $oneline = $shortcut{'Shortcut'} . "\t" . $shortcut{'Directory_'} . "\t" . $shortcut{'Name'} . "\t"
+ . $shortcut{'Component_'} . "\t" . $shortcut{'Target'} . "\t" . $shortcut{'Arguments'} . "\t"
+ . $shortcut{'Description'} . "\t" . $shortcut{'Hotkey'} . "\t" . $shortcut{'Icon_'} . "\t"
+ . $shortcut{'IconIndex'} . "\t" . $shortcut{'ShowCmd'} . "\t" . $shortcut{'WkDir'} . "\n";
+
+ push(@shortcuttable, $oneline);
+ }
+
+ # Second the entries into the start menu, defined in scp as Folder and Folderitem
+ # These shortcuts will fill the icons table.
+
+ for ( my $i = 0; $i <= $#{$folderref}; $i++ )
+ {
+ my $foldergid = ${$folderref}[$i]->{'gid'};
+
+ # iterating over all folderitems for this folder
+
+ for ( my $j = 0; $j <= $#{$folderitemsref}; $j++ )
+ {
+ my $onelink = ${$folderitemsref}[$j];
+
+ # Controlling the language!
+ # Only language independent folderitems or folderitems with the correct language
+ # will be included into the table
+
+ if (! (!(( $onelink->{'ismultilingual'} )) || ( $onelink->{'specificlanguage'} eq $onelanguage )) ) { next; }
+
+ # controlling the folder
+
+ my $localused = 0;
+
+ if ( $onelink->{'used'} ) { $localused = $onelink->{'used'}; }
+
+ if (!($localused == 1)) { $onelink->{'used'} = "0"; } # no resetting
+
+ if (!( $onelink->{'FolderID'} eq $foldergid )) { next; }
+
+ $onelink->{'used'} = "1";
+
+ my %shortcut = ();
+
+ $shortcut{'Shortcut'} = get_shortcut_identifier($onelink);
+ $shortcut{'Directory_'} = get_folderitem_directory($onelink);
+ $shortcut{'Name'} = get_shortcut_name($onelink, \@shortnames, $onelanguage); # localized name
+ $shortcut{'Component_'} = get_shortcut_component($onelink, $filesref);
+ $shortcut{'Target'} = get_folderitem_target($onelink, $filesref);
+ $shortcut{'Arguments'} = get_folderitem_arguments($onelink);
+ $shortcut{'Description'} = get_shortcut_description($onelink, $onelanguage); # localized description
+ $shortcut{'Hotkey'} = get_shortcut_hotkey($onelink);
+ $shortcut{'Icon_'} = get_folderitem_icon($onelink, $filesref, $iconfilecollector);
+ $shortcut{'IconIndex'} = get_folderitem_iconindex($onelink);
+ $shortcut{'ShowCmd'} = get_folderitem_showcmd($onelink);
+ $shortcut{'WkDir'} = get_folderitem_wkdir($onelink, $dirref);
+
+ my $oneline = $shortcut{'Shortcut'} . "\t" . $shortcut{'Directory_'} . "\t" . $shortcut{'Name'} . "\t"
+ . $shortcut{'Component_'} . "\t" . $shortcut{'Target'} . "\t" . $shortcut{'Arguments'} . "\t"
+ . $shortcut{'Description'} . "\t" . $shortcut{'Hotkey'} . "\t" . $shortcut{'Icon_'} . "\t"
+ . $shortcut{'IconIndex'} . "\t" . $shortcut{'ShowCmd'} . "\t" . $shortcut{'WkDir'} . "\n";
+
+ push(@shortcuttable, $oneline);
+ }
+ }
+
+ # The soffice.ico has to be included into the icon table
+ # as icon for the ARP applet
+
+ my $onefile = "";
+ my $sofficefile = "soffice.ico";
+
+ my $sourcepathref = installer::scriptitems::get_sourcepath_from_filename_and_includepath_classic(\$sofficefile, $includepatharrayref, 0);
+
+ if ($$sourcepathref eq "") { installer::exiter::exit_program("ERROR: Could not find $sofficefile as icon!", "create_shortcut_table"); }
+
+ if (! installer::existence::exists_in_array($$sourcepathref, $iconfilecollector))
+ {
+ unshift(@{$iconfilecollector}, $$sourcepathref);
+ $installer::globals::sofficeiconadded = 1;
+ }
+
+ my $localinfoline = "Added icon file $$sourcepathref for language pack into icon file collector.\n";
+ push(@installer::globals::logfileinfo, $localinfoline);
+
+ # Saving the file
+
+ my $shortcuttablename = $basedir . $installer::globals::separator . "Shortcut.idt" . "." . $onelanguage;
+ installer::files::save_file($shortcuttablename ,\@shortcuttable);
+ my $infoline = "Created idt file: $shortcuttablename\n";
+ push(@installer::globals::logfileinfo, $infoline);
+ }
+}
+
+
+1; \ No newline at end of file
diff --git a/solenv/bin/modules/installer/windows/sign.pm b/solenv/bin/modules/installer/windows/sign.pm
new file mode 100644
index 000000000000..70954bd9f5b1
--- /dev/null
+++ b/solenv/bin/modules/installer/windows/sign.pm
@@ -0,0 +1,1249 @@
+#*************************************************************************
+#
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+#
+# Copyright 2000, 2010 Oracle and/or its affiliates.
+#
+# OpenOffice.org - a multi-platform office productivity suite
+#
+# This file is part of OpenOffice.org.
+#
+# OpenOffice.org is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License version 3
+# only, as published by the Free Software Foundation.
+#
+# OpenOffice.org is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License version 3 for more details
+# (a copy is included in the LICENSE file that accompanied this code).
+#
+# You should have received a copy of the GNU Lesser General Public License
+# version 3 along with OpenOffice.org. If not, see
+# <http://www.openoffice.org/license.html>
+# for a copy of the LGPLv3 License.
+#
+#*************************************************************************
+
+package installer::windows::sign;
+
+use Cwd;
+use installer::converter;
+use installer::existence;
+use installer::files;
+use installer::globals;
+use installer::scriptitems;
+use installer::worker;
+use installer::windows::admin;
+
+########################################################
+# Copying an existing Windows installation set.
+########################################################
+
+sub copy_install_set
+{
+ my ( $installsetpath ) = @_;
+
+ installer::logger::include_header_into_logfile("Start: Copying installation set $installsetpath");
+
+ my $infoline = "";
+
+ my $dirname = $installsetpath;
+ installer::pathanalyzer::make_absolute_filename_to_relative_filename(\$dirname);
+
+ my $path = $installsetpath;
+ installer::pathanalyzer::get_path_from_fullqualifiedname(\$path);
+
+ $path =~ s/\Q$installer::globals::separator\E\s*$//;
+
+ if ( $dirname =~ /\./ ) { $dirname =~ s/\./_signed_inprogress./; }
+ else { $dirname = $dirname . "_signed_inprogress"; }
+
+ my $newpath = $path . $installer::globals::separator . $dirname;
+ my $removepath = $newpath;
+ $removepath =~ s/_inprogress/_witherror/;
+
+ if ( -d $newpath ) { installer::systemactions::remove_complete_directory($newpath, 1); }
+ if ( -d $removepath ) { installer::systemactions::remove_complete_directory($removepath, 1); }
+
+ $infoline = "Copy installation set from $installsetpath to $newpath\n";
+ push( @installer::globals::logfileinfo, $infoline);
+
+ $installsetpath = installer::systemactions::copy_complete_directory($installsetpath, $newpath);
+
+ installer::logger::include_header_into_logfile("End: Copying installation set $installsetpath");
+
+ return $newpath;
+}
+
+########################################################
+# Renaming an existing Windows installation set.
+########################################################
+
+sub rename_install_set
+{
+ my ( $installsetpath ) = @_;
+
+ my $infoline = "";
+
+ my $dirname = $installsetpath;
+ installer::pathanalyzer::make_absolute_filename_to_relative_filename(\$dirname);
+
+ my $path = $installsetpath;
+ installer::pathanalyzer::get_path_from_fullqualifiedname(\$path);
+
+ $path =~ s/\Q$installer::globals::separator\E\s*$//;
+
+ if ( $dirname =~ /\./ ) { $dirname =~ s/\./_inprogress./; }
+ else { $dirname = $dirname . "_inprogress"; }
+
+ my $newpath = $path . $installer::globals::separator . $dirname;
+ my $removepath = $newpath;
+ $removepath =~ s/_inprogress/_witherror/;
+
+ if ( -d $newpath ) { installer::systemactions::remove_complete_directory($newpath, 1); }
+ if ( -d $removepath ) { installer::systemactions::remove_complete_directory($removepath, 1); }
+
+ $installsetpath = installer::systemactions::rename_directory($installsetpath, $newpath);
+
+ return $newpath;
+}
+
+#########################################################
+# Checking the local system
+# Checking existence of needed files in include path
+#########################################################
+
+sub check_system_path
+{
+ # The following files have to be found in the environment variable PATH
+ # Only, if \"-sign\" is used.
+ # Windows : "msicert.exe", "diff.exe", "msidb.exe", "signtool.exe"
+
+ my @needed_files_in_path = ("msicert.exe", "msidb.exe", "signtool.exe", "diff.exe");
+ if ( $installer::globals::internal_cabinet_signing )
+ {
+ push(@needed_files_in_path, "cabarc.exe");
+ push(@needed_files_in_path, "makecab.exe");
+ }
+
+ my $onefile;
+ my $error = 0;
+ my $pathvariable = $ENV{'PATH'};
+ my $local_pathseparator = $installer::globals::pathseparator;
+
+ if( $^O =~ /cygwin/i )
+ { # When using cygwin's perl the PATH variable is POSIX style and ...
+ $pathvariable = qx{cygpath -mp "$pathvariable"} ;
+ # has to be converted to DOS style for further use.
+ $local_pathseparator = ';';
+ }
+
+ my $patharrayref = installer::converter::convert_stringlist_into_array(\$pathvariable, $local_pathseparator);
+
+ $installer::globals::patharray = $patharrayref;
+
+ foreach my $onefile ( @needed_files_in_path )
+ {
+ installer::logger::print_message( "...... searching $onefile ..." );
+
+ my $fileref = installer::scriptitems::get_sourcepath_from_filename_and_includepath_classic(\$onefile, $patharrayref , 0);
+
+ if ( $$fileref eq "" )
+ {
+ $error = 1;
+ installer::logger::print_error( "$onefile not found\n" );
+ }
+ else
+ {
+ installer::logger::print_message( "\tFound: $$fileref\n" );
+ }
+ }
+
+ $installer::globals::signfiles_checked = 1;
+
+ if ( $error ) { installer::exiter::exit_program("ERROR: Could not find all needed files in path!", "check_system_path"); }
+}
+
+######################################################
+# Making systemcall
+######################################################
+
+sub make_systemcall
+{
+ my ($systemcall, $displaysystemcall) = @_;
+
+ installer::logger::print_message( "... $displaysystemcall ...\n" );
+
+ my $success = 1;
+ my $returnvalue = system($systemcall);
+
+ my $infoline = "Systemcall: $displaysystemcall\n";
+ push( @installer::globals::logfileinfo, $infoline);
+
+ if ($returnvalue)
+ {
+ $infoline = "ERROR: Could not execute \"$displaysystemcall\"!\n";
+ push( @installer::globals::logfileinfo, $infoline);
+ $success = 0;
+ }
+ else
+ {
+ $infoline = "Success: Executed \"$displaysystemcall\" successfully!\n";
+ push( @installer::globals::logfileinfo, $infoline);
+ }
+
+ return $success;
+}
+
+######################################################
+# Making systemcall with warning
+######################################################
+
+sub make_systemcall_with_warning
+{
+ my ($systemcall, $displaysystemcall) = @_;
+
+ installer::logger::print_message( "... $displaysystemcall ...\n" );
+
+ my $success = 1;
+ my $returnvalue = system($systemcall);
+
+ my $infoline = "Systemcall: $displaysystemcall\n";
+ push( @installer::globals::logfileinfo, $infoline);
+
+ if ($returnvalue)
+ {
+ $infoline = "WARNING: Could not execute \"$displaysystemcall\"!\n";
+ push( @installer::globals::logfileinfo, $infoline);
+ $success = 0;
+ }
+ else
+ {
+ $infoline = "Success: Executed \"$displaysystemcall\" successfully!\n";
+ push( @installer::globals::logfileinfo, $infoline);
+ }
+
+ return $success;
+}
+
+######################################################
+# Making systemcall with more return data
+######################################################
+
+sub execute_open_system_call
+{
+ my ( $systemcall ) = @_;
+
+ my @openoutput = ();
+ my $success = 1;
+
+ my $comspec = $ENV{COMSPEC};
+ $comspec = $comspec . " -c ";
+
+ if( $^O =~ /cygwin/i )
+ {
+ # $comspec =~ s/\\/\\\\/g;
+ # $comspec = qx{cygpath -u "$comspec"};
+ # $comspec =~ s/\s*$//g;
+ $comspec = "";
+ }
+
+ my $localsystemcall = "$comspec $systemcall 2>&1 |";
+
+ open( OPN, "$localsystemcall") or warn "Can't execute $localsystemcall\n";
+ while (<OPN>) { push(@openoutput, $_); }
+ close (OPN);
+
+ my $returnvalue = $?; # $? contains the return value of the systemcall
+
+ if ($returnvalue)
+ {
+ $infoline = "ERROR: Could not execute \"$systemcall\"!\n";
+ push( @installer::globals::logfileinfo, $infoline);
+ $success = 0;
+ }
+ else
+ {
+ $infoline = "Success: Executed \"$systemcall\" successfully!\n";
+ push( @installer::globals::logfileinfo, $infoline);
+ }
+
+ return ($success, \@openoutput);
+}
+
+########################################################
+# Reading first line of pw file.
+########################################################
+
+sub get_pw
+{
+ my ( $file ) = @_;
+
+ my $filecontent = installer::files::read_file($file);
+
+ my $pw = ${$filecontent}[0];
+ $pw =~ s/^\s*//;
+ $pw =~ s/\s*$//;
+
+ return $pw;
+}
+
+########################################################
+# Counting the keys of a hash.
+########################################################
+
+sub get_hash_count
+{
+ my ($hashref) = @_;
+
+ my $counter = 0;
+
+ foreach my $key ( keys %{$hashref} ) { $counter++; }
+
+ return $counter;
+}
+
+############################################################
+# Collect all last files in a cabinet file. This is
+# necessary to control, if the cabinet file was damaged
+# by calling signtool.exe.
+############################################################
+
+sub analyze_file_file
+{
+ my ($filecontent) = @_;
+
+ my %filenamehash = ();
+
+ for ( my $i = 0; $i <= $#{$filecontent}; $i++ )
+ {
+ if ( $i < 3 ) { next; }
+
+ if ( ${$filecontent}[$i] =~ /^\s*(.*?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\s*$/ )
+ {
+ my $name = $1;
+ my $sequence = $8;
+
+ $filenamehash{$sequence} = $name;
+ }
+ }
+
+ return ( \%filenamehash );
+}
+
+############################################################
+# Collect all DiskIds to the corresponding cabinet files.
+############################################################
+
+sub analyze_media_file
+{
+ my ($filecontent) = @_;
+
+ my %diskidhash = ();
+ my %lastsequencehash = ();
+
+ for ( my $i = 0; $i <= $#{$filecontent}; $i++ )
+ {
+ if ( $i < 3 ) { next; }
+
+ if ( ${$filecontent}[$i] =~ /^\s*(.*?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\s*$/ )
+ {
+ my $diskid = $1;
+ my $lastsequence = $2;
+ my $cabfile = $4;
+
+ $diskidhash{$cabfile} = $diskid;
+ $lastsequencehash{$cabfile} = $lastsequence;
+ }
+ }
+
+ return ( \%diskidhash, \%lastsequencehash );
+}
+
+########################################################
+# Collect all DiskIds from database table "Media".
+########################################################
+
+sub collect_diskid_from_media_table
+{
+ my ($msidatabase, $languagestring) = @_;
+
+ # creating working directory
+ my $workdir = installer::systemactions::create_directories("media", \$languagestring);
+ installer::windows::admin::extract_tables_from_pcpfile($msidatabase, $workdir, "Media File");
+
+ # Reading tables
+ my $filename = $workdir . $installer::globals::separator . "Media.idt";
+ if ( ! -f $filename ) { installer::exiter::exit_program("ERROR: Could not find required file: $filename !", "collect_diskid_from_media_table"); }
+ my $filecontent = installer::files::read_file($filename);
+ my ( $diskidhash, $lastsequencehash ) = analyze_media_file($filecontent);
+
+ $filename = $workdir . $installer::globals::separator . "File.idt";
+ if ( ! -f $filename ) { installer::exiter::exit_program("ERROR: Could not find required file: $filename !", "collect_diskid_from_media_table"); }
+ $filecontent = installer::files::read_file($filename);
+ my $filenamehash = analyze_file_file($filecontent);
+
+ return ( $diskidhash, $filenamehash, $lastsequencehash );
+}
+
+########################################################
+# Check, if this installation set contains
+# internal cabinet files included into the msi
+# database.
+########################################################
+
+sub check_for_internal_cabfiles
+{
+ my ($cabfilehash) = @_;
+
+ my $contains_internal_cabfiles = 0;
+ my %allcabfileshash = ();
+
+ foreach my $filename ( keys %{$cabfilehash} )
+ {
+ if ( $filename =~ /^\s*\#/ ) # starting with a hash
+ {
+ $contains_internal_cabfiles = 1;
+ # setting real filename without hash as key and name with hash as value
+ my $realfilename = $filename;
+ $realfilename =~ s/^\s*\#//;
+ $allcabfileshash{$realfilename} = $filename;
+ }
+ }
+
+ return ( $contains_internal_cabfiles, \%allcabfileshash );
+}
+
+########################################################
+# Collecting all files in an installation set.
+########################################################
+
+sub analyze_installset_content
+{
+ my ( $installsetpath ) = @_;
+
+ my @sourcefiles = ();
+ my $pathstring = "";
+ installer::systemactions::read_complete_directory($installsetpath, $pathstring, \@sourcefiles);
+
+ if ( ! ( $#sourcefiles > -1 )) { installer::exiter::exit_program("ERROR: No file in installation set. Path: $installsetpath !", "analyze_installset_content"); }
+
+ my %allcabfileshash = ();
+ my %allmsidatabaseshash = ();
+ my %allfileshash = ();
+ my $contains_external_cabfiles = 0;
+ my $msidatabase = "";
+ my $contains_msidatabase = 0;
+
+ for ( my $j = 0; $j <= $#sourcefiles; $j++ )
+ {
+ if ( $sourcefiles[$j] =~ /\.cab\s*$/ ) { $allcabfileshash{$sourcefiles[$j]} = 1; }
+ else
+ {
+ if ( $sourcefiles[$j] =~ /\.txt\s*$/ ) { next; }
+ if ( $sourcefiles[$j] =~ /\.html\s*$/ ) { next; }
+ if ( $sourcefiles[$j] =~ /\.ini\s*$/ ) { next; }
+ if ( $sourcefiles[$j] =~ /\.bmp\s*$/ ) { next; }
+ if ( $sourcefiles[$j] =~ /\.msi\s*$/ )
+ {
+ if ( $msidatabase eq "" ) { $msidatabase = $sourcefiles[$j]; }
+ else { installer::exiter::exit_program("ERROR: There is more than one msi database in installation set. Path: $installsetpath !", "analyze_installset_content"); }
+ }
+ $allfileshash{$sourcefiles[$j]} = 1;
+ }
+ }
+
+ # Is there at least one cab file in the installation set?
+ my $cabcounter = get_hash_count(\%allcabfileshash);
+ if ( $cabcounter > 0 ) { $contains_external_cabfiles = 1; }
+
+ # How about a cab file without a msi database?
+ if (( $cabcounter > 0 ) && ( $msidatabase eq "" )) { installer::exiter::exit_program("ERROR: There is no msi database in the installation set, but an external cabinet file. Path: $installsetpath !", "collect_installset_content"); }
+
+ if ( $msidatabase ne "" ) { $contains_msidatabase = 1; }
+
+ return (\%allcabfileshash, \%allfileshash, $msidatabase, $contains_external_cabfiles, $contains_msidatabase, \@sourcefiles);
+}
+
+########################################################
+# Adding content of external cabinet files into the
+# msi database
+########################################################
+
+sub msicert_database
+{
+ my ($msidatabase, $allcabfiles, $cabfilehash, $internalcabfile) = @_;
+
+ my $fullsuccess = 1;
+
+ foreach my $cabfile ( keys %{$allcabfiles} )
+ {
+ my $origfilesize = -s $cabfile;
+
+ my $mediacabfilename = $cabfile;
+ if ( $internalcabfile ) { $mediacabfilename = "\#" . $mediacabfilename; }
+ if ( ! exists($cabfilehash->{$mediacabfilename}) ) { installer::exiter::exit_program("ERROR: Could not determine DiskId from media table for cabinet file \"$cabfile\" !", "msicert_database"); }
+ my $diskid = $cabfilehash->{$mediacabfilename};
+
+ my $systemcall = "msicert.exe -d $msidatabase -m $diskid -c $cabfile -h";
+ $success = make_systemcall($systemcall, $systemcall);
+ if ( ! $success ) { $fullsuccess = 0; }
+
+ # size of cabinet file must not change
+ my $finalfilesize = -s $cabfile;
+
+ if ( $origfilesize != $finalfilesize ) { installer::exiter::exit_program("ERROR: msicert.exe changed size of cabinet file !", "msicert_database"); }
+ }
+
+ return $fullsuccess;
+}
+
+########################################################
+# Checking if cabinet file was broken by signtool.
+########################################################
+
+sub cabinet_cosistency_check
+{
+ my ( $onefile, $followmeinfohash, $filenamehash, $lastsequencehash, $temppath ) = @_;
+
+ my $infoline = "Making consistency check of $onefile\n";
+ push( @installer::globals::logfileinfo, $infoline);
+ 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;
+ }
+
+ if ( $filenamehash == 0 )
+ {
+ $infoline = "Warning: Stopping consistency check: Important hash of filenames is empty!\n";
+ push( @installer::globals::logfileinfo, $infoline);
+ }
+ elsif ( $lastsequencehash == 0 )
+ {
+ $infoline = "Warning: Stopping consistency check; Important hash of last sequences is empty!\n";
+ push( @installer::globals::logfileinfo, $infoline);
+ }
+ else # both hashes are available
+ {
+ # $onefile contains only the name of the cabinet file without path
+ my $sequence = $lastsequencehash->{$onefile};
+ my $lastfile = $filenamehash->{$sequence};
+ $infoline = "Check of $onefile: Sequence: $sequence is file: $lastfile\n";
+ push( @installer::globals::logfileinfo, $infoline);
+
+ # Therefore the file $lastfile need to be binary compared.
+ # It has to be expanded from the cabinet file
+ # of the original installation set and from the
+ # newly signed cabinet file.
+
+ # How about cabinet files extracted from msi database?
+ my $finalinstalldir = $followmeinfohash->{'finalinstalldir'};
+
+ $finalinstalldir =~ s/\\\s*$//;
+ $finalinstalldir =~ s/\/\s*$//;
+ my $sourcecabfile = $finalinstalldir . $installer::globals::separator . $onefile;
+ my $currentpath = cwd();
+ my $destcabfile = $currentpath . $installer::globals::separator . $onefile;
+ # my $destcabfile = $onefile;
+
+ if ( $^O =~ /cygwin/i )
+ {
+ chomp( $destcabfile = qx{cygpath -w "$destcabfile"} );
+ $destcabfile =~ s/\\/\//g;
+ }
+
+ if ( ! -f $sourcecabfile )
+ {
+ $infoline = "WARNING: Check of cab file cannot happen, because source cabinet file was not found: $sourcecabfile\n";
+ push( @installer::globals::logfileinfo, $infoline);
+ }
+ elsif ( ! -f $destcabfile )
+ {
+ $infoline = "WARNING: Check of cab file cannot happen, because destination cabinet file was not found: $sourcecabfile\n";
+ push( @installer::globals::logfileinfo, $infoline);
+ }
+ else # everything is okay for the check
+ {
+ my $diffpath = get_diff_path($temppath);
+
+ my $origdiffpath = $diffpath . $installer::globals::separator . "orig";
+ my $newdiffpath = $diffpath . $installer::globals::separator . "new";
+
+ if ( ! -d $origdiffpath ) { mkdir($origdiffpath); }
+ if ( ! -d $newdiffpath ) { mkdir($newdiffpath); }
+
+ my $systemcall = "$expandfile $sourcecabfile $origdiffpath -f:$lastfile ";
+ $infoline = $systemcall . "\n";
+ push( @installer::globals::logfileinfo, $infoline);
+
+ my $success = make_systemcall($systemcall, $systemcall);
+ if ( ! $success ) { installer::exiter::exit_program("ERROR: Could not successfully execute: $systemcall !", "cabinet_cosistency_check"); }
+
+ $systemcall = "$expandfile $destcabfile $newdiffpath -f:$lastfile ";
+ $infoline = $systemcall . "\n";
+ push( @installer::globals::logfileinfo, $infoline);
+
+ $success = make_systemcall($systemcall, $systemcall);
+ if ( ! $success ) { installer::exiter::exit_program("ERROR: Could not successfully execute: $systemcall !", "cabinet_cosistency_check"); }
+
+ # and finally the two files can be diffed.
+ my $origfile = $origdiffpath . $installer::globals::separator . $lastfile;
+ my $newfile = $newdiffpath . $installer::globals::separator . $lastfile;
+
+ if ( ! -f $origfile ) { installer::exiter::exit_program("ERROR: Unpacked original file not found: $origfile !", "cabinet_cosistency_check"); }
+ if ( ! -f $newfile ) { installer::exiter::exit_program("ERROR: Unpacked new file not found: $newfile !", "cabinet_cosistency_check"); }
+
+ my $origsize = -s $origfile;
+ my $newsize = -s $newfile;
+
+ if ( $origsize != $newsize ) # This shows an error!
+ {
+ $infoline = "ERROR: Different filesize after signtool.exe was used. Original: $origsize Bytes, new: $newsize. File: $lastfile\n";
+ push( @installer::globals::logfileinfo, $infoline);
+ installer::exiter::exit_program("ERROR: The cabinet file $destcabfile is broken after signtool.exe signed this file !", "cabinet_cosistency_check");
+ }
+ else
+ {
+ $infoline = "Same size of last file in cabinet file after usage of signtool.exe: $newsize (File: $lastfile)\n";
+ push( @installer::globals::logfileinfo, $infoline);
+
+ # Also making a binary diff?
+
+ my $difffile = "diff.exe"; # has to be in the path
+ # $systemcall = "$difffile $sourcecabfile $destcabfile"; # Test for differences
+ $systemcall = "$difffile $origfile $newfile";
+ $infoline = $systemcall . "\n";
+ $returnvalue = make_systemcall($systemcall, $systemcall);
+
+ my $success = $?;
+
+ if ( $success == 0 )
+ {
+ $infoline = "Last files are identical after signing cabinet file (File: $lastfile)\n";
+ push( @installer::globals::logfileinfo, $infoline);
+ }
+ elsif ( $success == 1 )
+ {
+ $infoline = "ERROR: Last files are different after signing cabinet file (File: $lastfile)\n";
+ push( @installer::globals::logfileinfo, $infoline);
+ installer::exiter::exit_program("ERROR: Last files are different after signing cabinet file (File: $lastfile)!", "cabinet_cosistency_check");
+ }
+ else
+ {
+ $infoline = "ERROR: Problem occured calling diff.exe (File: $lastfile)\n";
+ push( @installer::globals::logfileinfo, $infoline);
+ installer::exiter::exit_program("ERROR: Problem occured calling diff.exe (File: $lastfile) !", "cabinet_cosistency_check");
+ }
+ }
+ }
+ }
+
+}
+
+########################################################
+# Signing a list of files
+########################################################
+
+sub sign_files
+{
+ my ( $followmeinfohash, $allfiles, $pw, $cabinternal, $filenamehash, $lastsequencehash, $temppath ) = @_;
+
+ my $infoline = "";
+ my $fullsuccess = 1;
+ my $maxcounter = 3;
+
+ my $productname = "";
+ if ( $followmeinfohash->{'allvariableshash'}->{'PRODUCTNAME'} ) { $productname = "/d " . "\"$followmeinfohash->{'allvariableshash'}->{'PRODUCTNAME'}\""; }
+ my $url = "";
+ if (( ! exists($followmeinfohash->{'allvariableshash'}->{'OPENSOURCE'}) ) || ( $followmeinfohash->{'allvariableshash'}->{'OPENSOURCE'} == 0 )) { $url = "/du " . "\"http://www.sun.com\""; }
+ else { $url = "/du " . "\"http://www.openoffice.org\""; }
+ my $timestampurl = "http://timestamp.verisign.com/scripts/timestamp.dll";
+
+ my $pfxfilepath = $installer::globals::pfxfile;
+
+ if( $^O =~ /cygwin/i )
+ {
+ $pfxfilepath = qx{cygpath -w "$pfxfilepath"};
+ $pfxfilepath =~ s/\\/\\\\/g;
+ $pfxfilepath =~ s/\s*$//g;
+ }
+
+ foreach my $onefile ( reverse sort keys %{$allfiles} )
+ {
+ if ( already_certified($onefile) )
+ {
+ $infoline = "Already certified: Skipping file $onefile\n";
+ push( @installer::globals::logfileinfo, $infoline);
+ next;
+ }
+
+ my $counter = 1;
+ my $success = 0;
+
+ while (( $counter <= $maxcounter ) && ( ! $success ))
+ {
+ if ( $counter > 1 ) { installer::logger::print_message( "\n\n... repeating file $onefile ...\n" ); }
+ if ( $cabinternal ) { installer::logger::print_message(" Signing: $onefile\n"); }
+ my $systemcall = "signtool.exe sign /f \"$pfxfilepath\" /p $pw $productname $url /t \"$timestampurl\" \"$onefile\"";
+ my $displaysystemcall = "signtool.exe sign /f \"$pfxfilepath\" /p ***** $productname $url /t \"$timestampurl\" \"$onefile\"";
+ $success = make_systemcall_with_warning($systemcall, $displaysystemcall);
+ $counter++;
+ }
+
+ # Special check for cabinet files, that sometimes get damaged by signtool.exe
+ if (( $success ) && ( $onefile =~ /\.cab\s*$/ ) && ( ! $cabinternal ))
+ {
+ cabinet_cosistency_check($onefile, $followmeinfohash, $filenamehash, $lastsequencehash, $temppath);
+ }
+
+ if ( ! $success )
+ {
+ $fullsuccess = 0;
+ installer::exiter::exit_program("ERROR: Could not sign file: $onefile!", "sign_files");
+ }
+ }
+
+ return $fullsuccess;
+}
+
+##########################################################################
+# Lines in ddf files must not contain more than 256 characters
+##########################################################################
+
+sub check_ddf_file
+{
+ my ( $ddffile, $ddffilename ) = @_;
+
+ my $maxlength = 0;
+ my $maxline = 0;
+ my $linelength = 0;
+ my $linenumber = 0;
+
+ for ( my $i = 0; $i <= $#{$ddffile}; $i++ )
+ {
+ my $oneline = ${$ddffile}[$i];
+
+ $linelength = length($oneline);
+ $linenumber = $i + 1;
+
+ if ( $linelength > 256 )
+ {
+ installer::exiter::exit_program("ERROR \"$ddffilename\" line $linenumber: Lines in ddf files must not contain more than 256 characters!", "check_ddf_file");
+ }
+
+ if ( $linelength > $maxlength )
+ {
+ $maxlength = $linelength;
+ $maxline = $linenumber;
+ }
+ }
+
+ my $infoline = "Check of ddf file \"$ddffilename\": Maximum length \"$maxlength\" in line \"$maxline\" (allowed line length: 256 characters)\n";
+ push( @installer::globals::logfileinfo, $infoline);
+}
+
+#################################################################
+# Setting the path, where the cab files are unpacked.
+#################################################################
+
+sub get_cab_path
+{
+ my ($temppath) = @_;
+
+ my $cabpath = "cabs_" . $$;
+ $cabpath = $temppath . $installer::globals::separator . $cabpath;
+ if ( ! -d $cabpath ) { installer::systemactions::create_directory($cabpath); }
+
+ return $cabpath;
+}
+
+#################################################################
+# Setting the path, where the diff can happen.
+#################################################################
+
+sub get_diff_path
+{
+ my ($temppath) = @_;
+
+ my $diffpath = "diff_" . $$;
+ $diffpath = $temppath . $installer::globals::separator . $diffpath;
+ if ( ! -d $diffpath ) { installer::systemactions::create_directory($diffpath); }
+
+ return $diffpath;
+}
+
+#################################################################
+# Exclude all cab files from the msi database.
+#################################################################
+
+sub extract_cabs_from_database
+{
+ my ($msidatabase, $allcabfiles) = @_;
+
+ installer::logger::include_header_into_logfile("Extracting cabs from msi database");
+
+ my $infoline = "";
+ my $fullsuccess = 1;
+ my $msidb = "msidb.exe"; # Has to be in the path
+
+ # msidb.exe really wants backslashes. (And double escaping because system() expands the string.)
+ $msidatabase =~ s/\//\\\\/g;
+
+ foreach my $onefile ( keys %{$allcabfiles} )
+ {
+ my $systemcall = $msidb . " -d " . $msidatabase . " -x " . $onefile;
+ my $success = make_systemcall($systemcall, $systemcall);
+ if ( ! $success ) { $fullsuccess = 0; }
+
+ # and removing the stream from the database
+ $systemcall = $msidb . " -d " . $msidatabase . " -k " . $onefile;
+ $success = make_systemcall($systemcall, $systemcall);
+ if ( ! $success ) { $fullsuccess = 0; }
+ }
+
+ return $fullsuccess;
+}
+
+#################################################################
+# Include cab files into the msi database.
+#################################################################
+
+sub include_cabs_into_database
+{
+ my ($msidatabase, $allcabfiles) = @_;
+
+ installer::logger::include_header_into_logfile("Including cabs into msi database");
+
+ my $infoline = "";
+ my $fullsuccess = 1;
+ my $msidb = "msidb.exe"; # Has to be in the path
+
+ # msidb.exe really wants backslashes. (And double escaping because system() expands the string.)
+ $msidatabase =~ s/\//\\\\/g;
+
+ foreach my $onefile ( keys %{$allcabfiles} )
+ {
+ my $systemcall = $msidb . " -d " . $msidatabase . " -a " . $onefile;
+ my $success = make_systemcall($systemcall, $systemcall);
+ if ( ! $success ) { $fullsuccess = 0; }
+ }
+
+ return $fullsuccess;
+}
+
+########################################################
+# Reading the order of the files inside the
+# cabinet files.
+########################################################
+
+sub read_cab_file
+{
+ my ($cabfilename) = @_;
+
+ installer::logger::print_message( "\n... reading cabinet file $cabfilename ...\n" );
+ my $infoline = "Reading cabinet file $cabfilename\n";
+ push( @installer::globals::logfileinfo, $infoline);
+
+ my $systemcall = "cabarc.exe" . " L " . $cabfilename;
+ push(@logfile, "$systemcall\n");
+
+ my ($success, $fileorder) = execute_open_system_call($systemcall);
+
+ my @allfiles = ();
+
+ for ( my $i = 0; $i <= $#{$fileorder}; $i++ )
+ {
+ my $line = ${$fileorder}[$i];
+ if ( $line =~ /^\s*(.*?)\s+\d+\s+\d+\/\d+\/\d+\s+\d+\:\d+\:\d+\s+[\w-]+\s*$/ )
+ {
+ my $filename = $1;
+ push(@allfiles, $filename);
+ }
+ }
+
+ return \@allfiles;
+}
+
+########################################################
+# Unpacking a cabinet file.
+########################################################
+
+sub unpack_cab_file
+{
+ my ($cabfilename, $temppath) = @_;
+
+ installer::logger::print_message( "\n... unpacking cabinet file $cabfilename ...\n" );
+ my $infoline = "Unpacking cabinet file $cabfilename\n";
+ push( @installer::globals::logfileinfo, $infoline);
+
+ my $dirname = $cabfilename;
+ $dirname =~ s/\.cab\s*$//;
+ my $workingpath = $temppath . $installer::globals::separator . "unpack_". $dirname . "_" . $$;
+ if ( ! -d $workingpath ) { installer::systemactions::create_directory($workingpath); }
+
+ # changing into unpack directory
+ my $from = cwd();
+ chdir($workingpath);
+
+ my $fullcabfilename = $from . $installer::globals::separator . $cabfilename;
+
+ if( $^O =~ /cygwin/i )
+ {
+ $fullcabfilename = qx{cygpath -w "$fullcabfilename"};
+ $fullcabfilename =~ s/\\/\\\\/g;
+ $fullcabfilename =~ s/\s*$//g;
+ }
+
+ my $systemcall = "cabarc.exe" . " -p X " . $fullcabfilename;
+ $success = make_systemcall($systemcall, $systemcall);
+ if ( ! $success ) { installer::exiter::exit_program("ERROR: Could not unpack cabinet file: $fullcabfilename!", "unpack_cab_file"); }
+
+ # returning to directory
+ chdir($from);
+
+ return $workingpath;
+}
+
+########################################################
+# Returning the header of a ddf file.
+########################################################
+
+sub get_ddf_file_header
+{
+ my ($ddffileref, $cabinetfile, $installdir) = @_;
+
+ my $oneline;
+ my $compressionlevel = 2;
+
+ if( $^O =~ /cygwin/i )
+ {
+ $installdir = qx{cygpath -w "$installdir"};
+ $installdir =~ s/\s*$//g;
+ }
+
+ $oneline = ".Set CabinetName1=" . $cabinetfile . "\n";
+ push(@{$ddffileref} ,$oneline);
+ $oneline = ".Set ReservePerCabinetSize=128\n"; # This reserves space for a digital signature.
+ push(@{$ddffileref} ,$oneline);
+ $oneline = ".Set MaxDiskSize=2147483648\n"; # This allows the .cab file to get a size of 2 GB.
+ push(@{$ddffileref} ,$oneline);
+ $oneline = ".Set CompressionType=LZX\n";
+ push(@{$ddffileref} ,$oneline);
+ $oneline = ".Set Compress=ON\n";
+ push(@{$ddffileref} ,$oneline);
+ $oneline = ".Set CompressionLevel=$compressionlevel\n";
+ push(@{$ddffileref} ,$oneline);
+ $oneline = ".Set Cabinet=ON\n";
+ push(@{$ddffileref} ,$oneline);
+ $oneline = ".Set DiskDirectoryTemplate=" . $installdir . "\n";
+ push(@{$ddffileref} ,$oneline);
+}
+
+########################################################
+# Writing content into ddf file.
+########################################################
+
+sub put_all_files_into_ddffile
+{
+ my ($ddffile, $allfiles, $workingpath) = @_;
+
+ $workingpath =~ s/\//\\/g;
+
+ for ( my $i = 0; $i <= $#{$allfiles}; $i++ )
+ {
+ my $filename = ${$allfiles}[$i];
+ if( $^O =~ /cygwin/i ) { $filename =~ s/\//\\/g; } # Backslash for Cygwin!
+ if ( ! -f $filename ) { installer::exiter::exit_program("ERROR: Could not find file: $filename!", "put_all_files_into_ddffile"); }
+ my $infoline = "\"" . $filename . "\"" . " " . ${$allfiles}[$i] . "\n";
+ push( @{$ddffile}, $infoline);
+ }
+}
+
+########################################################
+# Packing a cabinet file.
+########################################################
+
+sub do_pack_cab_file
+{
+ my ($cabfilename, $allfiles, $workingpath, $temppath) = @_;
+
+ installer::logger::print_message( "\n... packing cabinet file $cabfilename ...\n" );
+ my $infoline = "Packing cabinet file $cabfilename\n";
+ push( @installer::globals::logfileinfo, $infoline);
+
+ if ( -f $cabfilename ) { unlink($cabfilename); } # removing cab file
+ if ( -f $cabfilename ) { installer::exiter::exit_program("ERROR: Failed to remove file: $cabfilename!", "do_pack_cab_file"); }
+
+ # generate ddf file for makecab.exe
+ my @ddffile = ();
+
+ my $dirname = $cabfilename;
+ $dirname =~ s/\.cab\s*$//;
+ my $ddfpath = $temppath . $installer::globals::separator . "ddf_". $dirname . "_" . $$;
+
+ my $ddffilename = $cabfilename;
+ $ddffilename =~ s/.cab/.ddf/;
+ $ddffilename = $ddfpath . $installer::globals::separator . $ddffilename;
+
+ if ( ! -d $ddfpath ) { installer::systemactions::create_directory($ddfpath); }
+
+ my $from = cwd();
+
+ chdir($workingpath); # changing into the directory with the unpacked files
+
+ get_ddf_file_header(\@ddffile, $cabfilename, $from);
+ put_all_files_into_ddffile(\@ddffile, $allfiles, $workingpath);
+ # lines in ddf files must not be longer than 256 characters
+ check_ddf_file(\@ddffile, $ddffilename);
+
+ installer::files::save_file($ddffilename, \@ddffile);
+
+ if( $^O =~ /cygwin/i )
+ {
+ $ddffilename = qx{cygpath -w "$ddffilename"};
+ $ddffilename =~ s/\\/\\\\/g;
+ $ddffilename =~ s/\s*$//g;
+ }
+
+ my $systemcall = "makecab.exe /V1 /F " . $ddffilename;
+ my $success = make_systemcall($systemcall, $systemcall);
+ if ( ! $success ) { installer::exiter::exit_program("ERROR: Could not pack cabinet file!", "do_pack_cab_file"); }
+
+ chdir($from);
+
+ return ($success);
+}
+
+########################################################
+# Extraction the file extension from a file
+########################################################
+
+sub get_extension
+{
+ my ( $file ) = @_;
+
+ my $extension = "";
+
+ if ( $file =~ /^\s*(.*)\.(\w+?)\s*$/ ) { $extension = $2; }
+
+ return $extension;
+}
+
+########################################################
+# Checking, if a file already contains a certificate.
+# This must not be overwritten.
+########################################################
+
+sub already_certified
+{
+ my ( $filename ) = @_;
+
+ my $success = 1;
+ my $is_certified = 0;
+
+ my $systemcall = "signtool.exe verify /q /pa \"$filename\"";
+ my $returnvalue = system($systemcall);
+
+ if ( $returnvalue ) { $success = 0; }
+
+ # my $success = make_systemcall($systemcall, $systemcall);
+
+ if ( $success )
+ {
+ $is_certified = 1;
+ installer::logger::print_message( "... already certified -> skipping $filename ...\n" );
+ }
+
+ return $is_certified;
+}
+
+########################################################
+# Signing the files, that are included into
+# cabinet files.
+########################################################
+
+sub sign_files_in_cabinet_files
+{
+ my ( $followmeinfohash, $allcabfiles, $pw, $temppath ) = @_;
+
+ my $complete_success = 1;
+ my $from = cwd();
+
+ foreach my $cabfilename ( keys %{$allcabfiles} )
+ {
+ my $success = 1;
+
+ # saving order of files in cab file
+ my $fileorder = read_cab_file($cabfilename);
+
+ # unpack into $working path
+ my $workingpath = unpack_cab_file($cabfilename, $temppath);
+
+ chdir($workingpath);
+
+ # sign files
+ my %allfileshash = ();
+ foreach my $onefile ( @{$fileorder} )
+ {
+ my $extension = get_extension($onefile);
+ if ( exists( $installer::globals::sign_extensions{$extension} ) )
+ {
+ $allfileshash{$onefile} = 1;
+ }
+ }
+ $success = sign_files($followmeinfohash, \%allfileshash, $pw, 1, 0, 0, $temppath);
+ if ( ! $success ) { $complete_success = 0; }
+
+ chdir($from);
+
+ # pack into new directory
+ do_pack_cab_file($cabfilename, $fileorder, $workingpath, $temppath);
+ }
+
+ return $complete_success;
+}
+
+########################################################
+# Comparing the content of two directories.
+# Only filesize is compared.
+########################################################
+
+sub compare_directories
+{
+ my ( $dir1, $dir2, $files ) = @_;
+
+ $dir1 =~ s/\\\s*//;
+ $dir2 =~ s/\\\s*//;
+ $dir1 =~ s/\/\s*//;
+ $dir2 =~ s/\/\s*//;
+
+ my $infoline = "Comparing directories: $dir1 and $dir2\n";
+ push( @installer::globals::logfileinfo, $infoline);
+
+ foreach my $onefile ( @{$files} )
+ {
+ my $file1 = $dir1 . $installer::globals::separator . $onefile;
+ my $file2 = $dir2 . $installer::globals::separator . $onefile;
+
+ if ( ! -f $file1 ) { installer::exiter::exit_program("ERROR: Missing file : $file1!", "compare_directories"); }
+ if ( ! -f $file2 ) { installer::exiter::exit_program("ERROR: Missing file : $file2!", "compare_directories"); }
+
+ my $size1 = -s $file1;
+ my $size2 = -s $file2;
+
+ $infoline = "Comparing files: $file1 ($size1) and $file2 ($size2)\n";
+ push( @installer::globals::logfileinfo, $infoline);
+
+ if ( $size1 != $size2 )
+ {
+ installer::exiter::exit_program("ERROR: File defect after copy (different size) $file1 ($size1 bytes) and $file2 ($size2 bytes)!", "compare_directories");
+ }
+ }
+}
+
+########################################################
+# Signing an existing Windows installation set.
+########################################################
+
+sub sign_install_set
+{
+ my ($followmeinfohash, $make_copy, $temppath) = @_;
+
+ my $installsetpath = $followmeinfohash->{'finalinstalldir'};
+
+ installer::logger::include_header_into_logfile("Start: Signing installation set $installsetpath");
+
+ my $complete_success = 1;
+ my $success = 1;
+
+ my $infoline = "Signing installation set in $installsetpath\n";
+ push( @installer::globals::logfileinfo, $infoline);
+
+ # check required files.
+ if ( ! $installer::globals::signfiles_checked ) { check_system_path(); }
+
+ # get cerficate information
+ my $pw = get_pw($installer::globals::pwfile);
+
+ # making a copy of the installation set, if required
+ if ( $make_copy ) { $installsetpath = copy_install_set($installsetpath); }
+ else { $installsetpath = rename_install_set($installsetpath); }
+
+ # collecting all files in the installation set
+ my ($allcabfiles, $allfiles, $msidatabase, $contains_external_cabfiles, $contains_msidatabase, $sourcefiles) = analyze_installset_content($installsetpath);
+
+ if ( $make_copy ) { compare_directories($installsetpath, $followmeinfohash->{'finalinstalldir'}, $sourcefiles); }
+
+ # changing into installation set
+ my $from = cwd();
+ my $fullmsidatabase = $installsetpath . $installer::globals::separator . $msidatabase;
+
+ if( $^O =~ /cygwin/i )
+ {
+ $fullmsidatabase = qx{cygpath -w "$fullmsidatabase"};
+ $fullmsidatabase =~ s/\\/\\\\/g;
+ $fullmsidatabase =~ s/\s*$//g;
+ }
+
+ chdir($installsetpath);
+
+ if ( $contains_msidatabase )
+ {
+ # exclude media table from msi database and get all diskids.
+ my ( $cabfilehash, $filenamehash, $lastsequencehash ) = collect_diskid_from_media_table($msidatabase, $followmeinfohash->{'languagestring'});
+
+ # Check, if there are internal cab files
+ my ( $contains_internal_cabfiles, $all_internal_cab_files) = check_for_internal_cabfiles($cabfilehash);
+
+ if ( $contains_internal_cabfiles )
+ {
+ my $cabpath = get_cab_path($temppath);
+ chdir($cabpath);
+
+ # Exclude all cabinet files from database
+ $success = extract_cabs_from_database($fullmsidatabase, $all_internal_cab_files);
+ if ( ! $success ) { $complete_success = 0; }
+
+ if ( $installer::globals::internal_cabinet_signing ) { sign_files_in_cabinet_files($followmeinfohash, $all_internal_cab_files, $pw, $temppath); }
+
+ $success = sign_files($followmeinfohash, $all_internal_cab_files, $pw, 0, $filenamehash, $lastsequencehash, $temppath);
+ if ( ! $success ) { $complete_success = 0; }
+ $success = msicert_database($fullmsidatabase, $all_internal_cab_files, $cabfilehash, 1);
+ if ( ! $success ) { $complete_success = 0; }
+
+ # Include all cabinet files into database
+ $success = include_cabs_into_database($fullmsidatabase, $all_internal_cab_files);
+ if ( ! $success ) { $complete_success = 0; }
+ chdir($installsetpath);
+ }
+
+ # Warning: There might be a problem with very big cabinet files
+ # signing all external cab files first
+ if ( $contains_external_cabfiles )
+ {
+ if ( $installer::globals::internal_cabinet_signing ) { sign_files_in_cabinet_files($followmeinfohash, $allcabfiles, $pw, $temppath); }
+
+ $success = sign_files($followmeinfohash, $allcabfiles, $pw, 0, $filenamehash, $lastsequencehash, $temppath);
+ if ( ! $success ) { $complete_success = 0; }
+ $success = msicert_database($msidatabase, $allcabfiles, $cabfilehash, 0);
+ if ( ! $success ) { $complete_success = 0; }
+ }
+ }
+
+ # finally all other files can be signed
+ $success = sign_files($followmeinfohash, $allfiles, $pw, 0, 0, 0, $temppath);
+ if ( ! $success ) { $complete_success = 0; }
+
+ # and changing back
+ chdir($from);
+
+ installer::logger::include_header_into_logfile("End: Signing installation set $installsetpath");
+
+ return ($installsetpath);
+}
+
+1;
diff --git a/solenv/bin/modules/installer/windows/strip.pm b/solenv/bin/modules/installer/windows/strip.pm
new file mode 100644
index 000000000000..79b1046c1e8f
--- /dev/null
+++ b/solenv/bin/modules/installer/windows/strip.pm
@@ -0,0 +1,159 @@
+#*************************************************************************
+#
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+#
+# Copyright 2000, 2010 Oracle and/or its affiliates.
+#
+# OpenOffice.org - a multi-platform office productivity suite
+#
+# This file is part of OpenOffice.org.
+#
+# OpenOffice.org is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License version 3
+# only, as published by the Free Software Foundation.
+#
+# OpenOffice.org is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License version 3 for more details
+# (a copy is included in the LICENSE file that accompanied this code).
+#
+# You should have received a copy of the GNU Lesser General Public License
+# version 3 along with OpenOffice.org. If not, see
+# <http://www.openoffice.org/license.html>
+# for a copy of the LGPLv3 License.
+#
+#*************************************************************************
+
+package installer::windows::strip;
+
+use File::Temp qw(tmpnam);
+use installer::converter;
+use installer::existence;
+use installer::globals;
+use installer::logger;
+use installer::pathanalyzer;
+use installer::systemactions;
+
+#####################################################################
+# Checking whether a file has to be stripped
+#####################################################################
+
+sub need_to_strip
+{
+ my ( $filename ) = @_;
+
+ my $strip = 0;
+
+ # Check using the "nm" command
+
+ $filename =~ s/\\/\\\\/g;
+
+ open (FILE, "nm $filename 2>&1 |");
+ my $nmoutput = <FILE>;
+ close (FILE);
+
+ if ( $nmoutput && !( $nmoutput =~ /no symbols/i || $nmoutput =~ /not recognized/i )) { $strip = 1; }
+
+ return $strip
+}
+
+#####################################################################
+# Checking whether a file has to be stripped
+#####################################################################
+
+sub do_strip
+{
+ my ( $filename ) = @_;
+
+ my $systemcall = "strip" . " " . $filename;
+
+ my $returnvalue = system($systemcall);
+
+ my $infoline = "Systemcall: $systemcall\n";
+ push( @installer::globals::logfileinfo, $infoline);
+
+ if ($returnvalue)
+ {
+ $infoline = "ERROR: Could not strip $filename!\n";
+ push( @installer::globals::logfileinfo, $infoline);
+ }
+ else
+ {
+ $infoline = "SUCCESS: Stripped library $filename!\n";
+ push( @installer::globals::logfileinfo, $infoline);
+ }
+}
+
+#####################################################################
+# Resolving all variables in the packagename.
+#####################################################################
+
+sub strip_binaries
+{
+ my ( $filelist, $languagestringref ) = @_;
+
+ installer::logger::include_header_into_logfile("Stripping files:");
+
+ my $strippeddirbase = installer::systemactions::create_directories("stripped", $languagestringref);
+
+ if (! installer::existence::exists_in_array($strippeddirbase, \@installer::globals::removedirs))
+ {
+ push(@installer::globals::removedirs, $strippeddirbase);
+ }
+
+ my ($tmpfilehandle, $tmpfilename) = tmpnam();
+ open SOURCEPATHLIST, ">$tmpfilename" or die "oops...\n";
+ for ( my $i = 0; $i <= $#{$filelist}; $i++ )
+ {
+ print SOURCEPATHLIST "${$filelist}[$i]->{'sourcepath'}\n";
+ }
+ close SOURCEPATHLIST;
+ my @filetypelist = qx{file -f "$tmpfilename"};
+ chomp @filetypelist;
+ unlink "$tmpfilename" or die "oops\n";
+ for ( my $i = 0; $i <= $#{$filelist}; $i++ )
+ {
+ ${$filelist}[$i]->{'is_executable'} = ( $filetypelist[$i] =~ /:.*PE executable/ );
+ }
+
+ if ( $^O =~ /cygwin/i ) { installer::worker::generate_cygwin_pathes($filelist); }
+
+ for ( my $i = 0; $i <= $#{$filelist}; $i++ )
+ {
+ my $sourcefilename = ${$filelist}[$i]->{'cyg_sourcepath'};
+
+ if ( ${$filelist}[$i]->{'is_executable'} && need_to_strip($sourcefilename) )
+ {
+ my $shortfilename = $sourcefilename;
+ installer::pathanalyzer::make_absolute_filename_to_relative_filename(\$shortfilename);
+
+ $infoline = "Strip: $shortfilename\n";
+ push( @installer::globals::logfileinfo, $infoline);
+
+ # copy file into directory for stripped libraries
+
+ my $onelanguage = ${$filelist}[$i]->{'specificlanguage'};
+
+ # files without language into directory "00"
+
+ if ($onelanguage eq "") { $onelanguage = "00"; }
+
+ my $strippeddir = $strippeddirbase . $installer::globals::separator . $onelanguage;
+ installer::systemactions::create_directory($strippeddir); # creating language specific subdirectories
+
+ my $destfilename = $strippeddir . $installer::globals::separator . $shortfilename;
+ installer::systemactions::copy_one_file($sourcefilename, $destfilename);
+
+ # change sourcepath in files collector
+
+ ${$filelist}[$i]->{'sourcepath'} = $destfilename;
+
+ # strip file
+
+ do_strip($destfilename);
+ }
+ }
+}
+
+1;
diff --git a/solenv/bin/modules/installer/windows/update.pm b/solenv/bin/modules/installer/windows/update.pm
new file mode 100644
index 000000000000..efe6949b6fc4
--- /dev/null
+++ b/solenv/bin/modules/installer/windows/update.pm
@@ -0,0 +1,600 @@
+#*************************************************************************
+#
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+#
+# Copyright 2000, 2010 Oracle and/or its affiliates.
+#
+# OpenOffice.org - a multi-platform office productivity suite
+#
+# This file is part of OpenOffice.org.
+#
+# OpenOffice.org is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License version 3
+# only, as published by the Free Software Foundation.
+#
+# OpenOffice.org is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License version 3 for more details
+# (a copy is included in the LICENSE file that accompanied this code).
+#
+# You should have received a copy of the GNU Lesser General Public License
+# version 3 along with OpenOffice.org. If not, see
+# <http://www.openoffice.org/license.html>
+# for a copy of the LGPLv3 License.
+#
+#*************************************************************************
+
+package installer::windows::update;
+
+use installer::converter;
+use installer::exiter;
+use installer::files;
+use installer::globals;
+use installer::pathanalyzer;
+use installer::systemactions;
+
+#################################################################################
+# Extracting all tables from an msi database
+#################################################################################
+
+sub extract_all_tables_from_msidatabase
+{
+ my ($fulldatabasepath, $workdir) = @_;
+
+ my $msidb = "msidb.exe"; # Has to be in the path
+ my $infoline = "";
+ my $systemcall = "";
+ my $returnvalue = "";
+ my $extraslash = ""; # Has to be set for non-ActiveState perl
+
+ # Export of all tables by using "*"
+
+ if ( $^O =~ /cygwin/i ) {
+ # msidb.exe really wants backslashes. (And double escaping because system() expands the string.)
+ $fulldatabasepath =~ s/\//\\\\/g;
+ $workdir =~ s/\//\\\\/g;
+ $extraslash = "\\";
+ }
+
+ $systemcall = $msidb . " -d " . $fulldatabasepath . " -f " . $workdir . " -e " . $extraslash . "*";
+ $returnvalue = system($systemcall);
+
+ $infoline = "Systemcall: $systemcall\n";
+ push( @installer::globals::logfileinfo, $infoline);
+
+ if ($returnvalue)
+ {
+ $infoline = "ERROR: Could not execute $systemcall !\n";
+ push( @installer::globals::logfileinfo, $infoline);
+ installer::exiter::exit_program("ERROR: Could not exclude tables from msi database: $fulldatabasepath !", "extract_all_tables_from_msidatabase");
+ }
+ else
+ {
+ $infoline = "Success: Executed $systemcall successfully!\n";
+ push( @installer::globals::logfileinfo, $infoline);
+ }
+}
+
+#################################################################################
+# Collecting the keys from the first line of the idt file
+#################################################################################
+
+sub collect_all_keys
+{
+ my ($line) = @_;
+
+ my @allkeys = ();
+ my $rownumber = 0;
+ my $onekey = "";
+
+ while ( $line =~ /^\s*(\S+?)\t(.*)$/ )
+ {
+ $onekey = $1;
+ $line = $2;
+ $rownumber++;
+ push(@allkeys, $onekey);
+ }
+
+ # and the last key
+
+ $onekey = $line;
+ $onekey =~ s/^\s*//g;
+ $onekey =~ s/\s*$//g;
+
+ $rownumber++;
+ push(@allkeys, $onekey);
+
+ return (\@allkeys, $rownumber);
+}
+
+#################################################################################
+# Analyzing the content of one line of an idt file
+#################################################################################
+
+sub get_oneline_hash
+{
+ my ($line, $allkeys, $rownumber) = @_;
+
+ my $counter = 0;
+ my %linehash = ();
+
+ $line =~ s/^\s*//;
+ $line =~ s/\s*$//;
+
+ my $value = "";
+ my $onekey = "";
+
+ while ( $line =~ /^(.*?)\t(.*)$/ )
+ {
+ $value = $1;
+ $line = $2;
+ $onekey = ${$allkeys}[$counter];
+ $linehash{$onekey} = $value;
+ $counter++;
+ }
+
+ # the last column
+
+ $value = $line;
+ $onekey = ${$allkeys}[$counter];
+
+ $linehash{$onekey} = $value;
+
+ return \%linehash;
+}
+
+#################################################################################
+# Analyzing the content of an idt file
+#################################################################################
+
+sub analyze_idt_file
+{
+ my ($filecontent) = @_;
+
+ my %table = ();
+ # keys are written in first line
+ my ($allkeys, $rownumber) = collect_all_keys(${$filecontent}[0]);
+
+ for ( my $i = 0; $i <= $#{$filecontent}; $i++ )
+ {
+ if (( $i == 0 ) || ( $i == 1 ) || ( $i == 2 )) { next; }
+
+ my $onelinehash = get_oneline_hash(${$filecontent}[$i], $allkeys, $rownumber);
+ my $linekey = $i - 2; # ! : The linenumber is the unique key !? Always decrease by two, because of removed first three lines.
+ $table{$linekey} = $onelinehash;
+ }
+
+ return \%table;
+}
+
+#################################################################################
+# Reading all idt files in a specified directory
+#################################################################################
+
+sub read_all_tables_from_msidatabase
+{
+ my ($workdir) = @_;
+
+ my %database = ();
+
+ my $ext = "idt";
+
+ my $allidtfiles = installer::systemactions::find_file_with_file_extension($ext, $workdir);
+
+ for ( my $i = 0; $i <= $#{$allidtfiles}; $i++ )
+ {
+ my $onefilename = ${$allidtfiles}[$i];
+ my $longonefilename = $workdir . $installer::globals::separator . $onefilename;
+ if ( ! -f $longonefilename ) { installer::exiter::exit_program("ERROR: Could not find idt file: $longonefilename!", "read_all_tables_from_msidatabase"); }
+ my $filecontent = installer::files::read_file($longonefilename);
+ my $idtcontent = analyze_idt_file($filecontent);
+ my $key = $onefilename;
+ $key =~ s/\.idt\s*$//;
+ $database{$key} = $idtcontent;
+ }
+
+ return \%database;
+}
+
+#################################################################################
+# Checking, if this is the correct database.
+#################################################################################
+
+sub correct_database
+{
+ my ($product, $pro, $langs, $languagestringref) = @_;
+
+ my $correct_database = 0;
+
+ # Comparing $product with $installer::globals::product and
+ # $pro with $installer::globals::pro and
+ # $langs with $languagestringref
+
+ my $product_is_good = 0;
+
+ my $localproduct = $installer::globals::product;
+ if ( $installer::globals::languagepack ) { $localproduct = $localproduct . "LanguagePack"; }
+
+ if ( $product eq $localproduct ) { $product_is_good = 1; }
+
+ if ( $product_is_good )
+ {
+ my $pro_is_good = 0;
+
+ if ((( $pro eq "pro" ) && ( $installer::globals::pro )) || (( $pro eq "nonpro" ) && ( ! $installer::globals::pro ))) { $pro_is_good = 1; }
+
+ if ( $pro_is_good )
+ {
+ my $langlisthash = installer::converter::convert_stringlist_into_hash(\$langs, ",");
+ my $langstringhash = installer::converter::convert_stringlist_into_hash($languagestringref, "_");
+
+ my $not_included = 0;
+ foreach my $onelang ( keys %{$langlisthash} )
+ {
+ if ( ! exists($langstringhash->{$onelang}) )
+ {
+ $not_included = 1;
+ last;
+ }
+ }
+
+ if ( ! $not_included )
+ {
+ foreach my $onelanguage ( keys %{$langstringhash} )
+ {
+ if ( ! exists($langlisthash->{$onelanguage}) )
+ {
+ $not_included = 1;
+ last;
+ }
+ }
+
+ if ( ! $not_included ) { $correct_database = 1; }
+ }
+ }
+ }
+
+ return $correct_database;
+}
+
+#################################################################################
+# Searching for the path to the reference database for this special product.
+#################################################################################
+
+sub get_databasename_from_list
+{
+ my ($filecontent, $languagestringref, $filename) = @_;
+
+ my $databasepath = "";
+
+ for ( my $i = 0; $i <= $#{$filecontent}; $i++ )
+ {
+ my $line = ${$filecontent}[$i];
+ if ( $line =~ /^\s*$/ ) { next; } # empty line
+ if ( $line =~ /^\s*\#/ ) { next; } # comment line
+
+ if ( $line =~ /^\s*(.+?)\s*\t+\s*(.+?)\s*\t+\s*(.+?)\s*\t+\s*(.+?)\s*$/ )
+ {
+ my $product = $1;
+ my $pro = $2;
+ my $langs = $3;
+ my $path = $4;
+
+ if (( $pro ne "pro" ) && ( $pro ne "nonpro" )) { installer::exiter::exit_program("ERROR: Wrong syntax in file: $filename. Only \"pro\" or \"nonpro\" allowed in column 1! Line: \"$line\"", "get_databasename_from_list"); }
+
+ if ( correct_database($product, $pro, $langs, $languagestringref) )
+ {
+ $databasepath = $path;
+ last;
+ }
+ }
+ else
+ {
+ installer::exiter::exit_program("ERROR: Wrong syntax in file: $filename! Line: \"$line\"", "get_databasename_from_list");
+ }
+ }
+
+ return $databasepath;
+}
+
+#################################################################################
+# Reading an existing database completely
+#################################################################################
+
+sub readdatabase
+{
+ my ($allvariables, $languagestringref, $includepatharrayref) = @_;
+
+ my $database = "";
+ my $infoline = "";
+
+ if ( ! $allvariables->{'UPDATE_DATABASE_LISTNAME'} ) { installer::exiter::exit_program("ERROR: If \"UPDATE_DATABASE\" is set, \"UPDATE_DATABASE_LISTNAME\" is required.", "Main"); }
+ my $listfilename = $allvariables->{'UPDATE_DATABASE_LISTNAME'};
+
+ # Searching the list in the include pathes
+ my $listname = installer::scriptitems::get_sourcepath_from_filename_and_includepath(\$listfilename, $includepatharrayref, 1);
+ if ( $$listname eq "" ) { installer::exiter::exit_program("ERROR: List file not found: $listfilename !", "readdatabase"); }
+ my $completelistname = $$listname;
+
+ # Reading list file
+ my $listfile = installer::files::read_file($completelistname);
+
+ # Get name and path of reference database
+ my $databasename = get_databasename_from_list($listfile, $languagestringref, $completelistname);
+
+ # If the correct database was not found, this is not necessarily an error. But in this case, this is not an update packaging process!
+ if (( $databasename ) && ( $databasename ne "" )) # This is an update packaging process!
+ {
+ $installer::globals::updatedatabase = 1;
+ installer::logger::print_message( "... update process, using database $databasename ...\n" );
+ $infoline = "\nDatabase found in $completelistname: \"$databasename\"\n\n";
+ # Saving in global variable
+ $installer::globals::updatedatabasepath = $databasename;
+ }
+ else
+ {
+ # installer::logger::print_message( "... no update process, no database found ...\n" );
+ $infoline = "\nNo database found in $completelistname. This is no update process!\n\n";
+ }
+ push( @installer::globals::logfileinfo, $infoline);
+
+ if ( $installer::globals::updatedatabase )
+ {
+ if ( ! -f $databasename ) { installer::exiter::exit_program("ERROR: Could not find reference database: $databasename!", "readdatabase"); }
+
+ my $msifilename = $databasename;
+ installer::pathanalyzer::make_absolute_filename_to_relative_filename(\$msifilename);
+
+ installer::logger::include_timestamp_into_logfile("Performance Info: readdatabase start");
+
+ # create directory for unpacking
+ my $databasedir = installer::systemactions::create_directories("database", $languagestringref);
+
+ # copy database
+ my $fulldatabasepath = $databasedir . $installer::globals::separator . $msifilename;
+ installer::systemactions::copy_one_file($databasename, $fulldatabasepath);
+
+ installer::logger::include_timestamp_into_logfile("Performance Info: readdatabase: before extracting tables");
+
+ # extract all tables from database
+ extract_all_tables_from_msidatabase($fulldatabasepath, $databasedir);
+
+ installer::logger::include_timestamp_into_logfile("Performance Info: readdatabase: before reading tables");
+
+ # read all tables
+ $database = read_all_tables_from_msidatabase($databasedir);
+
+ # Test output:
+
+ # foreach my $key1 ( keys %{$database} )
+ # {
+ # print "Test1: $key1\n";
+ # foreach my $key2 ( keys %{$database->{$key1}} )
+ # {
+ # print "\tTest2: $key2\n";
+ # foreach my $key3 ( keys %{$database->{$key1}->{$key2}} )
+ # {
+ # print "\t\tTest3: $key3: $database->{$key1}->{$key2}->{$key3}\n";
+ # }
+ # }
+ # }
+
+ # Example: File table
+
+ # my $filetable = $database->{'File'};
+ # foreach my $linenumber ( keys %{$filetable} )
+ # {
+ # print "Test Filenumber: $linenumber\n";
+ # foreach my $key ( keys %{$filetable->{$linenumber}} )
+ # {
+ # print "\t\tTest: $key: $filetable->{$linenumber}->{$key}\n";
+ # }
+ # }
+
+ # Example: Searching for ProductCode in table Property
+
+ # my $column1 = "Property";
+ # my $column2 = "Value";
+ # my $searchkey = "ProductCode";
+ # my $propertytable = $database->{'Property'};
+ # foreach my $linenumber ( keys %{$propertytable} )
+ # {
+ # if ( $propertytable->{$linenumber}->{$column1} eq $searchkey )
+ # {
+ # print("Test: $searchkey : $propertytable->{$linenumber}->{$column2}\n");
+ # }
+ # }
+
+ installer::logger::include_timestamp_into_logfile("Performance Info: readdatabase end");
+ }
+
+ return $database;
+}
+
+#################################################################################
+# Files can be included in merge modules. This is also important for update.
+#################################################################################
+
+sub readmergedatabase
+{
+ my ( $mergemodules, $languagestringref, $includepatharrayref ) = @_;
+
+ installer::logger::include_timestamp_into_logfile("Performance Info: readmergedatabase start");
+
+ my $mergemoduledir = installer::systemactions::create_directories("mergedatabase", $languagestringref);
+
+ my %allmergefiles = ();
+
+ $installer::globals::mergemodulenumber = $#{$mergemodules} + 1;
+
+ foreach my $mergemodule ( @{$mergemodules} )
+ {
+ my $filename = $mergemodule->{'Name'};
+ my $mergefile = installer::scriptitems::get_sourcepath_from_filename_and_includepath(\$filename, $includepatharrayref, 1);
+
+ if ( $$mergefile eq "" ) { installer::exiter::exit_program("ERROR: msm file not found: $filename !", "readmergedatabase"); }
+ my $completesource = $$mergefile;
+
+ my $mergegid = $mergemodule->{'gid'};
+ my $workdir = $mergemoduledir . $installer::globals::separator . $mergegid;
+ if ( ! -d $workdir ) { installer::systemactions::create_directory($workdir); }
+
+ my $completedest = $workdir . $installer::globals::separator . $filename;
+ installer::systemactions::copy_one_file($completesource, $completedest);
+ if ( ! -f $completedest ) { installer::exiter::exit_program("ERROR: msm file not found: $completedest !", "readmergedatabase"); }
+
+ # extract all tables from database
+ extract_all_tables_from_msidatabase($completedest, $workdir);
+
+ # read all tables
+ my $onemergefile = read_all_tables_from_msidatabase($workdir);
+
+ $allmergefiles{$mergegid} = $onemergefile;
+ }
+
+ foreach my $mergefilegid ( keys %allmergefiles )
+ {
+ my $onemergefile = $allmergefiles{$mergefilegid};
+ my $filetable = $onemergefile->{'File'};
+
+ foreach my $linenumber ( keys %{$filetable} )
+ {
+ # Collecting all files from merge modules in global hash
+ $installer::globals::mergemodulefiles{$filetable->{$linenumber}->{'File'}} = 1;
+ }
+ }
+
+ installer::logger::include_timestamp_into_logfile("Performance Info: readmergedatabase end");
+}
+
+#################################################################################
+# Creating several useful hashes from old database
+#################################################################################
+
+sub create_database_hashes
+{
+ my ( $database ) = @_;
+
+ # 1. Hash ( Component -> UniqueFileName ), required in File table.
+ # Read from File table.
+
+ my %uniquefilename = ();
+ my %allupdatesequences = ();
+ my %allupdatecomponents = ();
+ my %allupdatefileorder = ();
+ my %allupdatecomponentorder = ();
+ my %revuniquefilename = ();
+ my %revshortfilename = ();
+ my %shortdirname = ();
+ my %componentid = ();
+ my %componentidkeypath = ();
+ my %alloldproperties = ();
+ my %allupdatelastsequences = ();
+ my %allupdatediskids = ();
+
+ my $filetable = $database->{'File'};
+
+ foreach my $linenumber ( keys %{$filetable} )
+ {
+ my $comp = $filetable->{$linenumber}->{'Component_'};
+ my $uniquename = $filetable->{$linenumber}->{'File'};
+ my $filename = $filetable->{$linenumber}->{'FileName'};
+ my $sequence = $filetable->{$linenumber}->{'Sequence'};
+
+ my $shortname = "";
+ if ( $filename =~ /^\s*(.*?)\|\s*(.*?)\s*$/ )
+ {
+ $shortname = $1;
+ $filename = $2;
+ }
+
+ # unique is the combination of $component and $filename
+ my $key = "$comp/$filename";
+
+ if ( exists($uniquefilename{$key}) ) { installer::exiter::exit_program("ERROR: Component/FileName \"$key\" is not unique in table \"File\" !", "create_database_hashes"); }
+
+ my $value = $uniquename;
+ if ( $shortname ne "" ) { $value = "$uniquename;$shortname"; }
+ $uniquefilename{$key} = $value; # saving the unique keys and short names in hash
+
+ # Saving reverse keys too
+ $revuniquefilename{$uniquename} = $key;
+ if ( $shortname ne "" ) { $revshortfilename{$shortname} = $key; }
+
+ # Saving Sequences for unique names (and also components)
+ $allupdatesequences{$uniquename} = $sequence;
+ $allupdatecomponents{$uniquename} = $comp;
+
+ # Saving unique names and components for sequences
+ $allupdatefileorder{$sequence} = $uniquename;
+ $allupdatecomponentorder{$sequence} = $comp;
+ }
+
+ # 2. Hash, required in Directory table.
+
+ my $dirtable = $database->{'Directory'};
+
+ foreach my $linenumber ( keys %{$dirtable} )
+ {
+ my $dir = $dirtable->{$linenumber}->{'Directory'}; # this is a unique name
+ my $defaultdir = $dirtable->{$linenumber}->{'DefaultDir'};
+
+ my $shortname = "";
+ if ( $defaultdir =~ /^\s*(.*?)\|\s*(.*?)\s*$/ )
+ {
+ $shortname = $1;
+ $shortdirname{$dir} = $shortname; # collecting only the short names
+ }
+ }
+
+ # 3. Hash, collecting info from Component table.
+ # ComponentID and KeyPath have to be reused.
+
+ my $comptable = $database->{'Component'};
+
+ foreach my $linenumber ( keys %{$comptable} )
+ {
+ my $comp = $comptable->{$linenumber}->{'Component'};
+ my $compid = $comptable->{$linenumber}->{'ComponentId'};
+ my $keypath = $comptable->{$linenumber}->{'KeyPath'};
+
+ $componentid{$comp} = $compid;
+ $componentidkeypath{$comp} = $keypath;
+ }
+
+ # 4. Hash, property table, required for ProductCode and Installlocation.
+
+ my $proptable = $database->{'Property'};
+
+ foreach my $linenumber ( keys %{$proptable} )
+ {
+ my $prop = $proptable->{$linenumber}->{'Property'};
+ my $value = $proptable->{$linenumber}->{'Value'};
+
+ $alloldproperties{$prop} = $value;
+ }
+
+ # 5. Media table, getting last sequence
+
+ my $mediatable = $database->{'Media'};
+ $installer::globals::updatelastsequence = 0;
+
+ foreach my $linenumber ( keys %{$mediatable} )
+ {
+ my $cabname = $mediatable->{$linenumber}->{'Cabinet'};
+ my $lastsequence = $mediatable->{$linenumber}->{'LastSequence'};
+ my $diskid = $mediatable->{$linenumber}->{'DiskId'};
+ $allupdatelastsequences{$cabname} = $lastsequence;
+ $allupdatediskids{$cabname} = $diskid;
+
+ if ( $lastsequence > $installer::globals::updatelastsequence ) { $installer::globals::updatelastsequence = $lastsequence; }
+ }
+
+ $installer::globals::updatesequencecounter = $installer::globals::updatelastsequence;
+
+ return (\%uniquefilename, \%revuniquefilename, \%revshortfilename, \%allupdatesequences, \%allupdatecomponents, \%allupdatefileorder, \%allupdatecomponentorder, \%shortdirname, \%componentid, \%componentidkeypath, \%alloldproperties, \%allupdatelastsequences, \%allupdatediskids);
+}
+
+
+1; \ No newline at end of file
diff --git a/solenv/bin/modules/installer/windows/upgrade.pm b/solenv/bin/modules/installer/windows/upgrade.pm
new file mode 100644
index 000000000000..49479b26bf49
--- /dev/null
+++ b/solenv/bin/modules/installer/windows/upgrade.pm
@@ -0,0 +1,167 @@
+#*************************************************************************
+#
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+#
+# Copyright 2000, 2010 Oracle and/or its affiliates.
+#
+# OpenOffice.org - a multi-platform office productivity suite
+#
+# This file is part of OpenOffice.org.
+#
+# OpenOffice.org is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License version 3
+# only, as published by the Free Software Foundation.
+#
+# OpenOffice.org is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License version 3 for more details
+# (a copy is included in the LICENSE file that accompanied this code).
+#
+# You should have received a copy of the GNU Lesser General Public License
+# version 3 along with OpenOffice.org. If not, see
+# <http://www.openoffice.org/license.html>
+# for a copy of the LGPLv3 License.
+#
+#*************************************************************************
+
+package installer::windows::upgrade;
+
+use installer::exiter;
+use installer::files;
+use installer::globals;
+use installer::windows::idtglobal;
+
+####################################################################################
+# Creating the file Upgrade.idt dynamically
+# Content:
+# UpgradeCode VersionMin VersionMax Language Attributes Remove ActionProperty
+####################################################################################
+
+sub create_upgrade_table
+{
+ my ($basedir, $allvariableshashref) = @_;
+
+ my @upgradetable = ();
+
+ # fix for problematic OOo 1.9 versions
+ my $include_ooo_fix = 0;
+ my $ooomaxnew = "";
+ if (($installer::globals::product =~ /^\s*OpenOffice/i ) && ( ! ( $installer::globals::product =~ /SDK/i )) && ( ! $installer::globals::languagepack ))
+ {
+ $include_ooo_fix = 1;
+ $ooomaxnew = "34.0.0";
+ }
+
+ installer::windows::idtglobal::write_idt_header(\@upgradetable, "upgrade");
+
+ # Setting also $installer::globals::msimajorproductversion, that is for example "3.0.0", to differ between old products for OOo 2.x and
+ # older products from OOo 3.x. The latter must be removed always, the removal of the first is controlled with a checkbox.
+ my $newline = $installer::globals::upgradecode . "\t" . "\t" . $installer::globals::msimajorproductversion . "\t" . "\t" . "0" . "\t" . "\t" . "OLDPRODUCTS" . "\n";
+ push(@upgradetable, $newline);
+
+ # Setting all products, that must be removed.
+ $newline = $installer::globals::upgradecode . "\t" . $installer::globals::msimajorproductversion . "\t" . $installer::globals::msiproductversion . "\t" . "\t" . "257" . "\t" . "\t" . "OLDPRODUCTSSAMEMAJOR" . "\n";
+ push(@upgradetable, $newline);
+
+ if ( ! $installer::globals::patch )
+ {
+ # preventing downgrading
+ $newline = $installer::globals::upgradecode . "\t" . $installer::globals::msiproductversion . "\t" . $ooomaxnew . "\t" . "\t" . "2" . "\t" . "\t" . "NEWPRODUCTS" . "\n";
+ push(@upgradetable, $newline);
+
+ $newline = $installer::globals::upgradecode . "\t" . $installer::globals::msiproductversion . "\t" . $ooomaxnew . "\t" . "\t" . "258" . "\t" . "\t" . "SAMEPRODUCTS" . "\n";
+ push(@upgradetable, $newline);
+
+ if ( $include_ooo_fix )
+ {
+ $newline = $installer::globals::upgradecode . "\t" . "35.0.0" . "\t" . "36.0.0" . "\t" . "\t" . "1" . "\t" . "\t" . "OLDPRODUCTS2" . "\n";
+ push(@upgradetable, $newline);
+ }
+
+ # if (( $allvariableshashref->{'PATCHUPGRADECODE'} ) && ( ! $installer::globals::languagepack ))
+ # {
+ # $newline = $allvariableshashref->{'PATCHUPGRADECODE'} . "\t" . "\t" . $installer::globals::msiproductversion . "\t" . "\t" . "1" . "\t" . "\t" . "OLDPRODUCTSPATCH" . "\n";
+ # push(@upgradetable, $newline);
+ #
+ # $newline = $allvariableshashref->{'PATCHUPGRADECODE'} . "\t" . $installer::globals::msiproductversion . "\t" . "\t" . "\t" . "2" . "\t" . "\t" . "NEWPRODUCTSPATCH" . "\n";
+ # push(@upgradetable, $newline);
+ #
+ # $newline = $allvariableshashref->{'PATCHUPGRADECODE'} . "\t" . $installer::globals::msiproductversion . "\t" . "\t" . "\t" . "258" . "\t" . "\t" . "SAMEPRODUCTSPATCH" . "\n";
+ # push(@upgradetable, $newline);
+ # }
+
+ # also searching for the beta
+
+ if (( $allvariableshashref->{'BETAUPGRADECODE'} ) && ( ! $installer::globals::languagepack ))
+ {
+ $newline = $allvariableshashref->{'BETAUPGRADECODE'} . "\t" . "1.0" . "\t" . "\t" . "\t" . "1" . "\t" . "\t" . "BETAPRODUCTS" . "\n";
+ push(@upgradetable, $newline);
+ }
+
+ # also searching for the stub
+
+ if (( $allvariableshashref->{'STUBUPGRADECODE'} ) && ( ! $installer::globals::languagepack ))
+ {
+ $newline = $allvariableshashref->{'STUBUPGRADECODE'} . "\t" . "1.0" . "\t" . "\t" . "\t" . "1" . "\t" . "\t" . "STUBPRODUCTS" . "\n";
+ push(@upgradetable, $newline);
+ }
+
+ # searching for all older patches and languagepacks (defined in a extra file)
+
+ if (( $allvariableshashref->{'REMOVE_UPGRADE_CODE_FILE'} ) && ( ! $installer::globals::languagepack ))
+ {
+ my $filename = $allvariableshashref->{'REMOVE_UPGRADE_CODE_FILE'};
+ my $langpackcodefilename = $installer::globals::idttemplatepath . $installer::globals::separator . $filename;
+ if ( ! -f $langpackcodefilename ) { installer::exiter::exit_program("ERROR: Could not find file \"$langpackcodefilename\".", "create_upgrade_table"); }
+
+ my $filecontent = installer::files::read_file($langpackcodefilename);
+ my $newlines = analyze_file_for_upgrade_table($filecontent);
+
+ for ( my $i = 0; $i <= $#{$newlines}; $i++ ) { push(@upgradetable, ${$newlines}[$i]); }
+ }
+ }
+
+ # No upgrade for Beta versions!
+
+ if (( $allvariableshashref->{'PRODUCTEXTENSION'} eq "Beta" ) && ( ! $installer::globals::patch ) && ( ! $installer::globals::languagepack ))
+ {
+ @upgradetable = ();
+ installer::windows::idtglobal::write_idt_header(\@upgradetable, "upgrade");
+ my $infoline = "Beta product -> empty Upgrade table\n";
+ push(@installer::globals::logfileinfo, $infoline);
+ }
+
+ # Saving the file
+
+ my $upgradetablename = $basedir . $installer::globals::separator . "Upgrade.idt";
+ installer::files::save_file($upgradetablename ,\@upgradetable);
+ my $infoline = "Created idt file: $upgradetablename\n";
+ push(@installer::globals::logfileinfo, $infoline);
+}
+
+##############################################################
+# Reading the file with UpgradeCodes of old products,
+# that can be removed, if the user wants to remove them.
+##############################################################
+
+sub analyze_file_for_upgrade_table
+{
+ my ($filecontent) = @_;
+
+ my @allnewlines = ();
+
+ for ( my $i = 0; $i <= $#{$filecontent}; $i++ )
+ {
+ my $line = ${$filecontent}[$i];
+ if ( $line =~ /^\s*$/ ) { next; } # empty lines can be ignored
+ if ( $line =~ /^\s*\#/ ) { next; } # comment lines starting with a hash
+
+ if ( $line =~ /^(.*)\t(.*)\t(.*)\t(.*)\t(.*)\t(.*)\t(.*)$/ ) { push(@allnewlines, $line); }
+ else { installer::exiter::exit_program("ERROR: Wrong syntax in file for upgrade table", "analyze_file_for_upgrade_table"); }
+ }
+
+ return \@allnewlines;
+}
+
+1;