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