diff --git a/src/calibre/utils/localunzip.py b/src/calibre/utils/localunzip.py index 48c51b7af6..81587ffa96 100644 --- a/src/calibre/utils/localunzip.py +++ b/src/calibre/utils/localunzip.py @@ -24,6 +24,7 @@ HEADER_BYTE_SIG = pack(b'= 16: + raw = f.read(50*1024) + idx = raw.find(DATA_DESCRIPTOR_SIG) + if idx != -1: + f.seek(f.tell() - len(raw) + idx + len(DATA_DESCRIPTOR_SIG)) + return DD(*unpack(b' 0: fname = f.read(header.filename_length) @@ -97,10 +118,16 @@ def read_local_file_header(f): except UnicodeDecodeError: pass fname = decode_arcname(fname).replace('\\', '/') + if header.extra_length > 0: extra = f.read(header.extra_length) if len(extra) != header.extra_length: return + if has_data_descriptors: + desc = find_data_descriptor(f) + header = header._replace(crc32=desc.crc32, + compressed_size=desc.compressed_size, + uncompressed_size=desc.uncompressed_size) return LocalHeader(*( header[:-2] + (fname, extra) )) @@ -142,11 +169,13 @@ def _extractall(f, path=None, file_info=None): header = read_local_file_header(f) if not header: break + has_data_descriptors = header.flags & (1 << 3) + seekval = header.compressed_size + (16 if has_data_descriptors else 0) found = True parts = header.filename.split('/') if header.uncompressed_size == 0: # Directory - f.seek(f.tell() + header.compressed_size) + f.seek(f.tell()+seekval) if path is not None: bdir = os.path.join(path, *parts) if not os.path.exists(bdir): @@ -167,7 +196,7 @@ def _extractall(f, path=None, file_info=None): else: copy_compressed_file(f, header.compressed_size, o) else: - f.seek(f.tell() + header.compressed_size) + f.seek(f.tell()+seekval) if not found: raise ValueError('Not a ZIP file')