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