IO::Compress::* 2.000_13
[p5sagit/p5-mst-13.2.git] / ext / Compress / IO / Zlib / lib / IO / Uncompress / Unzip.pm
index 177906a..ae123c9 100644 (file)
@@ -25,9 +25,9 @@ BEGIN
 
 require Exporter ;
 
-our ($VERSION, @ISA, @EXPORT_OK, %EXPORT_TAGS, $UnzipError);
+our ($VERSION, @ISA, @EXPORT_OK, %EXPORT_TAGS, $UnzipError, %headerLookup);
 
-$VERSION = '2.000_12';
+$VERSION = '2.000_13';
 $UnzipError = '';
 
 @ISA    = qw(Exporter IO::Uncompress::RawInflate);
@@ -36,6 +36,15 @@ $UnzipError = '';
 push @{ $EXPORT_TAGS{all} }, @EXPORT_OK ;
 Exporter::export_ok_tags('all');
 
+%headerLookup = (
+        ZIP_CENTRAL_HDR_SIG,            \&skipCentralDirectory,
+        ZIP_END_CENTRAL_HDR_SIG,        \&skipEndCentralDirectory,
+        ZIP64_END_CENTRAL_REC_HDR_SIG,  \&skipCentralDirectory64Rec,
+        ZIP64_END_CENTRAL_LOC_HDR_SIG,  \&skipCentralDirectory64Loc,
+        ZIP64_ARCHIVE_EXTRA_SIG,        \&skipArchiveExtra,
+        ZIP64_DIGITAL_SIGNATURE_SIG,    \&skipDigitalSignature,
+        );
+
 sub new
 {
     my $class = shift ;
@@ -145,7 +154,7 @@ sub readHeader
                 or return $self->saveErrorString(undef, "Truncated file");
         }
         else {
-            my $c = $hdr->{CompressedLength};
+            my $c = $hdr->{CompressedLength}->get32bit();
             $self->smartReadExact(\$buffer, $c)
                 or return $self->saveErrorString(undef, "Truncated file");
             $buffer = '';
@@ -169,8 +178,20 @@ sub chkTrailer
     my $trailer = shift;
 
     my ($sig, $CRC32, $cSize, $uSize) ;
+    my ($cSizeHi, $uSizeHi) = (0, 0);
     if (*$self->{ZipData}{Streaming}) {
-        ($sig, $CRC32, $cSize, $uSize) = unpack("V V V V", $trailer) ;
+        $sig   = unpack ("V", substr($trailer, 0, 4));
+        $CRC32 = unpack ("V", substr($trailer, 4, 4));
+
+        if (*$self->{ZipData}{Zip64} ) {
+            $cSize = U64::newUnpack_V64 substr($trailer,  8, 8);
+            $uSize = U64::newUnpack_V64 substr($trailer, 16, 8);
+        }
+        else {
+            $cSize = U64::newUnpack_V32 substr($trailer,  8, 4);
+            $uSize = U64::newUnpack_V32 substr($trailer, 12, 4);
+        }
+
         return $self->TrailerError("Data Descriptor signature, got $sig")
             if $sig != ZIP_DATA_HDR_SIG;
     }
@@ -185,15 +206,11 @@ sub chkTrailer
         return $self->TrailerError("CRC mismatch")
             if $CRC32  != *$self->{ZipData}{CRC32} ;
 
-        my $exp_isize = *$self->{Uncomp}->compressedBytes();
-        return $self->TrailerError("CSIZE mismatch. Got $cSize"
-                                  . ", expected $exp_isize")
-            if $cSize != $exp_isize ;
+        return $self->TrailerError("CSIZE mismatch.")
+            if ! $cSize->equal(*$self->{CompSize});
 
-        $exp_isize = *$self->{Uncomp}->uncompressedBytes();
-        return $self->TrailerError("USIZE mismatch. Got $uSize"
-                                  . ", expected $exp_isize")
-            if $uSize != $exp_isize ;
+        return $self->TrailerError("USIZE mismatch.")
+            if ! $uSize->equal(*$self->{UnCompSize});
     }
 
     my $reachedEnd = STATUS_ERROR ;
@@ -219,9 +236,9 @@ sub chkTrailer
 
         my $sig = unpack("V", $magic) ;
 
-        if ($sig == ZIP_CENTRAL_HDR_SIG)
+        if ($headerLookup{$sig})
         {
-            if ($self->skipCentralDirectory($magic) != STATUS_OK ) {
+            if ($headerLookup{$sig}($self, $magic) != STATUS_OK ) {
                 if (*$self->{Strict}) {
                     return STATUS_ERROR ;
                 }
@@ -230,21 +247,12 @@ sub chkTrailer
                     return STATUS_OK ;
                 }
             }
-        }
-        elsif ($sig == ZIP_END_CENTRAL_HDR_SIG)
-        {
-            if ($self->skipEndCentralDirectory($magic) != STATUS_OK) {
-                if (*$self->{Strict}) {
-                    return STATUS_ERROR ;
-                }
-                else {
-                    $self->clearError();
-                    return STATUS_OK ;
-                }
+
+            if ($sig == ZIP_END_CENTRAL_HDR_SIG)
+            {
+                return STATUS_OK ;
+                last;
             }
-            # $reachedEnd = STATUS_OK ;
-            return STATUS_OK ;
-            last;
         }
         elsif ($sig == ZIP_LOCAL_HDR_SIG)
         {
@@ -281,8 +289,8 @@ sub skipCentralDirectory
    #my $compressedMethod   = unpack ("v", substr($buffer, 10-4, 2));
    #my $lastModTime        = unpack ("V", substr($buffer, 12-4, 4));
    #my $crc32              = unpack ("V", substr($buffer, 16-4, 4));
-   #my $compressedLength   = unpack ("V", substr($buffer, 20-4, 4));
-   #my $uncompressedLength = unpack ("V", substr($buffer, 24-4, 4));
+    my $compressedLength   = unpack ("V", substr($buffer, 20-4, 4));
+    my $uncompressedLength = unpack ("V", substr($buffer, 24-4, 4));
     my $filename_length    = unpack ("v", substr($buffer, 28-4, 2)); 
     my $extra_length       = unpack ("v", substr($buffer, 30-4, 2));
     my $comment_length     = unpack ("v", substr($buffer, 32-4, 2));
@@ -319,6 +327,85 @@ sub skipCentralDirectory
     return STATUS_OK ;
 }
 
+sub skipArchiveExtra
+{
+    my $self = shift;
+    my $magic = shift ;
+
+    my $buffer;
+    $self->smartReadExact(\$buffer, 4)
+        or return $self->TrailerError("Minimum header size is " . 
+                                     4 . " bytes") ;
+
+    my $keep = $magic . $buffer ;
+
+    my $size = unpack ("V", $buffer);
+
+    $self->smartReadExact(\$buffer, $size)
+        or return $self->TrailerError("Minimum header size is " . 
+                                     $size . " bytes") ;
+
+    $keep .= $buffer ;
+    *$self->{HeaderPending} = $keep ;
+
+    return STATUS_OK ;
+}
+
+
+sub skipCentralDirectory64Rec
+{
+    my $self = shift;
+    my $magic = shift ;
+
+    my $buffer;
+    $self->smartReadExact(\$buffer, 8)
+        or return $self->TrailerError("Minimum header size is " . 
+                                     8 . " bytes") ;
+
+    my $keep = $magic . $buffer ;
+
+    my ($sizeLo, $sizeHi)  = unpack ("V V", $buffer);
+
+    # TODO - take SizeHi into account
+    $self->smartReadExact(\$buffer, $sizeLo)
+        or return $self->TrailerError("Minimum header size is " . 
+                                     $sizeLo . " bytes") ;
+
+    $keep .= $buffer ;
+    *$self->{HeaderPending} = $keep ;
+
+   #my $versionMadeBy      = unpack ("v",   substr($buffer,  0, 2));
+   #my $extractVersion     = unpack ("v",   substr($buffer,  2, 2));
+   #my $diskNumber         = unpack ("V",   substr($buffer,  4, 4));
+   #my $cntrlDirDiskNo     = unpack ("V",   substr($buffer,  8, 4));
+   #my $entriesInThisCD    = unpack ("V V", substr($buffer, 12, 8));
+   #my $entriesInCD        = unpack ("V V", substr($buffer, 20, 8));
+   #my $sizeOfCD           = unpack ("V V", substr($buffer, 28, 8));
+   #my $offsetToCD         = unpack ("V V", substr($buffer, 36, 8));
+
+    return STATUS_OK ;
+}
+
+sub skipCentralDirectory64Loc
+{
+    my $self = shift;
+    my $magic = shift ;
+
+    my $buffer;
+    $self->smartReadExact(\$buffer, 20 - 4)
+        or return $self->TrailerError("Minimum header size is " . 
+                                     20 . " bytes") ;
+
+    my $keep = $magic . $buffer ;
+    *$self->{HeaderPending} = $keep ;
+
+   #my $startCdDisk        = unpack ("V",   substr($buffer,  4-4, 4));
+   #my $offsetToCD         = unpack ("V V", substr($buffer,  8-4, 8));
+   #my $diskCount          = unpack ("V",   substr($buffer, 16-4, 4));
+
+    return STATUS_OK ;
+}
+
 sub skipEndCentralDirectory
 {
     my $self = shift;
@@ -353,8 +440,6 @@ sub skipEndCentralDirectory
 }
 
 
-
-
 sub _isZipMagic
 {
     my $buffer = shift ;
@@ -404,8 +489,8 @@ sub _readZipHeader($)
     my $compressedMethod   = unpack ("v", substr($buffer, 8-4,  2));
     my $lastModTime        = unpack ("V", substr($buffer, 10-4, 4));
     my $crc32              = unpack ("V", substr($buffer, 14-4, 4));
-    my $compressedLength   = unpack ("V", substr($buffer, 18-4, 4));
-    my $uncompressedLength = unpack ("V", substr($buffer, 22-4, 4));
+    my $compressedLength   = new U64 unpack ("V", substr($buffer, 18-4, 4));
+    my $uncompressedLength = new U64 unpack ("V", substr($buffer, 22-4, 4));
     my $filename_length    = unpack ("v", substr($buffer, 26-4, 2)); 
     my $extra_length       = unpack ("v", substr($buffer, 28-4, 2));
 
@@ -419,15 +504,6 @@ sub _readZipHeader($)
 
     *$self->{ZipData}{Streaming} = $streamingMode;
 
-    if (! $streamingMode) {
-        *$self->{ZipData}{Streaming} = 0;
-        *$self->{ZipData}{Crc32} = $crc32;
-        *$self->{ZipData}{CompressedLen} = $compressedLength;
-        *$self->{ZipData}{UnCompressedLen} = $uncompressedLength;
-        *$self->{CompressedInputLengthRemaining} =
-            *$self->{CompressedInputLength} = $compressedLength;
-    }
-
 
     if ($filename_length)
     {
@@ -436,6 +512,8 @@ sub _readZipHeader($)
         $keep .= $filename ;
     }
 
+    my $zip64 = 0 ;
+
     if ($extra_length)
     {
         $self->smartReadExact(\$extraField, $extra_length)
@@ -447,6 +525,35 @@ sub _readZipHeader($)
             if defined $bad;
 
         $keep .= $extraField ;
+
+        my %Extra ;
+        for (@EXTRA)
+        {
+            $Extra{$_->[0]} = \$_->[1];
+        }
+        
+        if (defined $Extra{ZIP_EXTRA_ID_ZIP64()})
+        {
+            $zip64 = 1 ;
+
+            my $buff = ${ $Extra{ZIP_EXTRA_ID_ZIP64()} };
+
+               $uncompressedLength   = U64::newUnpack_V64 substr($buff,  0, 8);
+               $compressedLength     = U64::newUnpack_V64 substr($buff,  8, 8);
+           #my $cheaderOffset        = U64::newUnpack_V64 substr($buff, 16, 8);
+           #my $diskNumber           = unpack ("V", substr($buff, 24, 4));
+        }
+    }
+
+    *$self->{ZipData}{Zip64} = $zip64;
+
+    if (! $streamingMode) {
+        *$self->{ZipData}{Streaming} = 0;
+        *$self->{ZipData}{Crc32} = $crc32;
+        *$self->{ZipData}{CompressedLen} = $compressedLength;
+        *$self->{ZipData}{UnCompressedLen} = $uncompressedLength;
+        *$self->{CompressedInputLengthRemaining} =
+            *$self->{CompressedInputLength} = $compressedLength->get32bit();
     }
 
     *$self->{ZipData}{Method} = $compressedMethod;
@@ -490,7 +597,8 @@ sub _readZipHeader($)
         'FingerprintLength'  => 4,
         #'HeaderLength'       => $compressedMethod == 8 ? length $keep : 0,
         'HeaderLength'       => length $keep,
-        'TrailerLength'      => $streamingMode ? 16  : 0,
+        'Zip64'              => $zip64,
+        'TrailerLength'      => ! $streamingMode ? 0 : $zip64 ? 24 : 16,
         'Header'             => $keep,
         'CompressedLength'   => $compressedLength ,
         'UncompressedLength' => $uncompressedLength ,
@@ -598,7 +706,8 @@ IO::Uncompress::Unzip - Read zip files/buffers
 
     $status = $z->inflateSync()
 
-    $z->trailingData()
+    $data = $z->trailingData()
+    $status = $z->nextStream()
     $data = $z->getHeaderInfo()
     $z->tell()
     $z->seek($position, $whence)
@@ -799,7 +908,7 @@ L</"Constructor Options"> section below.
 
 =over 5
 
-=item AutoClose =E<gt> 0|1
+=item C<< AutoClose => 0|1 >>
 
 This option applies to any input or output data streams to 
 C<unzip> that are filehandles.
@@ -811,8 +920,7 @@ completed.
 This parameter defaults to 0.
 
 
-
-=item BinModeOut =E<gt> 0|1
+=item C<< BinModeOut => 0|1 >>
 
 When writing to a file or filehandle, set C<binmode> before writing to the
 file.
@@ -823,15 +931,16 @@ Defaults to 0.
 
 
 
-=item -Append =E<gt> 0|1
+=item C<< Append => 0|1 >>
 
 TODO
 
-=item -MultiStream =E<gt> 0|1
+=item C<< MultiStream => 0|1 >>
 
-Creates a new stream after each file.
+If the input file/buffer contains multiple compressed data streams, this
+option will uncompress the whole lot as a single data stream.
 
-Defaults to 1.
+Defaults to 0.
 
 
 
@@ -953,7 +1062,7 @@ OPTS is a combination of the following options:
 
 =over 5
 
-=item -AutoClose =E<gt> 0|1
+=item C<< AutoClose => 0|1 >>
 
 This option is only valid when the C<$input> parameter is a filehandle. If
 specified, and the value is true, it will result in the file being closed once
@@ -962,21 +1071,19 @@ destroyed.
 
 This parameter defaults to 0.
 
-=item -MultiStream =E<gt> 0|1
+=item C<< MultiStream => 0|1 >>
 
 
 
-Allows multiple concatenated compressed streams to be treated as a single
-compressed stream. Decompression will stop once either the end of the
-file/buffer is reached, an error is encountered (premature eof, corrupt
-compressed data) or the end of a stream is not immediately followed by the
-start of another stream.
+Treats the complete zip file/buffer as a single compressed data
+stream. When reading in multi-stream mode each member of the zip
+file/buffer will be uncompressed in turn until the end of the file/buffer
+is encountered.
 
 This parameter defaults to 0.
 
 
-
-=item -Prime =E<gt> $string
+=item C<< Prime => $string >>
 
 This option will uncompress the contents of C<$string> before processing the
 input file/buffer.
@@ -987,21 +1094,21 @@ data begins without having to read the first few bytes. If this is the
 case, the uncompression can be I<primed> with these bytes using this
 option.
 
-=item -Transparent =E<gt> 0|1
+=item C<< Transparent => 0|1 >>
 
 If this option is set and the input file or buffer is not compressed data,
 the module will allow reading of it anyway.
 
 This option defaults to 1.
 
-=item -BlockSize =E<gt> $num
+=item C<< BlockSize => $num >>
 
 When reading the compressed input data, IO::Uncompress::Unzip will read it in
 blocks of C<$num> bytes.
 
 This option defaults to 4096.
 
-=item -InputLength =E<gt> $size
+=item C<< InputLength => $size >>
 
 When present this option will limit the number of compressed bytes read
 from the input file/buffer to C<$size>. This option can be used in the
@@ -1017,7 +1124,7 @@ compressed data stream.
 
 This option defaults to off.
 
-=item -Append =E<gt> 0|1
+=item C<< Append => 0|1 >>
 
 This option controls what the C<read> method does with uncompressed data.
 
@@ -1029,7 +1136,7 @@ will be overwritten by the uncompressed data.
 
 Defaults to 0.
 
-=item -Strict =E<gt> 0|1
+=item C<< Strict => 0|1 >>
 
 
 
@@ -1281,6 +1388,27 @@ underlying file will also be closed.
 
 
 
+=head2 nextStream
+
+Usage is
+
+    my $status = $z->nextStream();
+
+Skips to the next compressed data stream in the input file/buffer. If a new
+compressed data stream is found, the eof marker will be cleared, C<$.> will
+be reset to 0.
+
+Returns 1 if a new stream was found, 0 if none was found, and -1 if an
+error was encountered.
+
+=head2 trailingData
+
+Usage is
+
+    my $data = $z->trailingData();
+
+Returns any data that 
+
 =head1 Importing 
 
 No symbolic constants are required by this IO::Uncompress::Unzip at present.