@@ -1628,8 +1628,14 @@ def _validate_local_file_entry(self, fp, offset, end_offset):
1628
1628
zip64 = fheader [_FH_UNCOMPRESSED_SIZE ] == 0xffffffff
1629
1629
1630
1630
dd = self ._scan_data_descriptor (fp , pos , end_offset , zip64 )
1631
- if dd is None and not self .strict_descriptor :
1632
- dd = self ._scan_data_descriptor_no_sig (fp , pos , end_offset , zip64 )
1631
+ if dd is None :
1632
+ dd = self ._scan_data_descriptor_no_sig_by_decompression (
1633
+ fp , pos , end_offset , zip64 , fheader [_FH_COMPRESSION_METHOD ])
1634
+ if dd is False :
1635
+ if not self .strict_descriptor :
1636
+ dd = self ._scan_data_descriptor_no_sig (fp , pos , end_offset , zip64 )
1637
+ else :
1638
+ dd = None
1633
1639
if dd is None :
1634
1640
return None
1635
1641
@@ -1705,6 +1711,56 @@ def _scan_data_descriptor_no_sig(self, fp, offset, end_offset, zip64, chunk_size
1705
1711
1706
1712
return None
1707
1713
1714
+ def _scan_data_descriptor_no_sig_by_decompression (self , fp , offset , end_offset , zip64 , method ):
1715
+ dd_fmt = '<LQQ' if zip64 else '<LLL'
1716
+ dd_size = struct .calcsize (dd_fmt )
1717
+
1718
+ if offset + dd_size > end_offset :
1719
+ return False
1720
+
1721
+ try :
1722
+ decompressor = _get_decompressor (method )
1723
+ except NotImplementedError :
1724
+ return False
1725
+
1726
+ if decompressor is None :
1727
+ return False
1728
+
1729
+ # Current LZMADecompressor is unreliable since it's `.eof` is usually
1730
+ # not set as expected.
1731
+ if isinstance (decompressor , LZMADecompressor ):
1732
+ return False
1733
+
1734
+ try :
1735
+ pos = self ._find_compression_end_offset (fp , offset , end_offset - dd_size , decompressor )
1736
+ except Exception :
1737
+ return None
1738
+
1739
+ fp .seek (pos )
1740
+ dd = fp .read (dd_size )
1741
+ crc , compress_size , file_size = struct .unpack (dd_fmt , dd )
1742
+ if pos - offset != compress_size :
1743
+ return None
1744
+
1745
+ return crc , compress_size , file_size , dd_size
1746
+
1747
+ def _find_compression_end_offset (self , fp , offset , end_offset , decompressor , chunk_size = 4096 ):
1748
+ fp .seek (offset )
1749
+ read_size = 0
1750
+ while True :
1751
+ chunk = fp .read (min (chunk_size , end_offset - offset - read_size ))
1752
+ if not chunk :
1753
+ raise EOFError ('Unexpected EOF while decompressing' )
1754
+
1755
+ # may raise on error
1756
+ decompressor .decompress (chunk )
1757
+
1758
+ read_size += len (chunk )
1759
+
1760
+ if decompressor .eof :
1761
+ unused_len = len (decompressor .unused_data )
1762
+ return offset + read_size - unused_len
1763
+
1708
1764
def _calc_local_file_entry_size (self , fp , zinfo ):
1709
1765
fp .seek (zinfo .header_offset )
1710
1766
fheader = self ._read_local_file_header (fp )
0 commit comments