App-zipdetails

 view release on metacpan or  search on metacpan

bin/zipdetails  view on Meta::CPAN

                    my $outerEntry = $LocalDirectory->getByLocalOffset($loc);
                    say "Local Header for '" . $outerEntry->outputFilename . "' at offset " . decimalHex0x($loc) .  " has $count nested Local Headers";
                    for my $n ( @{ $nested{$loc} } )
                    {
                        my $innerEntry = $LocalDirectory->getByLocalOffset($n);

                        say "#  Nested Local Header for filename '" . $innerEntry->outputFilename . "' is at Offset " . decimalHex0x($n)  ;
                    }
                }
            }

            if (keys %bomb)
            {
                # Central Directory knows about these, so this is a zipbomb

                error undef, "Possible zipbomb -- Nested Local Entries found";
                for my $loc (sort keys %bomb)
                {
                    my $count = scalar @{ $bomb{$loc} };
                    my $outerEntry = $LocalDirectory->getByLocalOffset($loc);
                    say "# Local Header for '" . $outerEntry->outputFilename . "' at offset " . decimalHex0x($loc) .  " has $count nested Local Headers";

                    my $table = new SimpleTable;
                    $table->addHeaderRow('Offset', 'Filename');
                    $table->addDataRow(decimalHex0x($_), $LocalDirectory->getByLocalOffset($_)->outputFilename)
                        for sort @{ $bomb{$loc} } ;

                    $table->display();

                    delete $cleanCentralEntries{ $_ }
                        for grep { defined $_ }
                            map  { $CentralDirectory->{byLocalOffset}{$_}{centralHeaderOffset} }
                            @{ $bomb{$loc} } ;
                }
            }
        }

        # Check if contents of local headers match with Central Headers
        #
        # When Central Header encryption is used the local header values are masked (see APPNOTE 6.3.10, sec 4)
        # In this usecase the Central Header will appear to be absent
        #
        # key fields
        #    filename, compressed/uncompressed lengths, crc, compression method
        {
            for my $centralEntry ( sort { $a->centralHeaderOffset() <=> $b->centralHeaderOffset() } values %cleanCentralEntries )
            {
                my $localOffset = $centralEntry->localHeaderOffset;
                my $localEntry = $LocalDirectory->getByLocalOffset($localOffset);

                next
                    unless $localEntry;

                state $fields = [
                    # field name         offset    display name         stringify
                    ['filename',            ZIP_CD_FILENAME_OFFSET,
                                                'Filename',             undef, ],
                    ['extractVersion',       7, 'Extract Zip Spec',     sub { decimalHex0xUndef($_[0]) . " " . decodeZipVer($_[0]) }, ],
                    ['generalPurposeFlags',  8, 'General Purpose Flag', \&decimalHex0xUndef, ],
                    ['compressedMethod',    10, 'Compression Method',   sub { decimalHex0xUndef($_[0]) . " " . getcompressionMethodName($_[0]) }, ],
                    ['lastModDateTime',     12, 'Modification Time',    sub { decimalHex0xUndef($_[0]) . " " . LastModTime($_[0]) }, ],
                    ['crc32',               16, 'CRC32',                \&decimalHex0xUndef, ],
                    ['compressedSize',      20, 'Compressed Size',      \&decimalHex0xUndef, ],
                    ['uncompressedSize',    24, 'Uncompressed Size',    \&decimalHex0xUndef, ],

                ] ;

                my $table = new SimpleTable;
                $table->addHeaderRow('Field Name', 'Central Offset', 'Central Value', 'Local Offset', 'Local Value');

                for my $data (@$fields)
                {
                    my ($field, $offset, $name, $stringify) = @$data;
                    # if the local header uses streaming and we are running a scan/walk, the compressed/uncompressed sizes will not be known
                    my $localValue = $localEntry->{$field} ;
                    my $centralValue = $centralEntry->{$field};

                    if (($localValue // '-1') ne ($centralValue // '-2'))
                    {
                        if ($stringify)
                        {
                            $localValue = $stringify->($localValue);
                            $centralValue = $stringify->($centralValue);
                        }

                        $table->addDataRow($name,
                                            decimalHex0xUndef($centralEntry->centralHeaderOffset() + $offset),
                                            $centralValue,
                                            decimalHex0xUndef($localOffset+$offset),
                                            $localValue);
                    }
                }

                my $badFields = $table->hasData;
                if ($badFields)
                {
                    error undef, "Found $badFields Field Mismatch for Filename '". $centralEntry->outputFilename . "'";
                    $table->display();
                }
            }
        }

    }
    elsif ($CentralDirectory->exists())
    {
        my @messages = "Central Directory exists, but Local Directory not found" ;
        push @messages , "Try running with --walk' or '--scan' options"
            unless $opt_scan || $opt_walk ;
        error undef, @messages;
    }
    elsif ($LocalDirectory->exists())
    {
        if ($CentralDirectory->isEncryptedCD())
        {
            warning undef, "Local Directory exists, but Central Directory is encrypted"
        }
        else
        {
            error undef, "Local Directory exists, but Central Directory not found"
        }

bin/zipdetails  view on Meta::CPAN

    my ($bgp, $gpFlag) = read_v();
    my ($bcm, $compressedMethod) = read_v();

    out $bgp, "General Purpose Flag", Value_v($gpFlag) ;
    GeneralPurposeBits($compressedMethod, $gpFlag);
    my $LanguageEncodingFlag = $gpFlag & ZIP_GP_FLAG_LANGUAGE_ENCODING ;
    my $streaming = $gpFlag & ZIP_GP_FLAG_STREAMING_MASK ;
    $localEntry->languageEncodingFlag($LanguageEncodingFlag) ;

    out $bcm, "Compression Method",   compressionMethod($compressedMethod) ;
    info $FH->tell() - 2, "Unknown 'Compression Method' ID " . decimalHex0x($compressedMethod, 2)
        if ! defined $ZIP_CompressionMethods{$compressedMethod} ;

    my $lastMod = out_V "Modification Time", sub { LastModTime($_[0]) };

    my $crc              = out_V "CRC";

    # Weak encryption if
    # * encrypt flag (bit 0) set in General Purpose Flags
    # * strong encrypt (bit ) not set in General Purpose Flags
    # * not using AES encryption (compression method 99)
    my $weakEncryption = ($gpFlag & ZIP_GP_FLAG_ALL_ENCRYPT) == ZIP_GP_FLAG_ENCRYPTED_MASK &&
                         $compressedMethod != ZIP_CM_AES;
    # Weak encryption uses the CRC value even when streaming is in play.
    # This conflicts with appnote 6.3.10 section 4.4.4
    warning $FH->tell() - 4, "CRC field should be zero when streaming is enabled"
        if $streaming && $crc != 0 && ! $weakEncryption;

    my $compressedSize   = out_V "Compressed Size";
    # warning $FH->tell(), "Compressed Size should be zero when streaming is enabled";

    my $uncompressedSize = out_V "Uncompressed Size";
    # warning $FH->tell(), "Uncompressed Size should be zero when streaming is enabled";

    my $filenameLength   = out_v "Filename Length";

    if ($filenameLength == 0)
    {
        info $FH->tell()- 2, "Zero Length filename";
    }

    my $extraLength        = out_v "Extra Length";

    my $filename = '';
    if ($filenameLength)
    {
        need $filenameLength, Signatures::name($signature), 'Filename';

        myRead(my $raw_filename, $filenameLength);
        $localEntry->filename($raw_filename) ;
        $filename = outputFilename($raw_filename, $LanguageEncodingFlag);
        $localEntry->outputFilename($filename);
    }

    $localEntry->localHeaderOffset($locHeaderOffset) ;
    $localEntry->offsetStart($locHeaderOffset) ;
    $localEntry->compressedSize($compressedSize) ;
    $localEntry->uncompressedSize($uncompressedSize) ;
    $localEntry->extractVersion($extractVer);
    $localEntry->generalPurposeFlags($gpFlag);
    $localEntry->lastModDateTime($lastMod);
    $localEntry->crc32($crc) ;
    $localEntry->zip64ExtraPresent($cdZip64) ;
    $localEntry->zip64SizesPresent($zip64Sizes) ;

    $localEntry->compressedMethod($compressedMethod) ;
    $localEntry->streamed($gpFlag & ZIP_GP_FLAG_STREAMING_MASK) ;

    $localEntry->std_localHeaderOffset($locHeaderOffset + $PREFIX_DELTA) ;
    $localEntry->std_compressedSize($compressedSize) ;
    $localEntry->std_uncompressedSize($uncompressedSize) ;
    $localEntry->std_diskNumber(0) ;

    if ($extraLength)
    {
        need $extraLength, Signatures::name($signature), 'Extra';
        walkExtra($extraLength, $localEntry);
    }

    # Defer test for directory payload until Central Header processing.
    # Need to have external file attributes to deal with some edge conditions.
    # # APPNOTE 6.3.10, sec 4.3.8
    # warning $FH->tell - $filenameLength, "Directory '$filename' must not have a payload"
    #     if ! $streaming && $filename =~ m#/$# && $localEntry->uncompressedSize ;

    my @msg ;
    # if ($cdZip64 && ! $ZIP64)
    # {
    #     # Central directory said this was Zip64
    #     # some zip files don't have the Zip64 field in the local header
    #     # seems to be a streaming issue.
    #     push @msg, "Missing Zip64 extra field in Local Header #$hexHdrCount\n";

    #     if (! $zip64Sizes)
    #     {
    #         # Central has a ZIP64 entry that doesn't have sizes
    #         # Local doesn't have a Zip 64 at all
    #         push @msg, "Unzip may complain about 'overlapped components' #$hexHdrCount\n";
    #     }
    #     else
    #     {
    #         $ZIP64 = 1
    #     }
    # }


    my $minizip_encrypted = $localEntry->minizip_secure;
    my $pk_encrypted      = ($gpFlag & ZIP_GP_FLAG_STRONG_ENCRYPTED_MASK) && $compressedMethod != ZIP_CM_AES && ! $minizip_encrypted;

    # Detecting PK strong encryption from a local header is a bit convoluted.
    # Cannot just use ZIP_GP_FLAG_ENCRYPTED_CD because minizip also uses this bit.
    # so jump through some hoops
    #     extract ver is >= 5.0'
    #     all the encryption flags are set in gpflags
    #     TODO - add zero lengths for crc, compressed & uncompressed

    if (($gpFlag & ZIP_GP_FLAG_ALL_ENCRYPT) == ZIP_GP_FLAG_ALL_ENCRYPT  && $extractVer >= 0x32  )
    {
        $CentralDirectory->setPkEncryptedCD()
    }

bin/zipdetails  view on Meta::CPAN

                    0x0C =>  'Socket',           # 0x0C  0b1100
                    0x0A =>  'Symbolic Link',    # 0x0A  0b1010
                    0x08 =>  'Regular File',     # 0x08  0b1000
                    0x06 =>  'Block Device',     # 0x06  0b0110
                    0x04 =>  'Directory',        # 0x04  0b0100
                    0x02 =>  'Character Device', # 0x02  0b0010
                    0x01 =>  'FIFO',             # 0x01  0b0001
                };

                my $got = $masks->{$not_rwx} // 'Unknown Unix attrib' ;
                out1 "[Bits 28-31]",  Value_C($not_rwx) . " '$got'"
            }
        }
    }
    elsif ($native_attrib)
    {
        out1 "[Bits 24-31]",  Value_v($native_attrib) . " 'Unknown attributes for OS ID $made_by'"
    }

    my ($d, $locHeaderOffset) = read_V();
    my $out = Value_V($locHeaderOffset);
    my $std_localHeaderOffset = $locHeaderOffset;

    if ($locHeaderOffset != MAX32)
    {
        testPossiblePrefix($locHeaderOffset, ZIP_LOCAL_HDR_SIG);
        if ($PREFIX_DELTA)
        {
            $out .= " [Actual Offset is " . Value_V($locHeaderOffset + $PREFIX_DELTA) . "]"
        }
    }

    out $d, "Local Header Offset", $out;

    if ($locHeaderOffset != MAX32)
    {
        my $commonMessage = "'Local Header Offset' field in '" . Signatures::name($signature) .  "' is invalid";
        $locHeaderOffset = checkOffsetValue($locHeaderOffset, $startRecordOffset, 0, $commonMessage, $startRecordOffset + CentralDirectoryEntry::Offset_RelativeOffsetToLocal(), ZIP_LOCAL_HDR_SIG) ;
    }

    my $filename = '';
    if ($filenameLength)
    {
        need $filenameLength, Signatures::name($signature), 'Filename';

        myRead(my $raw_filename, $filenameLength);
        $cdEntry->filename($raw_filename) ;
        $filename = outputFilename($raw_filename, $LanguageEncodingFlag);
        $cdEntry->outputFilename($filename);
    }

    $cdEntry->centralHeaderOffset($cdEntryOffset) ;
    $cdEntry->localHeaderOffset($locHeaderOffset) ;
    $cdEntry->compressedSize($compressedSize) ;
    $cdEntry->uncompressedSize($uncompressedSize) ;
    $cdEntry->zip64ExtraPresent(undef) ; #$cdZip64; ### FIX ME
    $cdEntry->zip64SizesPresent(undef) ; # $zip64Sizes;   ### FIX ME
    $cdEntry->extractVersion($extractVer);
    $cdEntry->generalPurposeFlags($gpFlag);
    $cdEntry->compressedMethod($compressedMethod) ;
    $cdEntry->lastModDateTime($lastMod);
    $cdEntry->crc32($crc) ;
    $cdEntry->inCentralDir(1) ;

    $cdEntry->std_localHeaderOffset($std_localHeaderOffset) ;
    $cdEntry->std_compressedSize($std_compressedSize) ;
    $cdEntry->std_uncompressedSize($std_uncompressedSize) ;
    $cdEntry->std_diskNumber($std_disk_start) ;

    if ($extraLength)
    {
        need $extraLength, Signatures::name($signature), 'Extra';

        walkExtra($extraLength, $cdEntry);
    }

    # $cdEntry->endCentralHeaderOffset($FH->tell() - 1);

    # Can only validate for directory after zip64 data is read
    validateDirectory($cdEntryOffset, $filename, $extractVer, $made_by,
        $cdEntry->compressedSize, $cdEntry->uncompressedSize, $ext_file_attrib);

    if ($comment_length)
    {
        need $comment_length, Signatures::name($signature), 'Comment';

        my $comment ;
        myRead($comment, $comment_length);
        outputFilename $comment, $LanguageEncodingFlag, "Comment";
        $cdEntry->comment($comment);
    }

    $cdEntry->offsetStart($cdEntryOffset) ;
    $cdEntry->offsetEnd($FH->tell() - 1) ;

    $CentralDirectory->addEntry($cdEntry);

    return { 'encapsulated' => $cdEntry ? $cdEntry->encapsulated() : 0};
}

sub decodeZipVer
{
    my $ver = shift ;

    return ""
        if ! defined $ver;

    my $sHi = int($ver /10) ;
    my $sLo = $ver % 10 ;

    "$sHi.$sLo";
}

sub decodeOS
{
    my $ver = shift ;

    $OS_Lookup{$ver} || "Unknown" ;
}

sub Zip64EndCentralHeader

bin/zipdetails  view on Meta::CPAN

        if ($display && !$entry->streamed && ! full16 $entry->std_diskNumber)
        {
            info $FH->tell() - 4, "'$fieldName' should not be present in the ZIP64 extra field.",
                                  "Corresponding field from Central Header is not set to 0xFFFF, value is " . decimalHex0x($entry->diskNumber);
        }
        $entry->zip64_diskNumberPresent(1);
        $entry->diskNumber($value);
    }

    if (length $zip64Extended)
    {
        if ($display)
        {
            out2 $zip64Extended, "Unexpected Data", hexDump16 $zip64Extended ;
            info $fieldOffset, extraFieldIdentifier($extraID) .  ": Unexpected Data: " . decimalHex0x(length $zip64Extended) . " bytes";
        }
    }
}

sub Ntfs2Unix
{
    my $m = shift;
    my $v = shift;

    # NTFS offset is 19DB1DED53E8000

    my $hex = Value_Q($v) ;

    # Treat empty value as special case
    # Could decode to 1 Jan 1601
    return "$hex 'No Date/Time'"
        if $v == 0;

    $v -= 0x19DB1DED53E8000 ;
    my $ns = ($v % 10000000) * 100;
    my $elapse = int ($v/10000000);
    return "$hex '" . getT($elapse) .
           " " . sprintf("%0dns'", $ns);
}

sub decode_NTFS_Filetimes
{
    my $extraID = shift ;
    my $len = shift;
    my $entry = shift;

    out_V "  Reserved";
    out_v "  Tag1";
    out_v "  Size1" ;

    my ($m, $s1) = read_Q;
    out $m, "  Mtime", Ntfs2Unix($m, $s1);

    my ($a, $s3) = read_Q;
    out $a, "  Atime", Ntfs2Unix($a, $s3);

    my ($c, $s2) = read_Q;
    out $c, "  Ctime", Ntfs2Unix($c, $s2);
}

sub OpenVMS_DateTime
{
    my $ix = shift;
    my $tag = shift;
    my $size = shift;

    # VMS epoch is 17 Nov 1858
    # Offset to Unix Epoch is -0x7C95674C3DA5C0 (-35067168005400000)

    my ($data, $value) = read_Q();

    my $datetime = "No Date Time'";
    if ($value != 0)
    {
        my $v =  $value - 0x007C95674C3DA5C0 ;
        my $ns = ($v % 10000000) * 100 ;
        my $seconds = int($v / 10000000) ;
        $datetime = getT($seconds) .
           " " . sprintf("%0dns'", $ns);
    }

    out2 $data, "  Attribute", Value_Q($value) . " '$datetime";
}

sub OpenVMS_DumpBytes
{
    my $ix = shift;
    my $tag = shift;
    my $size = shift;

    myRead(my $data, $size);

    out($data, "    Attribute", hexDump16($data));

}

sub OpenVMS_4ByteValue
{
    my $ix = shift;
    my $tag = shift;
    my $size = shift;

    my ($data, $value) = read_V();

    out2 $data, "  Attribute", Value_V($value);
}

sub OpenVMS_UCHAR
{
    my $ix = shift;
    my $tag = shift;
    my $size = shift;

    state $FCH = {
        0     => 'FCH$M_WASCONTIG',
        1     => 'FCH$M_NOBACKUP',
        2     => 'FCH$M_WRITEBACK',
        3     => 'FCH$M_READCHECK',
        4     => 'FCH$M_WRITCHECK',
        5     => 'FCH$M_CONTIGB',
        6     => 'FCH$M_LOCKED',
        6     => 'FCH$M_CONTIG',
        11    => 'FCH$M_BADACL',
        12    => 'FCH$M_SPOOL',
        13    => 'FCH$M_DIRECTORY',
        14    => 'FCH$M_BADBLOCK',
        15    => 'FCH$M_MARKDEL',
        16    => 'FCH$M_NOCHARGE',
        17    => 'FCH$M_ERASE',
        18    => 'FCH$M_SHELVED',
        20    => 'FCH$M_SCRATCH',
        21    => 'FCH$M_NOMOVE',
        22    => 'FCH$M_NOSHELVABLE',
    } ;

    my ($data, $value) = read_V();

    out2 $data, "  Attribute", Value_V($value);

    for my $bit ( sort { $a <=> $b } keys %{ $FCH } )
    {
        # print "$bit\n";
        if ($value & (1 << $bit) )
        {
            out1 "      [Bit $bit]", $FCH->{$bit} ;
        }
    }
}

sub OpenVMS_2ByteValue
{
    my $ix = shift;
    my $tag = shift;
    my $size = shift;

    my ($data, $value) = read_v();

    out2 $data, "  Attribute", Value_v($value);
}

sub OpenVMS_revision
{
    my $ix = shift;
    my $tag = shift;
    my $size = shift;

    my ($data, $value) = read_v();

    out2 $data, "  Attribute", Value_v($value) . "'Revision Count " . Value_v($value) . "'";
}

sub decode_OpenVMS
{
    my $extraID = shift ;
    my $len = shift;
    my $entry = shift;

    state $openVMS_tags = {
        0x04    => [ 'ATR$C_RECATTR',   \&OpenVMS_DumpBytes  ],
        0x03    => [ 'ATR$C_UCHAR',     \&OpenVMS_UCHAR      ],
        0x11    => [ 'ATR$C_CREDATE',   \&OpenVMS_DateTime   ],
        0x12    => [ 'ATR$C_REVDATE',   \&OpenVMS_DateTime   ],
        0x13    => [ 'ATR$C_EXPDATE',   \&OpenVMS_DateTime   ],
        0x14    => [ 'ATR$C_BAKDATE',   \&OpenVMS_DateTime   ],
        0x0D    => [ 'ATR$C_ASCDATES',  \&OpenVMS_revision   ],
        0x15    => [ 'ATR$C_UIC',       \&OpenVMS_4ByteValue ],
        0x16    => [ 'ATR$C_FPRO',      \&OpenVMS_DumpBytes  ],
        0x17    => [ 'ATR$C_RPRO',      \&OpenVMS_2ByteValue ],
        0x1D    => [ 'ATR$C_JOURNAL',   \&OpenVMS_DumpBytes  ],
        0x1F    => [ 'ATR$C_ADDACLENT', \&OpenVMS_DumpBytes  ],
    } ;

    out_V "  CRC";
    $len -= 4;

    my $ix = 1;
    while ($len)
    {
        my ($data, $tag) = read_v();
        my $tagname = 'Unknown Tag';
        my $decoder = undef;

        if ($openVMS_tags->{$tag})
        {
            ($tagname, $decoder) = @{ $openVMS_tags->{$tag} } ;
        }

        out2 $data,  "Tag #$ix", Value_v($tag) . " '" . $tagname . "'" ;
        my $size = out_v "    Size";

        if (defined $decoder)
        {
            $decoder->($ix, $tag, $size) ;
        }
        else
        {
            outSomeData($size, "    Attribute");
        }

        ++ $ix;
        $len -= $size + 2 + 2;
    }

}

sub getT
{
    my $time = shift ;

    if ($opt_utc)
     { return scalar gmtime($time) // 'Unknown'}
    else
     { return scalar localtime($time) // 'Unknown' }
}

sub getTime
{
    my $time = shift ;

    return "'Invalid Date or Time'"
        if ! defined $time;

    return "'" . getT($time) . "'";
}

bin/zipdetails  view on Meta::CPAN


    return ()
        if ! defined $offset;

    $fh->seek($offset, SEEK_SET) ;

    # Now walk the Central Directory Records
    my $buffer ;
    my $cdIndex = 0;
    my $cdEntryOffset = 0;

    while ($fh->read($buffer, ZIP_CD_FILENAME_OFFSET) == ZIP_CD_FILENAME_OFFSET  &&
           unpack("V", $buffer) == ZIP_CENTRAL_HDR_SIG) {

        my $startHeader = $fh->tell() - ZIP_CD_FILENAME_OFFSET;

        my $cdEntryOffset = $fh->tell() - ZIP_CD_FILENAME_OFFSET;
        $HeaderOffsetIndex->addOffsetNoPrefix($cdEntryOffset, ZIP_CENTRAL_HDR_SIG) ;

        ++ $cdIndex ;

        my $extractVer         = unpack("v", substr($buffer,  6, 1));
        my $gpFlag             = unpack("v", substr($buffer,  8, 2));
        my $lastMod            = unpack("V", substr($buffer, 10, 4));
        my $crc                = unpack("V", substr($buffer, 16, 4));
        my $compressedSize   = unpack("V", substr($buffer, 20, 4));
        my $uncompressedSize = unpack("V", substr($buffer, 24, 4));
        my $filename_length    = unpack("v", substr($buffer, 28, 2));
        my $extra_length       = unpack("v", substr($buffer, 30, 2));
        my $comment_length     = unpack("v", substr($buffer, 32, 2));
        my $diskNumber         = unpack("v", substr($buffer, 34, 2));
        my $locHeaderOffset    = unpack("V", substr($buffer, 42, 4));

        my $cdZip64 = 0;
        my $zip64Sizes = 0;

        if (! full32 $locHeaderOffset)
        {
            # Check for corrupt offset
            # 1. pointing paset EOF
            # 2. offset points forward in the file
            # 3. value at offset is not a CD record signature

            my $commonMessage = "'Local Header Offset' field in '" . Signatures::name(ZIP_CENTRAL_HDR_SIG) . "' is invalid";
            checkOffsetValue($locHeaderOffset, $startHeader, 0, $commonMessage,
                $startHeader + CentralDirectoryEntry::Offset_RelativeOffsetToLocal(),
                ZIP_LOCAL_HDR_SIG, 1) ;
        }

        $fh->read(my $filename, $filename_length) ;

        my $cdEntry = CentralDirectoryEntry->new();

        $cdEntry->centralHeaderOffset($startHeader) ;
        $cdEntry->localHeaderOffset($locHeaderOffset) ;
        $cdEntry->compressedSize($compressedSize) ;
        $cdEntry->uncompressedSize($uncompressedSize) ;
        $cdEntry->extractVersion($extractVer);
        $cdEntry->generalPurposeFlags($gpFlag);
        $cdEntry->filename($filename) ;
        $cdEntry->lastModDateTime($lastMod);
        $cdEntry->languageEncodingFlag($gpFlag & ZIP_GP_FLAG_LANGUAGE_ENCODING) ;
        $cdEntry->diskNumber($diskNumber) ;
        $cdEntry->crc32($crc) ;
        $cdEntry->zip64ExtraPresent($cdZip64) ;

        $cdEntry->std_localHeaderOffset($locHeaderOffset) ;
        $cdEntry->std_compressedSize($compressedSize) ;
        $cdEntry->std_uncompressedSize($uncompressedSize) ;
        $cdEntry->std_diskNumber($diskNumber) ;


        if ($extra_length)
        {
            $fh->read(my $extraField, $extra_length) ;

            # Check for Zip64
            my $zip64Extended = findID(0x0001, $extraField);

            if ($zip64Extended)
            {
                $cdZip64 = 1;
                walk_Zip64_in_CD(1, $zip64Extended, $cdEntry, 0);
            }
        }

        $cdEntry->offsetStart($startHeader) ;
        $cdEntry->offsetEnd($FH->tell() - 1);

        # don't call addEntry until after the extra fields have been scanned
        # the localheader offset value may be updated in th ezip64 extra field.
        $CentralDirectory->addEntry($cdEntry);
        $HeaderOffsetIndex->addOffset($cdEntry->localHeaderOffset, ZIP_LOCAL_HDR_SIG) ;

        skip($fh, $comment_length ) ;
    }

    $FH->seek($fh->tell() - ZIP_CD_FILENAME_OFFSET, SEEK_SET);

    # Check for Digital Signature
    $HeaderOffsetIndex->addOffset($fh->tell() - 4, ZIP_DIGITAL_SIGNATURE_SIG)
        if $fh->read($buffer, 4) == 4  &&
            unpack("V", $buffer) == ZIP_DIGITAL_SIGNATURE_SIG ;

    $CentralDirectory->sortByLocalOffset();
    $HeaderOffsetIndex->sortOffsets();

    $fh->seek($here, SEEK_SET) ;

}

use constant ZIP64_END_CENTRAL_LOC_HDR_SIZE     => 20;
use constant ZIP64_END_CENTRAL_REC_HDR_MIN_SIZE => 56;

sub offsetFromZip64
{
    my $fh = shift ;
    my $here = shift;
    my $eocdSize = shift;

    #### Zip64 end of central directory locator

bin/zipdetails  view on Meta::CPAN

        }
    }
}

{
    package BaseEntry ;

    sub new
    {
        my $class = shift ;

        state $index = 0;

        my %fields = (
                        'index'                 => $index ++,
                        'zip64'                 => 0,
                        'offsetStart'           => 0,
                        'offsetEnd'             => 0,
                        'inCentralDir'          => 0,
                        'encapsulated'          => 0, # enclosed in outer zip
                        'childrenCount'         => 0, # this entry is a zip with enclosed children
                        'streamed'              => 0,
                        'languageEncodingFlag'  => 0,
                        'entryType'             => 0,
                     ) ;

        my $self = bless {}, $class;

        FieldsAndAccessors::Add($class, $self, \%fields) ;

        return $self;
    }

    sub increment_childrenCount
    {
        my $self = shift;
        $self->{childrenCount} ++;
    }
}

{
    package LocalCentralEntryBase ;

    use parent -norequire , 'BaseEntry' ;

    sub new
    {
        my $class = shift ;

        my $self = $class->SUPER::new();


        my %fields = (
                        # fields from the header
                        'centralHeaderOffset'   => 0,
                        'localHeaderOffset'     => 0,

                        'extractVersion'        => 0,
                        'generalPurposeFlags'   => 0,
                        'compressedMethod'      => 0,
                        'lastModDateTime'       => 0,
                        'crc32'                 => 0,
                        'compressedSize'        => 0,
                        'uncompressedSize'      => 0,
                        'filename'              => '',
                        'outputFilename'        => '',
                        # inferred data
                        # 'InCentralDir'          => 0,
                        # 'zip64'                 => 0,

                        'zip64ExtraPresent'     => 0,
                        'zip64SizesPresent'     => 0,
                        'payloadOffset'         => 0,

                        # zip64 extra
                        'zip64_compressedSize'    => undef,
                        'zip64_uncompressedSize'  => undef,
                        'zip64_localHeaderOffset' => undef,
                        'zip64_diskNumber'        => undef,
                        'zip64_diskNumberPresent' => 0,

                        # Values direct from the header before merging any Zip64 values
                        'std_compressedSize'    => undef,
                        'std_uncompressedSize'  => undef,
                        'std_localHeaderOffset' => undef,
                        'std_diskNumber'        => undef,

                        # AES
                        'aesStrength'             => 0,
                        'aesValid'                => 0,

                        # Minizip CD encryption
                        'minizip_secure'          => 0,

                     ) ;

        FieldsAndAccessors::Add($class, $self, \%fields) ;

        return $self;
    }
}

{
    package Zip64EndCentralHeaderEntry ;

    use parent -norequire , 'LocalCentralEntryBase' ;

    sub new
    {
        my $class = shift ;

        my $self = $class->SUPER::new();


        my %fields = (
                        'inCentralDir'          => 1,
                     ) ;

        FieldsAndAccessors::Add($class, $self, \%fields) ;

        return $self;



( run in 3.014 seconds using v1.01-cache-2.11-cpan-75ffa21a3d4 )