1#************************************************************************* 2# 3# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4# 5# Copyright 2000, 2010 Oracle and/or its affiliates. 6# 7# OpenOffice.org - a multi-platform office productivity suite 8# 9# This file is part of OpenOffice.org. 10# 11# OpenOffice.org is free software: you can redistribute it and/or modify 12# it under the terms of the GNU Lesser General Public License version 3 13# only, as published by the Free Software Foundation. 14# 15# OpenOffice.org is distributed in the hope that it will be useful, 16# but WITHOUT ANY WARRANTY; without even the implied warranty of 17# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18# GNU Lesser General Public License version 3 for more details 19# (a copy is included in the LICENSE file that accompanied this code). 20# 21# You should have received a copy of the GNU Lesser General Public License 22# version 3 along with OpenOffice.org. If not, see 23# <http://www.openoffice.org/license.html> 24# for a copy of the LGPLv3 License. 25# 26#************************************************************************* 27 28package installer::windows::admin; 29 30use File::Copy; 31use installer::exiter; 32use installer::files; 33use installer::globals; 34use installer::pathanalyzer; 35use installer::systemactions; 36use installer::worker; 37use installer::windows::idtglobal; 38 39################################################################################# 40# Unpacking cabinet files with expand 41################################################################################# 42 43sub unpack_cabinet_file 44{ 45 my ($cabfilename, $unpackdir) = @_; 46 47 my $infoline = "Unpacking cabinet file: $cabfilename\n"; 48 push( @installer::globals::logfileinfo, $infoline); 49 50 my $expandfile = "expand.exe"; # Has to be in the path 51 52 # expand.exe has to be located in the system directory. 53 # Cygwin has another tool expand.exe, that converts tabs to spaces. This cannot be used of course. 54 # But this wrong expand.exe is typically in the PATH before this expand.exe, to unpack 55 # cabinet files. 56 57# if ( $^O =~ /cygwin/i ) 58# { 59# $expandfile = $ENV{'SYSTEMROOT'} . "/system32/expand.exe"; # Has to be located in the systemdirectory 60# $expandfile =~ s/\\/\//; 61# if ( ! -f $expandfile ) { exit_program("ERROR: Did not find file $expandfile in the Windows system folder!"); } 62# } 63 64 if ( $^O =~ /cygwin/i ) 65 { 66 $expandfile = qx(cygpath -u "$ENV{WINDIR}"/System32/expand.exe); 67 chomp $expandfile; 68 } 69 70 my $expandlogfile = $unpackdir . $installer::globals::separator . "expand.log"; 71 72 # exclude cabinet file 73 # my $systemcall = $cabarc . " -o X " . $mergemodulehash->{'cabinetfile'}; 74 75 my $systemcall = ""; 76 if ( $^O =~ /cygwin/i ) { 77 my $localunpackdir = qx{cygpath -w "$unpackdir"}; 78 chomp ($localunpackdir); 79 $localunpackdir =~ s/\\/\\\\/g; 80 $cabfilename =~ s/\\/\\\\/g; 81 $cabfilename =~ s/\s*$//g; 82 $systemcall = $expandfile . " " . $cabfilename . " -F:\* " . $localunpackdir . " \> " . $expandlogfile; 83 } 84 else 85 { 86 $systemcall = $expandfile . " " . $cabfilename . " -F:\* " . $unpackdir . " \> " . $expandlogfile; 87 } 88 89 my $returnvalue = system($systemcall); 90 $infoline = "Systemcall: $systemcall\n"; 91 push( @installer::globals::logfileinfo, $infoline); 92 93 if ($returnvalue) 94 { 95 $infoline = "ERROR: Could not execute $systemcall !\n"; 96 push( @installer::globals::logfileinfo, $infoline); 97 installer::exiter::exit_program("ERROR: Could not extract cabinet file: $mergemodulehash->{'cabinetfile'} !", "change_file_table"); 98 } 99 else 100 { 101 $infoline = "Success: Executed $systemcall successfully!\n"; 102 push( @installer::globals::logfileinfo, $infoline); 103 } 104} 105 106################################################################################# 107# Include tables into a msi database 108################################################################################# 109 110sub include_tables_into_pcpfile 111{ 112 my ($fullmsidatabasepath, $workdir, $tables) = @_; 113 114 my $msidb = "msidb.exe"; # Has to be in the path 115 my $infoline = ""; 116 my $systemcall = ""; 117 my $returnvalue = ""; 118 119 # Make all table 8+3 conform 120 my $alltables = installer::converter::convert_stringlist_into_array(\$tables, " "); 121 122 for ( my $i = 0; $i <= $#{$alltables}; $i++ ) 123 { 124 my $tablename = ${$alltables}[$i]; 125 $tablename =~ s/\s*$//; 126 my $namelength = length($tablename); 127 if ( $namelength > 8 ) 128 { 129 my $newtablename = substr($tablename, 0, 8); # name, offset, length 130 my $oldfile = $workdir . $installer::globals::separator . $tablename . ".idt"; 131 my $newfile = $workdir . $installer::globals::separator . $newtablename . ".idt"; 132 if ( -f $newfile ) { unlink $newfile; } 133 installer::systemactions::copy_one_file($oldfile, $newfile); 134 my $savfile = $oldfile . ".orig"; 135 installer::systemactions::copy_one_file($oldfile, $savfile); 136 } 137 } 138 139 # Import of tables 140 141 $systemcall = $msidb . " -d " . $fullmsidatabasepath . " -f " . $workdir . " -i " . $tables; 142 143 $returnvalue = system($systemcall); 144 145 $infoline = "Systemcall: $systemcall\n"; 146 push( @installer::globals::logfileinfo, $infoline); 147 148 if ($returnvalue) 149 { 150 $infoline = "ERROR: Could not execute $systemcall !\n"; 151 push( @installer::globals::logfileinfo, $infoline); 152 installer::exiter::exit_program("ERROR: Could not include tables into msi database: $fullmsidatabasepath !", "include_tables_into_pcpfile"); 153 } 154 else 155 { 156 $infoline = "Success: Executed $systemcall successfully!\n"; 157 push( @installer::globals::logfileinfo, $infoline); 158 } 159} 160 161################################################################################# 162# Extracting tables from msi database 163################################################################################# 164 165sub extract_tables_from_pcpfile 166{ 167 my ($fullmsidatabasepath, $workdir, $tablelist) = @_; 168 169 my $msidb = "msidb.exe"; # Has to be in the path 170 my $infoline = ""; 171 my $systemcall = ""; 172 my $returnvalue = ""; 173 174 my $localfullmsidatabasepath = $fullmsidatabasepath; 175 176 # Export of all tables by using "*" 177 178 if ( $^O =~ /cygwin/i ) { 179 # Copying the msi database locally guarantees the format of the directory. 180 # Otherwise it is defined in the file of UPDATE_DATABASE_LISTNAME 181 182 my $msifilename = $localfullmsidatabasepath; 183 installer::pathanalyzer::make_absolute_filename_to_relative_filename(\$msifilename); 184 my $destdatabasename = $workdir . $installer::globals::separator . $msifilename; 185 installer::systemactions::copy_one_file($localfullmsidatabasepath, $destdatabasename); 186 $localfullmsidatabasepath = $destdatabasename; 187 188 chomp( $localfullmsidatabasepath = qx{cygpath -w "$localfullmsidatabasepath"} ); 189 chomp( $workdir = qx{cygpath -w "$workdir"} ); 190 191 # msidb.exe really wants backslashes. (And double escaping because system() expands the string.) 192 $localfullmsidatabasepath =~ s/\\/\\\\/g; 193 $workdir =~ s/\\/\\\\/g; 194 195 # and if there are still slashes, they also need to be double backslash 196 $localfullmsidatabasepath =~ s/\//\\\\/g; 197 $workdir =~ s/\//\\\\/g; 198 } 199 200 $systemcall = $msidb . " -d " . $localfullmsidatabasepath . " -f " . $workdir . " -e $tablelist"; 201 $returnvalue = system($systemcall); 202 203 $infoline = "Systemcall: $systemcall\n"; 204 push( @installer::globals::logfileinfo, $infoline); 205 206 if ($returnvalue) 207 { 208 $infoline = "ERROR: Could not execute $systemcall !\n"; 209 push( @installer::globals::logfileinfo, $infoline); 210 installer::exiter::exit_program("ERROR: Could not exclude tables from pcp file: $localfullmsidatabasepath !", "extract_tables_from_pcpfile"); 211 } 212 else 213 { 214 $infoline = "Success: Executed $systemcall successfully!\n"; 215 push( @installer::globals::logfileinfo, $infoline); 216 } 217} 218 219################################################################################ 220# Analyzing the content of Directory.idt 221################################################################################# 222 223sub analyze_directory_file 224{ 225 my ($filecontent) = @_; 226 227 my %table = (); 228 229 for ( my $i = 0; $i <= $#{$filecontent}; $i++ ) 230 { 231 if (( $i == 0 ) || ( $i == 1 ) || ( $i == 2 )) { next; } 232 233 if ( ${$filecontent}[$i] =~ /^\s*(.*?)\t(.*?)\t(.*?)\s*$/ ) 234 { 235 my $dir = $1; 236 my $parent = $2; 237 my $name = $3; 238 239 if ( $name =~ /^\s*(.*?)\s*\:\s*(.*?)\s*$/ ) { $name = $2; } 240 if ( $name =~ /^\s*(.*?)\s*\|\s*(.*?)\s*$/ ) { $name = $2; } 241 242 my %helphash = (); 243 $helphash{'Directory_Parent'} = $parent; 244 $helphash{'DefaultDir'} = $name; 245 $table{$dir} = \%helphash; 246 } 247 } 248 249 return \%table; 250} 251 252################################################################################# 253# Analyzing the content of Component.idt 254################################################################################# 255 256sub analyze_component_file 257{ 258 my ($filecontent) = @_; 259 260 my %table = (); 261 262 for ( my $i = 0; $i <= $#{$filecontent}; $i++ ) 263 { 264 if (( $i == 0 ) || ( $i == 1 ) || ( $i == 2 )) { next; } 265 266 if ( ${$filecontent}[$i] =~ /^\s*(.*?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\s*$/ ) 267 { 268 my $component = $1; 269 my $dir = $3; 270 271 $table{$component} = $dir; 272 } 273 } 274 275 return \%table; 276} 277 278################################################################################# 279# Analyzing the full content of Component.idt 280################################################################################# 281 282sub analyze_keypath_component_file 283{ 284 my ($filecontent) = @_; 285 286 my %keypathtable = (); 287 288 for ( my $i = 0; $i <= $#{$filecontent}; $i++ ) 289 { 290 if (( $i == 0 ) || ( $i == 1 ) || ( $i == 2 )) { next; } 291 292 if ( ${$filecontent}[$i] =~ /^\s*(.*?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\s*$/ ) 293 { 294 my $component = $1; 295 my $keypath = $6; 296 297 $keypathtable{$keypath} = $component; 298 } 299 } 300 301 return (\%keypathtable); 302 303} 304 305################################################################################# 306# Analyzing the content of Registry.idt 307################################################################################# 308 309sub analyze_registry_file 310{ 311 my ($filecontent) = @_; 312 313 my %table = (); 314 315 for ( my $i = 0; $i <= $#{$filecontent}; $i++ ) 316 { 317 if (( $i == 0 ) || ( $i == 1 ) || ( $i == 2 )) { next; } 318 319 if ( ${$filecontent}[$i] =~ /^\s*(.*?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\s*$/ ) 320 { 321 my $registry = $1; 322 my $root = $2; 323 my $key = $3; 324 my $name = $4; 325 my $value = $5; 326 my $component = $6; 327 328 my %helphash = (); 329 # $helphash{'Registry'} = $registry; 330 $helphash{'Root'} = $root; 331 $helphash{'Key'} = $key; 332 $helphash{'Name'} = $name; 333 $helphash{'Value'} = $value; 334 $helphash{'Component'} = $component; 335 336 $table{$registry} = \%helphash; 337 } 338 } 339 340 return \%table; 341} 342 343################################################################################# 344# Analyzing the content of File.idt 345################################################################################# 346 347sub analyze_file_file 348{ 349 my ($filecontent) = @_; 350 351 my %table = (); 352 my %fileorder = (); 353 my $maxsequence = 0; 354 355 for ( my $i = 0; $i <= $#{$filecontent}; $i++ ) 356 { 357 if (( $i == 0 ) || ( $i == 1 ) || ( $i == 2 )) { next; } 358 359 if ( ${$filecontent}[$i] =~ /^\s*(.*?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\s*$/ ) 360 { 361 my $file = $1; 362 my $comp = $2; 363 my $filename = $3; 364 my $sequence = $8; 365 366 if ( $filename =~ /^\s*(.*?)\s*\|\s*(.*?)\s*$/ ) { $filename = $2; } 367 368 my %helphash = (); 369 $helphash{'Component'} = $comp; 370 $helphash{'FileName'} = $filename; 371 $helphash{'Sequence'} = $sequence; 372 373 $table{$file} = \%helphash; 374 375 $fileorder{$sequence} = $file; 376 377 if ( $sequence > $maxsequence ) { $maxsequence = $sequence; } 378 } 379 } 380 381 return (\%table, \%fileorder, $maxsequence); 382} 383 384#################################################################################### 385# Recursively creating the directory tree 386#################################################################################### 387 388sub create_directory_tree 389{ 390 my ($parent, $pathcollector, $fulldir, $dirhash) = @_; 391 392 foreach my $dir ( keys %{$dirhash} ) 393 { 394 if (( $dirhash->{$dir}->{'Directory_Parent'} eq $parent ) && ( $dirhash->{$dir}->{'DefaultDir'} ne "." )) 395 { 396 my $dirname = $dirhash->{$dir}->{'DefaultDir'}; 397 # Create the directory 398 my $newdir = $fulldir . $installer::globals::separator . $dirname; 399 if ( ! -f $newdir ) { mkdir $newdir; } 400 # Saving in collector 401 $pathcollector->{$dir} = $newdir; 402 # Iteration 403 create_directory_tree($dir, $pathcollector, $newdir, $dirhash); 404 } 405 } 406} 407 408#################################################################################### 409# Creating the directory tree 410#################################################################################### 411 412sub create_directory_structure 413{ 414 my ($dirhash, $targetdir) = @_; 415 416 my %fullpathhash = (); 417 418 my @startparents = ("TARGETDIR", "INSTALLLOCATION"); 419 420 foreach $dir (@startparents) { create_directory_tree($dir, \%fullpathhash, $targetdir, $dirhash); } 421 422 # Also adding the pathes of the startparents 423 foreach $dir (@startparents) 424 { 425 if ( ! exists($fullpathhash{$dir}) ) { $fullpathhash{$dir} = $targetdir; } 426 } 427 428 return \%fullpathhash; 429} 430 431#################################################################################### 432# Copying files into installation set 433#################################################################################### 434 435sub copy_files_into_directory_structure 436{ 437 my ($fileorder, $filehash, $componenthash, $fullpathhash, $maxsequence, $unpackdir, $installdir, $dirhash) = @_; 438 439 my $unopkgfile = ""; 440 441 for ( my $i = 1; $i <= $maxsequence; $i++ ) 442 { 443 if ( exists($fileorder->{$i}) ) 444 { 445 my $file = $fileorder->{$i}; 446 if ( ! exists($filehash->{$file}->{'Component'}) ) { installer::exiter::exit_program("ERROR: Did not find component for file: \"$file\".", "copy_files_into_directory_structure"); } 447 my $component = $filehash->{$file}->{'Component'}; 448 if ( ! exists($componenthash->{$component}) ) { installer::exiter::exit_program("ERROR: Did not find directory for component: \"$component\".", "copy_files_into_directory_structure"); } 449 my $dirname = $componenthash->{$component}; 450 if ( ! exists($fullpathhash->{$dirname}) ) { installer::exiter::exit_program("ERROR: Did not find full directory path for dir: \"$dirname\".", "copy_files_into_directory_structure"); } 451 my $destdir = $fullpathhash->{$dirname}; 452 if ( ! exists($filehash->{$file}->{'FileName'}) ) { installer::exiter::exit_program("ERROR: Did not find \"FileName\" for file: \"$file\".", "copy_files_into_directory_structure"); } 453 my $destfile = $filehash->{$file}->{'FileName'}; 454 455 $destfile = $destdir . $installer::globals::separator . $destfile; 456 my $sourcefile = $unpackdir . $installer::globals::separator . $file; 457 458 if ( ! -f $sourcefile ) 459 { 460 # It is possible, that this was an unpacked file 461 # Looking in the dirhash, to find the subdirectory in the installation set (the id is $dirname) 462 # subdir is not recursively analyzed, only one directory. 463 464 my $oldsourcefile = $sourcefile; 465 my $subdir = ""; 466 if ( exists($dirhash->{$dirname}->{'DefaultDir'}) ) { $subdir = $dirhash->{$dirname}->{'DefaultDir'} . $installer::globals::separator; } 467 my $realfilename = $filehash->{$file}->{'FileName'}; 468 my $localinstalldir = $installdir; 469 470 $localinstalldir =~ s/\\\s*$//; 471 $localinstalldir =~ s/\/\s*$//; 472 473 $sourcefile = $localinstalldir . $installer::globals::separator . $subdir . $realfilename; 474 475 if ( ! -f $sourcefile ) 476 { 477 installer::exiter::exit_program("ERROR: File not found: \"$oldsourcefile\" (or \"$sourcefile\").", "copy_files_into_directory_structure"); 478 } 479 } 480 481 my $copyreturn = copy($sourcefile, $destfile); 482 483 if ( ! $copyreturn) # only logging problems 484 { 485 my $infoline = "ERROR: Could not copy $sourcefile to $destfile (insufficient disc space for $destfile ?)\n"; 486 $returnvalue = 0; 487 push(@installer::globals::logfileinfo, $infoline); 488 installer::exiter::exit_program($infoline, "copy_files_into_directory_structure"); 489 } 490 491 if ( $destfile =~ /unopkg\.exe\s*$/ ) { $unopkgfile = $destfile; } 492 493 # installer::systemactions::copy_one_file($sourcefile, $destfile); 494 } 495 # else # allowing missing sequence numbers ? 496 # { 497 # installer::exiter::exit_program("ERROR: No file assigned to sequence $i", "copy_files_into_directory_structure"); 498 # } 499 } 500 501 return $unopkgfile; 502} 503 504 505############################################################### 506# Setting the time string for the 507# Summary Information stream in the 508# msi database of the admin installations. 509############################################################### 510 511sub get_sis_time_string 512{ 513 # Syntax: <yyyy/mm/dd hh:mm:ss> 514 my $second = (localtime())[0]; 515 my $minute = (localtime())[1]; 516 my $hour = (localtime())[2]; 517 my $day = (localtime())[3]; 518 my $month = (localtime())[4]; 519 my $year = 1900 + (localtime())[5]; 520 521 $month++; # zero based month 522 523 if ( $second < 10 ) { $second = "0" . $second; } 524 if ( $minute < 10 ) { $minute = "0" . $minute; } 525 if ( $hour < 10 ) { $hour = "0" . $hour; } 526 if ( $day < 10 ) { $day = "0" . $day; } 527 if ( $month < 10 ) { $month = "0" . $month; } 528 529 my $timestring = $year . "/" . $month . "/" . $day . " " . $hour . ":" . $minute . ":" . $second; 530 531 return $timestring; 532} 533 534############################################################### 535# Windows registry entries containing properties are not set 536# correctly during msp patch process. The properties are 537# empty or do get their default values. This destroys the 538# values of many entries in Windows registry. 539# This can be fixed by removing all entries in Registry table, 540# containing a property before starting msimsp.exe. 541############################################################### 542 543sub remove_properties_from_registry_table 544{ 545 my ($registryhash, $componentkeypathhash, $registryfilecontent) = @_; 546 547 installer::logger::include_timestamp_into_logfile("\nPerformance Info: Start remove_properties_from_registry_table"); 548 549 my @registrytable = (); 550 551 # Registry hash 552 # Collecting all RegistryItems with values containing a property: [...] 553 # To which component do they belong 554 # Is this after removal an empty component? Create a replacement, so that 555 # no Component has to be removed. 556 # Is this RegistryItem a KeyPath of a component. Then it cannot be removed. 557 558 my %problemitems = (); 559 my %problemcomponents = (); 560 my %securecomponents = (); 561 my $changevalue = ""; 562 my $changeroot = ""; 563 my $infoline = ""; 564 565 my $newitemcounter = 0; 566 my $olditemcounter = 0; 567 568 foreach my $regitem ( keys %{$registryhash} ) 569 { 570 my $value = ""; 571 if ( exists($registryhash->{$regitem}->{'Value'}) ) { $value = $registryhash->{$regitem}->{'Value'}; } 572 573 if ( $value =~ /^.*(\[.*?\]).*$/ ) 574 { 575 my $property = $1; 576 577 # Collecting registry item 578 $problemitems{$regitem} = 1; # "1" -> can be removed 579 if ( exists($componentkeypathhash->{$regitem}) ) { $problemitems{$regitem} = 2; } # "2" -> cannot be removed, KeyPath 580 581 # Collecting component (and number of problematic registry items 582 # my $component = $registryhash->{$regitem}->{'Component'}; 583 # if ( exists($problemcomponents{$regitem}) ) { $problemcomponents{$regitem} = $problemcomponents{$regitem} + 1; } 584 # else { $problemcomponents{$regitem} = 1; } 585 } 586 else 587 { 588 # Collecting all components with secure regisry items 589 my $component = ""; 590 if ( exists($registryhash->{$regitem}->{'Component'}) ) { $component = $registryhash->{$regitem}->{'Component'}; } 591 if ( $component eq "" ) { installer::exiter::exit_program("ERROR: Did not find component for registry item \"$regitem\".", "remove_properties_from_registry_table"); } 592 $securecomponents{$component} = 1; 593 } 594 595 # Searching for change value 596 my $localkey = ""; 597 if ( exists($registryhash->{$regitem}->{'Key'}) ) { $localkey = $registryhash->{$regitem}->{'Key'}; } 598 if (( $localkey =~ /^\s*(Software\\.*\\)StartMenu\s*$/ ) && ( $changevalue eq "" )) 599 { 600 $changevalue = $1; 601 $changeroot = $registryhash->{$regitem}->{'Root'}; 602 } 603 604 $olditemcounter++; 605 } 606 607 my $removecounter = 0; 608 my $renamecounter = 0; 609 610 foreach my $regitem ( keys %{$registryhash} ) 611 { 612 my $value = ""; 613 if ( exists($registryhash->{$regitem}->{'Value'}) ) { $value = $registryhash->{$regitem}->{'Value'}; } 614 615 if ( $value =~ /^.*(\[.*?\]).*$/ ) 616 { 617 # Removing registry items, that are no KeyPath and that belong to components, 618 # that have other secure registry items. 619 620 my $component = ""; 621 if ( exists($registryhash->{$regitem}->{'Component'}) ) { $component = $registryhash->{$regitem}->{'Component'}; } 622 if ( $component eq "" ) { installer::exiter::exit_program("ERROR: Did not find component for registry item (2) \"$regitem\".", "remove_properties_from_registry_table"); } 623 624 if (( $problemitems{$regitem} == 1 ) && ( exists($securecomponents{$component}) )) 625 { 626 # remove complete registry item 627 delete($registryhash->{$regitem}); 628 $removecounter++; 629 $infoline = "Removing registry item: $regitem : $value\n"; 630 push( @installer::globals::logfileinfo, $infoline); 631 } 632 else 633 { 634 # Changing values of registry items, that are KeyPath or that contain to 635 # components with only unsecure registry items. 636 637 if (( $problemitems{$regitem} == 2 ) || ( ! exists($securecomponents{$component}) )) 638 { 639 # change value of registry item 640 if ( $changevalue eq "" ) { installer::exiter::exit_program("ERROR: Did not find good change value for registry items", "remove_properties_from_registry_table"); } 641 642 my $oldkey = ""; 643 if ( exists($registryhash->{$regitem}->{'Key'}) ) { $oldkey = $registryhash->{$regitem}->{'Key'}; }; 644 my $oldname = ""; 645 if ( exists($registryhash->{$regitem}->{'Name'}) ) { $oldname = $registryhash->{$regitem}->{'Name'}; } 646 my $oldvalue = ""; 647 if ( exists($registryhash->{$regitem}->{'Value'}) ) { $oldvalue = $registryhash->{$regitem}->{'Value'}; } 648 649 $registryhash->{$regitem}->{'Key'} = $changevalue . "RegistryItem"; 650 $registryhash->{$regitem}->{'Root'} = $changeroot; 651 $registryhash->{$regitem}->{'Name'} = $regitem; 652 $registryhash->{$regitem}->{'Value'} = 1; 653 $renamecounter++; 654 655 $infoline = "Changing registry item: $regitem\n"; 656 $infoline = "Old: $oldkey : $oldname : $oldvalue\n"; 657 $infoline = "New: $registryhash->{$regitem}->{'Key'} : $registryhash->{$regitem}->{'Name'} : $registryhash->{$regitem}->{'Value'}\n"; 658 push( @installer::globals::logfileinfo, $infoline); 659 } 660 } 661 } 662 } 663 664 $infoline = "Number of removed registry items: $removecounter\n"; 665 push( @installer::globals::logfileinfo, $infoline); 666 $infoline = "Number of changed registry items: $renamecounter\n"; 667 push( @installer::globals::logfileinfo, $infoline); 668 669 # Creating the new content of Registry table 670 # First three lines from $registryfilecontent 671 # All further files from changed $registryhash 672 673 for ( my $i = 0; $i <= 2; $i++ ) { push(@registrytable, ${$registryfilecontent}[$i]); } 674 675 foreach my $regitem ( keys %{$registryhash} ) 676 { 677 my $root = ""; 678 if ( exists($registryhash->{$regitem}->{'Root'}) ) { $root = $registryhash->{$regitem}->{'Root'}; } 679 else { installer::exiter::exit_program("ERROR: Did not find root in registry table for item: \"$regitem\".", "remove_properties_from_registry_table"); } 680 my $localkey = ""; 681 if ( exists($registryhash->{$regitem}->{'Key'}) ) { $localkey = $registryhash->{$regitem}->{'Key'}; } 682 my $name = ""; 683 if ( exists($registryhash->{$regitem}->{'Name'}) ) { $name = $registryhash->{$regitem}->{'Name'}; } 684 my $value = ""; 685 if ( exists($registryhash->{$regitem}->{'Value'}) ) { $value = $registryhash->{$regitem}->{'Value'}; } 686 my $comp = ""; 687 if ( exists($registryhash->{$regitem}->{'Component'}) ) { $comp = $registryhash->{$regitem}->{'Component'}; } 688 689 my $oneline = $regitem . "\t" . $root . "\t" . $localkey . "\t" . $name . "\t" . $value . "\t" . $comp . "\n"; 690 push(@registrytable, $oneline); 691 692 $newitemcounter++; 693 } 694 695 $infoline = "Number of registry items: $newitemcounter. Old value: $olditemcounter.\n"; 696 push( @installer::globals::logfileinfo, $infoline); 697 698 installer::logger::include_timestamp_into_logfile("\nPerformance Info: End remove_properties_from_registry_table"); 699 700 return (\@registrytable); 701} 702 703############################################################### 704# Writing content of administrative installations into 705# Summary Information Stream of msi database. 706# This is required for example for following 707# patch processes using Windows Installer service. 708############################################################### 709 710sub write_sis_info 711{ 712 my ($msidatabase) = @_ ; 713 714 if ( ! -f $msidatabase ) { installer::exiter::exit_program("ERROR: Cannot find file $msidatabase", "write_sis_info"); } 715 716 my $msiinfo = "msiinfo.exe"; # Has to be in the path 717 my $infoline = ""; 718 my $systemcall = ""; 719 my $returnvalue = ""; 720 721 # Required setting for administrative installations: 722 # -w 4 (source files are unpacked), wordcount 723 # -s <date of admin installation>, LastPrinted, Syntax: <yyyy/mm/dd hh:mm:ss> 724 # -l <person_making_admin_installation>, LastSavedBy 725 726 my $wordcount = 4; # Unpacked files 727 my $lastprinted = get_sis_time_string(); 728 my $lastsavedby = "Installer"; 729 730 my $localmsidatabase = $msidatabase; 731 732 if( $^O =~ /cygwin/i ) 733 { 734 $localmsidatabase = qx{cygpath -w "$localmsidatabase"}; 735 $localmsidatabase =~ s/\\/\\\\/g; 736 $localmsidatabase =~ s/\s*$//g; 737 } 738 739 $systemcall = $msiinfo . " " . "\"" . $localmsidatabase . "\"" . " -w " . $wordcount . " -s " . "\"" . $lastprinted . "\"" . " -l $lastsavedby"; 740 push(@installer::globals::logfileinfo, $systemcall); 741 $returnvalue = system($systemcall); 742 743 if ($returnvalue) 744 { 745 $infoline = "ERROR: Could not execute $systemcall !\n"; 746 push(@installer::globals::logfileinfo, $infoline); 747 installer::exiter::exit_program($infoline, "write_sis_info"); 748 } 749} 750 751#################################################### 752# Detecting the directory with extensions 753#################################################### 754 755sub get_extensions_dir 756{ 757 my ( $unopkgfile ) = @_; 758 759 my $localbranddir = $unopkgfile; 760 installer::pathanalyzer::get_path_from_fullqualifiedname(\$localbranddir); # "program" dir in brand layer 761 installer::pathanalyzer::get_path_from_fullqualifiedname(\$localbranddir); # root dir in brand layer 762 $localbranddir =~ s/\Q$installer::globals::separator\E\s*$//; 763 my $extensiondir = $localbranddir . $installer::globals::separator . "share" . $installer::globals::separator . "extensions"; 764 765 return $extensiondir; 766} 767 768############################################################## 769# Removing all empty directories below a specified directory 770############################################################## 771 772sub remove_empty_dirs_in_folder 773{ 774 my ( $dir, $firstrun ) = @_; 775 776 if ( $firstrun ) 777 { 778 print "Removing superfluous directories\n"; 779 } 780 781 my @content = (); 782 783 $dir =~ s/\Q$installer::globals::separator\E\s*$//; 784 785 if ( -d $dir ) 786 { 787 opendir(DIR, $dir); 788 @content = readdir(DIR); 789 closedir(DIR); 790 791 my $oneitem; 792 793 foreach $oneitem (@content) 794 { 795 if ((!($oneitem eq ".")) && (!($oneitem eq ".."))) 796 { 797 my $item = $dir . $installer::globals::separator . $oneitem; 798 799 if ( -d $item ) # recursive 800 { 801 remove_empty_dirs_in_folder($item, 0); 802 } 803 } 804 } 805 806 # try to remove empty directory 807 my $returnvalue = rmdir $dir; 808 809 # if ( $returnvalue ) { print "Successfully removed empty dir $dir\n"; } 810 } 811} 812 813#################################################################################### 814# Simulating an administrative installation 815#################################################################################### 816 817sub make_admin_install 818{ 819 my ($databasepath, $targetdir) = @_; 820 821 # Create helper directory 822 823 installer::logger::print_message( "... installing $databasepath in directory $targetdir ...\n" ); 824 825 my $helperdir = $targetdir . $installer::globals::separator . "installhelper"; 826 installer::systemactions::create_directory($helperdir); 827 828 # Get File.idt, Component.idt and Directory.idt from database 829 830 my $tablelist = "File Directory Component Registry"; 831 extract_tables_from_pcpfile($databasepath, $helperdir, $tablelist); 832 833 # Unpack all cab files into $helperdir, cab files must be located next to msi database 834 my $installdir = $databasepath; 835 836 if ( $^O =~ /cygwin/i ) { $installdir =~ s/\\/\//g; } # backslash to slash 837 838 installer::pathanalyzer::get_path_from_fullqualifiedname(\$installdir); 839 840 if ( $^O =~ /cygwin/i ) { $installdir =~ s/\//\\/g; } # slash to backslash 841 842 my $databasefilename = $databasepath; 843 installer::pathanalyzer::make_absolute_filename_to_relative_filename(\$databasefilename); 844 845 my $cabfiles = installer::systemactions::find_file_with_file_extension("cab", $installdir); 846 847 if ( $#{$cabfiles} < 0 ) { installer::exiter::exit_program("ERROR: Did not find any cab file in directory $installdir", "make_admin_install"); } 848 849 # Set unpackdir 850 my $unpackdir = $helperdir . $installer::globals::separator . "unpack"; 851 installer::systemactions::create_directory($unpackdir); 852 853 for ( my $i = 0; $i <= $#{$cabfiles}; $i++ ) 854 { 855 my $cabfile = ""; 856 if ( $^O =~ /cygwin/i ) 857 { 858 $cabfile = $installdir . ${$cabfiles}[$i]; 859 } 860 else 861 { 862 $cabfile = $installdir . $installer::globals::separator . ${$cabfiles}[$i]; 863 } 864 unpack_cabinet_file($cabfile, $unpackdir); 865 } 866 867 # Reading tables 868 my $filename = $helperdir . $installer::globals::separator . "Directory.idt"; 869 my $filecontent = installer::files::read_file($filename); 870 my $dirhash = analyze_directory_file($filecontent); 871 872 $filename = $helperdir . $installer::globals::separator . "Component.idt"; 873 my $componentfilecontent = installer::files::read_file($filename); 874 my $componenthash = analyze_component_file($componentfilecontent); 875 876 $filename = $helperdir . $installer::globals::separator . "File.idt"; 877 $filecontent = installer::files::read_file($filename); 878 my ( $filehash, $fileorder, $maxsequence ) = analyze_file_file($filecontent); 879 880 # Creating the directory structure 881 my $fullpathhash = create_directory_structure($dirhash, $targetdir); 882 883 # Copying files 884 my $unopkgfile = copy_files_into_directory_structure($fileorder, $filehash, $componenthash, $fullpathhash, $maxsequence, $unpackdir, $installdir, $dirhash); 885 886 my $msidatabase = $targetdir . $installer::globals::separator . $databasefilename; 887 installer::systemactions::copy_one_file($databasepath, $msidatabase); 888 889 if ( $unopkgfile ne "" ) 890 { 891 # Removing empty dirs in extension folder 892 my $extensionfolder = get_extensions_dir($unopkgfile); 893 if ( -d $extensionfolder ) { remove_empty_dirs_in_folder($extensionfolder, 1); } 894 } 895 896 # Editing registry table because of wrong Property value 897 # my $registryfilename = $helperdir . $installer::globals::separator . "Registry.idt"; 898 # my $componentfilename = $helperdir . $installer::globals::separator . "Component.idt"; 899 # my $componentkeypathhash = analyze_keypath_component_file($componentfilecontent); 900 901 # my $registryfilecontent = installer::files::read_file($registryfilename); 902 # my $registryhash = analyze_registry_file($registryfilecontent); 903 904 # $registryfilecontent = remove_properties_from_registry_table($registryhash, $componentkeypathhash, $registryfilecontent); 905 906 # installer::files::save_file($registryfilename, $registryfilecontent); 907 # $tablelist = "Registry"; 908 # include_tables_into_pcpfile($msidatabase, $helperdir, $tablelist); 909 910 # Saving info in Summary Information Stream of msi database (required for following patches) 911 write_sis_info($msidatabase); 912 913 return $msidatabase; 914} 915 9161; 917