File Coverage

blib/lib/IO/Compress/Zip.pm
Criterion Covered Total %
statement 359 424 84.6
branch 106 180 58.8
condition 29 63 46.0
subroutine 32 35 91.4
pod 2 19 10.5
total 528 721 73.2


line stmt bran cond sub pod time code
1             package IO::Compress::Zip ;
2              
3 47     47   90441 use strict ;
  47         71  
  47         1423  
4 47     47   222 use warnings;
  47         74  
  47         1661  
5 47     47   14449 use bytes;
  47         16169  
  47         214  
6              
7 47     47   18330 use IO::Compress::Base::Common 2.223 qw(:Status );
  47         798  
  47         5947  
8 47     47   23127 use IO::Compress::RawDeflate 2.223 ();
  47         1024  
  47         1610  
9 47     47   211 use IO::Compress::Adapter::Deflate 2.223 ;
  47         439  
  47         7887  
10 47     47   18511 use IO::Compress::Adapter::Identity 2.223 ;
  47         819  
  47         1432  
11 47     47   15610 use IO::Compress::Zlib::Extra 2.223 ;
  47         644  
  47         1347  
12 47     47   14893 use IO::Compress::Zip::Constants 2.223 ;
  47         710  
  47         7884  
13              
14 47     47   247 use File::Spec();
  47         69  
  47         654  
15 47     47   153 use Config;
  47         1556  
  47         1993  
16              
17 47     47   173 use Compress::Raw::Zlib 2.222 ();
  47         550  
  47         10469  
18              
19             BEGIN
20             {
21 47     47   106 eval { require IO::Compress::Adapter::Bzip2 ;
  47         19008  
22 47         850 IO::Compress::Adapter::Bzip2->VERSION( 2.218 );
23 47         20242 require IO::Compress::Bzip2 ;
24 47         946 IO::Compress::Bzip2->VERSION( 2.218 );
25             } ;
26              
27 47         75 eval { require IO::Compress::Adapter::Lzma ;
  47         5492  
28 0         0 IO::Compress::Adapter::Lzma->VERSION( 2.217 );
29 0         0 require IO::Compress::Lzma ;
30 0         0 IO::Compress::Lzma->VERSION( 2.217 );
31             } ;
32              
33 47         417 eval { require IO::Compress::Adapter::Xz ;
  47         3628  
34 0         0 IO::Compress::Adapter::Xz->VERSION( 2.217 );
35 0         0 require IO::Compress::Xz ;
36 0         0 IO::Compress::Xz->VERSION( 2.217 );
37             } ;
38 47         249 eval { require IO::Compress::Adapter::Zstd ;
  47         141226  
39 0         0 IO::Compress::Adapter::Zstd->VERSION( 2.217 );
40 0         0 require IO::Compress::Zstd ;
41 0         0 IO::Compress::Zstd->VERSION( 2.217 );
42             } ;
43             }
44              
45              
46             require Exporter ;
47              
48             our ($VERSION, @ISA, @EXPORT_OK, %EXPORT_TAGS, %DEFLATE_CONSTANTS, $ZipError);
49              
50             $VERSION = '2.223';
51             $ZipError = '';
52              
53             @ISA = qw(IO::Compress::RawDeflate Exporter);
54             @EXPORT_OK = qw( $ZipError zip ) ;
55             %EXPORT_TAGS = %IO::Compress::RawDeflate::DEFLATE_CONSTANTS ;
56              
57             my @zip_methods = qw( ZIP_CM_STORE ZIP_CM_DEFLATE ZIP_CM_BZIP2 ZIP_CM_LZMA ZIP_CM_XZ ZIP_CM_ZSTD ) ;
58             $EXPORT_TAGS{all} = [ defined $EXPORT_TAGS{all} ? @{ $EXPORT_TAGS{all} } : (), @EXPORT_OK, @zip_methods ] ;
59              
60             $EXPORT_TAGS{zip_method} = [ @zip_methods ];
61             push @{ $EXPORT_TAGS{constants} }, @zip_methods ;
62              
63             Exporter::export_ok_tags('all');
64              
65             sub new
66             {
67 151     151 1 673858 my $class = shift ;
68              
69 151         599 my $obj = IO::Compress::Base::Common::createSelfTiedObject($class, \$ZipError);
70 151         574 $obj->_create(undef, @_);
71              
72             }
73              
74             sub zip
75             {
76 225     225 1 9446924 my $obj = IO::Compress::Base::Common::createSelfTiedObject(undef, \$ZipError);
77 225         790 return $obj->_def(@_);
78             }
79              
80             sub isMethodAvailable
81             {
82 3     3 0 137 my $method = shift;
83              
84             # Store & Deflate are always available
85 3 100 100     16 return 1
86             if $method == ZIP_CM_STORE || $method == ZIP_CM_DEFLATE ;
87              
88             return 1
89             if $method == ZIP_CM_BZIP2 &&
90             defined $IO::Compress::Adapter::Bzip2::VERSION &&
91 1 0 33     3 defined &{ "IO::Compress::Adapter::Bzip2::mkRawZipCompObject" };
  0   33     0  
92              
93             return 1
94             if $method == ZIP_CM_LZMA &&
95             defined $IO::Compress::Adapter::Lzma::VERSION &&
96 1 0 33     4 defined &{ "IO::Compress::Adapter::Lzma::mkRawZipCompObject" };
  0   33     0  
97              
98             return 1
99             if $method == ZIP_CM_XZ &&
100             defined $IO::Compress::Adapter::Xz::VERSION &&
101 1 0 33     3 defined &{ "IO::Compress::Adapter::Xz::mkRawZipCompObject" };
  0   33     0  
102              
103             return 1
104             if $method == ZIP_CM_ZSTD &&
105             defined $IO::Compress::Adapter::ZSTD::VERSION &&
106 1 0 33     3 defined &{ "IO::Compress::Adapter::ZSTD::mkRawZipCompObject" };
  0   33     0  
107              
108 1         3 return 0;
109             }
110              
111             sub beforePayload
112             {
113 411     411 0 493 my $self = shift ;
114              
115 411 50       1003 if (*$self->{ZipData}{Sparse} ) {
116 0         0 my $inc = 1024 * 100 ;
117 0         0 my $NULLS = ("\x00" x $inc) ;
118 0         0 my $sparse = *$self->{ZipData}{Sparse} ;
119 0         0 *$self->{CompSize}->add( $sparse );
120 0         0 *$self->{UnCompSize}->add( $sparse );
121              
122 0         0 *$self->{FH}->seek($sparse, IO::Handle::SEEK_CUR);
123              
124             *$self->{ZipData}{CRC32} = Compress::Raw::Zlib::crc32($NULLS, *$self->{ZipData}{CRC32})
125 0         0 for 1 .. int $sparse / $inc;
126             *$self->{ZipData}{CRC32} = Compress::Raw::Zlib::crc32(substr($NULLS, 0, $sparse % $inc),
127             *$self->{ZipData}{CRC32})
128 0 0       0 if $sparse % $inc;
129             }
130             }
131              
132             sub mkComp
133             {
134 413     413 0 490 my $self = shift ;
135 413         511 my $got = shift ;
136              
137 413         556 my ($obj, $errstr, $errno) ;
138              
139 413 100       1348 if (*$self->{ZipData}{Method} == ZIP_CM_STORE) {
    100          
    50          
    0          
    0          
    0          
140 36         172 ($obj, $errstr, $errno) = IO::Compress::Adapter::Identity::mkCompObject(
141             $got->getValue('level'),
142             $got->getValue('strategy')
143             );
144 36         165 *$self->{ZipData}{CRC32} = Compress::Raw::Zlib::crc32(undef);
145             }
146             elsif (*$self->{ZipData}{Method} == ZIP_CM_DEFLATE) {
147 356         683 ($obj, $errstr, $errno) = IO::Compress::Adapter::Deflate::mkCompObject(
148             $got->getValue('crc32'),
149             $got->getValue('adler32'),
150             $got->getValue('level'),
151             $got->getValue('strategy')
152             );
153             }
154             elsif (*$self->{ZipData}{Method} == ZIP_CM_BZIP2) {
155 21         42 ($obj, $errstr, $errno) = IO::Compress::Adapter::Bzip2::mkCompObject(
156             $got->getValue('blocksize100k'),
157             $got->getValue('workfactor'),
158             $got->getValue('verbosity')
159             );
160 21         100 *$self->{ZipData}{CRC32} = Compress::Raw::Zlib::crc32(undef);
161             }
162             elsif (*$self->{ZipData}{Method} == ZIP_CM_LZMA) {
163 0         0 ($obj, $errstr, $errno) = IO::Compress::Adapter::Lzma::mkRawZipCompObject($got->getValue('preset'),
164             $got->getValue('extreme'),
165             );
166 0         0 *$self->{ZipData}{CRC32} = Compress::Raw::Zlib::crc32(undef);
167             }
168             elsif (*$self->{ZipData}{Method} == ZIP_CM_XZ) {
169 0         0 ($obj, $errstr, $errno) = IO::Compress::Adapter::Xz::mkCompObject($got->getValue('preset'),
170             $got->getValue('extreme'),
171             0
172             );
173 0         0 *$self->{ZipData}{CRC32} = Compress::Raw::Zlib::crc32(undef);
174             }
175             elsif (*$self->{ZipData}{Method} == ZIP_CM_ZSTD) {
176 0 0       0 ($obj, $errstr, $errno) = IO::Compress::Adapter::Zstd::mkCompObject(defined $got->getValue('level') ? $got->getValue('level') : 3,
177             );
178 0         0 *$self->{ZipData}{CRC32} = Compress::Raw::Zlib::crc32(undef);
179             }
180              
181 413 50       931 return $self->saveErrorString(undef, $errstr, $errno)
182             if ! defined $obj;
183              
184 413 100       1237 if (! defined *$self->{ZipData}{SizesOffset}) {
185 342         670 *$self->{ZipData}{SizesOffset} = 0;
186 342         1772 *$self->{ZipData}{Offset} = U64->new();
187             }
188              
189             *$self->{ZipData}{AnyZip64} = 0
190 413 100       1078 if ! defined *$self->{ZipData}{AnyZip64} ;
191              
192 413         2682 return $obj;
193             }
194              
195             sub reset
196             {
197 0     0 0 0 my $self = shift ;
198              
199 0         0 *$self->{Compress}->reset();
200 0         0 *$self->{ZipData}{CRC32} = Compress::Raw::Zlib::crc32('');
201              
202 0         0 return STATUS_OK;
203             }
204              
205             sub filterUncompressed
206             {
207 393     393 0 493 my $self = shift ;
208              
209 393 100       896 if (*$self->{ZipData}{Method} == ZIP_CM_DEFLATE) {
210 341         1141 *$self->{ZipData}{CRC32} = *$self->{Compress}->crc32();
211             }
212             else {
213 52         59 *$self->{ZipData}{CRC32} = Compress::Raw::Zlib::crc32(${$_[0]}, *$self->{ZipData}{CRC32});
  52         307  
214              
215             }
216             }
217              
218             sub canonicalName
219             {
220             # This sub is derived from Archive::Zip::_asZipDirName
221              
222             # Return the normalized name as used in a zip file (path
223             # separators become slashes, etc.).
224             # Will translate internal slashes in path components (i.e. on Macs) to
225             # underscores. Discards volume names.
226             # When $forceDir is set, returns paths with trailing slashes
227             #
228             # input output
229             # . '.'
230             # ./a a
231             # ./a/b a/b
232             # ./a/b/ a/b
233             # a/b/ a/b
234             # /a/b/ a/b
235             # c:\a\b\c.doc a/b/c.doc # on Windows
236             # "i/o maps:whatever" i_o maps/whatever # on Macs
237              
238 2     2 0 5 my $name = shift;
239 2         2 my $forceDir = shift ;
240              
241 2         42 my ( $volume, $directories, $file ) =
242             File::Spec->splitpath( File::Spec->canonpath($name), $forceDir );
243              
244 2         13 my @dirs = map { $_ =~ s{/}{_}g; $_ }
  6         9  
  6         11  
245             File::Spec->splitdir($directories);
246              
247 2 50       6 if ( @dirs > 0 ) { pop (@dirs) if $dirs[-1] eq '' } # remove empty component
  2 50       4  
248 2 50       6 push @dirs, defined($file) ? $file : '' ;
249              
250 2         5 my $normalised_path = join '/', @dirs;
251              
252             # Leading directory separators should not be stored in zip archives.
253             # Example:
254             # C:\a\b\c\ a/b/c
255             # C:\a\b\c.txt a/b/c.txt
256             # /a/b/c/ a/b/c
257             # /a/b/c.txt a/b/c.txt
258 2         4 $normalised_path =~ s{^/}{}; # remove leading separator
259              
260 2         6 return $normalised_path;
261             }
262              
263              
264             sub mkHeader
265             {
266 412     412 0 618 my $self = shift;
267 412         573 my $param = shift ;
268              
269 412         1189 *$self->{ZipData}{LocalHdrOffset} = U64::clone(*$self->{ZipData}{Offset});
270              
271 412         602 my $comment = '';
272 412         1000 $comment = $param->valueOrDefault('comment') ;
273              
274 412         536 my $filename = '';
275 412         663 $filename = $param->valueOrDefault('name') ;
276              
277 412 100 100     1148 $filename = canonicalName($filename)
278             if length $filename && $param->getValue('canonicalname') ;
279              
280 412 100       941 if (defined *$self->{ZipData}{FilterName} ) {
281 3         7 local *_ = \$filename ;
282 3         4 &{ *$self->{ZipData}{FilterName} }() ;
  3         11  
283             }
284              
285 412 100 66     863 if ( $param->getValue('efs') && $] >= 5.008004) {
286 5 50       10 if (length $filename) {
287 5 100       237 utf8::downgrade($filename, 1)
288             or Carp::croak "Wide character in zip filename";
289             }
290              
291 4 50       8 if (length $comment) {
292 0 0       0 utf8::downgrade($comment, 1)
293             or Carp::croak "Wide character in zip comment";
294             }
295             }
296              
297 411         565 my $hdr = '';
298              
299 411         687 my $time = _unixToDosTime($param->getValue('time'));
300              
301 411         559 my $extra = '';
302 411         452 my $ctlExtra = '';
303 411         541 my $empty = 0;
304 411         977 my $osCode = $param->getValue('os_code') ;
305 411         647 my $extFileAttr = 0 ;
306              
307             # This code assumes Unix.
308             # TODO - revisit this
309 411 50       1066 $extFileAttr = 0100644 << 16
310             if $osCode == ZIP_OS_CODE_UNIX ;
311              
312 411 100       1193 if (*$self->{ZipData}{Zip64}) {
313 26         32 $empty = IO::Compress::Base::Common::MAX32;
314              
315 26         36 my $x = '';
316 26         37 $x .= pack "V V", 0, 0 ; # uncompressedLength
317 26         35 $x .= pack "V V", 0, 0 ; # compressedLength
318              
319             # Zip64 needs to be first in extra field to workaround a Windows Explorer Bug
320             # See http://www.info-zip.org/phpBB3/viewtopic.php?f=3&t=440 for details
321 26         87 $extra .= IO::Compress::Zlib::Extra::mkSubField(ZIP_EXTRA_ID_ZIP64, $x);
322             }
323              
324 411 50       807 if (! $param->getValue('minimal')) {
325 411 100       764 if ($param->parsed('mtime'))
326             {
327 113         212 $extra .= mkExtendedTime($param->getValue('mtime'),
328             $param->getValue('atime'),
329             $param->getValue('ctime'));
330              
331 113         242 $ctlExtra .= mkExtendedTime($param->getValue('mtime'));
332             }
333              
334 411 50       882 if ( $osCode == ZIP_OS_CODE_UNIX )
335             {
336 411 100       662 if ( $param->getValue('want_exunixn') )
337             {
338 113         121 my $ux3 = mkUnixNExtra( @{ $param->getValue('want_exunixn') });
  113         172  
339 113         170 $extra .= $ux3;
340 113         208 $ctlExtra .= $ux3;
341             }
342              
343 411 50       806 if ( $param->getValue('exunix2') )
344             {
345 0         0 $extra .= mkUnix2Extra( @{ $param->getValue('exunix2') });
  0         0  
346 0         0 $ctlExtra .= mkUnix2Extra();
347             }
348             }
349              
350 411 50       682 $extFileAttr = $param->getValue('extattr')
351             if defined $param->getValue('extattr') ;
352              
353 411 50       640 $extra .= $param->getValue('extrafieldlocal')
354             if defined $param->getValue('extrafieldlocal');
355              
356 411 50       719 $ctlExtra .= $param->getValue('extrafieldcentral')
357             if defined $param->getValue('extrafieldcentral');
358             }
359              
360 411         762 my $method = *$self->{ZipData}{Method} ;
361 411         586 my $gpFlag = 0 ;
362             $gpFlag |= ZIP_GP_FLAG_STREAMING_MASK
363 411 100       907 if *$self->{ZipData}{Stream} ;
364              
365 411 50       676 $gpFlag |= ZIP_GP_FLAG_LZMA_EOS_PRESENT
366             if $method == ZIP_CM_LZMA ;
367              
368 411 50 33     744 $gpFlag |= ZIP_GP_FLAG_LANGUAGE_ENCODING
      66        
369             if $param->getValue('efs') && (length($filename) || length($comment));
370              
371 411         797 my $version = $ZIP_CM_MIN_VERSIONS{$method};
372             $version = ZIP64_MIN_VERSION
373 411 100 100     1606 if ZIP64_MIN_VERSION > $version && *$self->{ZipData}{Zip64};
374              
375 411         674 my $madeBy = ($param->getValue('os_code') << 8) + $version;
376 411         495 my $extract = $version;
377              
378 411         726 *$self->{ZipData}{Version} = $version;
379 411         631 *$self->{ZipData}{MadeBy} = $madeBy;
380              
381 411         568 my $ifa = 0;
382 411 100       694 $ifa |= ZIP_IFA_TEXT_MASK
383             if $param->getValue('textflag');
384              
385 411         753 $hdr .= pack "V", ZIP_LOCAL_HDR_SIG ; # signature
386 411         1085 $hdr .= pack 'v', $extract ; # extract Version & OS
387 411         548 $hdr .= pack 'v', $gpFlag ; # general purpose flag (set streaming mode)
388 411         533 $hdr .= pack 'v', $method ; # compression method (deflate)
389 411         606 $hdr .= pack 'V', $time ; # last mod date/time
390 411         702 $hdr .= pack 'V', 0 ; # crc32 - 0 when streaming
391 411         568 $hdr .= pack 'V', $empty ; # compressed length - 0 when streaming
392 411         577 $hdr .= pack 'V', $empty ; # uncompressed length - 0 when streaming
393 411         891 $hdr .= pack 'v', length $filename ; # filename length
394 411         534 $hdr .= pack 'v', length $extra ; # extra length
395              
396 411         517 $hdr .= $filename ;
397              
398             # Remember the offset for the compressed & uncompressed lengths in the
399             # local header.
400 411 100       837 if (*$self->{ZipData}{Zip64}) {
401             *$self->{ZipData}{SizesOffset} = *$self->{ZipData}{Offset}->get64bit()
402 26         63 + length($hdr) + 4 ;
403             }
404             else {
405             *$self->{ZipData}{SizesOffset} = *$self->{ZipData}{Offset}->get64bit()
406 385         974 + 18;
407             }
408              
409 411         683 $hdr .= $extra ;
410              
411              
412 411         610 my $ctl = '';
413              
414 411         582 $ctl .= pack "V", ZIP_CENTRAL_HDR_SIG ; # signature
415 411         561 $ctl .= pack 'v', $madeBy ; # version made by
416 411         511 $ctl .= pack 'v', $extract ; # extract Version
417 411         524 $ctl .= pack 'v', $gpFlag ; # general purpose flag (streaming mode)
418 411         558 $ctl .= pack 'v', $method ; # compression method (deflate)
419 411         637 $ctl .= pack 'V', $time ; # last mod date/time
420 411         471 $ctl .= pack 'V', 0 ; # crc32
421 411         656 $ctl .= pack 'V', $empty ; # compressed length
422 411         619 $ctl .= pack 'V', $empty ; # uncompressed length
423 411         659 $ctl .= pack 'v', length $filename ; # filename length
424              
425 411         1061 *$self->{ZipData}{ExtraOffset} = length $ctl;
426 411         698 *$self->{ZipData}{ExtraSize} = length $ctlExtra ;
427              
428 411         685 $ctl .= pack 'v', length $ctlExtra ; # extra length
429 411         577 $ctl .= pack 'v', length $comment ; # file comment length
430 411         530 $ctl .= pack 'v', 0 ; # disk number start
431 411         495 $ctl .= pack 'v', $ifa ; # internal file attributes
432 411         579 $ctl .= pack 'V', $extFileAttr ; # external file attributes
433              
434             # offset to local hdr
435 411 50       980 if (*$self->{ZipData}{LocalHdrOffset}->is64bit() ) {
436 0         0 $ctl .= pack 'V', IO::Compress::Base::Common::MAX32 ;
437             }
438             else {
439 411         873 $ctl .= *$self->{ZipData}{LocalHdrOffset}->getPacked_V32() ;
440             }
441              
442 411         602 $ctl .= $filename ;
443              
444 411         1184 *$self->{ZipData}{Offset}->add32(length $hdr) ;
445              
446 411         1084 *$self->{ZipData}{CentralHeader} = [ $ctl, $ctlExtra, $comment];
447              
448 411         1313 return $hdr;
449             }
450              
451             sub mkTrailer
452             {
453 411     411 0 563 my $self = shift ;
454              
455 411         487 my $crc32 ;
456 411 100       997 if (*$self->{ZipData}{Method} == ZIP_CM_DEFLATE) {
457 354         955 $crc32 = pack "V", *$self->{Compress}->crc32();
458             }
459             else {
460 57         171 $crc32 = pack "V", *$self->{ZipData}{CRC32};
461             }
462              
463 411         614 my ($ctl, $ctlExtra, $comment) = @{ *$self->{ZipData}{CentralHeader} };
  411         1038  
464              
465 411         485 my $sizes ;
466 411 100       860 if (! *$self->{ZipData}{Zip64}) {
467 385         1144 $sizes .= *$self->{CompSize}->getPacked_V32() ; # Compressed size
468 385         851 $sizes .= *$self->{UnCompSize}->getPacked_V32() ; # Uncompressed size
469             }
470             else {
471 26         70 $sizes .= *$self->{CompSize}->getPacked_V64() ; # Compressed size
472 26         50 $sizes .= *$self->{UnCompSize}->getPacked_V64() ; # Uncompressed size
473             }
474              
475 411         670 my $data = $crc32 . $sizes ;
476              
477 411         942 my $xtrasize = *$self->{UnCompSize}->getPacked_V64() ; # Uncompressed size
478 411         743 $xtrasize .= *$self->{CompSize}->getPacked_V64() ; # Compressed size
479              
480 411         540 my $hdr = '';
481              
482 411 100       824 if (*$self->{ZipData}{Stream}) {
483 346         526 $hdr = pack "V", ZIP_DATA_HDR_SIG ; # signature
484 346         514 $hdr .= $data ;
485             }
486             else {
487 65 50       179 $self->writeAt(*$self->{ZipData}{LocalHdrOffset}->get64bit() + 14, $crc32)
488             or return undef;
489             $self->writeAt(*$self->{ZipData}{SizesOffset},
490 65 100       272 *$self->{ZipData}{Zip64} ? $xtrasize : $sizes)
    50          
491             or return undef;
492             }
493              
494             # Central Header Record/Zip64 extended field
495              
496 411         909 substr($ctl, 16, length $crc32) = $crc32 ;
497              
498 411         579 my $zip64Payload = '';
499              
500             # uncompressed length - only set zip64 if needed
501 411 50       935 if (*$self->{UnCompSize}->isAlmost64bit()) { # || *$self->{ZipData}{Zip64}) {
502 0         0 $zip64Payload .= *$self->{UnCompSize}->getPacked_V64() ;
503             } else {
504 411         832 substr($ctl, 24, 4) = *$self->{UnCompSize}->getPacked_V32() ;
505             }
506              
507             # compressed length - only set zip64 if needed
508 411 50       821 if (*$self->{CompSize}->isAlmost64bit()) { # || *$self->{ZipData}{Zip64}) {
509 0         0 $zip64Payload .= *$self->{CompSize}->getPacked_V64() ;
510             } else {
511 411         704 substr($ctl, 20, 4) = *$self->{CompSize}->getPacked_V32() ;
512             }
513              
514             # Local Header offset
515             $zip64Payload .= *$self->{ZipData}{LocalHdrOffset}->getPacked_V64()
516 411 50       991 if *$self->{ZipData}{LocalHdrOffset}->is64bit() ;
517              
518             # disk no - always zero, so don't need to include it.
519             #$zip64Payload .= pack "V", 0 ;
520              
521 411         617 my $zip64Xtra = '';
522              
523 411 50       724 if (length $zip64Payload) {
524 0         0 $zip64Xtra = IO::Compress::Zlib::Extra::mkSubField(ZIP_EXTRA_ID_ZIP64, $zip64Payload);
525              
526             substr($ctl, *$self->{ZipData}{ExtraOffset}, 2) =
527 0         0 pack 'v', *$self->{ZipData}{ExtraSize} + length $zip64Xtra;
528              
529 0         0 *$self->{ZipData}{AnyZip64} = 1;
530             }
531              
532             # Zip64 needs to be first in extra field to workaround a Windows Explorer Bug
533             # See http://www.info-zip.org/phpBB3/viewtopic.php?f=3&t=440 for details
534 411         697 $ctl .= $zip64Xtra . $ctlExtra . $comment;
535              
536 411         1112 *$self->{ZipData}{Offset}->add32(length($hdr));
537 411         1089 *$self->{ZipData}{Offset}->add( *$self->{CompSize} );
538 411         494 push @{ *$self->{ZipData}{CentralDir} }, $ctl ;
  411         1207  
539              
540 411         956 return $hdr;
541             }
542              
543             sub mkFinalTrailer
544             {
545 345     345 0 443 my $self = shift ;
546              
547 345         414 my $comment = '';
548 345         567 $comment = *$self->{ZipData}{ZipComment} ;
549              
550 345         1006 my $cd_offset = *$self->{ZipData}{Offset}->get32bit() ; # offset to start central dir
551              
552 345         397 my $entries = @{ *$self->{ZipData}{CentralDir} };
  345         552  
553              
554             *$self->{ZipData}{AnyZip64} = 1
555 345 50 33     852 if *$self->{ZipData}{Offset}->is64bit || $entries >= 0xFFFF ;
556              
557 345         471 my $cd = join '', @{ *$self->{ZipData}{CentralDir} };
  345         885  
558 345         451 my $cd_len = length $cd ;
559              
560 345         443 my $z64e = '';
561              
562 345 100       769 if ( *$self->{ZipData}{AnyZip64} ) {
563              
564 21         27 my $v = *$self->{ZipData}{Version} ;
565 21         31 my $mb = *$self->{ZipData}{MadeBy} ;
566 21         35 $z64e .= pack 'v', $mb ; # Version made by
567 21         35 $z64e .= pack 'v', $v ; # Version to extract
568 21         22 $z64e .= pack 'V', 0 ; # number of disk
569 21         20 $z64e .= pack 'V', 0 ; # number of disk with central dir
570 21         36 $z64e .= U64::pack_V64 $entries ; # entries in central dir on this disk
571 21         33 $z64e .= U64::pack_V64 $entries ; # entries in central dir
572 21         29 $z64e .= U64::pack_V64 $cd_len ; # size of central dir
573 21         46 $z64e .= *$self->{ZipData}{Offset}->getPacked_V64() ; # offset to start central dir
574             $z64e .= *$self->{ZipData}{extrafieldzip64} # optional extra field
575 21 50       47 if defined *$self->{ZipData}{extrafieldzip64} ;
576              
577 21         36 $z64e = pack("V", ZIP64_END_CENTRAL_REC_HDR_SIG) # signature
578             . U64::pack_V64(length $z64e)
579             . $z64e ;
580              
581 21         46 *$self->{ZipData}{Offset}->add32(length $cd) ;
582              
583 21         25 $z64e .= pack "V", ZIP64_END_CENTRAL_LOC_HDR_SIG; # signature
584 21         22 $z64e .= pack 'V', 0 ; # number of disk with central dir
585 21         45 $z64e .= *$self->{ZipData}{Offset}->getPacked_V64() ; # offset to end zip64 central dir
586 21         35 $z64e .= pack 'V', 1 ; # Total number of disks
587              
588 21         27 $cd_offset = IO::Compress::Base::Common::MAX32 ;
589 21 50       39 $cd_len = IO::Compress::Base::Common::MAX32 if IO::Compress::Base::Common::isGeMax32 $cd_len ;
590 21 50       35 $entries = 0xFFFF if $entries >= 0xFFFF ;
591             }
592              
593 345         2058 my $ecd = '';
594 345         447 $ecd .= pack "V", ZIP_END_CENTRAL_HDR_SIG ; # signature
595 345         400 $ecd .= pack 'v', 0 ; # number of disk
596 345         399 $ecd .= pack 'v', 0 ; # number of disk with central dir
597 345         615 $ecd .= pack 'v', $entries ; # entries in central dir on this disk
598 345         546 $ecd .= pack 'v', $entries ; # entries in central dir
599 345         529 $ecd .= pack 'V', $cd_len ; # size of central dir
600 345         501 $ecd .= pack 'V', $cd_offset ; # offset to start central dir
601 345         541 $ecd .= pack 'v', length $comment ; # zipfile comment length
602 345         385 $ecd .= $comment;
603              
604 345         1031 return $cd . $z64e . $ecd ;
605             }
606              
607             sub ckParams
608             {
609 414     414 0 567 my $self = shift ;
610 414         552 my $got = shift;
611              
612 414         1101 $got->setValue('crc32' => 1);
613              
614 414 100       713 if (! $got->parsed('time') ) {
615             # Modification time defaults to now.
616 325         669 $got->setValue('time' => time) ;
617             }
618              
619 414 50       669 if ($got->parsed('extime') ) {
620 0         0 my $timeRef = $got->getValue('extime');
621 0 0       0 if ( defined $timeRef) {
622 0 0 0     0 return $self->saveErrorString(undef, "exTime not a 3-element array ref")
623             if ref $timeRef ne 'ARRAY' || @$timeRef != 3;
624             }
625              
626 0         0 $got->setValue("mtime", $timeRef->[1]);
627 0         0 $got->setValue("atime", $timeRef->[0]);
628 0         0 $got->setValue("ctime", $timeRef->[2]);
629             }
630              
631             # Unix2/3 Extended Attribute
632 414         684 for my $name (qw(exunix2 exunixn))
633             {
634 828 50       1143 if ($got->parsed($name) ) {
635 0         0 my $idRef = $got->getValue($name);
636 0 0       0 if ( defined $idRef) {
637 0 0 0     0 return $self->saveErrorString(undef, "$name not a 2-element array ref")
638             if ref $idRef ne 'ARRAY' || @$idRef != 2;
639             }
640              
641 0         0 $got->setValue("uid", $idRef->[0]);
642 0         0 $got->setValue("gid", $idRef->[1]);
643 0         0 $got->setValue("want_$name", $idRef);
644             }
645             }
646              
647 414 100 66     868 *$self->{ZipData}{AnyZip64} = 1
648             if $got->getValue('zip64') || $got->getValue('extrafieldzip64') ;
649 414         739 *$self->{ZipData}{Zip64} = $got->getValue('zip64');
650 414         758 *$self->{ZipData}{Stream} = $got->getValue('stream');
651              
652 414         675 my $method = $got->getValue('method');
653             return $self->saveErrorString(undef, "Unknown Method '$method'")
654 414 50       1138 if ! defined $ZIP_CM_MIN_VERSIONS{$method};
655              
656 414 50 66     1103 return $self->saveErrorString(undef, "Bzip2 not available")
657             if $method == ZIP_CM_BZIP2 and
658             ! defined $IO::Compress::Adapter::Bzip2::VERSION;
659              
660 414 50 33     779 return $self->saveErrorString(undef, "Lzma not available")
661             if $method == ZIP_CM_LZMA
662             and ! defined $IO::Compress::Adapter::Lzma::VERSION;
663              
664 414         877 *$self->{ZipData}{Method} = $method;
665              
666 414         725 *$self->{ZipData}{ZipComment} = $got->getValue('zipcomment') ;
667              
668 414         654 for my $name (qw( extrafieldlocal extrafieldcentral extrafieldzip64))
669             {
670 1242         1724 my $data = $got->getValue($name) ;
671 1242 50       1991 if (defined $data) {
672 0         0 my $bad = IO::Compress::Zlib::Extra::parseExtraField($data, 1, 0) ;
673 0 0       0 return $self->saveErrorString(undef, "Error with $name Parameter: $bad")
674             if $bad ;
675              
676 0         0 $got->setValue($name, $data) ;
677 0         0 *$self->{ZipData}{$name} = $data;
678             }
679             }
680              
681             return undef
682 414 50 33     1726 if defined $IO::Compress::Bzip2::VERSION
683             and ! IO::Compress::Bzip2::ckParams($self, $got);
684              
685 414 50       672 if ($got->parsed('sparse') ) {
686 0         0 *$self->{ZipData}{Sparse} = $got->getValue('sparse') ;
687 0         0 *$self->{ZipData}{Method} = ZIP_CM_STORE;
688             }
689              
690 414 100       781 if ($got->parsed('filtername')) {
691 3         5 my $v = $got->getValue('filtername') ;
692 3 50       10 *$self->{ZipData}{FilterName} = $v
693             if ref $v eq 'CODE' ;
694             }
695              
696 414         925 return 1 ;
697             }
698              
699             sub outputPayload
700             {
701 399     399 0 472 my $self = shift ;
702 399 50       925 return 1 if *$self->{ZipData}{Sparse} ;
703 399         873 return $self->output(@_);
704             }
705              
706              
707             #sub newHeader
708             #{
709             # my $self = shift ;
710             #
711             # return $self->mkHeader(*$self->{Got});
712             #}
713              
714              
715             our %PARAMS = (
716             'stream' => [IO::Compress::Base::Common::Parse_boolean, 1],
717             #'store' => [IO::Compress::Base::Common::Parse_boolean, 0],
718             'method' => [IO::Compress::Base::Common::Parse_unsigned, ZIP_CM_DEFLATE],
719              
720             # # Zip header fields
721             'minimal' => [IO::Compress::Base::Common::Parse_boolean, 0],
722             'zip64' => [IO::Compress::Base::Common::Parse_boolean, 0],
723             'comment' => [IO::Compress::Base::Common::Parse_any, ''],
724             'zipcomment'=> [IO::Compress::Base::Common::Parse_any, ''],
725             'name' => [IO::Compress::Base::Common::Parse_any, ''],
726             'filtername'=> [IO::Compress::Base::Common::Parse_code, undef],
727             'canonicalname'=> [IO::Compress::Base::Common::Parse_boolean, 0],
728             'efs' => [IO::Compress::Base::Common::Parse_boolean, 0],
729             'time' => [IO::Compress::Base::Common::Parse_any, undef],
730             'extime' => [IO::Compress::Base::Common::Parse_any, undef],
731             'exunix2' => [IO::Compress::Base::Common::Parse_any, undef],
732             'exunixn' => [IO::Compress::Base::Common::Parse_any, undef],
733             'extattr' => [IO::Compress::Base::Common::Parse_any,
734             $Compress::Raw::Zlib::gzip_os_code == 3
735             ? 0100644 << 16
736             : 0],
737             'os_code' => [IO::Compress::Base::Common::Parse_unsigned, $Compress::Raw::Zlib::gzip_os_code],
738              
739             'textflag' => [IO::Compress::Base::Common::Parse_boolean, 0],
740             'extrafieldlocal' => [IO::Compress::Base::Common::Parse_any, undef],
741             'extrafieldcentral'=> [IO::Compress::Base::Common::Parse_any, undef],
742             'extrafieldzip64' => [IO::Compress::Base::Common::Parse_any, undef],
743              
744             # Lzma
745             'preset' => [IO::Compress::Base::Common::Parse_unsigned, 6],
746             'extreme' => [IO::Compress::Base::Common::Parse_boolean, 0],
747              
748             # For internal use only
749             'sparse' => [IO::Compress::Base::Common::Parse_unsigned, 0],
750              
751             IO::Compress::RawDeflate::getZlibParams(),
752             defined $IO::Compress::Bzip2::VERSION
753             ? IO::Compress::Bzip2::getExtraParams()
754             : ()
755              
756              
757             );
758              
759             sub getExtraParams
760             {
761 414     414 0 6282 return %PARAMS ;
762             }
763              
764             sub getInverseClass
765             {
766 47     47   27403 no warnings 'once';
  47         77  
  47         11392  
767 0     0 0 0 return ('IO::Uncompress::Unzip',
768             \$IO::Uncompress::Unzip::UnzipError);
769             }
770              
771             sub getFileInfo
772             {
773 163     163 0 189 my $self = shift ;
774 163         169 my $params = shift;
775 163         183 my $filename = shift ;
776              
777 163 100       291 if (IO::Compress::Base::Common::isaScalar($filename))
778             {
779             $params->setValue(zip64 => 1)
780 50 50       56 if IO::Compress::Base::Common::isGeMax32 length (${ $filename }) ;
  50         156  
781              
782 50         88 return ;
783             }
784              
785 113         204 my ($mode, $uid, $gid, $size, $atime, $mtime, $ctime) ;
786 113 50       238 if ( $params->parsed('storelinks') )
787             {
788 0         0 ($mode, $uid, $gid, $size, $atime, $mtime, $ctime)
789             = (lstat($filename))[2, 4,5,7, 8,9,10] ;
790             }
791             else
792             {
793 113         1792 ($mode, $uid, $gid, $size, $atime, $mtime, $ctime)
794             = (stat($filename))[2, 4,5,7, 8,9,10] ;
795             }
796              
797 113 100       298 $params->setValue(textflag => -T $filename )
798             if ! $params->parsed('textflag');
799              
800 113 50       267 $params->setValue(zip64 => 1)
801             if IO::Compress::Base::Common::isGeMax32 $size ;
802              
803 113 100       211 $params->setValue('name' => $filename)
804             if ! $params->parsed('name') ;
805              
806 113 100       241 $params->setValue('time' => $mtime)
807             if ! $params->parsed('time') ;
808              
809 113 50       173 if ( ! $params->parsed('extime'))
810             {
811 113         259 $params->setValue('mtime' => $mtime) ;
812 113         195 $params->setValue('atime' => $atime) ;
813 113         181 $params->setValue('ctime' => undef) ; # No Creation time
814             # TODO - see if can fill out creation time on non-Unix
815             }
816              
817             # NOTE - Unix specific code alert
818 113 100       221 if (! $params->parsed('extattr'))
819             {
820 47     47   259 use Fcntl qw(:mode) ;
  47         67  
  47         29031  
821 108         161 my $attr = $mode << 16;
822 108 50       228 $attr |= ZIP_A_RONLY if ($mode & S_IWRITE) == 0 ;
823 108 50       311 $attr |= ZIP_A_DIR if ($mode & S_IFMT ) == S_IFDIR ;
824              
825 108         164 $params->setValue('extattr' => $attr);
826             }
827              
828 113         269 $params->setValue('want_exunixn', [$uid, $gid]);
829 113         211 $params->setValue('uid' => $uid) ;
830 113         153 $params->setValue('gid' => $gid) ;
831              
832             }
833              
834             sub mkExtendedTime
835             {
836             # order expected is m, a, c
837              
838 226     226 0 261 my $times = '';
839 226         220 my $bit = 1 ;
840 226         218 my $flags = 0;
841              
842 226         323 for my $time (@_)
843             {
844 452 100       568 if (defined $time)
845             {
846 339         335 $flags |= $bit;
847 339         519 $times .= pack("V", $time);
848             }
849              
850 452         491 $bit <<= 1 ;
851             }
852              
853 226         678 return IO::Compress::Zlib::Extra::mkSubField(ZIP_EXTRA_ID_EXT_TIMESTAMP,
854             pack("C", $flags) . $times);
855             }
856              
857             sub mkUnix2Extra
858             {
859 0     0 0 0 my $ids = '';
860 0         0 for my $id (@_)
861             {
862 0         0 $ids .= pack("v", $id);
863             }
864              
865 0         0 return IO::Compress::Zlib::Extra::mkSubField(ZIP_EXTRA_ID_INFO_ZIP_UNIX2,
866             $ids);
867             }
868              
869             sub mkUnixNExtra
870             {
871 113     113 0 125 my $uid = shift;
872 113         115 my $gid = shift;
873              
874             # Assumes UID/GID are 32-bit
875 113         151 my $ids ;
876 113         170 $ids .= pack "C", 1; # version
877 113         1499 $ids .= pack "C", $Config{uidsize};
878 113         283 $ids .= pack "V", $uid;
879 113         452 $ids .= pack "C", $Config{gidsize};
880 113         199 $ids .= pack "V", $gid;
881              
882 113         204 return IO::Compress::Zlib::Extra::mkSubField(ZIP_EXTRA_ID_INFO_ZIP_UNIXN,
883             $ids);
884             }
885              
886              
887             # from Archive::Zip
888             sub _unixToDosTime # Archive::Zip::Member
889             {
890 411     411   479 my $time_t = shift;
891              
892             # TODO - add something to cope with unix time < 1980
893 411         8694 my ( $sec, $min, $hour, $mday, $mon, $year ) = localtime($time_t);
894 411         715 my $dt = 0;
895 411         790 $dt += ( $sec >> 1 );
896 411         617 $dt += ( $min << 5 );
897 411         496 $dt += ( $hour << 11 );
898 411         494 $dt += ( $mday << 16 );
899 411         487 $dt += ( ( $mon + 1 ) << 21 );
900 411         907 $dt += ( ( $year - 80 ) << 25 );
901 411         662 return $dt;
902             }
903              
904             1;
905              
906             __END__