1 #*************************************************************************
3 # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 # Copyright 2008 by Sun Microsystems, Inc.
7 # OpenOffice.org - a multi-platform office productivity suite
11 # $Revision: 1.1.2.4 $
13 # This file is part of OpenOffice.org.
15 # OpenOffice.org is free software: you can redistribute it and/or modify
16 # it under the terms of the GNU Lesser General Public License version 3
17 # only, as published by the Free Software Foundation.
19 # OpenOffice.org is distributed in the hope that it will be useful,
20 # but WITHOUT ANY WARRANTY; without even the implied warranty of
21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 # GNU Lesser General Public License version 3 for more details
23 # (a copy is included in the LICENSE file that accompanied this code).
25 # You should have received a copy of the GNU Lesser General Public License
26 # version 3 along with OpenOffice.org. If not, see
27 # <http://www.openoffice.org/license.html>
28 # for a copy of the LGPLv3 License.
30 #*************************************************************************
32 package installer
::windows
::msp
;
34 use installer
::control
;
35 use installer
::converter
;
36 use installer
::exiter
;
38 use installer
::globals
;
39 use installer
::logger
;
40 use installer
::pathanalyzer
;
41 use installer
::systemactions
;
42 use installer
::windows
::admin
;
43 use installer
::windows
::idtglobal
;
44 use installer
::windows
::update
;
46 #################################################################################
47 # Making all required administrative installations
48 #################################################################################
50 sub install_installation_sets
52 my ($installationdir) = @_;
54 # Finding the msi database in the new installation set, that is located in $installationdir
56 my $msifiles = installer
::systemactions
::find_file_with_file_extension
("msi", $installationdir);
58 if ( $#{$msifiles} < 0 ) { installer::exiter::exit_program("ERROR: Did not find msi database in directory $installationdir", "create_msp_patch"); }
59 if ( $#{$msifiles} > 0 ) { installer::exiter::exit_program("ERROR: Did find more than one msi database in directory $installationdir", "create_msp_patch"); }
61 my $newinstallsetdatabasepath = $installationdir . $installer::globals
::separator
. ${$msifiles}[0];
62 my $oldinstallsetdatabasepath = $installer::globals
::updatedatabasepath
;
64 # Creating temp directory again
65 installer
::systemactions
::create_directory_structure
($installer::globals
::temppath
);
67 # Creating old installation directory
68 my $dirname = "admin";
69 my $installpath = $installer::globals
::temppath
. $installer::globals
::separator
. $dirname;
70 if ( ! -d
$installpath) { installer
::systemactions
::create_directory
($installpath); }
72 my $oldinstallpath = $installpath . $installer::globals
::separator
. "old";
73 my $newinstallpath = $installpath . $installer::globals
::separator
. "new";
75 if ( ! -d
$oldinstallpath) { installer
::systemactions
::create_directory
($oldinstallpath); }
76 if ( ! -d
$newinstallpath) { installer
::systemactions
::create_directory
($newinstallpath); }
78 my $olddatabase = installer
::windows
::admin
::make_admin_install
($oldinstallsetdatabasepath, $oldinstallpath);
79 my $newdatabase = installer
::windows
::admin
::make_admin_install
($newinstallsetdatabasepath, $newinstallpath);
81 if ( $^O
=~ /cygwin/i ) {
82 $olddatabase = qx{cygpath
-w
"$olddatabase"};
83 $olddatabase =~ s/\s*$//g;
84 $newdatabase = qx{cygpath
-w
"$newdatabase"};
85 $newdatabase =~ s/\s*$//g;
88 return ($olddatabase, $newdatabase);
91 #################################################################################
92 # Extracting all tables from a pcp file
93 #################################################################################
95 sub extract_all_tables_from_pcpfile
97 my ($fullpcpfilepath, $workdir) = @_;
99 my $msidb = "msidb.exe"; # Has to be in the path
102 my $returnvalue = "";
103 my $extraslash = ""; # Has to be set for non-ActiveState perl
105 my $localfullpcpfile = $fullpcpfilepath;
106 my $localworkdir = $workdir;
108 if ( $^O
=~ /cygwin/i ) {
109 # msidb.exe really wants backslashes. (And double escaping because system() expands the string.)
110 $localfullpcpfile =~ s/\//\\\\/g
;
111 $localworkdir =~ s/\//\\\\/g
;
115 # Export of all tables by using "*"
117 $systemcall = $msidb . " -d " . $localfullpcpfile . " -f " . $localworkdir . " -e " . $extraslash . "*";
118 $returnvalue = system($systemcall);
120 $infoline = "Systemcall: $systemcall\n";
121 push( @installer::globals
::logfileinfo
, $infoline);
125 $infoline = "ERROR: Could not execute $systemcall !\n";
126 push( @installer::globals
::logfileinfo
, $infoline);
127 installer
::exiter
::exit_program
("ERROR: Could not exclude tables from pcp file: $fullpcpfilepath !", "extract_all_tables_from_msidatabase");
131 $infoline = "Success: Executed $systemcall successfully!\n";
132 push( @installer::globals
::logfileinfo
, $infoline);
136 #################################################################################
137 # Include tables into a pcp file
138 #################################################################################
140 sub include_tables_into_pcpfile
142 my ($fullpcpfilepath, $workdir, $tables) = @_;
144 my $msidb = "msidb.exe"; # Has to be in the path
147 my $returnvalue = "";
149 # Make all table 8+3 conform
150 my $alltables = installer
::converter
::convert_stringlist_into_array
(\
$tables, " ");
152 for ( my $i = 0; $i <= $#{$alltables}; $i++ )
154 my $tablename = ${$alltables}[$i];
155 $tablename =~ s/\s*$//;
156 my $namelength = length($tablename);
157 if ( $namelength > 8 )
159 my $newtablename = substr($tablename, 0, 8); # name, offset, length
160 my $oldfile = $workdir . $installer::globals
::separator
. $tablename . ".idt";
161 my $newfile = $workdir . $installer::globals
::separator
. $newtablename . ".idt";
162 if ( -f
$newfile ) { unlink $newfile; }
163 installer
::systemactions
::copy_one_file
($oldfile, $newfile);
169 my $localworkdir = $workdir;
170 my $localfullpcpfilepath = $fullpcpfilepath;
172 if ( $^O
=~ /cygwin/i ) {
173 # msidb.exe really wants backslashes. (And double escaping because system() expands the string.)
174 $localfullpcpfilepath =~ s/\//\\\\/g
;
175 $localworkdir =~ s/\//\\\\/g
;
178 $systemcall = $msidb . " -d " . $localfullpcpfilepath . " -f " . $localworkdir . " -i " . $tables;
180 $returnvalue = system($systemcall);
182 $infoline = "Systemcall: $systemcall\n";
183 push( @installer::globals
::logfileinfo
, $infoline);
187 $infoline = "ERROR: Could not execute $systemcall !\n";
188 push( @installer::globals
::logfileinfo
, $infoline);
189 installer
::exiter
::exit_program
("ERROR: Could not include tables into pcp file: $fullpcpfilepath !", "include_tables_into_pcpfile");
193 $infoline = "Success: Executed $systemcall successfully!\n";
194 push( @installer::globals
::logfileinfo
, $infoline);
198 #################################################################################
200 #################################################################################
204 my ($fullpcpfilename, $mspfilename, $localmspdir) = @_;
206 my $msimsp = "msimsp.exe"; # Has to be in the path
209 my $returnvalue = "";
210 my $logfilename = $localmspdir . $installer::globals
::separator
. "msimsp.log";
212 # Using a specific temp for each msimsp.exe process
213 # Creating temp directory again (should already have happened)
214 installer
::systemactions
::create_directory_structure
($installer::globals
::temppath
);
216 # Creating old installation directory
217 my $dirname = "msimsptemp";
218 my $msimsptemppath = $installer::globals
::temppath
. $installer::globals
::separator
. $dirname;
219 if ( ! -d
$msimsptemppath) { installer
::systemactions
::create_directory
($msimsptemppath); }
221 # 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
223 if ( -f
$logfilename ) { unlink $logfilename; }
225 my $localfullpcpfilename = $fullpcpfilename;
226 my $localmspfilename = $mspfilename;
227 my $locallogfilename = $logfilename;
228 my $localmsimsptemppath = $msimsptemppath;
230 if ( $^O
=~ /cygwin/i ) {
231 # msimsp.exe really wants backslashes. (And double escaping because system() expands the string.)
232 $localfullpcpfilename =~ s/\//\\\\/g
;
233 $locallogfilename =~ s/\//\\\\/g
;
235 $localmspfilename =~ s/\\/\\\\/g; # path already contains backslash
236 # $localmspfilename =~ s/\//\\\\/g;
238 $localmsimsptemppath = qx{cygpath
-w
"$localmsimsptemppath"};
239 $localmsimsptemppath =~ s/\\/\\\\/g;
240 $localmsimsptemppath =~ s/\s*$//g;
243 $systemcall = $msimsp . " -s " . $localfullpcpfilename . " -p " . $localmspfilename . " -l " . $locallogfilename . " -f " . $localmsimsptemppath;
244 installer
::logger
::print_message
( "... $systemcall ...\n" );
246 $returnvalue = system($systemcall);
248 $infoline = "Systemcall: $systemcall\n";
249 push( @installer::globals
::logfileinfo
, $infoline);
253 $infoline = "ERROR: Could not execute $systemcall !\n";
254 push( @installer::globals
::logfileinfo
, $infoline);
255 installer
::exiter
::exit_program
("ERROR: Could not execute $systemcall !", "execute_msimsp");
259 $infoline = "Success: Executed $systemcall successfully!\n";
260 push( @installer::globals
::logfileinfo
, $infoline);
266 ####################################################################
267 # Checking existence and saving all tables, that need to be edited
268 ####################################################################
270 sub check_and_save_tables
272 my ($tablelist, $workdir) = @_;
274 my $tables = installer
::converter
::convert_stringlist_into_array
(\
$tablelist, " ");
276 for ( my $i = 0; $i <= $#{$tables}; $i++ )
278 my $filename = ${$tables}[$i];
279 $filename =~ s/\s*$//;
280 my $fullfilename = $workdir . $installer::globals
::separator
. $filename . ".idt";
282 if ( ! -f
$fullfilename ) { installer
::exiter
::exit_program
("ERROR: Required idt file could not be found: \"$fullfilename\"!", "check_and_save_tables"); }
284 my $savfilename = $fullfilename . ".sav";
285 installer
::systemactions
::copy_one_file
($fullfilename, $savfilename);
289 ####################################################################
290 # Setting the languages for the service packs
291 ####################################################################
293 sub create_langstring
295 my ( $languagesarrayref ) = @_;
298 for ( my $i = 0; $i <= $#{$languagesarrayref}; $i++ ) { $langstring = $langstring . "_" . ${$languagesarrayref}[$i]; }
303 ####################################################################
304 # Setting the name of the msp database
305 ####################################################################
309 my ($allvariables, $mspdir, $languagesarrayref) = @_;
311 my $databasename = $allvariables->{'PRODUCTNAME'};
312 $databasename = lc($databasename);
313 $databasename =~ s/\.//g;
314 $databasename =~ s/\-//g;
315 $databasename =~ s/\s//g;
317 if ( $allvariables->{'MSPPRODUCTVERSION'} ) { $databasename = $databasename . $allvariables->{'MSPPRODUCTVERSION'}; }
319 # possibility to overwrite the name with variable DATABASENAME
320 # if ( $allvariables->{'DATABASENAME'} ) { $databasename = $allvariables->{'DATABASENAME'}; }
322 # Adding patch info to database name
323 # if ( $installer::globals::buildid ) { $databasename = $databasename . "_" . $installer::globals::buildid; }
325 # if ( $allvariables->{'VENDORPATCHVERSION'} ) { $databasename = $databasename . "_" . $allvariables->{'VENDORPATCHVERSION'}; }
328 if (( $allvariables->{'SERVICEPACK'} ) && ( $allvariables->{'SERVICEPACK'} == 1 ))
330 my $windowspatchlevel = 0;
331 if ( $allvariables->{'MSPPATCHLEVEL'} ) { $windowspatchlevel = $allvariables->{'MSPPATCHLEVEL'}; }
332 $databasename = $databasename . "_servicepack_" . $windowspatchlevel;
333 my $languagestring = create_langstring
($languagesarrayref);
334 $databasename = $databasename . $languagestring;
338 my $hotfixaddon = "hotfix_";
339 $hotfixaddon = $hotfixaddon . $installer::globals
::buildid
;
341 if ( $ENV{'CWS_WORK_STAMP'} ) { $hotfixaddon = $ENV{'CWS_WORK_STAMP'}; }
342 if ( $allvariables->{'OVERWRITE_CWSNAME'} ) { $hotfixaddon = $allvariables->{'OVERWRITE_CWSNAME'}; }
343 $databasename = $databasename . "_" . $hotfixaddon;
346 $databasename = $databasename . ".msp";
348 my $fullmspname = $mspdir . $installer::globals
::separator
. $databasename;
350 if ( $^O
=~ /cygwin/i ) { $fullmspname =~ s/\//\\/g
; }
355 ####################################################################
356 # Editing table Properties
357 ####################################################################
359 sub change_properties_table
361 my ($localmspdir, $mspfilename) = @_;
363 my $infoline = "Changing content of table \"Properties\"\n";
364 push( @installer::globals
::logfileinfo
, $infoline);
366 my $filename = $localmspdir . $installer::globals
::separator
. "Properties.idt";
367 if ( ! -f
$filename ) { installer
::exiter
::exit_program
("ERROR: Could not find file \"$filename\" !", "change_properties_table"); }
369 my $filecontent = installer
::files
::read_file
($filename);
372 my $guidref = installer
::windows
::msiglobal
::get_guid_list
(1, 1);
373 ${$guidref}[0] =~ s/\s*$//; # removing ending spaces
374 my $patchcode = "\{" . ${$guidref}[0] . "\}";
376 # Setting "PatchOutputPath"
377 my $found_patchoutputpath = 0;
378 my $found_patchguid = 0;
380 for ( my $i = 0; $i <= $#{$filecontent}; $i++ )
382 if ( ${$filecontent}[$i] =~ /^\s*PatchOutputPath\t(.*?)\s*$/ )
385 ${$filecontent}[$i] =~ s/\Q$oldvalue\E/$mspfilename/;
386 $found_patchoutputpath = 1;
389 if ( ${$filecontent}[$i] =~ /^\s*PatchGUID\t(.*?)\s*$/ )
392 ${$filecontent}[$i] =~ s/\Q$oldvalue\E/$patchcode/;
393 $found_patchguid = 1;
397 if ( ! $found_patchoutputpath )
399 my $newline = "PatchOutputPath\t$mspfilename\n";
400 push(@
{$filecontent}, $newline);
403 if ( ! $found_patchguid )
405 my $newline = "PatchGUID\t$patchcode\n";
406 push(@
{$filecontent}, $newline);
410 installer
::files
::save_file
($filename, $filecontent);
413 ####################################################################
414 # Editing table TargetImages
415 ####################################################################
417 sub change_targetimages_table
419 my ($localmspdir, $olddatabase) = @_;
421 my $infoline = "Changing content of table \"TargetImages\"\n";
422 push( @installer::globals
::logfileinfo
, $infoline);
424 my $filename = $localmspdir . $installer::globals
::separator
. "TargetImages.idt";
425 if ( ! -f
$filename ) { installer
::exiter
::exit_program
("ERROR: Could not find file \"$filename\" !", "change_targetimages_table"); }
427 my $filecontent = installer
::files
::read_file
($filename);
431 for ( my $i = 0; $i <= $#{$filecontent}; $i++ ) { if ( $i < 3 ) { push(@newcontent, ${$filecontent}[$i]); } }
434 my $newline = "T1\t$olddatabase\t\tU1\t1\t0x00000922\t1\n";
435 push(@newcontent, $newline);
438 installer
::files
::save_file
($filename, \
@newcontent);
441 ####################################################################
442 # Editing table UpgradedImages
443 ####################################################################
445 sub change_upgradedimages_table
447 my ($localmspdir, $newdatabase) = @_;
449 my $infoline = "Changing content of table \"UpgradedImages\"\n";
450 push( @installer::globals
::logfileinfo
, $infoline);
452 my $filename = $localmspdir . $installer::globals
::separator
. "UpgradedImages.idt";
453 if ( ! -f
$filename ) { installer
::exiter
::exit_program
("ERROR: Could not find file \"$filename\" !", "change_upgradedimages_table"); }
455 my $filecontent = installer
::files
::read_file
($filename);
459 for ( my $i = 0; $i <= $#{$filecontent}; $i++ ) { if ( $i < 3 ) { push(@newcontent, ${$filecontent}[$i]); } }
461 # Syntax: Upgraded MsiPath PatchMsiPath SymbolPaths Family
465 my $msipath = $newdatabase;
466 my $patchmsipath = "";
467 my $symbolpaths = "";
468 my $family = "22334455";
470 if ( $#{$filecontent} >= 3 )
472 my $line = ${$filecontent}[3];
473 if ( $line =~ /^\s*(.*?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\s*$/ )
482 #Adding sequence line, saving PatchFamily
483 my $newline = "$upgraded\t$msipath\t$patchmsipath\t$symbolpaths\t$family\n";
484 push(@newcontent, $newline);
487 installer
::files
::save_file
($filename, \
@newcontent);
490 ####################################################################
491 # Editing table ImageFamilies
492 ####################################################################
494 sub change_imagefamilies_table
496 my ($localmspdir) = @_;
498 my $infoline = "Changing content of table \"ImageFamilies\"\n";
499 push( @installer::globals
::logfileinfo
, $infoline);
501 my $filename = $localmspdir . $installer::globals
::separator
. "ImageFamilies.idt";
502 if ( ! -f
$filename ) { installer
::exiter
::exit_program
("ERROR: Could not find file \"$filename\" !", "change_imagefamilies_table"); }
504 my $filecontent = installer
::files
::read_file
($filename);
508 for ( my $i = 0; $i <= $#{$filecontent}; $i++ ) { if ( $i < 3 ) { push(@newcontent, ${$filecontent}[$i]); } }
510 # Syntax: Family MediaSrcPropName MediaDiskId FileSequenceStart DiskPrompt VolumeLabel
511 # "FileSequenceStart has to be set
515 my $family = "22334455";
516 my $mediasrcpropname = "MediaSrcPropName";
517 my $mediadiskid = "2";
518 my $filesequencestart = get_filesequencestart
();
520 my $volumelabel = "";
522 if ( $#{$filecontent} >= 3 )
524 my $line = ${$filecontent}[3];
525 if ( $line =~ /^\s*(.*?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\s*$/ )
528 $mediasrcpropname = $2;
535 #Adding sequence line
536 my $newline = "$family\t$mediasrcpropname\t$mediadiskid\t$filesequencestart\t$diskprompt\t$volumelabel\n";
537 push(@newcontent, $newline);
540 installer
::files
::save_file
($filename, \
@newcontent);
543 ####################################################################
544 # Setting start sequence for patch
545 ####################################################################
547 sub get_filesequencestart
549 my $sequence = 1000; # default
551 if ( $installer::globals
::updatelastsequence
) { $sequence = $installer::globals
::updatelastsequence
+ 500; }
556 ####################################################################
557 # Setting time value into pcp file
558 # Format mm/dd/yyyy hh:mm
559 ####################################################################
561 sub get_patchtime_value
563 # Syntax: 8/8/2008 11:55
564 my $minute = (localtime())[1];
565 my $hour = (localtime())[2];
566 my $day = (localtime())[3];
567 my $month = (localtime())[4];
568 my $year = 1900 + (localtime())[5];
570 $month++; # zero based month
571 if ( $minute < 10 ) { $minute = "0" . $minute; }
572 if ( $hour < 10 ) { $hour = "0" . $hour; }
574 my $timestring = $month . "/" . $day . "/" . $year . " " . $hour . ":" . $minute;
579 #################################################################################
580 # Checking, if this is the correct database.
581 #################################################################################
585 my ($langs, $languagestringref) = @_;
587 my $correct_langs = 0;
589 # Comparing $langs with $languagestringref
591 my $langlisthash = installer
::converter
::convert_stringlist_into_hash
(\
$langs, ",");
592 my $langstringhash = installer
::converter
::convert_stringlist_into_hash
($languagestringref, "_");
594 my $not_included = 0;
595 foreach my $onelang ( keys %{$langlisthash} )
597 if ( ! exists($langstringhash->{$onelang}) )
604 if ( ! $not_included )
606 foreach my $onelanguage ( keys %{$langstringhash} )
608 if ( ! exists($langlisthash->{$onelanguage}) )
615 if ( ! $not_included ) { $correct_langs = 1; }
618 return $correct_langs;
621 #################################################################################
622 # Searching for the path to the reference database for this special product.
623 #################################################################################
625 sub get_patchid_from_list
627 my ($filecontent, $languagestringref, $filename) = @_;
631 for ( my $i = 0; $i <= $#{$filecontent}; $i++ )
633 my $line = ${$filecontent}[$i];
634 if ( $line =~ /^\s*$/ ) { next; } # empty line
635 if ( $line =~ /^\s*\#/ ) { next; } # comment line
637 if ( $line =~ /^\s*(.+?)\s*=\s*(.+?)\s*$/ )
640 my $localpatchid = $2;
642 if ( correct_langs
($langs, $languagestringref) )
644 $patchid = $localpatchid;
650 installer
::exiter
::exit_program
("ERROR: Wrong syntax in file: $filename! Line: \"$line\"", "get_patchid_from_list");
657 ####################################################################
658 # Editing table PatchMetadata
659 ####################################################################
661 sub change_patchmetadata_table
663 my ($localmspdir, $allvariables, $languagestringref) = @_;
665 my $infoline = "Changing content of table \"PatchMetadata\"\n";
666 push( @installer::globals
::logfileinfo
, $infoline);
668 my $filename = $localmspdir . $installer::globals
::separator
. "PatchMetadata.idt";
669 if ( ! -f
$filename ) { installer
::exiter
::exit_program
("ERROR: Could not find file \"$filename\" !", "change_patchmetadata_table"); }
671 my $filecontent = installer
::files
::read_file
($filename);
674 # Syntax: Company Property Value
675 # Interesting properties: "Classification" and "CreationTimeUTC"
677 my $classification_set = 0;
678 my $creationtime_set = 0;
679 my $targetproductname_set = 0;
680 my $manufacturer_set = 0;
681 my $displayname_set = 0;
682 my $description_set = 0;
683 my $allowremoval_set = 0;
685 my $defaultcompany = "";
687 my $classificationstring = "Classification";
688 my $classificationvalue = "Hotfix";
689 if (( $allvariables->{'SERVICEPACK'} ) && ( $allvariables->{'SERVICEPACK'} == 1 )) { $classificationvalue = "ServicePack"; }
691 my $allowremovalstring = "AllowRemoval";
692 my $allowremovalvalue = "1";
693 if (( exists($allvariables->{'MSPALLOWREMOVAL'}) ) && ( $allvariables->{'MSPALLOWREMOVAL'} == 0 )) { $allowremovalvalue = 0; }
695 my $timestring = "CreationTimeUTC";
696 # Syntax: 8/8/2008 11:55
697 my $timevalue = get_patchtime_value
();
699 my $targetproductnamestring = "TargetProductName";
700 my $targetproductnamevalue = $allvariables->{'PRODUCTNAME'};
701 if ( $allvariables->{'PROPERTYTABLEPRODUCTNAME'} ) { $targetproductnamevalue = $allvariables->{'PROPERTYTABLEPRODUCTNAME'}; }
703 my $manufacturerstring = "ManufacturerName";
704 my $manufacturervalue = "OpenOffice.org";
705 if ( $installer::globals
::longmanufacturer
) { $manufacturervalue = $installer::globals
::longmanufacturer
; }
707 my $displaynamestring = "DisplayName";
708 my $descriptionstring = "Description";
709 my $displaynamevalue = "";
710 my $descriptionvalue = "";
712 my $base = $allvariables->{'PRODUCTNAME'} . " " . $allvariables->{'PRODUCTVERSION'};
713 if ( $installer::globals
::languagepack
) { $base = $targetproductnamevalue; }
715 my $windowspatchlevel = 0;
716 if ( $allvariables->{'WINDOWSPATCHLEVEL'} ) { $windowspatchlevel = $allvariables->{'WINDOWSPATCHLEVEL'}; }
718 my $displayaddon = "";
719 if ( $allvariables->{'PATCHDISPLAYADDON'} ) { $displayaddon = $allvariables->{'PATCHDISPLAYADDON'}; }
722 if ( $ENV{'CWS_WORK_STAMP'} ) { $cwsname = $ENV{'CWS_WORK_STAMP'}; }
723 if (( $cwsname ne "" ) && ( $allvariables->{'OVERWRITE_CWSNAME'} )) { $cwsname = $allvariables->{'OVERWRITE_CWSNAME'}; }
725 my $patchsequence = get_patchsequence
($allvariables);
727 if (( $allvariables->{'SERVICEPACK'} ) && ( $allvariables->{'SERVICEPACK'} == 1 ))
729 $displaynamevalue = $base . " ServicePack " . $windowspatchlevel . " " . $patchsequence . " Build: " . $installer::globals
::buildid
;
730 $descriptionvalue = $base . " ServicePack " . $windowspatchlevel . " " . $patchsequence . " Build: " . $installer::globals
::buildid
;
734 $displaynamevalue = $base . " Hotfix " . $cwsname . " " . $displayaddon . " " . $patchsequence . " Build: " . $installer::globals
::buildid
;
735 $descriptionvalue = $base . " Hotfix " . $cwsname . " " . $displayaddon . " " . $patchsequence . " Build: " . $installer::globals
::buildid
;
736 $displaynamevalue =~ s/ / /g;
737 $descriptionvalue =~ s/ / /g;
738 $displaynamevalue =~ s/ / /g;
739 $descriptionvalue =~ s/ / /g;
740 $displaynamevalue =~ s/ / /g;
741 $descriptionvalue =~ s/ / /g;
744 if ( $allvariables->{'MSPPATCHNAMELIST'} )
746 my $patchnamelistfile = $allvariables->{'MSPPATCHNAMELIST'};
747 $patchnamelistfile = $installer::globals
::idttemplatepath
. $installer::globals
::separator
. $patchnamelistfile;
748 if ( ! -f
$patchnamelistfile ) { installer
::exiter
::exit_program
("ERROR: Could not find file \"$patchnamelistfile\".", "change_patchmetadata_table"); }
749 my $filecontent = installer
::files
::read_file
($patchnamelistfile);
751 # Get name and path of reference database
752 my $patchid = get_patchid_from_list
($filecontent, $languagestringref, $patchnamelistfile);
754 if ( $patchid eq "" ) { installer
::exiter
::exit_program
("ERROR: Could not find file patchid in file \"$patchnamelistfile\" for language(s) \"$$languagestringref\".", "change_patchmetadata_table"); }
756 # Setting language specific patch id
759 for ( my $i = 0; $i <= $#{$filecontent}; $i++ )
761 if ( ${$filecontent}[$i] =~ /^\s*(.*?)\t(.*?)\t(.*?)\s*$/ )
767 if ( $property eq $classificationstring )
769 ${$filecontent}[$i] = "$company\t$property\t$classificationvalue\n";
770 $classification_set = 1;
773 if ( $property eq $allowremovalstring )
775 ${$filecontent}[$i] = "$company\t$property\t$allowremovalvalue\n";
776 $allowremoval_set = 1;
779 if ( $property eq $timestring )
781 ${$filecontent}[$i] = "$company\t$property\t$timevalue\n";
782 $creationtime_set = 1;
785 if ( $property eq $targetproductnamestring )
787 ${$filecontent}[$i] = "$company\t$property\t$targetproductnamevalue\n";
788 $targetproductname_set = 1;
791 if ( $property eq $manufacturerstring )
793 ${$filecontent}[$i] = "$company\t$property\t$manufacturervalue\n";
794 $manufacturer_set = 1;
797 if ( $property eq $displaynamestring )
799 ${$filecontent}[$i] = "$company\t$property\t$displaynamevalue\n";
800 $displayname_set = 1;
803 if ( $property eq $descriptionstring )
805 ${$filecontent}[$i] = "$company\t$property\t$descriptionvalue\n";
806 $description_set = 1;
810 push(@newcontent, ${$filecontent}[$i]);
813 if ( ! $classification_set )
815 my $line = "$defaultcompany\t$classificationstring\t$classificationvalue\n";
816 push(@newcontent, $line);
819 if ( ! $allowremoval_set )
821 my $line = "$defaultcompany\t$classificationstring\t$allowremovalvalue\n";
822 push(@newcontent, $line);
825 if ( ! $allowremoval_set )
827 my $line = "$defaultcompany\t$classificationstring\t$allowremovalvalue\n";
828 push(@newcontent, $line);
831 if ( ! $creationtime_set )
833 my $line = "$defaultcompany\t$timestring\t$timevalue\n";
834 push(@newcontent, $line);
837 if ( ! $targetproductname_set )
839 my $line = "$defaultcompany\t$targetproductnamestring\t$targetproductnamevalue\n";
840 push(@newcontent, $line);
843 if ( ! $manufacturer_set )
845 my $line = "$defaultcompany\t$manufacturerstring\t$manufacturervalue\n";
846 push(@newcontent, $line);
849 if ( ! $displayname_set )
851 my $line = "$defaultcompany\t$displaynamestring\t$displaynamevalue\n";
852 push(@newcontent, $line);
855 if ( ! $description_set )
857 my $line = "$defaultcompany\t$descriptionstring\t$descriptionvalue\n";
858 push(@newcontent, $line);
862 installer
::files
::save_file
($filename, \
@newcontent);
865 ####################################################################
866 # Editing table PatchSequence
867 ####################################################################
869 sub change_patchsequence_table
871 my ($localmspdir, $allvariables) = @_;
873 my $infoline = "Changing content of table \"PatchSequence\"\n";
874 push( @installer::globals
::logfileinfo
, $infoline);
876 my $filename = $localmspdir . $installer::globals
::separator
. "PatchSequence.idt";
877 if ( ! -f
$filename ) { installer
::exiter
::exit_program
("ERROR: Could not find file \"$filename\" !", "change_patchsequence_table"); }
879 my $filecontent = installer
::files
::read_file
($filename);
883 for ( my $i = 0; $i <= $#{$filecontent}; $i++ ) { if ( $i < 3 ) { push(@newcontent, ${$filecontent}[$i]); } }
885 # Syntax: PatchFamily Target Sequence Supersede
887 my $patchfamily = "SO";
889 my $patchsequence = get_patchsequence
($allvariables);
890 my $supersede = get_supersede
($allvariables);
892 if ( $#{$filecontent} >= 3 )
894 my $line = ${$filecontent}[3];
895 if ( $line =~ /^\s*(.*?)\t(.*?)\t(.*?)\t(.*?)\s$/ )
902 #Adding sequence line, saving PatchFamily
903 my $newline = "$patchfamily\t$target\t$patchsequence\t$supersede\n";
904 push(@newcontent, $newline);
907 installer
::files
::save_file
($filename, \
@newcontent);
910 ####################################################################
911 # Setting supersede, "0" for Hotfixes, "1" for ServicePack
912 ####################################################################
916 my ( $allvariables ) = @_;
918 my $supersede = 0; # if not defined, this is a Hotfix
920 if (( $allvariables->{'SERVICEPACK'} ) && ( $allvariables->{'SERVICEPACK'} == 1 )) { $supersede = 1; }
925 ####################################################################
926 # Setting the sequence of the patch
927 ####################################################################
929 sub get_patchsequence
931 my ( $allvariables ) = @_;
933 my $patchsequence = "1.0";
935 if ( ! $allvariables->{'PACKAGEVERSION'} ) { installer
::exiter
::exit_program
("ERROR: PACKAGEVERSION must be set for msp patch creation!", "get_patchsequence"); }
937 my $packageversion = $allvariables->{'PACKAGEVERSION'};
939 if ( $packageversion =~ /^\s*(\d+)\.(\d+)\.(\d+)\s*$/ )
944 my $concat = 100 * $minor + $micro;
945 $packageversion = $major . "\." . $concat;
947 my $vendornumber = 0;
948 if ( $allvariables->{'VENDORPATCHVERSION'} ) { $vendornumber = $allvariables->{'VENDORPATCHVERSION'}; }
949 $patchsequence = $packageversion . "\." . $installer::globals
::buildid
. "\." . $vendornumber;
951 if ( $allvariables->{'PATCHSEQUENCE'} ) { $patchsequence = $allvariables->{'PATCHSEQUENCE'}; }
953 return $patchsequence;
956 ####################################################################
957 # Editing all tables from pcp file, that need to be edited
958 ####################################################################
962 my ($tablelist, $localmspdir, $olddatabase, $newdatabase, $mspfilename, $allvariables, $languagestringref) = @_;
964 # table list contains: my $tablelist = "Properties TargetImages UpgradedImages ImageFamilies PatchMetadata PatchSequence";
966 change_properties_table
($localmspdir, $mspfilename);
967 change_targetimages_table
($localmspdir, $olddatabase);
968 change_upgradedimages_table
($localmspdir, $newdatabase);
969 change_imagefamilies_table
($localmspdir);
970 change_patchmetadata_table
($localmspdir, $allvariables, $languagestringref);
971 change_patchsequence_table
($localmspdir, $allvariables);
974 #################################################################################
975 # Checking, if this is the correct database.
976 #################################################################################
980 my ($product, $pro, $langs, $languagestringref) = @_;
982 my $correct_patch = 0;
984 # Comparing $product with $installer::globals::product and
985 # $pro with $installer::globals::pro and
986 # $langs with $languagestringref
988 my $product_is_good = 0;
990 my $localproduct = $installer::globals
::product
;
991 if ( $installer::globals
::languagepack
) { $localproduct = $localproduct . "LanguagePack"; }
993 if ( $product eq $localproduct ) { $product_is_good = 1; }
995 if ( $product_is_good )
999 if ((( $pro eq "pro" ) && ( $installer::globals
::pro
)) || (( $pro eq "nonpro" ) && ( ! $installer::globals
::pro
))) { $pro_is_good = 1; }
1003 my $langlisthash = installer
::converter
::convert_stringlist_into_hash
(\
$langs, ",");
1004 my $langstringhash = installer
::converter
::convert_stringlist_into_hash
($languagestringref, "_");
1006 my $not_included = 0;
1007 foreach my $onelang ( keys %{$langlisthash} )
1009 if ( ! exists($langstringhash->{$onelang}) )
1016 if ( ! $not_included )
1018 foreach my $onelanguage ( keys %{$langstringhash} )
1020 if ( ! exists($langlisthash->{$onelanguage}) )
1027 if ( ! $not_included ) { $correct_patch = 1; }
1032 return $correct_patch;
1035 #################################################################################
1036 # Searching for the path to the required patch for this special product.
1037 #################################################################################
1039 sub get_requiredpatchfile_from_list
1041 my ($filecontent, $languagestringref, $filename) = @_;
1045 for ( my $i = 0; $i <= $#{$filecontent}; $i++ )
1047 my $line = ${$filecontent}[$i];
1048 if ( $line =~ /^\s*$/ ) { next; } # empty line
1049 if ( $line =~ /^\s*\#/ ) { next; } # comment line
1051 if ( $line =~ /^\s*(.+?)\s*\t+\s*(.+?)\s*\t+\s*(.+?)\s*\t+\s*(.+?)\s*$/ )
1058 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"); }
1060 if ( correct_patch
($product, $pro, $langs, $languagestringref) )
1068 installer
::exiter
::exit_program
("ERROR: Wrong syntax in file: $filename! Line: \"$line\"", "get_requiredpatchfile_from_list");
1075 ##################################################################
1076 # Converting unicode file to ascii
1077 # to be more precise: uft-16 little endian to ascii
1078 ##################################################################
1080 sub convert_unicode_to_ascii
1082 my ( $filename ) = @_;
1086 my $savfilename = $filename . "_before.unicode";
1087 installer
::systemactions
::copy_one_file
($filename, $savfilename);
1089 # open( IN, "<:utf16", $filename ) || installer::exiter::exit_program("ERROR: Cannot open file $filename for reading", "convert_unicode_to_ascii");
1090 # open( IN, "<:para:crlf:uni", $filename ) || installer::exiter::exit_program("ERROR: Cannot open file $filename for reading", "convert_unicode_to_ascii");
1091 open( IN
, "<:encoding(UTF16-LE)", $filename ) || installer
::exiter
::exit_program
("ERROR: Cannot open file $filename for reading", "convert_unicode_to_ascii");
1092 # open( IN, "<:encoding(UTF-8)", $filename ) || installer::exiter::exit_program("ERROR: Cannot open file $filename for reading", "convert_unicode_to_ascii");
1093 while ( $line = <IN
> ) {
1094 push @localfile, $line;
1098 if ( open( OUT
, ">", $filename ) )
1100 print OUT
@localfile;
1105 ####################################################################
1106 # Analyzing the log file created by msimsp.exe to find all
1107 # files included into the patch.
1108 ####################################################################
1110 sub analyze_msimsp_logfile
1112 my ($logfile, $filesarray) = @_;
1114 # Reading log file after converting from utf-16 (LE) to ascii
1115 convert_unicode_to_ascii
($logfile);
1116 my $logfilecontent = installer
::files
::read_file
($logfile);
1118 # Creating hash from $filesarray: unique file name -> destination of file
1120 my %destinationcollector = ();
1122 for ( my $i = 0; $i <= $#{$filesarray}; $i++ )
1124 my $onefile = ${$filesarray}[$i];
1126 # Only collecting files with "uniquename" and "destination"
1127 if (( exists($onefile->{'uniquename'}) ) && ( exists($onefile->{'uniquename'}) ))
1129 my $uniquefilename = $onefile->{'uniquename'};
1130 my $destpath = $onefile->{'destination'};
1131 $filehash{$uniquefilename} = $destpath;
1135 # Analyzing log file of msimsp.exe, finding all changed files
1136 # and searching all destinations of unique file names.
1137 # Content in log file: "INFO File Key: <file key> is modified"
1138 # Collecting content in @installer::globals::patchfilecollector
1140 for ( my $i = 0; $i <= $#{$logfilecontent}; $i++ )
1142 if ( ${$logfilecontent}[$i] =~ /Key\:\s*(.*?) is modified\s*$/ )
1145 if ( exists($filehash{$filekey}) ) { $destinationcollector{$filehash{$filekey}} = 1; }
1146 else { installer
::exiter
::exit_program
("ERROR: Could not find file key \"$filekey\" in file collector.", "analyze_msimsp_logfile"); }
1150 foreach my $onedest ( sort keys %destinationcollector ) { push(@installer::globals
::patchfilecollector
, "$onedest\n"); }
1154 ####################################################################
1155 # Creating msp patch files for Windows
1156 ####################################################################
1158 sub create_msp_patch
1160 my ($installationdir, $includepatharrayref, $allvariables, $languagestringref, $languagesarrayref, $filesarray) = @_;
1162 my $force = 1; # print this message even in 'quiet' mode
1163 installer
::logger
::print_message
( "\n******************************************\n" );
1164 installer
::logger
::print_message
( "... creating msp installation set ...\n", $force );
1165 installer
::logger
::print_message
( "******************************************\n" );
1167 $installer::globals
::creating_windows_installer_patch
= 1;
1169 my @needed_files = ("msimsp.exe"); # only required for patch creation process
1170 installer
::control
::check_needed_files_in_path
(\
@needed_files);
1172 installer
::logger
::include_header_into_logfile
("Creating msp installation sets:");
1174 my $firstdir = $installationdir;
1175 installer
::pathanalyzer
::get_path_from_fullqualifiedname
(\
$firstdir);
1177 my $lastdir = $installationdir;
1178 installer
::pathanalyzer
::make_absolute_filename_to_relative_filename
(\
$lastdir);
1180 if ( $lastdir =~ /\./ ) { $lastdir =~ s/\./_msp_inprogress\./ }
1181 else { $lastdir = $lastdir . "_msp_inprogress"; }
1183 # Removing existing directory "_native_packed_inprogress" and "_native_packed_witherror" and "_native_packed"
1185 my $mspdir = $firstdir . $lastdir;
1186 if ( -d
$mspdir ) { installer
::systemactions
::remove_complete_directory
($mspdir); }
1188 my $olddir = $mspdir;
1189 $olddir =~ s/_inprogress/_witherror/;
1190 if ( -d
$olddir ) { installer
::systemactions
::remove_complete_directory
($olddir); }
1193 $olddir =~ s/_inprogress//;
1194 if ( -d
$olddir ) { installer
::systemactions
::remove_complete_directory
($olddir); }
1196 # Creating the new directory for new installation set
1197 installer
::systemactions
::create_directory
($mspdir);
1199 $installer::globals
::saveinstalldir
= $mspdir;
1201 installer
::logger
::include_timestamp_into_logfile
("\nPerformance Info: Starting product installation");
1203 # Installing both installation sets
1204 installer
::logger
::print_message
( "... installing products ...\n" );
1205 my ($olddatabase, $newdatabase) = install_installation_sets
($installationdir);
1207 installer
::logger
::include_timestamp_into_logfile
("\nPerformance Info: Starting pcp file creation");
1210 installer
::logger
::print_message
( "... creating pcp file ...\n" );
1212 my $localmspdir = installer
::systemactions
::create_directories
("msp", $languagestringref);
1214 if ( ! $allvariables->{'PCPFILENAME'} ) { installer
::exiter
::exit_program
("ERROR: Property \"PCPFILENAME\" has to be defined.", "create_msp_patch"); }
1215 my $pcpfilename = $allvariables->{'PCPFILENAME'};
1217 if ( $installer::globals
::languagepack
) { $pcpfilename =~ s/.pcp\s*$/languagepack.pcp/; }
1219 # Searching the pcp file in the include pathes
1220 my $fullpcpfilenameref = installer
::scriptitems
::get_sourcepath_from_filename_and_includepath
(\
$pcpfilename, $includepatharrayref, 1);
1221 if ( $$fullpcpfilenameref eq "" ) { installer
::exiter
::exit_program
("ERROR: pcp file not found: $pcpfilename !", "create_msp_patch"); }
1222 my $fullpcpfilenamesource = $$fullpcpfilenameref;
1225 my $fullpcpfilename = $localmspdir . $installer::globals
::separator
. $pcpfilename;
1226 installer
::systemactions
::copy_one_file
($fullpcpfilenamesource, $fullpcpfilename);
1228 # a. Extracting tables from msi database: msidb.exe -d <msifile> -f <directory> -e File Media, ...
1229 # b. Changing content of msi database in tables: File, Media, Directory, FeatureComponent
1230 # c. Including tables into msi database: msidb.exe -d <msifile> -f <directory> -i File Media, ...
1232 # Unpacking tables from pcp file
1233 extract_all_tables_from_pcpfile
($fullpcpfilename, $localmspdir);
1235 # Tables, that need to be edited
1236 my $tablelist = "Properties TargetImages UpgradedImages ImageFamilies PatchMetadata PatchSequence"; # required tables
1239 check_and_save_tables
($tablelist, $localmspdir);
1241 # Setting the name of the new msp file
1242 my $mspfilename = set_mspfilename
($allvariables, $mspdir, $languagesarrayref);
1245 edit_tables
($tablelist, $localmspdir, $olddatabase, $newdatabase, $mspfilename, $allvariables, $languagestringref);
1247 # Adding edited tables into pcp file
1248 include_tables_into_pcpfile
($fullpcpfilename, $localmspdir, $tablelist);
1251 installer
::logger
::include_timestamp_into_logfile
("\nPerformance Info: Starting msimsp.exe");
1252 my $msimsplogfile = execute_msimsp
($fullpcpfilename, $mspfilename, $localmspdir);
1254 # Copy final installation set next to msp file
1255 installer
::logger
::include_timestamp_into_logfile
("\nPerformance Info: Copying installation set");
1256 installer
::logger
::print_message
( "... copying installation set ...\n" );
1258 my $oldinstallationsetpath = $installer::globals
::updatedatabasepath
;
1260 if ( $^O
=~ /cygwin/i ) { $oldinstallationsetpath =~ s/\\/\//g
; }
1262 installer
::pathanalyzer
::get_path_from_fullqualifiedname
(\
$oldinstallationsetpath);
1263 installer
::systemactions
::copy_complete_directory
($oldinstallationsetpath, $mspdir);
1265 # Copying additional patches into the installation set, if required
1266 if (( $allvariables->{'ADDITIONALREQUIREDPATCHES'} ) && ( $allvariables->{'ADDITIONALREQUIREDPATCHES'} ne "" ) && ( ! $installer::globals
::languagepack
))
1268 my $filename = $allvariables->{'ADDITIONALREQUIREDPATCHES'};
1270 my $fullfilenameref = installer
::scriptitems
::get_sourcepath_from_filename_and_includepath
(\
$filename, $includepatharrayref, 1);
1271 if ( $$fullfilenameref eq "" ) { installer
::exiter
::exit_program
("ERROR: Could not find file with required patches, although it is defined: $filename !", "create_msp_patch"); }
1272 my $fullfilename = $$fullfilenameref;
1275 my $listfile = installer
::files
::read_file
($fullfilename);
1277 # Get name and path of reference database
1278 my $requiredpatchfile = get_requiredpatchfile_from_list
($listfile, $languagestringref, $fullfilename);
1279 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"); }
1281 # Copying patch file
1282 installer
::systemactions
::copy_one_file
($requiredpatchfile, $mspdir);
1283 # my $infoline = "Copy $requiredpatchfile to $mspdir\n";
1284 # push( @installer::globals::logfileinfo, $infoline);
1287 # Find all files included into the patch
1288 # Analyzing the msimsp log file $msimsplogfile
1289 analyze_msimsp_logfile
($msimsplogfile, $filesarray);
1292 installer
::logger
::include_timestamp_into_logfile
("\nPerformance Info: msp creation done");