File Coverage

blib/lib/Archive/Zip/SimpleUnzip.pm
Criterion Covered Total %
statement 327 380 86.0
branch 72 126 57.1
condition 23 37 62.1
subroutine 57 70 81.4
pod 10 24 41.6
total 489 637 76.7


line stmt bran cond sub pod time code
1             package Archive::Zip::SimpleUnzip;
2              
3             require 5.006;
4              
5 2     2   274460 use strict ;
  2         4  
  2         127  
6 2     2   12 use warnings;
  2         5  
  2         140  
7 2     2   11 use bytes;
  2         4  
  2         15  
8              
9 2     2   53 use IO::File;
  2         3  
  2         439  
10 2     2   15 use Carp;
  2         3  
  2         126  
11 2     2   11 use Scalar::Util ();
  2         4  
  2         80  
12              
13 2     2   11 use IO::Compress::Base::Common 2.213 qw(:Status);
  2         74  
  2         346  
14 2     2   14 use IO::Compress::Zip::Constants 2.213 ;
  2         36  
  2         573  
15 2     2   14 use IO::Uncompress::Unzip 2.213 ;
  2         46  
  2         889  
16              
17              
18             require Exporter ;
19              
20             our ($VERSION, @ISA, @EXPORT_OK, %EXPORT_TAGS, $SimpleUnzipError);
21              
22             $VERSION = '1.002';
23             $SimpleUnzipError = '';
24              
25             @ISA = qw(IO::Uncompress::Unzip Exporter);
26             @EXPORT_OK = qw( $SimpleUnzipError unzip );
27             %EXPORT_TAGS = %IO::Uncompress::RawInflate::EXPORT_TAGS ;
28             push @{ $EXPORT_TAGS{all} }, @EXPORT_OK ;
29             Exporter::export_ok_tags('all');
30              
31             our %PARAMS = (
32             'filesonly' => [IO::Compress::Base::Common::Parse_boolean, 0],
33             );
34              
35             sub _ckParams
36             {
37 145     145   2012 my $got = IO::Compress::Base::Parameters->new();
38              
39 145 50       2368 $got->parse(\%PARAMS, @_)
40             or _myDie("Parameter Error: " . $got->getError()) ;
41              
42 145         12663 return $got;
43             }
44              
45             sub _setError
46             {
47 220     220   710 $SimpleUnzipError = $_[2] ;
48 220 50       833 $_[0]->{Error} = $_[2]
49             if defined $_[0] ;
50              
51 220         1161 return $_[1];
52             }
53              
54             sub _myDie
55             {
56 0     0   0 $SimpleUnzipError = $_[0];
57 0         0 Carp::croak $_[0];
58             }
59              
60             sub _illegalFilename
61             {
62 2     2   6 return _setError(undef, undef, "Illegal Filename") ;
63             }
64              
65             sub is64BitPerl
66             {
67 2     2   14 use Config;
  2         11  
  2         5182  
68             # possibly use presence of pack/unpack "Q" for int size test?
69 0 0   0 0 0 $Config{lseeksize} >= 8 and $Config{uvsize} >= 8;
70             }
71              
72             sub new
73             {
74 149     149 1 292248 my $class = shift ;
75              
76 149 100       887 return _setError(undef, undef, "Missing Filename")
77             unless @_ ;
78              
79 148         434 my $inValue = shift ;
80 148         288 my $fh;
81              
82 148 100       476 if (!defined $inValue)
83             {
84 1         4 return _illegalFilename
85             }
86              
87 147         446 my $isSTDOUT = ($inValue eq '-') ;
88 147         1083 my $inType = IO::Compress::Base::Common::whatIsOutput($inValue);
89              
90 147 100 33     7875 if ($inType eq 'filename')
    50          
91             {
92 98 100 66     4933 if (-e $inValue && ( ! -f _ || ! -r _))
      100        
93             {
94 1         7 return _illegalFilename
95             }
96              
97 97 100       1403 $fh = IO::File->new("<$inValue")
98             or return _setError(undef, undef, "cannot open file '$inValue': $!");
99             }
100             elsif( $inType eq 'buffer' || $inType eq 'handle')
101             {
102 49         188 $fh = $inValue;
103             }
104             else
105             {
106 0         0 return _illegalFilename
107             }
108              
109 145         11599 my %obj ;
110              
111 145         675 my $got = _ckParams(@_);
112 145         843 my $filesOnly = $got->getValue('filesonly');
113              
114 145         1298 my $inner = IO::Compress::Base::Common::createSelfTiedObject($class, \$SimpleUnzipError);
115              
116 145         8605 *$inner->{Pause} = 1;
117 145 50       873 $inner->_create(undef, 0, $fh)
118             or return undef;
119              
120 145         2161 my ($CD, $Members, $comment) = $inner->scanCentralDirectory($filesOnly);
121 145         524 $obj{CD} = $CD;
122 145         376 $obj{Members} = $Members ;
123 145         372 $obj{Comment} = $comment;
124 145         337 $obj{Cursor} = 0;
125 145         303 $obj{Inner} = $inner;
126 145         343 $obj{Open} = 1 ;
127              
128 145         1658 bless \%obj, $class;
129             }
130              
131             sub close
132             {
133 72     72 1 123208 my $self = shift;
134             # TODO - fix me
135             # $self->{Inner}->close();
136 72         492 return 1;
137             }
138              
139             sub DESTROY
140             {
141 290     290   36513 my $self = shift;
142             }
143              
144             sub resetter
145             {
146 1600     1600 0 2571 my $inner = shift;
147 1600         2267 my $member = shift;
148              
149              
150 1600         3994 *$inner->{NewStream} = 0 ;
151 1600         3213 *$inner->{EndStream} = 0 ;
152 1600         3464 *$inner->{TotalInflatedBytesRead} = 0;
153 1600         3318 *$inner->{Info}{TrailerLength} = 0;
154              
155             # disable streaming if present & set sizes from central dir
156             # TODO - this will only allow a single file to be read at a time.
157             # police it or fix it.
158 1600         3515 *$inner->{ZipData}{Streaming} = 0;
159 1600         3766 *$inner->{ZipData}{Crc32} = $member->{CRC32};
160 1600         3863 *$inner->{ZipData}{CompressedLen} = $member->{CompressedLength};
161 1600         3534 *$inner->{ZipData}{UnCompressedLen} = $member->{UncompressedLength};
162             *$inner->{CompressedInputLengthRemaining} =
163 1600         4221 *$inner->{CompressedInputLength} = $member->{CompressedLength};
164             }
165              
166             sub _readLocalHeader
167             {
168 657     657   1152 my $self = shift;
169 657         1008 my $member = shift;
170              
171 657         1542 my $inner = $self->{Inner};
172              
173 657         2076 resetter($inner, $member);
174              
175 657         3589 my $status = $inner->smartSeek($member->{LocalHeaderOffset}, 0, SEEK_SET);
176 657         20859 $inner->_readFullZipHeader() ;
177 657         486446 $member->{DataOffset} = $inner->smartTell();
178             }
179              
180             sub comment
181             {
182 144     144 1 154037 my $self = shift;
183              
184 144         1114 return $self->{Comment} ;
185             }
186              
187             sub _mkMember
188             {
189 657     657   1243 my $self = shift;
190 657         1385 my $member = shift;
191              
192 657         2830 $self->_readLocalHeader($member);
193              
194 657         10267 my %member ;
195 657         2250 $member{Inner} = $self->{Inner};
196 657         1592 $member{Info} = $member;
197             #Scalar::Util::weaken $member{Inner}; # for 5.8
198              
199              
200 657         5191 return bless \%member, 'Archive::Zip::SimpleUnzip::Member';
201             }
202              
203             sub member
204             {
205 507     507 1 38911 my $self = shift;
206 507         930 my $name = shift;
207              
208 507 50       1767 return _setError(undef, undef, "Member '$name' not in zip")
209             if ! defined $name ;
210              
211 507         1556 my $member = $self->{Members}{$name};
212              
213 507 100       2106 return _setError(undef, undef, "Member '$name' not in zip")
214             if ! defined $member ;
215              
216 291         886 return $self->_mkMember($member) ;
217             }
218              
219             sub open
220             {
221 0     0 1 0 my $self = shift;
222 0         0 my $name = shift;
223              
224 0         0 my $member = $self->{Members}{$name};
225              
226             # TODO - get to return unef
227 0 0       0 die "Member '$name' not in zip file\n"
228             if ! defined $member ;
229              
230 0         0 $self->_readLocalHeader($member);
231              
232             # return $self->{Inner};
233 0         0 my $z = IO::Compress::Base::Common::createSelfTiedObject("Archive::Zip::SimpleUnzip::Handle", \$SimpleUnzipError) ;
234              
235 0         0 *$z->{Open} = 1 ;
236 0         0 *$z->{SZ} = $self->{Inner};
237 0         0 Scalar::Util::weaken *$z->{SZ}; # for 5.8
238              
239 0         0 $z;
240             }
241              
242             sub extract # to file - return actual path or pass/fail?
243             {
244 1     1 1 2 my $self = shift;
245 1         3 my $name = shift;
246 1         12 my $out = shift;
247              
248 1 50       4 my $member = $self->member($name)
249             or return undef ;
250              
251 1 50       24 return $member->extract(defined $out ? $out : $name);
252             }
253              
254             sub getCanonicalPath
255             {
256 0     0 0 0 my $self = shift;
257 0         0 my $name = shift;
258              
259 0         0 return _canonicalPath($name);
260             }
261              
262              
263              
264             sub _isDirectory
265             {
266 0     0   0 my $self = shift;
267 0         0 my $name = shift ;
268              
269             return substr($name, -1, 1) eq '/' &&
270 0   0     0 $self->{Info}{UncompressedLength} == 0 ;
271             }
272              
273             sub content
274             {
275 361     361 1 50688 my $self = shift;
276 361         720 my $name = shift;
277              
278 361 100       1175 my $member = $self->member($name)
279             or return undef ;
280              
281 217         782 return $member->content();
282             }
283              
284             sub exists
285             {
286 288     288 1 174664 my $self = shift;
287 288         598 my $name = shift;
288              
289 288         1637 return exists $self->{Members}{$name};
290             }
291              
292             sub names
293             {
294 289     289 1 128245 my $self = shift ;
295 289 100       895 return wantarray ? map { $_->{Name} } @{ $self->{CD} } : scalar @{ $self->{CD} } ;
  648         2244  
  144         498  
  145         996  
296             }
297              
298             sub next
299             {
300 439     439 1 188500 my $self = shift;
301 439 100       1149 return undef if $self->{Cursor} >= @{ $self->{CD} } ;
  439         2318  
302 366         1963 return $self->_mkMember($self->{CD}[ $self->{Cursor} ++]) ;
303             }
304              
305             # sub rewind
306             # {
307             # my $self = shift;
308              
309             # $self->{Cursor} = 0;
310             # }
311              
312             # sub unzip
313             # {
314             # my $obj = IO::Compress::Base::Common::createSelfTiedObject(undef, \$SimpleUnzipError);
315             # return $obj->_inf(@_) ;
316             # }
317              
318             sub getExtraParams
319             {
320 145     145 0 8086 return ();
321             }
322              
323             sub ckParams
324             {
325 145     145 0 23471 my $self = shift ;
326 145         319 my $got = shift ;
327              
328             # unzip always needs crc32
329 145         668 $got->setValue('crc32' => 1);
330              
331 145         1817 return 1;
332             }
333              
334             sub mkUncomp
335             {
336 145     145 0 20646 my $self = shift ;
337 145         338 my $got = shift ;
338              
339 145 50       1113 my $magic = $self->ckMagic()
340             or return 0;
341              
342 145         16041 return 1;
343             }
344              
345             sub chkTrailer
346             {
347 583     583 0 227234 my $self = shift;
348 583         1272 my $trailer = shift;
349 583         1502 return STATUS_OK ;
350             }
351              
352              
353             sub seekOrDie
354             {
355             # temp method to die if bad seek
356             # TODO - revisist
357 723     723 0 1014 my $self = shift ;
358 723         947 my $offset = shift ;
359 723         888 my $truncate = shift;
360 723   100     2102 my $position = shift || SEEK_SET;
361 723   50     2212 my $message = shift || "Error Seeking in CentralDirectory" ;
362              
363 723         1839 my $got = $self->smartSeek($offset, $truncate, $position);
364              
365 723         14264 return $got ;
366             }
367              
368             sub readOrDie
369             {
370             # temp method to die if bad read
371             # TODO - revisist
372 505     505 0 622 my $self = shift;
373              
374 505 50       1165 $self->smartReadExact(@_)
375             or die "Error reading";
376             }
377              
378             sub scanCentralDirectory
379             {
380             # print "scanCentralDirectory\n";
381              
382 145     145 0 347 my $self = shift;
383 145         294 my $filesOnly = shift ; # *$self->{FilesOnly};
384 145         541 my $here = $self->smartTell();
385              
386             # Use cases
387             # 1 32-bit CD
388             # 2 64-bit CD
389              
390 145         2306 my @CD = ();
391 145         366 my %Members = ();
392 145         807 my ($entries, $offset, $zipcomment) = $self->findCentralDirectoryOffset();
393              
394             return ()
395 145 50       494 if ! defined $offset;
396              
397 145 50       447 return ([], {}, $zipcomment)
398             if $entries == 0;
399              
400 145         409 $self->seekOrDie($offset, 0, SEEK_SET) ;
401              
402             # Now walk the Central Directory Records
403 145         248 my $index = 0;
404 145         274 my $buffer ;
405 145   100     464 while ($self->smartReadExact(\$buffer, 46) &&
406             unpack("V", $buffer) == ZIP_CENTRAL_HDR_SIG) {
407              
408 726         35486 my $crc32 = unpack("V", substr($buffer, 16, 4));
409 726         1278 my $compressedLength = unpack("V", substr($buffer, 20, 4));
410 726         1159 my $uncompressedLength = unpack("V", substr($buffer, 24, 4));
411 726         1252 my $filename_length = unpack("v", substr($buffer, 28, 2));
412 726         1154 my $extra_length = unpack("v", substr($buffer, 30, 2));
413 726         1186 my $comment_length = unpack("v", substr($buffer, 32, 2));
414 726         1172 my $locHeaderOffset = unpack("V", substr($buffer, 42, 4));
415              
416 726         1017 my $filename;
417             my $extraField;
418 726         943 my $comment = '';
419 726 50       1268 if ($filename_length)
420             {
421 726 50       1469 $self->smartReadExact(\$filename, $filename_length)
422             or return $self->TruncatedTrailer("filename");
423             # print "Filename [$filename]\n";
424             }
425              
426 726 50       24284 if ($extra_length)
427             {
428 0 0       0 $self->smartReadExact(\$extraField, $extra_length)
429             or return $self->TruncatedTrailer("extra");
430              
431             # Check for Zip64
432 0         0 my $zip64Extended = IO::Compress::Zlib::Extra::findID("\x01\x00", $extraField);
433 0 0       0 if ($zip64Extended)
434             {
435 0 0       0 if ($uncompressedLength == 0xFFFFFFFF)
436             {
437 0         0 $uncompressedLength = U64::Value_VV64 substr($zip64Extended, 0, 8, "");
438             # $uncompressedLength = unpack "Q<", substr($zip64Extended, 0, 8, "");
439             }
440 0 0       0 if ($compressedLength == 0xFFFFFFFF)
441             {
442 0         0 $compressedLength = U64::Value_VV64 substr($zip64Extended, 0, 8, "");
443             # $compressedLength = unpack "Q<", substr($zip64Extended, 0, 8, "");
444             }
445 0 0       0 if ($locHeaderOffset == 0xFFFFFFFF)
446             {
447 0         0 $locHeaderOffset = U64::Value_VV64 substr($zip64Extended, 0, 8, "");
448             # $locHeaderOffset = unpack "Q<", substr($zip64Extended, 0, 8, "");
449             }
450             }
451             }
452              
453             next
454 726 100 100     2439 if $filesOnly && substr($filename, -1, 1) eq '/' && $uncompressedLength == 0;
      66        
455              
456 654 100       1093 if ($comment_length)
457             {
458 144 50       389 $self->smartReadExact(\$comment, $comment_length)
459             or return $self->TruncatedTrailer("comment");
460             }
461              
462 654         8574 my %data = (
463             'Name' => $filename,
464             'Comment' => $comment,
465             'LocalHeaderOffset' => $locHeaderOffset,
466             'CompressedLength' => $compressedLength ,
467             'UncompressedLength' => $uncompressedLength ,
468             'CRC32' => $crc32 ,
469             #'Time' => _dosToUnixTime($lastModTime),
470             #'Stream' => $streamingMode,
471             #'Zip64' => $zip64,
472             #
473             #'MethodID' => $compressedMethod,
474             );
475 654         1501 push @CD, \%data;
476 654         1516 $Members{$filename} = \%data ;
477              
478 654         1739 ++ $index;
479             }
480              
481 145         5720 $self->seekOrDie($here, 0, SEEK_SET) ;
482              
483 145         684 return (\@CD, \%Members, $zipcomment) ;
484             }
485              
486             sub offsetFromZip64
487             {
488             # print "offsetFromZip64\n";
489              
490 72     72 0 748 my $self = shift ;
491 72         128 my $here = shift;
492              
493 72         340 $self->seekOrDie($here - 20, 0, SEEK_SET) ;
494              
495 72         137 my $buffer;
496 72         138 my $got = 0;
497 72         244 $self->readOrDie(\$buffer, 20) ;
498             # or die "xxx $here $got $!" ;
499              
500 72 50       3149 if ( unpack("V", $buffer) == ZIP64_END_CENTRAL_LOC_HDR_SIG ) {
501 72         441 my $cd64 = U64::Value_VV64 substr($buffer, 8, 8);
502             # my $cd64 = unpack "Q<", substr($buffer, 8, 8);
503              
504 72         1034 $self->seekOrDie($cd64, 0, SEEK_SET) ;
505              
506 72         202 $self->readOrDie(\$buffer, 4) ;
507              
508 72 50       2787 if ( unpack("V", $buffer) == ZIP64_END_CENTRAL_REC_HDR_SIG ) {
509              
510 72         232 $self->readOrDie(\$buffer, 8);
511             # or die "xxx" ;
512 72         2436 my $size = U64::Value_VV64($buffer);
513             # my $size = unpack "Q<", $buffer;
514              
515 72         583 $self->readOrDie(\$buffer, $size);
516             # or die "xxx" ;
517              
518 72         2415 my $cd64 = U64::Value_VV64 substr($buffer, 36, 8);
519             # my $cd64 = unpack "Q<", substr($buffer, 36, 8);
520              
521 72         765 return $cd64 ;
522             }
523              
524 0         0 die "zzz1";
525             }
526              
527 0         0 die "zzz2";
528             }
529              
530 2     2   39 use constant Pack_ZIP_END_CENTRAL_HDR_SIG => pack("V", ZIP_END_CENTRAL_HDR_SIG);
  2         30  
  2         1188  
531              
532             sub findCentralDirectoryOffset
533             {
534 145     145 0 231 my $self = shift ;
535              
536             # Most common use-case is where there is no comment, so
537             # know exactly where the end of central directory record
538             # should be.
539              
540 145         724 $self->seekOrDie(-22, 0, SEEK_END) ;
541 145         374 my $here = $self->smartTell();
542              
543 145         1184 my $buffer;
544 145         530 $self->readOrDie(\$buffer, 22) ;
545              
546 145         6377 my $zip64 = 0;
547 145         196 my $centralDirOffset ;
548 145         365 my $comment = '';
549 145         271 my $entries = 0;
550 145 100       638 if ( unpack("V", $buffer) == ZIP_END_CENTRAL_HDR_SIG ) {
551 73         270 $entries = unpack("v", substr($buffer, 8, 2));
552 73         207 $centralDirOffset = unpack("V", substr($buffer, 16, 4));
553             }
554             else {
555 72         347 $self->seekOrDie(0, 0, SEEK_END) ;
556              
557 72         225 my $fileLen = $self->smartTell();
558 72         580 my $want = 0 ;
559              
560 72         168 while(1) {
561 72         197 $want += 1024;
562 72         170 my $seekTo = $fileLen - $want;
563 72 50       417 if ($seekTo < 0 ) {
564 72         141 $seekTo = 0;
565 72         152 $want = $fileLen ;
566             }
567              
568 72         254 $self->seekOrDie($seekTo, 0, SEEK_SET) ;
569 72         143 my $got;
570 72         224 $self->readOrDie(\$buffer, $want) ;
571 72         2955 my $pos = rindex( $buffer, Pack_ZIP_END_CENTRAL_HDR_SIG);
572              
573 72 50       335 if ($pos >= 0) {
574              
575             #$here = $self->smartTell();
576 72         183 $here = $seekTo + $pos ;
577 72         261 $entries = unpack("v", substr($buffer, $pos + 8, 2));
578 72         205 $centralDirOffset = unpack("V", substr($buffer, $pos + 16, 4));
579 72         236 my $comment_length = unpack("v", substr($buffer, $pos + 20, 2));
580 72 50       353 $comment = substr($buffer, $pos + 22, $comment_length)
581             if $comment_length ;
582              
583 72         216 last ;
584             }
585              
586             return undef
587 0 0       0 if $want == $fileLen;
588             }
589             }
590              
591 145 100 66     895 $centralDirOffset = $self->offsetFromZip64($here)
592             if $entries and U64::full32 $centralDirOffset ;
593              
594             # print "findCentralDirectoryOffset $centralDirOffset [$comment]\n";
595 145         1046 return ($entries, $centralDirOffset, $comment) ;
596             }
597              
598              
599             sub STORABLE_freeze
600             {
601 0     0 0 0 my $type = ref shift;
602 0         0 croak "Cannot freeze $type object\n";
603             }
604              
605             sub STORABLE_thaw
606             {
607 0     0 0 0 my $type = ref shift;
608 0         0 croak "Cannot thaw $type object\n";
609             }
610              
611             {
612             package Archive::Zip::SimpleUnzip::Member;
613              
614 2     2   16 use IO::File ;
  2         3  
  2         420  
615 2     2   15 use File::Basename;
  2         4  
  2         172  
616 2     2   12 use File::Path ;
  2         4  
  2         3862  
617              
618             sub name
619             {
620 438     438   203023 my $self = shift;
621             # $self->_stdPreq() or return 0 ;
622              
623 438         3495 return $self->{Info}{Name};
624             }
625              
626             sub isDirectory
627             {
628 454     454   844 my $self = shift;
629             # $self->_stdPreq() or return 0 ;
630              
631             return substr($self->{Info}{Name}, -1, 1) eq '/' &&
632 454   66     5543 $self->{Info}{UncompressedLength} == 0 ;
633             }
634              
635             sub isFile
636             {
637 368     368   733 my $self = shift;
638             # $self->_stdPreq() or return 0 ;
639              
640             # TODO - test for symlink
641 368         1266 return ! $self->isDirectory() ;
642             }
643              
644             # TODO
645             #
646             # isZip64
647             # isDir
648             # isSymLink
649             # isText
650             # isBinary
651             # isEncrypted
652             # isStreamed
653             # getComment
654             # getExtra
655             # compressedSize - 64 bit alert
656             # uncompressedSize
657             # time
658             # isStored
659             # compressionName
660             #
661              
662             sub compressedSize
663             {
664 0     0   0 my $self = shift;
665             # $self->_stdPreq() or return 0 ;
666              
667 0         0 return $self->{Info}{CompressedLength};
668             }
669              
670             sub uncompressedSize
671             {
672 0     0   0 my $self = shift;
673             # $self->_stdPreq() or return 0 ;
674              
675 0         0 return $self->{Info}{UncompressedLength};
676             }
677              
678             sub content
679             {
680 721     721   2027 my $self = shift;
681 721         1297 my $data ;
682              
683 721         1792 my $inner = $self->{Inner};
684              
685 721 100       3239 $inner->reset() if $self->{NeedsReset}; $self->{NeedsReset} ++ ;
  721         9454  
686 721         2875 Archive::Zip::SimpleUnzip::resetter($inner, $self->{Info});
687              
688 721         3738 $inner->smartSeek($self->{Info}{DataOffset}, 0, SEEK_SET);
689 721         21599 $self->{Inner}->read($data, $self->{Info}{UncompressedLength});
690              
691 721         67743 return $data;
692             }
693              
694             sub open
695             {
696 222     222   541 my $self = shift;
697              
698             # return return $self->{Inner} ;
699              
700             # my $handle = Symbol::gensym();
701             # tie *$handle, "Archive::Zip::SimpleUnzip::Handle", $self->{SZ}{UnZip};
702             # return $handle;
703              
704 222         1526 my $z = IO::Compress::Base::Common::createSelfTiedObject("Archive::Zip::SimpleUnzip::Handle", \$SimpleUnzipError) ;
705              
706 222         4343 *$z->{Open} = 1 ;
707 222         911 *$z->{SZ} = $self->{Inner};
708              
709 222         588 my $inner = $self->{Inner};
710 222 100       1168 $inner->reset() if $self->{NeedsReset}; $self->{NeedsReset} ++ ;
  222         2852  
711 222         1010 Archive::Zip::SimpleUnzip::resetter($self->{Inner}, $self->{Info});
712 222         1236 $inner->smartSeek($self->{Info}{DataOffset}, 0, SEEK_SET);
713              
714 222         6703 Scalar::Util::weaken *$z->{SZ}; # for 5.8
715              
716 222         735 $z;
717             }
718              
719             sub close
720             {
721 72     72   199 my $self = shift;
722 72         291 return 1;
723             }
724              
725             sub comment
726             {
727 360     360   915 my $self = shift;
728              
729 360         2770 return $self->{Info}{Comment};
730             }
731              
732             sub _canonicalPath
733             {
734 12     12   29 my $name = shift ;
735              
736             # Not an absolute path
737 12         30 $name =~ s#^/+## ;
738              
739             # Remove trailing slash
740 12         61 $name =~ s#/+$## ;
741              
742 12         52 $name =~ s#/+#/#g ;
743              
744             # Drop any ".." and "." paths
745             # Use of ".." is unsafe
746 12         57 my @paths = split '/', $name ;
747 12         26 my @have = grep { ! m#^\.(\.)?$# } @paths ;
  26         95  
748              
749 12         58 return @have ;
750              
751 0         0 $name = join '/', grep { ! m#^\.(\.)?$# } @paths ;
  0         0  
752              
753             # use Perl::OSType;
754             # my $type = Perl::OSType::os_type();
755             # if ( $type eq 'Unix' )
756             # {
757             # }
758             # # TODO Win32
759             }
760              
761             sub canonicalName
762             {
763 6     6   34 my $self = shift;
764              
765 6         32 return join '/', _canonicalPath($self->{Info}{Name});
766             }
767              
768             sub extract # to file
769             {
770 8     8   41 my $self = shift;
771 8         13 my $out = shift;
772              
773 8         15 my $path ;
774             my $filename ;
775              
776 8 100       35 if (defined $out)
777             {
778             # User has supplied output file, so allow absolute path
779 2         13 $filename = $out;
780             }
781             else
782             {
783             # using name in zip file, so make it safe
784 6 50       33 my @path = _canonicalPath(defined $out ? $out : $self->{Info}{Name}) ;
785 6         18 $filename = join '/', @path ;
786             }
787              
788 8 100       32 $path = $self->isDirectory() ? $filename : dirname $filename;
789              
790 8 50       41 if (defined $path)
791             {
792             # check path isn't already a plain file
793 8 50 66     437 return _setError("Path is not a directory '$path'")
794             if -e $path && ! -d $path ;
795              
796 8 100       85 if (! -d $path)
797             {
798 4         9 my $error ;
799 4 50       1808 File::Path::mkpath($path, {error => \$error})
800             or return _setError("Cannot create path '$path': $error");
801             }
802             }
803              
804             # TODO - symlink
805              
806 8 100       44 if ($self->isFile())
807             {
808 6         36 my $handle = $self->open();
809 6 50       114 my $fh = IO::File->new(">$filename")
810             or return _setError("Cannot open file '$filename': $!");
811             #$fh->binmode(); # not available in 5.8.0
812              
813 6         1728 my $data;
814 6         29 print $fh $data
815             while $handle->read($data);
816 6         32 $handle->close();
817 6         34 $fh->close();
818             }
819              
820             # TODO - set timestamps etc...
821              
822 8         89 return 1 ;
823             }
824              
825             sub _setError
826             {
827 0     0   0 $Archive::Zip::SimpleUnzip::SimpleUnzipError = $_[0] ;
828 0         0 return 0;
829             }
830             }
831              
832              
833             {
834             package Archive::Zip::SimpleUnzip::Handle ;
835              
836             sub TIEHANDLE
837             {
838 222 50   222   12222 return $_[0] if ref($_[0]);
839 0         0 die "OOPS\n" ;
840             }
841              
842             sub UNTIE
843             {
844 0     0   0 my $self = shift ;
845             }
846              
847             sub DESTROY
848             {
849             # print "DESTROY H";
850 222     222   300620 my $self = shift ;
851 222         1890 local ($., $@, $!, $^E, $?);
852 222         988 $self->close() ;
853              
854             # TODO - memory leak with 5.8.0 - this isn't called until
855             # global destruction
856             #
857 222         434 %{ *$self } = () ;
  222         1849  
858 222         3060 undef $self ;
859             }
860              
861              
862             sub close
863             {
864 300     300   85432 my $self = shift ;
865 300 100       1700 return 1 if ! *$self->{Open};
866              
867 222         656 *$self->{Open} = 0 ;
868              
869             # untie *$self
870             # if $] >= 5.008 ;
871              
872 222 50       990 if (defined *$self->{SZ})
873             {
874             # *$self->{SZ}{Raw} = undef ;
875 222         722 *$self->{SZ} = undef ;
876             }
877              
878 222         620 1;
879             }
880              
881             sub read
882             {
883             # TODO - remember to fix the return value to match real read & not the broken one in IO::Uncompress
884 515     515   388812 my $self = shift;
885 515 50       1609 $self->_stdPreq() or return 0 ;
886              
887             # warn "READ [$self]\n";
888             # warn "READ [*$self->{SZ}]\n";
889              
890             # $_[0] = *$self->{SZ}{Unzip};
891             # my $status = goto &IO::Uncompress::Base::read;
892             # $_[0] = \$_[0] unless ref $_[0];
893 515         2717 my $status = *$self->{SZ}->read(@_);
894 515 50       24054 $status = undef if $status < 0 ;
895 515         1510 return $status;
896             }
897              
898             sub readline
899             {
900 72     72   33626 my $self = shift;
901 72 50       263 $self->_stdPreq() or return 0 ;
902 72         756 *$self->{SZ}->getline(@_);
903             }
904              
905             sub tell
906             {
907 864     864   152382 my $self = shift;
908 864 50       2187 $self->_stdPreq() or return 0 ;
909              
910 864         3549 *$self->{SZ}->tell(@_);
911             }
912              
913             sub eof
914             {
915 576     576   281747 my $self = shift;
916 576 50       1644 $self->_stdPreq() or return 0 ;
917              
918 576         2668 *$self->{SZ}->eof;
919             }
920              
921             sub _stdPreq
922             {
923 2027     2027   2711 my $self = shift;
924              
925             # TODO - fix me
926 2027         5932 return 1;
927              
928             return _setError("Zip file closed")
929 0 0 0       if ! defined defined *$self->{SZ} || ! *$self->{Inner}{Open} ;
930              
931              
932             return _setError("member filehandle closed")
933 0 0         if ! *$self->{Open} ; #|| ! defined *$self->{SZ}{Raw};
934              
935             return 0
936 0 0         if *$self->{SZ}{Error} ;
937              
938 0           return 1;
939             }
940              
941             sub _setError
942             {
943 0     0     $Archive::Zip::SimpleUnzip::SimpleUnzipError = $_[0] ;
944 0           return 0;
945             }
946              
947 0     0     sub binmode { 1 }
948              
949             # sub clearerr { $Archive::Zip::SimpleUnzip::SimpleUnzipError = '' }
950              
951             *BINMODE = \&binmode;
952             # *SEEK = \&seek;
953             *READ = \&read;
954             *sysread = \&read;
955             *TELL = \&tell;
956             *READLINE = \&readline;
957             *EOF = \&eof;
958             *FILENO = \&fileno;
959             *CLOSE = \&close;
960             }
961              
962              
963             1;
964              
965             __END__