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::archivefiles; 29 30use Archive::Zip qw( :ERROR_CODES :CONSTANTS ); 31use installer::converter; 32use installer::existence; 33use installer::exiter; 34use installer::files; 35use installer::globals; 36use installer::logger; 37use installer::pathanalyzer; 38use installer::systemactions; 39 40################################################################# 41# Changing the name for files with flag RENAME_TO_LANGUAGE 42################################################################# 43 44sub put_language_into_name 45{ 46 my ( $oldname, $onelanguage ) = @_; 47 48 my $newname = ""; 49 50 my $filename = ""; 51 my $extension = ""; 52 53 if ( $oldname =~ /en-US/ ) # files, that contain the language in the file name 54 { 55 $newname = $oldname; 56 $newname =~ s/en-US/$onelanguage/; 57 } 58 else # files, that do not contain the language in the file name 59 { 60 if ( $oldname =~ /^\s*(.*)(\..*?)\s*$/ ) # files with extension 61 { 62 $filename = $1; 63 $extension = $2; 64 } 65 else 66 { 67 $filename = $oldname; 68 $extension = ""; 69 } 70 71 $newname = $1 . "_" . $onelanguage . $2; 72 } 73 74 return $newname; 75} 76 77################################################################# 78# Converting patchfiles string into array 79################################################################# 80 81sub get_patch_file_list 82{ 83 my ( $patchfilestring ) = @_; 84 85 $patchfilestring =~ s/^\s*\(?//; 86 $patchfilestring =~ s/\)?\s*$//; 87 $patchfilestring =~ s/^\s*\///; 88 $patchfilestring =~ s/^\s*\\//; 89 90 my $patchfilesarray = installer::converter::convert_stringlist_into_array_without_linebreak_and_quotes(\$patchfilestring, ","); 91 92 return $patchfilesarray; 93} 94 95################################################################# 96# Reading all executables in the "manifest.xml" 97################################################################# 98 99sub get_all_executables_from_manifest 100{ 101 my ($unzipdir, $manifestfile, $executable_files_in_extensions) = @_; 102 103 my $is_executable = 0; 104 105 for ( my $i = 0; $i <= $#{$manifestfile}; $i++ ) 106 { 107 my $line = ${$manifestfile}[$i]; 108 109 if ( $line =~ /\"application\/vnd\.sun\.star\.executable\"/ ) { $is_executable = 1; } 110 111 if (( $line =~ /manifest\:full\-path=\"(.*?)\"/ ) && ( $is_executable )) 112 { 113 my $filename = $unzipdir . $installer::globals::separator . $1; 114 # making only slashes for comparison reasons 115 $filename =~ s/\\/\//g; 116 $executable_files_in_extensions->{$filename} = 1; 117 } 118 119 if ( $line =~ /\/\>/ ) { $is_executable = 0; } 120 } 121} 122 123################################################################# 124# Reading the "manifest.xml" in extensions and determine, if 125# there are executable files 126################################################################# 127 128sub collect_all_executable_files_in_extensions 129{ 130 my ($unzipdir, $executable_files_in_extensions) = @_; 131 132 $unzipdir =~ s/\Q$installer::globals::separator\E\s*$//; 133 134 my $manifestfilename = $unzipdir . $installer::globals::separator . "META-INF" . $installer::globals::separator . "manifest.xml"; 135 136 if ( -f $manifestfilename ) 137 { 138 my $manifestfile = installer::files::read_file($manifestfilename); 139 get_all_executables_from_manifest($unzipdir, $manifestfile, $executable_files_in_extensions); 140 } 141} 142 143################################################################# 144# Analyzing files with flag ARCHIVE 145################################################################# 146 147sub resolving_archive_flag 148{ 149 my ($filesarrayref, $additionalpathsref, $languagestringref, $loggingdir) = @_; 150 151 if ( $installer::globals::debug ) { installer::logger::debuginfo("installer::archivefiles::resolving_archive_flag : $#{$filesarrayref} : $#{$additionalpathsref} : $$languagestringref : $loggingdir"); } 152 153 my @newallfilesarray = (); 154 155 my ($systemcall, $returnvalue, $infoline); 156 157 my $unziplistfile = $loggingdir . "unziplist_" . $installer::globals::build . "_" . $installer::globals::compiler . "_" . $$languagestringref . ".txt"; 158 159 my $platformunzipdirbase = installer::systemactions::create_directories("zip", $languagestringref); 160 push(@installer::globals::removedirs, $platformunzipdirbase); 161 162 installer::logger::include_header_into_logfile("Files with flag ARCHIVE:"); 163 164 my $repeat_unzip = 0; 165 my $maxcounter = 0; 166 167 for ( my $i = 0; $i <= $#{$filesarrayref}; $i++ ) 168 { 169 if ( $repeat_unzip ) { $i--; } # decreasing the counter 170 171 my $onefile = ${$filesarrayref}[$i]; 172 my $styles = ""; 173 174 if ( $onefile->{'Styles'} ) { $styles = $onefile->{'Styles'}; } 175 176 if ( $styles =~ /\bARCHIVE\b/ ) # copying, unzipping and changing the file list 177 { 178 my $iscommonfile = 0; 179 my $sourcepath = $onefile->{'sourcepath'}; 180 181 if ( $sourcepath =~ /\Q$installer::globals::separator\E\bcommon$installer::globals::productextension\Q$installer::globals::separator\E/ ) # /common/ or /common.pro/ 182 { 183 $iscommonfile = 1; 184 } 185 186 my $use_internal_rights = 0; 187 if ( $styles =~ /\bUSE_INTERNAL_RIGHTS\b/ ) { $use_internal_rights = 1; } # using the rights used inside the zip file 188 189 my $rename_to_language = 0; 190 if ( $styles =~ /\bRENAME_TO_LANGUAGE\b/ ) { $rename_to_language = 1; } # special handling for renamed files (scriptitems.pm) 191 192 my %executable_files_in_extensions = (); 193 my $set_executable_privileges = 0; # setting privileges for exectables is required for oxt files 194 if ( $onefile->{'Name'} =~ /\.oxt\s*$/ ) { $set_executable_privileges = 1; } 195 196 # mechanism to select files from an archive files 197 my $select_files = 0; 198 my $selectlistfiles = ""; 199 my @keptfiles = (); 200 if ( $onefile->{'Selectfiles'} ) 201 { 202 $select_files = 1; 203 $selectlistfiles = get_patch_file_list( $onefile->{'Selectfiles'} ); 204 $infoline = "Selected file list defined at file: $onefile->{'Name'} :\n"; 205 push( @installer::globals::logfileinfo, $infoline); 206 for ( my $k = 0; $k <= $#{$selectlistfiles}; $k++ ) 207 { 208 $infoline = "\"${$selectlistfiles}[$k]\"\n"; 209 push( @installer::globals::logfileinfo, $infoline); 210 } 211 } 212 213 if ( $onefile->{'Selectfiles'} ) { $onefile->{'Selectfiles'} = ""; } # Selected files list no longer required 214 215 # mechanism to define patch files inside an archive files 216 my $select_patch_files = 0; 217 my $patchlistfiles = ""; 218 my @keptpatchflags = (); 219 if (( $styles =~ /\bPATCH\b/ ) && ( $onefile->{'Patchfiles'} ) && ( $installer::globals::patch )) 220 { 221 $select_patch_files = 1; # special handling if a Patchlist is defined 222 $patchlistfiles = get_patch_file_list( $onefile->{'Patchfiles'} ); 223 $infoline = "Patch file list defined at file: $onefile->{'Name'} :\n"; 224 push( @installer::globals::logfileinfo, $infoline); 225 for ( my $k = 0; $k <= $#{$patchlistfiles}; $k++ ) 226 { 227 $infoline = "\"${$patchlistfiles}[$k]\"\n"; 228 push( @installer::globals::logfileinfo, $infoline); 229 } 230 } 231 232 if ( $onefile->{'Patchfiles'} ) { $onefile->{'Patchfiles'} = ""; } # Patch file list no longer required 233 234 # creating directories 235 236 my $onelanguage = $onefile->{'specificlanguage'}; 237 238 # files without language into directory "00" 239 240 if ($onelanguage eq "") { $onelanguage = "00"; } 241 242 my $unzipdir; 243 244 # if ($iscommonfile) { $unzipdir = $commonunzipdirbase . $installer::globals::separator . $onelanguage . $installer::globals::separator; } 245 # else { $unzipdir = $platformunzipdirbase . $installer::globals::separator . $onelanguage . $installer::globals::separator; } 246 247 $unzipdir = $platformunzipdirbase . $installer::globals::separator . $onelanguage . $installer::globals::separator; 248 249 installer::systemactions::create_directory($unzipdir); # creating language specific subdirectories 250 251 my $onefilename = $onefile->{'Name'}; 252 $onefilename =~ s/\./\_/g; # creating new directory name 253 $onefilename =~ s/\//\_/g; # only because of /letter/fontunxpsprint.zip, the only zip file with path 254 $unzipdir = $unzipdir . $onefilename . $installer::globals::separator; 255 256 if ( $installer::globals::dounzip ) { installer::systemactions::create_directory($unzipdir); } # creating subdirectories with the names of the zipfiles 257 258 my $zip = Archive::Zip->new(); 259 if ( $zip->read($sourcepath) != AZ_OK ) 260 { 261 $infoline = "ERROR: Could not unzip $sourcepath\n"; 262 push( @installer::globals::logfileinfo, $infoline); 263 } 264 265 my $counter = 0; 266 my $contains_dll = 0; 267 foreach my $member ( $zip->memberNames() ) 268 { 269 $counter++; 270 if ( $member =~ /.dll\s*$/ ) { $contains_dll = 1; } 271 } 272 273 if (! ( $counter > 0 )) # the zipfile is empty 274 { 275 $infoline = "ERROR: Could not unzip $sourcepath\n"; 276 push( @installer::globals::logfileinfo, $infoline); 277 278 } 279 else 280 { 281 if ( $installer::globals::dounzip ) # really unpacking the files 282 { 283 if ( $zip->extractTree("", $unzipdir) != AZ_OK ) { installer::exiter::exit_program("ERROR: $infoline", "resolving_archive_flag"); } 284 285 if (( $^O =~ /cygwin/i ) && ( $contains_dll )) 286 { 287 # Make dll's executable 288 $systemcall = "cd $unzipdir; find . -name \\*.dll -exec chmod 775 \{\} \\\;"; 289 $returnvalue = system($systemcall); 290 $infoline = "Systemcall: $systemcall\n"; 291 push( @installer::globals::logfileinfo, $infoline); 292 293 if ($returnvalue) 294 { 295 $infoline = "ERROR: Could not execute \"$systemcall\"!\n"; 296 push( @installer::globals::logfileinfo, $infoline); 297 } 298 } 299 300 if ( ! $installer::globals::iswindowsbuild ) 301 { 302 # Setting unix rights to "775" for all created directories inside the package 303 304 $systemcall = "cd $unzipdir; find . -type d -exec chmod 775 \{\} \\\;"; 305 $returnvalue = system($systemcall); 306 $infoline = "Systemcall: $systemcall\n"; 307 push( @installer::globals::logfileinfo, $infoline); 308 309 if ($returnvalue) 310 { 311 $infoline = "ERROR: Could not execute \"$systemcall\"!\n"; 312 push( @installer::globals::logfileinfo, $infoline); 313 } 314 } 315 316 # Selecting names of executable files in extensions 317 if ( $set_executable_privileges ) 318 { 319 collect_all_executable_files_in_extensions($unzipdir, \%executable_files_in_extensions); 320 } 321 } 322 323 my $zipfileref = \@zipfile; 324 my $unziperror = 0; 325 326 foreach my $zipname ( $zip->memberNames() ) 327 { 328 # Format from Archive:::Zip : 329 # dir1/ 330 # dir1/so7drawing.desktop 331 332 # some directories and files (from the help) start with "./simpress.idx" 333 334 $zipname =~ s/^\s*\.\///; 335 336 if ($installer::globals::iswin and $^O =~ /MSWin/i) { $zipname =~ s/\//\\/g; } 337 338 if ( $zipname =~ /\Q$installer::globals::separator\E\s*$/ ) # slash or backslash at the end characterizes a directory 339 { 340 $zipname = $zipname . "\n"; 341 push(@{$additionalpathsref}, $zipname); 342 343 # Also needed here: 344 # Name 345 # Language 346 # ismultilingual 347 # Basedirectory 348 349 # This is not needed, because the list of all directories for the 350 # epm list file is generated from the destination directories of the 351 # files included in the product! 352 } 353 else 354 { 355 my %newfile = (); 356 %newfile = %{$onefile}; 357 $newfile{'Name'} = $zipname; 358 my $destination = $onefile->{'destination'}; 359 installer::pathanalyzer::get_path_from_fullqualifiedname(\$destination); 360 $newfile{'destination'} = $destination . $zipname; 361 $newfile{'sourcepath'} = $unzipdir . $zipname; 362 $newfile{'zipfilename'} = $onefile->{'Name'}; 363 $newfile{'zipfilesource'} = $onefile->{'sourcepath'}; 364 $newfile{'zipfiledestination'} = $onefile->{'destination'}; 365 366 if (( $use_internal_rights ) && ( ! $installer::globals::iswin )) 367 { 368 my $value = sprintf("%o", (stat($newfile{'sourcepath'}))[2]); 369 $newfile{'UnixRights'} = substr($value, 3); 370 $infoline = "Setting unix rights for \"$newfile{'sourcepath'}\" to \"$newfile{'UnixRights'}\"\n"; 371 push( @installer::globals::logfileinfo, $infoline); 372 } 373 374 if ( $set_executable_privileges ) 375 { 376 # All pathes to executables are saved in the hash %executable_files_in_extensions 377 my $compare_path = $newfile{'sourcepath'}; 378 $compare_path =~ s/\\/\//g; # contains only slashes for comparison reasons 379 if ( exists($executable_files_in_extensions{$compare_path}) ) 380 { 381 $newfile{'UnixRights'} = "775"; 382 $infoline = "Executable in Extension: Setting unix rights for \"$newfile{'sourcepath'}\" to \"$newfile{'UnixRights'}\"\n"; 383 push( @installer::globals::logfileinfo, $infoline); 384 } 385 } 386 387 if ( $select_files ) 388 { 389 if ( ! installer::existence::exists_in_array($zipname,$selectlistfiles) ) 390 { 391 $infoline = "Removing from ARCHIVE file $onefilename: $zipname\n"; 392 push( @installer::globals::logfileinfo, $infoline); 393 next; # ignoring files, that are not included in $selectlistfiles 394 } 395 else 396 { 397 $infoline = "Keeping from ARCHIVE file $onefilename: $zipname\n"; 398 push( @installer::globals::logfileinfo, $infoline); 399 push( @keptfiles, $zipname); # collecting all kept files 400 } 401 } 402 403 if ( $select_patch_files ) 404 { 405 # Is this file listed in the Patchfile list? 406 # $zipname (filename including path in zip file has to be listed in patchfile list 407 408 if ( ! installer::existence::exists_in_array($zipname,$patchlistfiles) ) 409 { 410 $newfile{'Styles'} =~ s/\bPATCH\b//; # removing the flag PATCH 411 $newfile{'Styles'} =~ s/\,\s*\,/\,/; 412 $newfile{'Styles'} =~ s/\(\s*\,/\(/; 413 $newfile{'Styles'} =~ s/\,\s*\)/\)/; 414 # $infoline = "Removing PATCH flag from: $zipname\n"; 415 # push( @installer::globals::logfileinfo, $infoline); 416 } 417 else 418 { 419 # $infoline = "Keeping PATCH flag at: $zipname\n"; 420 # push( @installer::globals::logfileinfo, $infoline); 421 push( @keptpatchflags, $zipname); # collecting all PATCH flags 422 } 423 } 424 425 if ( $rename_to_language ) 426 { 427 my $newzipname = put_language_into_name($zipname, $onelanguage); 428 my $oldfilename = $unzipdir . $zipname; 429 my $newfilename = $unzipdir . $newzipname; 430 431 installer::systemactions::copy_one_file($oldfilename, $newfilename); 432 433 $newfile{'Name'} = $newzipname; 434 $newfile{'destination'} = $destination . $newzipname; 435 $newfile{'sourcepath'} = $unzipdir . $newzipname; 436 437 $infoline = "RENAME_TO_LANGUAGE: Using $newzipname instead of $zipname!\n"; 438 push( @installer::globals::logfileinfo, $infoline); 439 } 440 441 my $sourcefiletest = $unzipdir . $zipname; 442 if ( ! -f $sourcefiletest ) 443 { 444 $infoline = "ATTENTION: Unzip failed for $sourcefiletest!\n"; 445 push( @installer::globals::logfileinfo, $infoline); 446 $unziperror = 1; 447 } 448 449 # only adding the new line into the files array, if not in repeat modus 450 451 if ( ! $repeat_unzip ) { push(@newallfilesarray, \%newfile); } 452 } 453 } 454 455 # Comparing the content of @keptfiles and $selectlistfiles 456 # Do all files from the list of selected files are stored in @keptfiles ? 457 # @keptfiles contains only files included in $selectlistfiles. But are all 458 # files from $selectlistfiles included in @keptfiles? 459 460 if ( $select_files ) 461 { 462 my $number = $#{$selectlistfiles} + 1; 463 $infoline = "SELECTLIST: Number of files in file selection list: $number\n"; 464 push( @installer::globals::logfileinfo, $infoline); 465 $number = $#keptfiles + 1; 466 $infoline = "SELECTLIST: Number of kept files: $number\n"; 467 push( @installer::globals::logfileinfo, $infoline); 468 469 for ( my $k = 0; $k <= $#keptfiles; $k++ ) 470 { 471 $infoline = "KEPT FILES: $keptfiles[$k]\n"; 472 push( @installer::globals::logfileinfo, $infoline); 473 } 474 475 my @warningfiles = (); 476 477 for ( my $k = 0; $k <= $#{$selectlistfiles}; $k++ ) 478 { 479 if ( ! installer::existence::exists_in_array(${$selectlistfiles}[$k],\@keptfiles) ) 480 { 481 push(@warningfiles, ${$selectlistfiles}[$k]); 482 } 483 } 484 485 for ( my $k = 0; $k <= $#warningfiles; $k++ ) 486 { 487 $infoline = "WARNING: $warningfiles[$k] not included in install set (does not exist in zip file)!\n"; 488 push( @installer::globals::logfileinfo, $infoline); 489 } 490 491 } 492 493 # Comparing the content of @keptpatchflags and $patchlistfiles 494 # Do all files from the patch list have a PATCH flag ? 495 # @keptpatchflags contains only files included in $patchlistfiles. But are all 496 # files from $patchlistfiles included in @keptpatchflags? 497 498 if ( $select_patch_files ) 499 { 500 my $number = $#{$patchlistfiles} + 1; 501 $infoline = "PATCHLIST: Number of files in patch list: $number\n"; 502 push( @installer::globals::logfileinfo, $infoline); 503 $number = $#keptpatchflags + 1; 504 $infoline = "PATCHLIST: Number of kept PATCH flags: $number\n"; 505 push( @installer::globals::logfileinfo, $infoline); 506 507 for ( my $k = 0; $k <= $#keptpatchflags; $k++ ) 508 { 509 $infoline = "KEPT PATCH FLAGS: $keptpatchflags[$k]\n"; 510 push( @installer::globals::logfileinfo, $infoline); 511 } 512 513 my @warningfiles = (); 514 515 for ( my $k = 0; $k <= $#{$patchlistfiles}; $k++ ) 516 { 517 if ( ! installer::existence::exists_in_array(${$patchlistfiles}[$k],\@keptpatchflags) ) 518 { 519 push(@warningfiles, ${$patchlistfiles}[$k]); 520 } 521 } 522 523 for ( my $k = 0; $k <= $#warningfiles; $k++ ) 524 { 525 $infoline = "WARNING: $warningfiles[$k] did not keep PATCH flag (does not exist in zip file)!\n"; 526 push( @installer::globals::logfileinfo, $infoline); 527 } 528 } 529 530 if ( $unziperror ) 531 { 532 installer::logger::print_warning( "Repeating to unpack $sourcepath! \n" ); 533 $infoline = "ATTENTION: Repeating to unpack $sourcepath !\n"; 534 push( @installer::globals::logfileinfo, $infoline); 535 $repeat_unzip = 1; 536 $maxcounter++; 537 538 if ( $maxcounter == 5 ) # exiting the program 539 { 540 installer::exiter::exit_program("ERROR: Failed to unzip $sourcepath !", "resolving_archive_flag"); 541 } 542 } 543 else 544 { 545 $infoline = "Info: $sourcepath unpacked without problems !\n"; 546 push( @installer::globals::logfileinfo, $infoline); 547 $repeat_unzip = 0; 548 $maxcounter = 0; 549 } 550 } 551 } 552 else # nothing to do here, no zipped file (no ARCHIVE flag) 553 { 554 push(@newallfilesarray, $onefile); 555 } 556 } 557 558 $infoline = "\n"; 559 push( @installer::globals::logfileinfo, $infoline); 560 561 return \@newallfilesarray; 562} 563 564 5651; 566