use IO::Uncompress::RawInflate ;
use IO::Compress::Base::Common qw(:Status createSelfTiedObject);
use IO::Uncompress::Adapter::Identity;
+use IO::Compress::Zlib::Extra;
+use IO::Compress::Zip::Constants;
+
+use Compress::Raw::Zlib qw(crc32) ;
+
+BEGIN
+{
+ eval { require IO::Uncompress::Adapter::Bunzip2 ;
+ import IO::Uncompress::Adapter::Bunzip2 } ;
+}
+
require Exporter ;
-our ($VERSION, @ISA, @EXPORT_OK, %EXPORT_TAGS, $UnzipError);
+our ($VERSION, @ISA, @EXPORT_OK, %EXPORT_TAGS, $UnzipError, %headerLookup);
-$VERSION = '2.000_10';
+$VERSION = '2.000_13';
$UnzipError = '';
@ISA = qw(Exporter IO::Uncompress::RawInflate);
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
{
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 = '';
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 != 0x08074b50;
+ if $sig != ZIP_DATA_HDR_SIG;
}
else {
($CRC32, $cSize, $uSize) =
}
if (*$self->{Strict}) {
- #return $self->TrailerError("CRC mismatch")
- # if $CRC32 != *$self->{Uncomp}->crc32() ;
-
- my $exp_isize = *$self->{Uncomp}->compressedBytes();
- return $self->TrailerError("CSIZE mismatch. Got $cSize"
- . ", expected $exp_isize")
- if $cSize != $exp_isize ;
-
- $exp_isize = *$self->{Uncomp}->uncompressedBytes();
- return $self->TrailerError("USIZE mismatch. Got $uSize"
- . ", expected $exp_isize")
- if $uSize != $exp_isize ;
+ return $self->TrailerError("CRC mismatch")
+ if $CRC32 != *$self->{ZipData}{CRC32} ;
+
+ return $self->TrailerError("CSIZE mismatch.")
+ if ! $cSize->equal(*$self->{CompSize});
+
+ return $self->TrailerError("USIZE mismatch.")
+ if ! $uSize->equal(*$self->{UnCompSize});
}
my $reachedEnd = STATUS_ERROR ;
my $sig = unpack("V", $magic) ;
- if ($sig == 0x02014b50)
+ if ($headerLookup{$sig})
{
- if ($self->skipCentralDirectory($magic) != STATUS_OK ) {
+ if ($headerLookup{$sig}($self, $magic) != STATUS_OK ) {
if (*$self->{Strict}) {
return STATUS_ERROR ;
}
return STATUS_OK ;
}
}
- }
- elsif ($sig == 0x06054b50)
- {
- 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 == 0x04034b50)
+ elsif ($sig == ZIP_LOCAL_HDR_SIG)
{
$self->pushBack($magic) ;
return STATUS_OK ;
#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));
if ($filename_length)
{
$self->smartReadExact(\$filename, $filename_length)
- or return $self->TrailerError("xxx");
+ or return $self->TruncatedTrailer("filename");
$keep .= $filename ;
}
if ($extra_length)
{
$self->smartReadExact(\$extraField, $extra_length)
- or return $self->TrailerError("xxx");
+ or return $self->TruncatedTrailer("extra");
$keep .= $extraField ;
}
if ($comment_length)
{
$self->smartReadExact(\$comment, $comment_length)
- or return $self->TrailerError("xxx");
+ or return $self->TruncatedTrailer("comment");
$keep .= $comment ;
}
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;
if ($comment_length)
{
$self->smartReadExact(\$comment, $comment_length)
- or return $self->TrailerError("xxx");
+ or return $self->TruncatedTrailer("comment");
$keep .= $comment ;
}
}
-
-
sub _isZipMagic
{
my $buffer = shift ;
return 0 if length $buffer < 4 ;
my $sig = unpack("V", $buffer) ;
- return $sig == 0x04034b50 ;
+ return $sig == ZIP_LOCAL_HDR_SIG ;
}
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));
my $filename;
my $extraField;
- my $streamingMode = ($gpFlag & 0x08) ? 1 : 0 ;
+ my @EXTRA = ();
+ my $streamingMode = ($gpFlag & ZIP_GP_FLAG_STREAMING_MASK) ? 1 : 0 ;
return $self->HeaderError("Streamed Stored content not supported")
if $streamingMode && $compressedMethod == 0 ;
*$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)
{
$self->smartReadExact(\$filename, $filename_length)
- or return $self->HeaderError("xxx");
+ or return $self->TruncatedHeader("Filename");
$keep .= $filename ;
}
+ my $zip64 = 0 ;
+
if ($extra_length)
{
$self->smartReadExact(\$extraField, $extra_length)
- or return $self->HeaderError("xxx");
+ or return $self->TruncatedHeader("Extra Field");
+
+ my $bad = IO::Compress::Zlib::Extra::parseRawExtra($extraField,
+ \@EXTRA, 1, 0);
+ return $self->HeaderError($bad)
+ 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();
}
- if ($compressedMethod == 8)
+ *$self->{ZipData}{Method} = $compressedMethod;
+ if ($compressedMethod == ZIP_CM_DEFLATE)
+ {
+ *$self->{Type} = 'zip-deflate';
+ }
+ elsif ($compressedMethod == ZIP_CM_BZIP2)
{
- *$self->{Type} = 'zip';
+ #if (! defined $IO::Uncompress::Adapter::Bunzip2::VERSION)
+
+ *$self->{Type} = 'zip-bzip2';
+
+ my $obj = IO::Uncompress::Adapter::Bunzip2::mkUncompObject(
+ );
+
+ *$self->{Uncomp} = $obj;
+ *$self->{ZipData}{CRC32} = crc32(undef);
+
}
- elsif ($compressedMethod == 0)
+ elsif ($compressedMethod == ZIP_CM_STORE)
{
# TODO -- add support for reading uncompressed
- *$self->{Type} = 'zipStored';
+ *$self->{Type} = 'zip-stored';
my $obj = IO::Uncompress::Adapter::Identity::mkUncompObject(# $got->value('CRC32'),
# $got->value('ADLER32'),
'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 ,
'Stream' => $streamingMode,
'MethodID' => $compressedMethod,
- 'MethodName' => $compressedMethod == 8
+ 'MethodName' => $compressedMethod == ZIP_CM_DEFLATE
? "Deflated"
- : $compressedMethod == 0
- ? "Stored"
- : "Unknown" ,
+ : $compressedMethod == ZIP_CM_BZIP2
+ ? "Bzip2"
+ : $compressedMethod == ZIP_CM_STORE
+ ? "Stored"
+ : "Unknown" ,
# 'TextFlag' => $flag & GZIP_FLG_FTEXT ? 1 : 0,
# 'HeaderCRCFlag' => $flag & GZIP_FLG_FHCRC ? 1 : 0,
# 'HeaderCRC' => $HeaderCRC,
# 'Flags' => $flag,
# 'ExtraFlags' => $xfl,
-# 'ExtraFieldRaw' => $EXTRA,
-# 'ExtraField' => [ @EXTRA ],
+ 'ExtraFieldRaw' => $extraField,
+ 'ExtraField' => [ @EXTRA ],
}
}
+sub filterUncompressed
+{
+ my $self = shift ;
+
+ if (*$self->{ZipData}{Method} == 12) {
+ *$self->{ZipData}{CRC32} = crc32(${$_[0]}, *$self->{ZipData}{CRC32});
+ }
+ else {
+ *$self->{ZipData}{CRC32} = *$self->{Uncomp}->crc32() ;
+ }
+}
+
+
# from Archive::Zip
sub _dosToUnixTime
{
$status = $z->inflateSync()
- $z->trailingData()
+ $data = $z->trailingData()
+ $status = $z->nextStream()
$data = $z->getHeaderInfo()
$z->tell()
$z->seek($position, $whence)
=head2 Notes
-When C<$input> maps to multiple files/buffers and C<$output> is a single
-file/buffer the uncompressed input files/buffers will all be stored
-in C<$output> as a single uncompressed stream.
+
+When C<$input> maps to multiple compressed files/buffers and C<$output> is
+a single file/buffer, after uncompression C<$output> will contain a
+concatenation of all the uncompressed data from each of the input
+files/buffers.
+
+
=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.
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.
-=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.
=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
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.
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
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.
Defaults to 0.
-=item -Strict =E<gt> 0|1
+=item C<< Strict => 0|1 >>
+=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.