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