Archive-Zip-Crypt

 view release on metacpan or  search on metacpan

lib/Archive/Zip/Crypt.pm  view on Meta::CPAN

}

no warnings 'redefine';     # Prepare for ugliness

# New public method: set decryption password on a member
*Archive::Zip::Member::password = sub {
    my ($self, $pw) = @_;
    defined $pw and $self->{password} = $pw;
    $self->{password};
};

# Replaces original method
*Archive::Zip::Member::readChunk = sub {
    my ( $self, $chunkSize ) = @_;

    if ( $self->readIsDone() ) {
        $self->endRead();
        my $dummy = '';
        return ( \$dummy, AZ_STREAM_END );
    }

    $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;



( run in 1.362 second using v1.01-cache-2.11-cpan-75ffa21a3d4 )