xref: /trunk/main/solenv/bin/modules/installer/archivefiles.pm (revision fc9fd3f14a55d77b35643a64034752a178b2a5b0)
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