1*cda5da8dSAndroid Build Coastguard Worker""" 2*cda5da8dSAndroid Build Coastguard WorkerRead and write ZIP files. 3*cda5da8dSAndroid Build Coastguard Worker 4*cda5da8dSAndroid Build Coastguard WorkerXXX references to utf-8 need further investigation. 5*cda5da8dSAndroid Build Coastguard Worker""" 6*cda5da8dSAndroid Build Coastguard Workerimport binascii 7*cda5da8dSAndroid Build Coastguard Workerimport importlib.util 8*cda5da8dSAndroid Build Coastguard Workerimport io 9*cda5da8dSAndroid Build Coastguard Workerimport itertools 10*cda5da8dSAndroid Build Coastguard Workerimport os 11*cda5da8dSAndroid Build Coastguard Workerimport posixpath 12*cda5da8dSAndroid Build Coastguard Workerimport shutil 13*cda5da8dSAndroid Build Coastguard Workerimport stat 14*cda5da8dSAndroid Build Coastguard Workerimport struct 15*cda5da8dSAndroid Build Coastguard Workerimport sys 16*cda5da8dSAndroid Build Coastguard Workerimport threading 17*cda5da8dSAndroid Build Coastguard Workerimport time 18*cda5da8dSAndroid Build Coastguard Workerimport contextlib 19*cda5da8dSAndroid Build Coastguard Workerimport pathlib 20*cda5da8dSAndroid Build Coastguard Worker 21*cda5da8dSAndroid Build Coastguard Workertry: 22*cda5da8dSAndroid Build Coastguard Worker import zlib # We may need its compression method 23*cda5da8dSAndroid Build Coastguard Worker crc32 = zlib.crc32 24*cda5da8dSAndroid Build Coastguard Workerexcept ImportError: 25*cda5da8dSAndroid Build Coastguard Worker zlib = None 26*cda5da8dSAndroid Build Coastguard Worker crc32 = binascii.crc32 27*cda5da8dSAndroid Build Coastguard Worker 28*cda5da8dSAndroid Build Coastguard Workertry: 29*cda5da8dSAndroid Build Coastguard Worker import bz2 # We may need its compression method 30*cda5da8dSAndroid Build Coastguard Workerexcept ImportError: 31*cda5da8dSAndroid Build Coastguard Worker bz2 = None 32*cda5da8dSAndroid Build Coastguard Worker 33*cda5da8dSAndroid Build Coastguard Workertry: 34*cda5da8dSAndroid Build Coastguard Worker import lzma # We may need its compression method 35*cda5da8dSAndroid Build Coastguard Workerexcept ImportError: 36*cda5da8dSAndroid Build Coastguard Worker lzma = None 37*cda5da8dSAndroid Build Coastguard Worker 38*cda5da8dSAndroid Build Coastguard Worker__all__ = ["BadZipFile", "BadZipfile", "error", 39*cda5da8dSAndroid Build Coastguard Worker "ZIP_STORED", "ZIP_DEFLATED", "ZIP_BZIP2", "ZIP_LZMA", 40*cda5da8dSAndroid Build Coastguard Worker "is_zipfile", "ZipInfo", "ZipFile", "PyZipFile", "LargeZipFile", 41*cda5da8dSAndroid Build Coastguard Worker "Path"] 42*cda5da8dSAndroid Build Coastguard Worker 43*cda5da8dSAndroid Build Coastguard Workerclass BadZipFile(Exception): 44*cda5da8dSAndroid Build Coastguard Worker pass 45*cda5da8dSAndroid Build Coastguard Worker 46*cda5da8dSAndroid Build Coastguard Worker 47*cda5da8dSAndroid Build Coastguard Workerclass LargeZipFile(Exception): 48*cda5da8dSAndroid Build Coastguard Worker """ 49*cda5da8dSAndroid Build Coastguard Worker Raised when writing a zipfile, the zipfile requires ZIP64 extensions 50*cda5da8dSAndroid Build Coastguard Worker and those extensions are disabled. 51*cda5da8dSAndroid Build Coastguard Worker """ 52*cda5da8dSAndroid Build Coastguard Worker 53*cda5da8dSAndroid Build Coastguard Workererror = BadZipfile = BadZipFile # Pre-3.2 compatibility names 54*cda5da8dSAndroid Build Coastguard Worker 55*cda5da8dSAndroid Build Coastguard Worker 56*cda5da8dSAndroid Build Coastguard WorkerZIP64_LIMIT = (1 << 31) - 1 57*cda5da8dSAndroid Build Coastguard WorkerZIP_FILECOUNT_LIMIT = (1 << 16) - 1 58*cda5da8dSAndroid Build Coastguard WorkerZIP_MAX_COMMENT = (1 << 16) - 1 59*cda5da8dSAndroid Build Coastguard Worker 60*cda5da8dSAndroid Build Coastguard Worker# constants for Zip file compression methods 61*cda5da8dSAndroid Build Coastguard WorkerZIP_STORED = 0 62*cda5da8dSAndroid Build Coastguard WorkerZIP_DEFLATED = 8 63*cda5da8dSAndroid Build Coastguard WorkerZIP_BZIP2 = 12 64*cda5da8dSAndroid Build Coastguard WorkerZIP_LZMA = 14 65*cda5da8dSAndroid Build Coastguard Worker# Other ZIP compression methods not supported 66*cda5da8dSAndroid Build Coastguard Worker 67*cda5da8dSAndroid Build Coastguard WorkerDEFAULT_VERSION = 20 68*cda5da8dSAndroid Build Coastguard WorkerZIP64_VERSION = 45 69*cda5da8dSAndroid Build Coastguard WorkerBZIP2_VERSION = 46 70*cda5da8dSAndroid Build Coastguard WorkerLZMA_VERSION = 63 71*cda5da8dSAndroid Build Coastguard Worker# we recognize (but not necessarily support) all features up to that version 72*cda5da8dSAndroid Build Coastguard WorkerMAX_EXTRACT_VERSION = 63 73*cda5da8dSAndroid Build Coastguard Worker 74*cda5da8dSAndroid Build Coastguard Worker# Below are some formats and associated data for reading/writing headers using 75*cda5da8dSAndroid Build Coastguard Worker# the struct module. The names and structures of headers/records are those used 76*cda5da8dSAndroid Build Coastguard Worker# in the PKWARE description of the ZIP file format: 77*cda5da8dSAndroid Build Coastguard Worker# http://www.pkware.com/documents/casestudies/APPNOTE.TXT 78*cda5da8dSAndroid Build Coastguard Worker# (URL valid as of January 2008) 79*cda5da8dSAndroid Build Coastguard Worker 80*cda5da8dSAndroid Build Coastguard Worker# The "end of central directory" structure, magic number, size, and indices 81*cda5da8dSAndroid Build Coastguard Worker# (section V.I in the format document) 82*cda5da8dSAndroid Build Coastguard WorkerstructEndArchive = b"<4s4H2LH" 83*cda5da8dSAndroid Build Coastguard WorkerstringEndArchive = b"PK\005\006" 84*cda5da8dSAndroid Build Coastguard WorkersizeEndCentDir = struct.calcsize(structEndArchive) 85*cda5da8dSAndroid Build Coastguard Worker 86*cda5da8dSAndroid Build Coastguard Worker_ECD_SIGNATURE = 0 87*cda5da8dSAndroid Build Coastguard Worker_ECD_DISK_NUMBER = 1 88*cda5da8dSAndroid Build Coastguard Worker_ECD_DISK_START = 2 89*cda5da8dSAndroid Build Coastguard Worker_ECD_ENTRIES_THIS_DISK = 3 90*cda5da8dSAndroid Build Coastguard Worker_ECD_ENTRIES_TOTAL = 4 91*cda5da8dSAndroid Build Coastguard Worker_ECD_SIZE = 5 92*cda5da8dSAndroid Build Coastguard Worker_ECD_OFFSET = 6 93*cda5da8dSAndroid Build Coastguard Worker_ECD_COMMENT_SIZE = 7 94*cda5da8dSAndroid Build Coastguard Worker# These last two indices are not part of the structure as defined in the 95*cda5da8dSAndroid Build Coastguard Worker# spec, but they are used internally by this module as a convenience 96*cda5da8dSAndroid Build Coastguard Worker_ECD_COMMENT = 8 97*cda5da8dSAndroid Build Coastguard Worker_ECD_LOCATION = 9 98*cda5da8dSAndroid Build Coastguard Worker 99*cda5da8dSAndroid Build Coastguard Worker# The "central directory" structure, magic number, size, and indices 100*cda5da8dSAndroid Build Coastguard Worker# of entries in the structure (section V.F in the format document) 101*cda5da8dSAndroid Build Coastguard WorkerstructCentralDir = "<4s4B4HL2L5H2L" 102*cda5da8dSAndroid Build Coastguard WorkerstringCentralDir = b"PK\001\002" 103*cda5da8dSAndroid Build Coastguard WorkersizeCentralDir = struct.calcsize(structCentralDir) 104*cda5da8dSAndroid Build Coastguard Worker 105*cda5da8dSAndroid Build Coastguard Worker# indexes of entries in the central directory structure 106*cda5da8dSAndroid Build Coastguard Worker_CD_SIGNATURE = 0 107*cda5da8dSAndroid Build Coastguard Worker_CD_CREATE_VERSION = 1 108*cda5da8dSAndroid Build Coastguard Worker_CD_CREATE_SYSTEM = 2 109*cda5da8dSAndroid Build Coastguard Worker_CD_EXTRACT_VERSION = 3 110*cda5da8dSAndroid Build Coastguard Worker_CD_EXTRACT_SYSTEM = 4 111*cda5da8dSAndroid Build Coastguard Worker_CD_FLAG_BITS = 5 112*cda5da8dSAndroid Build Coastguard Worker_CD_COMPRESS_TYPE = 6 113*cda5da8dSAndroid Build Coastguard Worker_CD_TIME = 7 114*cda5da8dSAndroid Build Coastguard Worker_CD_DATE = 8 115*cda5da8dSAndroid Build Coastguard Worker_CD_CRC = 9 116*cda5da8dSAndroid Build Coastguard Worker_CD_COMPRESSED_SIZE = 10 117*cda5da8dSAndroid Build Coastguard Worker_CD_UNCOMPRESSED_SIZE = 11 118*cda5da8dSAndroid Build Coastguard Worker_CD_FILENAME_LENGTH = 12 119*cda5da8dSAndroid Build Coastguard Worker_CD_EXTRA_FIELD_LENGTH = 13 120*cda5da8dSAndroid Build Coastguard Worker_CD_COMMENT_LENGTH = 14 121*cda5da8dSAndroid Build Coastguard Worker_CD_DISK_NUMBER_START = 15 122*cda5da8dSAndroid Build Coastguard Worker_CD_INTERNAL_FILE_ATTRIBUTES = 16 123*cda5da8dSAndroid Build Coastguard Worker_CD_EXTERNAL_FILE_ATTRIBUTES = 17 124*cda5da8dSAndroid Build Coastguard Worker_CD_LOCAL_HEADER_OFFSET = 18 125*cda5da8dSAndroid Build Coastguard Worker 126*cda5da8dSAndroid Build Coastguard Worker# General purpose bit flags 127*cda5da8dSAndroid Build Coastguard Worker# Zip Appnote: 4.4.4 general purpose bit flag: (2 bytes) 128*cda5da8dSAndroid Build Coastguard Worker_MASK_ENCRYPTED = 1 << 0 129*cda5da8dSAndroid Build Coastguard Worker# Bits 1 and 2 have different meanings depending on the compression used. 130*cda5da8dSAndroid Build Coastguard Worker_MASK_COMPRESS_OPTION_1 = 1 << 1 131*cda5da8dSAndroid Build Coastguard Worker# _MASK_COMPRESS_OPTION_2 = 1 << 2 132*cda5da8dSAndroid Build Coastguard Worker# _MASK_USE_DATA_DESCRIPTOR: If set, crc-32, compressed size and uncompressed 133*cda5da8dSAndroid Build Coastguard Worker# size are zero in the local header and the real values are written in the data 134*cda5da8dSAndroid Build Coastguard Worker# descriptor immediately following the compressed data. 135*cda5da8dSAndroid Build Coastguard Worker_MASK_USE_DATA_DESCRIPTOR = 1 << 3 136*cda5da8dSAndroid Build Coastguard Worker# Bit 4: Reserved for use with compression method 8, for enhanced deflating. 137*cda5da8dSAndroid Build Coastguard Worker# _MASK_RESERVED_BIT_4 = 1 << 4 138*cda5da8dSAndroid Build Coastguard Worker_MASK_COMPRESSED_PATCH = 1 << 5 139*cda5da8dSAndroid Build Coastguard Worker_MASK_STRONG_ENCRYPTION = 1 << 6 140*cda5da8dSAndroid Build Coastguard Worker# _MASK_UNUSED_BIT_7 = 1 << 7 141*cda5da8dSAndroid Build Coastguard Worker# _MASK_UNUSED_BIT_8 = 1 << 8 142*cda5da8dSAndroid Build Coastguard Worker# _MASK_UNUSED_BIT_9 = 1 << 9 143*cda5da8dSAndroid Build Coastguard Worker# _MASK_UNUSED_BIT_10 = 1 << 10 144*cda5da8dSAndroid Build Coastguard Worker_MASK_UTF_FILENAME = 1 << 11 145*cda5da8dSAndroid Build Coastguard Worker# Bit 12: Reserved by PKWARE for enhanced compression. 146*cda5da8dSAndroid Build Coastguard Worker# _MASK_RESERVED_BIT_12 = 1 << 12 147*cda5da8dSAndroid Build Coastguard Worker# _MASK_ENCRYPTED_CENTRAL_DIR = 1 << 13 148*cda5da8dSAndroid Build Coastguard Worker# Bit 14, 15: Reserved by PKWARE 149*cda5da8dSAndroid Build Coastguard Worker# _MASK_RESERVED_BIT_14 = 1 << 14 150*cda5da8dSAndroid Build Coastguard Worker# _MASK_RESERVED_BIT_15 = 1 << 15 151*cda5da8dSAndroid Build Coastguard Worker 152*cda5da8dSAndroid Build Coastguard Worker# The "local file header" structure, magic number, size, and indices 153*cda5da8dSAndroid Build Coastguard Worker# (section V.A in the format document) 154*cda5da8dSAndroid Build Coastguard WorkerstructFileHeader = "<4s2B4HL2L2H" 155*cda5da8dSAndroid Build Coastguard WorkerstringFileHeader = b"PK\003\004" 156*cda5da8dSAndroid Build Coastguard WorkersizeFileHeader = struct.calcsize(structFileHeader) 157*cda5da8dSAndroid Build Coastguard Worker 158*cda5da8dSAndroid Build Coastguard Worker_FH_SIGNATURE = 0 159*cda5da8dSAndroid Build Coastguard Worker_FH_EXTRACT_VERSION = 1 160*cda5da8dSAndroid Build Coastguard Worker_FH_EXTRACT_SYSTEM = 2 161*cda5da8dSAndroid Build Coastguard Worker_FH_GENERAL_PURPOSE_FLAG_BITS = 3 162*cda5da8dSAndroid Build Coastguard Worker_FH_COMPRESSION_METHOD = 4 163*cda5da8dSAndroid Build Coastguard Worker_FH_LAST_MOD_TIME = 5 164*cda5da8dSAndroid Build Coastguard Worker_FH_LAST_MOD_DATE = 6 165*cda5da8dSAndroid Build Coastguard Worker_FH_CRC = 7 166*cda5da8dSAndroid Build Coastguard Worker_FH_COMPRESSED_SIZE = 8 167*cda5da8dSAndroid Build Coastguard Worker_FH_UNCOMPRESSED_SIZE = 9 168*cda5da8dSAndroid Build Coastguard Worker_FH_FILENAME_LENGTH = 10 169*cda5da8dSAndroid Build Coastguard Worker_FH_EXTRA_FIELD_LENGTH = 11 170*cda5da8dSAndroid Build Coastguard Worker 171*cda5da8dSAndroid Build Coastguard Worker# The "Zip64 end of central directory locator" structure, magic number, and size 172*cda5da8dSAndroid Build Coastguard WorkerstructEndArchive64Locator = "<4sLQL" 173*cda5da8dSAndroid Build Coastguard WorkerstringEndArchive64Locator = b"PK\x06\x07" 174*cda5da8dSAndroid Build Coastguard WorkersizeEndCentDir64Locator = struct.calcsize(structEndArchive64Locator) 175*cda5da8dSAndroid Build Coastguard Worker 176*cda5da8dSAndroid Build Coastguard Worker# The "Zip64 end of central directory" record, magic number, size, and indices 177*cda5da8dSAndroid Build Coastguard Worker# (section V.G in the format document) 178*cda5da8dSAndroid Build Coastguard WorkerstructEndArchive64 = "<4sQ2H2L4Q" 179*cda5da8dSAndroid Build Coastguard WorkerstringEndArchive64 = b"PK\x06\x06" 180*cda5da8dSAndroid Build Coastguard WorkersizeEndCentDir64 = struct.calcsize(structEndArchive64) 181*cda5da8dSAndroid Build Coastguard Worker 182*cda5da8dSAndroid Build Coastguard Worker_CD64_SIGNATURE = 0 183*cda5da8dSAndroid Build Coastguard Worker_CD64_DIRECTORY_RECSIZE = 1 184*cda5da8dSAndroid Build Coastguard Worker_CD64_CREATE_VERSION = 2 185*cda5da8dSAndroid Build Coastguard Worker_CD64_EXTRACT_VERSION = 3 186*cda5da8dSAndroid Build Coastguard Worker_CD64_DISK_NUMBER = 4 187*cda5da8dSAndroid Build Coastguard Worker_CD64_DISK_NUMBER_START = 5 188*cda5da8dSAndroid Build Coastguard Worker_CD64_NUMBER_ENTRIES_THIS_DISK = 6 189*cda5da8dSAndroid Build Coastguard Worker_CD64_NUMBER_ENTRIES_TOTAL = 7 190*cda5da8dSAndroid Build Coastguard Worker_CD64_DIRECTORY_SIZE = 8 191*cda5da8dSAndroid Build Coastguard Worker_CD64_OFFSET_START_CENTDIR = 9 192*cda5da8dSAndroid Build Coastguard Worker 193*cda5da8dSAndroid Build Coastguard Worker_DD_SIGNATURE = 0x08074b50 194*cda5da8dSAndroid Build Coastguard Worker 195*cda5da8dSAndroid Build Coastguard Worker_EXTRA_FIELD_STRUCT = struct.Struct('<HH') 196*cda5da8dSAndroid Build Coastguard Worker 197*cda5da8dSAndroid Build Coastguard Workerdef _strip_extra(extra, xids): 198*cda5da8dSAndroid Build Coastguard Worker # Remove Extra Fields with specified IDs. 199*cda5da8dSAndroid Build Coastguard Worker unpack = _EXTRA_FIELD_STRUCT.unpack 200*cda5da8dSAndroid Build Coastguard Worker modified = False 201*cda5da8dSAndroid Build Coastguard Worker buffer = [] 202*cda5da8dSAndroid Build Coastguard Worker start = i = 0 203*cda5da8dSAndroid Build Coastguard Worker while i + 4 <= len(extra): 204*cda5da8dSAndroid Build Coastguard Worker xid, xlen = unpack(extra[i : i + 4]) 205*cda5da8dSAndroid Build Coastguard Worker j = i + 4 + xlen 206*cda5da8dSAndroid Build Coastguard Worker if xid in xids: 207*cda5da8dSAndroid Build Coastguard Worker if i != start: 208*cda5da8dSAndroid Build Coastguard Worker buffer.append(extra[start : i]) 209*cda5da8dSAndroid Build Coastguard Worker start = j 210*cda5da8dSAndroid Build Coastguard Worker modified = True 211*cda5da8dSAndroid Build Coastguard Worker i = j 212*cda5da8dSAndroid Build Coastguard Worker if not modified: 213*cda5da8dSAndroid Build Coastguard Worker return extra 214*cda5da8dSAndroid Build Coastguard Worker if start != len(extra): 215*cda5da8dSAndroid Build Coastguard Worker buffer.append(extra[start:]) 216*cda5da8dSAndroid Build Coastguard Worker return b''.join(buffer) 217*cda5da8dSAndroid Build Coastguard Worker 218*cda5da8dSAndroid Build Coastguard Workerdef _check_zipfile(fp): 219*cda5da8dSAndroid Build Coastguard Worker try: 220*cda5da8dSAndroid Build Coastguard Worker if _EndRecData(fp): 221*cda5da8dSAndroid Build Coastguard Worker return True # file has correct magic number 222*cda5da8dSAndroid Build Coastguard Worker except OSError: 223*cda5da8dSAndroid Build Coastguard Worker pass 224*cda5da8dSAndroid Build Coastguard Worker return False 225*cda5da8dSAndroid Build Coastguard Worker 226*cda5da8dSAndroid Build Coastguard Workerdef is_zipfile(filename): 227*cda5da8dSAndroid Build Coastguard Worker """Quickly see if a file is a ZIP file by checking the magic number. 228*cda5da8dSAndroid Build Coastguard Worker 229*cda5da8dSAndroid Build Coastguard Worker The filename argument may be a file or file-like object too. 230*cda5da8dSAndroid Build Coastguard Worker """ 231*cda5da8dSAndroid Build Coastguard Worker result = False 232*cda5da8dSAndroid Build Coastguard Worker try: 233*cda5da8dSAndroid Build Coastguard Worker if hasattr(filename, "read"): 234*cda5da8dSAndroid Build Coastguard Worker result = _check_zipfile(fp=filename) 235*cda5da8dSAndroid Build Coastguard Worker else: 236*cda5da8dSAndroid Build Coastguard Worker with open(filename, "rb") as fp: 237*cda5da8dSAndroid Build Coastguard Worker result = _check_zipfile(fp) 238*cda5da8dSAndroid Build Coastguard Worker except OSError: 239*cda5da8dSAndroid Build Coastguard Worker pass 240*cda5da8dSAndroid Build Coastguard Worker return result 241*cda5da8dSAndroid Build Coastguard Worker 242*cda5da8dSAndroid Build Coastguard Workerdef _EndRecData64(fpin, offset, endrec): 243*cda5da8dSAndroid Build Coastguard Worker """ 244*cda5da8dSAndroid Build Coastguard Worker Read the ZIP64 end-of-archive records and use that to update endrec 245*cda5da8dSAndroid Build Coastguard Worker """ 246*cda5da8dSAndroid Build Coastguard Worker try: 247*cda5da8dSAndroid Build Coastguard Worker fpin.seek(offset - sizeEndCentDir64Locator, 2) 248*cda5da8dSAndroid Build Coastguard Worker except OSError: 249*cda5da8dSAndroid Build Coastguard Worker # If the seek fails, the file is not large enough to contain a ZIP64 250*cda5da8dSAndroid Build Coastguard Worker # end-of-archive record, so just return the end record we were given. 251*cda5da8dSAndroid Build Coastguard Worker return endrec 252*cda5da8dSAndroid Build Coastguard Worker 253*cda5da8dSAndroid Build Coastguard Worker data = fpin.read(sizeEndCentDir64Locator) 254*cda5da8dSAndroid Build Coastguard Worker if len(data) != sizeEndCentDir64Locator: 255*cda5da8dSAndroid Build Coastguard Worker return endrec 256*cda5da8dSAndroid Build Coastguard Worker sig, diskno, reloff, disks = struct.unpack(structEndArchive64Locator, data) 257*cda5da8dSAndroid Build Coastguard Worker if sig != stringEndArchive64Locator: 258*cda5da8dSAndroid Build Coastguard Worker return endrec 259*cda5da8dSAndroid Build Coastguard Worker 260*cda5da8dSAndroid Build Coastguard Worker if diskno != 0 or disks > 1: 261*cda5da8dSAndroid Build Coastguard Worker raise BadZipFile("zipfiles that span multiple disks are not supported") 262*cda5da8dSAndroid Build Coastguard Worker 263*cda5da8dSAndroid Build Coastguard Worker # Assume no 'zip64 extensible data' 264*cda5da8dSAndroid Build Coastguard Worker fpin.seek(offset - sizeEndCentDir64Locator - sizeEndCentDir64, 2) 265*cda5da8dSAndroid Build Coastguard Worker data = fpin.read(sizeEndCentDir64) 266*cda5da8dSAndroid Build Coastguard Worker if len(data) != sizeEndCentDir64: 267*cda5da8dSAndroid Build Coastguard Worker return endrec 268*cda5da8dSAndroid Build Coastguard Worker sig, sz, create_version, read_version, disk_num, disk_dir, \ 269*cda5da8dSAndroid Build Coastguard Worker dircount, dircount2, dirsize, diroffset = \ 270*cda5da8dSAndroid Build Coastguard Worker struct.unpack(structEndArchive64, data) 271*cda5da8dSAndroid Build Coastguard Worker if sig != stringEndArchive64: 272*cda5da8dSAndroid Build Coastguard Worker return endrec 273*cda5da8dSAndroid Build Coastguard Worker 274*cda5da8dSAndroid Build Coastguard Worker # Update the original endrec using data from the ZIP64 record 275*cda5da8dSAndroid Build Coastguard Worker endrec[_ECD_SIGNATURE] = sig 276*cda5da8dSAndroid Build Coastguard Worker endrec[_ECD_DISK_NUMBER] = disk_num 277*cda5da8dSAndroid Build Coastguard Worker endrec[_ECD_DISK_START] = disk_dir 278*cda5da8dSAndroid Build Coastguard Worker endrec[_ECD_ENTRIES_THIS_DISK] = dircount 279*cda5da8dSAndroid Build Coastguard Worker endrec[_ECD_ENTRIES_TOTAL] = dircount2 280*cda5da8dSAndroid Build Coastguard Worker endrec[_ECD_SIZE] = dirsize 281*cda5da8dSAndroid Build Coastguard Worker endrec[_ECD_OFFSET] = diroffset 282*cda5da8dSAndroid Build Coastguard Worker return endrec 283*cda5da8dSAndroid Build Coastguard Worker 284*cda5da8dSAndroid Build Coastguard Worker 285*cda5da8dSAndroid Build Coastguard Workerdef _EndRecData(fpin): 286*cda5da8dSAndroid Build Coastguard Worker """Return data from the "End of Central Directory" record, or None. 287*cda5da8dSAndroid Build Coastguard Worker 288*cda5da8dSAndroid Build Coastguard Worker The data is a list of the nine items in the ZIP "End of central dir" 289*cda5da8dSAndroid Build Coastguard Worker record followed by a tenth item, the file seek offset of this record.""" 290*cda5da8dSAndroid Build Coastguard Worker 291*cda5da8dSAndroid Build Coastguard Worker # Determine file size 292*cda5da8dSAndroid Build Coastguard Worker fpin.seek(0, 2) 293*cda5da8dSAndroid Build Coastguard Worker filesize = fpin.tell() 294*cda5da8dSAndroid Build Coastguard Worker 295*cda5da8dSAndroid Build Coastguard Worker # Check to see if this is ZIP file with no archive comment (the 296*cda5da8dSAndroid Build Coastguard Worker # "end of central directory" structure should be the last item in the 297*cda5da8dSAndroid Build Coastguard Worker # file if this is the case). 298*cda5da8dSAndroid Build Coastguard Worker try: 299*cda5da8dSAndroid Build Coastguard Worker fpin.seek(-sizeEndCentDir, 2) 300*cda5da8dSAndroid Build Coastguard Worker except OSError: 301*cda5da8dSAndroid Build Coastguard Worker return None 302*cda5da8dSAndroid Build Coastguard Worker data = fpin.read() 303*cda5da8dSAndroid Build Coastguard Worker if (len(data) == sizeEndCentDir and 304*cda5da8dSAndroid Build Coastguard Worker data[0:4] == stringEndArchive and 305*cda5da8dSAndroid Build Coastguard Worker data[-2:] == b"\000\000"): 306*cda5da8dSAndroid Build Coastguard Worker # the signature is correct and there's no comment, unpack structure 307*cda5da8dSAndroid Build Coastguard Worker endrec = struct.unpack(structEndArchive, data) 308*cda5da8dSAndroid Build Coastguard Worker endrec=list(endrec) 309*cda5da8dSAndroid Build Coastguard Worker 310*cda5da8dSAndroid Build Coastguard Worker # Append a blank comment and record start offset 311*cda5da8dSAndroid Build Coastguard Worker endrec.append(b"") 312*cda5da8dSAndroid Build Coastguard Worker endrec.append(filesize - sizeEndCentDir) 313*cda5da8dSAndroid Build Coastguard Worker 314*cda5da8dSAndroid Build Coastguard Worker # Try to read the "Zip64 end of central directory" structure 315*cda5da8dSAndroid Build Coastguard Worker return _EndRecData64(fpin, -sizeEndCentDir, endrec) 316*cda5da8dSAndroid Build Coastguard Worker 317*cda5da8dSAndroid Build Coastguard Worker # Either this is not a ZIP file, or it is a ZIP file with an archive 318*cda5da8dSAndroid Build Coastguard Worker # comment. Search the end of the file for the "end of central directory" 319*cda5da8dSAndroid Build Coastguard Worker # record signature. The comment is the last item in the ZIP file and may be 320*cda5da8dSAndroid Build Coastguard Worker # up to 64K long. It is assumed that the "end of central directory" magic 321*cda5da8dSAndroid Build Coastguard Worker # number does not appear in the comment. 322*cda5da8dSAndroid Build Coastguard Worker maxCommentStart = max(filesize - (1 << 16) - sizeEndCentDir, 0) 323*cda5da8dSAndroid Build Coastguard Worker fpin.seek(maxCommentStart, 0) 324*cda5da8dSAndroid Build Coastguard Worker data = fpin.read() 325*cda5da8dSAndroid Build Coastguard Worker start = data.rfind(stringEndArchive) 326*cda5da8dSAndroid Build Coastguard Worker if start >= 0: 327*cda5da8dSAndroid Build Coastguard Worker # found the magic number; attempt to unpack and interpret 328*cda5da8dSAndroid Build Coastguard Worker recData = data[start:start+sizeEndCentDir] 329*cda5da8dSAndroid Build Coastguard Worker if len(recData) != sizeEndCentDir: 330*cda5da8dSAndroid Build Coastguard Worker # Zip file is corrupted. 331*cda5da8dSAndroid Build Coastguard Worker return None 332*cda5da8dSAndroid Build Coastguard Worker endrec = list(struct.unpack(structEndArchive, recData)) 333*cda5da8dSAndroid Build Coastguard Worker commentSize = endrec[_ECD_COMMENT_SIZE] #as claimed by the zip file 334*cda5da8dSAndroid Build Coastguard Worker comment = data[start+sizeEndCentDir:start+sizeEndCentDir+commentSize] 335*cda5da8dSAndroid Build Coastguard Worker endrec.append(comment) 336*cda5da8dSAndroid Build Coastguard Worker endrec.append(maxCommentStart + start) 337*cda5da8dSAndroid Build Coastguard Worker 338*cda5da8dSAndroid Build Coastguard Worker # Try to read the "Zip64 end of central directory" structure 339*cda5da8dSAndroid Build Coastguard Worker return _EndRecData64(fpin, maxCommentStart + start - filesize, 340*cda5da8dSAndroid Build Coastguard Worker endrec) 341*cda5da8dSAndroid Build Coastguard Worker 342*cda5da8dSAndroid Build Coastguard Worker # Unable to find a valid end of central directory structure 343*cda5da8dSAndroid Build Coastguard Worker return None 344*cda5da8dSAndroid Build Coastguard Worker 345*cda5da8dSAndroid Build Coastguard Worker 346*cda5da8dSAndroid Build Coastguard Workerclass ZipInfo (object): 347*cda5da8dSAndroid Build Coastguard Worker """Class with attributes describing each file in the ZIP archive.""" 348*cda5da8dSAndroid Build Coastguard Worker 349*cda5da8dSAndroid Build Coastguard Worker __slots__ = ( 350*cda5da8dSAndroid Build Coastguard Worker 'orig_filename', 351*cda5da8dSAndroid Build Coastguard Worker 'filename', 352*cda5da8dSAndroid Build Coastguard Worker 'date_time', 353*cda5da8dSAndroid Build Coastguard Worker 'compress_type', 354*cda5da8dSAndroid Build Coastguard Worker '_compresslevel', 355*cda5da8dSAndroid Build Coastguard Worker 'comment', 356*cda5da8dSAndroid Build Coastguard Worker 'extra', 357*cda5da8dSAndroid Build Coastguard Worker 'create_system', 358*cda5da8dSAndroid Build Coastguard Worker 'create_version', 359*cda5da8dSAndroid Build Coastguard Worker 'extract_version', 360*cda5da8dSAndroid Build Coastguard Worker 'reserved', 361*cda5da8dSAndroid Build Coastguard Worker 'flag_bits', 362*cda5da8dSAndroid Build Coastguard Worker 'volume', 363*cda5da8dSAndroid Build Coastguard Worker 'internal_attr', 364*cda5da8dSAndroid Build Coastguard Worker 'external_attr', 365*cda5da8dSAndroid Build Coastguard Worker 'header_offset', 366*cda5da8dSAndroid Build Coastguard Worker 'CRC', 367*cda5da8dSAndroid Build Coastguard Worker 'compress_size', 368*cda5da8dSAndroid Build Coastguard Worker 'file_size', 369*cda5da8dSAndroid Build Coastguard Worker '_raw_time', 370*cda5da8dSAndroid Build Coastguard Worker ) 371*cda5da8dSAndroid Build Coastguard Worker 372*cda5da8dSAndroid Build Coastguard Worker def __init__(self, filename="NoName", date_time=(1980,1,1,0,0,0)): 373*cda5da8dSAndroid Build Coastguard Worker self.orig_filename = filename # Original file name in archive 374*cda5da8dSAndroid Build Coastguard Worker 375*cda5da8dSAndroid Build Coastguard Worker # Terminate the file name at the first null byte. Null bytes in file 376*cda5da8dSAndroid Build Coastguard Worker # names are used as tricks by viruses in archives. 377*cda5da8dSAndroid Build Coastguard Worker null_byte = filename.find(chr(0)) 378*cda5da8dSAndroid Build Coastguard Worker if null_byte >= 0: 379*cda5da8dSAndroid Build Coastguard Worker filename = filename[0:null_byte] 380*cda5da8dSAndroid Build Coastguard Worker # This is used to ensure paths in generated ZIP files always use 381*cda5da8dSAndroid Build Coastguard Worker # forward slashes as the directory separator, as required by the 382*cda5da8dSAndroid Build Coastguard Worker # ZIP format specification. 383*cda5da8dSAndroid Build Coastguard Worker if os.sep != "/" and os.sep in filename: 384*cda5da8dSAndroid Build Coastguard Worker filename = filename.replace(os.sep, "/") 385*cda5da8dSAndroid Build Coastguard Worker 386*cda5da8dSAndroid Build Coastguard Worker self.filename = filename # Normalized file name 387*cda5da8dSAndroid Build Coastguard Worker self.date_time = date_time # year, month, day, hour, min, sec 388*cda5da8dSAndroid Build Coastguard Worker 389*cda5da8dSAndroid Build Coastguard Worker if date_time[0] < 1980: 390*cda5da8dSAndroid Build Coastguard Worker raise ValueError('ZIP does not support timestamps before 1980') 391*cda5da8dSAndroid Build Coastguard Worker 392*cda5da8dSAndroid Build Coastguard Worker # Standard values: 393*cda5da8dSAndroid Build Coastguard Worker self.compress_type = ZIP_STORED # Type of compression for the file 394*cda5da8dSAndroid Build Coastguard Worker self._compresslevel = None # Level for the compressor 395*cda5da8dSAndroid Build Coastguard Worker self.comment = b"" # Comment for each file 396*cda5da8dSAndroid Build Coastguard Worker self.extra = b"" # ZIP extra data 397*cda5da8dSAndroid Build Coastguard Worker if sys.platform == 'win32': 398*cda5da8dSAndroid Build Coastguard Worker self.create_system = 0 # System which created ZIP archive 399*cda5da8dSAndroid Build Coastguard Worker else: 400*cda5da8dSAndroid Build Coastguard Worker # Assume everything else is unix-y 401*cda5da8dSAndroid Build Coastguard Worker self.create_system = 3 # System which created ZIP archive 402*cda5da8dSAndroid Build Coastguard Worker self.create_version = DEFAULT_VERSION # Version which created ZIP archive 403*cda5da8dSAndroid Build Coastguard Worker self.extract_version = DEFAULT_VERSION # Version needed to extract archive 404*cda5da8dSAndroid Build Coastguard Worker self.reserved = 0 # Must be zero 405*cda5da8dSAndroid Build Coastguard Worker self.flag_bits = 0 # ZIP flag bits 406*cda5da8dSAndroid Build Coastguard Worker self.volume = 0 # Volume number of file header 407*cda5da8dSAndroid Build Coastguard Worker self.internal_attr = 0 # Internal attributes 408*cda5da8dSAndroid Build Coastguard Worker self.external_attr = 0 # External file attributes 409*cda5da8dSAndroid Build Coastguard Worker self.compress_size = 0 # Size of the compressed file 410*cda5da8dSAndroid Build Coastguard Worker self.file_size = 0 # Size of the uncompressed file 411*cda5da8dSAndroid Build Coastguard Worker # Other attributes are set by class ZipFile: 412*cda5da8dSAndroid Build Coastguard Worker # header_offset Byte offset to the file header 413*cda5da8dSAndroid Build Coastguard Worker # CRC CRC-32 of the uncompressed file 414*cda5da8dSAndroid Build Coastguard Worker 415*cda5da8dSAndroid Build Coastguard Worker def __repr__(self): 416*cda5da8dSAndroid Build Coastguard Worker result = ['<%s filename=%r' % (self.__class__.__name__, self.filename)] 417*cda5da8dSAndroid Build Coastguard Worker if self.compress_type != ZIP_STORED: 418*cda5da8dSAndroid Build Coastguard Worker result.append(' compress_type=%s' % 419*cda5da8dSAndroid Build Coastguard Worker compressor_names.get(self.compress_type, 420*cda5da8dSAndroid Build Coastguard Worker self.compress_type)) 421*cda5da8dSAndroid Build Coastguard Worker hi = self.external_attr >> 16 422*cda5da8dSAndroid Build Coastguard Worker lo = self.external_attr & 0xFFFF 423*cda5da8dSAndroid Build Coastguard Worker if hi: 424*cda5da8dSAndroid Build Coastguard Worker result.append(' filemode=%r' % stat.filemode(hi)) 425*cda5da8dSAndroid Build Coastguard Worker if lo: 426*cda5da8dSAndroid Build Coastguard Worker result.append(' external_attr=%#x' % lo) 427*cda5da8dSAndroid Build Coastguard Worker isdir = self.is_dir() 428*cda5da8dSAndroid Build Coastguard Worker if not isdir or self.file_size: 429*cda5da8dSAndroid Build Coastguard Worker result.append(' file_size=%r' % self.file_size) 430*cda5da8dSAndroid Build Coastguard Worker if ((not isdir or self.compress_size) and 431*cda5da8dSAndroid Build Coastguard Worker (self.compress_type != ZIP_STORED or 432*cda5da8dSAndroid Build Coastguard Worker self.file_size != self.compress_size)): 433*cda5da8dSAndroid Build Coastguard Worker result.append(' compress_size=%r' % self.compress_size) 434*cda5da8dSAndroid Build Coastguard Worker result.append('>') 435*cda5da8dSAndroid Build Coastguard Worker return ''.join(result) 436*cda5da8dSAndroid Build Coastguard Worker 437*cda5da8dSAndroid Build Coastguard Worker def FileHeader(self, zip64=None): 438*cda5da8dSAndroid Build Coastguard Worker """Return the per-file header as a bytes object. 439*cda5da8dSAndroid Build Coastguard Worker 440*cda5da8dSAndroid Build Coastguard Worker When the optional zip64 arg is None rather than a bool, we will 441*cda5da8dSAndroid Build Coastguard Worker decide based upon the file_size and compress_size, if known, 442*cda5da8dSAndroid Build Coastguard Worker False otherwise. 443*cda5da8dSAndroid Build Coastguard Worker """ 444*cda5da8dSAndroid Build Coastguard Worker dt = self.date_time 445*cda5da8dSAndroid Build Coastguard Worker dosdate = (dt[0] - 1980) << 9 | dt[1] << 5 | dt[2] 446*cda5da8dSAndroid Build Coastguard Worker dostime = dt[3] << 11 | dt[4] << 5 | (dt[5] // 2) 447*cda5da8dSAndroid Build Coastguard Worker if self.flag_bits & _MASK_USE_DATA_DESCRIPTOR: 448*cda5da8dSAndroid Build Coastguard Worker # Set these to zero because we write them after the file data 449*cda5da8dSAndroid Build Coastguard Worker CRC = compress_size = file_size = 0 450*cda5da8dSAndroid Build Coastguard Worker else: 451*cda5da8dSAndroid Build Coastguard Worker CRC = self.CRC 452*cda5da8dSAndroid Build Coastguard Worker compress_size = self.compress_size 453*cda5da8dSAndroid Build Coastguard Worker file_size = self.file_size 454*cda5da8dSAndroid Build Coastguard Worker 455*cda5da8dSAndroid Build Coastguard Worker extra = self.extra 456*cda5da8dSAndroid Build Coastguard Worker 457*cda5da8dSAndroid Build Coastguard Worker min_version = 0 458*cda5da8dSAndroid Build Coastguard Worker if zip64 is None: 459*cda5da8dSAndroid Build Coastguard Worker # We always explicitly pass zip64 within this module.... This 460*cda5da8dSAndroid Build Coastguard Worker # remains for anyone using ZipInfo.FileHeader as a public API. 461*cda5da8dSAndroid Build Coastguard Worker zip64 = file_size > ZIP64_LIMIT or compress_size > ZIP64_LIMIT 462*cda5da8dSAndroid Build Coastguard Worker if zip64: 463*cda5da8dSAndroid Build Coastguard Worker fmt = '<HHQQ' 464*cda5da8dSAndroid Build Coastguard Worker extra = extra + struct.pack(fmt, 465*cda5da8dSAndroid Build Coastguard Worker 1, struct.calcsize(fmt)-4, file_size, compress_size) 466*cda5da8dSAndroid Build Coastguard Worker file_size = 0xffffffff 467*cda5da8dSAndroid Build Coastguard Worker compress_size = 0xffffffff 468*cda5da8dSAndroid Build Coastguard Worker min_version = ZIP64_VERSION 469*cda5da8dSAndroid Build Coastguard Worker 470*cda5da8dSAndroid Build Coastguard Worker if self.compress_type == ZIP_BZIP2: 471*cda5da8dSAndroid Build Coastguard Worker min_version = max(BZIP2_VERSION, min_version) 472*cda5da8dSAndroid Build Coastguard Worker elif self.compress_type == ZIP_LZMA: 473*cda5da8dSAndroid Build Coastguard Worker min_version = max(LZMA_VERSION, min_version) 474*cda5da8dSAndroid Build Coastguard Worker 475*cda5da8dSAndroid Build Coastguard Worker self.extract_version = max(min_version, self.extract_version) 476*cda5da8dSAndroid Build Coastguard Worker self.create_version = max(min_version, self.create_version) 477*cda5da8dSAndroid Build Coastguard Worker filename, flag_bits = self._encodeFilenameFlags() 478*cda5da8dSAndroid Build Coastguard Worker header = struct.pack(structFileHeader, stringFileHeader, 479*cda5da8dSAndroid Build Coastguard Worker self.extract_version, self.reserved, flag_bits, 480*cda5da8dSAndroid Build Coastguard Worker self.compress_type, dostime, dosdate, CRC, 481*cda5da8dSAndroid Build Coastguard Worker compress_size, file_size, 482*cda5da8dSAndroid Build Coastguard Worker len(filename), len(extra)) 483*cda5da8dSAndroid Build Coastguard Worker return header + filename + extra 484*cda5da8dSAndroid Build Coastguard Worker 485*cda5da8dSAndroid Build Coastguard Worker def _encodeFilenameFlags(self): 486*cda5da8dSAndroid Build Coastguard Worker try: 487*cda5da8dSAndroid Build Coastguard Worker return self.filename.encode('ascii'), self.flag_bits 488*cda5da8dSAndroid Build Coastguard Worker except UnicodeEncodeError: 489*cda5da8dSAndroid Build Coastguard Worker return self.filename.encode('utf-8'), self.flag_bits | _MASK_UTF_FILENAME 490*cda5da8dSAndroid Build Coastguard Worker 491*cda5da8dSAndroid Build Coastguard Worker def _decodeExtra(self): 492*cda5da8dSAndroid Build Coastguard Worker # Try to decode the extra field. 493*cda5da8dSAndroid Build Coastguard Worker extra = self.extra 494*cda5da8dSAndroid Build Coastguard Worker unpack = struct.unpack 495*cda5da8dSAndroid Build Coastguard Worker while len(extra) >= 4: 496*cda5da8dSAndroid Build Coastguard Worker tp, ln = unpack('<HH', extra[:4]) 497*cda5da8dSAndroid Build Coastguard Worker if ln+4 > len(extra): 498*cda5da8dSAndroid Build Coastguard Worker raise BadZipFile("Corrupt extra field %04x (size=%d)" % (tp, ln)) 499*cda5da8dSAndroid Build Coastguard Worker if tp == 0x0001: 500*cda5da8dSAndroid Build Coastguard Worker data = extra[4:ln+4] 501*cda5da8dSAndroid Build Coastguard Worker # ZIP64 extension (large files and/or large archives) 502*cda5da8dSAndroid Build Coastguard Worker try: 503*cda5da8dSAndroid Build Coastguard Worker if self.file_size in (0xFFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF): 504*cda5da8dSAndroid Build Coastguard Worker field = "File size" 505*cda5da8dSAndroid Build Coastguard Worker self.file_size, = unpack('<Q', data[:8]) 506*cda5da8dSAndroid Build Coastguard Worker data = data[8:] 507*cda5da8dSAndroid Build Coastguard Worker if self.compress_size == 0xFFFF_FFFF: 508*cda5da8dSAndroid Build Coastguard Worker field = "Compress size" 509*cda5da8dSAndroid Build Coastguard Worker self.compress_size, = unpack('<Q', data[:8]) 510*cda5da8dSAndroid Build Coastguard Worker data = data[8:] 511*cda5da8dSAndroid Build Coastguard Worker if self.header_offset == 0xFFFF_FFFF: 512*cda5da8dSAndroid Build Coastguard Worker field = "Header offset" 513*cda5da8dSAndroid Build Coastguard Worker self.header_offset, = unpack('<Q', data[:8]) 514*cda5da8dSAndroid Build Coastguard Worker except struct.error: 515*cda5da8dSAndroid Build Coastguard Worker raise BadZipFile(f"Corrupt zip64 extra field. " 516*cda5da8dSAndroid Build Coastguard Worker f"{field} not found.") from None 517*cda5da8dSAndroid Build Coastguard Worker 518*cda5da8dSAndroid Build Coastguard Worker extra = extra[ln+4:] 519*cda5da8dSAndroid Build Coastguard Worker 520*cda5da8dSAndroid Build Coastguard Worker @classmethod 521*cda5da8dSAndroid Build Coastguard Worker def from_file(cls, filename, arcname=None, *, strict_timestamps=True): 522*cda5da8dSAndroid Build Coastguard Worker """Construct an appropriate ZipInfo for a file on the filesystem. 523*cda5da8dSAndroid Build Coastguard Worker 524*cda5da8dSAndroid Build Coastguard Worker filename should be the path to a file or directory on the filesystem. 525*cda5da8dSAndroid Build Coastguard Worker 526*cda5da8dSAndroid Build Coastguard Worker arcname is the name which it will have within the archive (by default, 527*cda5da8dSAndroid Build Coastguard Worker this will be the same as filename, but without a drive letter and with 528*cda5da8dSAndroid Build Coastguard Worker leading path separators removed). 529*cda5da8dSAndroid Build Coastguard Worker """ 530*cda5da8dSAndroid Build Coastguard Worker if isinstance(filename, os.PathLike): 531*cda5da8dSAndroid Build Coastguard Worker filename = os.fspath(filename) 532*cda5da8dSAndroid Build Coastguard Worker st = os.stat(filename) 533*cda5da8dSAndroid Build Coastguard Worker isdir = stat.S_ISDIR(st.st_mode) 534*cda5da8dSAndroid Build Coastguard Worker mtime = time.localtime(st.st_mtime) 535*cda5da8dSAndroid Build Coastguard Worker date_time = mtime[0:6] 536*cda5da8dSAndroid Build Coastguard Worker if not strict_timestamps and date_time[0] < 1980: 537*cda5da8dSAndroid Build Coastguard Worker date_time = (1980, 1, 1, 0, 0, 0) 538*cda5da8dSAndroid Build Coastguard Worker elif not strict_timestamps and date_time[0] > 2107: 539*cda5da8dSAndroid Build Coastguard Worker date_time = (2107, 12, 31, 23, 59, 59) 540*cda5da8dSAndroid Build Coastguard Worker # Create ZipInfo instance to store file information 541*cda5da8dSAndroid Build Coastguard Worker if arcname is None: 542*cda5da8dSAndroid Build Coastguard Worker arcname = filename 543*cda5da8dSAndroid Build Coastguard Worker arcname = os.path.normpath(os.path.splitdrive(arcname)[1]) 544*cda5da8dSAndroid Build Coastguard Worker while arcname[0] in (os.sep, os.altsep): 545*cda5da8dSAndroid Build Coastguard Worker arcname = arcname[1:] 546*cda5da8dSAndroid Build Coastguard Worker if isdir: 547*cda5da8dSAndroid Build Coastguard Worker arcname += '/' 548*cda5da8dSAndroid Build Coastguard Worker zinfo = cls(arcname, date_time) 549*cda5da8dSAndroid Build Coastguard Worker zinfo.external_attr = (st.st_mode & 0xFFFF) << 16 # Unix attributes 550*cda5da8dSAndroid Build Coastguard Worker if isdir: 551*cda5da8dSAndroid Build Coastguard Worker zinfo.file_size = 0 552*cda5da8dSAndroid Build Coastguard Worker zinfo.external_attr |= 0x10 # MS-DOS directory flag 553*cda5da8dSAndroid Build Coastguard Worker else: 554*cda5da8dSAndroid Build Coastguard Worker zinfo.file_size = st.st_size 555*cda5da8dSAndroid Build Coastguard Worker 556*cda5da8dSAndroid Build Coastguard Worker return zinfo 557*cda5da8dSAndroid Build Coastguard Worker 558*cda5da8dSAndroid Build Coastguard Worker def is_dir(self): 559*cda5da8dSAndroid Build Coastguard Worker """Return True if this archive member is a directory.""" 560*cda5da8dSAndroid Build Coastguard Worker return self.filename[-1] == '/' 561*cda5da8dSAndroid Build Coastguard Worker 562*cda5da8dSAndroid Build Coastguard Worker 563*cda5da8dSAndroid Build Coastguard Worker# ZIP encryption uses the CRC32 one-byte primitive for scrambling some 564*cda5da8dSAndroid Build Coastguard Worker# internal keys. We noticed that a direct implementation is faster than 565*cda5da8dSAndroid Build Coastguard Worker# relying on binascii.crc32(). 566*cda5da8dSAndroid Build Coastguard Worker 567*cda5da8dSAndroid Build Coastguard Worker_crctable = None 568*cda5da8dSAndroid Build Coastguard Workerdef _gen_crc(crc): 569*cda5da8dSAndroid Build Coastguard Worker for j in range(8): 570*cda5da8dSAndroid Build Coastguard Worker if crc & 1: 571*cda5da8dSAndroid Build Coastguard Worker crc = (crc >> 1) ^ 0xEDB88320 572*cda5da8dSAndroid Build Coastguard Worker else: 573*cda5da8dSAndroid Build Coastguard Worker crc >>= 1 574*cda5da8dSAndroid Build Coastguard Worker return crc 575*cda5da8dSAndroid Build Coastguard Worker 576*cda5da8dSAndroid Build Coastguard Worker# ZIP supports a password-based form of encryption. Even though known 577*cda5da8dSAndroid Build Coastguard Worker# plaintext attacks have been found against it, it is still useful 578*cda5da8dSAndroid Build Coastguard Worker# to be able to get data out of such a file. 579*cda5da8dSAndroid Build Coastguard Worker# 580*cda5da8dSAndroid Build Coastguard Worker# Usage: 581*cda5da8dSAndroid Build Coastguard Worker# zd = _ZipDecrypter(mypwd) 582*cda5da8dSAndroid Build Coastguard Worker# plain_bytes = zd(cypher_bytes) 583*cda5da8dSAndroid Build Coastguard Worker 584*cda5da8dSAndroid Build Coastguard Workerdef _ZipDecrypter(pwd): 585*cda5da8dSAndroid Build Coastguard Worker key0 = 305419896 586*cda5da8dSAndroid Build Coastguard Worker key1 = 591751049 587*cda5da8dSAndroid Build Coastguard Worker key2 = 878082192 588*cda5da8dSAndroid Build Coastguard Worker 589*cda5da8dSAndroid Build Coastguard Worker global _crctable 590*cda5da8dSAndroid Build Coastguard Worker if _crctable is None: 591*cda5da8dSAndroid Build Coastguard Worker _crctable = list(map(_gen_crc, range(256))) 592*cda5da8dSAndroid Build Coastguard Worker crctable = _crctable 593*cda5da8dSAndroid Build Coastguard Worker 594*cda5da8dSAndroid Build Coastguard Worker def crc32(ch, crc): 595*cda5da8dSAndroid Build Coastguard Worker """Compute the CRC32 primitive on one byte.""" 596*cda5da8dSAndroid Build Coastguard Worker return (crc >> 8) ^ crctable[(crc ^ ch) & 0xFF] 597*cda5da8dSAndroid Build Coastguard Worker 598*cda5da8dSAndroid Build Coastguard Worker def update_keys(c): 599*cda5da8dSAndroid Build Coastguard Worker nonlocal key0, key1, key2 600*cda5da8dSAndroid Build Coastguard Worker key0 = crc32(c, key0) 601*cda5da8dSAndroid Build Coastguard Worker key1 = (key1 + (key0 & 0xFF)) & 0xFFFFFFFF 602*cda5da8dSAndroid Build Coastguard Worker key1 = (key1 * 134775813 + 1) & 0xFFFFFFFF 603*cda5da8dSAndroid Build Coastguard Worker key2 = crc32(key1 >> 24, key2) 604*cda5da8dSAndroid Build Coastguard Worker 605*cda5da8dSAndroid Build Coastguard Worker for p in pwd: 606*cda5da8dSAndroid Build Coastguard Worker update_keys(p) 607*cda5da8dSAndroid Build Coastguard Worker 608*cda5da8dSAndroid Build Coastguard Worker def decrypter(data): 609*cda5da8dSAndroid Build Coastguard Worker """Decrypt a bytes object.""" 610*cda5da8dSAndroid Build Coastguard Worker result = bytearray() 611*cda5da8dSAndroid Build Coastguard Worker append = result.append 612*cda5da8dSAndroid Build Coastguard Worker for c in data: 613*cda5da8dSAndroid Build Coastguard Worker k = key2 | 2 614*cda5da8dSAndroid Build Coastguard Worker c ^= ((k * (k^1)) >> 8) & 0xFF 615*cda5da8dSAndroid Build Coastguard Worker update_keys(c) 616*cda5da8dSAndroid Build Coastguard Worker append(c) 617*cda5da8dSAndroid Build Coastguard Worker return bytes(result) 618*cda5da8dSAndroid Build Coastguard Worker 619*cda5da8dSAndroid Build Coastguard Worker return decrypter 620*cda5da8dSAndroid Build Coastguard Worker 621*cda5da8dSAndroid Build Coastguard Worker 622*cda5da8dSAndroid Build Coastguard Workerclass LZMACompressor: 623*cda5da8dSAndroid Build Coastguard Worker 624*cda5da8dSAndroid Build Coastguard Worker def __init__(self): 625*cda5da8dSAndroid Build Coastguard Worker self._comp = None 626*cda5da8dSAndroid Build Coastguard Worker 627*cda5da8dSAndroid Build Coastguard Worker def _init(self): 628*cda5da8dSAndroid Build Coastguard Worker props = lzma._encode_filter_properties({'id': lzma.FILTER_LZMA1}) 629*cda5da8dSAndroid Build Coastguard Worker self._comp = lzma.LZMACompressor(lzma.FORMAT_RAW, filters=[ 630*cda5da8dSAndroid Build Coastguard Worker lzma._decode_filter_properties(lzma.FILTER_LZMA1, props) 631*cda5da8dSAndroid Build Coastguard Worker ]) 632*cda5da8dSAndroid Build Coastguard Worker return struct.pack('<BBH', 9, 4, len(props)) + props 633*cda5da8dSAndroid Build Coastguard Worker 634*cda5da8dSAndroid Build Coastguard Worker def compress(self, data): 635*cda5da8dSAndroid Build Coastguard Worker if self._comp is None: 636*cda5da8dSAndroid Build Coastguard Worker return self._init() + self._comp.compress(data) 637*cda5da8dSAndroid Build Coastguard Worker return self._comp.compress(data) 638*cda5da8dSAndroid Build Coastguard Worker 639*cda5da8dSAndroid Build Coastguard Worker def flush(self): 640*cda5da8dSAndroid Build Coastguard Worker if self._comp is None: 641*cda5da8dSAndroid Build Coastguard Worker return self._init() + self._comp.flush() 642*cda5da8dSAndroid Build Coastguard Worker return self._comp.flush() 643*cda5da8dSAndroid Build Coastguard Worker 644*cda5da8dSAndroid Build Coastguard Worker 645*cda5da8dSAndroid Build Coastguard Workerclass LZMADecompressor: 646*cda5da8dSAndroid Build Coastguard Worker 647*cda5da8dSAndroid Build Coastguard Worker def __init__(self): 648*cda5da8dSAndroid Build Coastguard Worker self._decomp = None 649*cda5da8dSAndroid Build Coastguard Worker self._unconsumed = b'' 650*cda5da8dSAndroid Build Coastguard Worker self.eof = False 651*cda5da8dSAndroid Build Coastguard Worker 652*cda5da8dSAndroid Build Coastguard Worker def decompress(self, data): 653*cda5da8dSAndroid Build Coastguard Worker if self._decomp is None: 654*cda5da8dSAndroid Build Coastguard Worker self._unconsumed += data 655*cda5da8dSAndroid Build Coastguard Worker if len(self._unconsumed) <= 4: 656*cda5da8dSAndroid Build Coastguard Worker return b'' 657*cda5da8dSAndroid Build Coastguard Worker psize, = struct.unpack('<H', self._unconsumed[2:4]) 658*cda5da8dSAndroid Build Coastguard Worker if len(self._unconsumed) <= 4 + psize: 659*cda5da8dSAndroid Build Coastguard Worker return b'' 660*cda5da8dSAndroid Build Coastguard Worker 661*cda5da8dSAndroid Build Coastguard Worker self._decomp = lzma.LZMADecompressor(lzma.FORMAT_RAW, filters=[ 662*cda5da8dSAndroid Build Coastguard Worker lzma._decode_filter_properties(lzma.FILTER_LZMA1, 663*cda5da8dSAndroid Build Coastguard Worker self._unconsumed[4:4 + psize]) 664*cda5da8dSAndroid Build Coastguard Worker ]) 665*cda5da8dSAndroid Build Coastguard Worker data = self._unconsumed[4 + psize:] 666*cda5da8dSAndroid Build Coastguard Worker del self._unconsumed 667*cda5da8dSAndroid Build Coastguard Worker 668*cda5da8dSAndroid Build Coastguard Worker result = self._decomp.decompress(data) 669*cda5da8dSAndroid Build Coastguard Worker self.eof = self._decomp.eof 670*cda5da8dSAndroid Build Coastguard Worker return result 671*cda5da8dSAndroid Build Coastguard Worker 672*cda5da8dSAndroid Build Coastguard Worker 673*cda5da8dSAndroid Build Coastguard Workercompressor_names = { 674*cda5da8dSAndroid Build Coastguard Worker 0: 'store', 675*cda5da8dSAndroid Build Coastguard Worker 1: 'shrink', 676*cda5da8dSAndroid Build Coastguard Worker 2: 'reduce', 677*cda5da8dSAndroid Build Coastguard Worker 3: 'reduce', 678*cda5da8dSAndroid Build Coastguard Worker 4: 'reduce', 679*cda5da8dSAndroid Build Coastguard Worker 5: 'reduce', 680*cda5da8dSAndroid Build Coastguard Worker 6: 'implode', 681*cda5da8dSAndroid Build Coastguard Worker 7: 'tokenize', 682*cda5da8dSAndroid Build Coastguard Worker 8: 'deflate', 683*cda5da8dSAndroid Build Coastguard Worker 9: 'deflate64', 684*cda5da8dSAndroid Build Coastguard Worker 10: 'implode', 685*cda5da8dSAndroid Build Coastguard Worker 12: 'bzip2', 686*cda5da8dSAndroid Build Coastguard Worker 14: 'lzma', 687*cda5da8dSAndroid Build Coastguard Worker 18: 'terse', 688*cda5da8dSAndroid Build Coastguard Worker 19: 'lz77', 689*cda5da8dSAndroid Build Coastguard Worker 97: 'wavpack', 690*cda5da8dSAndroid Build Coastguard Worker 98: 'ppmd', 691*cda5da8dSAndroid Build Coastguard Worker} 692*cda5da8dSAndroid Build Coastguard Worker 693*cda5da8dSAndroid Build Coastguard Workerdef _check_compression(compression): 694*cda5da8dSAndroid Build Coastguard Worker if compression == ZIP_STORED: 695*cda5da8dSAndroid Build Coastguard Worker pass 696*cda5da8dSAndroid Build Coastguard Worker elif compression == ZIP_DEFLATED: 697*cda5da8dSAndroid Build Coastguard Worker if not zlib: 698*cda5da8dSAndroid Build Coastguard Worker raise RuntimeError( 699*cda5da8dSAndroid Build Coastguard Worker "Compression requires the (missing) zlib module") 700*cda5da8dSAndroid Build Coastguard Worker elif compression == ZIP_BZIP2: 701*cda5da8dSAndroid Build Coastguard Worker if not bz2: 702*cda5da8dSAndroid Build Coastguard Worker raise RuntimeError( 703*cda5da8dSAndroid Build Coastguard Worker "Compression requires the (missing) bz2 module") 704*cda5da8dSAndroid Build Coastguard Worker elif compression == ZIP_LZMA: 705*cda5da8dSAndroid Build Coastguard Worker if not lzma: 706*cda5da8dSAndroid Build Coastguard Worker raise RuntimeError( 707*cda5da8dSAndroid Build Coastguard Worker "Compression requires the (missing) lzma module") 708*cda5da8dSAndroid Build Coastguard Worker else: 709*cda5da8dSAndroid Build Coastguard Worker raise NotImplementedError("That compression method is not supported") 710*cda5da8dSAndroid Build Coastguard Worker 711*cda5da8dSAndroid Build Coastguard Worker 712*cda5da8dSAndroid Build Coastguard Workerdef _get_compressor(compress_type, compresslevel=None): 713*cda5da8dSAndroid Build Coastguard Worker if compress_type == ZIP_DEFLATED: 714*cda5da8dSAndroid Build Coastguard Worker if compresslevel is not None: 715*cda5da8dSAndroid Build Coastguard Worker return zlib.compressobj(compresslevel, zlib.DEFLATED, -15) 716*cda5da8dSAndroid Build Coastguard Worker return zlib.compressobj(zlib.Z_DEFAULT_COMPRESSION, zlib.DEFLATED, -15) 717*cda5da8dSAndroid Build Coastguard Worker elif compress_type == ZIP_BZIP2: 718*cda5da8dSAndroid Build Coastguard Worker if compresslevel is not None: 719*cda5da8dSAndroid Build Coastguard Worker return bz2.BZ2Compressor(compresslevel) 720*cda5da8dSAndroid Build Coastguard Worker return bz2.BZ2Compressor() 721*cda5da8dSAndroid Build Coastguard Worker # compresslevel is ignored for ZIP_LZMA 722*cda5da8dSAndroid Build Coastguard Worker elif compress_type == ZIP_LZMA: 723*cda5da8dSAndroid Build Coastguard Worker return LZMACompressor() 724*cda5da8dSAndroid Build Coastguard Worker else: 725*cda5da8dSAndroid Build Coastguard Worker return None 726*cda5da8dSAndroid Build Coastguard Worker 727*cda5da8dSAndroid Build Coastguard Worker 728*cda5da8dSAndroid Build Coastguard Workerdef _get_decompressor(compress_type): 729*cda5da8dSAndroid Build Coastguard Worker _check_compression(compress_type) 730*cda5da8dSAndroid Build Coastguard Worker if compress_type == ZIP_STORED: 731*cda5da8dSAndroid Build Coastguard Worker return None 732*cda5da8dSAndroid Build Coastguard Worker elif compress_type == ZIP_DEFLATED: 733*cda5da8dSAndroid Build Coastguard Worker return zlib.decompressobj(-15) 734*cda5da8dSAndroid Build Coastguard Worker elif compress_type == ZIP_BZIP2: 735*cda5da8dSAndroid Build Coastguard Worker return bz2.BZ2Decompressor() 736*cda5da8dSAndroid Build Coastguard Worker elif compress_type == ZIP_LZMA: 737*cda5da8dSAndroid Build Coastguard Worker return LZMADecompressor() 738*cda5da8dSAndroid Build Coastguard Worker else: 739*cda5da8dSAndroid Build Coastguard Worker descr = compressor_names.get(compress_type) 740*cda5da8dSAndroid Build Coastguard Worker if descr: 741*cda5da8dSAndroid Build Coastguard Worker raise NotImplementedError("compression type %d (%s)" % (compress_type, descr)) 742*cda5da8dSAndroid Build Coastguard Worker else: 743*cda5da8dSAndroid Build Coastguard Worker raise NotImplementedError("compression type %d" % (compress_type,)) 744*cda5da8dSAndroid Build Coastguard Worker 745*cda5da8dSAndroid Build Coastguard Worker 746*cda5da8dSAndroid Build Coastguard Workerclass _SharedFile: 747*cda5da8dSAndroid Build Coastguard Worker def __init__(self, file, pos, close, lock, writing): 748*cda5da8dSAndroid Build Coastguard Worker self._file = file 749*cda5da8dSAndroid Build Coastguard Worker self._pos = pos 750*cda5da8dSAndroid Build Coastguard Worker self._close = close 751*cda5da8dSAndroid Build Coastguard Worker self._lock = lock 752*cda5da8dSAndroid Build Coastguard Worker self._writing = writing 753*cda5da8dSAndroid Build Coastguard Worker self.seekable = file.seekable 754*cda5da8dSAndroid Build Coastguard Worker 755*cda5da8dSAndroid Build Coastguard Worker def tell(self): 756*cda5da8dSAndroid Build Coastguard Worker return self._pos 757*cda5da8dSAndroid Build Coastguard Worker 758*cda5da8dSAndroid Build Coastguard Worker def seek(self, offset, whence=0): 759*cda5da8dSAndroid Build Coastguard Worker with self._lock: 760*cda5da8dSAndroid Build Coastguard Worker if self._writing(): 761*cda5da8dSAndroid Build Coastguard Worker raise ValueError("Can't reposition in the ZIP file while " 762*cda5da8dSAndroid Build Coastguard Worker "there is an open writing handle on it. " 763*cda5da8dSAndroid Build Coastguard Worker "Close the writing handle before trying to read.") 764*cda5da8dSAndroid Build Coastguard Worker self._file.seek(offset, whence) 765*cda5da8dSAndroid Build Coastguard Worker self._pos = self._file.tell() 766*cda5da8dSAndroid Build Coastguard Worker return self._pos 767*cda5da8dSAndroid Build Coastguard Worker 768*cda5da8dSAndroid Build Coastguard Worker def read(self, n=-1): 769*cda5da8dSAndroid Build Coastguard Worker with self._lock: 770*cda5da8dSAndroid Build Coastguard Worker if self._writing(): 771*cda5da8dSAndroid Build Coastguard Worker raise ValueError("Can't read from the ZIP file while there " 772*cda5da8dSAndroid Build Coastguard Worker "is an open writing handle on it. " 773*cda5da8dSAndroid Build Coastguard Worker "Close the writing handle before trying to read.") 774*cda5da8dSAndroid Build Coastguard Worker self._file.seek(self._pos) 775*cda5da8dSAndroid Build Coastguard Worker data = self._file.read(n) 776*cda5da8dSAndroid Build Coastguard Worker self._pos = self._file.tell() 777*cda5da8dSAndroid Build Coastguard Worker return data 778*cda5da8dSAndroid Build Coastguard Worker 779*cda5da8dSAndroid Build Coastguard Worker def close(self): 780*cda5da8dSAndroid Build Coastguard Worker if self._file is not None: 781*cda5da8dSAndroid Build Coastguard Worker fileobj = self._file 782*cda5da8dSAndroid Build Coastguard Worker self._file = None 783*cda5da8dSAndroid Build Coastguard Worker self._close(fileobj) 784*cda5da8dSAndroid Build Coastguard Worker 785*cda5da8dSAndroid Build Coastguard Worker# Provide the tell method for unseekable stream 786*cda5da8dSAndroid Build Coastguard Workerclass _Tellable: 787*cda5da8dSAndroid Build Coastguard Worker def __init__(self, fp): 788*cda5da8dSAndroid Build Coastguard Worker self.fp = fp 789*cda5da8dSAndroid Build Coastguard Worker self.offset = 0 790*cda5da8dSAndroid Build Coastguard Worker 791*cda5da8dSAndroid Build Coastguard Worker def write(self, data): 792*cda5da8dSAndroid Build Coastguard Worker n = self.fp.write(data) 793*cda5da8dSAndroid Build Coastguard Worker self.offset += n 794*cda5da8dSAndroid Build Coastguard Worker return n 795*cda5da8dSAndroid Build Coastguard Worker 796*cda5da8dSAndroid Build Coastguard Worker def tell(self): 797*cda5da8dSAndroid Build Coastguard Worker return self.offset 798*cda5da8dSAndroid Build Coastguard Worker 799*cda5da8dSAndroid Build Coastguard Worker def flush(self): 800*cda5da8dSAndroid Build Coastguard Worker self.fp.flush() 801*cda5da8dSAndroid Build Coastguard Worker 802*cda5da8dSAndroid Build Coastguard Worker def close(self): 803*cda5da8dSAndroid Build Coastguard Worker self.fp.close() 804*cda5da8dSAndroid Build Coastguard Worker 805*cda5da8dSAndroid Build Coastguard Worker 806*cda5da8dSAndroid Build Coastguard Workerclass ZipExtFile(io.BufferedIOBase): 807*cda5da8dSAndroid Build Coastguard Worker """File-like object for reading an archive member. 808*cda5da8dSAndroid Build Coastguard Worker Is returned by ZipFile.open(). 809*cda5da8dSAndroid Build Coastguard Worker """ 810*cda5da8dSAndroid Build Coastguard Worker 811*cda5da8dSAndroid Build Coastguard Worker # Max size supported by decompressor. 812*cda5da8dSAndroid Build Coastguard Worker MAX_N = 1 << 31 - 1 813*cda5da8dSAndroid Build Coastguard Worker 814*cda5da8dSAndroid Build Coastguard Worker # Read from compressed files in 4k blocks. 815*cda5da8dSAndroid Build Coastguard Worker MIN_READ_SIZE = 4096 816*cda5da8dSAndroid Build Coastguard Worker 817*cda5da8dSAndroid Build Coastguard Worker # Chunk size to read during seek 818*cda5da8dSAndroid Build Coastguard Worker MAX_SEEK_READ = 1 << 24 819*cda5da8dSAndroid Build Coastguard Worker 820*cda5da8dSAndroid Build Coastguard Worker def __init__(self, fileobj, mode, zipinfo, pwd=None, 821*cda5da8dSAndroid Build Coastguard Worker close_fileobj=False): 822*cda5da8dSAndroid Build Coastguard Worker self._fileobj = fileobj 823*cda5da8dSAndroid Build Coastguard Worker self._pwd = pwd 824*cda5da8dSAndroid Build Coastguard Worker self._close_fileobj = close_fileobj 825*cda5da8dSAndroid Build Coastguard Worker 826*cda5da8dSAndroid Build Coastguard Worker self._compress_type = zipinfo.compress_type 827*cda5da8dSAndroid Build Coastguard Worker self._compress_left = zipinfo.compress_size 828*cda5da8dSAndroid Build Coastguard Worker self._left = zipinfo.file_size 829*cda5da8dSAndroid Build Coastguard Worker 830*cda5da8dSAndroid Build Coastguard Worker self._decompressor = _get_decompressor(self._compress_type) 831*cda5da8dSAndroid Build Coastguard Worker 832*cda5da8dSAndroid Build Coastguard Worker self._eof = False 833*cda5da8dSAndroid Build Coastguard Worker self._readbuffer = b'' 834*cda5da8dSAndroid Build Coastguard Worker self._offset = 0 835*cda5da8dSAndroid Build Coastguard Worker 836*cda5da8dSAndroid Build Coastguard Worker self.newlines = None 837*cda5da8dSAndroid Build Coastguard Worker 838*cda5da8dSAndroid Build Coastguard Worker self.mode = mode 839*cda5da8dSAndroid Build Coastguard Worker self.name = zipinfo.filename 840*cda5da8dSAndroid Build Coastguard Worker 841*cda5da8dSAndroid Build Coastguard Worker if hasattr(zipinfo, 'CRC'): 842*cda5da8dSAndroid Build Coastguard Worker self._expected_crc = zipinfo.CRC 843*cda5da8dSAndroid Build Coastguard Worker self._running_crc = crc32(b'') 844*cda5da8dSAndroid Build Coastguard Worker else: 845*cda5da8dSAndroid Build Coastguard Worker self._expected_crc = None 846*cda5da8dSAndroid Build Coastguard Worker 847*cda5da8dSAndroid Build Coastguard Worker self._seekable = False 848*cda5da8dSAndroid Build Coastguard Worker try: 849*cda5da8dSAndroid Build Coastguard Worker if fileobj.seekable(): 850*cda5da8dSAndroid Build Coastguard Worker self._orig_compress_start = fileobj.tell() 851*cda5da8dSAndroid Build Coastguard Worker self._orig_compress_size = zipinfo.compress_size 852*cda5da8dSAndroid Build Coastguard Worker self._orig_file_size = zipinfo.file_size 853*cda5da8dSAndroid Build Coastguard Worker self._orig_start_crc = self._running_crc 854*cda5da8dSAndroid Build Coastguard Worker self._seekable = True 855*cda5da8dSAndroid Build Coastguard Worker except AttributeError: 856*cda5da8dSAndroid Build Coastguard Worker pass 857*cda5da8dSAndroid Build Coastguard Worker 858*cda5da8dSAndroid Build Coastguard Worker self._decrypter = None 859*cda5da8dSAndroid Build Coastguard Worker if pwd: 860*cda5da8dSAndroid Build Coastguard Worker if zipinfo.flag_bits & _MASK_USE_DATA_DESCRIPTOR: 861*cda5da8dSAndroid Build Coastguard Worker # compare against the file type from extended local headers 862*cda5da8dSAndroid Build Coastguard Worker check_byte = (zipinfo._raw_time >> 8) & 0xff 863*cda5da8dSAndroid Build Coastguard Worker else: 864*cda5da8dSAndroid Build Coastguard Worker # compare against the CRC otherwise 865*cda5da8dSAndroid Build Coastguard Worker check_byte = (zipinfo.CRC >> 24) & 0xff 866*cda5da8dSAndroid Build Coastguard Worker h = self._init_decrypter() 867*cda5da8dSAndroid Build Coastguard Worker if h != check_byte: 868*cda5da8dSAndroid Build Coastguard Worker raise RuntimeError("Bad password for file %r" % zipinfo.orig_filename) 869*cda5da8dSAndroid Build Coastguard Worker 870*cda5da8dSAndroid Build Coastguard Worker 871*cda5da8dSAndroid Build Coastguard Worker def _init_decrypter(self): 872*cda5da8dSAndroid Build Coastguard Worker self._decrypter = _ZipDecrypter(self._pwd) 873*cda5da8dSAndroid Build Coastguard Worker # The first 12 bytes in the cypher stream is an encryption header 874*cda5da8dSAndroid Build Coastguard Worker # used to strengthen the algorithm. The first 11 bytes are 875*cda5da8dSAndroid Build Coastguard Worker # completely random, while the 12th contains the MSB of the CRC, 876*cda5da8dSAndroid Build Coastguard Worker # or the MSB of the file time depending on the header type 877*cda5da8dSAndroid Build Coastguard Worker # and is used to check the correctness of the password. 878*cda5da8dSAndroid Build Coastguard Worker header = self._fileobj.read(12) 879*cda5da8dSAndroid Build Coastguard Worker self._compress_left -= 12 880*cda5da8dSAndroid Build Coastguard Worker return self._decrypter(header)[11] 881*cda5da8dSAndroid Build Coastguard Worker 882*cda5da8dSAndroid Build Coastguard Worker def __repr__(self): 883*cda5da8dSAndroid Build Coastguard Worker result = ['<%s.%s' % (self.__class__.__module__, 884*cda5da8dSAndroid Build Coastguard Worker self.__class__.__qualname__)] 885*cda5da8dSAndroid Build Coastguard Worker if not self.closed: 886*cda5da8dSAndroid Build Coastguard Worker result.append(' name=%r mode=%r' % (self.name, self.mode)) 887*cda5da8dSAndroid Build Coastguard Worker if self._compress_type != ZIP_STORED: 888*cda5da8dSAndroid Build Coastguard Worker result.append(' compress_type=%s' % 889*cda5da8dSAndroid Build Coastguard Worker compressor_names.get(self._compress_type, 890*cda5da8dSAndroid Build Coastguard Worker self._compress_type)) 891*cda5da8dSAndroid Build Coastguard Worker else: 892*cda5da8dSAndroid Build Coastguard Worker result.append(' [closed]') 893*cda5da8dSAndroid Build Coastguard Worker result.append('>') 894*cda5da8dSAndroid Build Coastguard Worker return ''.join(result) 895*cda5da8dSAndroid Build Coastguard Worker 896*cda5da8dSAndroid Build Coastguard Worker def readline(self, limit=-1): 897*cda5da8dSAndroid Build Coastguard Worker """Read and return a line from the stream. 898*cda5da8dSAndroid Build Coastguard Worker 899*cda5da8dSAndroid Build Coastguard Worker If limit is specified, at most limit bytes will be read. 900*cda5da8dSAndroid Build Coastguard Worker """ 901*cda5da8dSAndroid Build Coastguard Worker 902*cda5da8dSAndroid Build Coastguard Worker if limit < 0: 903*cda5da8dSAndroid Build Coastguard Worker # Shortcut common case - newline found in buffer. 904*cda5da8dSAndroid Build Coastguard Worker i = self._readbuffer.find(b'\n', self._offset) + 1 905*cda5da8dSAndroid Build Coastguard Worker if i > 0: 906*cda5da8dSAndroid Build Coastguard Worker line = self._readbuffer[self._offset: i] 907*cda5da8dSAndroid Build Coastguard Worker self._offset = i 908*cda5da8dSAndroid Build Coastguard Worker return line 909*cda5da8dSAndroid Build Coastguard Worker 910*cda5da8dSAndroid Build Coastguard Worker return io.BufferedIOBase.readline(self, limit) 911*cda5da8dSAndroid Build Coastguard Worker 912*cda5da8dSAndroid Build Coastguard Worker def peek(self, n=1): 913*cda5da8dSAndroid Build Coastguard Worker """Returns buffered bytes without advancing the position.""" 914*cda5da8dSAndroid Build Coastguard Worker if n > len(self._readbuffer) - self._offset: 915*cda5da8dSAndroid Build Coastguard Worker chunk = self.read(n) 916*cda5da8dSAndroid Build Coastguard Worker if len(chunk) > self._offset: 917*cda5da8dSAndroid Build Coastguard Worker self._readbuffer = chunk + self._readbuffer[self._offset:] 918*cda5da8dSAndroid Build Coastguard Worker self._offset = 0 919*cda5da8dSAndroid Build Coastguard Worker else: 920*cda5da8dSAndroid Build Coastguard Worker self._offset -= len(chunk) 921*cda5da8dSAndroid Build Coastguard Worker 922*cda5da8dSAndroid Build Coastguard Worker # Return up to 512 bytes to reduce allocation overhead for tight loops. 923*cda5da8dSAndroid Build Coastguard Worker return self._readbuffer[self._offset: self._offset + 512] 924*cda5da8dSAndroid Build Coastguard Worker 925*cda5da8dSAndroid Build Coastguard Worker def readable(self): 926*cda5da8dSAndroid Build Coastguard Worker if self.closed: 927*cda5da8dSAndroid Build Coastguard Worker raise ValueError("I/O operation on closed file.") 928*cda5da8dSAndroid Build Coastguard Worker return True 929*cda5da8dSAndroid Build Coastguard Worker 930*cda5da8dSAndroid Build Coastguard Worker def read(self, n=-1): 931*cda5da8dSAndroid Build Coastguard Worker """Read and return up to n bytes. 932*cda5da8dSAndroid Build Coastguard Worker If the argument is omitted, None, or negative, data is read and returned until EOF is reached. 933*cda5da8dSAndroid Build Coastguard Worker """ 934*cda5da8dSAndroid Build Coastguard Worker if self.closed: 935*cda5da8dSAndroid Build Coastguard Worker raise ValueError("read from closed file.") 936*cda5da8dSAndroid Build Coastguard Worker if n is None or n < 0: 937*cda5da8dSAndroid Build Coastguard Worker buf = self._readbuffer[self._offset:] 938*cda5da8dSAndroid Build Coastguard Worker self._readbuffer = b'' 939*cda5da8dSAndroid Build Coastguard Worker self._offset = 0 940*cda5da8dSAndroid Build Coastguard Worker while not self._eof: 941*cda5da8dSAndroid Build Coastguard Worker buf += self._read1(self.MAX_N) 942*cda5da8dSAndroid Build Coastguard Worker return buf 943*cda5da8dSAndroid Build Coastguard Worker 944*cda5da8dSAndroid Build Coastguard Worker end = n + self._offset 945*cda5da8dSAndroid Build Coastguard Worker if end < len(self._readbuffer): 946*cda5da8dSAndroid Build Coastguard Worker buf = self._readbuffer[self._offset:end] 947*cda5da8dSAndroid Build Coastguard Worker self._offset = end 948*cda5da8dSAndroid Build Coastguard Worker return buf 949*cda5da8dSAndroid Build Coastguard Worker 950*cda5da8dSAndroid Build Coastguard Worker n = end - len(self._readbuffer) 951*cda5da8dSAndroid Build Coastguard Worker buf = self._readbuffer[self._offset:] 952*cda5da8dSAndroid Build Coastguard Worker self._readbuffer = b'' 953*cda5da8dSAndroid Build Coastguard Worker self._offset = 0 954*cda5da8dSAndroid Build Coastguard Worker while n > 0 and not self._eof: 955*cda5da8dSAndroid Build Coastguard Worker data = self._read1(n) 956*cda5da8dSAndroid Build Coastguard Worker if n < len(data): 957*cda5da8dSAndroid Build Coastguard Worker self._readbuffer = data 958*cda5da8dSAndroid Build Coastguard Worker self._offset = n 959*cda5da8dSAndroid Build Coastguard Worker buf += data[:n] 960*cda5da8dSAndroid Build Coastguard Worker break 961*cda5da8dSAndroid Build Coastguard Worker buf += data 962*cda5da8dSAndroid Build Coastguard Worker n -= len(data) 963*cda5da8dSAndroid Build Coastguard Worker return buf 964*cda5da8dSAndroid Build Coastguard Worker 965*cda5da8dSAndroid Build Coastguard Worker def _update_crc(self, newdata): 966*cda5da8dSAndroid Build Coastguard Worker # Update the CRC using the given data. 967*cda5da8dSAndroid Build Coastguard Worker if self._expected_crc is None: 968*cda5da8dSAndroid Build Coastguard Worker # No need to compute the CRC if we don't have a reference value 969*cda5da8dSAndroid Build Coastguard Worker return 970*cda5da8dSAndroid Build Coastguard Worker self._running_crc = crc32(newdata, self._running_crc) 971*cda5da8dSAndroid Build Coastguard Worker # Check the CRC if we're at the end of the file 972*cda5da8dSAndroid Build Coastguard Worker if self._eof and self._running_crc != self._expected_crc: 973*cda5da8dSAndroid Build Coastguard Worker raise BadZipFile("Bad CRC-32 for file %r" % self.name) 974*cda5da8dSAndroid Build Coastguard Worker 975*cda5da8dSAndroid Build Coastguard Worker def read1(self, n): 976*cda5da8dSAndroid Build Coastguard Worker """Read up to n bytes with at most one read() system call.""" 977*cda5da8dSAndroid Build Coastguard Worker 978*cda5da8dSAndroid Build Coastguard Worker if n is None or n < 0: 979*cda5da8dSAndroid Build Coastguard Worker buf = self._readbuffer[self._offset:] 980*cda5da8dSAndroid Build Coastguard Worker self._readbuffer = b'' 981*cda5da8dSAndroid Build Coastguard Worker self._offset = 0 982*cda5da8dSAndroid Build Coastguard Worker while not self._eof: 983*cda5da8dSAndroid Build Coastguard Worker data = self._read1(self.MAX_N) 984*cda5da8dSAndroid Build Coastguard Worker if data: 985*cda5da8dSAndroid Build Coastguard Worker buf += data 986*cda5da8dSAndroid Build Coastguard Worker break 987*cda5da8dSAndroid Build Coastguard Worker return buf 988*cda5da8dSAndroid Build Coastguard Worker 989*cda5da8dSAndroid Build Coastguard Worker end = n + self._offset 990*cda5da8dSAndroid Build Coastguard Worker if end < len(self._readbuffer): 991*cda5da8dSAndroid Build Coastguard Worker buf = self._readbuffer[self._offset:end] 992*cda5da8dSAndroid Build Coastguard Worker self._offset = end 993*cda5da8dSAndroid Build Coastguard Worker return buf 994*cda5da8dSAndroid Build Coastguard Worker 995*cda5da8dSAndroid Build Coastguard Worker n = end - len(self._readbuffer) 996*cda5da8dSAndroid Build Coastguard Worker buf = self._readbuffer[self._offset:] 997*cda5da8dSAndroid Build Coastguard Worker self._readbuffer = b'' 998*cda5da8dSAndroid Build Coastguard Worker self._offset = 0 999*cda5da8dSAndroid Build Coastguard Worker if n > 0: 1000*cda5da8dSAndroid Build Coastguard Worker while not self._eof: 1001*cda5da8dSAndroid Build Coastguard Worker data = self._read1(n) 1002*cda5da8dSAndroid Build Coastguard Worker if n < len(data): 1003*cda5da8dSAndroid Build Coastguard Worker self._readbuffer = data 1004*cda5da8dSAndroid Build Coastguard Worker self._offset = n 1005*cda5da8dSAndroid Build Coastguard Worker buf += data[:n] 1006*cda5da8dSAndroid Build Coastguard Worker break 1007*cda5da8dSAndroid Build Coastguard Worker if data: 1008*cda5da8dSAndroid Build Coastguard Worker buf += data 1009*cda5da8dSAndroid Build Coastguard Worker break 1010*cda5da8dSAndroid Build Coastguard Worker return buf 1011*cda5da8dSAndroid Build Coastguard Worker 1012*cda5da8dSAndroid Build Coastguard Worker def _read1(self, n): 1013*cda5da8dSAndroid Build Coastguard Worker # Read up to n compressed bytes with at most one read() system call, 1014*cda5da8dSAndroid Build Coastguard Worker # decrypt and decompress them. 1015*cda5da8dSAndroid Build Coastguard Worker if self._eof or n <= 0: 1016*cda5da8dSAndroid Build Coastguard Worker return b'' 1017*cda5da8dSAndroid Build Coastguard Worker 1018*cda5da8dSAndroid Build Coastguard Worker # Read from file. 1019*cda5da8dSAndroid Build Coastguard Worker if self._compress_type == ZIP_DEFLATED: 1020*cda5da8dSAndroid Build Coastguard Worker ## Handle unconsumed data. 1021*cda5da8dSAndroid Build Coastguard Worker data = self._decompressor.unconsumed_tail 1022*cda5da8dSAndroid Build Coastguard Worker if n > len(data): 1023*cda5da8dSAndroid Build Coastguard Worker data += self._read2(n - len(data)) 1024*cda5da8dSAndroid Build Coastguard Worker else: 1025*cda5da8dSAndroid Build Coastguard Worker data = self._read2(n) 1026*cda5da8dSAndroid Build Coastguard Worker 1027*cda5da8dSAndroid Build Coastguard Worker if self._compress_type == ZIP_STORED: 1028*cda5da8dSAndroid Build Coastguard Worker self._eof = self._compress_left <= 0 1029*cda5da8dSAndroid Build Coastguard Worker elif self._compress_type == ZIP_DEFLATED: 1030*cda5da8dSAndroid Build Coastguard Worker n = max(n, self.MIN_READ_SIZE) 1031*cda5da8dSAndroid Build Coastguard Worker data = self._decompressor.decompress(data, n) 1032*cda5da8dSAndroid Build Coastguard Worker self._eof = (self._decompressor.eof or 1033*cda5da8dSAndroid Build Coastguard Worker self._compress_left <= 0 and 1034*cda5da8dSAndroid Build Coastguard Worker not self._decompressor.unconsumed_tail) 1035*cda5da8dSAndroid Build Coastguard Worker if self._eof: 1036*cda5da8dSAndroid Build Coastguard Worker data += self._decompressor.flush() 1037*cda5da8dSAndroid Build Coastguard Worker else: 1038*cda5da8dSAndroid Build Coastguard Worker data = self._decompressor.decompress(data) 1039*cda5da8dSAndroid Build Coastguard Worker self._eof = self._decompressor.eof or self._compress_left <= 0 1040*cda5da8dSAndroid Build Coastguard Worker 1041*cda5da8dSAndroid Build Coastguard Worker data = data[:self._left] 1042*cda5da8dSAndroid Build Coastguard Worker self._left -= len(data) 1043*cda5da8dSAndroid Build Coastguard Worker if self._left <= 0: 1044*cda5da8dSAndroid Build Coastguard Worker self._eof = True 1045*cda5da8dSAndroid Build Coastguard Worker self._update_crc(data) 1046*cda5da8dSAndroid Build Coastguard Worker return data 1047*cda5da8dSAndroid Build Coastguard Worker 1048*cda5da8dSAndroid Build Coastguard Worker def _read2(self, n): 1049*cda5da8dSAndroid Build Coastguard Worker if self._compress_left <= 0: 1050*cda5da8dSAndroid Build Coastguard Worker return b'' 1051*cda5da8dSAndroid Build Coastguard Worker 1052*cda5da8dSAndroid Build Coastguard Worker n = max(n, self.MIN_READ_SIZE) 1053*cda5da8dSAndroid Build Coastguard Worker n = min(n, self._compress_left) 1054*cda5da8dSAndroid Build Coastguard Worker 1055*cda5da8dSAndroid Build Coastguard Worker data = self._fileobj.read(n) 1056*cda5da8dSAndroid Build Coastguard Worker self._compress_left -= len(data) 1057*cda5da8dSAndroid Build Coastguard Worker if not data: 1058*cda5da8dSAndroid Build Coastguard Worker raise EOFError 1059*cda5da8dSAndroid Build Coastguard Worker 1060*cda5da8dSAndroid Build Coastguard Worker if self._decrypter is not None: 1061*cda5da8dSAndroid Build Coastguard Worker data = self._decrypter(data) 1062*cda5da8dSAndroid Build Coastguard Worker return data 1063*cda5da8dSAndroid Build Coastguard Worker 1064*cda5da8dSAndroid Build Coastguard Worker def close(self): 1065*cda5da8dSAndroid Build Coastguard Worker try: 1066*cda5da8dSAndroid Build Coastguard Worker if self._close_fileobj: 1067*cda5da8dSAndroid Build Coastguard Worker self._fileobj.close() 1068*cda5da8dSAndroid Build Coastguard Worker finally: 1069*cda5da8dSAndroid Build Coastguard Worker super().close() 1070*cda5da8dSAndroid Build Coastguard Worker 1071*cda5da8dSAndroid Build Coastguard Worker def seekable(self): 1072*cda5da8dSAndroid Build Coastguard Worker if self.closed: 1073*cda5da8dSAndroid Build Coastguard Worker raise ValueError("I/O operation on closed file.") 1074*cda5da8dSAndroid Build Coastguard Worker return self._seekable 1075*cda5da8dSAndroid Build Coastguard Worker 1076*cda5da8dSAndroid Build Coastguard Worker def seek(self, offset, whence=0): 1077*cda5da8dSAndroid Build Coastguard Worker if self.closed: 1078*cda5da8dSAndroid Build Coastguard Worker raise ValueError("seek on closed file.") 1079*cda5da8dSAndroid Build Coastguard Worker if not self._seekable: 1080*cda5da8dSAndroid Build Coastguard Worker raise io.UnsupportedOperation("underlying stream is not seekable") 1081*cda5da8dSAndroid Build Coastguard Worker curr_pos = self.tell() 1082*cda5da8dSAndroid Build Coastguard Worker if whence == 0: # Seek from start of file 1083*cda5da8dSAndroid Build Coastguard Worker new_pos = offset 1084*cda5da8dSAndroid Build Coastguard Worker elif whence == 1: # Seek from current position 1085*cda5da8dSAndroid Build Coastguard Worker new_pos = curr_pos + offset 1086*cda5da8dSAndroid Build Coastguard Worker elif whence == 2: # Seek from EOF 1087*cda5da8dSAndroid Build Coastguard Worker new_pos = self._orig_file_size + offset 1088*cda5da8dSAndroid Build Coastguard Worker else: 1089*cda5da8dSAndroid Build Coastguard Worker raise ValueError("whence must be os.SEEK_SET (0), " 1090*cda5da8dSAndroid Build Coastguard Worker "os.SEEK_CUR (1), or os.SEEK_END (2)") 1091*cda5da8dSAndroid Build Coastguard Worker 1092*cda5da8dSAndroid Build Coastguard Worker if new_pos > self._orig_file_size: 1093*cda5da8dSAndroid Build Coastguard Worker new_pos = self._orig_file_size 1094*cda5da8dSAndroid Build Coastguard Worker 1095*cda5da8dSAndroid Build Coastguard Worker if new_pos < 0: 1096*cda5da8dSAndroid Build Coastguard Worker new_pos = 0 1097*cda5da8dSAndroid Build Coastguard Worker 1098*cda5da8dSAndroid Build Coastguard Worker read_offset = new_pos - curr_pos 1099*cda5da8dSAndroid Build Coastguard Worker buff_offset = read_offset + self._offset 1100*cda5da8dSAndroid Build Coastguard Worker 1101*cda5da8dSAndroid Build Coastguard Worker if buff_offset >= 0 and buff_offset < len(self._readbuffer): 1102*cda5da8dSAndroid Build Coastguard Worker # Just move the _offset index if the new position is in the _readbuffer 1103*cda5da8dSAndroid Build Coastguard Worker self._offset = buff_offset 1104*cda5da8dSAndroid Build Coastguard Worker read_offset = 0 1105*cda5da8dSAndroid Build Coastguard Worker elif read_offset < 0: 1106*cda5da8dSAndroid Build Coastguard Worker # Position is before the current position. Reset the ZipExtFile 1107*cda5da8dSAndroid Build Coastguard Worker self._fileobj.seek(self._orig_compress_start) 1108*cda5da8dSAndroid Build Coastguard Worker self._running_crc = self._orig_start_crc 1109*cda5da8dSAndroid Build Coastguard Worker self._compress_left = self._orig_compress_size 1110*cda5da8dSAndroid Build Coastguard Worker self._left = self._orig_file_size 1111*cda5da8dSAndroid Build Coastguard Worker self._readbuffer = b'' 1112*cda5da8dSAndroid Build Coastguard Worker self._offset = 0 1113*cda5da8dSAndroid Build Coastguard Worker self._decompressor = _get_decompressor(self._compress_type) 1114*cda5da8dSAndroid Build Coastguard Worker self._eof = False 1115*cda5da8dSAndroid Build Coastguard Worker read_offset = new_pos 1116*cda5da8dSAndroid Build Coastguard Worker if self._decrypter is not None: 1117*cda5da8dSAndroid Build Coastguard Worker self._init_decrypter() 1118*cda5da8dSAndroid Build Coastguard Worker 1119*cda5da8dSAndroid Build Coastguard Worker while read_offset > 0: 1120*cda5da8dSAndroid Build Coastguard Worker read_len = min(self.MAX_SEEK_READ, read_offset) 1121*cda5da8dSAndroid Build Coastguard Worker self.read(read_len) 1122*cda5da8dSAndroid Build Coastguard Worker read_offset -= read_len 1123*cda5da8dSAndroid Build Coastguard Worker 1124*cda5da8dSAndroid Build Coastguard Worker return self.tell() 1125*cda5da8dSAndroid Build Coastguard Worker 1126*cda5da8dSAndroid Build Coastguard Worker def tell(self): 1127*cda5da8dSAndroid Build Coastguard Worker if self.closed: 1128*cda5da8dSAndroid Build Coastguard Worker raise ValueError("tell on closed file.") 1129*cda5da8dSAndroid Build Coastguard Worker if not self._seekable: 1130*cda5da8dSAndroid Build Coastguard Worker raise io.UnsupportedOperation("underlying stream is not seekable") 1131*cda5da8dSAndroid Build Coastguard Worker filepos = self._orig_file_size - self._left - len(self._readbuffer) + self._offset 1132*cda5da8dSAndroid Build Coastguard Worker return filepos 1133*cda5da8dSAndroid Build Coastguard Worker 1134*cda5da8dSAndroid Build Coastguard Worker 1135*cda5da8dSAndroid Build Coastguard Workerclass _ZipWriteFile(io.BufferedIOBase): 1136*cda5da8dSAndroid Build Coastguard Worker def __init__(self, zf, zinfo, zip64): 1137*cda5da8dSAndroid Build Coastguard Worker self._zinfo = zinfo 1138*cda5da8dSAndroid Build Coastguard Worker self._zip64 = zip64 1139*cda5da8dSAndroid Build Coastguard Worker self._zipfile = zf 1140*cda5da8dSAndroid Build Coastguard Worker self._compressor = _get_compressor(zinfo.compress_type, 1141*cda5da8dSAndroid Build Coastguard Worker zinfo._compresslevel) 1142*cda5da8dSAndroid Build Coastguard Worker self._file_size = 0 1143*cda5da8dSAndroid Build Coastguard Worker self._compress_size = 0 1144*cda5da8dSAndroid Build Coastguard Worker self._crc = 0 1145*cda5da8dSAndroid Build Coastguard Worker 1146*cda5da8dSAndroid Build Coastguard Worker @property 1147*cda5da8dSAndroid Build Coastguard Worker def _fileobj(self): 1148*cda5da8dSAndroid Build Coastguard Worker return self._zipfile.fp 1149*cda5da8dSAndroid Build Coastguard Worker 1150*cda5da8dSAndroid Build Coastguard Worker def writable(self): 1151*cda5da8dSAndroid Build Coastguard Worker return True 1152*cda5da8dSAndroid Build Coastguard Worker 1153*cda5da8dSAndroid Build Coastguard Worker def write(self, data): 1154*cda5da8dSAndroid Build Coastguard Worker if self.closed: 1155*cda5da8dSAndroid Build Coastguard Worker raise ValueError('I/O operation on closed file.') 1156*cda5da8dSAndroid Build Coastguard Worker 1157*cda5da8dSAndroid Build Coastguard Worker # Accept any data that supports the buffer protocol 1158*cda5da8dSAndroid Build Coastguard Worker if isinstance(data, (bytes, bytearray)): 1159*cda5da8dSAndroid Build Coastguard Worker nbytes = len(data) 1160*cda5da8dSAndroid Build Coastguard Worker else: 1161*cda5da8dSAndroid Build Coastguard Worker data = memoryview(data) 1162*cda5da8dSAndroid Build Coastguard Worker nbytes = data.nbytes 1163*cda5da8dSAndroid Build Coastguard Worker self._file_size += nbytes 1164*cda5da8dSAndroid Build Coastguard Worker 1165*cda5da8dSAndroid Build Coastguard Worker self._crc = crc32(data, self._crc) 1166*cda5da8dSAndroid Build Coastguard Worker if self._compressor: 1167*cda5da8dSAndroid Build Coastguard Worker data = self._compressor.compress(data) 1168*cda5da8dSAndroid Build Coastguard Worker self._compress_size += len(data) 1169*cda5da8dSAndroid Build Coastguard Worker self._fileobj.write(data) 1170*cda5da8dSAndroid Build Coastguard Worker return nbytes 1171*cda5da8dSAndroid Build Coastguard Worker 1172*cda5da8dSAndroid Build Coastguard Worker def close(self): 1173*cda5da8dSAndroid Build Coastguard Worker if self.closed: 1174*cda5da8dSAndroid Build Coastguard Worker return 1175*cda5da8dSAndroid Build Coastguard Worker try: 1176*cda5da8dSAndroid Build Coastguard Worker super().close() 1177*cda5da8dSAndroid Build Coastguard Worker # Flush any data from the compressor, and update header info 1178*cda5da8dSAndroid Build Coastguard Worker if self._compressor: 1179*cda5da8dSAndroid Build Coastguard Worker buf = self._compressor.flush() 1180*cda5da8dSAndroid Build Coastguard Worker self._compress_size += len(buf) 1181*cda5da8dSAndroid Build Coastguard Worker self._fileobj.write(buf) 1182*cda5da8dSAndroid Build Coastguard Worker self._zinfo.compress_size = self._compress_size 1183*cda5da8dSAndroid Build Coastguard Worker else: 1184*cda5da8dSAndroid Build Coastguard Worker self._zinfo.compress_size = self._file_size 1185*cda5da8dSAndroid Build Coastguard Worker self._zinfo.CRC = self._crc 1186*cda5da8dSAndroid Build Coastguard Worker self._zinfo.file_size = self._file_size 1187*cda5da8dSAndroid Build Coastguard Worker 1188*cda5da8dSAndroid Build Coastguard Worker if not self._zip64: 1189*cda5da8dSAndroid Build Coastguard Worker if self._file_size > ZIP64_LIMIT: 1190*cda5da8dSAndroid Build Coastguard Worker raise RuntimeError("File size too large, try using force_zip64") 1191*cda5da8dSAndroid Build Coastguard Worker if self._compress_size > ZIP64_LIMIT: 1192*cda5da8dSAndroid Build Coastguard Worker raise RuntimeError("Compressed size too large, try using force_zip64") 1193*cda5da8dSAndroid Build Coastguard Worker 1194*cda5da8dSAndroid Build Coastguard Worker # Write updated header info 1195*cda5da8dSAndroid Build Coastguard Worker if self._zinfo.flag_bits & _MASK_USE_DATA_DESCRIPTOR: 1196*cda5da8dSAndroid Build Coastguard Worker # Write CRC and file sizes after the file data 1197*cda5da8dSAndroid Build Coastguard Worker fmt = '<LLQQ' if self._zip64 else '<LLLL' 1198*cda5da8dSAndroid Build Coastguard Worker self._fileobj.write(struct.pack(fmt, _DD_SIGNATURE, self._zinfo.CRC, 1199*cda5da8dSAndroid Build Coastguard Worker self._zinfo.compress_size, self._zinfo.file_size)) 1200*cda5da8dSAndroid Build Coastguard Worker self._zipfile.start_dir = self._fileobj.tell() 1201*cda5da8dSAndroid Build Coastguard Worker else: 1202*cda5da8dSAndroid Build Coastguard Worker # Seek backwards and write file header (which will now include 1203*cda5da8dSAndroid Build Coastguard Worker # correct CRC and file sizes) 1204*cda5da8dSAndroid Build Coastguard Worker 1205*cda5da8dSAndroid Build Coastguard Worker # Preserve current position in file 1206*cda5da8dSAndroid Build Coastguard Worker self._zipfile.start_dir = self._fileobj.tell() 1207*cda5da8dSAndroid Build Coastguard Worker self._fileobj.seek(self._zinfo.header_offset) 1208*cda5da8dSAndroid Build Coastguard Worker self._fileobj.write(self._zinfo.FileHeader(self._zip64)) 1209*cda5da8dSAndroid Build Coastguard Worker self._fileobj.seek(self._zipfile.start_dir) 1210*cda5da8dSAndroid Build Coastguard Worker 1211*cda5da8dSAndroid Build Coastguard Worker # Successfully written: Add file to our caches 1212*cda5da8dSAndroid Build Coastguard Worker self._zipfile.filelist.append(self._zinfo) 1213*cda5da8dSAndroid Build Coastguard Worker self._zipfile.NameToInfo[self._zinfo.filename] = self._zinfo 1214*cda5da8dSAndroid Build Coastguard Worker finally: 1215*cda5da8dSAndroid Build Coastguard Worker self._zipfile._writing = False 1216*cda5da8dSAndroid Build Coastguard Worker 1217*cda5da8dSAndroid Build Coastguard Worker 1218*cda5da8dSAndroid Build Coastguard Worker 1219*cda5da8dSAndroid Build Coastguard Workerclass ZipFile: 1220*cda5da8dSAndroid Build Coastguard Worker """ Class with methods to open, read, write, close, list zip files. 1221*cda5da8dSAndroid Build Coastguard Worker 1222*cda5da8dSAndroid Build Coastguard Worker z = ZipFile(file, mode="r", compression=ZIP_STORED, allowZip64=True, 1223*cda5da8dSAndroid Build Coastguard Worker compresslevel=None) 1224*cda5da8dSAndroid Build Coastguard Worker 1225*cda5da8dSAndroid Build Coastguard Worker file: Either the path to the file, or a file-like object. 1226*cda5da8dSAndroid Build Coastguard Worker If it is a path, the file will be opened and closed by ZipFile. 1227*cda5da8dSAndroid Build Coastguard Worker mode: The mode can be either read 'r', write 'w', exclusive create 'x', 1228*cda5da8dSAndroid Build Coastguard Worker or append 'a'. 1229*cda5da8dSAndroid Build Coastguard Worker compression: ZIP_STORED (no compression), ZIP_DEFLATED (requires zlib), 1230*cda5da8dSAndroid Build Coastguard Worker ZIP_BZIP2 (requires bz2) or ZIP_LZMA (requires lzma). 1231*cda5da8dSAndroid Build Coastguard Worker allowZip64: if True ZipFile will create files with ZIP64 extensions when 1232*cda5da8dSAndroid Build Coastguard Worker needed, otherwise it will raise an exception when this would 1233*cda5da8dSAndroid Build Coastguard Worker be necessary. 1234*cda5da8dSAndroid Build Coastguard Worker compresslevel: None (default for the given compression type) or an integer 1235*cda5da8dSAndroid Build Coastguard Worker specifying the level to pass to the compressor. 1236*cda5da8dSAndroid Build Coastguard Worker When using ZIP_STORED or ZIP_LZMA this keyword has no effect. 1237*cda5da8dSAndroid Build Coastguard Worker When using ZIP_DEFLATED integers 0 through 9 are accepted. 1238*cda5da8dSAndroid Build Coastguard Worker When using ZIP_BZIP2 integers 1 through 9 are accepted. 1239*cda5da8dSAndroid Build Coastguard Worker 1240*cda5da8dSAndroid Build Coastguard Worker """ 1241*cda5da8dSAndroid Build Coastguard Worker 1242*cda5da8dSAndroid Build Coastguard Worker fp = None # Set here since __del__ checks it 1243*cda5da8dSAndroid Build Coastguard Worker _windows_illegal_name_trans_table = None 1244*cda5da8dSAndroid Build Coastguard Worker 1245*cda5da8dSAndroid Build Coastguard Worker def __init__(self, file, mode="r", compression=ZIP_STORED, allowZip64=True, 1246*cda5da8dSAndroid Build Coastguard Worker compresslevel=None, *, strict_timestamps=True, metadata_encoding=None): 1247*cda5da8dSAndroid Build Coastguard Worker """Open the ZIP file with mode read 'r', write 'w', exclusive create 'x', 1248*cda5da8dSAndroid Build Coastguard Worker or append 'a'.""" 1249*cda5da8dSAndroid Build Coastguard Worker if mode not in ('r', 'w', 'x', 'a'): 1250*cda5da8dSAndroid Build Coastguard Worker raise ValueError("ZipFile requires mode 'r', 'w', 'x', or 'a'") 1251*cda5da8dSAndroid Build Coastguard Worker 1252*cda5da8dSAndroid Build Coastguard Worker _check_compression(compression) 1253*cda5da8dSAndroid Build Coastguard Worker 1254*cda5da8dSAndroid Build Coastguard Worker self._allowZip64 = allowZip64 1255*cda5da8dSAndroid Build Coastguard Worker self._didModify = False 1256*cda5da8dSAndroid Build Coastguard Worker self.debug = 0 # Level of printing: 0 through 3 1257*cda5da8dSAndroid Build Coastguard Worker self.NameToInfo = {} # Find file info given name 1258*cda5da8dSAndroid Build Coastguard Worker self.filelist = [] # List of ZipInfo instances for archive 1259*cda5da8dSAndroid Build Coastguard Worker self.compression = compression # Method of compression 1260*cda5da8dSAndroid Build Coastguard Worker self.compresslevel = compresslevel 1261*cda5da8dSAndroid Build Coastguard Worker self.mode = mode 1262*cda5da8dSAndroid Build Coastguard Worker self.pwd = None 1263*cda5da8dSAndroid Build Coastguard Worker self._comment = b'' 1264*cda5da8dSAndroid Build Coastguard Worker self._strict_timestamps = strict_timestamps 1265*cda5da8dSAndroid Build Coastguard Worker self.metadata_encoding = metadata_encoding 1266*cda5da8dSAndroid Build Coastguard Worker 1267*cda5da8dSAndroid Build Coastguard Worker # Check that we don't try to write with nonconforming codecs 1268*cda5da8dSAndroid Build Coastguard Worker if self.metadata_encoding and mode != 'r': 1269*cda5da8dSAndroid Build Coastguard Worker raise ValueError( 1270*cda5da8dSAndroid Build Coastguard Worker "metadata_encoding is only supported for reading files") 1271*cda5da8dSAndroid Build Coastguard Worker 1272*cda5da8dSAndroid Build Coastguard Worker # Check if we were passed a file-like object 1273*cda5da8dSAndroid Build Coastguard Worker if isinstance(file, os.PathLike): 1274*cda5da8dSAndroid Build Coastguard Worker file = os.fspath(file) 1275*cda5da8dSAndroid Build Coastguard Worker if isinstance(file, str): 1276*cda5da8dSAndroid Build Coastguard Worker # No, it's a filename 1277*cda5da8dSAndroid Build Coastguard Worker self._filePassed = 0 1278*cda5da8dSAndroid Build Coastguard Worker self.filename = file 1279*cda5da8dSAndroid Build Coastguard Worker modeDict = {'r' : 'rb', 'w': 'w+b', 'x': 'x+b', 'a' : 'r+b', 1280*cda5da8dSAndroid Build Coastguard Worker 'r+b': 'w+b', 'w+b': 'wb', 'x+b': 'xb'} 1281*cda5da8dSAndroid Build Coastguard Worker filemode = modeDict[mode] 1282*cda5da8dSAndroid Build Coastguard Worker while True: 1283*cda5da8dSAndroid Build Coastguard Worker try: 1284*cda5da8dSAndroid Build Coastguard Worker self.fp = io.open(file, filemode) 1285*cda5da8dSAndroid Build Coastguard Worker except OSError: 1286*cda5da8dSAndroid Build Coastguard Worker if filemode in modeDict: 1287*cda5da8dSAndroid Build Coastguard Worker filemode = modeDict[filemode] 1288*cda5da8dSAndroid Build Coastguard Worker continue 1289*cda5da8dSAndroid Build Coastguard Worker raise 1290*cda5da8dSAndroid Build Coastguard Worker break 1291*cda5da8dSAndroid Build Coastguard Worker else: 1292*cda5da8dSAndroid Build Coastguard Worker self._filePassed = 1 1293*cda5da8dSAndroid Build Coastguard Worker self.fp = file 1294*cda5da8dSAndroid Build Coastguard Worker self.filename = getattr(file, 'name', None) 1295*cda5da8dSAndroid Build Coastguard Worker self._fileRefCnt = 1 1296*cda5da8dSAndroid Build Coastguard Worker self._lock = threading.RLock() 1297*cda5da8dSAndroid Build Coastguard Worker self._seekable = True 1298*cda5da8dSAndroid Build Coastguard Worker self._writing = False 1299*cda5da8dSAndroid Build Coastguard Worker 1300*cda5da8dSAndroid Build Coastguard Worker try: 1301*cda5da8dSAndroid Build Coastguard Worker if mode == 'r': 1302*cda5da8dSAndroid Build Coastguard Worker self._RealGetContents() 1303*cda5da8dSAndroid Build Coastguard Worker elif mode in ('w', 'x'): 1304*cda5da8dSAndroid Build Coastguard Worker # set the modified flag so central directory gets written 1305*cda5da8dSAndroid Build Coastguard Worker # even if no files are added to the archive 1306*cda5da8dSAndroid Build Coastguard Worker self._didModify = True 1307*cda5da8dSAndroid Build Coastguard Worker try: 1308*cda5da8dSAndroid Build Coastguard Worker self.start_dir = self.fp.tell() 1309*cda5da8dSAndroid Build Coastguard Worker except (AttributeError, OSError): 1310*cda5da8dSAndroid Build Coastguard Worker self.fp = _Tellable(self.fp) 1311*cda5da8dSAndroid Build Coastguard Worker self.start_dir = 0 1312*cda5da8dSAndroid Build Coastguard Worker self._seekable = False 1313*cda5da8dSAndroid Build Coastguard Worker else: 1314*cda5da8dSAndroid Build Coastguard Worker # Some file-like objects can provide tell() but not seek() 1315*cda5da8dSAndroid Build Coastguard Worker try: 1316*cda5da8dSAndroid Build Coastguard Worker self.fp.seek(self.start_dir) 1317*cda5da8dSAndroid Build Coastguard Worker except (AttributeError, OSError): 1318*cda5da8dSAndroid Build Coastguard Worker self._seekable = False 1319*cda5da8dSAndroid Build Coastguard Worker elif mode == 'a': 1320*cda5da8dSAndroid Build Coastguard Worker try: 1321*cda5da8dSAndroid Build Coastguard Worker # See if file is a zip file 1322*cda5da8dSAndroid Build Coastguard Worker self._RealGetContents() 1323*cda5da8dSAndroid Build Coastguard Worker # seek to start of directory and overwrite 1324*cda5da8dSAndroid Build Coastguard Worker self.fp.seek(self.start_dir) 1325*cda5da8dSAndroid Build Coastguard Worker except BadZipFile: 1326*cda5da8dSAndroid Build Coastguard Worker # file is not a zip file, just append 1327*cda5da8dSAndroid Build Coastguard Worker self.fp.seek(0, 2) 1328*cda5da8dSAndroid Build Coastguard Worker 1329*cda5da8dSAndroid Build Coastguard Worker # set the modified flag so central directory gets written 1330*cda5da8dSAndroid Build Coastguard Worker # even if no files are added to the archive 1331*cda5da8dSAndroid Build Coastguard Worker self._didModify = True 1332*cda5da8dSAndroid Build Coastguard Worker self.start_dir = self.fp.tell() 1333*cda5da8dSAndroid Build Coastguard Worker else: 1334*cda5da8dSAndroid Build Coastguard Worker raise ValueError("Mode must be 'r', 'w', 'x', or 'a'") 1335*cda5da8dSAndroid Build Coastguard Worker except: 1336*cda5da8dSAndroid Build Coastguard Worker fp = self.fp 1337*cda5da8dSAndroid Build Coastguard Worker self.fp = None 1338*cda5da8dSAndroid Build Coastguard Worker self._fpclose(fp) 1339*cda5da8dSAndroid Build Coastguard Worker raise 1340*cda5da8dSAndroid Build Coastguard Worker 1341*cda5da8dSAndroid Build Coastguard Worker def __enter__(self): 1342*cda5da8dSAndroid Build Coastguard Worker return self 1343*cda5da8dSAndroid Build Coastguard Worker 1344*cda5da8dSAndroid Build Coastguard Worker def __exit__(self, type, value, traceback): 1345*cda5da8dSAndroid Build Coastguard Worker self.close() 1346*cda5da8dSAndroid Build Coastguard Worker 1347*cda5da8dSAndroid Build Coastguard Worker def __repr__(self): 1348*cda5da8dSAndroid Build Coastguard Worker result = ['<%s.%s' % (self.__class__.__module__, 1349*cda5da8dSAndroid Build Coastguard Worker self.__class__.__qualname__)] 1350*cda5da8dSAndroid Build Coastguard Worker if self.fp is not None: 1351*cda5da8dSAndroid Build Coastguard Worker if self._filePassed: 1352*cda5da8dSAndroid Build Coastguard Worker result.append(' file=%r' % self.fp) 1353*cda5da8dSAndroid Build Coastguard Worker elif self.filename is not None: 1354*cda5da8dSAndroid Build Coastguard Worker result.append(' filename=%r' % self.filename) 1355*cda5da8dSAndroid Build Coastguard Worker result.append(' mode=%r' % self.mode) 1356*cda5da8dSAndroid Build Coastguard Worker else: 1357*cda5da8dSAndroid Build Coastguard Worker result.append(' [closed]') 1358*cda5da8dSAndroid Build Coastguard Worker result.append('>') 1359*cda5da8dSAndroid Build Coastguard Worker return ''.join(result) 1360*cda5da8dSAndroid Build Coastguard Worker 1361*cda5da8dSAndroid Build Coastguard Worker def _RealGetContents(self): 1362*cda5da8dSAndroid Build Coastguard Worker """Read in the table of contents for the ZIP file.""" 1363*cda5da8dSAndroid Build Coastguard Worker fp = self.fp 1364*cda5da8dSAndroid Build Coastguard Worker try: 1365*cda5da8dSAndroid Build Coastguard Worker endrec = _EndRecData(fp) 1366*cda5da8dSAndroid Build Coastguard Worker except OSError: 1367*cda5da8dSAndroid Build Coastguard Worker raise BadZipFile("File is not a zip file") 1368*cda5da8dSAndroid Build Coastguard Worker if not endrec: 1369*cda5da8dSAndroid Build Coastguard Worker raise BadZipFile("File is not a zip file") 1370*cda5da8dSAndroid Build Coastguard Worker if self.debug > 1: 1371*cda5da8dSAndroid Build Coastguard Worker print(endrec) 1372*cda5da8dSAndroid Build Coastguard Worker size_cd = endrec[_ECD_SIZE] # bytes in central directory 1373*cda5da8dSAndroid Build Coastguard Worker offset_cd = endrec[_ECD_OFFSET] # offset of central directory 1374*cda5da8dSAndroid Build Coastguard Worker self._comment = endrec[_ECD_COMMENT] # archive comment 1375*cda5da8dSAndroid Build Coastguard Worker 1376*cda5da8dSAndroid Build Coastguard Worker # "concat" is zero, unless zip was concatenated to another file 1377*cda5da8dSAndroid Build Coastguard Worker concat = endrec[_ECD_LOCATION] - size_cd - offset_cd 1378*cda5da8dSAndroid Build Coastguard Worker if endrec[_ECD_SIGNATURE] == stringEndArchive64: 1379*cda5da8dSAndroid Build Coastguard Worker # If Zip64 extension structures are present, account for them 1380*cda5da8dSAndroid Build Coastguard Worker concat -= (sizeEndCentDir64 + sizeEndCentDir64Locator) 1381*cda5da8dSAndroid Build Coastguard Worker 1382*cda5da8dSAndroid Build Coastguard Worker if self.debug > 2: 1383*cda5da8dSAndroid Build Coastguard Worker inferred = concat + offset_cd 1384*cda5da8dSAndroid Build Coastguard Worker print("given, inferred, offset", offset_cd, inferred, concat) 1385*cda5da8dSAndroid Build Coastguard Worker # self.start_dir: Position of start of central directory 1386*cda5da8dSAndroid Build Coastguard Worker self.start_dir = offset_cd + concat 1387*cda5da8dSAndroid Build Coastguard Worker if self.start_dir < 0: 1388*cda5da8dSAndroid Build Coastguard Worker raise BadZipFile("Bad offset for central directory") 1389*cda5da8dSAndroid Build Coastguard Worker fp.seek(self.start_dir, 0) 1390*cda5da8dSAndroid Build Coastguard Worker data = fp.read(size_cd) 1391*cda5da8dSAndroid Build Coastguard Worker fp = io.BytesIO(data) 1392*cda5da8dSAndroid Build Coastguard Worker total = 0 1393*cda5da8dSAndroid Build Coastguard Worker while total < size_cd: 1394*cda5da8dSAndroid Build Coastguard Worker centdir = fp.read(sizeCentralDir) 1395*cda5da8dSAndroid Build Coastguard Worker if len(centdir) != sizeCentralDir: 1396*cda5da8dSAndroid Build Coastguard Worker raise BadZipFile("Truncated central directory") 1397*cda5da8dSAndroid Build Coastguard Worker centdir = struct.unpack(structCentralDir, centdir) 1398*cda5da8dSAndroid Build Coastguard Worker if centdir[_CD_SIGNATURE] != stringCentralDir: 1399*cda5da8dSAndroid Build Coastguard Worker raise BadZipFile("Bad magic number for central directory") 1400*cda5da8dSAndroid Build Coastguard Worker if self.debug > 2: 1401*cda5da8dSAndroid Build Coastguard Worker print(centdir) 1402*cda5da8dSAndroid Build Coastguard Worker filename = fp.read(centdir[_CD_FILENAME_LENGTH]) 1403*cda5da8dSAndroid Build Coastguard Worker flags = centdir[_CD_FLAG_BITS] 1404*cda5da8dSAndroid Build Coastguard Worker if flags & _MASK_UTF_FILENAME: 1405*cda5da8dSAndroid Build Coastguard Worker # UTF-8 file names extension 1406*cda5da8dSAndroid Build Coastguard Worker filename = filename.decode('utf-8') 1407*cda5da8dSAndroid Build Coastguard Worker else: 1408*cda5da8dSAndroid Build Coastguard Worker # Historical ZIP filename encoding 1409*cda5da8dSAndroid Build Coastguard Worker filename = filename.decode(self.metadata_encoding or 'cp437') 1410*cda5da8dSAndroid Build Coastguard Worker # Create ZipInfo instance to store file information 1411*cda5da8dSAndroid Build Coastguard Worker x = ZipInfo(filename) 1412*cda5da8dSAndroid Build Coastguard Worker x.extra = fp.read(centdir[_CD_EXTRA_FIELD_LENGTH]) 1413*cda5da8dSAndroid Build Coastguard Worker x.comment = fp.read(centdir[_CD_COMMENT_LENGTH]) 1414*cda5da8dSAndroid Build Coastguard Worker x.header_offset = centdir[_CD_LOCAL_HEADER_OFFSET] 1415*cda5da8dSAndroid Build Coastguard Worker (x.create_version, x.create_system, x.extract_version, x.reserved, 1416*cda5da8dSAndroid Build Coastguard Worker x.flag_bits, x.compress_type, t, d, 1417*cda5da8dSAndroid Build Coastguard Worker x.CRC, x.compress_size, x.file_size) = centdir[1:12] 1418*cda5da8dSAndroid Build Coastguard Worker if x.extract_version > MAX_EXTRACT_VERSION: 1419*cda5da8dSAndroid Build Coastguard Worker raise NotImplementedError("zip file version %.1f" % 1420*cda5da8dSAndroid Build Coastguard Worker (x.extract_version / 10)) 1421*cda5da8dSAndroid Build Coastguard Worker x.volume, x.internal_attr, x.external_attr = centdir[15:18] 1422*cda5da8dSAndroid Build Coastguard Worker # Convert date/time code to (year, month, day, hour, min, sec) 1423*cda5da8dSAndroid Build Coastguard Worker x._raw_time = t 1424*cda5da8dSAndroid Build Coastguard Worker x.date_time = ( (d>>9)+1980, (d>>5)&0xF, d&0x1F, 1425*cda5da8dSAndroid Build Coastguard Worker t>>11, (t>>5)&0x3F, (t&0x1F) * 2 ) 1426*cda5da8dSAndroid Build Coastguard Worker 1427*cda5da8dSAndroid Build Coastguard Worker x._decodeExtra() 1428*cda5da8dSAndroid Build Coastguard Worker x.header_offset = x.header_offset + concat 1429*cda5da8dSAndroid Build Coastguard Worker self.filelist.append(x) 1430*cda5da8dSAndroid Build Coastguard Worker self.NameToInfo[x.filename] = x 1431*cda5da8dSAndroid Build Coastguard Worker 1432*cda5da8dSAndroid Build Coastguard Worker # update total bytes read from central directory 1433*cda5da8dSAndroid Build Coastguard Worker total = (total + sizeCentralDir + centdir[_CD_FILENAME_LENGTH] 1434*cda5da8dSAndroid Build Coastguard Worker + centdir[_CD_EXTRA_FIELD_LENGTH] 1435*cda5da8dSAndroid Build Coastguard Worker + centdir[_CD_COMMENT_LENGTH]) 1436*cda5da8dSAndroid Build Coastguard Worker 1437*cda5da8dSAndroid Build Coastguard Worker if self.debug > 2: 1438*cda5da8dSAndroid Build Coastguard Worker print("total", total) 1439*cda5da8dSAndroid Build Coastguard Worker 1440*cda5da8dSAndroid Build Coastguard Worker 1441*cda5da8dSAndroid Build Coastguard Worker def namelist(self): 1442*cda5da8dSAndroid Build Coastguard Worker """Return a list of file names in the archive.""" 1443*cda5da8dSAndroid Build Coastguard Worker return [data.filename for data in self.filelist] 1444*cda5da8dSAndroid Build Coastguard Worker 1445*cda5da8dSAndroid Build Coastguard Worker def infolist(self): 1446*cda5da8dSAndroid Build Coastguard Worker """Return a list of class ZipInfo instances for files in the 1447*cda5da8dSAndroid Build Coastguard Worker archive.""" 1448*cda5da8dSAndroid Build Coastguard Worker return self.filelist 1449*cda5da8dSAndroid Build Coastguard Worker 1450*cda5da8dSAndroid Build Coastguard Worker def printdir(self, file=None): 1451*cda5da8dSAndroid Build Coastguard Worker """Print a table of contents for the zip file.""" 1452*cda5da8dSAndroid Build Coastguard Worker print("%-46s %19s %12s" % ("File Name", "Modified ", "Size"), 1453*cda5da8dSAndroid Build Coastguard Worker file=file) 1454*cda5da8dSAndroid Build Coastguard Worker for zinfo in self.filelist: 1455*cda5da8dSAndroid Build Coastguard Worker date = "%d-%02d-%02d %02d:%02d:%02d" % zinfo.date_time[:6] 1456*cda5da8dSAndroid Build Coastguard Worker print("%-46s %s %12d" % (zinfo.filename, date, zinfo.file_size), 1457*cda5da8dSAndroid Build Coastguard Worker file=file) 1458*cda5da8dSAndroid Build Coastguard Worker 1459*cda5da8dSAndroid Build Coastguard Worker def testzip(self): 1460*cda5da8dSAndroid Build Coastguard Worker """Read all the files and check the CRC.""" 1461*cda5da8dSAndroid Build Coastguard Worker chunk_size = 2 ** 20 1462*cda5da8dSAndroid Build Coastguard Worker for zinfo in self.filelist: 1463*cda5da8dSAndroid Build Coastguard Worker try: 1464*cda5da8dSAndroid Build Coastguard Worker # Read by chunks, to avoid an OverflowError or a 1465*cda5da8dSAndroid Build Coastguard Worker # MemoryError with very large embedded files. 1466*cda5da8dSAndroid Build Coastguard Worker with self.open(zinfo.filename, "r") as f: 1467*cda5da8dSAndroid Build Coastguard Worker while f.read(chunk_size): # Check CRC-32 1468*cda5da8dSAndroid Build Coastguard Worker pass 1469*cda5da8dSAndroid Build Coastguard Worker except BadZipFile: 1470*cda5da8dSAndroid Build Coastguard Worker return zinfo.filename 1471*cda5da8dSAndroid Build Coastguard Worker 1472*cda5da8dSAndroid Build Coastguard Worker def getinfo(self, name): 1473*cda5da8dSAndroid Build Coastguard Worker """Return the instance of ZipInfo given 'name'.""" 1474*cda5da8dSAndroid Build Coastguard Worker info = self.NameToInfo.get(name) 1475*cda5da8dSAndroid Build Coastguard Worker if info is None: 1476*cda5da8dSAndroid Build Coastguard Worker raise KeyError( 1477*cda5da8dSAndroid Build Coastguard Worker 'There is no item named %r in the archive' % name) 1478*cda5da8dSAndroid Build Coastguard Worker 1479*cda5da8dSAndroid Build Coastguard Worker return info 1480*cda5da8dSAndroid Build Coastguard Worker 1481*cda5da8dSAndroid Build Coastguard Worker def setpassword(self, pwd): 1482*cda5da8dSAndroid Build Coastguard Worker """Set default password for encrypted files.""" 1483*cda5da8dSAndroid Build Coastguard Worker if pwd and not isinstance(pwd, bytes): 1484*cda5da8dSAndroid Build Coastguard Worker raise TypeError("pwd: expected bytes, got %s" % type(pwd).__name__) 1485*cda5da8dSAndroid Build Coastguard Worker if pwd: 1486*cda5da8dSAndroid Build Coastguard Worker self.pwd = pwd 1487*cda5da8dSAndroid Build Coastguard Worker else: 1488*cda5da8dSAndroid Build Coastguard Worker self.pwd = None 1489*cda5da8dSAndroid Build Coastguard Worker 1490*cda5da8dSAndroid Build Coastguard Worker @property 1491*cda5da8dSAndroid Build Coastguard Worker def comment(self): 1492*cda5da8dSAndroid Build Coastguard Worker """The comment text associated with the ZIP file.""" 1493*cda5da8dSAndroid Build Coastguard Worker return self._comment 1494*cda5da8dSAndroid Build Coastguard Worker 1495*cda5da8dSAndroid Build Coastguard Worker @comment.setter 1496*cda5da8dSAndroid Build Coastguard Worker def comment(self, comment): 1497*cda5da8dSAndroid Build Coastguard Worker if not isinstance(comment, bytes): 1498*cda5da8dSAndroid Build Coastguard Worker raise TypeError("comment: expected bytes, got %s" % type(comment).__name__) 1499*cda5da8dSAndroid Build Coastguard Worker # check for valid comment length 1500*cda5da8dSAndroid Build Coastguard Worker if len(comment) > ZIP_MAX_COMMENT: 1501*cda5da8dSAndroid Build Coastguard Worker import warnings 1502*cda5da8dSAndroid Build Coastguard Worker warnings.warn('Archive comment is too long; truncating to %d bytes' 1503*cda5da8dSAndroid Build Coastguard Worker % ZIP_MAX_COMMENT, stacklevel=2) 1504*cda5da8dSAndroid Build Coastguard Worker comment = comment[:ZIP_MAX_COMMENT] 1505*cda5da8dSAndroid Build Coastguard Worker self._comment = comment 1506*cda5da8dSAndroid Build Coastguard Worker self._didModify = True 1507*cda5da8dSAndroid Build Coastguard Worker 1508*cda5da8dSAndroid Build Coastguard Worker def read(self, name, pwd=None): 1509*cda5da8dSAndroid Build Coastguard Worker """Return file bytes for name.""" 1510*cda5da8dSAndroid Build Coastguard Worker with self.open(name, "r", pwd) as fp: 1511*cda5da8dSAndroid Build Coastguard Worker return fp.read() 1512*cda5da8dSAndroid Build Coastguard Worker 1513*cda5da8dSAndroid Build Coastguard Worker def open(self, name, mode="r", pwd=None, *, force_zip64=False): 1514*cda5da8dSAndroid Build Coastguard Worker """Return file-like object for 'name'. 1515*cda5da8dSAndroid Build Coastguard Worker 1516*cda5da8dSAndroid Build Coastguard Worker name is a string for the file name within the ZIP file, or a ZipInfo 1517*cda5da8dSAndroid Build Coastguard Worker object. 1518*cda5da8dSAndroid Build Coastguard Worker 1519*cda5da8dSAndroid Build Coastguard Worker mode should be 'r' to read a file already in the ZIP file, or 'w' to 1520*cda5da8dSAndroid Build Coastguard Worker write to a file newly added to the archive. 1521*cda5da8dSAndroid Build Coastguard Worker 1522*cda5da8dSAndroid Build Coastguard Worker pwd is the password to decrypt files (only used for reading). 1523*cda5da8dSAndroid Build Coastguard Worker 1524*cda5da8dSAndroid Build Coastguard Worker When writing, if the file size is not known in advance but may exceed 1525*cda5da8dSAndroid Build Coastguard Worker 2 GiB, pass force_zip64 to use the ZIP64 format, which can handle large 1526*cda5da8dSAndroid Build Coastguard Worker files. If the size is known in advance, it is best to pass a ZipInfo 1527*cda5da8dSAndroid Build Coastguard Worker instance for name, with zinfo.file_size set. 1528*cda5da8dSAndroid Build Coastguard Worker """ 1529*cda5da8dSAndroid Build Coastguard Worker if mode not in {"r", "w"}: 1530*cda5da8dSAndroid Build Coastguard Worker raise ValueError('open() requires mode "r" or "w"') 1531*cda5da8dSAndroid Build Coastguard Worker if pwd and (mode == "w"): 1532*cda5da8dSAndroid Build Coastguard Worker raise ValueError("pwd is only supported for reading files") 1533*cda5da8dSAndroid Build Coastguard Worker if not self.fp: 1534*cda5da8dSAndroid Build Coastguard Worker raise ValueError( 1535*cda5da8dSAndroid Build Coastguard Worker "Attempt to use ZIP archive that was already closed") 1536*cda5da8dSAndroid Build Coastguard Worker 1537*cda5da8dSAndroid Build Coastguard Worker # Make sure we have an info object 1538*cda5da8dSAndroid Build Coastguard Worker if isinstance(name, ZipInfo): 1539*cda5da8dSAndroid Build Coastguard Worker # 'name' is already an info object 1540*cda5da8dSAndroid Build Coastguard Worker zinfo = name 1541*cda5da8dSAndroid Build Coastguard Worker elif mode == 'w': 1542*cda5da8dSAndroid Build Coastguard Worker zinfo = ZipInfo(name) 1543*cda5da8dSAndroid Build Coastguard Worker zinfo.compress_type = self.compression 1544*cda5da8dSAndroid Build Coastguard Worker zinfo._compresslevel = self.compresslevel 1545*cda5da8dSAndroid Build Coastguard Worker else: 1546*cda5da8dSAndroid Build Coastguard Worker # Get info object for name 1547*cda5da8dSAndroid Build Coastguard Worker zinfo = self.getinfo(name) 1548*cda5da8dSAndroid Build Coastguard Worker 1549*cda5da8dSAndroid Build Coastguard Worker if mode == 'w': 1550*cda5da8dSAndroid Build Coastguard Worker return self._open_to_write(zinfo, force_zip64=force_zip64) 1551*cda5da8dSAndroid Build Coastguard Worker 1552*cda5da8dSAndroid Build Coastguard Worker if self._writing: 1553*cda5da8dSAndroid Build Coastguard Worker raise ValueError("Can't read from the ZIP file while there " 1554*cda5da8dSAndroid Build Coastguard Worker "is an open writing handle on it. " 1555*cda5da8dSAndroid Build Coastguard Worker "Close the writing handle before trying to read.") 1556*cda5da8dSAndroid Build Coastguard Worker 1557*cda5da8dSAndroid Build Coastguard Worker # Open for reading: 1558*cda5da8dSAndroid Build Coastguard Worker self._fileRefCnt += 1 1559*cda5da8dSAndroid Build Coastguard Worker zef_file = _SharedFile(self.fp, zinfo.header_offset, 1560*cda5da8dSAndroid Build Coastguard Worker self._fpclose, self._lock, lambda: self._writing) 1561*cda5da8dSAndroid Build Coastguard Worker try: 1562*cda5da8dSAndroid Build Coastguard Worker # Skip the file header: 1563*cda5da8dSAndroid Build Coastguard Worker fheader = zef_file.read(sizeFileHeader) 1564*cda5da8dSAndroid Build Coastguard Worker if len(fheader) != sizeFileHeader: 1565*cda5da8dSAndroid Build Coastguard Worker raise BadZipFile("Truncated file header") 1566*cda5da8dSAndroid Build Coastguard Worker fheader = struct.unpack(structFileHeader, fheader) 1567*cda5da8dSAndroid Build Coastguard Worker if fheader[_FH_SIGNATURE] != stringFileHeader: 1568*cda5da8dSAndroid Build Coastguard Worker raise BadZipFile("Bad magic number for file header") 1569*cda5da8dSAndroid Build Coastguard Worker 1570*cda5da8dSAndroid Build Coastguard Worker fname = zef_file.read(fheader[_FH_FILENAME_LENGTH]) 1571*cda5da8dSAndroid Build Coastguard Worker if fheader[_FH_EXTRA_FIELD_LENGTH]: 1572*cda5da8dSAndroid Build Coastguard Worker zef_file.read(fheader[_FH_EXTRA_FIELD_LENGTH]) 1573*cda5da8dSAndroid Build Coastguard Worker 1574*cda5da8dSAndroid Build Coastguard Worker if zinfo.flag_bits & _MASK_COMPRESSED_PATCH: 1575*cda5da8dSAndroid Build Coastguard Worker # Zip 2.7: compressed patched data 1576*cda5da8dSAndroid Build Coastguard Worker raise NotImplementedError("compressed patched data (flag bit 5)") 1577*cda5da8dSAndroid Build Coastguard Worker 1578*cda5da8dSAndroid Build Coastguard Worker if zinfo.flag_bits & _MASK_STRONG_ENCRYPTION: 1579*cda5da8dSAndroid Build Coastguard Worker # strong encryption 1580*cda5da8dSAndroid Build Coastguard Worker raise NotImplementedError("strong encryption (flag bit 6)") 1581*cda5da8dSAndroid Build Coastguard Worker 1582*cda5da8dSAndroid Build Coastguard Worker if fheader[_FH_GENERAL_PURPOSE_FLAG_BITS] & _MASK_UTF_FILENAME: 1583*cda5da8dSAndroid Build Coastguard Worker # UTF-8 filename 1584*cda5da8dSAndroid Build Coastguard Worker fname_str = fname.decode("utf-8") 1585*cda5da8dSAndroid Build Coastguard Worker else: 1586*cda5da8dSAndroid Build Coastguard Worker fname_str = fname.decode(self.metadata_encoding or "cp437") 1587*cda5da8dSAndroid Build Coastguard Worker 1588*cda5da8dSAndroid Build Coastguard Worker if fname_str != zinfo.orig_filename: 1589*cda5da8dSAndroid Build Coastguard Worker raise BadZipFile( 1590*cda5da8dSAndroid Build Coastguard Worker 'File name in directory %r and header %r differ.' 1591*cda5da8dSAndroid Build Coastguard Worker % (zinfo.orig_filename, fname)) 1592*cda5da8dSAndroid Build Coastguard Worker 1593*cda5da8dSAndroid Build Coastguard Worker # check for encrypted flag & handle password 1594*cda5da8dSAndroid Build Coastguard Worker is_encrypted = zinfo.flag_bits & _MASK_ENCRYPTED 1595*cda5da8dSAndroid Build Coastguard Worker if is_encrypted: 1596*cda5da8dSAndroid Build Coastguard Worker if not pwd: 1597*cda5da8dSAndroid Build Coastguard Worker pwd = self.pwd 1598*cda5da8dSAndroid Build Coastguard Worker if pwd and not isinstance(pwd, bytes): 1599*cda5da8dSAndroid Build Coastguard Worker raise TypeError("pwd: expected bytes, got %s" % type(pwd).__name__) 1600*cda5da8dSAndroid Build Coastguard Worker if not pwd: 1601*cda5da8dSAndroid Build Coastguard Worker raise RuntimeError("File %r is encrypted, password " 1602*cda5da8dSAndroid Build Coastguard Worker "required for extraction" % name) 1603*cda5da8dSAndroid Build Coastguard Worker else: 1604*cda5da8dSAndroid Build Coastguard Worker pwd = None 1605*cda5da8dSAndroid Build Coastguard Worker 1606*cda5da8dSAndroid Build Coastguard Worker return ZipExtFile(zef_file, mode, zinfo, pwd, True) 1607*cda5da8dSAndroid Build Coastguard Worker except: 1608*cda5da8dSAndroid Build Coastguard Worker zef_file.close() 1609*cda5da8dSAndroid Build Coastguard Worker raise 1610*cda5da8dSAndroid Build Coastguard Worker 1611*cda5da8dSAndroid Build Coastguard Worker def _open_to_write(self, zinfo, force_zip64=False): 1612*cda5da8dSAndroid Build Coastguard Worker if force_zip64 and not self._allowZip64: 1613*cda5da8dSAndroid Build Coastguard Worker raise ValueError( 1614*cda5da8dSAndroid Build Coastguard Worker "force_zip64 is True, but allowZip64 was False when opening " 1615*cda5da8dSAndroid Build Coastguard Worker "the ZIP file." 1616*cda5da8dSAndroid Build Coastguard Worker ) 1617*cda5da8dSAndroid Build Coastguard Worker if self._writing: 1618*cda5da8dSAndroid Build Coastguard Worker raise ValueError("Can't write to the ZIP file while there is " 1619*cda5da8dSAndroid Build Coastguard Worker "another write handle open on it. " 1620*cda5da8dSAndroid Build Coastguard Worker "Close the first handle before opening another.") 1621*cda5da8dSAndroid Build Coastguard Worker 1622*cda5da8dSAndroid Build Coastguard Worker # Size and CRC are overwritten with correct data after processing the file 1623*cda5da8dSAndroid Build Coastguard Worker zinfo.compress_size = 0 1624*cda5da8dSAndroid Build Coastguard Worker zinfo.CRC = 0 1625*cda5da8dSAndroid Build Coastguard Worker 1626*cda5da8dSAndroid Build Coastguard Worker zinfo.flag_bits = 0x00 1627*cda5da8dSAndroid Build Coastguard Worker if zinfo.compress_type == ZIP_LZMA: 1628*cda5da8dSAndroid Build Coastguard Worker # Compressed data includes an end-of-stream (EOS) marker 1629*cda5da8dSAndroid Build Coastguard Worker zinfo.flag_bits |= _MASK_COMPRESS_OPTION_1 1630*cda5da8dSAndroid Build Coastguard Worker if not self._seekable: 1631*cda5da8dSAndroid Build Coastguard Worker zinfo.flag_bits |= _MASK_USE_DATA_DESCRIPTOR 1632*cda5da8dSAndroid Build Coastguard Worker 1633*cda5da8dSAndroid Build Coastguard Worker if not zinfo.external_attr: 1634*cda5da8dSAndroid Build Coastguard Worker zinfo.external_attr = 0o600 << 16 # permissions: ?rw------- 1635*cda5da8dSAndroid Build Coastguard Worker 1636*cda5da8dSAndroid Build Coastguard Worker # Compressed size can be larger than uncompressed size 1637*cda5da8dSAndroid Build Coastguard Worker zip64 = force_zip64 or (zinfo.file_size * 1.05 > ZIP64_LIMIT) 1638*cda5da8dSAndroid Build Coastguard Worker if not self._allowZip64 and zip64: 1639*cda5da8dSAndroid Build Coastguard Worker raise LargeZipFile("Filesize would require ZIP64 extensions") 1640*cda5da8dSAndroid Build Coastguard Worker 1641*cda5da8dSAndroid Build Coastguard Worker if self._seekable: 1642*cda5da8dSAndroid Build Coastguard Worker self.fp.seek(self.start_dir) 1643*cda5da8dSAndroid Build Coastguard Worker zinfo.header_offset = self.fp.tell() 1644*cda5da8dSAndroid Build Coastguard Worker 1645*cda5da8dSAndroid Build Coastguard Worker self._writecheck(zinfo) 1646*cda5da8dSAndroid Build Coastguard Worker self._didModify = True 1647*cda5da8dSAndroid Build Coastguard Worker 1648*cda5da8dSAndroid Build Coastguard Worker self.fp.write(zinfo.FileHeader(zip64)) 1649*cda5da8dSAndroid Build Coastguard Worker 1650*cda5da8dSAndroid Build Coastguard Worker self._writing = True 1651*cda5da8dSAndroid Build Coastguard Worker return _ZipWriteFile(self, zinfo, zip64) 1652*cda5da8dSAndroid Build Coastguard Worker 1653*cda5da8dSAndroid Build Coastguard Worker def extract(self, member, path=None, pwd=None): 1654*cda5da8dSAndroid Build Coastguard Worker """Extract a member from the archive to the current working directory, 1655*cda5da8dSAndroid Build Coastguard Worker using its full name. Its file information is extracted as accurately 1656*cda5da8dSAndroid Build Coastguard Worker as possible. `member' may be a filename or a ZipInfo object. You can 1657*cda5da8dSAndroid Build Coastguard Worker specify a different directory using `path'. 1658*cda5da8dSAndroid Build Coastguard Worker """ 1659*cda5da8dSAndroid Build Coastguard Worker if path is None: 1660*cda5da8dSAndroid Build Coastguard Worker path = os.getcwd() 1661*cda5da8dSAndroid Build Coastguard Worker else: 1662*cda5da8dSAndroid Build Coastguard Worker path = os.fspath(path) 1663*cda5da8dSAndroid Build Coastguard Worker 1664*cda5da8dSAndroid Build Coastguard Worker return self._extract_member(member, path, pwd) 1665*cda5da8dSAndroid Build Coastguard Worker 1666*cda5da8dSAndroid Build Coastguard Worker def extractall(self, path=None, members=None, pwd=None): 1667*cda5da8dSAndroid Build Coastguard Worker """Extract all members from the archive to the current working 1668*cda5da8dSAndroid Build Coastguard Worker directory. `path' specifies a different directory to extract to. 1669*cda5da8dSAndroid Build Coastguard Worker `members' is optional and must be a subset of the list returned 1670*cda5da8dSAndroid Build Coastguard Worker by namelist(). 1671*cda5da8dSAndroid Build Coastguard Worker """ 1672*cda5da8dSAndroid Build Coastguard Worker if members is None: 1673*cda5da8dSAndroid Build Coastguard Worker members = self.namelist() 1674*cda5da8dSAndroid Build Coastguard Worker 1675*cda5da8dSAndroid Build Coastguard Worker if path is None: 1676*cda5da8dSAndroid Build Coastguard Worker path = os.getcwd() 1677*cda5da8dSAndroid Build Coastguard Worker else: 1678*cda5da8dSAndroid Build Coastguard Worker path = os.fspath(path) 1679*cda5da8dSAndroid Build Coastguard Worker 1680*cda5da8dSAndroid Build Coastguard Worker for zipinfo in members: 1681*cda5da8dSAndroid Build Coastguard Worker self._extract_member(zipinfo, path, pwd) 1682*cda5da8dSAndroid Build Coastguard Worker 1683*cda5da8dSAndroid Build Coastguard Worker @classmethod 1684*cda5da8dSAndroid Build Coastguard Worker def _sanitize_windows_name(cls, arcname, pathsep): 1685*cda5da8dSAndroid Build Coastguard Worker """Replace bad characters and remove trailing dots from parts.""" 1686*cda5da8dSAndroid Build Coastguard Worker table = cls._windows_illegal_name_trans_table 1687*cda5da8dSAndroid Build Coastguard Worker if not table: 1688*cda5da8dSAndroid Build Coastguard Worker illegal = ':<>|"?*' 1689*cda5da8dSAndroid Build Coastguard Worker table = str.maketrans(illegal, '_' * len(illegal)) 1690*cda5da8dSAndroid Build Coastguard Worker cls._windows_illegal_name_trans_table = table 1691*cda5da8dSAndroid Build Coastguard Worker arcname = arcname.translate(table) 1692*cda5da8dSAndroid Build Coastguard Worker # remove trailing dots 1693*cda5da8dSAndroid Build Coastguard Worker arcname = (x.rstrip('.') for x in arcname.split(pathsep)) 1694*cda5da8dSAndroid Build Coastguard Worker # rejoin, removing empty parts. 1695*cda5da8dSAndroid Build Coastguard Worker arcname = pathsep.join(x for x in arcname if x) 1696*cda5da8dSAndroid Build Coastguard Worker return arcname 1697*cda5da8dSAndroid Build Coastguard Worker 1698*cda5da8dSAndroid Build Coastguard Worker def _extract_member(self, member, targetpath, pwd): 1699*cda5da8dSAndroid Build Coastguard Worker """Extract the ZipInfo object 'member' to a physical 1700*cda5da8dSAndroid Build Coastguard Worker file on the path targetpath. 1701*cda5da8dSAndroid Build Coastguard Worker """ 1702*cda5da8dSAndroid Build Coastguard Worker if not isinstance(member, ZipInfo): 1703*cda5da8dSAndroid Build Coastguard Worker member = self.getinfo(member) 1704*cda5da8dSAndroid Build Coastguard Worker 1705*cda5da8dSAndroid Build Coastguard Worker # build the destination pathname, replacing 1706*cda5da8dSAndroid Build Coastguard Worker # forward slashes to platform specific separators. 1707*cda5da8dSAndroid Build Coastguard Worker arcname = member.filename.replace('/', os.path.sep) 1708*cda5da8dSAndroid Build Coastguard Worker 1709*cda5da8dSAndroid Build Coastguard Worker if os.path.altsep: 1710*cda5da8dSAndroid Build Coastguard Worker arcname = arcname.replace(os.path.altsep, os.path.sep) 1711*cda5da8dSAndroid Build Coastguard Worker # interpret absolute pathname as relative, remove drive letter or 1712*cda5da8dSAndroid Build Coastguard Worker # UNC path, redundant separators, "." and ".." components. 1713*cda5da8dSAndroid Build Coastguard Worker arcname = os.path.splitdrive(arcname)[1] 1714*cda5da8dSAndroid Build Coastguard Worker invalid_path_parts = ('', os.path.curdir, os.path.pardir) 1715*cda5da8dSAndroid Build Coastguard Worker arcname = os.path.sep.join(x for x in arcname.split(os.path.sep) 1716*cda5da8dSAndroid Build Coastguard Worker if x not in invalid_path_parts) 1717*cda5da8dSAndroid Build Coastguard Worker if os.path.sep == '\\': 1718*cda5da8dSAndroid Build Coastguard Worker # filter illegal characters on Windows 1719*cda5da8dSAndroid Build Coastguard Worker arcname = self._sanitize_windows_name(arcname, os.path.sep) 1720*cda5da8dSAndroid Build Coastguard Worker 1721*cda5da8dSAndroid Build Coastguard Worker targetpath = os.path.join(targetpath, arcname) 1722*cda5da8dSAndroid Build Coastguard Worker targetpath = os.path.normpath(targetpath) 1723*cda5da8dSAndroid Build Coastguard Worker 1724*cda5da8dSAndroid Build Coastguard Worker # Create all upper directories if necessary. 1725*cda5da8dSAndroid Build Coastguard Worker upperdirs = os.path.dirname(targetpath) 1726*cda5da8dSAndroid Build Coastguard Worker if upperdirs and not os.path.exists(upperdirs): 1727*cda5da8dSAndroid Build Coastguard Worker os.makedirs(upperdirs) 1728*cda5da8dSAndroid Build Coastguard Worker 1729*cda5da8dSAndroid Build Coastguard Worker if member.is_dir(): 1730*cda5da8dSAndroid Build Coastguard Worker if not os.path.isdir(targetpath): 1731*cda5da8dSAndroid Build Coastguard Worker os.mkdir(targetpath) 1732*cda5da8dSAndroid Build Coastguard Worker return targetpath 1733*cda5da8dSAndroid Build Coastguard Worker 1734*cda5da8dSAndroid Build Coastguard Worker with self.open(member, pwd=pwd) as source, \ 1735*cda5da8dSAndroid Build Coastguard Worker open(targetpath, "wb") as target: 1736*cda5da8dSAndroid Build Coastguard Worker shutil.copyfileobj(source, target) 1737*cda5da8dSAndroid Build Coastguard Worker 1738*cda5da8dSAndroid Build Coastguard Worker return targetpath 1739*cda5da8dSAndroid Build Coastguard Worker 1740*cda5da8dSAndroid Build Coastguard Worker def _writecheck(self, zinfo): 1741*cda5da8dSAndroid Build Coastguard Worker """Check for errors before writing a file to the archive.""" 1742*cda5da8dSAndroid Build Coastguard Worker if zinfo.filename in self.NameToInfo: 1743*cda5da8dSAndroid Build Coastguard Worker import warnings 1744*cda5da8dSAndroid Build Coastguard Worker warnings.warn('Duplicate name: %r' % zinfo.filename, stacklevel=3) 1745*cda5da8dSAndroid Build Coastguard Worker if self.mode not in ('w', 'x', 'a'): 1746*cda5da8dSAndroid Build Coastguard Worker raise ValueError("write() requires mode 'w', 'x', or 'a'") 1747*cda5da8dSAndroid Build Coastguard Worker if not self.fp: 1748*cda5da8dSAndroid Build Coastguard Worker raise ValueError( 1749*cda5da8dSAndroid Build Coastguard Worker "Attempt to write ZIP archive that was already closed") 1750*cda5da8dSAndroid Build Coastguard Worker _check_compression(zinfo.compress_type) 1751*cda5da8dSAndroid Build Coastguard Worker if not self._allowZip64: 1752*cda5da8dSAndroid Build Coastguard Worker requires_zip64 = None 1753*cda5da8dSAndroid Build Coastguard Worker if len(self.filelist) >= ZIP_FILECOUNT_LIMIT: 1754*cda5da8dSAndroid Build Coastguard Worker requires_zip64 = "Files count" 1755*cda5da8dSAndroid Build Coastguard Worker elif zinfo.file_size > ZIP64_LIMIT: 1756*cda5da8dSAndroid Build Coastguard Worker requires_zip64 = "Filesize" 1757*cda5da8dSAndroid Build Coastguard Worker elif zinfo.header_offset > ZIP64_LIMIT: 1758*cda5da8dSAndroid Build Coastguard Worker requires_zip64 = "Zipfile size" 1759*cda5da8dSAndroid Build Coastguard Worker if requires_zip64: 1760*cda5da8dSAndroid Build Coastguard Worker raise LargeZipFile(requires_zip64 + 1761*cda5da8dSAndroid Build Coastguard Worker " would require ZIP64 extensions") 1762*cda5da8dSAndroid Build Coastguard Worker 1763*cda5da8dSAndroid Build Coastguard Worker def write(self, filename, arcname=None, 1764*cda5da8dSAndroid Build Coastguard Worker compress_type=None, compresslevel=None): 1765*cda5da8dSAndroid Build Coastguard Worker """Put the bytes from filename into the archive under the name 1766*cda5da8dSAndroid Build Coastguard Worker arcname.""" 1767*cda5da8dSAndroid Build Coastguard Worker if not self.fp: 1768*cda5da8dSAndroid Build Coastguard Worker raise ValueError( 1769*cda5da8dSAndroid Build Coastguard Worker "Attempt to write to ZIP archive that was already closed") 1770*cda5da8dSAndroid Build Coastguard Worker if self._writing: 1771*cda5da8dSAndroid Build Coastguard Worker raise ValueError( 1772*cda5da8dSAndroid Build Coastguard Worker "Can't write to ZIP archive while an open writing handle exists" 1773*cda5da8dSAndroid Build Coastguard Worker ) 1774*cda5da8dSAndroid Build Coastguard Worker 1775*cda5da8dSAndroid Build Coastguard Worker zinfo = ZipInfo.from_file(filename, arcname, 1776*cda5da8dSAndroid Build Coastguard Worker strict_timestamps=self._strict_timestamps) 1777*cda5da8dSAndroid Build Coastguard Worker 1778*cda5da8dSAndroid Build Coastguard Worker if zinfo.is_dir(): 1779*cda5da8dSAndroid Build Coastguard Worker zinfo.compress_size = 0 1780*cda5da8dSAndroid Build Coastguard Worker zinfo.CRC = 0 1781*cda5da8dSAndroid Build Coastguard Worker self.mkdir(zinfo) 1782*cda5da8dSAndroid Build Coastguard Worker else: 1783*cda5da8dSAndroid Build Coastguard Worker if compress_type is not None: 1784*cda5da8dSAndroid Build Coastguard Worker zinfo.compress_type = compress_type 1785*cda5da8dSAndroid Build Coastguard Worker else: 1786*cda5da8dSAndroid Build Coastguard Worker zinfo.compress_type = self.compression 1787*cda5da8dSAndroid Build Coastguard Worker 1788*cda5da8dSAndroid Build Coastguard Worker if compresslevel is not None: 1789*cda5da8dSAndroid Build Coastguard Worker zinfo._compresslevel = compresslevel 1790*cda5da8dSAndroid Build Coastguard Worker else: 1791*cda5da8dSAndroid Build Coastguard Worker zinfo._compresslevel = self.compresslevel 1792*cda5da8dSAndroid Build Coastguard Worker 1793*cda5da8dSAndroid Build Coastguard Worker with open(filename, "rb") as src, self.open(zinfo, 'w') as dest: 1794*cda5da8dSAndroid Build Coastguard Worker shutil.copyfileobj(src, dest, 1024*8) 1795*cda5da8dSAndroid Build Coastguard Worker 1796*cda5da8dSAndroid Build Coastguard Worker def writestr(self, zinfo_or_arcname, data, 1797*cda5da8dSAndroid Build Coastguard Worker compress_type=None, compresslevel=None): 1798*cda5da8dSAndroid Build Coastguard Worker """Write a file into the archive. The contents is 'data', which 1799*cda5da8dSAndroid Build Coastguard Worker may be either a 'str' or a 'bytes' instance; if it is a 'str', 1800*cda5da8dSAndroid Build Coastguard Worker it is encoded as UTF-8 first. 1801*cda5da8dSAndroid Build Coastguard Worker 'zinfo_or_arcname' is either a ZipInfo instance or 1802*cda5da8dSAndroid Build Coastguard Worker the name of the file in the archive.""" 1803*cda5da8dSAndroid Build Coastguard Worker if isinstance(data, str): 1804*cda5da8dSAndroid Build Coastguard Worker data = data.encode("utf-8") 1805*cda5da8dSAndroid Build Coastguard Worker if not isinstance(zinfo_or_arcname, ZipInfo): 1806*cda5da8dSAndroid Build Coastguard Worker zinfo = ZipInfo(filename=zinfo_or_arcname, 1807*cda5da8dSAndroid Build Coastguard Worker date_time=time.localtime(time.time())[:6]) 1808*cda5da8dSAndroid Build Coastguard Worker zinfo.compress_type = self.compression 1809*cda5da8dSAndroid Build Coastguard Worker zinfo._compresslevel = self.compresslevel 1810*cda5da8dSAndroid Build Coastguard Worker if zinfo.filename[-1] == '/': 1811*cda5da8dSAndroid Build Coastguard Worker zinfo.external_attr = 0o40775 << 16 # drwxrwxr-x 1812*cda5da8dSAndroid Build Coastguard Worker zinfo.external_attr |= 0x10 # MS-DOS directory flag 1813*cda5da8dSAndroid Build Coastguard Worker else: 1814*cda5da8dSAndroid Build Coastguard Worker zinfo.external_attr = 0o600 << 16 # ?rw------- 1815*cda5da8dSAndroid Build Coastguard Worker else: 1816*cda5da8dSAndroid Build Coastguard Worker zinfo = zinfo_or_arcname 1817*cda5da8dSAndroid Build Coastguard Worker 1818*cda5da8dSAndroid Build Coastguard Worker if not self.fp: 1819*cda5da8dSAndroid Build Coastguard Worker raise ValueError( 1820*cda5da8dSAndroid Build Coastguard Worker "Attempt to write to ZIP archive that was already closed") 1821*cda5da8dSAndroid Build Coastguard Worker if self._writing: 1822*cda5da8dSAndroid Build Coastguard Worker raise ValueError( 1823*cda5da8dSAndroid Build Coastguard Worker "Can't write to ZIP archive while an open writing handle exists." 1824*cda5da8dSAndroid Build Coastguard Worker ) 1825*cda5da8dSAndroid Build Coastguard Worker 1826*cda5da8dSAndroid Build Coastguard Worker if compress_type is not None: 1827*cda5da8dSAndroid Build Coastguard Worker zinfo.compress_type = compress_type 1828*cda5da8dSAndroid Build Coastguard Worker 1829*cda5da8dSAndroid Build Coastguard Worker if compresslevel is not None: 1830*cda5da8dSAndroid Build Coastguard Worker zinfo._compresslevel = compresslevel 1831*cda5da8dSAndroid Build Coastguard Worker 1832*cda5da8dSAndroid Build Coastguard Worker zinfo.file_size = len(data) # Uncompressed size 1833*cda5da8dSAndroid Build Coastguard Worker with self._lock: 1834*cda5da8dSAndroid Build Coastguard Worker with self.open(zinfo, mode='w') as dest: 1835*cda5da8dSAndroid Build Coastguard Worker dest.write(data) 1836*cda5da8dSAndroid Build Coastguard Worker 1837*cda5da8dSAndroid Build Coastguard Worker def mkdir(self, zinfo_or_directory_name, mode=511): 1838*cda5da8dSAndroid Build Coastguard Worker """Creates a directory inside the zip archive.""" 1839*cda5da8dSAndroid Build Coastguard Worker if isinstance(zinfo_or_directory_name, ZipInfo): 1840*cda5da8dSAndroid Build Coastguard Worker zinfo = zinfo_or_directory_name 1841*cda5da8dSAndroid Build Coastguard Worker if not zinfo.is_dir(): 1842*cda5da8dSAndroid Build Coastguard Worker raise ValueError("The given ZipInfo does not describe a directory") 1843*cda5da8dSAndroid Build Coastguard Worker elif isinstance(zinfo_or_directory_name, str): 1844*cda5da8dSAndroid Build Coastguard Worker directory_name = zinfo_or_directory_name 1845*cda5da8dSAndroid Build Coastguard Worker if not directory_name.endswith("/"): 1846*cda5da8dSAndroid Build Coastguard Worker directory_name += "/" 1847*cda5da8dSAndroid Build Coastguard Worker zinfo = ZipInfo(directory_name) 1848*cda5da8dSAndroid Build Coastguard Worker zinfo.compress_size = 0 1849*cda5da8dSAndroid Build Coastguard Worker zinfo.CRC = 0 1850*cda5da8dSAndroid Build Coastguard Worker zinfo.external_attr = ((0o40000 | mode) & 0xFFFF) << 16 1851*cda5da8dSAndroid Build Coastguard Worker zinfo.file_size = 0 1852*cda5da8dSAndroid Build Coastguard Worker zinfo.external_attr |= 0x10 1853*cda5da8dSAndroid Build Coastguard Worker else: 1854*cda5da8dSAndroid Build Coastguard Worker raise TypeError("Expected type str or ZipInfo") 1855*cda5da8dSAndroid Build Coastguard Worker 1856*cda5da8dSAndroid Build Coastguard Worker with self._lock: 1857*cda5da8dSAndroid Build Coastguard Worker if self._seekable: 1858*cda5da8dSAndroid Build Coastguard Worker self.fp.seek(self.start_dir) 1859*cda5da8dSAndroid Build Coastguard Worker zinfo.header_offset = self.fp.tell() # Start of header bytes 1860*cda5da8dSAndroid Build Coastguard Worker if zinfo.compress_type == ZIP_LZMA: 1861*cda5da8dSAndroid Build Coastguard Worker # Compressed data includes an end-of-stream (EOS) marker 1862*cda5da8dSAndroid Build Coastguard Worker zinfo.flag_bits |= _MASK_COMPRESS_OPTION_1 1863*cda5da8dSAndroid Build Coastguard Worker 1864*cda5da8dSAndroid Build Coastguard Worker self._writecheck(zinfo) 1865*cda5da8dSAndroid Build Coastguard Worker self._didModify = True 1866*cda5da8dSAndroid Build Coastguard Worker 1867*cda5da8dSAndroid Build Coastguard Worker self.filelist.append(zinfo) 1868*cda5da8dSAndroid Build Coastguard Worker self.NameToInfo[zinfo.filename] = zinfo 1869*cda5da8dSAndroid Build Coastguard Worker self.fp.write(zinfo.FileHeader(False)) 1870*cda5da8dSAndroid Build Coastguard Worker self.start_dir = self.fp.tell() 1871*cda5da8dSAndroid Build Coastguard Worker 1872*cda5da8dSAndroid Build Coastguard Worker def __del__(self): 1873*cda5da8dSAndroid Build Coastguard Worker """Call the "close()" method in case the user forgot.""" 1874*cda5da8dSAndroid Build Coastguard Worker self.close() 1875*cda5da8dSAndroid Build Coastguard Worker 1876*cda5da8dSAndroid Build Coastguard Worker def close(self): 1877*cda5da8dSAndroid Build Coastguard Worker """Close the file, and for mode 'w', 'x' and 'a' write the ending 1878*cda5da8dSAndroid Build Coastguard Worker records.""" 1879*cda5da8dSAndroid Build Coastguard Worker if self.fp is None: 1880*cda5da8dSAndroid Build Coastguard Worker return 1881*cda5da8dSAndroid Build Coastguard Worker 1882*cda5da8dSAndroid Build Coastguard Worker if self._writing: 1883*cda5da8dSAndroid Build Coastguard Worker raise ValueError("Can't close the ZIP file while there is " 1884*cda5da8dSAndroid Build Coastguard Worker "an open writing handle on it. " 1885*cda5da8dSAndroid Build Coastguard Worker "Close the writing handle before closing the zip.") 1886*cda5da8dSAndroid Build Coastguard Worker 1887*cda5da8dSAndroid Build Coastguard Worker try: 1888*cda5da8dSAndroid Build Coastguard Worker if self.mode in ('w', 'x', 'a') and self._didModify: # write ending records 1889*cda5da8dSAndroid Build Coastguard Worker with self._lock: 1890*cda5da8dSAndroid Build Coastguard Worker if self._seekable: 1891*cda5da8dSAndroid Build Coastguard Worker self.fp.seek(self.start_dir) 1892*cda5da8dSAndroid Build Coastguard Worker self._write_end_record() 1893*cda5da8dSAndroid Build Coastguard Worker finally: 1894*cda5da8dSAndroid Build Coastguard Worker fp = self.fp 1895*cda5da8dSAndroid Build Coastguard Worker self.fp = None 1896*cda5da8dSAndroid Build Coastguard Worker self._fpclose(fp) 1897*cda5da8dSAndroid Build Coastguard Worker 1898*cda5da8dSAndroid Build Coastguard Worker def _write_end_record(self): 1899*cda5da8dSAndroid Build Coastguard Worker for zinfo in self.filelist: # write central directory 1900*cda5da8dSAndroid Build Coastguard Worker dt = zinfo.date_time 1901*cda5da8dSAndroid Build Coastguard Worker dosdate = (dt[0] - 1980) << 9 | dt[1] << 5 | dt[2] 1902*cda5da8dSAndroid Build Coastguard Worker dostime = dt[3] << 11 | dt[4] << 5 | (dt[5] // 2) 1903*cda5da8dSAndroid Build Coastguard Worker extra = [] 1904*cda5da8dSAndroid Build Coastguard Worker if zinfo.file_size > ZIP64_LIMIT \ 1905*cda5da8dSAndroid Build Coastguard Worker or zinfo.compress_size > ZIP64_LIMIT: 1906*cda5da8dSAndroid Build Coastguard Worker extra.append(zinfo.file_size) 1907*cda5da8dSAndroid Build Coastguard Worker extra.append(zinfo.compress_size) 1908*cda5da8dSAndroid Build Coastguard Worker file_size = 0xffffffff 1909*cda5da8dSAndroid Build Coastguard Worker compress_size = 0xffffffff 1910*cda5da8dSAndroid Build Coastguard Worker else: 1911*cda5da8dSAndroid Build Coastguard Worker file_size = zinfo.file_size 1912*cda5da8dSAndroid Build Coastguard Worker compress_size = zinfo.compress_size 1913*cda5da8dSAndroid Build Coastguard Worker 1914*cda5da8dSAndroid Build Coastguard Worker if zinfo.header_offset > ZIP64_LIMIT: 1915*cda5da8dSAndroid Build Coastguard Worker extra.append(zinfo.header_offset) 1916*cda5da8dSAndroid Build Coastguard Worker header_offset = 0xffffffff 1917*cda5da8dSAndroid Build Coastguard Worker else: 1918*cda5da8dSAndroid Build Coastguard Worker header_offset = zinfo.header_offset 1919*cda5da8dSAndroid Build Coastguard Worker 1920*cda5da8dSAndroid Build Coastguard Worker extra_data = zinfo.extra 1921*cda5da8dSAndroid Build Coastguard Worker min_version = 0 1922*cda5da8dSAndroid Build Coastguard Worker if extra: 1923*cda5da8dSAndroid Build Coastguard Worker # Append a ZIP64 field to the extra's 1924*cda5da8dSAndroid Build Coastguard Worker extra_data = _strip_extra(extra_data, (1,)) 1925*cda5da8dSAndroid Build Coastguard Worker extra_data = struct.pack( 1926*cda5da8dSAndroid Build Coastguard Worker '<HH' + 'Q'*len(extra), 1927*cda5da8dSAndroid Build Coastguard Worker 1, 8*len(extra), *extra) + extra_data 1928*cda5da8dSAndroid Build Coastguard Worker 1929*cda5da8dSAndroid Build Coastguard Worker min_version = ZIP64_VERSION 1930*cda5da8dSAndroid Build Coastguard Worker 1931*cda5da8dSAndroid Build Coastguard Worker if zinfo.compress_type == ZIP_BZIP2: 1932*cda5da8dSAndroid Build Coastguard Worker min_version = max(BZIP2_VERSION, min_version) 1933*cda5da8dSAndroid Build Coastguard Worker elif zinfo.compress_type == ZIP_LZMA: 1934*cda5da8dSAndroid Build Coastguard Worker min_version = max(LZMA_VERSION, min_version) 1935*cda5da8dSAndroid Build Coastguard Worker 1936*cda5da8dSAndroid Build Coastguard Worker extract_version = max(min_version, zinfo.extract_version) 1937*cda5da8dSAndroid Build Coastguard Worker create_version = max(min_version, zinfo.create_version) 1938*cda5da8dSAndroid Build Coastguard Worker filename, flag_bits = zinfo._encodeFilenameFlags() 1939*cda5da8dSAndroid Build Coastguard Worker centdir = struct.pack(structCentralDir, 1940*cda5da8dSAndroid Build Coastguard Worker stringCentralDir, create_version, 1941*cda5da8dSAndroid Build Coastguard Worker zinfo.create_system, extract_version, zinfo.reserved, 1942*cda5da8dSAndroid Build Coastguard Worker flag_bits, zinfo.compress_type, dostime, dosdate, 1943*cda5da8dSAndroid Build Coastguard Worker zinfo.CRC, compress_size, file_size, 1944*cda5da8dSAndroid Build Coastguard Worker len(filename), len(extra_data), len(zinfo.comment), 1945*cda5da8dSAndroid Build Coastguard Worker 0, zinfo.internal_attr, zinfo.external_attr, 1946*cda5da8dSAndroid Build Coastguard Worker header_offset) 1947*cda5da8dSAndroid Build Coastguard Worker self.fp.write(centdir) 1948*cda5da8dSAndroid Build Coastguard Worker self.fp.write(filename) 1949*cda5da8dSAndroid Build Coastguard Worker self.fp.write(extra_data) 1950*cda5da8dSAndroid Build Coastguard Worker self.fp.write(zinfo.comment) 1951*cda5da8dSAndroid Build Coastguard Worker 1952*cda5da8dSAndroid Build Coastguard Worker pos2 = self.fp.tell() 1953*cda5da8dSAndroid Build Coastguard Worker # Write end-of-zip-archive record 1954*cda5da8dSAndroid Build Coastguard Worker centDirCount = len(self.filelist) 1955*cda5da8dSAndroid Build Coastguard Worker centDirSize = pos2 - self.start_dir 1956*cda5da8dSAndroid Build Coastguard Worker centDirOffset = self.start_dir 1957*cda5da8dSAndroid Build Coastguard Worker requires_zip64 = None 1958*cda5da8dSAndroid Build Coastguard Worker if centDirCount > ZIP_FILECOUNT_LIMIT: 1959*cda5da8dSAndroid Build Coastguard Worker requires_zip64 = "Files count" 1960*cda5da8dSAndroid Build Coastguard Worker elif centDirOffset > ZIP64_LIMIT: 1961*cda5da8dSAndroid Build Coastguard Worker requires_zip64 = "Central directory offset" 1962*cda5da8dSAndroid Build Coastguard Worker elif centDirSize > ZIP64_LIMIT: 1963*cda5da8dSAndroid Build Coastguard Worker requires_zip64 = "Central directory size" 1964*cda5da8dSAndroid Build Coastguard Worker if requires_zip64: 1965*cda5da8dSAndroid Build Coastguard Worker # Need to write the ZIP64 end-of-archive records 1966*cda5da8dSAndroid Build Coastguard Worker if not self._allowZip64: 1967*cda5da8dSAndroid Build Coastguard Worker raise LargeZipFile(requires_zip64 + 1968*cda5da8dSAndroid Build Coastguard Worker " would require ZIP64 extensions") 1969*cda5da8dSAndroid Build Coastguard Worker zip64endrec = struct.pack( 1970*cda5da8dSAndroid Build Coastguard Worker structEndArchive64, stringEndArchive64, 1971*cda5da8dSAndroid Build Coastguard Worker 44, 45, 45, 0, 0, centDirCount, centDirCount, 1972*cda5da8dSAndroid Build Coastguard Worker centDirSize, centDirOffset) 1973*cda5da8dSAndroid Build Coastguard Worker self.fp.write(zip64endrec) 1974*cda5da8dSAndroid Build Coastguard Worker 1975*cda5da8dSAndroid Build Coastguard Worker zip64locrec = struct.pack( 1976*cda5da8dSAndroid Build Coastguard Worker structEndArchive64Locator, 1977*cda5da8dSAndroid Build Coastguard Worker stringEndArchive64Locator, 0, pos2, 1) 1978*cda5da8dSAndroid Build Coastguard Worker self.fp.write(zip64locrec) 1979*cda5da8dSAndroid Build Coastguard Worker centDirCount = min(centDirCount, 0xFFFF) 1980*cda5da8dSAndroid Build Coastguard Worker centDirSize = min(centDirSize, 0xFFFFFFFF) 1981*cda5da8dSAndroid Build Coastguard Worker centDirOffset = min(centDirOffset, 0xFFFFFFFF) 1982*cda5da8dSAndroid Build Coastguard Worker 1983*cda5da8dSAndroid Build Coastguard Worker endrec = struct.pack(structEndArchive, stringEndArchive, 1984*cda5da8dSAndroid Build Coastguard Worker 0, 0, centDirCount, centDirCount, 1985*cda5da8dSAndroid Build Coastguard Worker centDirSize, centDirOffset, len(self._comment)) 1986*cda5da8dSAndroid Build Coastguard Worker self.fp.write(endrec) 1987*cda5da8dSAndroid Build Coastguard Worker self.fp.write(self._comment) 1988*cda5da8dSAndroid Build Coastguard Worker if self.mode == "a": 1989*cda5da8dSAndroid Build Coastguard Worker self.fp.truncate() 1990*cda5da8dSAndroid Build Coastguard Worker self.fp.flush() 1991*cda5da8dSAndroid Build Coastguard Worker 1992*cda5da8dSAndroid Build Coastguard Worker def _fpclose(self, fp): 1993*cda5da8dSAndroid Build Coastguard Worker assert self._fileRefCnt > 0 1994*cda5da8dSAndroid Build Coastguard Worker self._fileRefCnt -= 1 1995*cda5da8dSAndroid Build Coastguard Worker if not self._fileRefCnt and not self._filePassed: 1996*cda5da8dSAndroid Build Coastguard Worker fp.close() 1997*cda5da8dSAndroid Build Coastguard Worker 1998*cda5da8dSAndroid Build Coastguard Worker 1999*cda5da8dSAndroid Build Coastguard Workerclass PyZipFile(ZipFile): 2000*cda5da8dSAndroid Build Coastguard Worker """Class to create ZIP archives with Python library files and packages.""" 2001*cda5da8dSAndroid Build Coastguard Worker 2002*cda5da8dSAndroid Build Coastguard Worker def __init__(self, file, mode="r", compression=ZIP_STORED, 2003*cda5da8dSAndroid Build Coastguard Worker allowZip64=True, optimize=-1): 2004*cda5da8dSAndroid Build Coastguard Worker ZipFile.__init__(self, file, mode=mode, compression=compression, 2005*cda5da8dSAndroid Build Coastguard Worker allowZip64=allowZip64) 2006*cda5da8dSAndroid Build Coastguard Worker self._optimize = optimize 2007*cda5da8dSAndroid Build Coastguard Worker 2008*cda5da8dSAndroid Build Coastguard Worker def writepy(self, pathname, basename="", filterfunc=None): 2009*cda5da8dSAndroid Build Coastguard Worker """Add all files from "pathname" to the ZIP archive. 2010*cda5da8dSAndroid Build Coastguard Worker 2011*cda5da8dSAndroid Build Coastguard Worker If pathname is a package directory, search the directory and 2012*cda5da8dSAndroid Build Coastguard Worker all package subdirectories recursively for all *.py and enter 2013*cda5da8dSAndroid Build Coastguard Worker the modules into the archive. If pathname is a plain 2014*cda5da8dSAndroid Build Coastguard Worker directory, listdir *.py and enter all modules. Else, pathname 2015*cda5da8dSAndroid Build Coastguard Worker must be a Python *.py file and the module will be put into the 2016*cda5da8dSAndroid Build Coastguard Worker archive. Added modules are always module.pyc. 2017*cda5da8dSAndroid Build Coastguard Worker This method will compile the module.py into module.pyc if 2018*cda5da8dSAndroid Build Coastguard Worker necessary. 2019*cda5da8dSAndroid Build Coastguard Worker If filterfunc(pathname) is given, it is called with every argument. 2020*cda5da8dSAndroid Build Coastguard Worker When it is False, the file or directory is skipped. 2021*cda5da8dSAndroid Build Coastguard Worker """ 2022*cda5da8dSAndroid Build Coastguard Worker pathname = os.fspath(pathname) 2023*cda5da8dSAndroid Build Coastguard Worker if filterfunc and not filterfunc(pathname): 2024*cda5da8dSAndroid Build Coastguard Worker if self.debug: 2025*cda5da8dSAndroid Build Coastguard Worker label = 'path' if os.path.isdir(pathname) else 'file' 2026*cda5da8dSAndroid Build Coastguard Worker print('%s %r skipped by filterfunc' % (label, pathname)) 2027*cda5da8dSAndroid Build Coastguard Worker return 2028*cda5da8dSAndroid Build Coastguard Worker dir, name = os.path.split(pathname) 2029*cda5da8dSAndroid Build Coastguard Worker if os.path.isdir(pathname): 2030*cda5da8dSAndroid Build Coastguard Worker initname = os.path.join(pathname, "__init__.py") 2031*cda5da8dSAndroid Build Coastguard Worker if os.path.isfile(initname): 2032*cda5da8dSAndroid Build Coastguard Worker # This is a package directory, add it 2033*cda5da8dSAndroid Build Coastguard Worker if basename: 2034*cda5da8dSAndroid Build Coastguard Worker basename = "%s/%s" % (basename, name) 2035*cda5da8dSAndroid Build Coastguard Worker else: 2036*cda5da8dSAndroid Build Coastguard Worker basename = name 2037*cda5da8dSAndroid Build Coastguard Worker if self.debug: 2038*cda5da8dSAndroid Build Coastguard Worker print("Adding package in", pathname, "as", basename) 2039*cda5da8dSAndroid Build Coastguard Worker fname, arcname = self._get_codename(initname[0:-3], basename) 2040*cda5da8dSAndroid Build Coastguard Worker if self.debug: 2041*cda5da8dSAndroid Build Coastguard Worker print("Adding", arcname) 2042*cda5da8dSAndroid Build Coastguard Worker self.write(fname, arcname) 2043*cda5da8dSAndroid Build Coastguard Worker dirlist = sorted(os.listdir(pathname)) 2044*cda5da8dSAndroid Build Coastguard Worker dirlist.remove("__init__.py") 2045*cda5da8dSAndroid Build Coastguard Worker # Add all *.py files and package subdirectories 2046*cda5da8dSAndroid Build Coastguard Worker for filename in dirlist: 2047*cda5da8dSAndroid Build Coastguard Worker path = os.path.join(pathname, filename) 2048*cda5da8dSAndroid Build Coastguard Worker root, ext = os.path.splitext(filename) 2049*cda5da8dSAndroid Build Coastguard Worker if os.path.isdir(path): 2050*cda5da8dSAndroid Build Coastguard Worker if os.path.isfile(os.path.join(path, "__init__.py")): 2051*cda5da8dSAndroid Build Coastguard Worker # This is a package directory, add it 2052*cda5da8dSAndroid Build Coastguard Worker self.writepy(path, basename, 2053*cda5da8dSAndroid Build Coastguard Worker filterfunc=filterfunc) # Recursive call 2054*cda5da8dSAndroid Build Coastguard Worker elif ext == ".py": 2055*cda5da8dSAndroid Build Coastguard Worker if filterfunc and not filterfunc(path): 2056*cda5da8dSAndroid Build Coastguard Worker if self.debug: 2057*cda5da8dSAndroid Build Coastguard Worker print('file %r skipped by filterfunc' % path) 2058*cda5da8dSAndroid Build Coastguard Worker continue 2059*cda5da8dSAndroid Build Coastguard Worker fname, arcname = self._get_codename(path[0:-3], 2060*cda5da8dSAndroid Build Coastguard Worker basename) 2061*cda5da8dSAndroid Build Coastguard Worker if self.debug: 2062*cda5da8dSAndroid Build Coastguard Worker print("Adding", arcname) 2063*cda5da8dSAndroid Build Coastguard Worker self.write(fname, arcname) 2064*cda5da8dSAndroid Build Coastguard Worker else: 2065*cda5da8dSAndroid Build Coastguard Worker # This is NOT a package directory, add its files at top level 2066*cda5da8dSAndroid Build Coastguard Worker if self.debug: 2067*cda5da8dSAndroid Build Coastguard Worker print("Adding files from directory", pathname) 2068*cda5da8dSAndroid Build Coastguard Worker for filename in sorted(os.listdir(pathname)): 2069*cda5da8dSAndroid Build Coastguard Worker path = os.path.join(pathname, filename) 2070*cda5da8dSAndroid Build Coastguard Worker root, ext = os.path.splitext(filename) 2071*cda5da8dSAndroid Build Coastguard Worker if ext == ".py": 2072*cda5da8dSAndroid Build Coastguard Worker if filterfunc and not filterfunc(path): 2073*cda5da8dSAndroid Build Coastguard Worker if self.debug: 2074*cda5da8dSAndroid Build Coastguard Worker print('file %r skipped by filterfunc' % path) 2075*cda5da8dSAndroid Build Coastguard Worker continue 2076*cda5da8dSAndroid Build Coastguard Worker fname, arcname = self._get_codename(path[0:-3], 2077*cda5da8dSAndroid Build Coastguard Worker basename) 2078*cda5da8dSAndroid Build Coastguard Worker if self.debug: 2079*cda5da8dSAndroid Build Coastguard Worker print("Adding", arcname) 2080*cda5da8dSAndroid Build Coastguard Worker self.write(fname, arcname) 2081*cda5da8dSAndroid Build Coastguard Worker else: 2082*cda5da8dSAndroid Build Coastguard Worker if pathname[-3:] != ".py": 2083*cda5da8dSAndroid Build Coastguard Worker raise RuntimeError( 2084*cda5da8dSAndroid Build Coastguard Worker 'Files added with writepy() must end with ".py"') 2085*cda5da8dSAndroid Build Coastguard Worker fname, arcname = self._get_codename(pathname[0:-3], basename) 2086*cda5da8dSAndroid Build Coastguard Worker if self.debug: 2087*cda5da8dSAndroid Build Coastguard Worker print("Adding file", arcname) 2088*cda5da8dSAndroid Build Coastguard Worker self.write(fname, arcname) 2089*cda5da8dSAndroid Build Coastguard Worker 2090*cda5da8dSAndroid Build Coastguard Worker def _get_codename(self, pathname, basename): 2091*cda5da8dSAndroid Build Coastguard Worker """Return (filename, archivename) for the path. 2092*cda5da8dSAndroid Build Coastguard Worker 2093*cda5da8dSAndroid Build Coastguard Worker Given a module name path, return the correct file path and 2094*cda5da8dSAndroid Build Coastguard Worker archive name, compiling if necessary. For example, given 2095*cda5da8dSAndroid Build Coastguard Worker /python/lib/string, return (/python/lib/string.pyc, string). 2096*cda5da8dSAndroid Build Coastguard Worker """ 2097*cda5da8dSAndroid Build Coastguard Worker def _compile(file, optimize=-1): 2098*cda5da8dSAndroid Build Coastguard Worker import py_compile 2099*cda5da8dSAndroid Build Coastguard Worker if self.debug: 2100*cda5da8dSAndroid Build Coastguard Worker print("Compiling", file) 2101*cda5da8dSAndroid Build Coastguard Worker try: 2102*cda5da8dSAndroid Build Coastguard Worker py_compile.compile(file, doraise=True, optimize=optimize) 2103*cda5da8dSAndroid Build Coastguard Worker except py_compile.PyCompileError as err: 2104*cda5da8dSAndroid Build Coastguard Worker print(err.msg) 2105*cda5da8dSAndroid Build Coastguard Worker return False 2106*cda5da8dSAndroid Build Coastguard Worker return True 2107*cda5da8dSAndroid Build Coastguard Worker 2108*cda5da8dSAndroid Build Coastguard Worker file_py = pathname + ".py" 2109*cda5da8dSAndroid Build Coastguard Worker file_pyc = pathname + ".pyc" 2110*cda5da8dSAndroid Build Coastguard Worker pycache_opt0 = importlib.util.cache_from_source(file_py, optimization='') 2111*cda5da8dSAndroid Build Coastguard Worker pycache_opt1 = importlib.util.cache_from_source(file_py, optimization=1) 2112*cda5da8dSAndroid Build Coastguard Worker pycache_opt2 = importlib.util.cache_from_source(file_py, optimization=2) 2113*cda5da8dSAndroid Build Coastguard Worker if self._optimize == -1: 2114*cda5da8dSAndroid Build Coastguard Worker # legacy mode: use whatever file is present 2115*cda5da8dSAndroid Build Coastguard Worker if (os.path.isfile(file_pyc) and 2116*cda5da8dSAndroid Build Coastguard Worker os.stat(file_pyc).st_mtime >= os.stat(file_py).st_mtime): 2117*cda5da8dSAndroid Build Coastguard Worker # Use .pyc file. 2118*cda5da8dSAndroid Build Coastguard Worker arcname = fname = file_pyc 2119*cda5da8dSAndroid Build Coastguard Worker elif (os.path.isfile(pycache_opt0) and 2120*cda5da8dSAndroid Build Coastguard Worker os.stat(pycache_opt0).st_mtime >= os.stat(file_py).st_mtime): 2121*cda5da8dSAndroid Build Coastguard Worker # Use the __pycache__/*.pyc file, but write it to the legacy pyc 2122*cda5da8dSAndroid Build Coastguard Worker # file name in the archive. 2123*cda5da8dSAndroid Build Coastguard Worker fname = pycache_opt0 2124*cda5da8dSAndroid Build Coastguard Worker arcname = file_pyc 2125*cda5da8dSAndroid Build Coastguard Worker elif (os.path.isfile(pycache_opt1) and 2126*cda5da8dSAndroid Build Coastguard Worker os.stat(pycache_opt1).st_mtime >= os.stat(file_py).st_mtime): 2127*cda5da8dSAndroid Build Coastguard Worker # Use the __pycache__/*.pyc file, but write it to the legacy pyc 2128*cda5da8dSAndroid Build Coastguard Worker # file name in the archive. 2129*cda5da8dSAndroid Build Coastguard Worker fname = pycache_opt1 2130*cda5da8dSAndroid Build Coastguard Worker arcname = file_pyc 2131*cda5da8dSAndroid Build Coastguard Worker elif (os.path.isfile(pycache_opt2) and 2132*cda5da8dSAndroid Build Coastguard Worker os.stat(pycache_opt2).st_mtime >= os.stat(file_py).st_mtime): 2133*cda5da8dSAndroid Build Coastguard Worker # Use the __pycache__/*.pyc file, but write it to the legacy pyc 2134*cda5da8dSAndroid Build Coastguard Worker # file name in the archive. 2135*cda5da8dSAndroid Build Coastguard Worker fname = pycache_opt2 2136*cda5da8dSAndroid Build Coastguard Worker arcname = file_pyc 2137*cda5da8dSAndroid Build Coastguard Worker else: 2138*cda5da8dSAndroid Build Coastguard Worker # Compile py into PEP 3147 pyc file. 2139*cda5da8dSAndroid Build Coastguard Worker if _compile(file_py): 2140*cda5da8dSAndroid Build Coastguard Worker if sys.flags.optimize == 0: 2141*cda5da8dSAndroid Build Coastguard Worker fname = pycache_opt0 2142*cda5da8dSAndroid Build Coastguard Worker elif sys.flags.optimize == 1: 2143*cda5da8dSAndroid Build Coastguard Worker fname = pycache_opt1 2144*cda5da8dSAndroid Build Coastguard Worker else: 2145*cda5da8dSAndroid Build Coastguard Worker fname = pycache_opt2 2146*cda5da8dSAndroid Build Coastguard Worker arcname = file_pyc 2147*cda5da8dSAndroid Build Coastguard Worker else: 2148*cda5da8dSAndroid Build Coastguard Worker fname = arcname = file_py 2149*cda5da8dSAndroid Build Coastguard Worker else: 2150*cda5da8dSAndroid Build Coastguard Worker # new mode: use given optimization level 2151*cda5da8dSAndroid Build Coastguard Worker if self._optimize == 0: 2152*cda5da8dSAndroid Build Coastguard Worker fname = pycache_opt0 2153*cda5da8dSAndroid Build Coastguard Worker arcname = file_pyc 2154*cda5da8dSAndroid Build Coastguard Worker else: 2155*cda5da8dSAndroid Build Coastguard Worker arcname = file_pyc 2156*cda5da8dSAndroid Build Coastguard Worker if self._optimize == 1: 2157*cda5da8dSAndroid Build Coastguard Worker fname = pycache_opt1 2158*cda5da8dSAndroid Build Coastguard Worker elif self._optimize == 2: 2159*cda5da8dSAndroid Build Coastguard Worker fname = pycache_opt2 2160*cda5da8dSAndroid Build Coastguard Worker else: 2161*cda5da8dSAndroid Build Coastguard Worker msg = "invalid value for 'optimize': {!r}".format(self._optimize) 2162*cda5da8dSAndroid Build Coastguard Worker raise ValueError(msg) 2163*cda5da8dSAndroid Build Coastguard Worker if not (os.path.isfile(fname) and 2164*cda5da8dSAndroid Build Coastguard Worker os.stat(fname).st_mtime >= os.stat(file_py).st_mtime): 2165*cda5da8dSAndroid Build Coastguard Worker if not _compile(file_py, optimize=self._optimize): 2166*cda5da8dSAndroid Build Coastguard Worker fname = arcname = file_py 2167*cda5da8dSAndroid Build Coastguard Worker archivename = os.path.split(arcname)[1] 2168*cda5da8dSAndroid Build Coastguard Worker if basename: 2169*cda5da8dSAndroid Build Coastguard Worker archivename = "%s/%s" % (basename, archivename) 2170*cda5da8dSAndroid Build Coastguard Worker return (fname, archivename) 2171*cda5da8dSAndroid Build Coastguard Worker 2172*cda5da8dSAndroid Build Coastguard Worker 2173*cda5da8dSAndroid Build Coastguard Workerdef _parents(path): 2174*cda5da8dSAndroid Build Coastguard Worker """ 2175*cda5da8dSAndroid Build Coastguard Worker Given a path with elements separated by 2176*cda5da8dSAndroid Build Coastguard Worker posixpath.sep, generate all parents of that path. 2177*cda5da8dSAndroid Build Coastguard Worker 2178*cda5da8dSAndroid Build Coastguard Worker >>> list(_parents('b/d')) 2179*cda5da8dSAndroid Build Coastguard Worker ['b'] 2180*cda5da8dSAndroid Build Coastguard Worker >>> list(_parents('/b/d/')) 2181*cda5da8dSAndroid Build Coastguard Worker ['/b'] 2182*cda5da8dSAndroid Build Coastguard Worker >>> list(_parents('b/d/f/')) 2183*cda5da8dSAndroid Build Coastguard Worker ['b/d', 'b'] 2184*cda5da8dSAndroid Build Coastguard Worker >>> list(_parents('b')) 2185*cda5da8dSAndroid Build Coastguard Worker [] 2186*cda5da8dSAndroid Build Coastguard Worker >>> list(_parents('')) 2187*cda5da8dSAndroid Build Coastguard Worker [] 2188*cda5da8dSAndroid Build Coastguard Worker """ 2189*cda5da8dSAndroid Build Coastguard Worker return itertools.islice(_ancestry(path), 1, None) 2190*cda5da8dSAndroid Build Coastguard Worker 2191*cda5da8dSAndroid Build Coastguard Worker 2192*cda5da8dSAndroid Build Coastguard Workerdef _ancestry(path): 2193*cda5da8dSAndroid Build Coastguard Worker """ 2194*cda5da8dSAndroid Build Coastguard Worker Given a path with elements separated by 2195*cda5da8dSAndroid Build Coastguard Worker posixpath.sep, generate all elements of that path 2196*cda5da8dSAndroid Build Coastguard Worker 2197*cda5da8dSAndroid Build Coastguard Worker >>> list(_ancestry('b/d')) 2198*cda5da8dSAndroid Build Coastguard Worker ['b/d', 'b'] 2199*cda5da8dSAndroid Build Coastguard Worker >>> list(_ancestry('/b/d/')) 2200*cda5da8dSAndroid Build Coastguard Worker ['/b/d', '/b'] 2201*cda5da8dSAndroid Build Coastguard Worker >>> list(_ancestry('b/d/f/')) 2202*cda5da8dSAndroid Build Coastguard Worker ['b/d/f', 'b/d', 'b'] 2203*cda5da8dSAndroid Build Coastguard Worker >>> list(_ancestry('b')) 2204*cda5da8dSAndroid Build Coastguard Worker ['b'] 2205*cda5da8dSAndroid Build Coastguard Worker >>> list(_ancestry('')) 2206*cda5da8dSAndroid Build Coastguard Worker [] 2207*cda5da8dSAndroid Build Coastguard Worker """ 2208*cda5da8dSAndroid Build Coastguard Worker path = path.rstrip(posixpath.sep) 2209*cda5da8dSAndroid Build Coastguard Worker while path and path != posixpath.sep: 2210*cda5da8dSAndroid Build Coastguard Worker yield path 2211*cda5da8dSAndroid Build Coastguard Worker path, tail = posixpath.split(path) 2212*cda5da8dSAndroid Build Coastguard Worker 2213*cda5da8dSAndroid Build Coastguard Worker 2214*cda5da8dSAndroid Build Coastguard Worker_dedupe = dict.fromkeys 2215*cda5da8dSAndroid Build Coastguard Worker"""Deduplicate an iterable in original order""" 2216*cda5da8dSAndroid Build Coastguard Worker 2217*cda5da8dSAndroid Build Coastguard Worker 2218*cda5da8dSAndroid Build Coastguard Workerdef _difference(minuend, subtrahend): 2219*cda5da8dSAndroid Build Coastguard Worker """ 2220*cda5da8dSAndroid Build Coastguard Worker Return items in minuend not in subtrahend, retaining order 2221*cda5da8dSAndroid Build Coastguard Worker with O(1) lookup. 2222*cda5da8dSAndroid Build Coastguard Worker """ 2223*cda5da8dSAndroid Build Coastguard Worker return itertools.filterfalse(set(subtrahend).__contains__, minuend) 2224*cda5da8dSAndroid Build Coastguard Worker 2225*cda5da8dSAndroid Build Coastguard Worker 2226*cda5da8dSAndroid Build Coastguard Workerclass CompleteDirs(ZipFile): 2227*cda5da8dSAndroid Build Coastguard Worker """ 2228*cda5da8dSAndroid Build Coastguard Worker A ZipFile subclass that ensures that implied directories 2229*cda5da8dSAndroid Build Coastguard Worker are always included in the namelist. 2230*cda5da8dSAndroid Build Coastguard Worker """ 2231*cda5da8dSAndroid Build Coastguard Worker 2232*cda5da8dSAndroid Build Coastguard Worker @staticmethod 2233*cda5da8dSAndroid Build Coastguard Worker def _implied_dirs(names): 2234*cda5da8dSAndroid Build Coastguard Worker parents = itertools.chain.from_iterable(map(_parents, names)) 2235*cda5da8dSAndroid Build Coastguard Worker as_dirs = (p + posixpath.sep for p in parents) 2236*cda5da8dSAndroid Build Coastguard Worker return _dedupe(_difference(as_dirs, names)) 2237*cda5da8dSAndroid Build Coastguard Worker 2238*cda5da8dSAndroid Build Coastguard Worker def namelist(self): 2239*cda5da8dSAndroid Build Coastguard Worker names = super(CompleteDirs, self).namelist() 2240*cda5da8dSAndroid Build Coastguard Worker return names + list(self._implied_dirs(names)) 2241*cda5da8dSAndroid Build Coastguard Worker 2242*cda5da8dSAndroid Build Coastguard Worker def _name_set(self): 2243*cda5da8dSAndroid Build Coastguard Worker return set(self.namelist()) 2244*cda5da8dSAndroid Build Coastguard Worker 2245*cda5da8dSAndroid Build Coastguard Worker def resolve_dir(self, name): 2246*cda5da8dSAndroid Build Coastguard Worker """ 2247*cda5da8dSAndroid Build Coastguard Worker If the name represents a directory, return that name 2248*cda5da8dSAndroid Build Coastguard Worker as a directory (with the trailing slash). 2249*cda5da8dSAndroid Build Coastguard Worker """ 2250*cda5da8dSAndroid Build Coastguard Worker names = self._name_set() 2251*cda5da8dSAndroid Build Coastguard Worker dirname = name + '/' 2252*cda5da8dSAndroid Build Coastguard Worker dir_match = name not in names and dirname in names 2253*cda5da8dSAndroid Build Coastguard Worker return dirname if dir_match else name 2254*cda5da8dSAndroid Build Coastguard Worker 2255*cda5da8dSAndroid Build Coastguard Worker def getinfo(self, name): 2256*cda5da8dSAndroid Build Coastguard Worker """ 2257*cda5da8dSAndroid Build Coastguard Worker Supplement getinfo for implied dirs. 2258*cda5da8dSAndroid Build Coastguard Worker """ 2259*cda5da8dSAndroid Build Coastguard Worker try: 2260*cda5da8dSAndroid Build Coastguard Worker return super().getinfo(name) 2261*cda5da8dSAndroid Build Coastguard Worker except KeyError: 2262*cda5da8dSAndroid Build Coastguard Worker if not name.endswith('/') or name not in self._name_set(): 2263*cda5da8dSAndroid Build Coastguard Worker raise 2264*cda5da8dSAndroid Build Coastguard Worker return ZipInfo(filename=name) 2265*cda5da8dSAndroid Build Coastguard Worker 2266*cda5da8dSAndroid Build Coastguard Worker @classmethod 2267*cda5da8dSAndroid Build Coastguard Worker def make(cls, source): 2268*cda5da8dSAndroid Build Coastguard Worker """ 2269*cda5da8dSAndroid Build Coastguard Worker Given a source (filename or zipfile), return an 2270*cda5da8dSAndroid Build Coastguard Worker appropriate CompleteDirs subclass. 2271*cda5da8dSAndroid Build Coastguard Worker """ 2272*cda5da8dSAndroid Build Coastguard Worker if isinstance(source, CompleteDirs): 2273*cda5da8dSAndroid Build Coastguard Worker return source 2274*cda5da8dSAndroid Build Coastguard Worker 2275*cda5da8dSAndroid Build Coastguard Worker if not isinstance(source, ZipFile): 2276*cda5da8dSAndroid Build Coastguard Worker return cls(source) 2277*cda5da8dSAndroid Build Coastguard Worker 2278*cda5da8dSAndroid Build Coastguard Worker # Only allow for FastLookup when supplied zipfile is read-only 2279*cda5da8dSAndroid Build Coastguard Worker if 'r' not in source.mode: 2280*cda5da8dSAndroid Build Coastguard Worker cls = CompleteDirs 2281*cda5da8dSAndroid Build Coastguard Worker 2282*cda5da8dSAndroid Build Coastguard Worker source.__class__ = cls 2283*cda5da8dSAndroid Build Coastguard Worker return source 2284*cda5da8dSAndroid Build Coastguard Worker 2285*cda5da8dSAndroid Build Coastguard Worker 2286*cda5da8dSAndroid Build Coastguard Workerclass FastLookup(CompleteDirs): 2287*cda5da8dSAndroid Build Coastguard Worker """ 2288*cda5da8dSAndroid Build Coastguard Worker ZipFile subclass to ensure implicit 2289*cda5da8dSAndroid Build Coastguard Worker dirs exist and are resolved rapidly. 2290*cda5da8dSAndroid Build Coastguard Worker """ 2291*cda5da8dSAndroid Build Coastguard Worker 2292*cda5da8dSAndroid Build Coastguard Worker def namelist(self): 2293*cda5da8dSAndroid Build Coastguard Worker with contextlib.suppress(AttributeError): 2294*cda5da8dSAndroid Build Coastguard Worker return self.__names 2295*cda5da8dSAndroid Build Coastguard Worker self.__names = super(FastLookup, self).namelist() 2296*cda5da8dSAndroid Build Coastguard Worker return self.__names 2297*cda5da8dSAndroid Build Coastguard Worker 2298*cda5da8dSAndroid Build Coastguard Worker def _name_set(self): 2299*cda5da8dSAndroid Build Coastguard Worker with contextlib.suppress(AttributeError): 2300*cda5da8dSAndroid Build Coastguard Worker return self.__lookup 2301*cda5da8dSAndroid Build Coastguard Worker self.__lookup = super(FastLookup, self)._name_set() 2302*cda5da8dSAndroid Build Coastguard Worker return self.__lookup 2303*cda5da8dSAndroid Build Coastguard Worker 2304*cda5da8dSAndroid Build Coastguard Worker 2305*cda5da8dSAndroid Build Coastguard Workerdef _extract_text_encoding(encoding=None, *args, **kwargs): 2306*cda5da8dSAndroid Build Coastguard Worker # stacklevel=3 so that the caller of the caller see any warning. 2307*cda5da8dSAndroid Build Coastguard Worker return io.text_encoding(encoding, 3), args, kwargs 2308*cda5da8dSAndroid Build Coastguard Worker 2309*cda5da8dSAndroid Build Coastguard Worker 2310*cda5da8dSAndroid Build Coastguard Workerclass Path: 2311*cda5da8dSAndroid Build Coastguard Worker """ 2312*cda5da8dSAndroid Build Coastguard Worker A pathlib-compatible interface for zip files. 2313*cda5da8dSAndroid Build Coastguard Worker 2314*cda5da8dSAndroid Build Coastguard Worker Consider a zip file with this structure:: 2315*cda5da8dSAndroid Build Coastguard Worker 2316*cda5da8dSAndroid Build Coastguard Worker . 2317*cda5da8dSAndroid Build Coastguard Worker ├── a.txt 2318*cda5da8dSAndroid Build Coastguard Worker └── b 2319*cda5da8dSAndroid Build Coastguard Worker ├── c.txt 2320*cda5da8dSAndroid Build Coastguard Worker └── d 2321*cda5da8dSAndroid Build Coastguard Worker └── e.txt 2322*cda5da8dSAndroid Build Coastguard Worker 2323*cda5da8dSAndroid Build Coastguard Worker >>> data = io.BytesIO() 2324*cda5da8dSAndroid Build Coastguard Worker >>> zf = ZipFile(data, 'w') 2325*cda5da8dSAndroid Build Coastguard Worker >>> zf.writestr('a.txt', 'content of a') 2326*cda5da8dSAndroid Build Coastguard Worker >>> zf.writestr('b/c.txt', 'content of c') 2327*cda5da8dSAndroid Build Coastguard Worker >>> zf.writestr('b/d/e.txt', 'content of e') 2328*cda5da8dSAndroid Build Coastguard Worker >>> zf.filename = 'mem/abcde.zip' 2329*cda5da8dSAndroid Build Coastguard Worker 2330*cda5da8dSAndroid Build Coastguard Worker Path accepts the zipfile object itself or a filename 2331*cda5da8dSAndroid Build Coastguard Worker 2332*cda5da8dSAndroid Build Coastguard Worker >>> root = Path(zf) 2333*cda5da8dSAndroid Build Coastguard Worker 2334*cda5da8dSAndroid Build Coastguard Worker From there, several path operations are available. 2335*cda5da8dSAndroid Build Coastguard Worker 2336*cda5da8dSAndroid Build Coastguard Worker Directory iteration (including the zip file itself): 2337*cda5da8dSAndroid Build Coastguard Worker 2338*cda5da8dSAndroid Build Coastguard Worker >>> a, b = root.iterdir() 2339*cda5da8dSAndroid Build Coastguard Worker >>> a 2340*cda5da8dSAndroid Build Coastguard Worker Path('mem/abcde.zip', 'a.txt') 2341*cda5da8dSAndroid Build Coastguard Worker >>> b 2342*cda5da8dSAndroid Build Coastguard Worker Path('mem/abcde.zip', 'b/') 2343*cda5da8dSAndroid Build Coastguard Worker 2344*cda5da8dSAndroid Build Coastguard Worker name property: 2345*cda5da8dSAndroid Build Coastguard Worker 2346*cda5da8dSAndroid Build Coastguard Worker >>> b.name 2347*cda5da8dSAndroid Build Coastguard Worker 'b' 2348*cda5da8dSAndroid Build Coastguard Worker 2349*cda5da8dSAndroid Build Coastguard Worker join with divide operator: 2350*cda5da8dSAndroid Build Coastguard Worker 2351*cda5da8dSAndroid Build Coastguard Worker >>> c = b / 'c.txt' 2352*cda5da8dSAndroid Build Coastguard Worker >>> c 2353*cda5da8dSAndroid Build Coastguard Worker Path('mem/abcde.zip', 'b/c.txt') 2354*cda5da8dSAndroid Build Coastguard Worker >>> c.name 2355*cda5da8dSAndroid Build Coastguard Worker 'c.txt' 2356*cda5da8dSAndroid Build Coastguard Worker 2357*cda5da8dSAndroid Build Coastguard Worker Read text: 2358*cda5da8dSAndroid Build Coastguard Worker 2359*cda5da8dSAndroid Build Coastguard Worker >>> c.read_text() 2360*cda5da8dSAndroid Build Coastguard Worker 'content of c' 2361*cda5da8dSAndroid Build Coastguard Worker 2362*cda5da8dSAndroid Build Coastguard Worker existence: 2363*cda5da8dSAndroid Build Coastguard Worker 2364*cda5da8dSAndroid Build Coastguard Worker >>> c.exists() 2365*cda5da8dSAndroid Build Coastguard Worker True 2366*cda5da8dSAndroid Build Coastguard Worker >>> (b / 'missing.txt').exists() 2367*cda5da8dSAndroid Build Coastguard Worker False 2368*cda5da8dSAndroid Build Coastguard Worker 2369*cda5da8dSAndroid Build Coastguard Worker Coercion to string: 2370*cda5da8dSAndroid Build Coastguard Worker 2371*cda5da8dSAndroid Build Coastguard Worker >>> import os 2372*cda5da8dSAndroid Build Coastguard Worker >>> str(c).replace(os.sep, posixpath.sep) 2373*cda5da8dSAndroid Build Coastguard Worker 'mem/abcde.zip/b/c.txt' 2374*cda5da8dSAndroid Build Coastguard Worker 2375*cda5da8dSAndroid Build Coastguard Worker At the root, ``name``, ``filename``, and ``parent`` 2376*cda5da8dSAndroid Build Coastguard Worker resolve to the zipfile. Note these attributes are not 2377*cda5da8dSAndroid Build Coastguard Worker valid and will raise a ``ValueError`` if the zipfile 2378*cda5da8dSAndroid Build Coastguard Worker has no filename. 2379*cda5da8dSAndroid Build Coastguard Worker 2380*cda5da8dSAndroid Build Coastguard Worker >>> root.name 2381*cda5da8dSAndroid Build Coastguard Worker 'abcde.zip' 2382*cda5da8dSAndroid Build Coastguard Worker >>> str(root.filename).replace(os.sep, posixpath.sep) 2383*cda5da8dSAndroid Build Coastguard Worker 'mem/abcde.zip' 2384*cda5da8dSAndroid Build Coastguard Worker >>> str(root.parent) 2385*cda5da8dSAndroid Build Coastguard Worker 'mem' 2386*cda5da8dSAndroid Build Coastguard Worker """ 2387*cda5da8dSAndroid Build Coastguard Worker 2388*cda5da8dSAndroid Build Coastguard Worker __repr = "{self.__class__.__name__}({self.root.filename!r}, {self.at!r})" 2389*cda5da8dSAndroid Build Coastguard Worker 2390*cda5da8dSAndroid Build Coastguard Worker def __init__(self, root, at=""): 2391*cda5da8dSAndroid Build Coastguard Worker """ 2392*cda5da8dSAndroid Build Coastguard Worker Construct a Path from a ZipFile or filename. 2393*cda5da8dSAndroid Build Coastguard Worker 2394*cda5da8dSAndroid Build Coastguard Worker Note: When the source is an existing ZipFile object, 2395*cda5da8dSAndroid Build Coastguard Worker its type (__class__) will be mutated to a 2396*cda5da8dSAndroid Build Coastguard Worker specialized type. If the caller wishes to retain the 2397*cda5da8dSAndroid Build Coastguard Worker original type, the caller should either create a 2398*cda5da8dSAndroid Build Coastguard Worker separate ZipFile object or pass a filename. 2399*cda5da8dSAndroid Build Coastguard Worker """ 2400*cda5da8dSAndroid Build Coastguard Worker self.root = FastLookup.make(root) 2401*cda5da8dSAndroid Build Coastguard Worker self.at = at 2402*cda5da8dSAndroid Build Coastguard Worker 2403*cda5da8dSAndroid Build Coastguard Worker def open(self, mode='r', *args, pwd=None, **kwargs): 2404*cda5da8dSAndroid Build Coastguard Worker """ 2405*cda5da8dSAndroid Build Coastguard Worker Open this entry as text or binary following the semantics 2406*cda5da8dSAndroid Build Coastguard Worker of ``pathlib.Path.open()`` by passing arguments through 2407*cda5da8dSAndroid Build Coastguard Worker to io.TextIOWrapper(). 2408*cda5da8dSAndroid Build Coastguard Worker """ 2409*cda5da8dSAndroid Build Coastguard Worker if self.is_dir(): 2410*cda5da8dSAndroid Build Coastguard Worker raise IsADirectoryError(self) 2411*cda5da8dSAndroid Build Coastguard Worker zip_mode = mode[0] 2412*cda5da8dSAndroid Build Coastguard Worker if not self.exists() and zip_mode == 'r': 2413*cda5da8dSAndroid Build Coastguard Worker raise FileNotFoundError(self) 2414*cda5da8dSAndroid Build Coastguard Worker stream = self.root.open(self.at, zip_mode, pwd=pwd) 2415*cda5da8dSAndroid Build Coastguard Worker if 'b' in mode: 2416*cda5da8dSAndroid Build Coastguard Worker if args or kwargs: 2417*cda5da8dSAndroid Build Coastguard Worker raise ValueError("encoding args invalid for binary operation") 2418*cda5da8dSAndroid Build Coastguard Worker return stream 2419*cda5da8dSAndroid Build Coastguard Worker # Text mode: 2420*cda5da8dSAndroid Build Coastguard Worker encoding, args, kwargs = _extract_text_encoding(*args, **kwargs) 2421*cda5da8dSAndroid Build Coastguard Worker return io.TextIOWrapper(stream, encoding, *args, **kwargs) 2422*cda5da8dSAndroid Build Coastguard Worker 2423*cda5da8dSAndroid Build Coastguard Worker @property 2424*cda5da8dSAndroid Build Coastguard Worker def name(self): 2425*cda5da8dSAndroid Build Coastguard Worker return pathlib.Path(self.at).name or self.filename.name 2426*cda5da8dSAndroid Build Coastguard Worker 2427*cda5da8dSAndroid Build Coastguard Worker @property 2428*cda5da8dSAndroid Build Coastguard Worker def suffix(self): 2429*cda5da8dSAndroid Build Coastguard Worker return pathlib.Path(self.at).suffix or self.filename.suffix 2430*cda5da8dSAndroid Build Coastguard Worker 2431*cda5da8dSAndroid Build Coastguard Worker @property 2432*cda5da8dSAndroid Build Coastguard Worker def suffixes(self): 2433*cda5da8dSAndroid Build Coastguard Worker return pathlib.Path(self.at).suffixes or self.filename.suffixes 2434*cda5da8dSAndroid Build Coastguard Worker 2435*cda5da8dSAndroid Build Coastguard Worker @property 2436*cda5da8dSAndroid Build Coastguard Worker def stem(self): 2437*cda5da8dSAndroid Build Coastguard Worker return pathlib.Path(self.at).stem or self.filename.stem 2438*cda5da8dSAndroid Build Coastguard Worker 2439*cda5da8dSAndroid Build Coastguard Worker @property 2440*cda5da8dSAndroid Build Coastguard Worker def filename(self): 2441*cda5da8dSAndroid Build Coastguard Worker return pathlib.Path(self.root.filename).joinpath(self.at) 2442*cda5da8dSAndroid Build Coastguard Worker 2443*cda5da8dSAndroid Build Coastguard Worker def read_text(self, *args, **kwargs): 2444*cda5da8dSAndroid Build Coastguard Worker encoding, args, kwargs = _extract_text_encoding(*args, **kwargs) 2445*cda5da8dSAndroid Build Coastguard Worker with self.open('r', encoding, *args, **kwargs) as strm: 2446*cda5da8dSAndroid Build Coastguard Worker return strm.read() 2447*cda5da8dSAndroid Build Coastguard Worker 2448*cda5da8dSAndroid Build Coastguard Worker def read_bytes(self): 2449*cda5da8dSAndroid Build Coastguard Worker with self.open('rb') as strm: 2450*cda5da8dSAndroid Build Coastguard Worker return strm.read() 2451*cda5da8dSAndroid Build Coastguard Worker 2452*cda5da8dSAndroid Build Coastguard Worker def _is_child(self, path): 2453*cda5da8dSAndroid Build Coastguard Worker return posixpath.dirname(path.at.rstrip("/")) == self.at.rstrip("/") 2454*cda5da8dSAndroid Build Coastguard Worker 2455*cda5da8dSAndroid Build Coastguard Worker def _next(self, at): 2456*cda5da8dSAndroid Build Coastguard Worker return self.__class__(self.root, at) 2457*cda5da8dSAndroid Build Coastguard Worker 2458*cda5da8dSAndroid Build Coastguard Worker def is_dir(self): 2459*cda5da8dSAndroid Build Coastguard Worker return not self.at or self.at.endswith("/") 2460*cda5da8dSAndroid Build Coastguard Worker 2461*cda5da8dSAndroid Build Coastguard Worker def is_file(self): 2462*cda5da8dSAndroid Build Coastguard Worker return self.exists() and not self.is_dir() 2463*cda5da8dSAndroid Build Coastguard Worker 2464*cda5da8dSAndroid Build Coastguard Worker def exists(self): 2465*cda5da8dSAndroid Build Coastguard Worker return self.at in self.root._name_set() 2466*cda5da8dSAndroid Build Coastguard Worker 2467*cda5da8dSAndroid Build Coastguard Worker def iterdir(self): 2468*cda5da8dSAndroid Build Coastguard Worker if not self.is_dir(): 2469*cda5da8dSAndroid Build Coastguard Worker raise ValueError("Can't listdir a file") 2470*cda5da8dSAndroid Build Coastguard Worker subs = map(self._next, self.root.namelist()) 2471*cda5da8dSAndroid Build Coastguard Worker return filter(self._is_child, subs) 2472*cda5da8dSAndroid Build Coastguard Worker 2473*cda5da8dSAndroid Build Coastguard Worker def __str__(self): 2474*cda5da8dSAndroid Build Coastguard Worker return posixpath.join(self.root.filename, self.at) 2475*cda5da8dSAndroid Build Coastguard Worker 2476*cda5da8dSAndroid Build Coastguard Worker def __repr__(self): 2477*cda5da8dSAndroid Build Coastguard Worker return self.__repr.format(self=self) 2478*cda5da8dSAndroid Build Coastguard Worker 2479*cda5da8dSAndroid Build Coastguard Worker def joinpath(self, *other): 2480*cda5da8dSAndroid Build Coastguard Worker next = posixpath.join(self.at, *other) 2481*cda5da8dSAndroid Build Coastguard Worker return self._next(self.root.resolve_dir(next)) 2482*cda5da8dSAndroid Build Coastguard Worker 2483*cda5da8dSAndroid Build Coastguard Worker __truediv__ = joinpath 2484*cda5da8dSAndroid Build Coastguard Worker 2485*cda5da8dSAndroid Build Coastguard Worker @property 2486*cda5da8dSAndroid Build Coastguard Worker def parent(self): 2487*cda5da8dSAndroid Build Coastguard Worker if not self.at: 2488*cda5da8dSAndroid Build Coastguard Worker return self.filename.parent 2489*cda5da8dSAndroid Build Coastguard Worker parent_at = posixpath.dirname(self.at.rstrip('/')) 2490*cda5da8dSAndroid Build Coastguard Worker if parent_at: 2491*cda5da8dSAndroid Build Coastguard Worker parent_at += '/' 2492*cda5da8dSAndroid Build Coastguard Worker return self._next(parent_at) 2493*cda5da8dSAndroid Build Coastguard Worker 2494*cda5da8dSAndroid Build Coastguard Worker 2495*cda5da8dSAndroid Build Coastguard Workerdef main(args=None): 2496*cda5da8dSAndroid Build Coastguard Worker import argparse 2497*cda5da8dSAndroid Build Coastguard Worker 2498*cda5da8dSAndroid Build Coastguard Worker description = 'A simple command-line interface for zipfile module.' 2499*cda5da8dSAndroid Build Coastguard Worker parser = argparse.ArgumentParser(description=description) 2500*cda5da8dSAndroid Build Coastguard Worker group = parser.add_mutually_exclusive_group(required=True) 2501*cda5da8dSAndroid Build Coastguard Worker group.add_argument('-l', '--list', metavar='<zipfile>', 2502*cda5da8dSAndroid Build Coastguard Worker help='Show listing of a zipfile') 2503*cda5da8dSAndroid Build Coastguard Worker group.add_argument('-e', '--extract', nargs=2, 2504*cda5da8dSAndroid Build Coastguard Worker metavar=('<zipfile>', '<output_dir>'), 2505*cda5da8dSAndroid Build Coastguard Worker help='Extract zipfile into target dir') 2506*cda5da8dSAndroid Build Coastguard Worker group.add_argument('-c', '--create', nargs='+', 2507*cda5da8dSAndroid Build Coastguard Worker metavar=('<name>', '<file>'), 2508*cda5da8dSAndroid Build Coastguard Worker help='Create zipfile from sources') 2509*cda5da8dSAndroid Build Coastguard Worker group.add_argument('-t', '--test', metavar='<zipfile>', 2510*cda5da8dSAndroid Build Coastguard Worker help='Test if a zipfile is valid') 2511*cda5da8dSAndroid Build Coastguard Worker parser.add_argument('--metadata-encoding', metavar='<encoding>', 2512*cda5da8dSAndroid Build Coastguard Worker help='Specify encoding of member names for -l, -e and -t') 2513*cda5da8dSAndroid Build Coastguard Worker args = parser.parse_args(args) 2514*cda5da8dSAndroid Build Coastguard Worker 2515*cda5da8dSAndroid Build Coastguard Worker encoding = args.metadata_encoding 2516*cda5da8dSAndroid Build Coastguard Worker 2517*cda5da8dSAndroid Build Coastguard Worker if args.test is not None: 2518*cda5da8dSAndroid Build Coastguard Worker src = args.test 2519*cda5da8dSAndroid Build Coastguard Worker with ZipFile(src, 'r', metadata_encoding=encoding) as zf: 2520*cda5da8dSAndroid Build Coastguard Worker badfile = zf.testzip() 2521*cda5da8dSAndroid Build Coastguard Worker if badfile: 2522*cda5da8dSAndroid Build Coastguard Worker print("The following enclosed file is corrupted: {!r}".format(badfile)) 2523*cda5da8dSAndroid Build Coastguard Worker print("Done testing") 2524*cda5da8dSAndroid Build Coastguard Worker 2525*cda5da8dSAndroid Build Coastguard Worker elif args.list is not None: 2526*cda5da8dSAndroid Build Coastguard Worker src = args.list 2527*cda5da8dSAndroid Build Coastguard Worker with ZipFile(src, 'r', metadata_encoding=encoding) as zf: 2528*cda5da8dSAndroid Build Coastguard Worker zf.printdir() 2529*cda5da8dSAndroid Build Coastguard Worker 2530*cda5da8dSAndroid Build Coastguard Worker elif args.extract is not None: 2531*cda5da8dSAndroid Build Coastguard Worker src, curdir = args.extract 2532*cda5da8dSAndroid Build Coastguard Worker with ZipFile(src, 'r', metadata_encoding=encoding) as zf: 2533*cda5da8dSAndroid Build Coastguard Worker zf.extractall(curdir) 2534*cda5da8dSAndroid Build Coastguard Worker 2535*cda5da8dSAndroid Build Coastguard Worker elif args.create is not None: 2536*cda5da8dSAndroid Build Coastguard Worker if encoding: 2537*cda5da8dSAndroid Build Coastguard Worker print("Non-conforming encodings not supported with -c.", 2538*cda5da8dSAndroid Build Coastguard Worker file=sys.stderr) 2539*cda5da8dSAndroid Build Coastguard Worker sys.exit(1) 2540*cda5da8dSAndroid Build Coastguard Worker 2541*cda5da8dSAndroid Build Coastguard Worker zip_name = args.create.pop(0) 2542*cda5da8dSAndroid Build Coastguard Worker files = args.create 2543*cda5da8dSAndroid Build Coastguard Worker 2544*cda5da8dSAndroid Build Coastguard Worker def addToZip(zf, path, zippath): 2545*cda5da8dSAndroid Build Coastguard Worker if os.path.isfile(path): 2546*cda5da8dSAndroid Build Coastguard Worker zf.write(path, zippath, ZIP_DEFLATED) 2547*cda5da8dSAndroid Build Coastguard Worker elif os.path.isdir(path): 2548*cda5da8dSAndroid Build Coastguard Worker if zippath: 2549*cda5da8dSAndroid Build Coastguard Worker zf.write(path, zippath) 2550*cda5da8dSAndroid Build Coastguard Worker for nm in sorted(os.listdir(path)): 2551*cda5da8dSAndroid Build Coastguard Worker addToZip(zf, 2552*cda5da8dSAndroid Build Coastguard Worker os.path.join(path, nm), os.path.join(zippath, nm)) 2553*cda5da8dSAndroid Build Coastguard Worker # else: ignore 2554*cda5da8dSAndroid Build Coastguard Worker 2555*cda5da8dSAndroid Build Coastguard Worker with ZipFile(zip_name, 'w') as zf: 2556*cda5da8dSAndroid Build Coastguard Worker for path in files: 2557*cda5da8dSAndroid Build Coastguard Worker zippath = os.path.basename(path) 2558*cda5da8dSAndroid Build Coastguard Worker if not zippath: 2559*cda5da8dSAndroid Build Coastguard Worker zippath = os.path.basename(os.path.dirname(path)) 2560*cda5da8dSAndroid Build Coastguard Worker if zippath in ('', os.curdir, os.pardir): 2561*cda5da8dSAndroid Build Coastguard Worker zippath = '' 2562*cda5da8dSAndroid Build Coastguard Worker addToZip(zf, path, zippath) 2563*cda5da8dSAndroid Build Coastguard Worker 2564*cda5da8dSAndroid Build Coastguard Worker 2565*cda5da8dSAndroid Build Coastguard Workerif __name__ == "__main__": 2566*cda5da8dSAndroid Build Coastguard Worker main() 2567