diff --git a/Project.toml b/Project.toml index 4c91185..f427e4f 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "ZipArchives" uuid = "49080126-0e18-4c2a-b176-c102e4b3760c" authors = ["nhz2 "] -version = "2.1.3" +version = "2.1.4" [deps] ArgCheck = "dce04be8-c92d-5529-be00-80e4d2c0e197" diff --git a/src/reader.jl b/src/reader.jl index 43a5ff5..062abf4 100644 --- a/src/reader.jl +++ b/src/reader.jl @@ -99,6 +99,19 @@ Note: if the zip file was corrupted, this might be wrong. """ zip_compressed_size(x::HasEntries, i::Integer)::UInt64 = x.entries[i].compressed_size +""" + zip_compression_method(x::HasEntries, i::Integer)::UInt16 + +Return the compression method used for entry `i`. + +See https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT for a current list of methods. + +Only Store(0x0000) and Deflate(0x0008) supported for now. + +Note: if the zip file was corrupted, this might be wrong. +""" +zip_compression_method(x::HasEntries, i::Integer)::UInt16 = x.entries[i].method + """ zip_iscompressed(x::HasEntries, i::Integer)::Bool @@ -599,15 +612,12 @@ end Throw an ArgumentError if entry cannot be extracted. =# function validate_entry(entry::EntryInfo, fsize::Int64) - if entry.method != Store && entry.method != Deflate - throw(ArgumentError("invalid compression method: $(entry.method). Only Store and Deflate supported for now")) - end # Check for unsupported bit flags @argcheck iszero(entry.bit_flags & 1<<0) "encrypted files not supported" @argcheck iszero(entry.bit_flags & 1<<5) "patched data not supported" @argcheck iszero(entry.bit_flags & 1<<6) "encrypted files not supported" @argcheck iszero(entry.bit_flags & 1<<13) "encrypted files not supported" - @argcheck entry.version_needed ≤ 45 + @argcheck entry.version_needed ≤ 63 # This allows for files to overlap, which sometimes can happen. min_loc_h_size::Int64 = min_local_header_size(entry) @argcheck min_loc_h_size > 29 @@ -691,8 +701,18 @@ function Base.show(io::IO, ::MIME"text/plain", r::ZipReader) end end +""" + zip_entry_data_offset(r::ZipReader, i::Integer)::Int64 -function zip_openentry(r::ZipReader, i::Int) +Return the offset of the start of the compressed data for entry `i` from +the start of the buffer in `r`. + +Throw an error if the local header is invalid. + +See also [`zip_compression_method`](@ref) and [`zip_compressed_size`](@ref). +""" +zip_entry_data_offset(r::ZipReader, i::Integer) = zip_entry_data_offset(r, Int(i)) +function zip_entry_data_offset(r::ZipReader, i::Int)::Int64 fsize::Int64 = length(r.buffer) entry::EntryInfo = r.entries[i] compressed_size::Int64 = entry.compressed_size @@ -719,6 +739,17 @@ function zip_openentry(r::ZipReader, i::Int) @argcheck read(io, local_name_len) == view(r.central_dir_buffer, entry.name_range) + entry_data_offset +end + +function zip_openentry(r::ZipReader, i::Int) + compressed_size::Int64 = zip_compressed_size(r, i) + method = zip_compression_method(r, i) + if method != Store && method != Deflate + throw(ArgumentError("invalid compression method: $(method). Only Store(0) and Deflate(8) supported for now")) + end + entry_data_offset = zip_entry_data_offset(r, i) + begin_ind::Int64 = firstindex(r.buffer) startidx = begin_ind + entry_data_offset @argcheck startidx > begin_ind @@ -733,7 +764,7 @@ function zip_openentry(r::ZipReader, i::Int) elseif method == Deflate return DeflateDecompressorStream(base_io) else - # validate_entry should throw and ArgumentError before this + # should throw and ArgumentError before this error("unreachable") end end \ No newline at end of file diff --git a/test/test_reader.jl b/test/test_reader.jl index cabdd00..35ecaad 100644 --- a/test/test_reader.jl +++ b/test/test_reader.jl @@ -147,10 +147,16 @@ end data_b64 = "UEsDBD8AAgAOAHJb0FaLksVmIgAAABAAAAAJAAAAbHptYV9kYXRhCQQFAF0AAIAAADoaCWd+rnMR0beE5IbQKkMGbV//6/YgAFBLAQI/AD8AAgAOAHJb0FaLksVmIgAAABAAAAAJAAAAAAAAAAAAAACAAQAAAABsem1hX2RhdGFQSwUGAAAAAAEAAQA3AAAASQAAAAAA" data = base64decode(data_b64) r = ZipReader(data) - @test_throws ArgumentError("invalid compression method: 14. Only Store and Deflate supported for now") zip_test_entry(r, 1) - @test_throws ArgumentError("invalid compression method: 14. Only Store and Deflate supported for now") zip_openentry(r, 1) + @test_throws ArgumentError("invalid compression method: 14. Only Store(0) and Deflate(8) supported for now") zip_test_entry(r, 1) + @test_throws ArgumentError("invalid compression method: 14. Only Store(0) and Deflate(8) supported for now") zip_openentry(r, 1) @test zip_iscompressed(r, 1) @test zip_names(r) == ["lzma_data"] + @test ZipArchives.zip_compression_method(r, 1) === 0x000e + entry_data_offset = 39 + compressed_size = 34 + @test ZipArchives.zip_entry_data_offset(r, 1) === Int64(entry_data_offset) + @test ZipArchives.zip_entry_data_offset(r, big(1)) === Int64(entry_data_offset) + @test ZipArchives.zip_compressed_size(r, 1) === UInt64(compressed_size) end @testset "reading file with zip64 disk number" begin