1#************************************************************************* 2# 3# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4# 5# Copyright 2000, 2010 Oracle and/or its affiliates. 6# 7# OpenOffice.org - a multi-platform office productivity suite 8# 9# This file is part of OpenOffice.org. 10# 11# OpenOffice.org is free software: you can redistribute it and/or modify 12# it under the terms of the GNU Lesser General Public License version 3 13# only, as published by the Free Software Foundation. 14# 15# OpenOffice.org is distributed in the hope that it will be useful, 16# but WITHOUT ANY WARRANTY; without even the implied warranty of 17# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18# GNU Lesser General Public License version 3 for more details 19# (a copy is included in the LICENSE file that accompanied this code). 20# 21# You should have received a copy of the GNU Lesser General Public License 22# version 3 along with OpenOffice.org. If not, see 23# <http://www.openoffice.org/license.html> 24# for a copy of the LGPLv3 License. 25# 26#************************************************************************* 27 28package installer::windows::file; 29 30use Digest::MD5; 31use installer::existence; 32use installer::exiter; 33use installer::files; 34use installer::globals; 35use installer::logger; 36use installer::pathanalyzer; 37use installer::worker; 38use installer::windows::font; 39use installer::windows::idtglobal; 40use installer::windows::language; 41 42########################################################################## 43# Assigning one cabinet file to each file. This is requrired, 44# if cabinet files shall be equivalent to packages. 45########################################################################## 46 47sub assign_cab_to_files 48{ 49 my ( $filesref ) = @_; 50 51 my $infoline = ""; 52 53 for ( my $i = 0; $i <= $#{$filesref}; $i++ ) 54 { 55 if ( ! exists(${$filesref}[$i]->{'modules'}) ) { installer::exiter::exit_program("ERROR: No module assignment found for ${$filesref}[$i]->{'gid'} !", "assign_cab_to_files"); } 56 my $module = ${$filesref}[$i]->{'modules'}; 57 # If modules contains a list of modules, only taking the first one. 58 if ( $module =~ /^\s*(.*?)\,/ ) { $module = $1; } 59 60 if ( ! exists($installer::globals::allcabinetassigns{$module}) ) { installer::exiter::exit_program("ERROR: No cabinet file assigned to module \"$module\" (${$filesref}[$i]->{'gid'}) !", "assign_cab_to_files"); } 61 ${$filesref}[$i]->{'assignedcabinetfile'} = $installer::globals::allcabinetassigns{$module}; 62 63 # Counting the files in each cabinet file 64 if ( ! exists($installer::globals::cabfilecounter{${$filesref}[$i]->{'assignedcabinetfile'}}) ) 65 { 66 $installer::globals::cabfilecounter{${$filesref}[$i]->{'assignedcabinetfile'}} = 1; 67 } 68 else 69 { 70 $installer::globals::cabfilecounter{${$filesref}[$i]->{'assignedcabinetfile'}}++; 71 } 72 } 73 74 # logging the number of files in each cabinet file 75 76 $infoline = "\nCabinet file content:\n"; 77 push(@installer::globals::logfileinfo, $infoline); 78 my $cabfile; 79 foreach $cabfile ( sort keys %installer::globals::cabfilecounter ) 80 { 81 $infoline = "$cabfile : $installer::globals::cabfilecounter{$cabfile} files\n"; 82 push(@installer::globals::logfileinfo, $infoline); 83 } 84 85 # assigning startsequencenumbers for each cab file 86 87 my $offset = 1; 88 foreach $cabfile ( sort keys %installer::globals::cabfilecounter ) 89 { 90 my $filecount = $installer::globals::cabfilecounter{$cabfile}; 91 $installer::globals::cabfilecounter{$cabfile} = $offset; 92 $offset = $offset + $filecount; 93 94 $installer::globals::lastsequence{$cabfile} = $offset - 1; 95 } 96 97 # logging the start sequence numbers 98 99 $infoline = "\nCabinet file start sequences:\n"; 100 push(@installer::globals::logfileinfo, $infoline); 101 foreach $cabfile ( sort keys %installer::globals::cabfilecounter ) 102 { 103 $infoline = "$cabfile : $installer::globals::cabfilecounter{$cabfile}\n"; 104 push(@installer::globals::logfileinfo, $infoline); 105 } 106 107 # logging the last sequence numbers 108 109 $infoline = "\nCabinet file last sequences:\n"; 110 push(@installer::globals::logfileinfo, $infoline); 111 foreach $cabfile ( sort keys %installer::globals::lastsequence ) 112 { 113 $infoline = "$cabfile : $installer::globals::lastsequence{$cabfile}\n"; 114 push(@installer::globals::logfileinfo, $infoline); 115 } 116} 117 118########################################################################## 119# Assigning sequencenumbers to files. This is requrired, 120# if cabinet files shall be equivalent to packages. 121########################################################################## 122 123sub assign_sequencenumbers_to_files 124{ 125 my ( $filesref ) = @_; 126 127 my %directaccess = (); 128 my %allassigns = (); 129 130 for ( my $i = 0; $i <= $#{$filesref}; $i++ ) 131 { 132 my $onefile = ${$filesref}[$i]; 133 134 # Keeping order in cabinet files 135 # -> collecting all files in one cabinet file 136 # -> sorting files and assigning numbers 137 138 # Saving counter $i for direct access into files array 139 # "destination" of the file is a unique identifier ('Name' is not unique!) 140 if ( exists($directaccess{$onefile->{'destination'}}) ) { installer::exiter::exit_program("ERROR: 'destination' at file not unique: $onefile->{'destination'}", "assign_sequencenumbers_to_files"); } 141 $directaccess{$onefile->{'destination'}} = $i; 142 143 my $cabfilename = $onefile->{'assignedcabinetfile'}; 144 # collecting files in cabinet files 145 if ( ! exists($allassigns{$cabfilename}) ) 146 { 147 my %onecabfile = (); 148 $onecabfile{$onefile->{'destination'}} = 1; 149 $allassigns{$cabfilename} = \%onecabfile; 150 } 151 else 152 { 153 $allassigns{$cabfilename}->{$onefile->{'destination'}} = 1; 154 } 155 } 156 157 # Sorting each hash and assigning numbers 158 # The destination of the file determines the sort order, not the filename! 159 my $cabfile; 160 foreach $cabfile ( sort keys %allassigns ) 161 { 162 my $counter = $installer::globals::cabfilecounter{$cabfile}; 163 my $dest; 164 foreach $dest ( sort keys %{$allassigns{$cabfile}} ) # <- sorting the destination! 165 { 166 my $directaccessnumber = $directaccess{$dest}; 167 ${$filesref}[$directaccessnumber]->{'assignedsequencenumber'} = $counter; 168 $counter++; 169 } 170 } 171} 172 173######################################################### 174# Create a shorter version of a long component name, 175# because maximum length in msi database is 72. 176# Attention: In multi msi installation sets, the short 177# names have to be unique over all packages, because 178# this string is used to create the globally unique id 179# -> no resetting of 180# %installer::globals::allshortcomponents 181# after a package was created. 182######################################################### 183 184sub generate_new_short_componentname 185{ 186 my ($componentname) = @_; 187 188 my $shortcomponentname = ""; 189 my $counter = 1; 190 191 my $startversion = substr($componentname, 0, 60); # taking only the first 60 characters 192 $startversion = $startversion . "_"; 193 194 $shortcomponentname = $startversion . $counter; 195 196 while ( exists($installer::globals::allshortcomponents{$shortcomponentname}) ) 197 { 198 $counter++; 199 $shortcomponentname = $startversion . $counter; 200 } 201 202 $installer::globals::allshortcomponents{$shortcomponentname} = 1; 203 204 return $shortcomponentname; 205} 206 207############################################### 208# Generating the component name from a file 209############################################### 210 211sub get_file_component_name 212{ 213 my ($fileref, $filesref) = @_; 214 215 my $componentname = ""; 216 217 # Special handling for files with ASSIGNCOMPOMENT 218 219 my $styles = ""; 220 if ( $fileref->{'Styles'} ) { $styles = $fileref->{'Styles'}; } 221 if ( $styles =~ /\bASSIGNCOMPOMENT\b/ ) 222 { 223 $componentname = get_component_from_assigned_file($fileref->{'AssignComponent'}, $filesref); 224 } 225 else 226 { 227 # In this function exists the rule to create components from files 228 # Rule: 229 # Two files get the same componentid, if: 230 # both have the same destination directory. 231 # both have the same "gid" -> both were packed in the same zip file 232 # All other files are included into different components! 233 234 # my $componentname = $fileref->{'gid'} . "_" . $fileref->{'Dir'}; 235 236 # $fileref->{'Dir'} is not sufficient! All files in a zip file have the same $fileref->{'Dir'}, 237 # but can be in different subdirectories. 238 # Solution: destination=share\Scripts\beanshell\Capitalise\capitalise.bsh 239 # in which the filename (capitalise.bsh) has to be removed and all backslashes (slashes) are 240 # converted into underline. 241 242 my $destination = $fileref->{'destination'}; 243 installer::pathanalyzer::get_path_from_fullqualifiedname(\$destination); 244 $destination =~ s/\s//g; 245 $destination =~ s/\\/\_/g; 246 $destination =~ s/\//\_/g; 247 $destination =~ s/\_\s*$//g; # removing ending underline 248 249 $componentname = $fileref->{'gid'} . "__" . $destination; 250 251 # Files with different languages, need to be packed into different components. 252 # Then the installation of the language specific component is determined by a language condition. 253 254 if ( $fileref->{'ismultilingual'} ) 255 { 256 my $officelanguage = $fileref->{'specificlanguage'}; 257 $componentname = $componentname . "_" . $officelanguage; 258 } 259 260 $componentname = lc($componentname); # componentnames always lowercase 261 262 $componentname =~ s/\-/\_/g; # converting "-" to "_" 263 $componentname =~ s/\./\_/g; # converting "-" to "_" 264 265 # Attention: Maximum length for the componentname is 72 266 # %installer::globals::allcomponents_in_this_database : resetted for each database 267 # %installer::globals::allcomponents : not resetted for each database 268 # Component strings must be unique for the complete product, because they are used for 269 # the creation of the globally unique identifier. 270 271 my $fullname = $componentname; # This can be longer than 72 272 273 if (( exists($installer::globals::allcomponents{$fullname}) ) && ( ! exists($installer::globals::allcomponents_in_this_database{$fullname}) )) 274 { 275 # This is not allowed: One component cannot be installed with different packages. 276 installer::exiter::exit_program("ERROR: Component \"$fullname\" is already included into another package. This is not allowed.", "get_file_component_name"); 277 } 278 279 if ( exists($installer::globals::allcomponents{$fullname}) ) 280 { 281 $componentname = $installer::globals::allcomponents{$fullname}; 282 } 283 else 284 { 285 if ( length($componentname) > 60 ) 286 { 287 # Using md5sum needs much time 288 # chomp(my $shorter = `echo $componentname | md5sum | sed -e "s/ .*//g"`); 289 # $componentname = "comp_$shorter"; 290 $componentname = generate_new_short_componentname($componentname); # This has to be unique for the complete product, not only one package 291 } 292 293 $installer::globals::allcomponents{$fullname} = $componentname; 294 $installer::globals::allcomponents_in_this_database{$fullname} = 1; 295 } 296 297 # $componentname =~ s/gid_file_/g_f_/g; 298 # $componentname =~ s/_extra_/_e_/g; 299 # $componentname =~ s/_config_/_c_/g; 300 # $componentname =~ s/_org_openoffice_/_o_o_/g; 301 # $componentname =~ s/_program_/_p_/g; 302 # $componentname =~ s/_typedetection_/_td_/g; 303 # $componentname =~ s/_linguistic_/_l_/g; 304 # $componentname =~ s/_module_/_m_/g; 305 # $componentname =~ s/_optional_/_opt_/g; 306 # $componentname =~ s/_packages/_pack/g; 307 # $componentname =~ s/_menubar/_mb/g; 308 # $componentname =~ s/_common_/_cm_/g; 309 # $componentname =~ s/_export_/_exp_/g; 310 # $componentname =~ s/_table_/_tb_/g; 311 # $componentname =~ s/_sofficecfg_/_sc_/g; 312 # $componentname =~ s/_soffice_cfg_/_sc_/g; 313 # $componentname =~ s/_startmodulecommands_/_smc_/g; 314 # $componentname =~ s/_drawimpresscommands_/_dic_/g; 315 # $componentname =~ s/_basiccommands_/_bac_/g; 316 # $componentname =~ s/_basicidecommands_/_baic_/g; 317 # $componentname =~ s/_genericcommands_/_genc_/g; 318 # $componentname =~ s/_bibliographycommands_/_bibc_/g; 319 # $componentname =~ s/_gentiumbookbasicbolditalic_/_gbbbi_/g; 320 # $componentname =~ s/_share_/_s_/g; 321 # $componentname =~ s/_extension_/_ext_/g; 322 # $componentname =~ s/_extensions_/_exs_/g; 323 # $componentname =~ s/_modules_/_ms_/g; 324 # $componentname =~ s/_uiconfig_zip_/_ucz_/g; 325 # $componentname =~ s/_productivity_/_pr_/g; 326 # $componentname =~ s/_wizard_/_wz_/g; 327 # $componentname =~ s/_import_/_im_/g; 328 # $componentname =~ s/_javascript_/_js_/g; 329 # $componentname =~ s/_template_/_tpl_/g; 330 # $componentname =~ s/_tplwizletter_/_twl_/g; 331 # $componentname =~ s/_beanshell_/_bs_/g; 332 # $componentname =~ s/_presentation_/_bs_/g; 333 # $componentname =~ s/_columns_/_cls_/g; 334 # $componentname =~ s/_python_/_py_/g; 335 336 # $componentname =~ s/_tools/_ts/g; 337 # $componentname =~ s/_transitions/_trs/g; 338 # $componentname =~ s/_scriptbinding/_scrb/g; 339 # $componentname =~ s/_spreadsheet/_ssh/g; 340 # $componentname =~ s/_publisher/_pub/g; 341 # $componentname =~ s/_presenter/_pre/g; 342 # $componentname =~ s/_registry/_reg/g; 343 344 # $componentname =~ s/screen/sc/g; 345 # $componentname =~ s/wordml/wm/g; 346 # $componentname =~ s/openoffice/oo/g; 347 } 348 349 return $componentname; 350} 351 352#################################################################### 353# Returning the component name for a defined file gid. 354# This is necessary for files with flag ASSIGNCOMPOMENT 355#################################################################### 356 357sub get_component_from_assigned_file 358{ 359 my ($gid, $filesref) = @_; 360 361 my $onefile = installer::existence::get_specified_file($filesref, $gid); 362 my $componentname = ""; 363 if ( $onefile->{'componentname'} ) { $componentname = $onefile->{'componentname'}; } 364 else { installer::exiter::exit_program("ERROR: No component defined for file: $gid", "get_component_from_assigned_file"); } 365 366 return $componentname; 367} 368 369#################################################################### 370# Generating the special filename for the database file File.idt 371# Sample: CONTEXTS, CONTEXTS1 372# This name has to be unique. 373# In most cases this is simply the filename. 374#################################################################### 375 376sub generate_unique_filename_for_filetable 377{ 378 my ($fileref, $component, $uniquefilenamehashref) = @_; 379 380 # This new filename has to be saved into $fileref, because this is needed to find the source. 381 # The filename sbasic.idx/OFFSETS is changed to OFFSETS, but OFFSETS is not unique. 382 # In this procedure names like OFFSETS5 are produced. And exactly this string has to be added to 383 # the array of all files. 384 385 my $uniquefilename = ""; 386 my $counter = 0; 387 388 if ( $fileref->{'Name'} ) { $uniquefilename = $fileref->{'Name'}; } 389 390 installer::pathanalyzer::make_absolute_filename_to_relative_filename(\$uniquefilename); # making /registry/schema/org/openoffice/VCL.xcs to VCL.xcs 391 392 # Reading unique filename with help of "Component_" in File table from old database 393 if (( $installer::globals::updatedatabase ) && ( exists($uniquefilenamehashref->{"$component/$uniquefilename"}) )) 394 { 395 $uniquefilename = $uniquefilenamehashref->{"$component/$uniquefilename"}; # syntax of $value: ($uniquename;$shortname) 396 if ( $uniquefilename =~ /^\s*(.*?)\;\s*(.*?)\s*$/ ) { $uniquefilename = $1; } 397 $lcuniquefilename = lc($uniquefilename); 398 $installer::globals::alluniquefilenames{$uniquefilename} = 1; 399 $installer::globals::alllcuniquefilenames{$lcuniquefilename} = 1; 400 return $uniquefilename; 401 } 402 elsif (( $installer::globals::prepare_winpatch ) && ( exists($installer::globals::savedmapping{"$component/$uniquefilename"}) )) 403 { 404 # If we have a FTK mapping for this component/file, use it. 405 $installer::globals::savedmapping{"$component/$uniquefilename"} =~ m/^(.*);/; 406 $uniquefilename = $1; 407 $lcuniquefilename = lc($uniquefilename); 408 $installer::globals::alluniquefilenames{$uniquefilename} = 1; 409 $installer::globals::alllcuniquefilenames{$lcuniquefilename} = 1; 410 return $uniquefilename; 411 } 412 413 $uniquefilename =~ s/\-/\_/g; # no "-" allowed 414 $uniquefilename =~ s/\@/\_/g; # no "@" allowed 415 $uniquefilename =~ s/\$/\_/g; # no "$" allowed 416 $uniquefilename =~ s/^\s*\./\_/g; # no "." at the beginning allowed allowed 417 $uniquefilename =~ s/^\s*\d/\_d/g; # no number at the beginning allowed allowed (even file "0.gif", replacing to "_d.gif") 418 $uniquefilename =~ s/org_openoffice_/ooo_/g; # shorten the unique file name 419 420 my $lcuniquefilename = lc($uniquefilename); # only lowercase names 421 422 my $newname = 0; 423 424 if ( ! exists($installer::globals::alllcuniquefilenames{$lcuniquefilename}) && 425 ! exists($installer::globals::savedrevmapping{$lcuniquefilename}) ) 426 { 427 $installer::globals::alluniquefilenames{$uniquefilename} = 1; 428 $installer::globals::alllcuniquefilenames{$lcuniquefilename} = 1; 429 $newname = 1; 430 } 431 432 if ( ! $newname ) 433 { 434 # adding a number until the name is really unique: OFFSETS, OFFSETS1, OFFSETS2, ... 435 # But attention: Making "abc.xcu" to "abc1.xcu" 436 437 my $uniquefilenamebase = $uniquefilename; 438 439 do 440 { 441 $counter++; 442 443 if ( $uniquefilenamebase =~ /\./ ) 444 { 445 $uniquefilename = $uniquefilenamebase; 446 $uniquefilename =~ s/\./$counter\./; 447 } 448 else 449 { 450 $uniquefilename = $uniquefilenamebase . $counter; 451 } 452 453 $newname = 0; 454 $lcuniquefilename = lc($uniquefilename); # only lowercase names 455 456 if ( ! exists($installer::globals::alllcuniquefilenames{$lcuniquefilename}) && 457 ! exists($installer::globals::savedrevmapping{$lcuniquefilename}) ) 458 { 459 $installer::globals::alluniquefilenames{$uniquefilename} = 1; 460 $installer::globals::alllcuniquefilenames{$lcuniquefilename} = 1; 461 $newname = 1; 462 } 463 } 464 until ( $newname ) 465 } 466 467 return $uniquefilename; 468} 469 470#################################################################### 471# Generating the special file column for the database file File.idt 472# Sample: NAMETR~1.TAB|.nametranslation.table 473# The first part has to be 8.3 conform. 474#################################################################### 475 476sub generate_filename_for_filetable 477{ 478 my ($fileref, $shortnamesref, $uniquefilenamehashref) = @_; 479 480 my $returnstring = ""; 481 482 my $filename = $fileref->{'Name'}; 483 484 installer::pathanalyzer::make_absolute_filename_to_relative_filename(\$filename); # making /registry/schema/org/openoffice/VCL.xcs to VCL.xcs 485 486 my $shortstring; 487 488 # Reading short string with help of "FileName" in File table from old database 489 if (( $installer::globals::updatedatabase ) && ( exists($uniquefilenamehashref->{"$fileref->{'componentname'}/$filename"}) )) 490 { 491 my $value = $uniquefilenamehashref->{"$fileref->{'componentname'}/$filename"}; # syntax of $value: ($uniquename;$shortname) 492 if ( $value =~ /^\s*(.*?)\;\s*(.*?)\s*$/ ) { $shortstring = $2; } # already collected in function "collect_shortnames_from_old_database" 493 else { $shortstring = $filename; } 494 } 495 elsif (( $installer::globals::prepare_winpatch ) && ( exists($installer::globals::savedmapping{"$fileref->{'componentname'}/$filename"}) )) 496 { 497 $installer::globals::savedmapping{"$fileref->{'componentname'}/$filename"} =~ m/.*;(.*)/; 498 if ($1 ne '') 499 { 500 $shortstring = $1; 501 } 502 else 503 { 504 $shortstring = installer::windows::idtglobal::make_eight_three_conform_with_hash($filename, "file", $shortnamesref); 505 } 506 } 507 else 508 { 509 $shortstring = installer::windows::idtglobal::make_eight_three_conform_with_hash($filename, "file", $shortnamesref); 510 } 511 512 if ( $shortstring eq $filename ) { $returnstring = $filename; } # nothing changed 513 else {$returnstring = $shortstring . "\|" . $filename; } 514 515 return $returnstring; 516} 517 518######################################### 519# Returning the filesize of a file 520######################################### 521 522sub get_filesize 523{ 524 my ($fileref) = @_; 525 526 my $file = $fileref->{'sourcepath'}; 527 528 my $filesize; 529 530 if ( -f $file ) # test of existence. For instance services.rdb does not always exist 531 { 532 $filesize = ( -s $file ); # file size can be "0" 533 } 534 else 535 { 536 $filesize = -1; 537 } 538 539 return $filesize; 540} 541 542############################################# 543# Returning the file version, if required 544# Sample: "8.0.1.8976"; 545############################################# 546 547sub get_fileversion 548{ 549 my ($onefile, $allvariables, $styles) = @_; 550 551 my $fileversion = ""; 552 553 if ( $allvariables->{'USE_FILEVERSION'} ) 554 { 555 if ( ! $allvariables->{'LIBRARYVERSION'} ) { installer::exiter::exit_program("ERROR: USE_FILEVERSION is set, but not LIBRARYVERSION", "get_fileversion"); } 556 my $libraryversion = $allvariables->{'LIBRARYVERSION'}; 557 if ( $libraryversion =~ /^\s*(\d+)\.(\d+)\.(\d+)\s*$/ ) 558 { 559 my $major = $1; 560 my $minor = $2; 561 my $micro = $3; 562 my $concat = 100 * $minor + $micro; 563 $libraryversion = $major . "\." . $concat; 564 } 565 my $vendornumber = 0; 566 if ( $allvariables->{'VENDORPATCHVERSION'} ) { $vendornumber = $allvariables->{'VENDORPATCHVERSION'}; } 567 $fileversion = $libraryversion . "\." . $installer::globals::buildid . "\." . $vendornumber; 568 if ( $onefile->{'FileVersion'} ) { $fileversion = $onefile->{'FileVersion'}; } # overriding FileVersion in scp 569 570 # if ( $styles =~ /\bFONT\b/ ) 571 # { 572 # my $newfileversion = installer::windows::font::get_font_version($onefile->{'sourcepath'}); 573 # if ( $newfileversion != 0 ) { $fileversion = $newfileversion; } 574 # } 575 } 576 577 if ( $installer::globals::prepare_winpatch ) { $fileversion = ""; } # Windows patches do not allow this version # -> who says so? 578 579 return $fileversion; 580} 581 582############################################# 583# Returning the sequence for a file 584############################################# 585 586sub get_sequence_for_file 587{ 588 my ($number, $onefile, $fileentry, $allupdatesequenceshashref, $allupdatecomponentshashref, $allupdatefileorderhashref, $allfilecomponents) = @_; 589 590 my $sequence = ""; 591 my $infoline = ""; 592 my $pffcomponentname = $onefile->{'componentname'} . "_pff"; 593 594 if ( $installer::globals::updatedatabase ) 595 { 596 if (( exists($allupdatesequenceshashref->{$onefile->{'uniquename'}}) ) && 597 (( $onefile->{'componentname'} eq $allupdatecomponentshashref->{$onefile->{'uniquename'}} ) || 598 ( $pffcomponentname eq $allupdatecomponentshashref->{$onefile->{'uniquename'}} ))) 599 { 600 # The second condition is necessary to find shifted files, that have same "uniquename", but are now 601 # located in another directory. This can be seen at the component name. 602 $sequence = $allupdatesequenceshashref->{$onefile->{'uniquename'}}; 603 $onefile->{'assignedsequencenumber'} = $sequence; 604 # Collecting all used sequences, to guarantee, that no number is unused 605 $installer::globals::allusedupdatesequences{$sequence} = 1; 606 # Special help for files, that already have a "pff" component name (for example after ServicePack 1) 607 if ( $pffcomponentname eq $allupdatecomponentshashref->{$onefile->{'uniquename'}} ) 608 { 609 $infoline = "Warning: Special handling for component \"$pffcomponentname\". This file was added after the final, but before this ServicePack.\n"; 610 push(@installer::globals::logfileinfo, $infoline); 611 $onefile->{'componentname'} = $pffcomponentname; # pff for "post final file" 612 $fileentry->{'Component_'} = $onefile->{'componentname'}; 613 if ( ! exists($allfilecomponents->{$fileentry->{'Component_'}}) ) { $allfilecomponents->{$fileentry->{'Component_'}} = 1; } 614 } 615 } 616 else 617 { 618 $installer::globals::updatesequencecounter++; 619 $sequence = $installer::globals::updatesequencecounter; 620 $onefile->{'assignedsequencenumber'} = $sequence; 621 # $onefile->{'assignedcabinetfile'} = $installer::globals::pffcabfilename; # assigning to cabinet file for "post final files" 622 # Collecting all new files 623 $installer::globals::newupdatefiles{$sequence} = $onefile; 624 # Saving in sequence hash 625 $allupdatefileorderhashref->{$sequence} = $onefile->{'uniquename'}; 626 627 # If the new file is part of an existing component, this must be changed now. All files 628 # of one component have to be included in one cabinet file. But because the order must 629 # not change, all new files have to be added to new components. 630 # $onefile->{'componentname'} = $file{'Component_'}; 631 632 $onefile->{'componentname'} = $onefile->{'componentname'} . "_pff"; # pff for "post final file" 633 $fileentry->{'Component_'} = $onefile->{'componentname'}; 634 if ( ! exists($allfilecomponents->{$fileentry->{'Component_'}}) ) { $allfilecomponents->{$fileentry->{'Component_'}} = 1; } 635 $onefile->{'PostFinalFile'} = 1; 636 # $installer::globals::pfffileexists = 1; 637 # The sequence for this file has changed. It has to be inserted at the end of the files collector. 638 $installer::globals::insert_file_at_end = 1; 639 $installer::globals::newfilescollector{$sequence} = $onefile; # Adding new files to the end of the filescollector 640 $installer::globals::newfilesexist = 1; 641 } 642 } 643 elsif (( $onefile->{'assignedsequencenumber'} ) && ( $installer::globals::use_packages_for_cabs )) 644 { 645 $sequence = $onefile->{'assignedsequencenumber'}; 646 } 647 else 648 { 649 $sequence = $number; 650 # my $sequence = $number + 1; 651 652 # Idea: Each component is packed into a cab file. 653 # This requires that all files in one cab file have sequences directly follwing each other, 654 # for instance from 1456 to 1466. Then in the media table the LastSequence for this cab file 655 # is 1466. 656 # Because all files belonging to one component are directly behind each other in the file 657 # collector, it is possible to use simply an increasing number as sequence value. 658 # If files belonging to one component are not directly behind each other in the files collector 659 # this mechanism will no longer work. 660 } 661 662 return $sequence; 663} 664 665############################################# 666# Returning the Windows language of a file 667############################################# 668 669sub get_language_for_file 670{ 671 my ($fileref) = @_; 672 673 my $language = ""; 674 675 if ( $fileref->{'specificlanguage'} ) { $language = $fileref->{'specificlanguage'}; } 676 677 if ( $language eq "" ) 678 { 679 $language = 0; # language independent 680 # If this is not a font, the return value should be "0" (Check ICE 60) 681 my $styles = ""; 682 if ( $fileref->{'Styles'} ) { $styles = $fileref->{'Styles'}; } 683 if ( $styles =~ /\bFONT\b/ ) { $language = ""; } 684 } 685 else 686 { 687 $language = installer::windows::language::get_windows_language($language); 688 } 689 690 return $language; 691} 692 693#################################################################### 694# Creating a new KeyPath for components in TemplatesFolder. 695#################################################################### 696 697sub generate_registry_keypath 698{ 699 my ($onefile) = @_; 700 701 my $keypath = $onefile->{'Name'}; 702 $keypath =~ s/\.//g; 703 $keypath = lc($keypath); 704 $keypath = "userreg_" . $keypath; 705 706 return $keypath; 707} 708 709#################################################################### 710# Check, if in an update process files are missing. No removal 711# of files allowed for Windows Patch creation. 712# Also logging all new files, that have to be included in extra 713# components and cab files. 714#################################################################### 715 716sub check_file_sequences 717{ 718 my ($allupdatefileorderhashref, $allupdatecomponentorderhashref) = @_; 719 720 # All used sequences stored in %installer::globals::allusedupdatesequences 721 # Maximum sequence number of old database stored in $installer::globals::updatelastsequence 722 # All new files stored in %installer::globals::newupdatefiles 723 724 my $infoline = ""; 725 726 my @missing_sequences = (); 727 my @really_missing_sequences = (); 728 729 for ( my $i = 1; $i <= $installer::globals::updatelastsequence; $i++ ) 730 { 731 if ( ! exists($installer::globals::allusedupdatesequences{$i}) ) { push(@missing_sequences, $i); } 732 } 733 734 if ( $#missing_sequences > -1 ) 735 { 736 # Missing sequences can also be caused by files included in merge modules. This files are added later into the file table. 737 # Therefore now it is time to check the content of the merge modules. 738 739 for ( my $j = 0; $j <= $#missing_sequences; $j++ ) 740 { 741 my $filename = $allupdatefileorderhashref->{$missing_sequences[$j]}; 742 743 # Is this a file from a merge module? Then this is no error. 744 if ( ! exists($installer::globals::mergemodulefiles{$filename}) ) 745 { 746 push(@really_missing_sequences, $missing_sequences[$j]); 747 } 748 } 749 } 750 751 if ( $#really_missing_sequences > -1 ) 752 { 753 my $errorstring = ""; 754 for ( my $j = 0; $j <= $#really_missing_sequences; $j++ ) 755 { 756 my $filename = $allupdatefileorderhashref->{$really_missing_sequences[$j]}; 757 my $comp = $allupdatecomponentorderhashref->{$really_missing_sequences[$j]}; 758 $errorstring = "$errorstring$filename (Sequence: $really_missing_sequences[$j], Component: \"$comp\")\n"; 759 } 760 761 $infoline = "ERROR: Files are removed compared with update database.\nThe following files are missing:\n$errorstring"; 762 push(@installer::globals::logfileinfo, $infoline); 763 installer::exiter::exit_program($infoline, "check_file_sequences"); 764 } 765 766 # Searching for new files 767 768 my $counter = 0; 769 770 foreach my $key ( keys %installer::globals::newupdatefiles ) 771 { 772 my $onefile = $installer::globals::newupdatefiles{$key}; 773 $counter++; 774 if ( $counter == 1 ) 775 { 776 $infoline = "\nNew files compared to the update database:\n"; 777 push(@installer::globals::logfileinfo, $infoline); 778 } 779 780 $infoline = "$onefile->{'Name'} ($onefile->{'gid'}) Sequence: $onefile->{'assignedsequencenumber'}\n"; 781 push(@installer::globals::logfileinfo, $infoline); 782 } 783 784 if ( $counter == 0 ) 785 { 786 $infoline = "Info: No new file compared with update database!\n"; 787 push(@installer::globals::logfileinfo, $infoline); 788 } 789 790} 791 792################################################################### 793# Collecting further conditions for the component table. 794# This is used by multilayer products, to enable installation 795# of separate layers. 796################################################################### 797 798sub get_tree_condition_for_component 799{ 800 my ($onefile, $componentname) = @_; 801 802 if ( $onefile->{'destination'} ) 803 { 804 my $dest = $onefile->{'destination'}; 805 806 # Comparing the destination path with 807 # $installer::globals::hostnametreestyles{$hostname} = $treestyle; 808 # (-> hostname is the key, the style the value!) 809 810 foreach my $hostname ( keys %installer::globals::hostnametreestyles ) 811 { 812 if (( $dest eq $hostname ) || ( $dest =~ /^\s*\Q$hostname\E\\/ )) 813 { 814 # the value is the style 815 my $style = $installer::globals::hostnametreestyles{$hostname}; 816 # the condition is saved in %installer::globals::treestyles 817 my $condition = $installer::globals::treestyles{$style}; 818 # Saving condition to be added in table Property 819 $installer::globals::usedtreeconditions{$condition} = 1; 820 $condition = $condition . "=1"; 821 # saving this condition 822 $installer::globals::treeconditions{$componentname} = $condition; 823 824 # saving also at the file, for usage in fileinfo 825 $onefile->{'layer'} = $installer::globals::treelayername{$style}; 826 } 827 } 828 } 829} 830 831############################################ 832# Collecting all short names, that are 833# already used by the old database 834############################################ 835 836sub collect_shortnames_from_old_database 837{ 838 my ($uniquefilenamehashref, $shortnameshashref) = @_; 839 840 foreach my $key ( keys %{$uniquefilenamehashref} ) 841 { 842 my $value = $uniquefilenamehashref->{$key}; # syntax of $value: ($uniquename;$shortname) 843 844 if ( $value =~ /^\s*(.*?)\;\s*(.*?)\s*$/ ) 845 { 846 my $shortstring = $2; 847 $shortnameshashref->{$shortstring} = 1; # adding the shortname to the array of all shortnames 848 } 849 } 850} 851 852############################################ 853# Creating the file File.idt dynamically 854############################################ 855 856sub create_files_table 857{ 858 my ($filesref, $allfilecomponentsref, $basedir, $allvariables, $uniquefilenamehashref, $allupdatesequenceshashref, $allupdatecomponentshashref, $allupdatefileorderhashref) = @_; 859 860 installer::logger::include_timestamp_into_logfile("Performance Info: File Table start"); 861 862 # Structure of the files table: 863 # File Component_ FileName FileSize Version Language Attributes Sequence 864 # In this function, all components are created. 865 # 866 # $allfilecomponentsref is empty at the beginning 867 868 my $infoline; 869 870 my @allfiles = (); 871 my @filetable = (); 872 my @filehashtable = (); 873 my %allfilecomponents = (); 874 my $counter = 0; 875 876 if ( $^O =~ /cygwin/i ) { installer::worker::generate_cygwin_pathes($filesref); } 877 878 # The filenames must be collected because of uniqueness 879 # 01-44-~1.DAT, 01-44-~2.DAT, ... 880 # my @shortnames = (); 881 my %shortnames = (); 882 883 if ( $installer::globals::updatedatabase ) { collect_shortnames_from_old_database($uniquefilenamehashref, \%shortnames); } 884 885 installer::windows::idtglobal::write_idt_header(\@filetable, "file"); 886 installer::windows::idtglobal::write_idt_header(\@filehashtable, "filehash"); 887 888 for ( my $i = 0; $i <= $#{$filesref}; $i++ ) 889 { 890 my %file = (); 891 892 my $onefile = ${$filesref}[$i]; 893 894 my $styles = ""; 895 if ( $onefile->{'Styles'} ) { $styles = $onefile->{'Styles'}; } 896 if (( $styles =~ /\bJAVAFILE\b/ ) && ( ! ($allvariables->{'JAVAPRODUCT'} ))) { next; } 897 898 $file{'Component_'} = get_file_component_name($onefile, $filesref); 899 $file{'File'} = generate_unique_filename_for_filetable($onefile, $file{'Component_'}, $uniquefilenamehashref); 900 901 $onefile->{'uniquename'} = $file{'File'}; 902 $onefile->{'componentname'} = $file{'Component_'}; 903 904 # Collecting all components 905 # if (!(installer::existence::exists_in_array($file{'Component_'}, $allfilecomponentsref))) { push(@{$allfilecomponentsref}, $file{'Component_'}); } 906 907 if ( ! exists($allfilecomponents{$file{'Component_'}}) ) { $allfilecomponents{$file{'Component_'}} = 1; } 908 909 $file{'FileName'} = generate_filename_for_filetable($onefile, \%shortnames, $uniquefilenamehashref); 910 911 $file{'FileSize'} = get_filesize($onefile); 912 913 $file{'Version'} = get_fileversion($onefile, $allvariables, $styles); 914 915 $file{'Language'} = get_language_for_file($onefile); 916 917 if ( $styles =~ /\bDONT_PACK\b/ ) { $file{'Attributes'} = "8192"; } 918 else { $file{'Attributes'} = "16384"; } 919 920 # $file{'Attributes'} = "16384"; # Sourcefile is packed 921 # $file{'Attributes'} = "8192"; # Sourcefile is unpacked 922 923 $installer::globals::insert_file_at_end = 0; 924 $counter++; 925 $file{'Sequence'} = get_sequence_for_file($counter, $onefile, \%file, $allupdatesequenceshashref, $allupdatecomponentshashref, $allupdatefileorderhashref, \%allfilecomponents); 926 927 $onefile->{'sequencenumber'} = $file{'Sequence'}; 928 929 my $oneline = $file{'File'} . "\t" . $file{'Component_'} . "\t" . $file{'FileName'} . "\t" 930 . $file{'FileSize'} . "\t" . $file{'Version'} . "\t" . $file{'Language'} . "\t" 931 . $file{'Attributes'} . "\t" . $file{'Sequence'} . "\n"; 932 933 push(@filetable, $oneline); 934 935 if ( ! $installer::globals::insert_file_at_end ) { push(@allfiles, $onefile); } 936 937 # Collecting all component conditions 938 if ( $onefile->{'ComponentCondition'} ) 939 { 940 if ( ! exists($installer::globals::componentcondition{$file{'Component_'}})) 941 { 942 $installer::globals::componentcondition{$file{'Component_'}} = $onefile->{'ComponentCondition'}; 943 } 944 } 945 946 # Collecting also all tree conditions for multilayer products 947 get_tree_condition_for_component($onefile, $file{'Component_'}); 948 949 # Collecting all component names, that have flag VERSION_INDEPENDENT_COMP_ID 950 # This should be all components with constant API, for example URE 951 if ( $styles =~ /\bVERSION_INDEPENDENT_COMP_ID\b/ ) 952 { 953 $installer::globals::base_independent_components{$onefile->{'componentname'}} = 1; 954 } 955 956 # Collecting all component ids, that are defined at files in scp project (should not be used anymore) 957 if ( $onefile->{'CompID'} ) 958 { 959 if ( ! exists($installer::globals::componentid{$onefile->{'componentname'}})) 960 { 961 $installer::globals::componentid{$onefile->{'componentname'}} = $onefile->{'CompID'}; 962 } 963 else 964 { 965 if ( $installer::globals::componentid{$onefile->{'componentname'}} ne $onefile->{'CompID'} ) 966 { 967 installer::exiter::exit_program("ERROR: There is already a ComponentID for component \"$onefile->{'componentname'}\" : \"$installer::globals::componentid{$onefile->{'componentname'}}\" . File \"$onefile->{'gid'}\" uses \"$onefile->{'CompID'}\" !", "create_files_table"); 968 } 969 } 970 971 # Also checking vice versa. Is this ComponentID already used? If yes, is the componentname the same? 972 973 if ( ! exists($installer::globals::comparecomponentname{$onefile->{'CompID'}})) 974 { 975 $installer::globals::comparecomponentname{$onefile->{'CompID'}} = $onefile->{'componentname'}; 976 } 977 else 978 { 979 if ( $installer::globals::comparecomponentname{$onefile->{'CompID'}} ne $onefile->{'componentname'} ) 980 { 981 installer::exiter::exit_program("ERROR: There is already a component for ComponentID \"$onefile->{'CompID'}\" : \"$installer::globals::comparecomponentname{$onefile->{'CompID'}}\" . File \"$onefile->{'gid'}\" has same component id but is included in component \"$onefile->{'componentname'}\" !", "create_files_table"); 982 } 983 } 984 } 985 986 # Collecting all language specific conditions 987 # if ( $onefile->{'haslanguagemodule'} ) 988 if ( $onefile->{'ismultilingual'} ) 989 { 990 if ( $onefile->{'ComponentCondition'} ) { installer::exiter::exit_program("ERROR: Cannot set language condition. There is already another component condition for file $onefile->{'gid'}: \"$onefile->{'ComponentCondition'}\" !", "create_files_table"); } 991 992 if ( $onefile->{'specificlanguage'} eq "" ) { installer::exiter::exit_program("ERROR: There is no specific language for file at language module: $onefile->{'gid'} !", "create_files_table"); } 993 my $locallanguage = $onefile->{'specificlanguage'}; 994 my $property = "IS" . $file{'Language'}; 995 my $value = 1; 996 my $condition = $property . "=" . $value; 997 998 $onefile->{'ComponentCondition'} = $condition; 999 1000 if ( exists($installer::globals::componentcondition{$file{'Component_'}})) 1001 { 1002 if ( $installer::globals::componentcondition{$file{'Component_'}} ne $condition ) { installer::exiter::exit_program("ERROR: There is already another component condition for file $onefile->{'gid'}: \"$installer::globals::componentcondition{$file{'Component_'}}\" and \"$condition\" !", "create_files_table"); } 1003 } 1004 else 1005 { 1006 $installer::globals::componentcondition{$file{'Component_'}} = $condition; 1007 } 1008 1009 # collecting all properties for table Property 1010 if ( ! exists($installer::globals::languageproperties{$property}) ) { $installer::globals::languageproperties{$property} = $value; } 1011 } 1012 1013 if ( $installer::globals::prepare_winpatch ) 1014 { 1015 my $path = $onefile->{'sourcepath'}; 1016 if ( $^O =~ /cygwin/i ) { $path = $onefile->{'cyg_sourcepath'}; } 1017 1018 open(FILE, $path) or die "ERROR: Can't open $path for creating file hash"; 1019 binmode(FILE); 1020 my $hashinfo = pack("l", 20); 1021 $hashinfo .= Digest::MD5->new->addfile(*FILE)->digest; 1022 1023 my @i = unpack ('x[l]l4', $hashinfo); 1024 $oneline = $file{'File'} . "\t" . 1025 "0" . "\t" . 1026 $i[0] . "\t" . 1027 $i[1] . "\t" . 1028 $i[2] . "\t" . 1029 $i[3] . "\n"; 1030 push (@filehashtable, $oneline); 1031 } 1032 1033 # Saving the sequence number in a hash with uniquefilename as key. 1034 # This is used for better performance in "save_packorder" 1035 $installer::globals::uniquefilenamesequence{$onefile->{'uniquename'}} = $onefile->{'sequencenumber'}; 1036 1037 # Special handling for files in PREDEFINED_OSSHELLNEWDIR. These components 1038 # need as KeyPath a RegistryItem in HKCU 1039 my $destdir = ""; 1040 if ( $onefile->{'Dir'} ) { $destdir = $onefile->{'Dir'}; } 1041 1042 if (( $destdir =~ /\bPREDEFINED_OSSHELLNEWDIR\b/ ) || ( $onefile->{'needs_user_registry_key'} )) 1043 { 1044 my $keypath = generate_registry_keypath($onefile); 1045 $onefile->{'userregkeypath'} = $keypath; 1046 push(@installer::globals::userregistrycollector, $onefile); 1047 $installer::globals::addeduserregitrykeys = 1; 1048 } 1049 } 1050 1051 # putting content from %allfilecomponents to $allfilecomponentsref for later usage 1052 foreach $localkey (keys %allfilecomponents ) { push( @{$allfilecomponentsref}, $localkey); } 1053 1054 my $filetablename = $basedir . $installer::globals::separator . "File.idt"; 1055 installer::files::save_file($filetablename ,\@filetable); 1056 $infoline = "\nCreated idt file: $filetablename\n"; 1057 push(@installer::globals::logfileinfo, $infoline); 1058 1059 installer::logger::include_timestamp_into_logfile("Performance Info: File Table end"); 1060 1061 my $filehashtablename = $basedir . $installer::globals::separator . "MsiFileHash.idt"; 1062 installer::files::save_file($filehashtablename ,\@filehashtable); 1063 $infoline = "\nCreated idt file: $filehashtablename\n"; 1064 push(@installer::globals::logfileinfo, $infoline); 1065 1066 # Now the new files can be added to the files collector (only in update packaging processes) 1067 if ( $installer::globals::newfilesexist ) 1068 { 1069 foreach my $seq (sort keys %installer::globals::newfilescollector) { push(@allfiles, $installer::globals::newfilescollector{$seq}) } 1070 } 1071 1072 return \@allfiles; 1073} 1074 10751; 1076