1# 2# This file is part of the LibreOffice project. 3# 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/. 7# 8# This file incorporates work covered by the following license notice: 9# 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 . 17# 18 19package installer::windows::msp; 20 21use File::Copy; 22use installer::control; 23use installer::converter; 24use installer::exiter; 25use installer::files; 26use installer::globals; 27use installer::logger; 28use installer::pathanalyzer; 29use installer::systemactions; 30use installer::windows::admin; 31use installer::windows::idtglobal; 32use installer::windows::update; 33 34################################################################################# 35# Making all required administrative installations 36################################################################################# 37 38sub install_installation_sets 39{ 40 my ($installationdir) = @_; 41 42 # Finding the msi database in the new installation set, that is located in $installationdir 43 44 my $msifiles = installer::systemactions::find_file_with_file_extension("msi", $installationdir); 45 46 if ( $#{$msifiles} < 0 ) { installer::exiter::exit_program("ERROR: Did not find msi database in directory $installationdir", "create_msp_patch"); } 47 if ( $#{$msifiles} > 0 ) { installer::exiter::exit_program("ERROR: Did find more than one msi database in directory $installationdir", "create_msp_patch"); } 48 49 my $newinstallsetdatabasepath = $installationdir . $installer::globals::separator . ${$msifiles}[0]; 50 my $oldinstallsetdatabasepath = $installer::globals::updatedatabasepath; 51 52 # Creating temp directory again 53 installer::systemactions::create_directory_structure($installer::globals::temppath); 54 55 # Creating old installation directory 56 my $dirname = "admin"; 57 my $installpath = $installer::globals::temppath . $installer::globals::separator . $dirname; 58 if ( ! -d $installpath) { installer::systemactions::create_directory($installpath); } 59 60 my $oldinstallpath = $installpath . $installer::globals::separator . "old"; 61 my $newinstallpath = $installpath . $installer::globals::separator . "new"; 62 63 if ( ! -d $oldinstallpath) { installer::systemactions::create_directory($oldinstallpath); } 64 if ( ! -d $newinstallpath) { installer::systemactions::create_directory($newinstallpath); } 65 66 my $olddatabase = installer::windows::admin::make_admin_install($oldinstallsetdatabasepath, $oldinstallpath); 67 my $newdatabase = installer::windows::admin::make_admin_install($newinstallsetdatabasepath, $newinstallpath); 68 69 if ( $^O =~ /cygwin/i ) { 70 $olddatabase = qx{cygpath -w "$olddatabase"}; 71 $olddatabase =~ s/\s*$//g; 72 $newdatabase = qx{cygpath -w "$newdatabase"}; 73 $newdatabase =~ s/\s*$//g; 74 } 75 76 return ($olddatabase, $newdatabase); 77} 78 79################################################################################# 80# Extracting all tables from a pcp file 81################################################################################# 82 83sub extract_all_tables_from_pcpfile 84{ 85 my ($fullpcpfilepath, $workdir) = @_; 86 87 my $msidb = "msidb.exe"; # Has to be in the path 88 my $infoline = ""; 89 my $systemcall = ""; 90 my $returnvalue = ""; 91 my $extraslash = ""; # Has to be set for non-ActiveState perl 92 93 my $localfullpcpfile = $fullpcpfilepath; 94 my $localworkdir = $workdir; 95 96 if ( $^O =~ /cygwin/i ) { 97 # msidb.exe really wants backslashes. (And double escaping because system() expands the string.) 98 $localfullpcpfile =~ s/\//\\\\/g; 99 $localworkdir =~ s/\//\\\\/g; 100 $extraslash = "\\"; 101 } 102 if ( $^O =~ /linux/i ) { 103 $extraslash = "\\"; 104 } 105 106 # Export of all tables by using "*" 107 108 $systemcall = $msidb . " -d " . $localfullpcpfile . " -f " . $localworkdir . " -e " . $extraslash . "*"; 109 $returnvalue = system($systemcall); 110 111 $infoline = "Systemcall: $systemcall\n"; 112 push( @installer::globals::logfileinfo, $infoline); 113 114 if ($returnvalue) 115 { 116 $infoline = "ERROR: Could not execute $systemcall !\n"; 117 push( @installer::globals::logfileinfo, $infoline); 118 installer::exiter::exit_program("ERROR: Could not exclude tables from pcp file: $fullpcpfilepath !", "extract_all_tables_from_msidatabase"); 119 } 120 else 121 { 122 $infoline = "Success: Executed $systemcall successfully!\n"; 123 push( @installer::globals::logfileinfo, $infoline); 124 } 125} 126 127################################################################################# 128# Include tables into a pcp file 129################################################################################# 130 131sub include_tables_into_pcpfile 132{ 133 my ($fullpcpfilepath, $workdir, $tables) = @_; 134 135 my $msidb = "msidb.exe"; # Has to be in the path 136 my $infoline = ""; 137 my $systemcall = ""; 138 my $returnvalue = ""; 139 140 # Make all table 8+3 conform 141 my $alltables = installer::converter::convert_stringlist_into_array(\$tables, " "); 142 143 for ( my $i = 0; $i <= $#{$alltables}; $i++ ) 144 { 145 my $tablename = ${$alltables}[$i]; 146 $tablename =~ s/\s*$//; 147 my $namelength = length($tablename); 148 if ( $namelength > 8 ) 149 { 150 my $newtablename = substr($tablename, 0, 8); # name, offset, length 151 my $oldfile = $workdir . $installer::globals::separator . $tablename . ".idt"; 152 my $newfile = $workdir . $installer::globals::separator . $newtablename . ".idt"; 153 if ( -f $newfile ) { unlink $newfile; } 154 installer::systemactions::copy_one_file($oldfile, $newfile); 155 } 156 } 157 158 # Import of tables 159 160 my $localworkdir = $workdir; 161 my $localfullpcpfilepath = $fullpcpfilepath; 162 163 if ( $^O =~ /cygwin/i ) { 164 # msidb.exe really wants backslashes. (And double escaping because system() expands the string.) 165 $localfullpcpfilepath =~ s/\//\\\\/g; 166 $localworkdir =~ s/\//\\\\/g; 167 } 168 169 my @tables = split(' ', $tables); # I found that msidb from Windows SDK 7.1 did not accept more than one table. 170 foreach my $table (@tables) 171 { 172 $systemcall = $msidb . " -d " . $localfullpcpfilepath . " -f " . $localworkdir . " -i " . $table; 173 174 $returnvalue = system($systemcall); 175 176 $infoline = "Systemcall: $systemcall\n"; 177 push( @installer::globals::logfileinfo, $infoline); 178 179 if ($returnvalue) 180 { 181 $infoline = "ERROR: Could not execute $systemcall !\n"; 182 push( @installer::globals::logfileinfo, $infoline); 183 installer::exiter::exit_program("ERROR: Could not include tables into pcp file: $fullpcpfilepath !", "include_tables_into_pcpfile"); 184 } 185 else 186 { 187 $infoline = "Success: Executed $systemcall successfully!\n"; 188 push( @installer::globals::logfileinfo, $infoline); 189 } 190 } 191} 192 193################################################################################# 194# Calling msimsp.exe 195################################################################################# 196 197sub execute_msimsp 198{ 199 my ($fullpcpfilename, $mspfilename, $localmspdir) = @_; 200 201 my $msimsp = "msimsp.exe"; # Has to be in the path 202 my $infoline = ""; 203 my $systemcall = ""; 204 my $returnvalue = ""; 205 my $logfilename = $localmspdir . $installer::globals::separator . "msimsp.log"; 206 207 # Using a specific temp for each msimsp.exe process 208 # Creating temp directory again (should already have happened) 209 installer::systemactions::create_directory_structure($installer::globals::temppath); 210 211 # Creating old installation directory 212 my $dirname = "msimsptemp"; 213 my $msimsptemppath = $installer::globals::temppath . $installer::globals::separator . $dirname; 214 if ( ! -d $msimsptemppath) { installer::systemactions::create_directory($msimsptemppath); } 215 216 # 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 217 218 if ( -f $logfilename ) { unlink $logfilename; } 219 220 my $localfullpcpfilename = $fullpcpfilename; 221 my $localmspfilename = $mspfilename; 222 my $locallogfilename = $logfilename; 223 my $localmsimsptemppath = $msimsptemppath; 224 225 if ( $^O =~ /cygwin/i ) { 226 # msimsp.exe really wants backslashes. (And double escaping because system() expands the string.) 227 $localfullpcpfilename =~ s/\//\\\\/g; 228 $locallogfilename =~ s/\//\\\\/g; 229 230 $localmspfilename =~ s/\\/\\\\/g; # path already contains backslash 231 232 $localmsimsptemppath = qx{cygpath -w "$localmsimsptemppath"}; 233 $localmsimsptemppath =~ s/\\/\\\\/g; 234 $localmsimsptemppath =~ s/\s*$//g; 235 } 236 237 $systemcall = $msimsp . " -s " . $localfullpcpfilename . " -p " . $localmspfilename . " -l " . $locallogfilename . " -f " . $localmsimsptemppath; 238 installer::logger::print_message( "... $systemcall ...\n" ); 239 240 $returnvalue = system($systemcall); 241 242 $infoline = "Systemcall: $systemcall\n"; 243 push( @installer::globals::logfileinfo, $infoline); 244 245 if ($returnvalue) 246 { 247 $infoline = "ERROR: Could not execute $systemcall !\n"; 248 push( @installer::globals::logfileinfo, $infoline); 249 installer::exiter::exit_program("ERROR: Could not execute $systemcall !", "execute_msimsp"); 250 } 251 else 252 { 253 $infoline = "Success: Executed $systemcall successfully!\n"; 254 push( @installer::globals::logfileinfo, $infoline); 255 } 256 257 return $logfilename; 258} 259 260#################################################################### 261# Checking existence and saving all tables, that need to be edited 262#################################################################### 263 264sub check_and_save_tables 265{ 266 my ($tablelist, $workdir) = @_; 267 268 my $tables = installer::converter::convert_stringlist_into_array(\$tablelist, " "); 269 270 for ( my $i = 0; $i <= $#{$tables}; $i++ ) 271 { 272 my $filename = ${$tables}[$i]; 273 $filename =~ s/\s*$//; 274 my $fullfilename = $workdir . $installer::globals::separator . $filename . ".idt"; 275 276 if ( ! -f $fullfilename ) { installer::exiter::exit_program("ERROR: Required idt file could not be found: \"$fullfilename\"!", "check_and_save_tables"); } 277 278 my $savfilename = $fullfilename . ".sav"; 279 installer::systemactions::copy_one_file($fullfilename, $savfilename); 280 } 281} 282 283#################################################################### 284# Setting the name of the msp database 285#################################################################### 286 287sub set_mspfilename 288{ 289 my ($allvariables, $mspdir, $languagesarrayref) = @_; 290 291 my $databasename = $allvariables->{'PRODUCTNAME'} . "-" . $allvariables->{'PRODUCTVERSION'} . "-" . $allvariables->{'WINDOWSPATCHLEVEL'} . ".msp"; 292 293 my $fullmspname = $mspdir . $installer::globals::separator . $databasename; 294 295 if ( $^O =~ /cygwin/i ) { $fullmspname =~ s/\//\\/g; } 296 297 return $fullmspname; 298} 299 300#################################################################### 301# Editing table Properties 302#################################################################### 303 304sub change_properties_table 305{ 306 my ($localmspdir, $mspfilename) = @_; 307 308 my $infoline = "Changing content of table \"Properties\"\n"; 309 push( @installer::globals::logfileinfo, $infoline); 310 311 my $filename = $localmspdir . $installer::globals::separator . "Properties.idt"; 312 if ( ! -f $filename ) { installer::exiter::exit_program("ERROR: Could not find file \"$filename\" !", "change_properties_table"); } 313 314 my $filecontent = installer::files::read_file($filename); 315 316 317 my $guidref = installer::windows::msiglobal::get_guid_list(1, 1); 318 ${$guidref}[0] =~ s/\s*$//; # removing ending spaces 319 my $patchcode = "\{" . ${$guidref}[0] . "\}"; 320 321 # Setting "PatchOutputPath" 322 my $found_patchoutputpath = 0; 323 my $found_patchguid = 0; 324 325 for ( my $i = 0; $i <= $#{$filecontent}; $i++ ) 326 { 327 if ( ${$filecontent}[$i] =~ /^\s*PatchOutputPath\t(.*?)\s*$/ ) 328 { 329 my $oldvalue = $1; 330 ${$filecontent}[$i] =~ s/\Q$oldvalue\E/$mspfilename/; 331 $found_patchoutputpath = 1; 332 } 333 334 if ( ${$filecontent}[$i] =~ /^\s*PatchGUID\t(.*?)\s*$/ ) 335 { 336 my $oldvalue = $1; 337 ${$filecontent}[$i] =~ s/\Q$oldvalue\E/$patchcode/; 338 $found_patchguid = 1; 339 } 340 } 341 342 if ( ! $found_patchoutputpath ) 343 { 344 my $newline = "PatchOutputPath\t$mspfilename\n"; 345 push(@{$filecontent}, $newline); 346 } 347 348 if ( ! $found_patchguid ) 349 { 350 my $newline = "PatchGUID\t$patchcode\n"; 351 push(@{$filecontent}, $newline); 352 } 353 354 # saving file 355 installer::files::save_file($filename, $filecontent); 356} 357 358#################################################################### 359# Editing table TargetImages 360#################################################################### 361 362sub change_targetimages_table 363{ 364 my ($localmspdir, $olddatabase) = @_; 365 366 my $infoline = "Changing content of table \"TargetImages\"\n"; 367 push( @installer::globals::logfileinfo, $infoline); 368 369 my $filename = $localmspdir . $installer::globals::separator . "TargetImages.idt"; 370 if ( ! -f $filename ) { installer::exiter::exit_program("ERROR: Could not find file \"$filename\" !", "change_targetimages_table"); } 371 372 my $filecontent = installer::files::read_file($filename); 373 my @newcontent = (); 374 375 # Copying the header 376 for ( my $i = 0; $i <= $#{$filecontent}; $i++ ) { if ( $i < 3 ) { push(@newcontent, ${$filecontent}[$i]); } } 377 378 #Adding all targets 379 my $newline = "T1\t$olddatabase\t\tU1\t1\t0x00000922\t1\n"; 380 push(@newcontent, $newline); 381 382 # saving file 383 installer::files::save_file($filename, \@newcontent); 384} 385 386#################################################################### 387# Editing table UpgradedImages 388#################################################################### 389 390sub change_upgradedimages_table 391{ 392 my ($localmspdir, $newdatabase) = @_; 393 394 my $infoline = "Changing content of table \"UpgradedImages\"\n"; 395 push( @installer::globals::logfileinfo, $infoline); 396 397 my $filename = $localmspdir . $installer::globals::separator . "UpgradedImages.idt"; 398 if ( ! -f $filename ) { installer::exiter::exit_program("ERROR: Could not find file \"$filename\" !", "change_upgradedimages_table"); } 399 400 my $filecontent = installer::files::read_file($filename); 401 my @newcontent = (); 402 403 # Copying the header 404 for ( my $i = 0; $i <= $#{$filecontent}; $i++ ) { if ( $i < 3 ) { push(@newcontent, ${$filecontent}[$i]); } } 405 406 # Syntax: Upgraded MsiPath PatchMsiPath SymbolPaths Family 407 408 # default values 409 my $upgraded = "U1"; 410 my $msipath = $newdatabase; 411 my $patchmsipath = ""; 412 my $symbolpaths = ""; 413 my $family = "22334455"; 414 415 if ( $#{$filecontent} >= 3 ) 416 { 417 my $line = ${$filecontent}[3]; 418 if ( $line =~ /^\s*(.*?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\s*$/ ) 419 { 420 $upgraded = $1; 421 $patchmsipath = $3; 422 $symbolpaths = $4; 423 $family = $5; 424 } 425 } 426 427 #Adding sequence line, saving PatchFamily 428 my $newline = "$upgraded\t$msipath\t$patchmsipath\t$symbolpaths\t$family\n"; 429 push(@newcontent, $newline); 430 431 # saving file 432 installer::files::save_file($filename, \@newcontent); 433} 434 435#################################################################### 436# Editing table ImageFamilies 437#################################################################### 438 439sub change_imagefamilies_table 440{ 441 my ($localmspdir) = @_; 442 443 my $infoline = "Changing content of table \"ImageFamilies\"\n"; 444 push( @installer::globals::logfileinfo, $infoline); 445 446 my $filename = $localmspdir . $installer::globals::separator . "ImageFamilies.idt"; 447 if ( ! -f $filename ) { installer::exiter::exit_program("ERROR: Could not find file \"$filename\" !", "change_imagefamilies_table"); } 448 449 my $filecontent = installer::files::read_file($filename); 450 my @newcontent = (); 451 452 # Copying the header 453 for ( my $i = 0; $i <= $#{$filecontent}; $i++ ) { if ( $i < 3 ) { push(@newcontent, ${$filecontent}[$i]); } } 454 455 # Syntax: Family MediaSrcPropName MediaDiskId FileSequenceStart DiskPrompt VolumeLabel 456 # "FileSequenceStart has to be set 457 458 # Default values: 459 460 my $family = "22334455"; 461 my $mediasrcpropname = "MediaSrcPropName"; 462 my $mediadiskid = "2"; 463 my $filesequencestart = get_filesequencestart(); 464 my $diskprompt = ""; 465 my $volumelabel = ""; 466 467 if ( $#{$filecontent} >= 3 ) 468 { 469 my $line = ${$filecontent}[3]; 470 if ( $line =~ /^\s*(.*?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\s*$/ ) 471 { 472 $family = $1; 473 $mediasrcpropname = $2; 474 $mediadiskid = $3; 475 $diskprompt = $5; 476 $volumelabel = $6; 477 } 478 } 479 480 #Adding sequence line 481 my $newline = "$family\t$mediasrcpropname\t$mediadiskid\t$filesequencestart\t$diskprompt\t$volumelabel\n"; 482 push(@newcontent, $newline); 483 484 # saving file 485 installer::files::save_file($filename, \@newcontent); 486} 487 488#################################################################### 489# Setting start sequence for patch 490#################################################################### 491 492sub get_filesequencestart 493{ 494 my $sequence = 1000; # default 495 496 if ( $installer::globals::updatelastsequence ) { $sequence = $installer::globals::updatelastsequence + 500; } 497 498 return $sequence; 499} 500 501#################################################################### 502# Setting time value into pcp file 503# Format mm/dd/yyyy hh:mm 504#################################################################### 505 506sub get_patchtime_value 507{ 508 # Syntax: 8/8/2008 11:55 509 my $minute = (localtime())[1]; 510 my $hour = (localtime())[2]; 511 my $day = (localtime())[3]; 512 my $month = (localtime())[4]; 513 my $year = 1900 + (localtime())[5]; 514 515 $month++; # zero based month 516 if ( $minute < 10 ) { $minute = "0" . $minute; } 517 if ( $hour < 10 ) { $hour = "0" . $hour; } 518 519 my $timestring = $month . "/" . $day . "/" . $year . " " . $hour . ":" . $minute; 520 521 return $timestring; 522} 523 524################################################################################# 525# Checking, if this is the correct database. 526################################################################################# 527 528sub correct_langs 529{ 530 my ($langs, $languagestringref) = @_; 531 532 my $correct_langs = 0; 533 534 # Comparing $langs with $languagestringref 535 536 my $langlisthash = installer::converter::convert_stringlist_into_hash(\$langs, ","); 537 my $langstringhash = installer::converter::convert_stringlist_into_hash($languagestringref, "_"); 538 539 my $not_included = 0; 540 foreach my $onelang ( keys %{$langlisthash} ) 541 { 542 if ( ! exists($langstringhash->{$onelang}) ) 543 { 544 $not_included = 1; 545 last; 546 } 547 } 548 549 if ( ! $not_included ) 550 { 551 foreach my $onelanguage ( keys %{$langstringhash} ) 552 { 553 if ( ! exists($langlisthash->{$onelanguage}) ) 554 { 555 $not_included = 1; 556 last; 557 } 558 } 559 560 if ( ! $not_included ) { $correct_langs = 1; } 561 } 562 563 return $correct_langs; 564} 565 566################################################################################# 567# Searching for the path to the reference database for this special product. 568################################################################################# 569 570sub get_patchid_from_list 571{ 572 my ($filecontent, $languagestringref, $filename) = @_; 573 574 my $patchid = ""; 575 576 for ( my $i = 0; $i <= $#{$filecontent}; $i++ ) 577 { 578 my $line = ${$filecontent}[$i]; 579 if ( $line =~ /^\s*$/ ) { next; } # empty line 580 if ( $line =~ /^\s*\#/ ) { next; } # comment line 581 582 if ( $line =~ /^\s*(.+?)\s*=\s*(.+?)\s*$/ ) 583 { 584 my $langs = $1; 585 my $localpatchid = $2; 586 587 if ( correct_langs($langs, $languagestringref) ) 588 { 589 $patchid = $localpatchid; 590 last; 591 } 592 } 593 else 594 { 595 installer::exiter::exit_program("ERROR: Wrong syntax in file: $filename! Line: \"$line\"", "get_patchid_from_list"); 596 } 597 } 598 599 return $patchid; 600} 601 602#################################################################### 603# Editing table PatchMetadata 604#################################################################### 605 606sub change_patchmetadata_table 607{ 608 my ($localmspdir, $allvariables, $languagestringref) = @_; 609 610 my $infoline = "Changing content of table \"PatchMetadata\"\n"; 611 push( @installer::globals::logfileinfo, $infoline); 612 613 my $filename = $localmspdir . $installer::globals::separator . "PatchMetadata.idt"; 614 if ( ! -f $filename ) { installer::exiter::exit_program("ERROR: Could not find file \"$filename\" !", "change_patchmetadata_table"); } 615 616 my $filecontent = installer::files::read_file($filename); 617 my @newcontent = (); 618 619 # Syntax: Company Property Value 620 # Interesting properties: "Classification" and "CreationTimeUTC" 621 622 my $classification_set = 0; 623 my $creationtime_set = 0; 624 my $targetproductname_set = 0; 625 my $manufacturer_set = 0; 626 my $displayname_set = 0; 627 my $description_set = 0; 628 my $allowremoval_set = 0; 629 630 my $defaultcompany = ""; 631 632 my $classificationstring = "Classification"; 633 my $classificationvalue = "Hotfix"; 634 if (( $allvariables->{'SERVICEPACK'} ) && ( $allvariables->{'SERVICEPACK'} == 1 )) { $classificationvalue = "ServicePack"; } 635 636 my $allowremovalstring = "AllowRemoval"; 637 my $allowremovalvalue = "1"; 638 if (( exists($allvariables->{'MSPALLOWREMOVAL'}) ) && ( $allvariables->{'MSPALLOWREMOVAL'} == 0 )) { $allowremovalvalue = 0; } 639 640 my $timestring = "CreationTimeUTC"; 641 # Syntax: 8/8/2008 11:55 642 my $timevalue = get_patchtime_value(); 643 644 my $targetproductnamestring = "TargetProductName"; 645 my $targetproductnamevalue = $allvariables->{'PRODUCTNAME'}; 646 if ( $allvariables->{'PROPERTYTABLEPRODUCTNAME'} ) { $targetproductnamevalue = $allvariables->{'PROPERTYTABLEPRODUCTNAME'}; } 647 648 my $manufacturerstring = "ManufacturerName"; 649 my $manufacturervalue = $ENV{'OOO_VENDOR'}; 650 if ( $installer::globals::longmanufacturer ) { $manufacturervalue = $installer::globals::longmanufacturer; } 651 652 my $displaynamestring = "DisplayName"; 653 my $descriptionstring = "Description"; 654 my $displaynamevalue = ""; 655 my $descriptionvalue = ""; 656 657 my $base = $allvariables->{'PRODUCTNAME'} . " " . $allvariables->{'PRODUCTVERSION'}; 658 if ( $installer::globals::languagepack || $installer::globals::helppack ) { $base = $targetproductnamevalue; } 659 660 my $windowspatchlevel = 0; 661 if ( $allvariables->{'WINDOWSPATCHLEVEL'} ) { $windowspatchlevel = $allvariables->{'WINDOWSPATCHLEVEL'}; } 662 663 my $displayaddon = ""; 664 if ( $allvariables->{'PATCHDISPLAYADDON'} ) { $displayaddon = $allvariables->{'PATCHDISPLAYADDON'}; } 665 666 my $patchsequence = get_patchsequence($allvariables); 667 668 if (( $allvariables->{'SERVICEPACK'} ) && ( $allvariables->{'SERVICEPACK'} == 1 )) 669 { 670 $displaynamevalue = $base . " ServicePack " . $windowspatchlevel . " " . $patchsequence . " Build: " . $installer::globals::buildid; 671 $descriptionvalue = $base . " ServicePack " . $windowspatchlevel . " " . $patchsequence . " Build: " . $installer::globals::buildid; 672 } 673 else 674 { 675 $displaynamevalue = $base . " Hotfix " . $displayaddon . " " . $patchsequence . " Build: " . $installer::globals::buildid; 676 $descriptionvalue = $base . " Hotfix " . $displayaddon . " " . $patchsequence . " Build: " . $installer::globals::buildid; 677 $displaynamevalue =~ s/ / /g; 678 $descriptionvalue =~ s/ / /g; 679 $displaynamevalue =~ s/ / /g; 680 $descriptionvalue =~ s/ / /g; 681 $displaynamevalue =~ s/ / /g; 682 $descriptionvalue =~ s/ / /g; 683 } 684 685 if ( $allvariables->{'MSPPATCHNAMELIST'} ) 686 { 687 my $patchnamelistfile = $allvariables->{'MSPPATCHNAMELIST'}; 688 $patchnamelistfile = $installer::globals::idttemplatepath . $installer::globals::separator . $patchnamelistfile; 689 if ( ! -f $patchnamelistfile ) { installer::exiter::exit_program("ERROR: Could not find file \"$patchnamelistfile\".", "change_patchmetadata_table"); } 690 my $filecontent = installer::files::read_file($patchnamelistfile); 691 692 # Get name and path of reference database 693 my $patchid = get_patchid_from_list($filecontent, $languagestringref, $patchnamelistfile); 694 695 if ( $patchid eq "" ) { installer::exiter::exit_program("ERROR: Could not find file patchid in file \"$patchnamelistfile\" for language(s) \"$$languagestringref\".", "change_patchmetadata_table"); } 696 697 # Setting language specific patch id 698 } 699 700 for ( my $i = 0; $i <= $#{$filecontent}; $i++ ) 701 { 702 if ( ${$filecontent}[$i] =~ /^\s*(.*?)\t(.*?)\t(.*?)\s*$/ ) 703 { 704 my $company = $1; 705 my $property = $2; 706 my $value = $3; 707 708 if ( $property eq $classificationstring ) 709 { 710 ${$filecontent}[$i] = "$company\t$property\t$classificationvalue\n"; 711 $classification_set = 1; 712 } 713 714 if ( $property eq $allowremovalstring ) 715 { 716 ${$filecontent}[$i] = "$company\t$property\t$allowremovalvalue\n"; 717 $allowremoval_set = 1; 718 } 719 720 if ( $property eq $timestring ) 721 { 722 ${$filecontent}[$i] = "$company\t$property\t$timevalue\n"; 723 $creationtime_set = 1; 724 } 725 726 if ( $property eq $targetproductnamestring ) 727 { 728 ${$filecontent}[$i] = "$company\t$property\t$targetproductnamevalue\n"; 729 $targetproductname_set = 1; 730 } 731 732 if ( $property eq $manufacturerstring ) 733 { 734 ${$filecontent}[$i] = "$company\t$property\t$manufacturervalue\n"; 735 $manufacturer_set = 1; 736 } 737 738 if ( $property eq $displaynamestring ) 739 { 740 ${$filecontent}[$i] = "$company\t$property\t$displaynamevalue\n"; 741 $displayname_set = 1; 742 } 743 744 if ( $property eq $descriptionstring ) 745 { 746 ${$filecontent}[$i] = "$company\t$property\t$descriptionvalue\n"; 747 $description_set = 1; 748 } 749 } 750 751 push(@newcontent, ${$filecontent}[$i]); 752 } 753 754 if ( ! $classification_set ) 755 { 756 my $line = "$defaultcompany\t$classificationstring\t$classificationvalue\n"; 757 push(@newcontent, $line); 758 } 759 760 if ( ! $allowremoval_set ) 761 { 762 my $line = "$defaultcompany\t$classificationstring\t$allowremovalvalue\n"; 763 push(@newcontent, $line); 764 } 765 766 if ( ! $allowremoval_set ) 767 { 768 my $line = "$defaultcompany\t$classificationstring\t$allowremovalvalue\n"; 769 push(@newcontent, $line); 770 } 771 772 if ( ! $creationtime_set ) 773 { 774 my $line = "$defaultcompany\t$timestring\t$timevalue\n"; 775 push(@newcontent, $line); 776 } 777 778 if ( ! $targetproductname_set ) 779 { 780 my $line = "$defaultcompany\t$targetproductnamestring\t$targetproductnamevalue\n"; 781 push(@newcontent, $line); 782 } 783 784 if ( ! $manufacturer_set ) 785 { 786 my $line = "$defaultcompany\t$manufacturerstring\t$manufacturervalue\n"; 787 push(@newcontent, $line); 788 } 789 790 if ( ! $displayname_set ) 791 { 792 my $line = "$defaultcompany\t$displaynamestring\t$displaynamevalue\n"; 793 push(@newcontent, $line); 794 } 795 796 if ( ! $description_set ) 797 { 798 my $line = "$defaultcompany\t$descriptionstring\t$descriptionvalue\n"; 799 push(@newcontent, $line); 800 } 801 802 # saving file 803 installer::files::save_file($filename, \@newcontent); 804} 805 806#################################################################### 807# Editing table PatchSequence 808#################################################################### 809 810sub change_patchsequence_table 811{ 812 my ($localmspdir, $allvariables) = @_; 813 814 my $infoline = "Changing content of table \"PatchSequence\"\n"; 815 push( @installer::globals::logfileinfo, $infoline); 816 817 my $filename = $localmspdir . $installer::globals::separator . "PatchSequence.idt"; 818 if ( ! -f $filename ) { installer::exiter::exit_program("ERROR: Could not find file \"$filename\" !", "change_patchsequence_table"); } 819 820 my $filecontent = installer::files::read_file($filename); 821 my @newcontent = (); 822 823 # Copying the header 824 for ( my $i = 0; $i <= $#{$filecontent}; $i++ ) { if ( $i < 3 ) { push(@newcontent, ${$filecontent}[$i]); } } 825 826 # Syntax: PatchFamily Target Sequence Supersede 827 828 my $patchfamily = "SO"; 829 my $target = ""; 830 my $patchsequence = get_patchsequence($allvariables); 831 my $supersede = get_supersede($allvariables); 832 833 if ( $#{$filecontent} >= 3 ) 834 { 835 my $line = ${$filecontent}[3]; 836 if ( $line =~ /^\s*(.*?)\t(.*?)\t(.*?)\t(.*?)\s$/ ) 837 { 838 $patchfamily = $1; 839 $target = $2; 840 } 841 } 842 843 #Adding sequence line, saving PatchFamily 844 my $newline = "$patchfamily\t$target\t$patchsequence\t$supersede\n"; 845 push(@newcontent, $newline); 846 847 # saving file 848 installer::files::save_file($filename, \@newcontent); 849} 850 851#################################################################### 852# Setting supersede, "0" for Hotfixes, "1" for ServicePack 853#################################################################### 854 855sub get_supersede 856{ 857 my ( $allvariables ) = @_; 858 859 my $supersede = 0; # if not defined, this is a Hotfix 860 861 if (( $allvariables->{'SERVICEPACK'} ) && ( $allvariables->{'SERVICEPACK'} == 1 )) { $supersede = 1; } 862 863 return $supersede; 864} 865 866#################################################################### 867# Setting the sequence of the patch 868#################################################################### 869 870sub get_patchsequence 871{ 872 my ( $allvariables ) = @_; 873 874 my $patchsequence = "1.0"; 875 876 if ( ! $allvariables->{'PACKAGEVERSION'} ) { installer::exiter::exit_program("ERROR: PACKAGEVERSION must be set for msp patch creation!", "get_patchsequence"); } 877 878 my $packageversion = $allvariables->{'PACKAGEVERSION'}; 879 880 if ( $packageversion =~ /^\s*(\d+)\.(\d+)\.(\d+)\.(\d+)\s*$/ ) 881 { 882 my $major = $1; 883 my $minor = $2; 884 my $micro = $3; 885 my $patch = $4; 886 $patchsequence = $major . "\." . $minor . "\." . $micro . "\." . $patch; 887 } 888 889 return $patchsequence; 890} 891 892#################################################################### 893# Editing all tables from pcp file, that need to be edited 894#################################################################### 895 896sub edit_tables 897{ 898 my ($tablelist, $localmspdir, $olddatabase, $newdatabase, $mspfilename, $allvariables, $languagestringref) = @_; 899 900 # table list contains: my $tablelist = "Properties TargetImages UpgradedImages ImageFamilies PatchMetadata PatchSequence"; 901 902 change_properties_table($localmspdir, $mspfilename); 903 change_targetimages_table($localmspdir, $olddatabase); 904 change_upgradedimages_table($localmspdir, $newdatabase); 905 change_imagefamilies_table($localmspdir); 906 change_patchmetadata_table($localmspdir, $allvariables, $languagestringref); 907 change_patchsequence_table($localmspdir, $allvariables); 908} 909 910################################################################################# 911# Checking, if this is the correct database. 912################################################################################# 913 914sub correct_patch 915{ 916 my ($product, $pro, $langs, $languagestringref) = @_; 917 918 my $correct_patch = 0; 919 920 # Comparing $product with $installer::globals::product and 921 # $pro with $installer::globals::pro and 922 # $langs with $languagestringref 923 924 my $product_is_good = 0; 925 926 my $localproduct = $installer::globals::product; 927 if ( $installer::globals::languagepack ) { $localproduct = $localproduct . "LanguagePack"; } 928 elsif ( $installer::globals::helppack ) { $localproduct = $localproduct . "HelpPack"; } 929 930 if ( $product eq $localproduct ) { $product_is_good = 1; } 931 932 if ( $product_is_good ) 933 { 934 my $pro_is_good = 0; 935 936 if ((( $pro eq "pro" ) && ( $installer::globals::pro )) || (( $pro eq "nonpro" ) && ( ! $installer::globals::pro ))) { $pro_is_good = 1; } 937 938 if ( $pro_is_good ) 939 { 940 my $langlisthash = installer::converter::convert_stringlist_into_hash(\$langs, ","); 941 my $langstringhash = installer::converter::convert_stringlist_into_hash($languagestringref, "_"); 942 943 my $not_included = 0; 944 foreach my $onelang ( keys %{$langlisthash} ) 945 { 946 if ( ! exists($langstringhash->{$onelang}) ) 947 { 948 $not_included = 1; 949 last; 950 } 951 } 952 953 if ( ! $not_included ) 954 { 955 foreach my $onelanguage ( keys %{$langstringhash} ) 956 { 957 if ( ! exists($langlisthash->{$onelanguage}) ) 958 { 959 $not_included = 1; 960 last; 961 } 962 } 963 964 if ( ! $not_included ) { $correct_patch = 1; } 965 } 966 } 967 } 968 969 return $correct_patch; 970} 971 972################################################################################# 973# Searching for the path to the required patch for this special product. 974################################################################################# 975 976sub get_requiredpatchfile_from_list 977{ 978 my ($filecontent, $languagestringref, $filename) = @_; 979 980 my $patchpath = ""; 981 982 for ( my $i = 0; $i <= $#{$filecontent}; $i++ ) 983 { 984 my $line = ${$filecontent}[$i]; 985 if ( $line =~ /^\s*$/ ) { next; } # empty line 986 if ( $line =~ /^\s*\#/ ) { next; } # comment line 987 988 if ( $line =~ /^\s*(.+?)\s*\t+\s*(.+?)\s*\t+\s*(.+?)\s*\t+\s*(.+?)\s*$/ ) 989 { 990 my $product = $1; 991 my $pro = $2; 992 my $langs = $3; 993 my $path = $4; 994 995 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"); } 996 997 if ( correct_patch($product, $pro, $langs, $languagestringref) ) 998 { 999 $patchpath = $path; 1000 last; 1001 } 1002 } 1003 else 1004 { 1005 installer::exiter::exit_program("ERROR: Wrong syntax in file: $filename! Line: \"$line\"", "get_requiredpatchfile_from_list"); 1006 } 1007 } 1008 1009 return $patchpath; 1010} 1011 1012################################################################## 1013# Converting unicode file to ascii 1014# to be more precise: uft-16 little endian to ascii 1015################################################################## 1016 1017sub convert_unicode_to_ascii 1018{ 1019 my ( $filename ) = @_; 1020 1021 my @localfile = (); 1022 1023 my $savfilename = $filename . "_before.unicode"; 1024 installer::systemactions::copy_one_file($filename, $savfilename); 1025 1026 open( IN, "<:encoding(UTF16-LE)", $filename ) || installer::exiter::exit_program("ERROR: Cannot open file $filename for reading", "convert_unicode_to_ascii"); 1027 while ( $line = <IN> ) { 1028 push @localfile, $line; 1029 } 1030 close( IN ); 1031 1032 if ( open( OUT, ">", $filename ) ) 1033 { 1034 print OUT @localfile; 1035 close(OUT); 1036 } 1037} 1038 1039#################################################################### 1040# Analyzing the log file created by msimsp.exe to find all 1041# files included into the patch. 1042#################################################################### 1043 1044sub analyze_msimsp_logfile 1045{ 1046 my ($logfile, $filesarray) = @_; 1047 1048 # Reading log file after converting from utf-16 (LE) to ascii 1049 convert_unicode_to_ascii($logfile); 1050 my $logfilecontent = installer::files::read_file($logfile); 1051 1052 # Creating hash from $filesarray: unique file name -> destination of file 1053 my %filehash = (); 1054 my %destinationcollector = (); 1055 1056 for ( my $i = 0; $i <= $#{$filesarray}; $i++ ) 1057 { 1058 my $onefile = ${$filesarray}[$i]; 1059 1060 # Only collecting files with "uniquename" and "destination" 1061 if (( exists($onefile->{'uniquename'}) ) && ( exists($onefile->{'uniquename'}) )) 1062 { 1063 my $uniquefilename = $onefile->{'uniquename'}; 1064 my $destpath = $onefile->{'destination'}; 1065 $filehash{$uniquefilename} = $destpath; 1066 } 1067 } 1068 1069 # Analyzing log file of msimsp.exe, finding all changed files 1070 # and searching all destinations of unique file names. 1071 # Content in log file: "INFO File Key: <file key> is modified" 1072 # Collecting content in @installer::globals::patchfilecollector 1073 1074 for ( my $i = 0; $i <= $#{$logfilecontent}; $i++ ) 1075 { 1076 if ( ${$logfilecontent}[$i] =~ /Key\:\s*(.*?) is modified\s*$/ ) 1077 { 1078 my $filekey = $1; 1079 if ( exists($filehash{$filekey}) ) { $destinationcollector{$filehash{$filekey}} = 1; } 1080 else { installer::exiter::exit_program("ERROR: Could not find file key \"$filekey\" in file collector.", "analyze_msimsp_logfile"); } 1081 } 1082 } 1083 1084 foreach my $onedest ( sort keys %destinationcollector ) { push(@installer::globals::patchfilecollector, "$onedest\n"); } 1085 1086} 1087 1088#################################################################### 1089# Creating msp patch files for Windows 1090#################################################################### 1091 1092sub create_msp_patch 1093{ 1094 my ($installationdir, $includepatharrayref, $allvariables, $languagestringref, $languagesarrayref, $filesarray) = @_; 1095 1096 my $force = 1; # print this message even in 'quiet' mode 1097 installer::logger::print_message( "\n******************************************\n" ); 1098 installer::logger::print_message( "... creating msp installation set ...\n", $force ); 1099 installer::logger::print_message( "******************************************\n" ); 1100 1101 $installer::globals::creating_windows_installer_patch = 1; 1102 1103 my @needed_files = ("msimsp.exe"); # only required for patch creation process 1104 installer::control::check_needed_files_in_path(\@needed_files); 1105 1106 installer::logger::include_header_into_logfile("Creating msp installation sets:"); 1107 1108 my $firstdir = $installationdir; 1109 installer::pathanalyzer::get_path_from_fullqualifiedname(\$firstdir); 1110 1111 my $lastdir = $installationdir; 1112 installer::pathanalyzer::make_absolute_filename_to_relative_filename(\$lastdir); 1113 1114 if ( $lastdir =~ /\./ ) { $lastdir =~ s/\./_msp_inprogress\./ } 1115 else { $lastdir = $lastdir . "_msp_inprogress"; } 1116 1117 # Removing existing directory "_native_packed_inprogress" and "_native_packed_witherror" and "_native_packed" 1118 1119 my $mspdir = $firstdir . $lastdir; 1120 if ( -d $mspdir ) { installer::systemactions::remove_complete_directory($mspdir); } 1121 1122 my $olddir = $mspdir; 1123 $olddir =~ s/_inprogress/_witherror/; 1124 if ( -d $olddir ) { installer::systemactions::remove_complete_directory($olddir); } 1125 1126 $olddir = $mspdir; 1127 $olddir =~ s/_inprogress//; 1128 if ( -d $olddir ) { installer::systemactions::remove_complete_directory($olddir); } 1129 1130 # Creating the new directory for new installation set 1131 installer::systemactions::create_directory($mspdir); 1132 1133 $installer::globals::saveinstalldir = $mspdir; 1134 1135 installer::logger::include_timestamp_into_logfile("\nPerformance Info: Starting product installation"); 1136 1137 # Installing both installation sets 1138 installer::logger::print_message( "... installing products ...\n" ); 1139 my ($olddatabase, $newdatabase) = install_installation_sets($installationdir); 1140 1141 installer::logger::include_timestamp_into_logfile("\nPerformance Info: Starting pcp file creation"); 1142 1143 # Create pcp file 1144 installer::logger::print_message( "... creating pcp file ...\n" ); 1145 1146 my $localmspdir = installer::systemactions::create_directories("msp", $languagestringref); 1147 1148 if ( ! $allvariables->{'PCPFILENAME'} ) { installer::exiter::exit_program("ERROR: Property \"PCPFILENAME\" has to be defined.", "create_msp_patch"); } 1149 my $pcpfilename = $allvariables->{'PCPFILENAME'}; 1150 1151 if ( $installer::globals::languagepack ) { $pcpfilename =~ s/.pcp\s*$/languagepack.pcp/; } 1152 elsif ( $installer::globals::helppack ) { $pcpfilename =~ s/.pcp\s*$/helppack.pcp/; } 1153 1154 # Searching the pcp file in the include paths 1155 my $fullpcpfilenameref = installer::scriptitems::get_sourcepath_from_filename_and_includepath(\$pcpfilename, $includepatharrayref, 1); 1156 if ( $$fullpcpfilenameref eq "" ) { installer::exiter::exit_program("ERROR: pcp file not found: $pcpfilename !", "create_msp_patch"); } 1157 my $fullpcpfilenamesource = $$fullpcpfilenameref; 1158 1159 # Copying pcp file 1160 my $fullpcpfilename = $localmspdir . $installer::globals::separator . $pcpfilename; 1161 installer::systemactions::copy_one_file($fullpcpfilenamesource, $fullpcpfilename); 1162 1163 # a. Extracting tables from msi database: msidb.exe -d <msifile> -f <directory> -e File Media, ... 1164 # b. Changing content of msi database in tables: File, Media, Directory, FeatureComponent 1165 # c. Including tables into msi database: msidb.exe -d <msifile> -f <directory> -i File Media, ... 1166 1167 # Unpacking tables from pcp file 1168 extract_all_tables_from_pcpfile($fullpcpfilename, $localmspdir); 1169 1170 # Tables, that need to be edited 1171 my $tablelist = "Properties TargetImages UpgradedImages ImageFamilies PatchMetadata PatchSequence"; # required tables 1172 1173 # Saving all tables 1174 check_and_save_tables($tablelist, $localmspdir); 1175 1176 # Setting the name of the new msp file 1177 my $mspfilename = set_mspfilename($allvariables, $mspdir, $languagesarrayref); 1178 1179 # Editing tables 1180 edit_tables($tablelist, $localmspdir, $olddatabase, $newdatabase, $mspfilename, $allvariables, $languagestringref); 1181 1182 # Adding edited tables into pcp file 1183 include_tables_into_pcpfile($fullpcpfilename, $localmspdir, $tablelist); 1184 1185 # Start msimsp.exe 1186 installer::logger::include_timestamp_into_logfile("\nPerformance Info: Starting msimsp.exe"); 1187 my $msimsplogfile = execute_msimsp($fullpcpfilename, $mspfilename, $localmspdir); 1188 1189 # Sign .msp file 1190 if ( defined($ENV{'WINDOWS_BUILD_SIGNING'}) && ($ENV{'WINDOWS_BUILD_SIGNING'} eq 'TRUE') ) 1191 { 1192 my $localmspfilename = $mspfilename; 1193 $localmspfilename =~ s/\\/\\\\/g; 1194 my $systemcall = "signtool.exe sign "; 1195 if ( defined($ENV{'PFXFILE'}) ) { $systemcall .= "-f $ENV{'PFXFILE'} "; } 1196 if ( defined($ENV{'PFXPASSWORD'}) ) { $systemcall .= "-p $ENV{'PFXPASSWORD'} "; } 1197 if ( defined($ENV{'TIMESTAMPURL'}) ) { $systemcall .= "-t $ENV{'TIMESTAMPURL'} "; } else { $systemcall .= "-t http://timestamp.globalsign.com/scripts/timestamp.dll "; } 1198 $systemcall .= "-d \"" . $allvariables->{'PRODUCTNAME'} . " " . $allvariables->{'PRODUCTVERSION'} . " Patch " . $allvariables->{'WINDOWSPATCHLEVEL'} . "\" "; 1199 $systemcall .= $localmspfilename; 1200 installer::logger::print_message( "... code signing and timestamping with signtool.exe ...\n" ); 1201 1202 my $returnvalue = system($systemcall); 1203 1204 # do not print password to log 1205 if ( defined($ENV{'PFXPASSWORD'}) ) { $systemcall =~ s/$ENV{'PFXPASSWORD'}/********/; } 1206 my $infoline = "Systemcall: $systemcall\n"; 1207 push( @installer::globals::logfileinfo, $infoline); 1208 1209 if ($returnvalue) 1210 { 1211 $infoline = "ERROR: Could not execute \"$systemcall\"!\n"; 1212 push( @installer::globals::logfileinfo, $infoline); 1213 } 1214 else 1215 { 1216 $infoline = "Success: Executed \"$systemcall\" successfully!\n"; 1217 push( @installer::globals::logfileinfo, $infoline); 1218 } 1219 } 1220 1221 # Copy final installation set next to msp file 1222 installer::logger::include_timestamp_into_logfile("\nPerformance Info: Copying installation set"); 1223 installer::logger::print_message( "... copying installation set ...\n" ); 1224 1225 my $oldinstallationsetpath = $installer::globals::updatedatabasepath; 1226 1227 if ( $^O =~ /cygwin/i ) { $oldinstallationsetpath =~ s/\\/\//g; } 1228 1229 installer::pathanalyzer::get_path_from_fullqualifiedname(\$oldinstallationsetpath); 1230 installer::systemactions::copy_complete_directory($oldinstallationsetpath, $mspdir); 1231 1232 # Copying additional patches into the installation set, if required 1233 if (( $allvariables->{'ADDITIONALREQUIREDPATCHES'} ) && ( $allvariables->{'ADDITIONALREQUIREDPATCHES'} ne "" ) && ( ! $installer::globals::languagepack ) && ( ! $installer::globals::helppack )) 1234 { 1235 my $filename = $allvariables->{'ADDITIONALREQUIREDPATCHES'}; 1236 1237 my $fullfilenameref = installer::scriptitems::get_sourcepath_from_filename_and_includepath(\$filename, $includepatharrayref, 1); 1238 if ( $$fullfilenameref eq "" ) { installer::exiter::exit_program("ERROR: Could not find file with required patches, although it is defined: $filename !", "create_msp_patch"); } 1239 my $fullfilename = $$fullfilenameref; 1240 1241 # Reading list file 1242 my $listfile = installer::files::read_file($fullfilename); 1243 1244 # Get name and path of reference database 1245 my $requiredpatchfile = get_requiredpatchfile_from_list($listfile, $languagestringref, $fullfilename); 1246 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"); } 1247 1248 # Copying patch file 1249 installer::systemactions::copy_one_file($requiredpatchfile, $mspdir); 1250 # my $infoline = "Copy $requiredpatchfile to $mspdir\n"; 1251 # push( @installer::globals::logfileinfo, $infoline); 1252 } 1253 1254 # Find all files included into the patch 1255 # Analyzing the msimsp log file $msimsplogfile 1256 analyze_msimsp_logfile($msimsplogfile, $filesarray); 1257 1258 # Done 1259 installer::logger::include_timestamp_into_logfile("\nPerformance Info: msp creation done"); 1260 1261 return $mspdir; 1262} 1263 12641; 1265