1*b1cdbd2cSJim Jagielski#**************************************************************
2*b1cdbd2cSJim Jagielski#
3*b1cdbd2cSJim Jagielski#  Licensed to the Apache Software Foundation (ASF) under one
4*b1cdbd2cSJim Jagielski#  or more contributor license agreements.  See the NOTICE file
5*b1cdbd2cSJim Jagielski#  distributed with this work for additional information
6*b1cdbd2cSJim Jagielski#  regarding copyright ownership.  The ASF licenses this file
7*b1cdbd2cSJim Jagielski#  to you under the Apache License, Version 2.0 (the
8*b1cdbd2cSJim Jagielski#  "License"); you may not use this file except in compliance
9*b1cdbd2cSJim Jagielski#  with the License.  You may obtain a copy of the License at
10*b1cdbd2cSJim Jagielski#
11*b1cdbd2cSJim Jagielski#    http://www.apache.org/licenses/LICENSE-2.0
12*b1cdbd2cSJim Jagielski#
13*b1cdbd2cSJim Jagielski#  Unless required by applicable law or agreed to in writing,
14*b1cdbd2cSJim Jagielski#  software distributed under the License is distributed on an
15*b1cdbd2cSJim Jagielski#  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16*b1cdbd2cSJim Jagielski#  KIND, either express or implied.  See the License for the
17*b1cdbd2cSJim Jagielski#  specific language governing permissions and limitations
18*b1cdbd2cSJim Jagielski#  under the License.
19*b1cdbd2cSJim Jagielski#
20*b1cdbd2cSJim Jagielski#**************************************************************
21*b1cdbd2cSJim Jagielski
22*b1cdbd2cSJim Jagielskipackage installer::patch::InstallationSet;
23*b1cdbd2cSJim Jagielski
24*b1cdbd2cSJim Jagielskiuse installer::patch::Tools;
25*b1cdbd2cSJim Jagielskiuse installer::patch::Version;
26*b1cdbd2cSJim Jagielskiuse installer::logger;
27*b1cdbd2cSJim Jagielski
28*b1cdbd2cSJim Jagielskiuse strict;
29*b1cdbd2cSJim Jagielski
30*b1cdbd2cSJim Jagielski# Call Get7Zip() to get access to the filename of the 7z executable.
31*b1cdbd2cSJim Jagielskimy $SevenZip = undef;
32*b1cdbd2cSJim Jagielski
33*b1cdbd2cSJim Jagielski
34*b1cdbd2cSJim Jagielski=head1 NAME
35*b1cdbd2cSJim Jagielski
36*b1cdbd2cSJim Jagielski    package installer::patch::InstallationSet  -  Functions for handling installation sets
37*b1cdbd2cSJim Jagielski
38*b1cdbd2cSJim Jagielski=head1 DESCRIPTION
39*b1cdbd2cSJim Jagielski
40*b1cdbd2cSJim Jagielski    This package contains functions for unpacking the .exe files that
41*b1cdbd2cSJim Jagielski    are created by the NSIS installer creator and the .cab files in
42*b1cdbd2cSJim Jagielski    the installation sets.
43*b1cdbd2cSJim Jagielski
44*b1cdbd2cSJim Jagielski=cut
45*b1cdbd2cSJim Jagielski
46*b1cdbd2cSJim Jagielski
47*b1cdbd2cSJim Jagielski
48*b1cdbd2cSJim Jagielski
49*b1cdbd2cSJim Jagielski=head2 Detect7ZipOnWindows ()
50*b1cdbd2cSJim Jagielski
51*b1cdbd2cSJim Jagielski    7Zip seems to be the only program able to unpack an NSIS installer.
52*b1cdbd2cSJim Jagielski    Search for it.
53*b1cdbd2cSJim Jagielski
54*b1cdbd2cSJim Jagielski=cut
55*b1cdbd2cSJim Jagielski
56*b1cdbd2cSJim Jagielskisub Detect7ZipOnWindows ()
57*b1cdbd2cSJim Jagielski{
58*b1cdbd2cSJim Jagielski    # Use 'reg query' to read registry entry from Windows registry.
59*b1cdbd2cSJim Jagielski    my $registry_key = "HKEY_CURRENT_USER\\\\Software\\\\7-Zip";
60*b1cdbd2cSJim Jagielski    my $registry_value_name = "Path";
61*b1cdbd2cSJim Jagielski    my $command = sprintf("reg query %s /v %s", $registry_key, $registry_value_name);
62*b1cdbd2cSJim Jagielski    my $response = qx($command);
63*b1cdbd2cSJim Jagielski
64*b1cdbd2cSJim Jagielski    # Process the response.
65*b1cdbd2cSJim Jagielski    my $path_to_7zip = undef;
66*b1cdbd2cSJim Jagielski    if ($response =~ /\s+REG_SZ\s+([^\r\n]*)/m)
67*b1cdbd2cSJim Jagielski    {
68*b1cdbd2cSJim Jagielski        $path_to_7zip = $1;
69*b1cdbd2cSJim Jagielski    }
70*b1cdbd2cSJim Jagielski
71*b1cdbd2cSJim Jagielski    # If that failed, then make an educated guess.
72*b1cdbd2cSJim Jagielski    if ( ! defined $path_to_7zip)
73*b1cdbd2cSJim Jagielski    {
74*b1cdbd2cSJim Jagielski        $path_to_7zip = "c:\\Program Files\\7-Zip\\";
75*b1cdbd2cSJim Jagielski    }
76*b1cdbd2cSJim Jagielski
77*b1cdbd2cSJim Jagielski    # Check if the executable exists and is, well, executable.
78*b1cdbd2cSJim Jagielski    return undef unless -d $path_to_7zip;
79*b1cdbd2cSJim Jagielski    my $fullname = File::Spec->catfile($path_to_7zip, "7z.exe");
80*b1cdbd2cSJim Jagielski    return undef unless -f $fullname;
81*b1cdbd2cSJim Jagielski    return undef unless -x $fullname;
82*b1cdbd2cSJim Jagielski
83*b1cdbd2cSJim Jagielski    return $fullname;
84*b1cdbd2cSJim Jagielski}
85*b1cdbd2cSJim Jagielski
86*b1cdbd2cSJim Jagielski
87*b1cdbd2cSJim Jagielski
88*b1cdbd2cSJim Jagielski
89*b1cdbd2cSJim Jagielskisub Get7Zip ()
90*b1cdbd2cSJim Jagielski{
91*b1cdbd2cSJim Jagielski    if ( ! defined $SevenZip)
92*b1cdbd2cSJim Jagielski    {
93*b1cdbd2cSJim Jagielski        if ($ENV{'OS'} eq "WNT")
94*b1cdbd2cSJim Jagielski        {
95*b1cdbd2cSJim Jagielski            $SevenZip = Detect7ZipOnWindows();
96*b1cdbd2cSJim Jagielski        }
97*b1cdbd2cSJim Jagielski        if ( ! defined $SevenZip)
98*b1cdbd2cSJim Jagielski        {
99*b1cdbd2cSJim Jagielski            # Use an empty string to avoid repeated (and failing) detections of a missing 7z.
100*b1cdbd2cSJim Jagielski            $SevenZip = "";
101*b1cdbd2cSJim Jagielski        }
102*b1cdbd2cSJim Jagielski    }
103*b1cdbd2cSJim Jagielski
104*b1cdbd2cSJim Jagielski    return $SevenZip eq "" ? undef : $SevenZip;
105*b1cdbd2cSJim Jagielski}
106*b1cdbd2cSJim Jagielski
107*b1cdbd2cSJim Jagielski
108*b1cdbd2cSJim Jagielski
109*b1cdbd2cSJim Jagielski
110*b1cdbd2cSJim Jagielskisub UnpackExe ($$)
111*b1cdbd2cSJim Jagielski{
112*b1cdbd2cSJim Jagielski    my ($filename, $destination_path) = @_;
113*b1cdbd2cSJim Jagielski
114*b1cdbd2cSJim Jagielski    $installer::logger::Info->printf("unpacking installation set to '%s'\n", $destination_path);
115*b1cdbd2cSJim Jagielski
116*b1cdbd2cSJim Jagielski    # Unpack to a temporary path and change its name to the destination path
117*b1cdbd2cSJim Jagielski    # only when the unpacking has completed successfully.
118*b1cdbd2cSJim Jagielski    File::Path::make_path($destination_path);
119*b1cdbd2cSJim Jagielski
120*b1cdbd2cSJim Jagielski    my $windows_filename = installer::patch::Tools::ToEscapedWindowsPath($filename);
121*b1cdbd2cSJim Jagielski    my $windows_destination_path = installer::patch::Tools::ToEscapedWindowsPath($destination_path);
122*b1cdbd2cSJim Jagielski    my $command = join(" ",
123*b1cdbd2cSJim Jagielski        "\"".Get7Zip()."\"",
124*b1cdbd2cSJim Jagielski        "x",
125*b1cdbd2cSJim Jagielski        "-y",
126*b1cdbd2cSJim Jagielski        "-o".$windows_destination_path,
127*b1cdbd2cSJim Jagielski        $windows_filename);
128*b1cdbd2cSJim Jagielski    my $result = qx($command);
129*b1cdbd2cSJim Jagielski    if ( ! $result)
130*b1cdbd2cSJim Jagielski    {
131*b1cdbd2cSJim Jagielski        installer::exiter::exit_program(
132*b1cdbd2cSJim Jagielski            "ERROR: can not unpack downloadable installation set: ".$!,
133*b1cdbd2cSJim Jagielski            "installer::patch::InstallationSet::UnpackExe");
134*b1cdbd2cSJim Jagielski    }
135*b1cdbd2cSJim Jagielski
136*b1cdbd2cSJim Jagielski    # Check the existence of the .cab files.
137*b1cdbd2cSJim Jagielski    my $cab_filename = File::Spec->catfile($destination_path, "openoffice1.cab");
138*b1cdbd2cSJim Jagielski    if ( ! -f $cab_filename)
139*b1cdbd2cSJim Jagielski    {
140*b1cdbd2cSJim Jagielski        installer::logger::PrintError("cab file '%s' was not extracted from installation set\n", $cab_filename);
141*b1cdbd2cSJim Jagielski        return 0;
142*b1cdbd2cSJim Jagielski    }
143*b1cdbd2cSJim Jagielski    return 1;
144*b1cdbd2cSJim Jagielski}
145*b1cdbd2cSJim Jagielski
146*b1cdbd2cSJim Jagielski
147*b1cdbd2cSJim Jagielski
148*b1cdbd2cSJim Jagielski
149*b1cdbd2cSJim Jagielski=head2 UnpackCab($cab_filename, $destination_path)
150*b1cdbd2cSJim Jagielski
151*b1cdbd2cSJim Jagielski    Unpacking the cabinet file inside an .exe installation set is a
152*b1cdbd2cSJim Jagielski    three step process because there is no directory information stored
153*b1cdbd2cSJim Jagielski    inside the cab file.  This has to be taken from the 'File' and
154*b1cdbd2cSJim Jagielski    'Directory' tables in the .msi file.
155*b1cdbd2cSJim Jagielski
156*b1cdbd2cSJim Jagielski    1. Setup the directory structure of all files in the cab from the 'File' and 'Directory' tables in the msi.
157*b1cdbd2cSJim Jagielski
158*b1cdbd2cSJim Jagielski    2. Unpack the cab file.
159*b1cdbd2cSJim Jagielski
160*b1cdbd2cSJim Jagielski    3. Move the files to their destination directories.
161*b1cdbd2cSJim Jagielski
162*b1cdbd2cSJim Jagielski=cut
163*b1cdbd2cSJim Jagielskisub UnpackCab ($$$)
164*b1cdbd2cSJim Jagielski{
165*b1cdbd2cSJim Jagielski    my ($cab_filename, $msi, $destination_path) = @_;
166*b1cdbd2cSJim Jagielski
167*b1cdbd2cSJim Jagielski    # Step 1
168*b1cdbd2cSJim Jagielski    # Extract the directory structure from the 'File' and 'Directory' tables in the given msi.
169*b1cdbd2cSJim Jagielski    $installer::logger::Info->printf("setting up directory tree\n");
170*b1cdbd2cSJim Jagielski    my $file_table = $msi->GetTable("File");
171*b1cdbd2cSJim Jagielski    my $file_map = $msi->GetFileMap();
172*b1cdbd2cSJim Jagielski
173*b1cdbd2cSJim Jagielski    # Step 2
174*b1cdbd2cSJim Jagielski    # Unpack the .cab file to a temporary path.
175*b1cdbd2cSJim Jagielski    my $temporary_destination_path = $destination_path . ".tmp";
176*b1cdbd2cSJim Jagielski    if ( -d $temporary_destination_path)
177*b1cdbd2cSJim Jagielski    {
178*b1cdbd2cSJim Jagielski        # Temporary directory already exists => cab file has already been unpacked (flat), nothing to do.
179*b1cdbd2cSJim Jagielski        printf("%s exists\n", $temporary_destination_path);
180*b1cdbd2cSJim Jagielski        $installer::logger::Info->printf("cab file has already been unpacked to flat structure\n");
181*b1cdbd2cSJim Jagielski    }
182*b1cdbd2cSJim Jagielski    else
183*b1cdbd2cSJim Jagielski    {
184*b1cdbd2cSJim Jagielski        UnpackCabFlat($cab_filename, $temporary_destination_path, $file_table);
185*b1cdbd2cSJim Jagielski    }
186*b1cdbd2cSJim Jagielski
187*b1cdbd2cSJim Jagielski    # Step 3
188*b1cdbd2cSJim Jagielski    # Move the files to their destinations.
189*b1cdbd2cSJim Jagielski    File::Path::make_path($destination_path);
190*b1cdbd2cSJim Jagielski    $installer::logger::Info->printf("moving files to their directories\n");
191*b1cdbd2cSJim Jagielski    my $directory_map = $msi->GetDirectoryMap();
192*b1cdbd2cSJim Jagielski    my $office_menu_folder_name = $directory_map->{'INSTALLLOCATION'}->{'target_long_name'};
193*b1cdbd2cSJim Jagielski    my $count = 0;
194*b1cdbd2cSJim Jagielski    foreach my $file_row (@{$file_table->GetAllRows()})
195*b1cdbd2cSJim Jagielski    {
196*b1cdbd2cSJim Jagielski        my $unique_name = $file_row->GetValue('File');
197*b1cdbd2cSJim Jagielski        my $file_item = $file_map->{$unique_name};
198*b1cdbd2cSJim Jagielski        my $directory_item = $file_item->{'directory'};
199*b1cdbd2cSJim Jagielski        my $long_file_name = $file_item->{'long_name'};
200*b1cdbd2cSJim Jagielski        my $full_name = $directory_item->{'full_source_long_name'};
201*b1cdbd2cSJim Jagielski        # Strip away the leading OfficeMenuFolder part.
202*b1cdbd2cSJim Jagielski        $full_name =~ s/^$office_menu_folder_name\///;
203*b1cdbd2cSJim Jagielski        my $flat_filename = File::Spec->catfile($temporary_destination_path, $unique_name);
204*b1cdbd2cSJim Jagielski        my $dir_path = File::Spec->catfile($destination_path, $full_name);
205*b1cdbd2cSJim Jagielski        my $dir_filename = File::Spec->catfile($dir_path, $long_file_name);
206*b1cdbd2cSJim Jagielski
207*b1cdbd2cSJim Jagielski        if ( ! -d $dir_path)
208*b1cdbd2cSJim Jagielski        {
209*b1cdbd2cSJim Jagielski            File::Path::make_path($dir_path);
210*b1cdbd2cSJim Jagielski        }
211*b1cdbd2cSJim Jagielski
212*b1cdbd2cSJim Jagielski        $installer::logger::Lang->printf("moving %s to %s\n", $flat_filename, $dir_filename);
213*b1cdbd2cSJim Jagielski        File::Copy::move($flat_filename, $dir_filename)
214*b1cdbd2cSJim Jagielski            || die("can not move file ".$flat_filename.":".$!);
215*b1cdbd2cSJim Jagielski
216*b1cdbd2cSJim Jagielski        ++$count;
217*b1cdbd2cSJim Jagielski    }
218*b1cdbd2cSJim Jagielski
219*b1cdbd2cSJim Jagielski    # Cleanup.  Remove the temporary directory.  It should be empty by now.
220*b1cdbd2cSJim Jagielski    rmdir($temporary_destination_path);
221*b1cdbd2cSJim Jagielski}
222*b1cdbd2cSJim Jagielski
223*b1cdbd2cSJim Jagielski
224*b1cdbd2cSJim Jagielski
225*b1cdbd2cSJim Jagielski
226*b1cdbd2cSJim Jagielski=head2 UnpackCabFlat ($cab_filename, $destination_path, $file_table)
227*b1cdbd2cSJim Jagielski
228*b1cdbd2cSJim Jagielski    Unpack the flat file structure of the $cab_filename to $destination_path.
229*b1cdbd2cSJim Jagielski
230*b1cdbd2cSJim Jagielski    In order to detect and handle an incomplete (arborted) previous
231*b1cdbd2cSJim Jagielski    extraction, the cab file is unpacked to a temprorary directory
232*b1cdbd2cSJim Jagielski    that after successful extraction is renamed to $destination_path.
233*b1cdbd2cSJim Jagielski
234*b1cdbd2cSJim Jagielski=cut
235*b1cdbd2cSJim Jagielskisub UnpackCabFlat ($$$)
236*b1cdbd2cSJim Jagielski{
237*b1cdbd2cSJim Jagielski    my ($cab_filename, $destination_path, $file_table) = @_;
238*b1cdbd2cSJim Jagielski
239*b1cdbd2cSJim Jagielski    # Unpack the .cab file to a temporary path (note that
240*b1cdbd2cSJim Jagielski    # $destination_path may alreay bee a temporary path). Using a
241*b1cdbd2cSJim Jagielski    # second one prevents the lengthy flat unpacking to be repeated
242*b1cdbd2cSJim Jagielski    # when another step fails.
243*b1cdbd2cSJim Jagielski
244*b1cdbd2cSJim Jagielski    $installer::logger::Info->printf("unpacking cab file\n");
245*b1cdbd2cSJim Jagielski    File::Path::make_path($destination_path);
246*b1cdbd2cSJim Jagielski    my $windows_cab_filename = installer::patch::Tools::ToEscapedWindowsPath($cab_filename);
247*b1cdbd2cSJim Jagielski    my $windows_destination_path = installer::patch::Tools::ToEscapedWindowsPath($destination_path);
248*b1cdbd2cSJim Jagielski    my $command = join(" ",
249*b1cdbd2cSJim Jagielski        "\"".Get7Zip()."\"",
250*b1cdbd2cSJim Jagielski        "x", "-o".$windows_destination_path,
251*b1cdbd2cSJim Jagielski        $windows_cab_filename,
252*b1cdbd2cSJim Jagielski        "-y");
253*b1cdbd2cSJim Jagielski    open my $cmd, $command."|";
254*b1cdbd2cSJim Jagielski    my $extraction_count = 0;
255*b1cdbd2cSJim Jagielski    my $file_count = $file_table->GetRowCount();
256*b1cdbd2cSJim Jagielski    while (<$cmd>)
257*b1cdbd2cSJim Jagielski    {
258*b1cdbd2cSJim Jagielski        my $message = $_;
259*b1cdbd2cSJim Jagielski        chomp($message);
260*b1cdbd2cSJim Jagielski        ++$extraction_count;
261*b1cdbd2cSJim Jagielski        printf("%4d/%4d  %3.2f%%   \r",
262*b1cdbd2cSJim Jagielski            $extraction_count,
263*b1cdbd2cSJim Jagielski            $file_count,
264*b1cdbd2cSJim Jagielski            $extraction_count*100/$file_count);
265*b1cdbd2cSJim Jagielski    }
266*b1cdbd2cSJim Jagielski    close $cmd;
267*b1cdbd2cSJim Jagielski}
268*b1cdbd2cSJim Jagielski
269*b1cdbd2cSJim Jagielski
270*b1cdbd2cSJim Jagielski
271*b1cdbd2cSJim Jagielski
272*b1cdbd2cSJim Jagielski=head GetUnpackedExePath ($version, $is_current_version, $language, $package_format, $product)
273*b1cdbd2cSJim Jagielski
274*b1cdbd2cSJim Jagielski    Convenience function that returns where a downloadable installation set is extracted to.
275*b1cdbd2cSJim Jagielski
276*b1cdbd2cSJim Jagielski=cut
277*b1cdbd2cSJim Jagielskisub GetUnpackedExePath ($$$$$)
278*b1cdbd2cSJim Jagielski{
279*b1cdbd2cSJim Jagielski    my ($version, $is_current_version, $language, $package_format, $product) = @_;
280*b1cdbd2cSJim Jagielski
281*b1cdbd2cSJim Jagielski    my $path = GetUnpackedPath($version, $is_current_version, $language, $package_format, $product);
282*b1cdbd2cSJim Jagielski    return File::Spec->catfile($path, "unpacked");
283*b1cdbd2cSJim Jagielski}
284*b1cdbd2cSJim Jagielski
285*b1cdbd2cSJim Jagielski
286*b1cdbd2cSJim Jagielski
287*b1cdbd2cSJim Jagielski
288*b1cdbd2cSJim Jagielski=head GetUnpackedCabPath ($version, $is_current_version, $language, $package_format, $product)
289*b1cdbd2cSJim Jagielski
290*b1cdbd2cSJim Jagielski    Convenience function that returns where a cab file is extracted
291*b1cdbd2cSJim Jagielski    (with injected directory structure from the msi file) to.
292*b1cdbd2cSJim Jagielski
293*b1cdbd2cSJim Jagielski=cut
294*b1cdbd2cSJim Jagielskisub GetUnpackedCabPath ($$$$$)
295*b1cdbd2cSJim Jagielski{
296*b1cdbd2cSJim Jagielski    my ($version, $is_current_version, $language, $package_format, $product) = @_;
297*b1cdbd2cSJim Jagielski
298*b1cdbd2cSJim Jagielski    my $path = GetUnpackedPath($version, $is_current_version, $language, $package_format, $product);
299*b1cdbd2cSJim Jagielski    return File::Spec->catfile($path, "unpacked");
300*b1cdbd2cSJim Jagielski}
301*b1cdbd2cSJim Jagielski
302*b1cdbd2cSJim Jagielski
303*b1cdbd2cSJim Jagielski
304*b1cdbd2cSJim Jagielski
305*b1cdbd2cSJim Jagielski=head2 GetUnpackedPath($version, $is_current_version, $language, $package_format, $product)
306*b1cdbd2cSJim Jagielski
307*b1cdbd2cSJim Jagielski    Internal function for creating paths to where archives are unpacked.
308*b1cdbd2cSJim Jagielski
309*b1cdbd2cSJim Jagielski=cut
310*b1cdbd2cSJim Jagielskisub GetUnpackedPath ($$$$$)
311*b1cdbd2cSJim Jagielski{
312*b1cdbd2cSJim Jagielski    my ($version, $is_current_version, $language, $package_format, $product) = @_;
313*b1cdbd2cSJim Jagielski
314*b1cdbd2cSJim Jagielski    return File::Spec->catfile(
315*b1cdbd2cSJim Jagielski        $ENV{'SRC_ROOT'},
316*b1cdbd2cSJim Jagielski        "instsetoo_native",
317*b1cdbd2cSJim Jagielski        $ENV{'INPATH'},
318*b1cdbd2cSJim Jagielski        $product,
319*b1cdbd2cSJim Jagielski        $package_format,
320*b1cdbd2cSJim Jagielski        installer::patch::Version::ArrayToDirectoryName(
321*b1cdbd2cSJim Jagielski            installer::patch::Version::StringToNumberArray($version)),
322*b1cdbd2cSJim Jagielski        installer::languages::get_normalized_language($language));
323*b1cdbd2cSJim Jagielski}
324*b1cdbd2cSJim Jagielski
325*b1cdbd2cSJim Jagielski
326*b1cdbd2cSJim Jagielski
327*b1cdbd2cSJim Jagielski
328*b1cdbd2cSJim Jagielskisub GetMsiFilename ($$)
329*b1cdbd2cSJim Jagielski{
330*b1cdbd2cSJim Jagielski    my ($path, $version) = @_;
331*b1cdbd2cSJim Jagielski
332*b1cdbd2cSJim Jagielski    my $no_dot_version = installer::patch::Version::ArrayToNoDotName(
333*b1cdbd2cSJim Jagielski        installer::patch::Version::StringToNumberArray(
334*b1cdbd2cSJim Jagielski            $version));
335*b1cdbd2cSJim Jagielski    return File::Spec->catfile(
336*b1cdbd2cSJim Jagielski        $path,
337*b1cdbd2cSJim Jagielski        "openoffice" . $no_dot_version . ".msi");
338*b1cdbd2cSJim Jagielski}
339*b1cdbd2cSJim Jagielski
340*b1cdbd2cSJim Jagielski
341*b1cdbd2cSJim Jagielski
342*b1cdbd2cSJim Jagielski
343*b1cdbd2cSJim Jagielskisub GetCabFilename ($$)
344*b1cdbd2cSJim Jagielski{
345*b1cdbd2cSJim Jagielski    my ($path, $version) = @_;
346*b1cdbd2cSJim Jagielski
347*b1cdbd2cSJim Jagielski    return File::Spec->catfile(
348*b1cdbd2cSJim Jagielski        $path,
349*b1cdbd2cSJim Jagielski        "openoffice1.cab");
350*b1cdbd2cSJim Jagielski}
351*b1cdbd2cSJim Jagielski
352*b1cdbd2cSJim Jagielski
353*b1cdbd2cSJim Jagielski
354*b1cdbd2cSJim Jagielski
355*b1cdbd2cSJim Jagielski=head2 Download($language, $release_data, $filename)
356*b1cdbd2cSJim Jagielski
357*b1cdbd2cSJim Jagielski    Download an installation set to $filename.  The URL for the
358*b1cdbd2cSJim Jagielski    download is taken from $release_data, a snippet from the
359*b1cdbd2cSJim Jagielski    instsetoo_native/data/releases.xml file.
360*b1cdbd2cSJim Jagielski
361*b1cdbd2cSJim Jagielski=cut
362*b1cdbd2cSJim Jagielskisub Download ($$$)
363*b1cdbd2cSJim Jagielski{
364*b1cdbd2cSJim Jagielski    my ($language, $release_data, $filename) = @_;
365*b1cdbd2cSJim Jagielski
366*b1cdbd2cSJim Jagielski    my $url = $release_data->{'URL'};
367*b1cdbd2cSJim Jagielski    $release_data->{'URL'} =~ /^(.*)\/([^\/]+)$/;
368*b1cdbd2cSJim Jagielski    my ($location, $basename) = ($1,$2);
369*b1cdbd2cSJim Jagielski
370*b1cdbd2cSJim Jagielski    $installer::logger::Info->printf("downloading %s\n", $basename);
371*b1cdbd2cSJim Jagielski    $installer::logger::Info->printf("    from '%s'\n", $location);
372*b1cdbd2cSJim Jagielski    my $filesize = $release_data->{'file-size'};
373*b1cdbd2cSJim Jagielski    if (defined $filesize)
374*b1cdbd2cSJim Jagielski    {
375*b1cdbd2cSJim Jagielski        $installer::logger::Info->printf("    expected size is %d\n", $filesize);
376*b1cdbd2cSJim Jagielski    }
377*b1cdbd2cSJim Jagielski    else
378*b1cdbd2cSJim Jagielski    {
379*b1cdbd2cSJim Jagielski        $installer::logger::Info->printf("    file size is not yet known\n");
380*b1cdbd2cSJim Jagielski    }
381*b1cdbd2cSJim Jagielski    my $temporary_filename = $filename . ".part";
382*b1cdbd2cSJim Jagielski    my $resume_size = 0;
383*b1cdbd2cSJim Jagielski
384*b1cdbd2cSJim Jagielski    # Prepare checksum.
385*b1cdbd2cSJim Jagielski    my $checksum = undef;
386*b1cdbd2cSJim Jagielski    my $checksum_type = $release_data->{'checksum-type'};
387*b1cdbd2cSJim Jagielski    my $checksum_value = $release_data->{'checksum-value'};
388*b1cdbd2cSJim Jagielski    my $digest = undef;
389*b1cdbd2cSJim Jagielski    if ( ! defined $checksum_value)
390*b1cdbd2cSJim Jagielski    {
391*b1cdbd2cSJim Jagielski        # No checksum available.  Skip test.
392*b1cdbd2cSJim Jagielski    }
393*b1cdbd2cSJim Jagielski    elsif ($checksum_type eq "sha256")
394*b1cdbd2cSJim Jagielski    {
395*b1cdbd2cSJim Jagielski        $digest = Digest->new("SHA-256");
396*b1cdbd2cSJim Jagielski    }
397*b1cdbd2cSJim Jagielski    elsif ($checksum_type eq "md5")
398*b1cdbd2cSJim Jagielski    {
399*b1cdbd2cSJim Jagielski        $digest = Digest->new("md5");
400*b1cdbd2cSJim Jagielski    }
401*b1cdbd2cSJim Jagielski    else
402*b1cdbd2cSJim Jagielski    {
403*b1cdbd2cSJim Jagielski        installer::logger::PrintError(
404*b1cdbd2cSJim Jagielski            "checksum type %s is not supported.  Supported checksum types are: sha256,md5\n",
405*b1cdbd2cSJim Jagielski            $checksum_type);
406*b1cdbd2cSJim Jagielski        return 0;
407*b1cdbd2cSJim Jagielski    }
408*b1cdbd2cSJim Jagielski
409*b1cdbd2cSJim Jagielski    # Download the extension.
410*b1cdbd2cSJim Jagielski    open my $out, ">$temporary_filename";
411*b1cdbd2cSJim Jagielski    binmode($out);
412*b1cdbd2cSJim Jagielski
413*b1cdbd2cSJim Jagielski    my $mode = $|;
414*b1cdbd2cSJim Jagielski    my $handle = select STDOUT;
415*b1cdbd2cSJim Jagielski    $| = 1;
416*b1cdbd2cSJim Jagielski    select $handle;
417*b1cdbd2cSJim Jagielski
418*b1cdbd2cSJim Jagielski    my $agent = LWP::UserAgent->new();
419*b1cdbd2cSJim Jagielski    $agent->timeout(120);
420*b1cdbd2cSJim Jagielski    $agent->show_progress(0);
421*b1cdbd2cSJim Jagielski    my $last_was_redirect = 0;
422*b1cdbd2cSJim Jagielski    my $bytes_read = 0;
423*b1cdbd2cSJim Jagielski    $agent->add_handler('response_redirect'
424*b1cdbd2cSJim Jagielski        => sub{
425*b1cdbd2cSJim Jagielski            $last_was_redirect = 1;
426*b1cdbd2cSJim Jagielski            return;
427*b1cdbd2cSJim Jagielski        });
428*b1cdbd2cSJim Jagielski    $agent->add_handler('response_data'
429*b1cdbd2cSJim Jagielski        => sub{
430*b1cdbd2cSJim Jagielski            if ($last_was_redirect)
431*b1cdbd2cSJim Jagielski            {
432*b1cdbd2cSJim Jagielski                $last_was_redirect = 0;
433*b1cdbd2cSJim Jagielski                # Throw away the data we got so far.
434*b1cdbd2cSJim Jagielski                $digest->reset() if defined $digest;
435*b1cdbd2cSJim Jagielski                close $out;
436*b1cdbd2cSJim Jagielski                open $out, ">$temporary_filename";
437*b1cdbd2cSJim Jagielski                binmode($out);
438*b1cdbd2cSJim Jagielski            }
439*b1cdbd2cSJim Jagielski            my($response,$agent,$h,$data)=@_;
440*b1cdbd2cSJim Jagielski            print $out $data;
441*b1cdbd2cSJim Jagielski            $digest->add($data) if defined $digest;
442*b1cdbd2cSJim Jagielski            $bytes_read += length($data);
443*b1cdbd2cSJim Jagielski            if (defined $filesize)
444*b1cdbd2cSJim Jagielski            {
445*b1cdbd2cSJim Jagielski                printf("read %*d / %d  %d%%  \r",
446*b1cdbd2cSJim Jagielski                    length($filesize),
447*b1cdbd2cSJim Jagielski                    $bytes_read,
448*b1cdbd2cSJim Jagielski                    $filesize,
449*b1cdbd2cSJim Jagielski                    $bytes_read*100/$filesize);
450*b1cdbd2cSJim Jagielski            }
451*b1cdbd2cSJim Jagielski            else
452*b1cdbd2cSJim Jagielski            {
453*b1cdbd2cSJim Jagielski                printf("read %6.2f MB\r", $bytes_read/(1024.0*1024.0));
454*b1cdbd2cSJim Jagielski            }
455*b1cdbd2cSJim Jagielski            });
456*b1cdbd2cSJim Jagielski    my $response;
457*b1cdbd2cSJim Jagielski    if ($resume_size > 0)
458*b1cdbd2cSJim Jagielski    {
459*b1cdbd2cSJim Jagielski        $response = $agent->get($url, 'Range' => "bytes=$resume_size-");
460*b1cdbd2cSJim Jagielski    }
461*b1cdbd2cSJim Jagielski    else
462*b1cdbd2cSJim Jagielski    {
463*b1cdbd2cSJim Jagielski        $response = $agent->get($url);
464*b1cdbd2cSJim Jagielski    }
465*b1cdbd2cSJim Jagielski    close $out;
466*b1cdbd2cSJim Jagielski
467*b1cdbd2cSJim Jagielski    $handle = select STDOUT;
468*b1cdbd2cSJim Jagielski    $| = $mode;
469*b1cdbd2cSJim Jagielski    select $handle;
470*b1cdbd2cSJim Jagielski
471*b1cdbd2cSJim Jagielski    $installer::logger::Info->print("                                        \r");
472*b1cdbd2cSJim Jagielski
473*b1cdbd2cSJim Jagielski    if ($response->is_success())
474*b1cdbd2cSJim Jagielski    {
475*b1cdbd2cSJim Jagielski        if ( ! defined $digest
476*b1cdbd2cSJim Jagielski            || $digest->hexdigest() eq $checksum_value)
477*b1cdbd2cSJim Jagielski        {
478*b1cdbd2cSJim Jagielski            $installer::logger::Info->print("download was successfull\n");
479*b1cdbd2cSJim Jagielski            if ( ! rename($temporary_filename, $filename))
480*b1cdbd2cSJim Jagielski            {
481*b1cdbd2cSJim Jagielski                installer::logger::PrintError("can not rename '%s' to '%s'\n", $temporary_filename, $filename);
482*b1cdbd2cSJim Jagielski                return 0;
483*b1cdbd2cSJim Jagielski            }
484*b1cdbd2cSJim Jagielski            else
485*b1cdbd2cSJim Jagielski            {
486*b1cdbd2cSJim Jagielski                return 1;
487*b1cdbd2cSJim Jagielski            }
488*b1cdbd2cSJim Jagielski        }
489*b1cdbd2cSJim Jagielski        else
490*b1cdbd2cSJim Jagielski        {
491*b1cdbd2cSJim Jagielski            installer::logger::PrintError("%s checksum is wrong\n", $checksum_type);
492*b1cdbd2cSJim Jagielski            return 0;
493*b1cdbd2cSJim Jagielski        }
494*b1cdbd2cSJim Jagielski    }
495*b1cdbd2cSJim Jagielski    else
496*b1cdbd2cSJim Jagielski    {
497*b1cdbd2cSJim Jagielski        installer::logger::PrintError("there was a download error\n");
498*b1cdbd2cSJim Jagielski        return 0;
499*b1cdbd2cSJim Jagielski    }
500*b1cdbd2cSJim Jagielski}
501*b1cdbd2cSJim Jagielski
502*b1cdbd2cSJim Jagielski
503*b1cdbd2cSJim Jagielski
504*b1cdbd2cSJim Jagielski
505*b1cdbd2cSJim Jagielski=head2 ProvideDownloadSet ($version, $language, $package_format)
506*b1cdbd2cSJim Jagielski
507*b1cdbd2cSJim Jagielski    Download an installation set when it is not yet present to
508*b1cdbd2cSJim Jagielski    $ENV{'TARFILE_LOCATION'}.  Verify the downloaded file with the
509*b1cdbd2cSJim Jagielski    checksum that is extracted from the
510*b1cdbd2cSJim Jagielski    instsetoo_native/data/releases.xml file.
511*b1cdbd2cSJim Jagielski
512*b1cdbd2cSJim Jagielski=cut
513*b1cdbd2cSJim Jagielskisub ProvideDownloadSet ($$$)
514*b1cdbd2cSJim Jagielski{
515*b1cdbd2cSJim Jagielski    my ($version, $language, $package_format) = @_;
516*b1cdbd2cSJim Jagielski
517*b1cdbd2cSJim Jagielski    my $release_item = installer::patch::ReleasesList::Instance()->{$version}->{$package_format}->{$language};
518*b1cdbd2cSJim Jagielski    return undef unless defined $release_item;
519*b1cdbd2cSJim Jagielski
520*b1cdbd2cSJim Jagielski    # Get basename of installation set from URL.
521*b1cdbd2cSJim Jagielski    $release_item->{'URL'} =~ /^(.*)\/([^\/]+)$/;
522*b1cdbd2cSJim Jagielski    my ($location, $basename) = ($1,$2);
523*b1cdbd2cSJim Jagielski
524*b1cdbd2cSJim Jagielski    # Is the installation set already present in ext_sources/ ?
525*b1cdbd2cSJim Jagielski    my $need_download = 0;
526*b1cdbd2cSJim Jagielski    my $ext_sources_filename = File::Spec->catfile(
527*b1cdbd2cSJim Jagielski        $ENV{'TARFILE_LOCATION'},
528*b1cdbd2cSJim Jagielski        $basename);
529*b1cdbd2cSJim Jagielski    if ( ! -f $ext_sources_filename)
530*b1cdbd2cSJim Jagielski    {
531*b1cdbd2cSJim Jagielski        $installer::logger::Info->printf("download set is not in ext_sources/ (%s)\n", $ext_sources_filename);
532*b1cdbd2cSJim Jagielski        $need_download = 1;
533*b1cdbd2cSJim Jagielski    }
534*b1cdbd2cSJim Jagielski    else
535*b1cdbd2cSJim Jagielski    {
536*b1cdbd2cSJim Jagielski        $installer::logger::Info->printf("download set exists at '%s'\n", $ext_sources_filename);
537*b1cdbd2cSJim Jagielski        if (defined $release_item->{'checksum-value'}
538*b1cdbd2cSJim Jagielski            && $release_item->{'checksum-type'} eq 'sha256')
539*b1cdbd2cSJim Jagielski        {
540*b1cdbd2cSJim Jagielski            $installer::logger::Info->printf("checking SHA256 checksum\n");
541*b1cdbd2cSJim Jagielski            my $digest = Digest->new("SHA-256");
542*b1cdbd2cSJim Jagielski            open my $in, "<", $ext_sources_filename;
543*b1cdbd2cSJim Jagielski            $digest->addfile($in);
544*b1cdbd2cSJim Jagielski            close $in;
545*b1cdbd2cSJim Jagielski            if ($digest->hexdigest() ne $release_item->{'checksum-value'})
546*b1cdbd2cSJim Jagielski            {
547*b1cdbd2cSJim Jagielski                $installer::logger::Info->printf("    mismatch\n", $ext_sources_filename);
548*b1cdbd2cSJim Jagielski                $need_download = 1;
549*b1cdbd2cSJim Jagielski            }
550*b1cdbd2cSJim Jagielski            else
551*b1cdbd2cSJim Jagielski            {
552*b1cdbd2cSJim Jagielski                $installer::logger::Info->printf("    match\n");
553*b1cdbd2cSJim Jagielski            }
554*b1cdbd2cSJim Jagielski        }
555*b1cdbd2cSJim Jagielski    }
556*b1cdbd2cSJim Jagielski
557*b1cdbd2cSJim Jagielski    if ($need_download)
558*b1cdbd2cSJim Jagielski    {
559*b1cdbd2cSJim Jagielski        if ( ! installer::patch::InstallationSet::Download(
560*b1cdbd2cSJim Jagielski            $language,
561*b1cdbd2cSJim Jagielski            $release_item,
562*b1cdbd2cSJim Jagielski            $ext_sources_filename))
563*b1cdbd2cSJim Jagielski        {
564*b1cdbd2cSJim Jagielski            return 0;
565*b1cdbd2cSJim Jagielski        }
566*b1cdbd2cSJim Jagielski        if ( ! -f $ext_sources_filename)
567*b1cdbd2cSJim Jagielski        {
568*b1cdbd2cSJim Jagielski            $installer::logger::Info->printf("download set could not be downloaded\n");
569*b1cdbd2cSJim Jagielski            return 0;
570*b1cdbd2cSJim Jagielski        }
571*b1cdbd2cSJim Jagielski    }
572*b1cdbd2cSJim Jagielski
573*b1cdbd2cSJim Jagielski    return $ext_sources_filename;
574*b1cdbd2cSJim Jagielski}
575*b1cdbd2cSJim Jagielski
576*b1cdbd2cSJim Jagielski
577*b1cdbd2cSJim Jagielski
578*b1cdbd2cSJim Jagielski
579*b1cdbd2cSJim Jagielskisub ProvideUnpackedExe ($$$$$)
580*b1cdbd2cSJim Jagielski{
581*b1cdbd2cSJim Jagielski    my ($version, $is_current_version, $language, $package_format, $product_name) = @_;
582*b1cdbd2cSJim Jagielski
583*b1cdbd2cSJim Jagielski    # Check if the exe has already been unpacked.
584*b1cdbd2cSJim Jagielski    my $unpacked_exe_path = installer::patch::InstallationSet::GetUnpackedExePath(
585*b1cdbd2cSJim Jagielski        $version,
586*b1cdbd2cSJim Jagielski        $is_current_version,
587*b1cdbd2cSJim Jagielski        $language,
588*b1cdbd2cSJim Jagielski        $package_format,
589*b1cdbd2cSJim Jagielski        $product_name);
590*b1cdbd2cSJim Jagielski    my $unpacked_exe_flag_filename = File::Spec->catfile($unpacked_exe_path, "__exe_is_unpacked");
591*b1cdbd2cSJim Jagielski    my $exe_is_unpacked = -f $unpacked_exe_flag_filename;
592*b1cdbd2cSJim Jagielski
593*b1cdbd2cSJim Jagielski    if ($exe_is_unpacked)
594*b1cdbd2cSJim Jagielski    {
595*b1cdbd2cSJim Jagielski        # Yes, exe has already been unpacked.  There is nothing more to do.
596*b1cdbd2cSJim Jagielski        $installer::logger::Info->printf("downloadable installation set has already been unpacked to\n");
597*b1cdbd2cSJim Jagielski        $installer::logger::Info->printf("    %s\n", $unpacked_exe_path);
598*b1cdbd2cSJim Jagielski        return 1;
599*b1cdbd2cSJim Jagielski    }
600*b1cdbd2cSJim Jagielski    elsif ($is_current_version)
601*b1cdbd2cSJim Jagielski    {
602*b1cdbd2cSJim Jagielski        # For the current version the exe is created from the unpacked
603*b1cdbd2cSJim Jagielski        # content and both are expected to be already present.
604*b1cdbd2cSJim Jagielski
605*b1cdbd2cSJim Jagielski        # In order to have the .cab and its unpacked content in one
606*b1cdbd2cSJim Jagielski        # directory and don't interfere with the creation of regular
607*b1cdbd2cSJim Jagielski        # installation sets, we copy the unpacked .exe into a separate
608*b1cdbd2cSJim Jagielski        # directory.
609*b1cdbd2cSJim Jagielski
610*b1cdbd2cSJim Jagielski        my $original_path = File::Spec->catfile(
611*b1cdbd2cSJim Jagielski            $ENV{'SRC_ROOT'},
612*b1cdbd2cSJim Jagielski            "instsetoo_native",
613*b1cdbd2cSJim Jagielski            $ENV{'INPATH'},
614*b1cdbd2cSJim Jagielski            $product_name,
615*b1cdbd2cSJim Jagielski            $package_format,
616*b1cdbd2cSJim Jagielski            "install",
617*b1cdbd2cSJim Jagielski            $language);
618*b1cdbd2cSJim Jagielski        $installer::logger::Info->printf("creating a copy\n");
619*b1cdbd2cSJim Jagielski        $installer::logger::Info->printf("    of %s\n", $original_path);
620*b1cdbd2cSJim Jagielski        $installer::logger::Info->printf("    at %s\n", $unpacked_exe_path);
621*b1cdbd2cSJim Jagielski        File::Path::make_path($unpacked_exe_path) unless -d $unpacked_exe_path;
622*b1cdbd2cSJim Jagielski	my ($file_count,$directory_count) = CopyRecursive($original_path, $unpacked_exe_path);
623*b1cdbd2cSJim Jagielski	return 0 if ( ! defined $file_count);
624*b1cdbd2cSJim Jagielski        $installer::logger::Info->printf("    copied %d files in %d directories\n",
625*b1cdbd2cSJim Jagielski	    $file_count,
626*b1cdbd2cSJim Jagielski	    $directory_count);
627*b1cdbd2cSJim Jagielski
628*b1cdbd2cSJim Jagielski        installer::patch::Tools::touch($unpacked_exe_flag_filename);
629*b1cdbd2cSJim Jagielski
630*b1cdbd2cSJim Jagielski        return 1;
631*b1cdbd2cSJim Jagielski    }
632*b1cdbd2cSJim Jagielski    else
633*b1cdbd2cSJim Jagielski    {
634*b1cdbd2cSJim Jagielski        # No, we have to unpack the exe.
635*b1cdbd2cSJim Jagielski
636*b1cdbd2cSJim Jagielski        # Provide the exe.
637*b1cdbd2cSJim Jagielski        my $filename = installer::patch::InstallationSet::ProvideDownloadSet(
638*b1cdbd2cSJim Jagielski            $version,
639*b1cdbd2cSJim Jagielski            $language,
640*b1cdbd2cSJim Jagielski            $package_format);
641*b1cdbd2cSJim Jagielski
642*b1cdbd2cSJim Jagielski        # Unpack it.
643*b1cdbd2cSJim Jagielski        if (defined $filename)
644*b1cdbd2cSJim Jagielski        {
645*b1cdbd2cSJim Jagielski            if (installer::patch::InstallationSet::UnpackExe($filename, $unpacked_exe_path))
646*b1cdbd2cSJim Jagielski            {
647*b1cdbd2cSJim Jagielski                $installer::logger::Info->printf("downloadable installation set has been unpacked to\n");
648*b1cdbd2cSJim Jagielski                $installer::logger::Info->printf("    %s\n", $unpacked_exe_path);
649*b1cdbd2cSJim Jagielski
650*b1cdbd2cSJim Jagielski                installer::patch::Tools::touch($unpacked_exe_flag_filename);
651*b1cdbd2cSJim Jagielski
652*b1cdbd2cSJim Jagielski                return 1;
653*b1cdbd2cSJim Jagielski            }
654*b1cdbd2cSJim Jagielski        }
655*b1cdbd2cSJim Jagielski        else
656*b1cdbd2cSJim Jagielski        {
657*b1cdbd2cSJim Jagielski            installer::logger::PrintError("could not provide .exe installation set at '%s'\n", $filename);
658*b1cdbd2cSJim Jagielski        }
659*b1cdbd2cSJim Jagielski    }
660*b1cdbd2cSJim Jagielski
661*b1cdbd2cSJim Jagielski    return 0;
662*b1cdbd2cSJim Jagielski}
663*b1cdbd2cSJim Jagielski
664*b1cdbd2cSJim Jagielski
665*b1cdbd2cSJim Jagielski
666*b1cdbd2cSJim Jagielski
667*b1cdbd2cSJim Jagielskisub CopyRecursive ($$)
668*b1cdbd2cSJim Jagielski{
669*b1cdbd2cSJim Jagielski    my ($source_path, $destination_path) = @_;
670*b1cdbd2cSJim Jagielski
671*b1cdbd2cSJim Jagielski    return (undef,undef) unless -d $source_path;
672*b1cdbd2cSJim Jagielski
673*b1cdbd2cSJim Jagielski    my @todo = ([$source_path, $destination_path]);
674*b1cdbd2cSJim Jagielski    my $file_count = 0;
675*b1cdbd2cSJim Jagielski    my $directory_count = 0;
676*b1cdbd2cSJim Jagielski    while (scalar @todo > 0)
677*b1cdbd2cSJim Jagielski    {
678*b1cdbd2cSJim Jagielski	my ($source,$destination) = @{shift @todo};
679*b1cdbd2cSJim Jagielski
680*b1cdbd2cSJim Jagielski	next if ! -d $source;
681*b1cdbd2cSJim Jagielski	File::Path::make_path($destination);
682*b1cdbd2cSJim Jagielski	++$directory_count;
683*b1cdbd2cSJim Jagielski
684*b1cdbd2cSJim Jagielski	# Read list of files in the current source directory.
685*b1cdbd2cSJim Jagielski	opendir( my $dir, $source);
686*b1cdbd2cSJim Jagielski	my @files = readdir $dir;
687*b1cdbd2cSJim Jagielski	closedir $dir;
688*b1cdbd2cSJim Jagielski
689*b1cdbd2cSJim Jagielski	# Copy all files and push all directories to @todo.
690*b1cdbd2cSJim Jagielski	foreach my $file (@files)
691*b1cdbd2cSJim Jagielski	{
692*b1cdbd2cSJim Jagielski	    next if $file =~ /^\.+$/;
693*b1cdbd2cSJim Jagielski
694*b1cdbd2cSJim Jagielski	    my $source_file = File::Spec->catfile($source, $file);
695*b1cdbd2cSJim Jagielski	    my $destination_file = File::Spec->catfile($destination, $file);
696*b1cdbd2cSJim Jagielski	    if ( -f $source_file)
697*b1cdbd2cSJim Jagielski	    {
698*b1cdbd2cSJim Jagielski		File::Copy::copy($source_file, $destination_file);
699*b1cdbd2cSJim Jagielski		++$file_count;
700*b1cdbd2cSJim Jagielski	    }
701*b1cdbd2cSJim Jagielski	    elsif ( -d $source_file)
702*b1cdbd2cSJim Jagielski	    {
703*b1cdbd2cSJim Jagielski		push @todo, [$source_file, $destination_file];
704*b1cdbd2cSJim Jagielski	    }
705*b1cdbd2cSJim Jagielski	}
706*b1cdbd2cSJim Jagielski    }
707*b1cdbd2cSJim Jagielski
708*b1cdbd2cSJim Jagielski    return ($file_count, $directory_count);
709*b1cdbd2cSJim Jagielski}
710*b1cdbd2cSJim Jagielski
711*b1cdbd2cSJim Jagielski
712*b1cdbd2cSJim Jagielski
713*b1cdbd2cSJim Jagielski
714*b1cdbd2cSJim Jagielskisub CheckLocalCopy ($$$$)
715*b1cdbd2cSJim Jagielski{
716*b1cdbd2cSJim Jagielski    my ($version, $language, $package_format, $product_name) = @_;
717*b1cdbd2cSJim Jagielski
718*b1cdbd2cSJim Jagielski    # Compare creation times of the original .msi and its copy.
719*b1cdbd2cSJim Jagielski
720*b1cdbd2cSJim Jagielski    my $original_path = File::Spec->catfile(
721*b1cdbd2cSJim Jagielski        $ENV{'SRC_ROOT'},
722*b1cdbd2cSJim Jagielski        "instsetoo_native",
723*b1cdbd2cSJim Jagielski        $ENV{'INPATH'},
724*b1cdbd2cSJim Jagielski        $product_name,
725*b1cdbd2cSJim Jagielski        $package_format,
726*b1cdbd2cSJim Jagielski        "install",
727*b1cdbd2cSJim Jagielski        $language);
728*b1cdbd2cSJim Jagielski
729*b1cdbd2cSJim Jagielski    my $copy_path = installer::patch::InstallationSet::GetUnpackedExePath(
730*b1cdbd2cSJim Jagielski        $version,
731*b1cdbd2cSJim Jagielski        1,
732*b1cdbd2cSJim Jagielski        $language,
733*b1cdbd2cSJim Jagielski        $package_format,
734*b1cdbd2cSJim Jagielski        $product_name);
735*b1cdbd2cSJim Jagielski
736*b1cdbd2cSJim Jagielski    my $msi_basename = "openoffice"
737*b1cdbd2cSJim Jagielski        . installer::patch::Version::ArrayToNoDotName(
738*b1cdbd2cSJim Jagielski            installer::patch::Version::StringToNumberArray($version))
739*b1cdbd2cSJim Jagielski        . ".msi";
740*b1cdbd2cSJim Jagielski
741*b1cdbd2cSJim Jagielski    my $original_msi_filename = File::Spec->catfile($original_path, $msi_basename);
742*b1cdbd2cSJim Jagielski    my $copied_msi_filename = File::Spec->catfile($copy_path, $msi_basename);
743*b1cdbd2cSJim Jagielski
744*b1cdbd2cSJim Jagielski    my @original_msi_stats = stat($original_msi_filename);
745*b1cdbd2cSJim Jagielski    my @copied_msi_stats = stat($copied_msi_filename);
746*b1cdbd2cSJim Jagielski    my $original_msi_mtime = $original_msi_stats[9];
747*b1cdbd2cSJim Jagielski    my $copied_msi_mtime = $copied_msi_stats[9];
748*b1cdbd2cSJim Jagielski
749*b1cdbd2cSJim Jagielski    if (defined $original_msi_mtime
750*b1cdbd2cSJim Jagielski        && defined $copied_msi_mtime
751*b1cdbd2cSJim Jagielski        && $original_msi_mtime > $copied_msi_mtime)
752*b1cdbd2cSJim Jagielski    {
753*b1cdbd2cSJim Jagielski        # The installation set is newer than its copy.
754*b1cdbd2cSJim Jagielski        # Remove the copy.
755*b1cdbd2cSJim Jagielski        $installer::logger::Info->printf(
756*b1cdbd2cSJim Jagielski            "removing copy of installation set (version %s) because it is out of date\n",
757*b1cdbd2cSJim Jagielski            $version);
758*b1cdbd2cSJim Jagielski        File::Path::remove_tree($copy_path);
759*b1cdbd2cSJim Jagielski    }
760*b1cdbd2cSJim Jagielski}
761*b1cdbd2cSJim Jagielski
762*b1cdbd2cSJim Jagielski
763*b1cdbd2cSJim Jagielski
764*b1cdbd2cSJim Jagielski
765*b1cdbd2cSJim Jagielski=head2 ProvideUnpackedCab
766*b1cdbd2cSJim Jagielski
767*b1cdbd2cSJim Jagielski    1a. Make sure that a downloadable installation set is present.
768*b1cdbd2cSJim Jagielski    1b. or that a freshly built installation set (packed and unpacked is present)
769*b1cdbd2cSJim Jagielski    2. Unpack the downloadable installation set
770*b1cdbd2cSJim Jagielski    3. Unpack the .cab file.
771*b1cdbd2cSJim Jagielski
772*b1cdbd2cSJim Jagielski    The 'Provide' in the function name means that any step that has
773*b1cdbd2cSJim Jagielski    already been made is not made again.
774*b1cdbd2cSJim Jagielski
775*b1cdbd2cSJim Jagielski=cut
776*b1cdbd2cSJim Jagielskisub ProvideUnpackedCab ($$$$$)
777*b1cdbd2cSJim Jagielski{
778*b1cdbd2cSJim Jagielski    my ($version, $is_current_version, $language, $package_format, $product_name) = @_;
779*b1cdbd2cSJim Jagielski
780*b1cdbd2cSJim Jagielski    if ($is_current_version)
781*b1cdbd2cSJim Jagielski    {
782*b1cdbd2cSJim Jagielski        # For creating patches we maintain a copy of the unpacked .exe.  Make sure that that is updated when
783*b1cdbd2cSJim Jagielski        # a new installation set has been built.
784*b1cdbd2cSJim Jagielski        CheckLocalCopy($version, $language, $package_format, $product_name);
785*b1cdbd2cSJim Jagielski    }
786*b1cdbd2cSJim Jagielski
787*b1cdbd2cSJim Jagielski    # Check if the cab file has already been unpacked.
788*b1cdbd2cSJim Jagielski    my $unpacked_cab_path = installer::patch::InstallationSet::GetUnpackedCabPath(
789*b1cdbd2cSJim Jagielski        $version,
790*b1cdbd2cSJim Jagielski        $is_current_version,
791*b1cdbd2cSJim Jagielski        $language,
792*b1cdbd2cSJim Jagielski        $package_format,
793*b1cdbd2cSJim Jagielski        $product_name);
794*b1cdbd2cSJim Jagielski    my $unpacked_cab_flag_filename = File::Spec->catfile($unpacked_cab_path, "__cab_is_unpacked");
795*b1cdbd2cSJim Jagielski    my $cab_is_unpacked = -f $unpacked_cab_flag_filename;
796*b1cdbd2cSJim Jagielski
797*b1cdbd2cSJim Jagielski    if ($cab_is_unpacked)
798*b1cdbd2cSJim Jagielski    {
799*b1cdbd2cSJim Jagielski        # Yes. Cab was already unpacked. There is nothing more to do.
800*b1cdbd2cSJim Jagielski        $installer::logger::Info->printf("cab has already been unpacked to\n");
801*b1cdbd2cSJim Jagielski        $installer::logger::Info->printf("    %s\n", $unpacked_cab_path);
802*b1cdbd2cSJim Jagielski
803*b1cdbd2cSJim Jagielski        return 1;
804*b1cdbd2cSJim Jagielski    }
805*b1cdbd2cSJim Jagielski    else
806*b1cdbd2cSJim Jagielski    {
807*b1cdbd2cSJim Jagielski        # Make sure that the exe is unpacked and the cab file exists.
808*b1cdbd2cSJim Jagielski        ProvideUnpackedExe($version, $is_current_version, $language, $package_format, $product_name);
809*b1cdbd2cSJim Jagielski
810*b1cdbd2cSJim Jagielski        # Unpack the cab file.
811*b1cdbd2cSJim Jagielski        my $unpacked_exe_path = installer::patch::InstallationSet::GetUnpackedExePath(
812*b1cdbd2cSJim Jagielski                $version,
813*b1cdbd2cSJim Jagielski                $is_current_version,
814*b1cdbd2cSJim Jagielski                $language,
815*b1cdbd2cSJim Jagielski                $package_format,
816*b1cdbd2cSJim Jagielski                $product_name);
817*b1cdbd2cSJim Jagielski        my $msi = new installer::patch::Msi(
818*b1cdbd2cSJim Jagielski                installer::patch::InstallationSet::GetMsiFilename($unpacked_exe_path, $version),
819*b1cdbd2cSJim Jagielski                $version,
820*b1cdbd2cSJim Jagielski                $is_current_version,
821*b1cdbd2cSJim Jagielski                $language,
822*b1cdbd2cSJim Jagielski                $product_name);
823*b1cdbd2cSJim Jagielski
824*b1cdbd2cSJim Jagielski        my $cab_filename = installer::patch::InstallationSet::GetCabFilename(
825*b1cdbd2cSJim Jagielski            $unpacked_exe_path,
826*b1cdbd2cSJim Jagielski            $version);
827*b1cdbd2cSJim Jagielski        if ( ! -f $cab_filename)
828*b1cdbd2cSJim Jagielski        {
829*b1cdbd2cSJim Jagielski             # Cab file does not exist.
830*b1cdbd2cSJim Jagielski            installer::logger::PrintError(
831*b1cdbd2cSJim Jagielski                "could not find .cab file at '%s'.  Extraction of .exe seems to have failed.\n",
832*b1cdbd2cSJim Jagielski                $cab_filename);
833*b1cdbd2cSJim Jagielski            return 0;
834*b1cdbd2cSJim Jagielski        }
835*b1cdbd2cSJim Jagielski
836*b1cdbd2cSJim Jagielski        if (installer::patch::InstallationSet::UnpackCab(
837*b1cdbd2cSJim Jagielski            $cab_filename,
838*b1cdbd2cSJim Jagielski            $msi,
839*b1cdbd2cSJim Jagielski            $unpacked_cab_path))
840*b1cdbd2cSJim Jagielski        {
841*b1cdbd2cSJim Jagielski            $installer::logger::Info->printf("unpacked cab file '%s'\n", $cab_filename);
842*b1cdbd2cSJim Jagielski            $installer::logger::Info->printf("    to '%s'\n", $unpacked_cab_path);
843*b1cdbd2cSJim Jagielski
844*b1cdbd2cSJim Jagielski            installer::patch::Tools::touch($unpacked_cab_flag_filename);
845*b1cdbd2cSJim Jagielski
846*b1cdbd2cSJim Jagielski            return 1;
847*b1cdbd2cSJim Jagielski        }
848*b1cdbd2cSJim Jagielski        else
849*b1cdbd2cSJim Jagielski        {
850*b1cdbd2cSJim Jagielski            return 0;
851*b1cdbd2cSJim Jagielski        }
852*b1cdbd2cSJim Jagielski    }
853*b1cdbd2cSJim Jagielski}
854*b1cdbd2cSJim Jagielski1;
855