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