Update ooo320-m1
[ooovba.git] / solenv / bin / modules / installer / windows / admin.pm
blob808b3673902e3d6b5349d3248f0c6c5add2755ee
1 #*************************************************************************
3 # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 #
5 # Copyright 2008 by Sun Microsystems, Inc.
7 # OpenOffice.org - a multi-platform office productivity suite
9 # $RCSfile: admin.pm,v $
11 # $Revision: 1.1.2.2 $
13 # This file is part of OpenOffice.org.
15 # OpenOffice.org is free software: you can redistribute it and/or modify
16 # it under the terms of the GNU Lesser General Public License version 3
17 # only, as published by the Free Software Foundation.
19 # OpenOffice.org is distributed in the hope that it will be useful,
20 # but WITHOUT ANY WARRANTY; without even the implied warranty of
21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 # GNU Lesser General Public License version 3 for more details
23 # (a copy is included in the LICENSE file that accompanied this code).
25 # You should have received a copy of the GNU Lesser General Public License
26 # version 3 along with OpenOffice.org. If not, see
27 # <http://www.openoffice.org/license.html>
28 # for a copy of the LGPLv3 License.
30 #*************************************************************************
32 package installer::windows::admin;
34 use File::Copy;
35 use installer::exiter;
36 use installer::files;
37 use installer::globals;
38 use installer::pathanalyzer;
39 use installer::systemactions;
40 use installer::worker;
41 use installer::windows::idtglobal;
43 #################################################################################
44 # Unpacking cabinet files with expand
45 #################################################################################
47 sub unpack_cabinet_file
49 my ($cabfilename, $unpackdir) = @_;
51 my $infoline = "Unpacking cabinet file: $cabfilename\n";
52 push( @installer::globals::logfileinfo, $infoline);
54 my $expandfile = "expand.exe"; # Has to be in the path
56 # expand.exe has to be located in the system directory.
57 # Cygwin has another tool expand.exe, that converts tabs to spaces. This cannot be used of course.
58 # But this wrong expand.exe is typically in the PATH before this expand.exe, to unpack
59 # cabinet files.
61 # if ( $^O =~ /cygwin/i )
62 # {
63 # $expandfile = $ENV{'SYSTEMROOT'} . "/system32/expand.exe"; # Has to be located in the systemdirectory
64 # $expandfile =~ s/\\/\//;
65 # if ( ! -f $expandfile ) { exit_program("ERROR: Did not find file $expandfile in the Windows system folder!"); }
66 # }
68 if ( $^O =~ /cygwin/i )
70 $expandfile = qx(cygpath -u "$ENV{WINDIR}"/System32/expand.exe);
71 chomp $expandfile;
74 my $expandlogfile = $unpackdir . $installer::globals::separator . "expand.log";
76 # exclude cabinet file
77 # my $systemcall = $cabarc . " -o X " . $mergemodulehash->{'cabinetfile'};
79 my $systemcall = "";
80 if ( $^O =~ /cygwin/i ) {
81 my $localunpackdir = qx{cygpath -w "$unpackdir"};
82 chomp ($localunpackdir);
83 $localunpackdir =~ s/\\/\\\\/g;
84 $cabfilename =~ s/\\/\\\\/g;
85 $cabfilename =~ s/\s*$//g;
86 $systemcall = $expandfile . " " . $cabfilename . " -F:\* " . $localunpackdir . " \> " . $expandlogfile;
88 else
90 $systemcall = $expandfile . " " . $cabfilename . " -F:\* " . $unpackdir . " \> " . $expandlogfile;
93 my $returnvalue = system($systemcall);
94 $infoline = "Systemcall: $systemcall\n";
95 push( @installer::globals::logfileinfo, $infoline);
97 if ($returnvalue)
99 $infoline = "ERROR: Could not execute $systemcall !\n";
100 push( @installer::globals::logfileinfo, $infoline);
101 installer::exiter::exit_program("ERROR: Could not extract cabinet file: $mergemodulehash->{'cabinetfile'} !", "change_file_table");
103 else
105 $infoline = "Success: Executed $systemcall successfully!\n";
106 push( @installer::globals::logfileinfo, $infoline);
110 #################################################################################
111 # Include tables into a msi database
112 #################################################################################
114 sub include_tables_into_pcpfile
116 my ($fullmsidatabasepath, $workdir, $tables) = @_;
118 my $msidb = "msidb.exe"; # Has to be in the path
119 my $infoline = "";
120 my $systemcall = "";
121 my $returnvalue = "";
123 # Make all table 8+3 conform
124 my $alltables = installer::converter::convert_stringlist_into_array(\$tables, " ");
126 for ( my $i = 0; $i <= $#{$alltables}; $i++ )
128 my $tablename = ${$alltables}[$i];
129 $tablename =~ s/\s*$//;
130 my $namelength = length($tablename);
131 if ( $namelength > 8 )
133 my $newtablename = substr($tablename, 0, 8); # name, offset, length
134 my $oldfile = $workdir . $installer::globals::separator . $tablename . ".idt";
135 my $newfile = $workdir . $installer::globals::separator . $newtablename . ".idt";
136 if ( -f $newfile ) { unlink $newfile; }
137 installer::systemactions::copy_one_file($oldfile, $newfile);
138 my $savfile = $oldfile . ".orig";
139 installer::systemactions::copy_one_file($oldfile, $savfile);
143 # Import of tables
145 $systemcall = $msidb . " -d " . $fullmsidatabasepath . " -f " . $workdir . " -i " . $tables;
147 $returnvalue = system($systemcall);
149 $infoline = "Systemcall: $systemcall\n";
150 push( @installer::globals::logfileinfo, $infoline);
152 if ($returnvalue)
154 $infoline = "ERROR: Could not execute $systemcall !\n";
155 push( @installer::globals::logfileinfo, $infoline);
156 installer::exiter::exit_program("ERROR: Could not include tables into msi database: $fullmsidatabasepath !", "include_tables_into_pcpfile");
158 else
160 $infoline = "Success: Executed $systemcall successfully!\n";
161 push( @installer::globals::logfileinfo, $infoline);
165 #################################################################################
166 # Extracting tables from msi database
167 #################################################################################
169 sub extract_tables_from_pcpfile
171 my ($fullmsidatabasepath, $workdir, $tablelist) = @_;
173 my $msidb = "msidb.exe"; # Has to be in the path
174 my $infoline = "";
175 my $systemcall = "";
176 my $returnvalue = "";
178 my $localfullmsidatabasepath = $fullmsidatabasepath;
180 # Export of all tables by using "*"
182 if ( $^O =~ /cygwin/i ) {
183 # Copying the msi database locally guarantees the format of the directory.
184 # Otherwise it is defined in the file of UPDATE_DATABASE_LISTNAME
186 my $msifilename = $localfullmsidatabasepath;
187 installer::pathanalyzer::make_absolute_filename_to_relative_filename(\$msifilename);
188 my $destdatabasename = $workdir . $installer::globals::separator . $msifilename;
189 installer::systemactions::copy_one_file($localfullmsidatabasepath, $destdatabasename);
190 $localfullmsidatabasepath = $destdatabasename;
192 chomp( $localfullmsidatabasepath = qx{cygpath -w "$localfullmsidatabasepath"} );
193 chomp( $workdir = qx{cygpath -w "$workdir"} );
195 # msidb.exe really wants backslashes. (And double escaping because system() expands the string.)
196 $localfullmsidatabasepath =~ s/\\/\\\\/g;
197 $workdir =~ s/\\/\\\\/g;
199 # and if there are still slashes, they also need to be double backslash
200 $localfullmsidatabasepath =~ s/\//\\\\/g;
201 $workdir =~ s/\//\\\\/g;
204 $systemcall = $msidb . " -d " . $localfullmsidatabasepath . " -f " . $workdir . " -e $tablelist";
205 $returnvalue = system($systemcall);
207 $infoline = "Systemcall: $systemcall\n";
208 push( @installer::globals::logfileinfo, $infoline);
210 if ($returnvalue)
212 $infoline = "ERROR: Could not execute $systemcall !\n";
213 push( @installer::globals::logfileinfo, $infoline);
214 installer::exiter::exit_program("ERROR: Could not exclude tables from pcp file: $localfullmsidatabasepath !", "extract_tables_from_pcpfile");
216 else
218 $infoline = "Success: Executed $systemcall successfully!\n";
219 push( @installer::globals::logfileinfo, $infoline);
223 ################################################################################
224 # Analyzing the content of Directory.idt
225 #################################################################################
227 sub analyze_directory_file
229 my ($filecontent) = @_;
231 my %table = ();
233 for ( my $i = 0; $i <= $#{$filecontent}; $i++ )
235 if (( $i == 0 ) || ( $i == 1 ) || ( $i == 2 )) { next; }
237 if ( ${$filecontent}[$i] =~ /^\s*(.*?)\t(.*?)\t(.*?)\s*$/ )
239 my $dir = $1;
240 my $parent = $2;
241 my $name = $3;
243 if ( $name =~ /^\s*(.*?)\s*\:\s*(.*?)\s*$/ ) { $name = $2; }
244 if ( $name =~ /^\s*(.*?)\s*\|\s*(.*?)\s*$/ ) { $name = $2; }
246 my %helphash = ();
247 $helphash{'Directory_Parent'} = $parent;
248 $helphash{'DefaultDir'} = $name;
249 $table{$dir} = \%helphash;
253 return \%table;
256 #################################################################################
257 # Analyzing the content of Component.idt
258 #################################################################################
260 sub analyze_component_file
262 my ($filecontent) = @_;
264 my %table = ();
266 for ( my $i = 0; $i <= $#{$filecontent}; $i++ )
268 if (( $i == 0 ) || ( $i == 1 ) || ( $i == 2 )) { next; }
270 if ( ${$filecontent}[$i] =~ /^\s*(.*?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\s*$/ )
272 my $component = $1;
273 my $dir = $3;
275 $table{$component} = $dir;
279 return \%table;
282 #################################################################################
283 # Analyzing the full content of Component.idt
284 #################################################################################
286 sub analyze_keypath_component_file
288 my ($filecontent) = @_;
290 my %keypathtable = ();
292 for ( my $i = 0; $i <= $#{$filecontent}; $i++ )
294 if (( $i == 0 ) || ( $i == 1 ) || ( $i == 2 )) { next; }
296 if ( ${$filecontent}[$i] =~ /^\s*(.*?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\s*$/ )
298 my $component = $1;
299 my $keypath = $6;
301 $keypathtable{$keypath} = $component;
305 return (\%keypathtable);
309 #################################################################################
310 # Analyzing the content of Registry.idt
311 #################################################################################
313 sub analyze_registry_file
315 my ($filecontent) = @_;
317 my %table = ();
319 for ( my $i = 0; $i <= $#{$filecontent}; $i++ )
321 if (( $i == 0 ) || ( $i == 1 ) || ( $i == 2 )) { next; }
323 if ( ${$filecontent}[$i] =~ /^\s*(.*?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\s*$/ )
325 my $registry = $1;
326 my $root = $2;
327 my $key = $3;
328 my $name = $4;
329 my $value = $5;
330 my $component = $6;
332 my %helphash = ();
333 # $helphash{'Registry'} = $registry;
334 $helphash{'Root'} = $root;
335 $helphash{'Key'} = $key;
336 $helphash{'Name'} = $name;
337 $helphash{'Value'} = $value;
338 $helphash{'Component'} = $component;
340 $table{$registry} = \%helphash;
344 return \%table;
347 #################################################################################
348 # Analyzing the content of File.idt
349 #################################################################################
351 sub analyze_file_file
353 my ($filecontent) = @_;
355 my %table = ();
356 my %fileorder = ();
357 my $maxsequence = 0;
359 for ( my $i = 0; $i <= $#{$filecontent}; $i++ )
361 if (( $i == 0 ) || ( $i == 1 ) || ( $i == 2 )) { next; }
363 if ( ${$filecontent}[$i] =~ /^\s*(.*?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\s*$/ )
365 my $file = $1;
366 my $comp = $2;
367 my $filename = $3;
368 my $sequence = $8;
370 if ( $filename =~ /^\s*(.*?)\s*\|\s*(.*?)\s*$/ ) { $filename = $2; }
372 my %helphash = ();
373 $helphash{'Component'} = $comp;
374 $helphash{'FileName'} = $filename;
375 $helphash{'Sequence'} = $sequence;
377 $table{$file} = \%helphash;
379 $fileorder{$sequence} = $file;
381 if ( $sequence > $maxsequence ) { $maxsequence = $sequence; }
385 return (\%table, \%fileorder, $maxsequence);
388 ####################################################################################
389 # Recursively creating the directory tree
390 ####################################################################################
392 sub create_directory_tree
394 my ($parent, $pathcollector, $fulldir, $dirhash) = @_;
396 foreach my $dir ( keys %{$dirhash} )
398 if (( $dirhash->{$dir}->{'Directory_Parent'} eq $parent ) && ( $dirhash->{$dir}->{'DefaultDir'} ne "." ))
400 my $dirname = $dirhash->{$dir}->{'DefaultDir'};
401 # Create the directory
402 my $newdir = $fulldir . $installer::globals::separator . $dirname;
403 if ( ! -f $newdir ) { mkdir $newdir; }
404 # Saving in collector
405 $pathcollector->{$dir} = $newdir;
406 # Iteration
407 create_directory_tree($dir, $pathcollector, $newdir, $dirhash);
412 ####################################################################################
413 # Creating the directory tree
414 ####################################################################################
416 sub create_directory_structure
418 my ($dirhash, $targetdir) = @_;
420 my %fullpathhash = ();
422 my @startparents = ("TARGETDIR", "INSTALLLOCATION");
424 foreach $dir (@startparents) { create_directory_tree($dir, \%fullpathhash, $targetdir, $dirhash); }
426 return \%fullpathhash;
429 ####################################################################################
430 # Copying files into installation set
431 ####################################################################################
433 sub copy_files_into_directory_structure
435 my ($fileorder, $filehash, $componenthash, $fullpathhash, $maxsequence, $unpackdir, $installdir, $dirhash) = @_;
437 for ( my $i = 1; $i <= $maxsequence; $i++ )
439 if ( exists($fileorder->{$i}) )
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'};
451 $destfile = $destdir . $installer::globals::separator . $destfile;
452 my $sourcefile = $unpackdir . $installer::globals::separator . $file;
454 if ( ! -f $sourcefile )
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.
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;
466 $localinstalldir =~ s/\\\s*$//;
467 $localinstalldir =~ s/\/\s*$//;
469 $sourcefile = $localinstalldir . $installer::globals::separator . $subdir . $realfilename;
471 if ( ! -f $sourcefile )
473 installer::exiter::exit_program("ERROR: File not found: \"$oldsourcefile\" (or \"$sourcefile\").", "copy_files_into_directory_structure");
477 my $copyreturn = copy($sourcefile, $destfile);
479 if ( ! $copyreturn) # only logging problems
481 my $infoline = "ERROR: Could not copy $sourcefile to $destfile (insufficient disc space for $destfile ?)\n";
482 $returnvalue = 0;
483 push(@installer::globals::logfileinfo, $infoline);
484 installer::exiter::exit_program($infoline, "copy_files_into_directory_structure");
487 # installer::systemactions::copy_one_file($sourcefile, $destfile);
489 # else # allowing missing sequence numbers ?
491 # installer::exiter::exit_program("ERROR: No file assigned to sequence $i", "copy_files_into_directory_structure");
497 ###############################################################
498 # Setting the time string for the
499 # Summary Information stream in the
500 # msi database of the admin installations.
501 ###############################################################
503 sub get_sis_time_string
505 # Syntax: <yyyy/mm/dd hh:mm:ss>
506 my $second = (localtime())[0];
507 my $minute = (localtime())[1];
508 my $hour = (localtime())[2];
509 my $day = (localtime())[3];
510 my $month = (localtime())[4];
511 my $year = 1900 + (localtime())[5];
513 $month++; # zero based month
515 if ( $second < 10 ) { $second = "0" . $second; }
516 if ( $minute < 10 ) { $minute = "0" . $minute; }
517 if ( $hour < 10 ) { $hour = "0" . $hour; }
518 if ( $day < 10 ) { $day = "0" . $day; }
519 if ( $month < 10 ) { $month = "0" . $month; }
521 my $timestring = $year . "/" . $month . "/" . $day . " " . $hour . ":" . $minute . ":" . $second;
523 return $timestring;
526 ###############################################################
527 # Windows registry entries containing properties are not set
528 # correctly during msp patch process. The properties are
529 # empty or do get their default values. This destroys the
530 # values of many entries in Windows registry.
531 # This can be fixed by removing all entries in Registry table,
532 # containing a property before starting msimsp.exe.
533 ###############################################################
535 sub remove_properties_from_registry_table
537 my ($registryhash, $componentkeypathhash, $registryfilecontent) = @_;
539 installer::logger::include_timestamp_into_logfile("\nPerformance Info: Start remove_properties_from_registry_table");
541 my @registrytable = ();
543 # Registry hash
544 # Collecting all RegistryItems with values containing a property: [...]
545 # To which component do they belong
546 # Is this after removal an empty component? Create a replacement, so that
547 # no Component has to be removed.
548 # Is this RegistryItem a KeyPath of a component. Then it cannot be removed.
550 my %problemitems = ();
551 my %problemcomponents = ();
552 my %securecomponents = ();
553 my $changevalue = "";
554 my $changeroot = "";
555 my $infoline = "";
557 my $newitemcounter = 0;
558 my $olditemcounter = 0;
560 foreach my $regitem ( keys %{$registryhash} )
562 my $value = "";
563 if ( exists($registryhash->{$regitem}->{'Value'}) ) { $value = $registryhash->{$regitem}->{'Value'}; }
565 if ( $value =~ /^.*(\[.*?\]).*$/ )
567 my $property = $1;
569 # Collecting registry item
570 $problemitems{$regitem} = 1; # "1" -> can be removed
571 if ( exists($componentkeypathhash->{$regitem}) ) { $problemitems{$regitem} = 2; } # "2" -> cannot be removed, KeyPath
573 # Collecting component (and number of problematic registry items
574 # my $component = $registryhash->{$regitem}->{'Component'};
575 # if ( exists($problemcomponents{$regitem}) ) { $problemcomponents{$regitem} = $problemcomponents{$regitem} + 1; }
576 # else { $problemcomponents{$regitem} = 1; }
578 else
580 # Collecting all components with secure regisry items
581 my $component = "";
582 if ( exists($registryhash->{$regitem}->{'Component'}) ) { $component = $registryhash->{$regitem}->{'Component'}; }
583 if ( $component eq "" ) { installer::exiter::exit_program("ERROR: Did not find component for registry item \"$regitem\".", "remove_properties_from_registry_table"); }
584 $securecomponents{$component} = 1;
587 # Searching for change value
588 my $localkey = "";
589 if ( exists($registryhash->{$regitem}->{'Key'}) ) { $localkey = $registryhash->{$regitem}->{'Key'}; }
590 if (( $localkey =~ /^\s*(Software\\.*\\)StartMenu\s*$/ ) && ( $changevalue eq "" ))
592 $changevalue = $1;
593 $changeroot = $registryhash->{$regitem}->{'Root'};
596 $olditemcounter++;
599 my $removecounter = 0;
600 my $renamecounter = 0;
602 foreach my $regitem ( keys %{$registryhash} )
604 my $value = "";
605 if ( exists($registryhash->{$regitem}->{'Value'}) ) { $value = $registryhash->{$regitem}->{'Value'}; }
607 if ( $value =~ /^.*(\[.*?\]).*$/ )
609 # Removing registry items, that are no KeyPath and that belong to components,
610 # that have other secure registry items.
612 my $component = "";
613 if ( exists($registryhash->{$regitem}->{'Component'}) ) { $component = $registryhash->{$regitem}->{'Component'}; }
614 if ( $component eq "" ) { installer::exiter::exit_program("ERROR: Did not find component for registry item (2) \"$regitem\".", "remove_properties_from_registry_table"); }
616 if (( $problemitems{$regitem} == 1 ) && ( exists($securecomponents{$component}) ))
618 # remove complete registry item
619 delete($registryhash->{$regitem});
620 $removecounter++;
621 $infoline = "Removing registry item: $regitem : $value\n";
622 push( @installer::globals::logfileinfo, $infoline);
624 else
626 # Changing values of registry items, that are KeyPath or that contain to
627 # components with only unsecure registry items.
629 if (( $problemitems{$regitem} == 2 ) || ( ! exists($securecomponents{$component}) ))
631 # change value of registry item
632 if ( $changevalue eq "" ) { installer::exiter::exit_program("ERROR: Did not find good change value for registry items", "remove_properties_from_registry_table"); }
634 my $oldkey = "";
635 if ( exists($registryhash->{$regitem}->{'Key'}) ) { $oldkey = $registryhash->{$regitem}->{'Key'}; };
636 my $oldname = "";
637 if ( exists($registryhash->{$regitem}->{'Name'}) ) { $oldname = $registryhash->{$regitem}->{'Name'}; }
638 my $oldvalue = "";
639 if ( exists($registryhash->{$regitem}->{'Value'}) ) { $oldvalue = $registryhash->{$regitem}->{'Value'}; }
641 $registryhash->{$regitem}->{'Key'} = $changevalue . "RegistryItem";
642 $registryhash->{$regitem}->{'Root'} = $changeroot;
643 $registryhash->{$regitem}->{'Name'} = $regitem;
644 $registryhash->{$regitem}->{'Value'} = 1;
645 $renamecounter++;
647 $infoline = "Changing registry item: $regitem\n";
648 $infoline = "Old: $oldkey : $oldname : $oldvalue\n";
649 $infoline = "New: $registryhash->{$regitem}->{'Key'} : $registryhash->{$regitem}->{'Name'} : $registryhash->{$regitem}->{'Value'}\n";
650 push( @installer::globals::logfileinfo, $infoline);
656 $infoline = "Number of removed registry items: $removecounter\n";
657 push( @installer::globals::logfileinfo, $infoline);
658 $infoline = "Number of changed registry items: $renamecounter\n";
659 push( @installer::globals::logfileinfo, $infoline);
661 # Creating the new content of Registry table
662 # First three lines from $registryfilecontent
663 # All further files from changed $registryhash
665 for ( my $i = 0; $i <= 2; $i++ ) { push(@registrytable, ${$registryfilecontent}[$i]); }
667 foreach my $regitem ( keys %{$registryhash} )
669 my $root = "";
670 if ( exists($registryhash->{$regitem}->{'Root'}) ) { $root = $registryhash->{$regitem}->{'Root'}; }
671 else { installer::exiter::exit_program("ERROR: Did not find root in registry table for item: \"$regitem\".", "remove_properties_from_registry_table"); }
672 my $localkey = "";
673 if ( exists($registryhash->{$regitem}->{'Key'}) ) { $localkey = $registryhash->{$regitem}->{'Key'}; }
674 my $name = "";
675 if ( exists($registryhash->{$regitem}->{'Name'}) ) { $name = $registryhash->{$regitem}->{'Name'}; }
676 my $value = "";
677 if ( exists($registryhash->{$regitem}->{'Value'}) ) { $value = $registryhash->{$regitem}->{'Value'}; }
678 my $comp = "";
679 if ( exists($registryhash->{$regitem}->{'Component'}) ) { $comp = $registryhash->{$regitem}->{'Component'}; }
681 my $oneline = $regitem . "\t" . $root . "\t" . $localkey . "\t" . $name . "\t" . $value . "\t" . $comp . "\n";
682 push(@registrytable, $oneline);
684 $newitemcounter++;
687 $infoline = "Number of registry items: $newitemcounter. Old value: $olditemcounter.\n";
688 push( @installer::globals::logfileinfo, $infoline);
690 installer::logger::include_timestamp_into_logfile("\nPerformance Info: End remove_properties_from_registry_table");
692 return (\@registrytable);
695 ###############################################################
696 # Writing content of administrative installations into
697 # Summary Information Stream of msi database.
698 # This is required for example for following
699 # patch processes using Windows Installer service.
700 ###############################################################
702 sub write_sis_info
704 my ($msidatabase) = @_ ;
706 if ( ! -f $msidatabase ) { installer::exiter::exit_program("ERROR: Cannot find file $msidatabase", "write_sis_info"); }
708 my $msiinfo = "msiinfo.exe"; # Has to be in the path
709 my $infoline = "";
710 my $systemcall = "";
711 my $returnvalue = "";
713 # Required setting for administrative installations:
714 # -w 4 (source files are unpacked), wordcount
715 # -s <date of admin installation>, LastPrinted, Syntax: <yyyy/mm/dd hh:mm:ss>
716 # -l <person_making_admin_installation>, LastSavedBy
718 my $wordcount = 4; # Unpacked files
719 my $lastprinted = get_sis_time_string();
720 my $lastsavedby = "Installer";
722 my $localmsidatabase = $msidatabase;
724 if( $^O =~ /cygwin/i )
726 $localmsidatabase = qx{cygpath -w "$localmsidatabase"};
727 $localmsidatabase =~ s/\\/\\\\/g;
728 $localmsidatabase =~ s/\s*$//g;
731 $systemcall = $msiinfo . " " . "\"" . $localmsidatabase . "\"" . " -w " . $wordcount . " -s " . "\"" . $lastprinted . "\"" . " -l $lastsavedby";
732 push(@installer::globals::logfileinfo, $systemcall);
733 $returnvalue = system($systemcall);
735 if ($returnvalue)
737 $infoline = "ERROR: Could not execute $systemcall !\n";
738 push(@installer::globals::logfileinfo, $infoline);
739 installer::exiter::exit_program($infoline, "write_sis_info");
743 ####################################################################################
744 # Simulating an administrative installation
745 ####################################################################################
747 sub make_admin_install
749 my ($databasepath, $targetdir) = @_;
751 # Create helper directory
753 installer::logger::print_message( "... installing $databasepath in directory $targetdir ...\n" );
755 my $helperdir = $targetdir . $installer::globals::separator . "installhelper";
756 installer::systemactions::create_directory($helperdir);
758 # Get File.idt, Component.idt and Directory.idt from database
760 my $tablelist = "File Directory Component Registry";
761 extract_tables_from_pcpfile($databasepath, $helperdir, $tablelist);
763 # Unpack all cab files into $helperdir, cab files must be located next to msi database
764 my $installdir = $databasepath;
766 if ( $^O =~ /cygwin/i ) { $installdir =~ s/\\/\//g; } # backslash to slash
768 installer::pathanalyzer::get_path_from_fullqualifiedname(\$installdir);
770 if ( $^O =~ /cygwin/i ) { $installdir =~ s/\//\\/g; } # slash to backslash
772 my $databasefilename = $databasepath;
773 installer::pathanalyzer::make_absolute_filename_to_relative_filename(\$databasefilename);
775 my $cabfiles = installer::systemactions::find_file_with_file_extension("cab", $installdir);
777 if ( $#{$cabfiles} < 0 ) { installer::exiter::exit_program("ERROR: Did not find any cab file in directory $installdir", "make_admin_install"); }
779 # Set unpackdir
780 my $unpackdir = $helperdir . $installer::globals::separator . "unpack";
781 installer::systemactions::create_directory($unpackdir);
783 for ( my $i = 0; $i <= $#{$cabfiles}; $i++ )
785 my $cabfile = "";
786 if ( $^O =~ /cygwin/i )
788 $cabfile = $installdir . ${$cabfiles}[$i];
790 else
792 $cabfile = $installdir . $installer::globals::separator . ${$cabfiles}[$i];
794 unpack_cabinet_file($cabfile, $unpackdir);
797 # Reading tables
798 my $filename = $helperdir . $installer::globals::separator . "Directory.idt";
799 my $filecontent = installer::files::read_file($filename);
800 my $dirhash = analyze_directory_file($filecontent);
802 $filename = $helperdir . $installer::globals::separator . "Component.idt";
803 my $componentfilecontent = installer::files::read_file($filename);
804 my $componenthash = analyze_component_file($componentfilecontent);
806 $filename = $helperdir . $installer::globals::separator . "File.idt";
807 $filecontent = installer::files::read_file($filename);
808 my ( $filehash, $fileorder, $maxsequence ) = analyze_file_file($filecontent);
810 # Creating the directory structure
811 my $fullpathhash = create_directory_structure($dirhash, $targetdir);
813 # Copying files
814 copy_files_into_directory_structure($fileorder, $filehash, $componenthash, $fullpathhash, $maxsequence, $unpackdir, $installdir, $dirhash);
816 my $msidatabase = $targetdir . $installer::globals::separator . $databasefilename;
817 installer::systemactions::copy_one_file($databasepath, $msidatabase);
819 # Editing registry table because of wrong Property value
820 # my $registryfilename = $helperdir . $installer::globals::separator . "Registry.idt";
821 # my $componentfilename = $helperdir . $installer::globals::separator . "Component.idt";
822 # my $componentkeypathhash = analyze_keypath_component_file($componentfilecontent);
824 # my $registryfilecontent = installer::files::read_file($registryfilename);
825 # my $registryhash = analyze_registry_file($registryfilecontent);
827 # $registryfilecontent = remove_properties_from_registry_table($registryhash, $componentkeypathhash, $registryfilecontent);
829 # installer::files::save_file($registryfilename, $registryfilecontent);
830 # $tablelist = "Registry";
831 # include_tables_into_pcpfile($msidatabase, $helperdir, $tablelist);
833 # Saving info in Summary Information Stream of msi database (required for following patches)
834 write_sis_info($msidatabase);
836 return $msidatabase;