File Coverage

blib/lib/IO/Uncompress/Unzip.pm
Criterion Covered Total %
statement 300 434 69.1
branch 106 214 49.5
condition 12 20 60.0
subroutine 33 39 84.6
pod 2 20 10.0
total 453 727 62.3


line stmt bran cond sub pod time code
1             package IO::Uncompress::Unzip;
2              
3             require 5.006 ;
4              
5             # for RFC1952
6              
7 83     83   8784 use strict ;
  83         173  
  83         3301  
8 83     83   428 use warnings;
  83         171  
  83         5120  
9 83     83   620 use bytes;
  83         265  
  83         980  
10              
11 83     83   2195 use IO::File;
  83         175  
  83         15917  
12 83     83   5871 use IO::Uncompress::RawInflate 2.220 ;
  83         1718  
  83         4832  
13 83     83   516 use IO::Compress::Base::Common 2.220 qw(:Status );
  83         1653  
  83         12862  
14 83     83   581 use IO::Uncompress::Adapter::Inflate 2.220 ;
  83         1578  
  83         2782  
15 83     83   46284 use IO::Uncompress::Adapter::Identity 2.220 ;
  83         2154  
  83         3549  
16 83     83   867 use IO::Compress::Zlib::Extra 2.220 ;
  83         1325  
  83         2416  
17 83     83   483 use IO::Compress::Zip::Constants 2.220 ;
  83         970  
  83         19955  
18              
19 83     83   642 use Compress::Raw::Zlib 2.218 () ;
  83         1245  
  83         11339  
20              
21             BEGIN
22             {
23             # Don't trigger any __DIE__ Hooks.
24 83     83   543 local $SIG{__DIE__};
25              
26 83         201 eval{ require IO::Uncompress::Adapter::Bunzip2 ;
  83         7484  
27 83         1627 IO::Uncompress::Adapter::Bunzip2->VERSION(2.220) } ;
28 83         260 eval{ require IO::Uncompress::Adapter::UnLzma ;
  83         8062  
29 0         0 IO::Uncompress::Adapter::UnLzma->VERSION(2.217) } ;
30 83         6828 eval{ require IO::Uncompress::Adapter::UnXz ;
  83         7054  
31 0         0 IO::Uncompress::Adapter::UnXz->VERSION(2.217) } ;
32 83         6258 eval{ require IO::Uncompress::Adapter::UnZstd ;
  83         381263  
33 0         0 IO::Uncompress::Adapter::UnZstd->VERSION(2.217) } ;
34             }
35              
36              
37             require Exporter ;
38              
39             our ($VERSION, @ISA, @EXPORT_OK, %EXPORT_TAGS, $UnzipError, %headerLookup);
40              
41             $VERSION = '2.220';
42             $UnzipError = '';
43              
44             @ISA = qw(IO::Uncompress::RawInflate Exporter);
45             @EXPORT_OK = qw($UnzipError unzip );
46             %EXPORT_TAGS = %IO::Uncompress::RawInflate::EXPORT_TAGS ;
47             $EXPORT_TAGS{all} = [ defined $EXPORT_TAGS{all} ? @{ $EXPORT_TAGS{all} } : (), @EXPORT_OK ] ;
48             Exporter::export_ok_tags('all');
49              
50             %headerLookup = (
51             ZIP_CENTRAL_HDR_SIG, \&skipCentralDirectory,
52             ZIP_END_CENTRAL_HDR_SIG, \&skipEndCentralDirectory,
53             ZIP64_END_CENTRAL_REC_HDR_SIG, \&skipCentralDirectory64Rec,
54             ZIP64_END_CENTRAL_LOC_HDR_SIG, \&skipCentralDirectory64Loc,
55             ZIP64_ARCHIVE_EXTRA_SIG, \&skipArchiveExtra,
56             ZIP64_DIGITAL_SIGNATURE_SIG, \&skipDigitalSignature,
57             );
58              
59             my %MethodNames = (
60             ZIP_CM_DEFLATE() => 'Deflated',
61             ZIP_CM_BZIP2() => 'Bzip2',
62             ZIP_CM_LZMA() => 'Lzma',
63             ZIP_CM_STORE() => 'Stored',
64             ZIP_CM_XZ() => 'Xz',
65             ZIP_CM_ZSTD() => 'Zstd',
66             );
67              
68             sub new
69             {
70 315     315 1 284759 my $class = shift ;
71 315         1646 my $obj = IO::Compress::Base::Common::createSelfTiedObject($class, \$UnzipError);
72 315         1790 $obj->_create(undef, 0, @_);
73             }
74              
75             sub unzip
76             {
77 172     172 1 115005 my $obj = IO::Compress::Base::Common::createSelfTiedObject(undef, \$UnzipError);
78 172         1034 return $obj->_inf(@_) ;
79             }
80              
81             sub getExtraParams
82             {
83              
84             return (
85             # # Zip header fields
86 470     470 0 7215 'name' => [IO::Compress::Base::Common::Parse_any, undef],
87              
88             'stream' => [IO::Compress::Base::Common::Parse_boolean, 0],
89             'efs' => [IO::Compress::Base::Common::Parse_boolean, 0],
90              
91             # TODO - This means reading the central directory to get
92             # 1. the local header offsets
93             # 2. The compressed data length
94             );
95             }
96              
97             sub ckParams
98             {
99 478     478 0 885 my $self = shift ;
100 478         998 my $got = shift ;
101              
102             # unzip always needs crc32
103 478         1992 $got->setValue('crc32' => 1);
104              
105 478         4556 *$self->{UnzipData}{Name} = $got->getValue('name');
106 478         1412 *$self->{UnzipData}{efs} = $got->getValue('efs');
107              
108 478         1525 return 1;
109             }
110              
111             sub mkUncomp
112             {
113 477     477 0 962 my $self = shift ;
114 477         798 my $got = shift ;
115              
116 477 100       1751 my $magic = $self->ckMagic()
117             or return 0;
118              
119 423 100       1710 *$self->{Info} = $self->readHeader($magic)
120             or return undef ;
121              
122 420         1519 return 1;
123              
124             }
125              
126             sub ckMagic
127             {
128 1389     1389 0 2384 my $self = shift;
129              
130 1389         2011 my $magic ;
131 1389         5402 $self->smartReadExact(\$magic, 4);
132              
133 1389         3938 *$self->{HeaderPending} = $magic ;
134              
135 1389 100       3723 return $self->HeaderError("Minimum header size is " .
136             4 . " bytes")
137             if length $magic != 4 ;
138              
139 1342 100       4353 return $self->HeaderError("Bad Magic")
140             if ! _isZipMagic($magic) ;
141              
142 1018         2675 *$self->{Type} = 'zip';
143              
144 1018         3588 return $magic ;
145             }
146              
147              
148             sub fastForward
149             {
150 60     60 0 107 my $self = shift;
151 60         85 my $offset = shift;
152              
153             # TODO - if Stream isn't enabled & reading from file, use seek
154              
155 60         138 my $buffer = '';
156 60         83 my $c = 1024 * 16;
157              
158 60         136 while ($offset > 0)
159             {
160 42 50       132 $c = $offset
161             if $offset < $c ;
162              
163 42         67 $offset -= $c;
164              
165 42 50       150 $self->smartReadExact(\$buffer, $c)
166             or return 0;
167             }
168              
169 60         171 return 1;
170             }
171              
172              
173             sub readHeader
174             {
175 1018     1018 0 1832 my $self = shift;
176 1018         2518 my $magic = shift ;
177              
178 1018         2552 my $name = *$self->{UnzipData}{Name} ;
179 1018         3895 my $hdr = $self->_readZipHeader($magic) ;
180              
181 1017         3435 while (defined $hdr)
182             {
183 1052 100 100     7194 if (! defined $name || $hdr->{Name} eq $name)
184             {
185 1015         5234 return $hdr ;
186             }
187              
188             # skip the data
189             # TODO - when Stream is off, use seek
190 37         57 my $buffer;
191 37 100       116 if (*$self->{ZipData}{Streaming}) {
192 11         27 while (1) {
193              
194 12         27 my $b;
195 12         61 my $status = $self->smartRead(\$b, 1024 * 16);
196              
197 12 50       44 return $self->saveErrorString(undef, "Truncated file")
198             if $status <= 0 ;
199              
200 12         35 my $temp_buf ;
201             my $out;
202              
203 12         68 $status = *$self->{Uncomp}->uncompr(\$b, \$temp_buf, 0, $out);
204              
205             return $self->saveErrorString(undef, *$self->{Uncomp}{Error},
206             *$self->{Uncomp}{ErrorNo})
207 12 50       89 if $self->saveStatus($status) == STATUS_ERROR;
208              
209 12         49 $self->pushBack($b) ;
210              
211 12 100       44 if ($status == STATUS_ENDSTREAM) {
212 11         59 *$self->{Uncomp}->reset();
213 11         28 last;
214             }
215             }
216              
217             # skip the trailer
218             $self->smartReadExact(\$buffer, $hdr->{TrailerLength})
219 11 50       44 or return $self->saveErrorString(undef, "Truncated file");
220             }
221             else {
222 26         72 my $c = $hdr->{CompressedLength}->get64bit();
223 26 50       55 $self->fastForward($c)
224             or return $self->saveErrorString(undef, "Truncated file");
225 26         43 $buffer = '';
226             }
227              
228 37 50       143 $self->chkTrailer($buffer) == STATUS_OK
229             or return $self->saveErrorString(undef, "Truncated file");
230              
231 37         113 $hdr = $self->_readFullZipHeader();
232              
233 37 50       155 return $self->saveErrorString(undef, "Cannot find '$name'")
234             if $self->smartEof();
235             }
236              
237 2         11 return undef;
238             }
239              
240             sub chkTrailer
241             {
242 1041     1041 0 1815 my $self = shift;
243 1041         1799 my $trailer = shift;
244              
245 1041         2039 my ($sig, $CRC32, $cSize, $uSize) ;
246 1041         2576 my ($cSizeHi, $uSizeHi) = (0, 0);
247 1041 100       2911 if (*$self->{ZipData}{Streaming}) {
248 939         3523 $sig = unpack ("V", substr($trailer, 0, 4));
249 939         2338 $CRC32 = unpack ("V", substr($trailer, 4, 4));
250              
251 939 100       2739 if (*$self->{ZipData}{Zip64} ) {
252 31         139 $cSize = U64::newUnpack_V64 substr($trailer, 8, 8);
253 31         132 $uSize = U64::newUnpack_V64 substr($trailer, 16, 8);
254             }
255             else {
256 908         4088 $cSize = U64::newUnpack_V32 substr($trailer, 8, 4);
257 908         3229 $uSize = U64::newUnpack_V32 substr($trailer, 12, 4);
258             }
259              
260 939 100       2897 return $self->TrailerError("Data Descriptor signature, got $sig")
261             if $sig != ZIP_DATA_HDR_SIG;
262             }
263             else {
264             ($CRC32, $cSize, $uSize) =
265             (*$self->{ZipData}{Crc32},
266             *$self->{ZipData}{CompressedLen},
267 102         482 *$self->{ZipData}{UnCompressedLen});
268             }
269              
270 1039         2891 *$self->{Info}{CRC32} = *$self->{ZipData}{CRC32} ;
271 1039         3541 *$self->{Info}{CompressedLength} = $cSize->get64bit();
272 1039         2592 *$self->{Info}{UncompressedLength} = $uSize->get64bit();
273              
274 1039 100       3185 if (*$self->{Strict}) {
275             return $self->TrailerError("CRC mismatch")
276 489 50       1173 if $CRC32 != *$self->{ZipData}{CRC32} ;
277              
278             return $self->TrailerError("CSIZE mismatch.")
279 489 50       1485 if ! $cSize->equal(*$self->{CompSize});
280              
281             return $self->TrailerError("USIZE mismatch.")
282 489 50       1254 if ! $uSize->equal(*$self->{UnCompSize});
283             }
284              
285 1039         1762 my $reachedEnd = STATUS_ERROR ;
286             # check for central directory or end of central directory
287 1039         1854 while (1)
288             {
289 2113         3203 my $magic ;
290 2113         5440 my $got = $self->smartRead(\$magic, 4);
291              
292             return $self->saveErrorString(STATUS_ERROR, "Truncated file")
293 2113 0 33     5024 if $got != 4 && *$self->{Strict};
294              
295 2113 50       7253 if ($got == 0) {
    50          
    50          
296 0         0 return STATUS_EOF ;
297             }
298             elsif ($got < 0) {
299 0         0 return STATUS_ERROR ;
300             }
301             elsif ($got < 4) {
302 0         0 $self->pushBack($magic) ;
303 0         0 return STATUS_OK ;
304             }
305              
306 2113         4361 my $sig = unpack("V", $magic) ;
307              
308 2113         3166 my $hdr;
309 2113 100       6933 if ($hdr = $headerLookup{$sig})
    50          
310             {
311 1725 50       4693 if (&$hdr($self, $magic) != STATUS_OK ) {
312 0 0       0 if (*$self->{Strict}) {
313 0         0 return STATUS_ERROR ;
314             }
315             else {
316 0         0 $self->clearError();
317 0         0 return STATUS_OK ;
318             }
319             }
320              
321 1725 100       4180 if ($sig == ZIP_END_CENTRAL_HDR_SIG)
322             {
323 651         3508 return STATUS_OK ;
324 0         0 last;
325             }
326             }
327             elsif ($sig == ZIP_LOCAL_HDR_SIG)
328             {
329 388         1247 $self->pushBack($magic) ;
330 388         1705 return STATUS_OK ;
331             }
332             else
333             {
334             # put the data back
335 0         0 $self->pushBack($magic) ;
336 0         0 last;
337             }
338             }
339              
340 0         0 return $reachedEnd ;
341             }
342              
343             sub skipCentralDirectory
344             {
345 1006     1006 0 1795 my $self = shift;
346 1006         1900 my $magic = shift ;
347              
348 1006         1582 my $buffer;
349 1006 50       2849 $self->smartReadExact(\$buffer, 46 - 4)
350             or return $self->TrailerError("Minimum header size is " .
351             46 . " bytes") ;
352              
353 1006         2258 my $keep = $magic . $buffer ;
354 1006         5361 *$self->{HeaderPending} = $keep ;
355              
356             #my $versionMadeBy = unpack ("v", substr($buffer, 4-4, 2));
357             #my $extractVersion = unpack ("v", substr($buffer, 6-4, 2));
358             #my $gpFlag = unpack ("v", substr($buffer, 8-4, 2));
359             #my $compressedMethod = unpack ("v", substr($buffer, 10-4, 2));
360             #my $lastModTime = unpack ("V", substr($buffer, 12-4, 4));
361             #my $crc32 = unpack ("V", substr($buffer, 16-4, 4));
362 1006         2458 my $compressedLength = unpack ("V", substr($buffer, 20-4, 4));
363 1006         2234 my $uncompressedLength = unpack ("V", substr($buffer, 24-4, 4));
364 1006         2368 my $filename_length = unpack ("v", substr($buffer, 28-4, 2));
365 1006         2020 my $extra_length = unpack ("v", substr($buffer, 30-4, 2));
366 1006         1961 my $comment_length = unpack ("v", substr($buffer, 32-4, 2));
367             #my $disk_start = unpack ("v", substr($buffer, 34-4, 2));
368             #my $int_file_attrib = unpack ("v", substr($buffer, 36-4, 2));
369             #my $ext_file_attrib = unpack ("V", substr($buffer, 38-4, 2));
370             #my $lcl_hdr_offset = unpack ("V", substr($buffer, 42-4, 2));
371              
372              
373 1006         2586 my $filename;
374             my $extraField;
375 1006         0 my $comment ;
376 1006 100       2324 if ($filename_length)
377             {
378 240 50       809 $self->smartReadExact(\$filename, $filename_length)
379             or return $self->TruncatedTrailer("filename");
380 240         544 $keep .= $filename ;
381             }
382              
383 1006 100       2186 if ($extra_length)
384             {
385 166 50       460 $self->smartReadExact(\$extraField, $extra_length)
386             or return $self->TruncatedTrailer("extra");
387 166         321 $keep .= $extraField ;
388             }
389              
390 1006 100       2243 if ($comment_length)
391             {
392 1 50       5 $self->smartReadExact(\$comment, $comment_length)
393             or return $self->TruncatedTrailer("comment");
394 1         6 $keep .= $comment ;
395             }
396              
397 1006         2903 return STATUS_OK ;
398             }
399              
400             sub skipArchiveExtra
401             {
402 0     0 0 0 my $self = shift;
403 0         0 my $magic = shift ;
404              
405 0         0 my $buffer;
406 0 0       0 $self->smartReadExact(\$buffer, 4)
407             or return $self->TrailerError("Minimum header size is " .
408             4 . " bytes") ;
409              
410 0         0 my $keep = $magic . $buffer ;
411              
412 0         0 my $size = unpack ("V", $buffer);
413              
414 0 0       0 $self->smartReadExact(\$buffer, $size)
415             or return $self->TrailerError("Minimum header size is " .
416             $size . " bytes") ;
417              
418 0         0 $keep .= $buffer ;
419 0         0 *$self->{HeaderPending} = $keep ;
420              
421 0         0 return STATUS_OK ;
422             }
423              
424              
425             sub skipCentralDirectory64Rec
426             {
427 34     34 0 62 my $self = shift;
428 34         67 my $magic = shift ;
429              
430 34         56 my $buffer;
431 34 50       107 $self->smartReadExact(\$buffer, 8)
432             or return $self->TrailerError("Minimum header size is " .
433             8 . " bytes") ;
434              
435 34         73 my $keep = $magic . $buffer ;
436              
437 34         130 my ($sizeLo, $sizeHi) = unpack ("V V", $buffer);
438 34         118 my $size = $sizeHi * U64::MAX32 + $sizeLo;
439              
440 34 50       127 $self->fastForward($size)
441             or return $self->TrailerError("Minimum header size is " .
442             $size . " bytes") ;
443              
444             #$keep .= $buffer ;
445             #*$self->{HeaderPending} = $keep ;
446              
447             #my $versionMadeBy = unpack ("v", substr($buffer, 0, 2));
448             #my $extractVersion = unpack ("v", substr($buffer, 2, 2));
449             #my $diskNumber = unpack ("V", substr($buffer, 4, 4));
450             #my $cntrlDirDiskNo = unpack ("V", substr($buffer, 8, 4));
451             #my $entriesInThisCD = unpack ("V V", substr($buffer, 12, 8));
452             #my $entriesInCD = unpack ("V V", substr($buffer, 20, 8));
453             #my $sizeOfCD = unpack ("V V", substr($buffer, 28, 8));
454             #my $offsetToCD = unpack ("V V", substr($buffer, 36, 8));
455              
456 34         121 return STATUS_OK ;
457             }
458              
459             sub skipCentralDirectory64Loc
460             {
461 34     34 0 64 my $self = shift;
462 34         67 my $magic = shift ;
463              
464 34         53 my $buffer;
465 34 50       107 $self->smartReadExact(\$buffer, 20 - 4)
466             or return $self->TrailerError("Minimum header size is " .
467             20 . " bytes") ;
468              
469 34         113 my $keep = $magic . $buffer ;
470 34         79 *$self->{HeaderPending} = $keep ;
471              
472             #my $startCdDisk = unpack ("V", substr($buffer, 4-4, 4));
473             #my $offsetToCD = unpack ("V V", substr($buffer, 8-4, 8));
474             #my $diskCount = unpack ("V", substr($buffer, 16-4, 4));
475              
476 34         91 return STATUS_OK ;
477             }
478              
479             sub skipEndCentralDirectory
480             {
481 651     651 0 1090 my $self = shift;
482 651         1133 my $magic = shift ;
483              
484              
485 651         950 my $buffer;
486 651 50       1891 $self->smartReadExact(\$buffer, 22 - 4)
487             or return $self->TrailerError("Minimum header size is " .
488             22 . " bytes") ;
489              
490 651         1582 my $keep = $magic . $buffer ;
491 651         1471 *$self->{HeaderPending} = $keep ;
492              
493             #my $diskNumber = unpack ("v", substr($buffer, 4-4, 2));
494             #my $cntrlDirDiskNo = unpack ("v", substr($buffer, 6-4, 2));
495             #my $entriesInThisCD = unpack ("v", substr($buffer, 8-4, 2));
496             #my $entriesInCD = unpack ("v", substr($buffer, 10-4, 2));
497             #my $sizeOfCD = unpack ("V", substr($buffer, 12-4, 4));
498             #my $offsetToCD = unpack ("V", substr($buffer, 16-4, 4));
499 651         1707 my $comment_length = unpack ("v", substr($buffer, 20-4, 2));
500              
501              
502 651         1033 my $comment ;
503 651 50       1612 if ($comment_length)
504             {
505 0 0       0 $self->smartReadExact(\$comment, $comment_length)
506             or return $self->TruncatedTrailer("comment");
507 0         0 $keep .= $comment ;
508             }
509              
510 651         1669 return STATUS_OK ;
511             }
512              
513              
514             sub _isZipMagic
515             {
516 1379     1379   2622 my $buffer = shift ;
517 1379 50       3627 return 0 if length $buffer < 4 ;
518 1379         3994 my $sig = unpack("V", $buffer) ;
519 1379         5027 return $sig == ZIP_LOCAL_HDR_SIG ;
520             }
521              
522              
523             sub _readFullZipHeader($)
524             {
525 37     37   101 my ($self) = @_ ;
526 37         61 my $magic = '' ;
527              
528 37         109 $self->smartReadExact(\$magic, 4);
529              
530 37         77 *$self->{HeaderPending} = $magic ;
531              
532 37 50       85 return $self->HeaderError("Minimum header size is " .
533             30 . " bytes")
534             if length $magic != 4 ;
535              
536              
537 37 50       89 return $self->HeaderError("Bad Magic")
538             if ! _isZipMagic($magic) ;
539              
540 37         93 my $status = $self->_readZipHeader($magic);
541 37 50       99 delete *$self->{Transparent} if ! defined $status ;
542 37         242 return $status ;
543             }
544              
545             sub _readZipHeader($)
546             {
547 1055     1055   2635 my ($self, $magic) = @_ ;
548 1055         1676 my ($HeaderCRC) ;
549 1055         2201 my ($buffer) = '' ;
550              
551 1055 50       3316 $self->smartReadExact(\$buffer, 30 - 4)
552             or return $self->HeaderError("Minimum header size is " .
553             30 . " bytes") ;
554              
555 1055         2750 my $keep = $magic . $buffer ;
556 1055         2817 *$self->{HeaderPending} = $keep ;
557              
558 1055         3275 my $extractVersion = unpack ("v", substr($buffer, 4-4, 2));
559 1055         2899 my $gpFlag = unpack ("v", substr($buffer, 6-4, 2));
560 1055         2346 my $compressedMethod = unpack ("v", substr($buffer, 8-4, 2));
561 1055         2233 my $lastModTime = unpack ("V", substr($buffer, 10-4, 4));
562 1055         2228 my $crc32 = unpack ("V", substr($buffer, 14-4, 4));
563 1055         5073 my $compressedLength = U64::newUnpack_V32 substr($buffer, 18-4, 4);
564 1055         3659 my $uncompressedLength = U64::newUnpack_V32 substr($buffer, 22-4, 4);
565 1055         2758 my $filename_length = unpack ("v", substr($buffer, 26-4, 2));
566 1055         2366 my $extra_length = unpack ("v", substr($buffer, 28-4, 2));
567              
568 1055         2205 my $filename;
569             my $extraField;
570 1055         2296 my @EXTRA = ();
571              
572             # Some programs (some versions of LibreOffice) mark entries as streamed, but still fill out
573             # compressedLength/uncompressedLength & crc32 in the local file header.
574             # The expected data descriptor is not populated.
575             # So only assume streaming if the Streaming bit is set AND the compressed length is zero
576 1055 100 100     4927 my $streamingMode = (($gpFlag & ZIP_GP_FLAG_STREAMING_MASK) && $crc32 == 0) ? 1 : 0 ;
577              
578 1055 100       2694 my $efs_flag = ($gpFlag & ZIP_GP_FLAG_LANGUAGE_ENCODING) ? 1 : 0;
579              
580 1055 100       2589 return $self->HeaderError("Encrypted content not supported")
581             if $gpFlag & (ZIP_GP_FLAG_ENCRYPTED_MASK|ZIP_GP_FLAG_STRONG_ENCRYPTED_MASK);
582              
583 1053 50       2415 return $self->HeaderError("Patch content not supported")
584             if $gpFlag & ZIP_GP_FLAG_PATCHED_MASK;
585              
586 1053         3715 *$self->{ZipData}{Streaming} = $streamingMode;
587              
588              
589 1053 100       2466 if ($filename_length)
590             {
591 278 50       1039 $self->smartReadExact(\$filename, $filename_length)
592             or return $self->TruncatedHeader("Filename");
593              
594 278 50 66     1117 if (*$self->{UnzipData}{efs} && $efs_flag && $] >= 5.008004)
      66        
595             {
596 5         27 require Encode;
597 5 100       8 eval { $filename = Encode::decode_utf8($filename, 1) }
  5         46  
598             or Carp::croak "Zip Filename not UTF-8" ;
599             }
600              
601 277         939 $keep .= $filename ;
602             }
603              
604 1052         3911 my $zip64 = 0 ;
605              
606 1052 100       5353 if ($extra_length)
607             {
608 197 50       589 $self->smartReadExact(\$extraField, $extra_length)
609             or return $self->TruncatedHeader("Extra Field");
610              
611 197         1044 my $bad = IO::Compress::Zlib::Extra::parseRawExtra($extraField,
612             \@EXTRA, 1, 0);
613 197 50       498 return $self->HeaderError($bad)
614             if defined $bad;
615              
616 197         573 $keep .= $extraField ;
617              
618 197         342 my %Extra ;
619 197         523 for (@EXTRA)
620             {
621 400         1414 $Extra{$_->[0]} = \$_->[1];
622             }
623              
624 197 100       763 if (defined $Extra{ZIP_EXTRA_ID_ZIP64()})
625             {
626 47         94 $zip64 = 1 ;
627              
628 47         72 my $buff = ${ $Extra{ZIP_EXTRA_ID_ZIP64()} };
  47         102  
629              
630             # This code assumes that all the fields in the Zip64
631             # extra field aren't necessarily present. The spec says that
632             # they only exist if the equivalent local headers are -1.
633              
634 47 100       184 if (! $streamingMode) {
635 16         30 my $offset = 0 ;
636              
637 16 50       94 if (U64::full32 $uncompressedLength->get32bit() ) {
638 16         61 $uncompressedLength
639             = U64::newUnpack_V64 substr($buff, 0, 8);
640              
641 16         39 $offset += 8 ;
642             }
643              
644 16 50       62 if (U64::full32 $compressedLength->get32bit() ) {
645              
646 16         42 $compressedLength
647             = U64::newUnpack_V64 substr($buff, $offset, 8);
648              
649 16         54 $offset += 8 ;
650             }
651             }
652             }
653             }
654              
655 1052         3131 *$self->{ZipData}{Zip64} = $zip64;
656              
657 1052 100       2618 if (! $streamingMode) {
658 110         252 *$self->{ZipData}{Streaming} = 0;
659 110         330 *$self->{ZipData}{Crc32} = $crc32;
660 110         295 *$self->{ZipData}{CompressedLen} = $compressedLength;
661 110         314 *$self->{ZipData}{UnCompressedLen} = $uncompressedLength;
662             *$self->{CompressedInputLengthRemaining} =
663 110         434 *$self->{CompressedInputLength} = $compressedLength->get64bit();
664             }
665              
666 1052         5523 *$self->{ZipData}{CRC32} = Compress::Raw::Zlib::crc32(undef);
667 1052         3155 *$self->{ZipData}{Method} = $compressedMethod;
668 1052 100       2830 if ($compressedMethod == ZIP_CM_DEFLATE)
    100          
    50          
    50          
    50          
    50          
669             {
670 943         2171 *$self->{Type} = 'zip-deflate';
671 943         3707 my $obj = IO::Uncompress::Adapter::Inflate::mkUncompObject(1,0,0);
672              
673 943         5091 *$self->{Uncomp} = $obj;
674             }
675             elsif ($compressedMethod == ZIP_CM_BZIP2)
676             {
677 32 50       141 return $self->HeaderError("Unsupported Compression format $compressedMethod")
678             if ! defined $IO::Uncompress::Adapter::Bunzip2::VERSION ;
679              
680 32         90 *$self->{Type} = 'zip-bzip2';
681              
682 32         161 my $obj = IO::Uncompress::Adapter::Bunzip2::mkUncompObject();
683              
684 32         130 *$self->{Uncomp} = $obj;
685             }
686             elsif ($compressedMethod == ZIP_CM_XZ)
687             {
688 0 0       0 return $self->HeaderError("Unsupported Compression format $compressedMethod")
689             if ! defined $IO::Uncompress::Adapter::UnXz::VERSION ;
690              
691 0         0 *$self->{Type} = 'zip-xz';
692              
693 0         0 my $obj = IO::Uncompress::Adapter::UnXz::mkUncompObject();
694              
695 0         0 *$self->{Uncomp} = $obj;
696             }
697             elsif ($compressedMethod == ZIP_CM_ZSTD)
698             {
699 0 0       0 return $self->HeaderError("Unsupported Compression format $compressedMethod")
700             if ! defined $IO::Uncompress::Adapter::UnZstd::VERSION ;
701              
702 0         0 *$self->{Type} = 'zip-zstd';
703              
704 0         0 my $obj = IO::Uncompress::Adapter::UnZstd::mkUncompObject();
705              
706 0         0 *$self->{Uncomp} = $obj;
707             }
708             elsif ($compressedMethod == ZIP_CM_LZMA)
709             {
710 0 0       0 return $self->HeaderError("Unsupported Compression format $compressedMethod")
711             if ! defined $IO::Uncompress::Adapter::UnLzma::VERSION ;
712              
713 0         0 *$self->{Type} = 'zip-lzma';
714 0         0 my $LzmaHeader;
715 0 0       0 $self->smartReadExact(\$LzmaHeader, 4)
716             or return $self->saveErrorString(undef, "Truncated file");
717 0         0 my ($verHi, $verLo) = unpack ("CC", substr($LzmaHeader, 0, 2));
718 0         0 my $LzmaPropertiesSize = unpack ("v", substr($LzmaHeader, 2, 2));
719              
720              
721 0         0 my $LzmaPropertyData;
722 0 0       0 $self->smartReadExact(\$LzmaPropertyData, $LzmaPropertiesSize)
723             or return $self->saveErrorString(undef, "Truncated file");
724              
725 0 0       0 if (! $streamingMode) {
726 0         0 *$self->{ZipData}{CompressedLen}->subtract(4 + $LzmaPropertiesSize) ;
727             *$self->{CompressedInputLengthRemaining} =
728 0         0 *$self->{CompressedInputLength} = *$self->{ZipData}{CompressedLen}->get64bit();
729             }
730              
731 0         0 my $obj =
732             IO::Uncompress::Adapter::UnLzma::mkUncompZipObject($LzmaPropertyData);
733              
734 0         0 *$self->{Uncomp} = $obj;
735             }
736             elsif ($compressedMethod == ZIP_CM_STORE)
737             {
738 77         174 *$self->{Type} = 'zip-stored';
739              
740 77         344 my $obj =
741             IO::Uncompress::Adapter::Identity::mkUncompObject($streamingMode,
742             $zip64);
743              
744 77         304 *$self->{Uncomp} = $obj;
745             }
746             else
747             {
748 0         0 return $self->HeaderError("Unsupported Compression format $compressedMethod");
749             }
750              
751             return {
752             'Type' => 'zip',
753             'FingerprintLength' => 4,
754             #'HeaderLength' => $compressedMethod == 8 ? length $keep : 0,
755             'HeaderLength' => length $keep,
756             'Zip64' => $zip64,
757             'TrailerLength' => ! $streamingMode ? 0 : $zip64 ? 24 : 16,
758             'Header' => $keep,
759             'CompressedLength' => $compressedLength ,
760             'UncompressedLength' => $uncompressedLength ,
761             'CRC32' => $crc32 ,
762             'Name' => $filename,
763             'efs' => $efs_flag, # language encoding flag
764             'Time' => _dosToUnixTime($lastModTime),
765             'Stream' => $streamingMode,
766              
767             'MethodID' => $compressedMethod,
768 1052 100 50     8895 'MethodName' => $MethodNames{$compressedMethod} || 'Unknown',
    100          
769              
770             # 'TextFlag' => $flag & GZIP_FLG_FTEXT ? 1 : 0,
771             # 'HeaderCRCFlag' => $flag & GZIP_FLG_FHCRC ? 1 : 0,
772             # 'NameFlag' => $flag & GZIP_FLG_FNAME ? 1 : 0,
773             # 'CommentFlag' => $flag & GZIP_FLG_FCOMMENT ? 1 : 0,
774             # 'ExtraFlag' => $flag & GZIP_FLG_FEXTRA ? 1 : 0,
775             # 'Comment' => $comment,
776             # 'OsID' => $os,
777             # 'OsName' => defined $GZIP_OS_Names{$os}
778             # ? $GZIP_OS_Names{$os} : "Unknown",
779             # 'HeaderCRC' => $HeaderCRC,
780             # 'Flags' => $flag,
781             # 'ExtraFlags' => $xfl,
782             'ExtraFieldRaw' => $extraField,
783             'ExtraField' => [ @EXTRA ],
784              
785              
786             }
787             }
788              
789             sub filterUncompressed
790             {
791 1222     1222 0 2151 my $self = shift ;
792              
793 1222 100       3455 if (*$self->{ZipData}{Method} == ZIP_CM_DEFLATE) {
794 1125         4147 *$self->{ZipData}{CRC32} = *$self->{Uncomp}->crc32() ;
795             }
796             else {
797 97         183 *$self->{ZipData}{CRC32} = Compress::Raw::Zlib::crc32(${$_[0]}, *$self->{ZipData}{CRC32}, $_[1]);
  97         964  
798             }
799             }
800              
801              
802             # from Archive::Zip & info-zip
803             sub _dosToUnixTime
804             {
805             # Returns zero when $dt is already zero or it doesn't expand to a value that Time::Local::timelocal()
806             # can handle.
807              
808 1052     1052   1965 my $dt = shift;
809             # warn "_dosToUnixTime dt=[$dt]\n";
810              
811             # some zip files don't populate the datetime field at all
812 1052 100       5910 return 0 if ! $dt;
813              
814 1051         2704 my $year = ( ( $dt >> 25 ) & 0x7f ) + 80;
815 1051         2193 my $mon = ( ( $dt >> 21 ) & 0x0f ) - 1;
816 1051         1899 my $mday = ( ( $dt >> 16 ) & 0x1f );
817              
818 1051         2012 my $hour = ( ( $dt >> 11 ) & 0x1f );
819 1051         1718 my $min = ( ( $dt >> 5 ) & 0x3f );
820 1051         2065 my $sec = ( ( $dt << 1 ) & 0x3e );
821              
822 83     83   50971 use Time::Local ;
  83         190488  
  83         15690  
823              
824 1051         1689 my $time_t ;
825             # wrap in an eval to catch out of range errors
826 1051         2265 eval {
827 1051         4688 $time_t = Time::Local::timelocal( $sec, $min, $hour, $mday, $mon, $year);
828             } ;
829              
830 1051 100       100308 return 0 if ! defined $time_t;
831 1050         19612 return $time_t;
832             }
833              
834             #sub scanCentralDirectory
835             #{
836             # # Use cases
837             # # 1 32-bit CD
838             # # 2 64-bit CD
839             #
840             # my $self = shift ;
841             #
842             # my @CD = ();
843             # my $offset = $self->findCentralDirectoryOffset();
844             #
845             # return 0
846             # if ! defined $offset;
847             #
848             # $self->smarkSeek($offset, 0, SEEK_SET) ;
849             #
850             # # Now walk the Central Directory Records
851             # my $buffer ;
852             # while ($self->smartReadExact(\$buffer, 46) &&
853             # unpack("V", $buffer) == ZIP_CENTRAL_HDR_SIG) {
854             #
855             # my $compressedLength = unpack ("V", substr($buffer, 20, 4));
856             # my $filename_length = unpack ("v", substr($buffer, 28, 2));
857             # my $extra_length = unpack ("v", substr($buffer, 30, 2));
858             # my $comment_length = unpack ("v", substr($buffer, 32, 2));
859             #
860             # $self->smarkSeek($filename_length + $extra_length + $comment_length, 0, SEEK_CUR)
861             # if $extra_length || $comment_length || $filename_length;
862             # push @CD, $compressedLength ;
863             # }
864             #
865             #}
866             #
867             #sub findCentralDirectoryOffset
868             #{
869             # my $self = shift ;
870             #
871             # # Most common use-case is where there is no comment, so
872             # # know exactly where the end of central directory record
873             # # should be.
874             #
875             # $self->smarkSeek(-22, 0, SEEK_END) ;
876             #
877             # my $buffer;
878             # $self->smartReadExact(\$buffer, 22) ;
879             #
880             # my $zip64 = 0;
881             # my $centralDirOffset ;
882             # if ( unpack("V", $buffer) == ZIP_END_CENTRAL_HDR_SIG ) {
883             # $centralDirOffset = unpack ("V", substr($buffer, 16, 2));
884             # }
885             # else {
886             # die "xxxx";
887             # }
888             #
889             # return $centralDirOffset ;
890             #}
891             #
892             #sub is84BitCD
893             #{
894             # # TODO
895             # my $self = shift ;
896             #}
897              
898              
899             sub skip
900             {
901 0     0 0   my $self = shift;
902 0           my $size = shift;
903              
904 83     83   729 use Fcntl qw(SEEK_CUR);
  83         166  
  83         69018  
905 0 0         if (ref $size eq 'U64') {
906 0           $self->smartSeek($size->get64bit(), SEEK_CUR);
907             }
908             else {
909 0           $self->smartSeek($size, SEEK_CUR);
910             }
911              
912             }
913              
914              
915             sub scanCentralDirectory
916             {
917 0     0 0   my $self = shift;
918              
919 0           my $here = $self->tell();
920              
921             # Use cases
922             # 1 32-bit CD
923             # 2 64-bit CD
924              
925 0           my @CD = ();
926 0           my $offset = $self->findCentralDirectoryOffset();
927              
928             return ()
929 0 0         if ! defined $offset;
930              
931 0           $self->smarkSeek($offset, 0, SEEK_SET) ;
932              
933             # Now walk the Central Directory Records
934 0           my $buffer ;
935 0   0       while ($self->smartReadExact(\$buffer, 46) &&
936             unpack("V", $buffer) == ZIP_CENTRAL_HDR_SIG) {
937              
938 0           my $compressedLength = unpack("V", substr($buffer, 20, 4));
939 0           my $uncompressedLength = unpack("V", substr($buffer, 24, 4));
940 0           my $filename_length = unpack("v", substr($buffer, 28, 2));
941 0           my $extra_length = unpack("v", substr($buffer, 30, 2));
942 0           my $comment_length = unpack("v", substr($buffer, 32, 2));
943              
944 0           $self->skip($filename_length ) ;
945              
946 0           my $v64 = U64->new( $compressedLength );
947              
948 0 0         if (U64::full32 $compressedLength ) {
949 0           $self->smartReadExact(\$buffer, $extra_length) ;
950 0 0         die "xxx $offset $comment_length $filename_length $extra_length" . length($buffer)
951             if length($buffer) != $extra_length;
952 0           my $got = $self->get64Extra($buffer, U64::full32 $uncompressedLength);
953              
954             # If not Zip64 extra field, assume size is 0xFFFFFFFF
955 0 0         $v64 = $got if defined $got;
956             }
957             else {
958 0           $self->skip($extra_length) ;
959             }
960              
961 0           $self->skip($comment_length ) ;
962              
963 0           push @CD, $v64 ;
964             }
965              
966 0           $self->smartSeek($here, 0, SEEK_SET) ;
967              
968 0           return @CD;
969             }
970              
971             sub get64Extra
972             {
973 0     0 0   my $self = shift ;
974              
975 0           my $buffer = shift;
976 0           my $is_uncomp = shift ;
977              
978 0           my $extra = IO::Compress::Zlib::Extra::findID(0x0001, $buffer);
979              
980 0 0         if (! defined $extra)
981             {
982 0           return undef;
983             }
984             else
985             {
986 0 0         my $u64 = U64::newUnpack_V64(substr($extra, $is_uncomp ? 8 : 0)) ;
987 0           return $u64;
988             }
989             }
990              
991             sub offsetFromZip64
992             {
993 0     0 0   my $self = shift ;
994 0           my $here = shift;
995              
996 0 0         $self->smartSeek($here - 20, 0, SEEK_SET)
997             or die "xx $!" ;
998              
999 0           my $buffer;
1000 0           my $got = 0;
1001 0 0         $self->smartReadExact(\$buffer, 20)
1002             or die "xxx $here $got $!" ;
1003              
1004 0 0         if ( unpack("V", $buffer) == ZIP64_END_CENTRAL_LOC_HDR_SIG ) {
1005 0           my $cd64 = U64::Value_VV64 substr($buffer, 8, 8);
1006              
1007 0           $self->smartSeek($cd64, 0, SEEK_SET) ;
1008              
1009 0 0         $self->smartReadExact(\$buffer, 4)
1010             or die "xxx" ;
1011              
1012 0 0         if ( unpack("V", $buffer) == ZIP64_END_CENTRAL_REC_HDR_SIG ) {
1013              
1014 0 0         $self->smartReadExact(\$buffer, 8)
1015             or die "xxx" ;
1016 0           my $size = U64::Value_VV64($buffer);
1017 0 0         $self->smartReadExact(\$buffer, $size)
1018             or die "xxx" ;
1019              
1020 0           my $cd64 = U64::Value_VV64 substr($buffer, 36, 8);
1021              
1022 0           return $cd64 ;
1023             }
1024              
1025 0           die "zzz";
1026             }
1027              
1028 0           die "zzz";
1029             }
1030              
1031 83     83   739 use constant Pack_ZIP_END_CENTRAL_HDR_SIG => pack("V", ZIP_END_CENTRAL_HDR_SIG);
  83         244  
  83         34951  
1032              
1033             sub findCentralDirectoryOffset
1034             {
1035 0     0 0   my $self = shift ;
1036              
1037             # Most common use-case is where there is no comment, so
1038             # know exactly where the end of central directory record
1039             # should be.
1040              
1041 0           $self->smartSeek(-22, 0, SEEK_END) ;
1042 0           my $here = $self->tell();
1043              
1044 0           my $buffer;
1045 0 0         $self->smartReadExact(\$buffer, 22)
1046             or die "xxx" ;
1047              
1048 0           my $zip64 = 0;
1049 0           my $centralDirOffset ;
1050 0 0         if ( unpack("V", $buffer) == ZIP_END_CENTRAL_HDR_SIG ) {
1051 0           $centralDirOffset = unpack("V", substr($buffer, 16, 4));
1052             }
1053             else {
1054 0           $self->smartSeek(0, 0, SEEK_END) ;
1055              
1056 0           my $fileLen = $self->tell();
1057 0           my $want = 0 ;
1058              
1059 0           while(1) {
1060 0           $want += 1024;
1061 0           my $seekTo = $fileLen - $want;
1062 0 0         if ($seekTo < 0 ) {
1063 0           $seekTo = 0;
1064 0           $want = $fileLen ;
1065             }
1066 0 0         $self->smartSeek( $seekTo, 0, SEEK_SET)
1067             or die "xxx $!" ;
1068 0           my $got;
1069 0 0         $self->smartReadExact($buffer, $want)
1070             or die "xxx " ;
1071 0           my $pos = rindex( $buffer, Pack_ZIP_END_CENTRAL_HDR_SIG);
1072              
1073 0 0         if ($pos >= 0) {
1074             #$here = $self->tell();
1075 0           $here = $seekTo + $pos ;
1076 0           $centralDirOffset = unpack("V", substr($buffer, $pos + 16, 4));
1077 0           last ;
1078             }
1079              
1080             return undef
1081 0 0         if $want == $fileLen;
1082             }
1083             }
1084              
1085 0 0         $centralDirOffset = $self->offsetFromZip64($here)
1086             if U64::full32 $centralDirOffset ;
1087              
1088 0           return $centralDirOffset ;
1089             }
1090              
1091             1;
1092              
1093             __END__