2 # This file is part of the LibreOffice project.
4 # This Source Code Form is subject to the terms of the Mozilla Public
5 # License, v. 2.0. If a copy of the MPL was not distributed with this
6 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 # This file incorporates work covered by the following license notice:
10 # Licensed to the Apache Software Foundation (ASF) under one or more
11 # contributor license agreements. See the NOTICE file distributed
12 # with this work for additional information regarding copyright
13 # ownership. The ASF licenses this file to you under the Apache
14 # License, Version 2.0 (the "License"); you may not use this file
15 # except in compliance with the License. You may obtain a copy of
16 # the License at http://www.apache.org/licenses/LICENSE-2.0 .
19 package installer
::windows
::update
;
21 use installer
::converter
;
22 use installer
::exiter
;
24 use installer
::globals
;
25 use installer
::pathanalyzer
;
26 use installer
::systemactions
;
28 #################################################################################
29 # Extracting all tables from an msi database
30 #################################################################################
32 sub extract_all_tables_from_msidatabase
34 my ($fulldatabasepath, $workdir) = @_;
36 my $msidb = "msidb.exe"; # Has to be in the path
40 my $extraslash = ""; # Has to be set for non-ActiveState perl
42 # Export of all tables by using "*"
44 if ( $^O
=~ /cygwin/i ) {
45 # msidb.exe really wants backslashes. (And double escaping because system() expands the string.)
46 $fulldatabasepath =~ s/\//\\\\/g
;
47 $workdir =~ s/\//\\\\/g
;
50 if ( $^O
=~ /linux/i) {
54 $systemcall = $msidb . " -d " . $fulldatabasepath . " -f " . $workdir . " -e " . $extraslash . "*";
55 $returnvalue = system($systemcall);
57 $infoline = "Systemcall: $systemcall\n";
58 push( @installer::globals
::logfileinfo
, $infoline);
62 $infoline = "ERROR: Could not execute $systemcall !\n";
63 push( @installer::globals
::logfileinfo
, $infoline);
64 installer
::exiter
::exit_program
("ERROR: Could not exclude tables from msi database: $fulldatabasepath !", "extract_all_tables_from_msidatabase");
68 $infoline = "Success: Executed $systemcall successfully!\n";
69 push( @installer::globals
::logfileinfo
, $infoline);
73 #################################################################################
74 # Collecting the keys from the first line of the idt file
75 #################################################################################
85 while ( $line =~ /^\s*(\S+?)\t(.*)$/ )
90 push(@allkeys, $onekey);
100 push(@allkeys, $onekey);
102 return (\
@allkeys, $rownumber);
105 #################################################################################
106 # Analyzing the content of one line of an idt file
107 #################################################################################
111 my ($line, $allkeys, $rownumber) = @_;
122 while ( $line =~ /^(.*?)\t(.*)$/ )
126 $onekey = ${$allkeys}[$counter];
127 $linehash{$onekey} = $value;
134 $onekey = ${$allkeys}[$counter];
136 $linehash{$onekey} = $value;
141 #################################################################################
142 # Analyzing the content of an idt file
143 #################################################################################
147 my ($filecontent) = @_;
150 # keys are written in first line
151 my ($allkeys, $rownumber) = collect_all_keys
(${$filecontent}[0]);
153 for ( my $i = 0; $i <= $#{$filecontent}; $i++ )
155 if (( $i == 0 ) || ( $i == 1 ) || ( $i == 2 )) { next; }
157 my $onelinehash = get_oneline_hash
(${$filecontent}[$i], $allkeys, $rownumber);
158 my $linekey = $i - 2; # ! : The linenumber is the unique key !? Always decrease by two, because of removed first three lines.
159 $table{$linekey} = $onelinehash;
165 #################################################################################
166 # Reading all idt files in a specified directory
167 #################################################################################
169 sub read_all_tables_from_msidatabase
177 my $allidtfiles = installer
::systemactions
::find_file_with_file_extension
($ext, $workdir);
179 for ( my $i = 0; $i <= $#{$allidtfiles}; $i++ )
181 my $onefilename = ${$allidtfiles}[$i];
182 my $longonefilename = $workdir . $installer::globals
::separator
. $onefilename;
183 if ( ! -f
$longonefilename ) { installer
::exiter
::exit_program
("ERROR: Could not find idt file: $longonefilename!", "read_all_tables_from_msidatabase"); }
184 my $filecontent = installer
::files
::read_file
($longonefilename);
185 my $idtcontent = analyze_idt_file
($filecontent);
186 my $key = $onefilename;
187 $key =~ s/\.idt\s*$//;
188 $database{$key} = $idtcontent;
194 #################################################################################
195 # Checking, if this is the correct database.
196 #################################################################################
200 my ($product, $pro, $langs, $languagestringref) = @_;
202 my $correct_database = 0;
204 # Comparing $product with $installer::globals::product and
205 # $pro with $installer::globals::pro and
206 # $langs with $languagestringref
208 my $product_is_good = 0;
210 my $localproduct = $installer::globals
::product
;
211 if ( $installer::globals
::languagepack
) { $localproduct = $localproduct . "LanguagePack"; }
212 elsif ( $installer::globals
::helppack
) { $localproduct = $localproduct . "HelpPack"; }
214 if ( $product eq $localproduct ) { $product_is_good = 1; }
216 if ( $product_is_good )
220 if ((( $pro eq "pro" ) && ( $installer::globals
::pro
)) || (( $pro eq "nonpro" ) && ( ! $installer::globals
::pro
))) { $pro_is_good = 1; }
224 my $langlisthash = installer
::converter
::convert_stringlist_into_hash
(\
$langs, ",");
225 my $langstringhash = installer
::converter
::convert_stringlist_into_hash
($languagestringref, "_");
227 my $not_included = 0;
228 foreach my $onelang ( keys %{$langlisthash} )
230 if ( ! exists($langstringhash->{$onelang}) )
237 if ( ! $not_included )
239 foreach my $onelanguage ( keys %{$langstringhash} )
241 if ( ! exists($langlisthash->{$onelanguage}) )
248 if ( ! $not_included ) { $correct_database = 1; }
253 return $correct_database;
256 #################################################################################
257 # Searching for the path to the reference database for this special product.
258 #################################################################################
260 sub get_databasename_from_list
262 my ($filecontent, $languagestringref, $filename) = @_;
264 my $databasepath = "";
266 for ( my $i = 0; $i <= $#{$filecontent}; $i++ )
268 my $line = ${$filecontent}[$i];
269 if ( $line =~ /^\s*$/ ) { next; } # empty line
270 if ( $line =~ /^\s*\#/ ) { next; } # comment line
272 if ( $line =~ /^\s*(.+?)\s*\t+\s*(.+?)\s*\t+\s*(.+?)\s*\t+\s*(.+?)\s*$/ )
279 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"); }
281 if ( correct_database
($product, $pro, $langs, $languagestringref) )
283 $databasepath = $path;
289 installer
::exiter
::exit_program
("ERROR: Wrong syntax in file: $filename! Line: \"$line\"", "get_databasename_from_list");
293 return $databasepath;
296 #################################################################################
297 # Reading an existing database completely
298 #################################################################################
302 my ($allvariables, $languagestringref, $includepatharrayref) = @_;
307 if ( ! $allvariables->{'UPDATE_DATABASE_LISTNAME'} ) { installer
::exiter
::exit_program
("ERROR: If \"UPDATE_DATABASE\" is set, \"UPDATE_DATABASE_LISTNAME\" is required.", "Main"); }
308 my $listfilename = $allvariables->{'UPDATE_DATABASE_LISTNAME'};
310 # Searching the list in the include paths
311 my $listname = installer
::scriptitems
::get_sourcepath_from_filename_and_includepath
(\
$listfilename, $includepatharrayref, 1);
312 if ( $$listname eq "" ) { installer
::exiter
::exit_program
("ERROR: List file not found: $listfilename !", "readdatabase"); }
313 my $completelistname = $$listname;
316 my $listfile = installer
::files
::read_file
($completelistname);
318 # Get name and path of reference database
319 my $databasename = get_databasename_from_list
($listfile, $languagestringref, $completelistname);
321 # If the correct database was not found, this is not necessarily an error. But in this case, this is not an update packaging process!
322 if (( $databasename ) && ( $databasename ne "" )) # This is an update packaging process!
324 $installer::globals
::updatedatabase
= 1;
325 installer
::logger
::print_message
( "... update process, using database $databasename ...\n" );
326 $infoline = "\nDatabase found in $completelistname: \"$databasename\"\n\n";
327 # Saving in global variable
328 $installer::globals
::updatedatabasepath
= $databasename;
332 $infoline = "\nNo database found in $completelistname. This is no update process!\n\n";
334 push( @installer::globals
::logfileinfo
, $infoline);
336 if ( $installer::globals
::updatedatabase
)
338 if ( ! -f
$databasename ) { installer
::exiter
::exit_program
("ERROR: Could not find reference database: $databasename!", "readdatabase"); }
340 my $msifilename = $databasename;
341 installer
::pathanalyzer
::make_absolute_filename_to_relative_filename
(\
$msifilename);
343 installer
::logger
::include_timestamp_into_logfile
("Performance Info: readdatabase start");
345 # create directory for unpacking
346 my $databasedir = installer
::systemactions
::create_directories
("database", $languagestringref);
349 my $fulldatabasepath = $databasedir . $installer::globals
::separator
. $msifilename;
350 installer
::systemactions
::copy_one_file
($databasename, $fulldatabasepath);
352 installer
::logger
::include_timestamp_into_logfile
("Performance Info: readdatabase: before extracting tables");
354 # extract all tables from database
355 extract_all_tables_from_msidatabase
($fulldatabasepath, $databasedir);
357 installer
::logger
::include_timestamp_into_logfile
("Performance Info: readdatabase: before reading tables");
360 $database = read_all_tables_from_msidatabase
($databasedir);
364 # foreach my $key1 ( keys %{$database} )
366 # print "Test1: $key1\n";
367 # foreach my $key2 ( keys %{$database->{$key1}} )
369 # print "\tTest2: $key2\n";
370 # foreach my $key3 ( keys %{$database->{$key1}->{$key2}} )
372 # print "\t\tTest3: $key3: $database->{$key1}->{$key2}->{$key3}\n";
377 # Example: File table
379 # my $filetable = $database->{'File'};
380 # foreach my $linenumber ( keys %{$filetable} )
382 # print "Test Filenumber: $linenumber\n";
383 # foreach my $key ( keys %{$filetable->{$linenumber}} )
385 # print "\t\tTest: $key: $filetable->{$linenumber}->{$key}\n";
389 # Example: Searching for ProductCode in table Property
391 # my $column1 = "Property";
392 # my $column2 = "Value";
393 # my $searchkey = "ProductCode";
394 # my $propertytable = $database->{'Property'};
395 # foreach my $linenumber ( keys %{$propertytable} )
397 # if ( $propertytable->{$linenumber}->{$column1} eq $searchkey )
399 # print("Test: $searchkey : $propertytable->{$linenumber}->{$column2}\n");
403 installer
::logger
::include_timestamp_into_logfile
("Performance Info: readdatabase end");
409 #################################################################################
410 # Files can be included in merge modules. This is also important for update.
411 #################################################################################
413 sub readmergedatabase
415 my ( $mergemodules, $languagestringref, $includepatharrayref ) = @_;
417 installer
::logger
::include_timestamp_into_logfile
("Performance Info: readmergedatabase start");
419 my $mergemoduledir = installer
::systemactions
::create_directories
("mergedatabase", $languagestringref);
421 my %allmergefiles = ();
423 foreach my $mergemodule ( @
{$mergemodules} )
425 my $filename = $mergemodule->{'Name'};
426 my $mergefile = $ENV{'MSM_PATH'} . $filename;
428 if ( ! -f
$mergefile ) { installer
::exiter
::exit_program
("ERROR: msm file not found: $filename !", "readmergedatabase"); }
429 my $completesource = $mergefile;
431 my $mergegid = $mergemodule->{'gid'};
432 my $workdir = $mergemoduledir . $installer::globals
::separator
. $mergegid;
433 if ( ! -d
$workdir ) { installer
::systemactions
::create_directory
($workdir); }
435 my $completedest = $workdir . $installer::globals
::separator
. $filename;
436 installer
::systemactions
::copy_one_file
($completesource, $completedest);
437 if ( ! -f
$completedest ) { installer
::exiter
::exit_program
("ERROR: msm file not found: $completedest !", "readmergedatabase"); }
439 # extract all tables from database
440 extract_all_tables_from_msidatabase
($completedest, $workdir);
443 my $onemergefile = read_all_tables_from_msidatabase
($workdir);
445 $allmergefiles{$mergegid} = $onemergefile;
448 foreach my $mergefilegid ( keys %allmergefiles )
450 my $onemergefile = $allmergefiles{$mergefilegid};
451 my $filetable = $onemergefile->{'File'};
453 foreach my $linenumber ( keys %{$filetable} )
455 # Collecting all files from merge modules in global hash
456 $installer::globals
::mergemodulefiles
{$filetable->{$linenumber}->{'File'}} = 1;
460 installer
::logger
::include_timestamp_into_logfile
("Performance Info: readmergedatabase end");
463 #################################################################################
464 # Creating several useful hashes from old database
465 #################################################################################
467 sub create_database_hashes
469 my ( $database ) = @_;
471 # 1. Hash ( Component -> UniqueFileName ), required in File table.
472 # Read from File table.
474 my %uniquefilename = ();
475 my %allupdatesequences = ();
476 my %allupdatecomponents = ();
477 my %allupdatefileorder = ();
478 my %allupdatecomponentorder = ();
479 my %revuniquefilename = ();
480 my %revshortfilename = ();
481 my %shortdirname = ();
482 my %componentid = ();
483 my %componentidkeypath = ();
484 my %alloldproperties = ();
485 my %allupdatelastsequences = ();
486 my %allupdatediskids = ();
488 my $filetable = $database->{'File'};
490 foreach my $linenumber ( keys %{$filetable} )
492 my $comp = $filetable->{$linenumber}->{'Component_'};
493 my $uniquename = $filetable->{$linenumber}->{'File'};
494 my $filename = $filetable->{$linenumber}->{'FileName'};
495 my $sequence = $filetable->{$linenumber}->{'Sequence'};
498 if ( $filename =~ /^\s*(.*?)\|\s*(.*?)\s*$/ )
504 # unique is the combination of $component and $filename
505 my $key = "$comp/$filename";
507 if ( exists($uniquefilename{$key}) ) { installer
::exiter
::exit_program
("ERROR: Component/FileName \"$key\" is not unique in table \"File\" !", "create_database_hashes"); }
509 my $value = $uniquename;
510 if ( $shortname ne "" ) { $value = "$uniquename;$shortname"; }
511 $uniquefilename{$key} = $value; # saving the unique keys and short names in hash
513 # Saving reverse keys too
514 $revuniquefilename{$uniquename} = $key;
515 if ( $shortname ne "" ) { $revshortfilename{$shortname} = $key; }
517 # Saving Sequences for unique names (and also components)
518 $allupdatesequences{$uniquename} = $sequence;
519 $allupdatecomponents{$uniquename} = $comp;
521 # Saving unique names and components for sequences
522 $allupdatefileorder{$sequence} = $uniquename;
523 $allupdatecomponentorder{$sequence} = $comp;
526 # 2. Hash, required in Directory table.
528 my $dirtable = $database->{'Directory'};
530 foreach my $linenumber ( keys %{$dirtable} )
532 my $dir = $dirtable->{$linenumber}->{'Directory'}; # this is a unique name
533 my $defaultdir = $dirtable->{$linenumber}->{'DefaultDir'};
536 if ( $defaultdir =~ /^\s*(.*?)\|\s*(.*?)\s*$/ )
539 $shortdirname{$dir} = $shortname; # collecting only the short names
543 # 3. Hash, collecting info from Component table.
544 # ComponentID and KeyPath have to be reused.
546 my $comptable = $database->{'Component'};
548 foreach my $linenumber ( keys %{$comptable} )
550 my $comp = $comptable->{$linenumber}->{'Component'};
551 my $compid = $comptable->{$linenumber}->{'ComponentId'};
552 my $keypath = $comptable->{$linenumber}->{'KeyPath'};
554 $componentid{$comp} = $compid;
555 $componentidkeypath{$comp} = $keypath;
558 # 4. Hash, property table, required for ProductCode and Installlocation.
560 my $proptable = $database->{'Property'};
562 foreach my $linenumber ( keys %{$proptable} )
564 my $prop = $proptable->{$linenumber}->{'Property'};
565 my $value = $proptable->{$linenumber}->{'Value'};
567 $alloldproperties{$prop} = $value;
570 # 5. Media table, getting last sequence
572 my $mediatable = $database->{'Media'};
573 $installer::globals
::updatelastsequence
= 0;
575 foreach my $linenumber ( keys %{$mediatable} )
577 my $cabname = $mediatable->{$linenumber}->{'Cabinet'};
578 my $lastsequence = $mediatable->{$linenumber}->{'LastSequence'};
579 my $diskid = $mediatable->{$linenumber}->{'DiskId'};
580 $allupdatelastsequences{$cabname} = $lastsequence;
581 $allupdatediskids{$cabname} = $diskid;
583 if ( $lastsequence > $installer::globals
::updatelastsequence
) { $installer::globals
::updatelastsequence
= $lastsequence; }
586 $installer::globals
::updatesequencecounter
= $installer::globals
::updatelastsequence
;
588 return (\
%uniquefilename, \
%revuniquefilename, \
%revshortfilename, \
%allupdatesequences, \
%allupdatecomponents, \
%allupdatefileorder, \
%allupdatecomponentorder, \
%shortdirname, \
%componentid, \
%componentidkeypath, \
%alloldproperties, \
%allupdatelastsequences, \
%allupdatediskids);