1 """
2 Read and write ZIP files.
3 """
4 import struct, os, time, sys, shutil
5 import binascii, cStringIO
6
7 try:
8 import zlib
9 crc32 = zlib.crc32
10 except ImportError:
11 zlib = None
12 crc32 = binascii.crc32
13
14 __all__ = ["BadZipfile", "error", "ZIP_STORED", "ZIP_DEFLATED", "is_zipfile",
15 "ZipInfo", "ZipFile", "PyZipFile", "LargeZipFile" ]
16
19
20
22 """
23 Raised when writing a zipfile, the zipfile requires ZIP64 extensions
24 and those extensions are disabled.
25 """
26
27 error = BadZipfile
28
29 ZIP64_LIMIT= (1 << 31) - 1
30
31
32 ZIP_STORED = 0
33 ZIP_DEFLATED = 8
34
35
36
37 structEndArchive = "<4s4H2LH"
38 stringEndArchive = "PK\005\006"
39 structCentralDir = "<4s4B4HLLL5HLL"
40 stringCentralDir = "PK\001\002"
41 structFileHeader = "<4s2B4HLLL2H"
42 stringFileHeader = "PK\003\004"
43 structEndArchive64Locator = "<4sLQL"
44 stringEndArchive64Locator = "PK\x06\x07"
45 structEndArchive64 = "<4sQHHLLQQQQ"
46 stringEndArchive64 = "PK\x06\x06"
47
48
49
50 _CD_SIGNATURE = 0
51 _CD_CREATE_VERSION = 1
52 _CD_CREATE_SYSTEM = 2
53 _CD_EXTRACT_VERSION = 3
54 _CD_EXTRACT_SYSTEM = 4
55 _CD_FLAG_BITS = 5
56 _CD_COMPRESS_TYPE = 6
57 _CD_TIME = 7
58 _CD_DATE = 8
59 _CD_CRC = 9
60 _CD_COMPRESSED_SIZE = 10
61 _CD_UNCOMPRESSED_SIZE = 11
62 _CD_FILENAME_LENGTH = 12
63 _CD_EXTRA_FIELD_LENGTH = 13
64 _CD_COMMENT_LENGTH = 14
65 _CD_DISK_NUMBER_START = 15
66 _CD_INTERNAL_FILE_ATTRIBUTES = 16
67 _CD_EXTERNAL_FILE_ATTRIBUTES = 17
68 _CD_LOCAL_HEADER_OFFSET = 18
69
70
71 _FH_SIGNATURE = 0
72 _FH_EXTRACT_VERSION = 1
73 _FH_EXTRACT_SYSTEM = 2
74 _FH_GENERAL_PURPOSE_FLAG_BITS = 3
75 _FH_COMPRESSION_METHOD = 4
76 _FH_LAST_MOD_TIME = 5
77 _FH_LAST_MOD_DATE = 6
78 _FH_CRC = 7
79 _FH_COMPRESSED_SIZE = 8
80 _FH_UNCOMPRESSED_SIZE = 9
81 _FH_FILENAME_LENGTH = 10
82 _FH_EXTRA_FIELD_LENGTH = 11
83
85 """Quickly see if file is a ZIP file by checking the magic number."""
86 try:
87 fpin = open(filename, "rb")
88 endrec = _EndRecData(fpin)
89 fpin.close()
90 if endrec:
91 return True
92 except IOError:
93 pass
94 return False
95
97 """
98 Read the ZIP64 end-of-archive records and use that to update endrec
99 """
100 locatorSize = struct.calcsize(structEndArchive64Locator)
101 fpin.seek(offset - locatorSize, 2)
102 data = fpin.read(locatorSize)
103 sig, diskno, reloff, disks = struct.unpack(structEndArchive64Locator, data)
104 if sig != stringEndArchive64Locator:
105 return endrec
106
107 if diskno != 0 or disks != 1:
108 raise BadZipfile("zipfiles that span multiple disks are not supported")
109
110
111 endArchiveSize = struct.calcsize(structEndArchive64)
112 fpin.seek(offset - locatorSize - endArchiveSize, 2)
113 data = fpin.read(endArchiveSize)
114 sig, sz, create_version, read_version, disk_num, disk_dir, \
115 dircount, dircount2, dirsize, diroffset = \
116 struct.unpack(structEndArchive64, data)
117 if sig != stringEndArchive64:
118 return endrec
119
120
121 endrec[1] = disk_num
122 endrec[2] = disk_dir
123 endrec[3] = dircount
124 endrec[4] = dircount2
125 endrec[5] = dirsize
126 endrec[6] = diroffset
127 return endrec
128
129
131 """Return data from the "End of Central Directory" record, or None.
132
133 The data is a list of the nine items in the ZIP "End of central dir"
134 record followed by a tenth item, the file seek offset of this record."""
135 fpin.seek(-22, 2)
136 filesize = fpin.tell() + 22
137 data = fpin.read()
138 if data[0:4] == stringEndArchive and data[-2:] == "\000\000":
139 endrec = struct.unpack(structEndArchive, data)
140 endrec = list(endrec)
141 endrec.append("")
142 endrec.append(filesize - 22)
143 if endrec[-4] == 0xffffffff:
144 return _EndRecData64(fpin, -22, endrec)
145 return endrec
146
147
148
149
150
151 END_BLOCK = min(filesize, 1024 * 4)
152 fpin.seek(filesize - END_BLOCK, 0)
153 data = fpin.read()
154 start = data.rfind(stringEndArchive)
155 if start >= 0:
156 endrec = struct.unpack(structEndArchive, data[start:start+22])
157 endrec = list(endrec)
158 comment = data[start+22:]
159 if endrec[7] == len(comment):
160
161 endrec.append(comment)
162 endrec.append(filesize - END_BLOCK + start)
163 if endrec[-4] == 0xffffffff:
164 return _EndRecData64(fpin, - END_BLOCK + start, endrec)
165 return endrec
166 return
167
168
170 """Class with attributes describing each file in the ZIP archive."""
171
172 __slots__ = (
173 'orig_filename',
174 'filename',
175 'date_time',
176 'compress_type',
177 'comment',
178 'extra',
179 'create_system',
180 'create_version',
181 'extract_version',
182 'reserved',
183 'flag_bits',
184 'volume',
185 'internal_attr',
186 'external_attr',
187 'header_offset',
188 'CRC',
189 'compress_size',
190 'file_size',
191 '_raw_time',
192 )
193
194 - def __init__(self, filename="NoName", date_time=(1980,1,1,0,0,0)):
226
227
228
229
230
231
233 """Return the per-file header as a string."""
234 dt = self.date_time
235 dosdate = (dt[0] - 1980) << 9 | dt[1] << 5 | dt[2]
236 dostime = dt[3] << 11 | dt[4] << 5 | (dt[5] // 2)
237 if self.flag_bits & 0x08:
238
239 CRC = compress_size = file_size = 0
240 else:
241 CRC = self.CRC
242 compress_size = self.compress_size
243 file_size = self.file_size
244
245 extra = self.extra
246
247 if file_size > ZIP64_LIMIT or compress_size > ZIP64_LIMIT:
248
249
250 fmt = '<HHQQ'
251 extra = extra + struct.pack(fmt,
252 1, struct.calcsize(fmt)-4, file_size, compress_size)
253 file_size = 0xffffffff
254 compress_size = 0xffffffff
255 self.extract_version = max(45, self.extract_version)
256 self.create_version = max(45, self.extract_version)
257
258 header = struct.pack(structFileHeader, stringFileHeader,
259 self.extract_version, self.reserved, self.flag_bits,
260 self.compress_type, dostime, dosdate, CRC,
261 compress_size, file_size,
262 len(self.filename), len(extra))
263 return header + self.filename + extra
264
266
267 extra = self.extra
268 unpack = struct.unpack
269 while extra:
270 tp, ln = unpack('<HH', extra[:4])
271 if tp == 1:
272 if ln >= 24:
273 counts = unpack('<QQQ', extra[4:28])
274 elif ln == 16:
275 counts = unpack('<QQ', extra[4:20])
276 elif ln == 8:
277 counts = unpack('<Q', extra[4:12])
278 elif ln == 0:
279 counts = ()
280 else:
281 raise RuntimeError, "Corrupt extra field %s"%(ln,)
282
283 idx = 0
284
285
286
287 if self.file_size in (0xffffffffffffffffL, 0xffffffffL):
288 self.file_size = counts[idx]
289 idx += 1
290
291 if self.compress_size == -1 or self.compress_size == 0xFFFFFFFFL:
292 self.compress_size = counts[idx]
293 idx += 1
294
295 if self.header_offset == -1 or self.header_offset == 0xffffffffL:
296 old = self.header_offset
297 self.header_offset = counts[idx]
298 idx+=1
299
300 extra = extra[ln+4:]
301
302
304 """
305 Class to handle decryption of files stored within a ZIP archive.
306
307 ZIP supports a password-based form of encryption. Even though known
308 plaintext attacks have been found against it, it is still useful
309 to be able to get data out of such a file.
310
311 Usage ::
312 zd = _ZipDecrypter(mypwd)
313 plain_char = zd(cypher_char)
314 plain_text = map(zd, cypher_text)
315 """
316
318 """Generate a CRC-32 table.
319
320 ZIP encryption uses the CRC32 one-byte primitive for scrambling some
321 internal keys. We noticed that a direct implementation is faster than
322 relying on binascii.crc32().
323 """
324 poly = 0xedb88320
325 table = [0] * 256
326 for i in range(256):
327 crc = i
328 for j in range(8):
329 if crc & 1:
330 crc = ((crc >> 1) & 0x7FFFFFFF) ^ poly
331 else:
332 crc = ((crc >> 1) & 0x7FFFFFFF)
333 table[i] = crc
334 return table
335 crctable = _GenerateCRCTable()
336
338 """Compute the CRC32 primitive on one byte."""
339 return ((crc >> 8) & 0xffffff) ^ self.crctable[(crc ^ ord(ch)) & 0xff]
340
342 self.key0 = 305419896
343 self.key1 = 591751049
344 self.key2 = 878082192
345 for p in pwd:
346 self._UpdateKeys(p)
347
349 self.key0 = self._crc32(c, self.key0)
350 self.key1 = (self.key1 + (self.key0 & 255)) & 4294967295
351 self.key1 = (self.key1 * 134775813 + 1) & 4294967295
352 self.key2 = self._crc32(chr((self.key1 >> 24) & 255), self.key2)
353
355 """Decrypt a single character."""
356 c = ord(c)
357 k = self.key2 | 2
358 c = c ^ (((k * (k^1)) >> 8) & 255)
359 c = chr(c)
360 self._UpdateKeys(c)
361 return c
362
364 """File-like object for reading an archive member.
365 Is returned by ZipFile.open().
366 """
367
368 - def __init__(self, fileobj, zipinfo, decrypt=None):
369 self.fileobj = fileobj
370 self.decrypter = decrypt
371 self.bytes_read = 0L
372 self.rawbuffer = ''
373 self.readbuffer = ''
374 self.linebuffer = ''
375 self.eof = False
376 self.univ_newlines = False
377 self.nlSeps = ("\n", )
378 self.lastdiscard = ''
379
380 self.compress_type = zipinfo.compress_type
381 self.compress_size = zipinfo.compress_size
382
383 self.closed = False
384 self.mode = "r"
385 self.name = zipinfo.filename
386
387
388 self.compreadsize = 64*1024
389 if self.compress_type == ZIP_DEFLATED:
390 self.dc = zlib.decompressobj(-15)
391
393 self.univ_newlines = univ_newlines
394
395
396 self.nlSeps = ("\n", )
397 if self.univ_newlines:
398 self.nlSeps = ("\r\n", "\r", "\n")
399
402
404 nextline = self.readline()
405 if not nextline:
406 raise StopIteration()
407
408 return nextline
409
412
414 nl, nllen = -1, -1
415 if self.linebuffer:
416
417
418
419 if (self.lastdiscard, self.linebuffer[0]) == ('\r','\n'):
420 self.linebuffer = self.linebuffer[1:]
421
422 for sep in self.nlSeps:
423 nl = self.linebuffer.find(sep)
424 if nl >= 0:
425 nllen = len(sep)
426 return nl, nllen
427
428 return nl, nllen
429
431 """Read a line with approx. size. If size is negative,
432 read a whole line.
433 """
434 if size < 0:
435 size = sys.maxint
436 elif size == 0:
437 return ''
438
439
440 nl, nllen = self._checkfornewline()
441
442 if nl >= 0:
443
444 nl = min(nl, size)
445 else:
446
447 size -= len(self.linebuffer)
448 while nl < 0 and size > 0:
449 buf = self.read(min(size, 100))
450 if not buf:
451 break
452 self.linebuffer += buf
453 size -= len(buf)
454
455
456 nl, nllen = self._checkfornewline()
457
458
459
460
461 if nl < 0:
462 s = self.linebuffer
463 self.linebuffer = ''
464 return s
465
466 buf = self.linebuffer[:nl]
467 self.lastdiscard = self.linebuffer[nl:nl + nllen]
468 self.linebuffer = self.linebuffer[nl + nllen:]
469
470
471
472 return buf + "\n"
473
475 """Return a list with all (following) lines. The sizehint parameter
476 is ignored in this implementation.
477 """
478 result = []
479 while True:
480 line = self.readline()
481 if not line: break
482 result.append(line)
483 return result
484
485 - def read(self, size = None):
486
487 if size == 0:
488 return ''
489
490
491 bytesToRead = self.compress_size - self.bytes_read
492
493
494
495 if self.decrypter is not None:
496 bytesToRead -= 12
497
498 if size is not None and size >= 0:
499 if self.compress_type == ZIP_STORED:
500 lr = len(self.readbuffer)
501 bytesToRead = min(bytesToRead, size - lr)
502 elif self.compress_type == ZIP_DEFLATED:
503 if len(self.readbuffer) > size:
504
505
506 bytesToRead = 0
507 else:
508
509 lr = len(self.rawbuffer)
510 bytesToRead = min(bytesToRead, self.compreadsize - lr)
511
512
513 if bytesToRead + self.bytes_read > self.compress_size:
514 bytesToRead = self.compress_size - self.bytes_read
515
516
517 if bytesToRead > 0:
518 bytes = self.fileobj.read(bytesToRead)
519 self.bytes_read += len(bytes)
520 self.rawbuffer += bytes
521
522
523 if self.rawbuffer:
524 newdata = self.rawbuffer
525 self.rawbuffer = ''
526
527
528 if newdata and self.decrypter is not None:
529 newdata = ''.join(map(self.decrypter, newdata))
530
531
532 if newdata and self.compress_type == ZIP_DEFLATED:
533 newdata = self.dc.decompress(newdata)
534 self.rawbuffer = self.dc.unconsumed_tail
535 if self.eof and len(self.rawbuffer) == 0:
536
537
538
539 newdata += self.dc.flush()
540
541 self.dc = None
542
543 self.readbuffer += newdata
544
545
546
547 if size is None or len(self.readbuffer) <= size:
548 bytes = self.readbuffer
549 self.readbuffer = ''
550 else:
551 bytes = self.readbuffer[:size]
552 self.readbuffer = self.readbuffer[size:]
553
554 return bytes
555
556
558 """ Class with methods to open, read, write, close, list zip files.
559
560 z = ZipFile(file, mode="r", compression=ZIP_STORED, allowZip64=True)
561
562 @var file: Either the path to the file, or a file-like object.
563 If it is a path, the file will be opened and closed by ZipFile.
564 @var mode: The mode can be either read "r", write "w" or append "a".
565 @var compression: ZIP_STORED (no compression) or ZIP_DEFLATED (requires zlib).
566 @var allowZip64: if True ZipFile will create files with ZIP64 extensions when
567 needed, otherwise it will raise an exception when this would
568 be necessary.
569
570 """
571
572 fp = None
573
575 """Open the ZIP file with mode read "r", write "w" or append "a"."""
576 if mode not in ("r", "w", "a"):
577 raise RuntimeError('ZipFile() requires mode "r", "w", or "a"')
578
579 if compression == ZIP_STORED:
580 pass
581 elif compression == ZIP_DEFLATED:
582 if not zlib:
583 raise RuntimeError,\
584 "Compression requires the (missing) zlib module"
585 else:
586 raise RuntimeError, "That compression method is not supported"
587
588 self._allowZip64 = allowZip64
589 self._didModify = False
590 self.debug = 0
591 self.NameToInfo = {}
592 self.filelist = []
593 self.compression = compression
594 self.mode = key = mode.replace('b', '')[0]
595 self.pwd = None
596
597
598 if isinstance(file, basestring):
599 self._filePassed = 0
600 self.filename = file
601 modeDict = {'r' : 'rb', 'w': 'wb', 'a' : 'r+b'}
602 try:
603 self.fp = open(file, modeDict[mode])
604 except IOError:
605 if mode == 'a':
606 mode = key = 'w'
607 self.fp = open(file, modeDict[mode])
608 else:
609 raise
610 else:
611 self._filePassed = 1
612 self.fp = file
613 self.filename = getattr(file, 'name', None)
614
615 if key == 'r':
616 self._GetContents()
617 elif key == 'w':
618 pass
619 elif key == 'a':
620 try:
621 self._RealGetContents()
622
623 self.fp.seek(self.start_dir, 0)
624 except BadZipfile:
625 self.fp.seek(0, 2)
626 else:
627 if not self._filePassed:
628 self.fp.close()
629 self.fp = None
630 raise RuntimeError, 'Mode must be "r", "w" or "a"'
631
632 - def _GetContents(self):
633 """Read the directory, making sure we close the file if the format
634 is bad."""
635 try:
636 self._RealGetContents()
637 except BadZipfile:
638 if not self._filePassed:
639 self.fp.close()
640 self.fp = None
641 raise
642
644 """Read in the table of contents for the ZIP file."""
645 fp = self.fp
646 endrec = _EndRecData(fp)
647 if not endrec:
648 raise BadZipfile, "File is not a zip file"
649 if self.debug > 1:
650 print endrec
651 size_cd = endrec[5]
652 offset_cd = endrec[6]
653 self.comment = endrec[8]
654
655 if endrec[9] > ZIP64_LIMIT:
656 x = endrec[9] - size_cd - 56 - 20
657 else:
658 x = endrec[9] - size_cd
659
660 concat = x - offset_cd
661 if self.debug > 2:
662 print "given, inferred, offset", offset_cd, x, concat
663
664 self.start_dir = offset_cd + concat
665 fp.seek(self.start_dir, 0)
666 data = fp.read(size_cd)
667 fp = cStringIO.StringIO(data)
668 total = 0
669 while total < size_cd:
670 centdir = fp.read(46)
671 total = total + 46
672 if centdir[0:4] != stringCentralDir:
673 raise BadZipfile, "Bad magic number for central directory"
674 centdir = struct.unpack(structCentralDir, centdir)
675 if self.debug > 2:
676 print centdir
677 filename = fp.read(centdir[_CD_FILENAME_LENGTH])
678
679 x = ZipInfo(filename)
680 x.extra = fp.read(centdir[_CD_EXTRA_FIELD_LENGTH])
681 x.comment = fp.read(centdir[_CD_COMMENT_LENGTH])
682 total = (total + centdir[_CD_FILENAME_LENGTH]
683 + centdir[_CD_EXTRA_FIELD_LENGTH]
684 + centdir[_CD_COMMENT_LENGTH])
685 x.header_offset = centdir[_CD_LOCAL_HEADER_OFFSET]
686 (x.create_version, x.create_system, x.extract_version, x.reserved,
687 x.flag_bits, x.compress_type, t, d,
688 x.CRC, x.compress_size, x.file_size) = centdir[1:12]
689 x.volume, x.internal_attr, x.external_attr = centdir[15:18]
690
691 x._raw_time = t
692 x.date_time = ( (d>>9)+1980, (d>>5)&0xF, d&0x1F,
693 t>>11, (t>>5)&0x3F, (t&0x1F) * 2 )
694
695 x._decodeExtra()
696 x.header_offset = x.header_offset + concat
697 self.filelist.append(x)
698 self.NameToInfo[x.filename] = x
699 if self.debug > 2:
700 print "total", total
701
702
704 """Return a list of file names in the archive."""
705 l = []
706 for data in self.filelist:
707 l.append(data.filename)
708 return l
709
711 """Return a list of class ZipInfo instances for files in the
712 archive."""
713 return self.filelist
714
716 """Print a table of contents for the zip file."""
717 print "%-46s %19s %12s" % ("File Name", "Modified ", "Size")
718 for zinfo in self.filelist:
719 date = "%d-%02d-%02d %02d:%02d:%02d" % zinfo.date_time[:6]
720 print "%-46s %s %12d" % (zinfo.filename, date, zinfo.file_size)
721
723 """Read all the files and check the CRC."""
724 for zinfo in self.filelist:
725 try:
726 self.read(zinfo.filename)
727 except BadZipfile:
728 return zinfo.filename
729
730
732 """Return the instance of ZipInfo given 'name'."""
733 info = self.NameToInfo.get(name)
734 if info is None:
735 raise KeyError(
736 'There is no item named %r in the archive' % name)
737
738 return info
739
741 """Set default password for encrypted files."""
742 self.pwd = pwd
743
744 - def read(self, name, pwd=None):
745 """Return file bytes (as a string) for name."""
746 return self.open(name, "r", pwd).read()
747
748 - def open(self, name, mode="r", pwd=None):
749 """Return file-like object for 'name'."""
750 if mode not in ("r", "U", "rU"):
751 raise RuntimeError, 'open() requires mode "r", "U", or "rU"'
752 if not self.fp:
753 raise RuntimeError, \
754 "Attempt to read ZIP archive that was already closed"
755
756
757
758 if self._filePassed:
759 zef_file = self.fp
760 else:
761 zef_file = open(self.filename, 'rb')
762
763
764 zinfo = self.getinfo(name)
765
766 filepos = zef_file.tell()
767
768 zef_file.seek(zinfo.header_offset, 0)
769
770
771 fheader = zef_file.read(30)
772 if fheader[0:4] != stringFileHeader:
773 raise BadZipfile, "Bad magic number for file header"
774
775 fheader = struct.unpack(structFileHeader, fheader)
776 fname = zef_file.read(fheader[_FH_FILENAME_LENGTH])
777 if fheader[_FH_EXTRA_FIELD_LENGTH]:
778 zef_file.read(fheader[_FH_EXTRA_FIELD_LENGTH])
779
780 if fname != zinfo.orig_filename:
781 raise BadZipfile, \
782 'File name in directory "%s" and header "%s" differ.' % (
783 zinfo.orig_filename, fname)
784
785
786 is_encrypted = zinfo.flag_bits & 0x1
787 zd = None
788 if is_encrypted:
789 if not pwd:
790 pwd = self.pwd
791 if not pwd:
792 raise RuntimeError, "File %s is encrypted, " \
793 "password required for extraction" % name
794
795 zd = _ZipDecrypter(pwd)
796
797
798
799
800
801 bytes = zef_file.read(12)
802 h = map(zd, bytes[0:12])
803 if zinfo.flag_bits & 0x8:
804
805 check_byte = (zinfo._raw_time >> 8) & 0xff
806 else:
807
808 check_byte = (zinfo.CRC >> 24) & 0xff
809 if ord(h[11]) != check_byte:
810 raise RuntimeError("Bad password for file", name)
811
812
813 if zd is None:
814 zef = ZipExtFile(zef_file, zinfo)
815 else:
816 zef = ZipExtFile(zef_file, zinfo, zd)
817
818
819 if "U" in mode:
820 zef.set_univ_newlines(True)
821 return zef
822
824 """Extract a member from the archive to the current working directory,
825 using its full name. Its file information is extracted as accurately
826 as possible. `member' may be a filename or a ZipInfo object. You can
827 specify a different directory using `path'.
828 """
829 if not isinstance(member, ZipInfo):
830 member = self.getinfo(member)
831
832 if path is None:
833 path = os.getcwd()
834
835 return self._extract_member(member, path, pwd)
836
838 """Extract all members from the archive to the current working
839 directory. `path' specifies a different directory to extract to.
840 `members' is optional and must be a subset of the list returned
841 by namelist().
842 """
843 if members is None:
844 members = self.namelist()
845
846 for zipinfo in members:
847 self.extract(zipinfo, path, pwd)
848
850 """Extract the ZipInfo object 'member' to a physical
851 file on the path targetpath.
852 """
853
854
855 if targetpath[-1:] == "/":
856 targetpath = targetpath[:-1]
857
858
859 if os.path.isabs(member.filename):
860 targetpath = os.path.join(targetpath, member.filename[1:])
861 else:
862 targetpath = os.path.join(targetpath, member.filename)
863
864 targetpath = os.path.normpath(targetpath)
865
866
867 upperdirs = os.path.dirname(targetpath)
868 if upperdirs and not os.path.exists(upperdirs):
869 os.makedirs(upperdirs)
870
871 source = self.open(member.filename, pwd=pwd)
872 target = file(targetpath, "wb")
873 shutil.copyfileobj(source, target)
874 source.close()
875 target.close()
876
877 return targetpath
878
880 """Check for errors before writing a file to the archive."""
881 if zinfo.filename in self.NameToInfo:
882 if self.debug:
883 print "Duplicate name:", zinfo.filename
884 if self.mode not in ("w", "a"):
885 raise RuntimeError, 'write() requires mode "w" or "a"'
886 if not self.fp:
887 raise RuntimeError, \
888 "Attempt to write ZIP archive that was already closed"
889 if zinfo.compress_type == ZIP_DEFLATED and not zlib:
890 raise RuntimeError, \
891 "Compression requires the (missing) zlib module"
892 if zinfo.compress_type not in (ZIP_STORED, ZIP_DEFLATED):
893 raise RuntimeError, \
894 "That compression method is not supported"
895 if zinfo.file_size > ZIP64_LIMIT:
896 if not self._allowZip64:
897 raise LargeZipFile("Filesize would require ZIP64 extensions")
898 if zinfo.header_offset > ZIP64_LIMIT:
899 if not self._allowZip64:
900 raise LargeZipFile("Zipfile size would require ZIP64 extensions")
901
902 - def write(self, filename, arcname=None, compress_type=None):
903 """Put the bytes from filename into the archive under the name
904 arcname."""
905 if not self.fp:
906 raise RuntimeError(
907 "Attempt to write to ZIP archive that was already closed")
908
909 st = os.stat(filename)
910 mtime = time.localtime(st.st_mtime)
911 date_time = mtime[0:6]
912
913 if arcname is None:
914 arcname = filename
915 arcname = os.path.normpath(os.path.splitdrive(arcname)[1])
916 while arcname[0] in (os.sep, os.altsep):
917 arcname = arcname[1:]
918 zinfo = ZipInfo(arcname, date_time)
919 zinfo.external_attr = (st[0] & 0xFFFF) << 16L
920 if compress_type is None:
921 zinfo.compress_type = self.compression
922 else:
923 zinfo.compress_type = compress_type
924
925 zinfo.file_size = st.st_size
926 zinfo.flag_bits = 0x00
927 zinfo.header_offset = self.fp.tell()
928
929 self._writecheck(zinfo)
930 self._didModify = True
931 fp = open(filename, "rb")
932
933 zinfo.CRC = CRC = 0
934 zinfo.compress_size = compress_size = 0
935 zinfo.file_size = file_size = 0
936 self.fp.write(zinfo.FileHeader())
937 if zinfo.compress_type == ZIP_DEFLATED:
938 cmpr = zlib.compressobj(zlib.Z_DEFAULT_COMPRESSION,
939 zlib.DEFLATED, -15)
940 else:
941 cmpr = None
942 while 1:
943 buf = fp.read(1024 * 8)
944 if not buf:
945 break
946 file_size = file_size + len(buf)
947 CRC = crc32(buf, CRC) & 0xffffffff
948 if cmpr:
949 buf = cmpr.compress(buf)
950 compress_size = compress_size + len(buf)
951 self.fp.write(buf)
952 fp.close()
953 if cmpr:
954 buf = cmpr.flush()
955 compress_size = compress_size + len(buf)
956 self.fp.write(buf)
957 zinfo.compress_size = compress_size
958 else:
959 zinfo.compress_size = file_size
960 zinfo.CRC = CRC
961 zinfo.file_size = file_size
962
963 position = self.fp.tell()
964 self.fp.seek(zinfo.header_offset + 14, 0)
965 self.fp.write(struct.pack("<LLL", zinfo.CRC, zinfo.compress_size,
966 zinfo.file_size))
967 self.fp.seek(position, 0)
968 self.filelist.append(zinfo)
969 self.NameToInfo[zinfo.filename] = zinfo
970
971 - def writestr(self, zinfo_or_arcname, bytes):
1008
1010 """Call the "close()" method in case the user forgot."""
1011 self.close()
1012
1014 """Close the file, and for mode "w" and "a" write the ending
1015 records."""
1016 if self.fp is None:
1017 return
1018
1019 if self.mode in ("w", "a") and self._didModify:
1020 count = 0
1021 pos1 = self.fp.tell()
1022 for zinfo in self.filelist:
1023 count = count + 1
1024 dt = zinfo.date_time
1025 dosdate = (dt[0] - 1980) << 9 | dt[1] << 5 | dt[2]
1026 dostime = dt[3] << 11 | dt[4] << 5 | (dt[5] // 2)
1027 extra = []
1028 if zinfo.file_size > ZIP64_LIMIT \
1029 or zinfo.compress_size > ZIP64_LIMIT:
1030 extra.append(zinfo.file_size)
1031 extra.append(zinfo.compress_size)
1032 file_size = 0xffffffff
1033 compress_size = 0xffffffff
1034 else:
1035 file_size = zinfo.file_size
1036 compress_size = zinfo.compress_size
1037
1038 if zinfo.header_offset > ZIP64_LIMIT:
1039 extra.append(zinfo.header_offset)
1040 header_offset = 0xffffffffL
1041 else:
1042 header_offset = zinfo.header_offset
1043
1044 extra_data = zinfo.extra
1045 if extra:
1046
1047 extra_data = struct.pack(
1048 '<HH' + 'Q'*len(extra),
1049 1, 8*len(extra), *extra) + extra_data
1050
1051 extract_version = max(45, zinfo.extract_version)
1052 create_version = max(45, zinfo.create_version)
1053 else:
1054 extract_version = zinfo.extract_version
1055 create_version = zinfo.create_version
1056
1057 try:
1058 centdir = struct.pack(structCentralDir,
1059 stringCentralDir, create_version,
1060 zinfo.create_system, extract_version, zinfo.reserved,
1061 zinfo.flag_bits, zinfo.compress_type, dostime, dosdate,
1062 zinfo.CRC, compress_size, file_size,
1063 len(zinfo.filename), len(extra_data), len(zinfo.comment),
1064 0, zinfo.internal_attr, zinfo.external_attr,
1065 header_offset)
1066 except DeprecationWarning:
1067 print >>sys.stderr, (structCentralDir,
1068 stringCentralDir, create_version,
1069 zinfo.create_system, extract_version, zinfo.reserved,
1070 zinfo.flag_bits, zinfo.compress_type, dostime, dosdate,
1071 zinfo.CRC, compress_size, file_size,
1072 len(zinfo.filename), len(extra_data), len(zinfo.comment),
1073 0, zinfo.internal_attr, zinfo.external_attr,
1074 header_offset)
1075 raise
1076 self.fp.write(centdir)
1077 self.fp.write(zinfo.filename)
1078 self.fp.write(extra_data)
1079 self.fp.write(zinfo.comment)
1080
1081 pos2 = self.fp.tell()
1082
1083 if pos1 > ZIP64_LIMIT:
1084
1085 zip64endrec = struct.pack(
1086 structEndArchive64, stringEndArchive64,
1087 44, 45, 45, 0, 0, count, count, pos2 - pos1, pos1)
1088 self.fp.write(zip64endrec)
1089
1090 zip64locrec = struct.pack(
1091 structEndArchive64Locator,
1092 stringEndArchive64Locator, 0, pos2, 1)
1093 self.fp.write(zip64locrec)
1094
1095 endrec = struct.pack(structEndArchive, stringEndArchive,
1096 0, 0, count, count, pos2 - pos1, 0xffffffffL, 0)
1097 self.fp.write(endrec)
1098
1099 else:
1100 endrec = struct.pack(structEndArchive, stringEndArchive,
1101 0, 0, count, count, pos2 - pos1, pos1, 0)
1102 self.fp.write(endrec)
1103 self.fp.flush()
1104 if not self._filePassed:
1105 self.fp.close()
1106 self.fp = None
1107
1108
1110 """Class to create ZIP archives with Python library files and packages."""
1111
1112 - def writepy(self, pathname, basename = ""):
1113 """Add all files from "pathname" to the ZIP archive.
1114
1115 If pathname is a package directory, search the directory and
1116 all package subdirectories recursively for all *.py and enter
1117 the modules into the archive. If pathname is a plain
1118 directory, listdir *.py and enter all modules. Else, pathname
1119 must be a Python *.py file and the module will be put into the
1120 archive. Added modules are always module.pyo or module.pyc.
1121 This method will compile the module.py into module.pyc if
1122 necessary.
1123 """
1124 dir, name = os.path.split(pathname)
1125 if os.path.isdir(pathname):
1126 initname = os.path.join(pathname, "__init__.py")
1127 if os.path.isfile(initname):
1128
1129 if basename:
1130 basename = "%s/%s" % (basename, name)
1131 else:
1132 basename = name
1133 if self.debug:
1134 print "Adding package in", pathname, "as", basename
1135 fname, arcname = self._get_codename(initname[0:-3], basename)
1136 if self.debug:
1137 print "Adding", arcname
1138 self.write(fname, arcname)
1139 dirlist = os.listdir(pathname)
1140 dirlist.remove("__init__.py")
1141
1142 for filename in dirlist:
1143 path = os.path.join(pathname, filename)
1144 root, ext = os.path.splitext(filename)
1145 if os.path.isdir(path):
1146 if os.path.isfile(os.path.join(path, "__init__.py")):
1147
1148 self.writepy(path, basename)
1149 elif ext == ".py":
1150 fname, arcname = self._get_codename(path[0:-3],
1151 basename)
1152 if self.debug:
1153 print "Adding", arcname
1154 self.write(fname, arcname)
1155 else:
1156
1157 if self.debug:
1158 print "Adding files from directory", pathname
1159 for filename in os.listdir(pathname):
1160 path = os.path.join(pathname, filename)
1161 root, ext = os.path.splitext(filename)
1162 if ext == ".py":
1163 fname, arcname = self._get_codename(path[0:-3],
1164 basename)
1165 if self.debug:
1166 print "Adding", arcname
1167 self.write(fname, arcname)
1168 else:
1169 if pathname[-3:] != ".py":
1170 raise RuntimeError, \
1171 'Files added with writepy() must end with ".py"'
1172 fname, arcname = self._get_codename(pathname[0:-3], basename)
1173 if self.debug:
1174 print "Adding file", arcname
1175 self.write(fname, arcname)
1176
1178 """Return (filename, archivename) for the path.
1179
1180 Given a module name path, return the correct file path and
1181 archive name, compiling if necessary. For example, given
1182 /python/lib/string, return (/python/lib/string.pyc, string).
1183 """
1184 file_py = pathname + ".py"
1185 file_pyc = pathname + ".pyc"
1186 file_pyo = pathname + ".pyo"
1187 if os.path.isfile(file_pyo) and \
1188 os.stat(file_pyo).st_mtime >= os.stat(file_py).st_mtime:
1189 fname = file_pyo
1190 elif not os.path.isfile(file_pyc) or \
1191 os.stat(file_pyc).st_mtime < os.stat(file_py).st_mtime:
1192 import py_compile
1193 if self.debug:
1194 print "Compiling", file_py
1195 try:
1196 py_compile.compile(file_py, file_pyc, None, True)
1197 except py_compile.PyCompileError,err:
1198 print err.msg
1199 fname = file_pyc
1200 else:
1201 fname = file_pyc
1202 archivename = os.path.split(fname)[1]
1203 if basename:
1204 archivename = "%s/%s" % (basename, archivename)
1205 return (fname, archivename)
1206
1207
1208 -def main(args = None):
1209 import textwrap
1210 USAGE=textwrap.dedent("""\
1211 Usage:
1212 zipfile.py -l zipfile.zip # Show listing of a zipfile
1213 zipfile.py -t zipfile.zip # Test if a zipfile is valid
1214 zipfile.py -e zipfile.zip target # Extract zipfile into target dir
1215 zipfile.py -c zipfile.zip src ... # Create zipfile from sources
1216 """)
1217 if args is None:
1218 args = sys.argv[1:]
1219
1220 if not args or args[0] not in ('-l', '-c', '-e', '-t'):
1221 print USAGE
1222 sys.exit(1)
1223
1224 if args[0] == '-l':
1225 if len(args) != 2:
1226 print USAGE
1227 sys.exit(1)
1228 zf = ZipFile(args[1], 'r')
1229 zf.printdir()
1230 zf.close()
1231
1232 elif args[0] == '-t':
1233 if len(args) != 2:
1234 print USAGE
1235 sys.exit(1)
1236 zf = ZipFile(args[1], 'r')
1237 zf.testzip()
1238 print "Done testing"
1239
1240 elif args[0] == '-e':
1241 if len(args) != 3:
1242 print USAGE
1243 sys.exit(1)
1244
1245 zf = ZipFile(args[1], 'r')
1246 out = args[2]
1247 for path in zf.namelist():
1248 if path.startswith('./'):
1249 tgt = os.path.join(out, path[2:])
1250 else:
1251 tgt = os.path.join(out, path)
1252
1253 tgtdir = os.path.dirname(tgt)
1254 if not os.path.exists(tgtdir):
1255 os.makedirs(tgtdir)
1256 fp = open(tgt, 'wb')
1257 fp.write(zf.read(path))
1258 fp.close()
1259 zf.close()
1260
1261 elif args[0] == '-c':
1262 if len(args) < 3:
1263 print USAGE
1264 sys.exit(1)
1265
1266 def addToZip(zf, path, zippath):
1267 if os.path.isfile(path):
1268 zf.write(path, zippath, ZIP_DEFLATED)
1269 elif os.path.isdir(path):
1270 for nm in os.listdir(path):
1271 addToZip(zf,
1272 os.path.join(path, nm), os.path.join(zippath, nm))
1273
1274
1275 zf = ZipFile(args[1], 'w', allowZip64=True)
1276 for src in args[2:]:
1277 addToZip(zf, src, os.path.basename(src))
1278
1279 zf.close()
1280
1281 if __name__ == "__main__":
1282 main()
1283