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 )