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   1695 use strict ;
  2         4  
  2         58  
6 2     2   10 use warnings;
  2         3  
  2         47  
7 2     2   11 use bytes;
  2         4  
  2         9  
8              
9 2     2   43 use IO::File;
  2         2  
  2         313  
10 2     2   13 use Carp;
  2         4  
  2         96  
11 2     2   10 use Scalar::Util ();
  2         12  
  2         63  
12              
13 2     2   11 use IO::Compress::Base::Common 2.096 qw(:Status);
  2         65  
  2         266  
14 2     2   14 use IO::Compress::Zip::Constants 2.096 ;
  2         35  
  2         435  
15 2     2   14 use IO::Uncompress::Unzip 2.096 ;
  2         40  
  2         753  
16              
17              
18             require Exporter ;
19              
20             our ($VERSION, @ISA, @EXPORT_OK, %EXPORT_TAGS, $SimpleUnzipError);
21              
22             $VERSION = '0.039';
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 169     169   552 my $got = IO::Compress::Base::Parameters::new();
38              
39 169 50       2578 $got->parse(\%PARAMS, @_)
40             or _myDie("Parameter Error: " . $got->getError()) ;
41              
42 169         12464 return $got;
43             }
44              
45             sub _setError
46             {
47 256     256   586 $SimpleUnzipError = $_[2] ;
48 256 50       575 $_[0]->{Error} = $_[2]
49             if defined $_[0] ;
50              
51 256         1028 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   5 return _setError(undef, undef, "Illegal Filename") ;
63             }
64              
65             sub is64BitPerl
66             {
67 2     2   15 use Config;
  2         11  
  2         4580  
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 173     173 1 5219 my $class = shift ;
75              
76 173 100       549 return _setError(undef, undef, "Missing Filename")
77             unless @_ ;
78              
79 172         352 my $inValue = shift ;
80 172         289 my $fh;
81              
82 172 100       492 if (!defined $inValue)
83             {
84 1         3 return _illegalFilename
85             }
86              
87 171         446 my $isSTDOUT = ($inValue eq '-') ;
88 171         633 my $inType = IO::Compress::Base::Common::whatIsOutput($inValue);
89              
90 171 100 33     6337 if ($inType eq 'filename')
    50          
91             {
92 114 100 66     3590 if (-e $inValue && ( ! -f _ || ! -r _))
      100        
93             {
94 1         4 return _illegalFilename
95             }
96              
97 113 100       1135 $fh = new IO::File "<$inValue"
98             or return _setError(undef, undef, "cannot open file '$inValue': $!");
99             }
100             elsif( $inType eq 'buffer' || $inType eq 'handle')
101             {
102 57         142 $fh = $inValue;
103             }
104             else
105             {
106 0         0 return _illegalFilename
107             }
108              
109 169         9847 my %obj ;
110              
111 169         572 my $got = _ckParams(@_);
112 169         567 my $filesOnly = $got->getValue('filesonly');
113              
114 169         1002 my $inner = IO::Compress::Base::Common::createSelfTiedObject($class, \$SimpleUnzipError);
115              
116 169         6923 *$inner->{Pause} = 1;
117 169 50       599 $inner->_create(undef, 0, $fh)
118             or return undef;
119              
120 169         1858 my ($CD, $Members, $comment) = $inner->scanCentralDirectory($filesOnly);
121 169         458 $obj{CD} = $CD;
122 169         270 $obj{Members} = $Members ;
123 169         286 $obj{Comment} = $comment;
124 169         250 $obj{Cursor} = 0;
125 169         252 $obj{Inner} = $inner;
126 169         342 $obj{Open} = 1 ;
127              
128 169         1366 bless \%obj, $class;
129             }
130              
131             sub close
132             {
133 84     84 1 53176 my $self = shift;
134             # TODO - fix me
135             # $self->{Inner}->close();
136 84         354 return 1;
137             }
138              
139             sub DESTROY
140             {
141 338     338   25878 my $self = shift;
142             }
143              
144             sub resetter
145             {
146 1864     1864 0 2324 my $inner = shift;
147 1864         2006 my $member = shift;
148              
149              
150 1864         3018 *$inner->{NewStream} = 0 ;
151 1864         2439 *$inner->{EndStream} = 0 ;
152 1864         2455 *$inner->{TotalInflatedBytesRead} = 0;
153 1864         2963 *$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 1864         3138 *$inner->{ZipData}{Streaming} = 0;
159 1864         3226 *$inner->{ZipData}{Crc32} = $member->{CRC32};
160 1864         3014 *$inner->{ZipData}{CompressedLen} = $member->{CompressedLength};
161 1864         2852 *$inner->{ZipData}{UnCompressedLen} = $member->{UncompressedLength};
162             *$inner->{CompressedInputLengthRemaining} =
163 1864         3111 *$inner->{CompressedInputLength} = $member->{CompressedLength};
164             }
165              
166             sub _readLocalHeader
167             {
168 765     765   1094 my $self = shift;
169 765         814 my $member = shift;
170              
171 765         1022 my $inner = $self->{Inner};
172              
173 765         1598 resetter($inner, $member);
174              
175 765         2468 my $status = $inner->smartSeek($member->{LocalHeaderOffset}, 0, SEEK_SET);
176 765         19841 $inner->_readFullZipHeader() ;
177 765         397424 $member->{DataOffset} = $inner->smartTell();
178             }
179              
180             sub comment
181             {
182 168     168 1 153949 my $self = shift;
183              
184 168         1146 return $self->{Comment} ;
185             }
186              
187             sub _mkMember
188             {
189 765     765   1008 my $self = shift;
190 765         811 my $member = shift;
191              
192 765         1690 $self->_readLocalHeader($member);
193              
194 765         8807 my %member ;
195 765         1695 $member{Inner} = $self->{Inner};
196 765         1246 $member{Info} = $member;
197             #Scalar::Util::weaken $member{Inner}; # for 5.8
198              
199              
200 765         4056 return bless \%member, 'Archive::Zip::SimpleUnzip::Member';
201             }
202              
203             sub member
204             {
205 591     591 1 19050 my $self = shift;
206 591         835 my $name = shift;
207              
208 591 50       1339 return _setError(undef, undef, "Member '$name' not in zip")
209             if ! defined $name ;
210              
211 591         1203 my $member = $self->{Members}{$name};
212              
213 591 100       1701 return _setError(undef, undef, "Member '$name' not in zip")
214             if ! defined $member ;
215              
216 339         660 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 3 my $self = shift;
245 1         7 my $name = shift;
246 1         3 my $out = shift;
247              
248 1 50       4 my $member = $self->member($name)
249             or return undef ;
250              
251 1 50       6 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 421     421 1 33463 my $self = shift;
276 421         668 my $name = shift;
277              
278 421 100       954 my $member = $self->member($name)
279             or return undef ;
280              
281 253         645 return $member->content();
282             }
283              
284             sub exists
285             {
286 336     336 1 80791 my $self = shift;
287 336         613 my $name = shift;
288              
289 336         1549 return exists $self->{Members}{$name};
290             }
291              
292             sub names
293             {
294 337     337 1 75675 my $self = shift ;
295 337 100       883 return wantarray ? map { $_->{Name} } @{ $self->{CD} } : scalar @{ $self->{CD} } ;
  756         2298  
  168         503  
  169         808  
296             }
297              
298             sub next
299             {
300 511     511 1 82623 my $self = shift;
301 511 100       850 return undef if $self->{Cursor} >= @{ $self->{CD} } ;
  511         1545  
302 426         1112 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 169     169 0 6937 return ();
321             }
322              
323             sub ckParams
324             {
325 169     169 0 23873 my $self = shift ;
326 169         238 my $got = shift ;
327              
328             # unzip always needs crc32
329 169         533 $got->setValue('crc32' => 1);
330              
331 169         1355 return 1;
332             }
333              
334             sub mkUncomp
335             {
336 169     169 0 18403 my $self = shift ;
337 169         271 my $got = shift ;
338              
339 169 50       588 my $magic = $self->ckMagic()
340             or return 0;
341              
342 169         13521 return 1;
343             }
344              
345             sub chkTrailer
346             {
347 703     703 0 189377 my $self = shift;
348 703         1449 my $trailer = shift;
349 703         1285 return STATUS_OK ;
350             }
351              
352              
353             sub seekOrDie
354             {
355             # temp method to die if bad seek
356             # TODO - revisist
357 843     843 0 1037 my $self = shift ;
358 843         926 my $offset = shift ;
359 843         968 my $truncate = shift;
360 843   100     2047 my $position = shift || SEEK_SET;
361 843   50     2085 my $message = shift || "Error Seeking in CentralDirectory" ;
362              
363 843         1721 my $got = $self->smartSeek($offset, $truncate, $position);
364              
365 843         18671 return $got ;
366             }
367              
368             sub readOrDie
369             {
370             # temp method to die if bad read
371             # TODO - revisist
372 589     589 0 759 my $self = shift;
373              
374 589 50       1259 $self->smartReadExact(@_)
375             or die "Error reading";
376             }
377              
378             sub scanCentralDirectory
379             {
380             # print "scanCentralDirectory\n";
381              
382 169     169 0 274 my $self = shift;
383 169         256 my $filesOnly = shift ; # *$self->{FilesOnly};
384 169         468 my $here = $self->smartTell();
385              
386             # Use cases
387             # 1 32-bit CD
388             # 2 64-bit CD
389              
390 169         1884 my @CD = ();
391 169         321 my %Members = ();
392 169         560 my ($entries, $offset, $zipcomment) = $self->findCentralDirectoryOffset();
393              
394             return ()
395 169 50       408 if ! defined $offset;
396              
397 169 50       400 return ([], {}, $zipcomment)
398             if $entries == 0;
399              
400 169         443 $self->seekOrDie($offset, 0, SEEK_SET) ;
401              
402             # Now walk the Central Directory Records
403 169         288 my $index = 0;
404 169         267 my $buffer ;
405 169   100     641 while ($self->smartReadExact(\$buffer, 46) &&
406             unpack("V", $buffer) == ZIP_CENTRAL_HDR_SIG) {
407              
408 846         32800 my $crc32 = unpack("V", substr($buffer, 16, 4));
409 846         1367 my $compressedLength = unpack("V", substr($buffer, 20, 4));
410 846         1328 my $uncompressedLength = unpack("V", substr($buffer, 24, 4));
411 846         1223 my $filename_length = unpack("v", substr($buffer, 28, 2));
412 846         1224 my $extra_length = unpack("v", substr($buffer, 30, 2));
413 846         1242 my $comment_length = unpack("v", substr($buffer, 32, 2));
414 846         1178 my $locHeaderOffset = unpack("V", substr($buffer, 42, 4));
415              
416 846         1126 my $filename;
417             my $extraField;
418 846         1020 my $comment = '';
419 846 50       1296 if ($filename_length)
420             {
421 846 50       1700 $self->smartReadExact(\$filename, $filename_length)
422             or return $self->TruncatedTrailer("filename");
423             # print "Filename [$filename]\n";
424             }
425              
426 846 50       28764 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 846 100 100     2527 if $filesOnly && substr($filename, -1, 1) eq '/' && $uncompressedLength == 0;
      66        
455              
456 762 100       1214 if ($comment_length)
457             {
458 168 50       433 $self->smartReadExact(\$comment, $comment_length)
459             or return $self->TruncatedTrailer("comment");
460             }
461              
462 762         8785 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 762         1417 push @CD, \%data;
476 762         1431 $Members{$filename} = \%data ;
477              
478 762         2073 ++ $index;
479             }
480              
481 169         6941 $self->seekOrDie($here, 0, SEEK_SET) ;
482              
483 169         716 return (\@CD, \%Members, $zipcomment) ;
484             }
485              
486             sub offsetFromZip64
487             {
488             # print "offsetFromZip64\n";
489              
490 84     84 0 685 my $self = shift ;
491 84         121 my $here = shift;
492              
493 84         256 $self->seekOrDie($here - 20, 0, SEEK_SET) ;
494              
495 84         127 my $buffer;
496 84         174 my $got = 0;
497 84         310 $self->readOrDie(\$buffer, 20) ;
498             # or die "xxx $here $got $!" ;
499              
500 84 50       3838 if ( unpack("V", $buffer) == ZIP64_END_CENTRAL_LOC_HDR_SIG ) {
501 84         376 my $cd64 = U64::Value_VV64 substr($buffer, 8, 8);
502             # my $cd64 = unpack "Q<", substr($buffer, 8, 8);
503              
504 84         1071 $self->seekOrDie($cd64, 0, SEEK_SET) ;
505              
506 84         320 $self->readOrDie(\$buffer, 4) ;
507              
508 84 50       3847 if ( unpack("V", $buffer) == ZIP64_END_CENTRAL_REC_HDR_SIG ) {
509              
510 84         260 $self->readOrDie(\$buffer, 8);
511             # or die "xxx" ;
512 84         2987 my $size = U64::Value_VV64($buffer);
513             # my $size = unpack "Q<", $buffer;
514              
515 84         737 $self->readOrDie(\$buffer, $size);
516             # or die "xxx" ;
517              
518 84         3084 my $cd64 = U64::Value_VV64 substr($buffer, 36, 8);
519             # my $cd64 = unpack "Q<", substr($buffer, 36, 8);
520              
521 84         745 return $cd64 ;
522             }
523              
524 0         0 die "zzz1";
525             }
526              
527 0         0 die "zzz2";
528             }
529              
530 2     2   27 use constant Pack_ZIP_END_CENTRAL_HDR_SIG => pack("V", ZIP_END_CENTRAL_HDR_SIG);
  2         5  
  2         946  
531              
532             sub findCentralDirectoryOffset
533             {
534 169     169 0 278 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 169         611 $self->seekOrDie(-22, 0, SEEK_END) ;
541 169         407 my $here = $self->smartTell();
542              
543 169         1397 my $buffer;
544 169         546 $self->readOrDie(\$buffer, 22) ;
545              
546 169         7687 my $zip64 = 0;
547 169         218 my $centralDirOffset ;
548 169         299 my $comment = '';
549 169         247 my $entries = 0;
550 169 100       553 if ( unpack("V", $buffer) == ZIP_END_CENTRAL_HDR_SIG ) {
551 85         263 $entries = unpack("v", substr($buffer, 8, 2));
552 85         206 $centralDirOffset = unpack("V", substr($buffer, 16, 4));
553             }
554             else {
555 84         305 $self->seekOrDie(0, 0, SEEK_END) ;
556              
557 84         325 my $fileLen = $self->smartTell();
558 84         859 my $want = 0 ;
559              
560 84         169 while(1) {
561 84         142 $want += 1024;
562 84         170 my $seekTo = $fileLen - $want;
563 84 50       295 if ($seekTo < 0 ) {
564 84         144 $seekTo = 0;
565 84         118 $want = $fileLen ;
566             }
567              
568 84         231 $self->seekOrDie($seekTo, 0, SEEK_SET) ;
569 84         154 my $got;
570 84         274 $self->readOrDie(\$buffer, $want) ;
571 84         3983 my $pos = rindex( $buffer, Pack_ZIP_END_CENTRAL_HDR_SIG);
572              
573 84 50       286 if ($pos >= 0) {
574              
575             #$here = $self->smartTell();
576 84         144 $here = $seekTo + $pos ;
577 84         305 $entries = unpack("v", substr($buffer, $pos + 8, 2));
578 84         239 $centralDirOffset = unpack("V", substr($buffer, $pos + 16, 4));
579 84         232 my $comment_length = unpack("v", substr($buffer, $pos + 20, 2));
580 84 50       260 $comment = substr($buffer, $pos + 22, $comment_length)
581             if $comment_length ;
582              
583 84         181 last ;
584             }
585              
586             return undef
587 0 0       0 if $want == $fileLen;
588             }
589             }
590              
591 169 100 66     759 $centralDirOffset = $self->offsetFromZip64($here)
592             if $entries and U64::full32 $centralDirOffset ;
593              
594             # print "findCentralDirectoryOffset $centralDirOffset [$comment]\n";
595 169         836 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   18 use IO::File ;
  2         4  
  2         362  
615 2     2   14 use File::Basename;
  2         5  
  2         185  
616 2     2   15 use File::Path ;
  2         3  
  2         3354  
617              
618             sub name
619             {
620 510     510   119479 my $self = shift;
621             # $self->_stdPreq() or return 0 ;
622              
623 510         3554 return $self->{Info}{Name};
624             }
625              
626             sub isDirectory
627             {
628 526     526   619 my $self = shift;
629             # $self->_stdPreq() or return 0 ;
630              
631             return substr($self->{Info}{Name}, -1, 1) eq '/' &&
632 526   66     4882 $self->{Info}{UncompressedLength} == 0 ;
633             }
634              
635             sub isFile
636             {
637 428     428   657 my $self = shift;
638             # $self->_stdPreq() or return 0 ;
639              
640             # TODO - test for symlink
641 428         706 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 841     841   1380 my $self = shift;
681 841         980 my $data ;
682              
683 841         1324 my $inner = $self->{Inner};
684              
685 841 100       2649 $inner->reset() if $self->{NeedsReset}; $self->{NeedsReset} ++ ;
  841         6110  
686 841         2006 Archive::Zip::SimpleUnzip::resetter($inner, $self->{Info});
687              
688 841         3069 $inner->smartSeek($self->{Info}{DataOffset}, 0, SEEK_SET);
689 841         21187 $self->{Inner}->read($data, $self->{Info}{UncompressedLength});
690              
691 841         64987 return $data;
692             }
693              
694             sub open
695             {
696 258     258   537 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 258         940 my $z = IO::Compress::Base::Common::createSelfTiedObject("Archive::Zip::SimpleUnzip::Handle", \$SimpleUnzipError) ;
705              
706 258         3199 *$z->{Open} = 1 ;
707 258         596 *$z->{SZ} = $self->{Inner};
708              
709 258         382 my $inner = $self->{Inner};
710 258 100       704 $inner->reset() if $self->{NeedsReset}; $self->{NeedsReset} ++ ;
  258         1913  
711 258         735 Archive::Zip::SimpleUnzip::resetter($self->{Inner}, $self->{Info});
712 258         986 $inner->smartSeek($self->{Info}{DataOffset}, 0, SEEK_SET);
713              
714 258         7003 Scalar::Util::weaken *$z->{SZ}; # for 5.8
715              
716 258         708 $z;
717             }
718              
719             sub close
720             {
721 84     84   175 my $self = shift;
722 84         224 return 1;
723             }
724              
725             sub comment
726             {
727 420     420   795 my $self = shift;
728              
729 420         2840 return $self->{Info}{Comment};
730             }
731              
732             sub _canonicalPath
733             {
734 12     12   19 my $name = shift ;
735              
736             # Not an absolute path
737 12         25 $name =~ s#^/+## ;
738              
739             # Remove trailing slash
740 12         42 $name =~ s#/+$## ;
741              
742 12         38 $name =~ s#/+#/#g ;
743              
744             # Drop any ".." and "." paths
745             # Use of ".." is unsafe
746 12         35 my @paths = split '/', $name ;
747 12         19 my @have = grep { ! m#^\.(\.)?$# } @paths ;
  26         80  
748              
749 12         41 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   28 my $self = shift;
764              
765 6         27 return join '/', _canonicalPath($self->{Info}{Name});
766             }
767              
768             sub extract # to file
769             {
770 8     8   27 my $self = shift;
771 8         17 my $out = shift;
772              
773 8         12 my $path ;
774             my $filename ;
775              
776 8 100       23 if (defined $out)
777             {
778             # User has supplied output file, so allow absolute path
779 2         5 $filename = $out;
780             }
781             else
782             {
783             # using name in zip file, so make it safe
784 6 50       16 my @path = _canonicalPath(defined $out ? $out : $self->{Info}{Name}) ;
785 6         14 $filename = join '/', @path ;
786             }
787              
788 8 100       21 $path = $self->isDirectory() ? $filename : dirname $filename;
789              
790 8 50       39 if (defined $path)
791             {
792             # check path isn't already a plain file
793 8 50 66     232 return _setError("Path is not a directory '$path'")
794             if -e $path && ! -d $path ;
795              
796 8 100       80 if (! -d $path)
797             {
798 4         7 my $error ;
799 4 50       953 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       38 if ($self->isFile())
807             {
808 6         13 my $handle = $self->open();
809 6 50       45 my $fh = new IO::File ">$filename"
810             or return _setError("Cannot open file '$filename': $!");
811             #$fh->binmode(); # not available in 5.8.0
812              
813 6         678 my $data;
814 6         25 print $fh $data
815             while $handle->read($data);
816 6         21 $handle->close();
817 6         23 $fh->close();
818             }
819              
820             # TODO - set timestamps etc...
821              
822 8         50 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 258 50   258   8684 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 258     258   110690 my $self = shift ;
851 258         1814 local ($., $@, $!, $^E, $?);
852 258         727 $self->close() ;
853              
854             # TODO - memory leak with 5.8.0 - this isn't called until
855             # global destruction
856             #
857 258         345 %{ *$self } = () ;
  258         1197  
858 258         2329 undef $self ;
859             }
860              
861              
862             sub close
863             {
864 348     348   41705 my $self = shift ;
865 348 100       1112 return 1 if ! *$self->{Open};
866              
867 258         381 *$self->{Open} = 0 ;
868              
869             # untie *$self
870             # if $] >= 5.008 ;
871              
872 258 50       622 if (defined *$self->{SZ})
873             {
874             # *$self->{SZ}{Raw} = undef ;
875 258         478 *$self->{SZ} = undef ;
876             }
877              
878 258         474 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 599     599   151563 my $self = shift;
885 599 50       1264 $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 599         2516 my $status = *$self->{SZ}->read(@_);
894 599 50       24018 $status = undef if $status < 0 ;
895 599         1220 return $status;
896             }
897              
898             sub readline
899             {
900 84     84   20714 my $self = shift;
901 84 50       201 $self->_stdPreq() or return 0 ;
902 84         559 *$self->{SZ}->getline(@_);
903             }
904              
905             sub tell
906             {
907 1008     1008   75834 my $self = shift;
908 1008 50       1672 $self->_stdPreq() or return 0 ;
909              
910 1008         3349 *$self->{SZ}->tell(@_);
911             }
912              
913             sub eof
914             {
915 672     672   134569 my $self = shift;
916 672 50       1409 $self->_stdPreq() or return 0 ;
917              
918 672         2327 *$self->{SZ}->eof;
919             }
920              
921             sub _stdPreq
922             {
923 2363     2363   2594 my $self = shift;
924              
925             # TODO - fix me
926 2363         5181 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__