Archive-Zip-Crypt
view release on metacpan or search on metacpan
lib/Archive/Zip/Crypt.pm view on Meta::CPAN
$chunkSize = $Archive::Zip::ChunkSize if not defined $chunkSize;
$chunkSize = $self->_readDataRemaining()
if $chunkSize > $self->_readDataRemaining();
my $buffer = '';
my $outputRef;
my ( $bytesRead, $status ) = $self->_readRawChunk( \$buffer, $chunkSize );
return ( \$buffer, $status ) unless $status == AZ_OK;
# Begin patch
if($self->isEncrypted) {
unless($self->{readOffset}) {
croak("can't decrypt, use \$member->password('mypassword') first") unless defined $self->{password};
Archive::Zip::Crypt::_init_keys($self);
}
Archive::Zip::Crypt::_decrypt($self,\$buffer);
$buffer = substr($buffer,12) unless $self->{readOffset};
}
# End patch
$self->{'readDataRemaining'} -= $bytesRead;
$self->{'readOffset'} += $bytesRead;
if ( $self->compressionMethod() == COMPRESSION_STORED ) {
$self->{'crc32'} = $self->computeCRC32( $buffer, $self->{'crc32'} );
}
( $outputRef, $status ) = &{ $self->{'chunkHandler'} }( $self, \$buffer );
$self->{'writeOffset'} += length($$outputRef);
$self->endRead()
if $self->readIsDone();
return ( $outputRef, $status );
};
# Replaces original method. Avoids patching larger methods just to fool them
# about encryption. Yes, it's ugly :)
*Archive::Zip::Member::isEncrypted = sub {
return 0 if((caller(1))[3] =~ /::(?:extractToFile(?:Handle|Named))$/);
shift->bitFlag() & GPBF_ENCRYPTED_MASK;
};
# Replaces original method. Just takes the 12 bytes of encryption header into
# account if present
*Archive::Zip::ZipFileMember::_skipLocalFileHeader = sub {
my $self = shift;
my $header;
my $bytesRead = $self->fh()->read( $header, LOCAL_FILE_HEADER_LENGTH );
if ( $bytesRead != LOCAL_FILE_HEADER_LENGTH ) {
return _ioError("reading local file header");
}
my $fileNameLength;
my $extraFieldLength;
my $bitFlag;
(
undef, # $self->{'versionNeededToExtract'},
$bitFlag,
undef, # $self->{'compressionMethod'},
undef, # $self->{'lastModFileDateTime'},
undef, # $crc32,
undef, # $compressedSize,
undef, # $uncompressedSize,
$fileNameLength,
$extraFieldLength
) = unpack( LOCAL_FILE_HEADER_FORMAT, $header );
if ($fileNameLength) {
$self->fh()->seek( $fileNameLength, IO::Seekable::SEEK_CUR )
or return _ioError("skipping local file name");
}
if ($extraFieldLength) {
$bytesRead =
$self->fh()->read( $self->{'localExtraField'}, $extraFieldLength );
if ( $bytesRead != $extraFieldLength ) {
return _ioError("reading local extra field");
}
}
$self->{'dataOffset'} = $self->fh()->tell();
if ( $bitFlag & GPBF_HAS_DATA_DESCRIPTOR_MASK ) {
# Read the crc32, compressedSize, and uncompressedSize from the
# extended data descriptor, which directly follows the compressed data.
#
# Skip over the compressed file data (assumes that EOCD compressedSize
# was correct)
$self->fh()->seek( $self->{'compressedSize'}, IO::Seekable::SEEK_CUR )
or return _ioError("seeking to extended local header");
# these values should be set correctly from before.
my $oldCrc32 = $self->{'eocdCrc32'};
my $oldCompressedSize = $self->{'compressedSize'};
my $oldUncompressedSize = $self->{'uncompressedSize'};
my $status = $self->_readDataDescriptor();
return $status unless $status == AZ_OK;
# Begin patch
# Account for 12 bytes of encryption header in $oldUncompressedSize for encrypted members
# TODO uncompressedSize seems to differ only for some types of archives?
$oldUncompressedSize -= 12 if($self->isEncrypted and $oldUncompressedSize != $self->{'uncompressedSize'});
# End patch
return _formatError(
"CRC or size mismatch while skipping data descriptor")
if ( $oldCrc32 != $self->{'crc32'}
|| $oldUncompressedSize != $self->{'uncompressedSize'} );
}
return AZ_OK;
};
1;
( run in 1.719 second using v1.01-cache-2.11-cpan-39bf76dae61 )