mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Fix #4426 (Error opening epub for view)
This commit is contained in:
parent
536e629356
commit
8bfffc74e2
@ -1,9 +1,10 @@
|
|||||||
"""
|
"""
|
||||||
Read and write ZIP files. Modified by Kovid Goyal to support replacing files in
|
Read and write ZIP files. Modified by Kovid Goyal to support replacing files in
|
||||||
a zip archive.
|
a zip archive.
|
||||||
"""
|
"""
|
||||||
from __future__ import with_statement
|
from __future__ import with_statement
|
||||||
from calibre.ptempfile import TemporaryDirectory
|
from calibre.ptempfile import TemporaryDirectory
|
||||||
|
from calibre import sanitize_file_name
|
||||||
import struct, os, time, sys, shutil
|
import struct, os, time, sys, shutil
|
||||||
import binascii, cStringIO
|
import binascii, cStringIO
|
||||||
|
|
||||||
@ -789,80 +790,80 @@ class ZipFile:
|
|||||||
|
|
||||||
if self.debug > 2:
|
if self.debug > 2:
|
||||||
print "total", total
|
print "total", total
|
||||||
|
|
||||||
def _calculate_file_offsets(self):
|
|
||||||
for zip_info in self.filelist:
|
|
||||||
self.fp.seek(zip_info.header_offset, 0)
|
|
||||||
fheader = self.fp.read(30)
|
|
||||||
if fheader[0:4] != stringFileHeader:
|
|
||||||
raise BadZipfile, "Bad magic number for file header"
|
|
||||||
fheader = struct.unpack(structFileHeader, fheader)
|
|
||||||
# file_offset is computed here, since the extra field for
|
|
||||||
# the central directory and for the local file header
|
|
||||||
# refer to different fields, and they can have different
|
|
||||||
# lengths
|
|
||||||
file_offset = (zip_info.header_offset + 30
|
|
||||||
+ fheader[_FH_FILENAME_LENGTH]
|
|
||||||
+ fheader[_FH_EXTRA_FIELD_LENGTH])
|
|
||||||
fname = self.fp.read(fheader[_FH_FILENAME_LENGTH])
|
|
||||||
if fname != zip_info.orig_filename:
|
|
||||||
raise RuntimeError(
|
|
||||||
'File name in directory "%s" and header "%s" differ.' % (
|
|
||||||
zip_info.orig_filename, fname))
|
|
||||||
|
|
||||||
zip_info.file_offset = file_offset
|
|
||||||
|
|
||||||
def replace(self, filename, arcname=None, compress_type=None):
|
|
||||||
"""Delete arcname, and put the bytes from filename into the
|
|
||||||
archive under the name arcname."""
|
|
||||||
deleteName = arcname
|
|
||||||
if deleteName is None:
|
|
||||||
deleteName = filename
|
|
||||||
self.delete(deleteName)
|
|
||||||
self.write(filename, arcname, compress_type)
|
|
||||||
|
|
||||||
def replacestr(self, zinfo, bytes):
|
|
||||||
"""Delete zinfo.filename, and write a new file into the archive. The
|
|
||||||
contents is the string 'bytes'."""
|
|
||||||
self.delete(zinfo.filename)
|
|
||||||
self.writestr(zinfo, bytes)
|
|
||||||
|
|
||||||
def delete(self, name):
|
def _calculate_file_offsets(self):
|
||||||
"""Delete the file from the archive. If it appears multiple
|
for zip_info in self.filelist:
|
||||||
times only the first instance will be deleted."""
|
self.fp.seek(zip_info.header_offset, 0)
|
||||||
for i in range (0, len(self.filelist)):
|
fheader = self.fp.read(30)
|
||||||
if self.filelist[i].filename == name:
|
if fheader[0:4] != stringFileHeader:
|
||||||
if self.debug:
|
raise BadZipfile, "Bad magic number for file header"
|
||||||
print "Removing", name
|
fheader = struct.unpack(structFileHeader, fheader)
|
||||||
deleted_offset = self.filelist[i].header_offset
|
# file_offset is computed here, since the extra field for
|
||||||
|
# the central directory and for the local file header
|
||||||
|
# refer to different fields, and they can have different
|
||||||
|
# lengths
|
||||||
|
file_offset = (zip_info.header_offset + 30
|
||||||
|
+ fheader[_FH_FILENAME_LENGTH]
|
||||||
|
+ fheader[_FH_EXTRA_FIELD_LENGTH])
|
||||||
|
fname = self.fp.read(fheader[_FH_FILENAME_LENGTH])
|
||||||
|
if fname != zip_info.orig_filename:
|
||||||
|
raise RuntimeError(
|
||||||
|
'File name in directory "%s" and header "%s" differ.' % (
|
||||||
|
zip_info.orig_filename, fname))
|
||||||
|
|
||||||
|
zip_info.file_offset = file_offset
|
||||||
|
|
||||||
|
def replace(self, filename, arcname=None, compress_type=None):
|
||||||
|
"""Delete arcname, and put the bytes from filename into the
|
||||||
|
archive under the name arcname."""
|
||||||
|
deleteName = arcname
|
||||||
|
if deleteName is None:
|
||||||
|
deleteName = filename
|
||||||
|
self.delete(deleteName)
|
||||||
|
self.write(filename, arcname, compress_type)
|
||||||
|
|
||||||
|
def replacestr(self, zinfo, bytes):
|
||||||
|
"""Delete zinfo.filename, and write a new file into the archive. The
|
||||||
|
contents is the string 'bytes'."""
|
||||||
|
self.delete(zinfo.filename)
|
||||||
|
self.writestr(zinfo, bytes)
|
||||||
|
|
||||||
|
def delete(self, name):
|
||||||
|
"""Delete the file from the archive. If it appears multiple
|
||||||
|
times only the first instance will be deleted."""
|
||||||
|
for i in range (0, len(self.filelist)):
|
||||||
|
if self.filelist[i].filename == name:
|
||||||
|
if self.debug:
|
||||||
|
print "Removing", name
|
||||||
|
deleted_offset = self.filelist[i].header_offset
|
||||||
deleted_size = (self.filelist[i].file_offset - self.filelist[i].header_offset) + self.filelist[i].compress_size
|
deleted_size = (self.filelist[i].file_offset - self.filelist[i].header_offset) + self.filelist[i].compress_size
|
||||||
zinfo_size = struct.calcsize(structCentralDir) + len(self.filelist[i].filename) + len(self.filelist[i].extra)
|
zinfo_size = struct.calcsize(structCentralDir) + len(self.filelist[i].filename) + len(self.filelist[i].extra)
|
||||||
# Remove the file's data from the archive.
|
# Remove the file's data from the archive.
|
||||||
current_offset = self.fp.tell()
|
current_offset = self.fp.tell()
|
||||||
self.fp.seek(0, 2)
|
self.fp.seek(0, 2)
|
||||||
archive_size = self.fp.tell()
|
archive_size = self.fp.tell()
|
||||||
self.fp.seek(deleted_offset + deleted_size)
|
self.fp.seek(deleted_offset + deleted_size)
|
||||||
buf = self.fp.read()
|
buf = self.fp.read()
|
||||||
self.fp.seek(deleted_offset)
|
self.fp.seek(deleted_offset)
|
||||||
self.fp.write(buf)
|
self.fp.write(buf)
|
||||||
self.fp.truncate(archive_size - deleted_size - zinfo_size)
|
self.fp.truncate(archive_size - deleted_size - zinfo_size)
|
||||||
if current_offset > deleted_offset + deleted_size:
|
if current_offset > deleted_offset + deleted_size:
|
||||||
current_offset -= deleted_size
|
current_offset -= deleted_size
|
||||||
elif current_offset > deleted_offset:
|
elif current_offset > deleted_offset:
|
||||||
current_offset = deleted_offset
|
current_offset = deleted_offset
|
||||||
self.fp.seek(current_offset, 0)
|
self.fp.seek(current_offset, 0)
|
||||||
# Remove file from central directory.
|
# Remove file from central directory.
|
||||||
del self.filelist[i]
|
del self.filelist[i]
|
||||||
# Adjust the remaining offsets in the central directory.
|
# Adjust the remaining offsets in the central directory.
|
||||||
for j in range (i, len(self.filelist)):
|
for j in range (i, len(self.filelist)):
|
||||||
if self.filelist[j].header_offset > deleted_offset:
|
if self.filelist[j].header_offset > deleted_offset:
|
||||||
self.filelist[j].header_offset -= deleted_size
|
self.filelist[j].header_offset -= deleted_size
|
||||||
if self.filelist[j].file_offset > deleted_offset:
|
if self.filelist[j].file_offset > deleted_offset:
|
||||||
self.filelist[j].file_offset -= deleted_size
|
self.filelist[j].file_offset -= deleted_size
|
||||||
self._didModify = True
|
self._didModify = True
|
||||||
return
|
return
|
||||||
if self.debug:
|
if self.debug:
|
||||||
print name, "not in archive"
|
print name, "not in archive"
|
||||||
|
|
||||||
def namelist(self):
|
def namelist(self):
|
||||||
"""Return a list of file names in the archive."""
|
"""Return a list of file names in the archive."""
|
||||||
@ -1035,10 +1036,14 @@ class ZipFile:
|
|||||||
os.unlink(upperdirs)
|
os.unlink(upperdirs)
|
||||||
if upperdirs and not os.path.exists(upperdirs):
|
if upperdirs and not os.path.exists(upperdirs):
|
||||||
os.makedirs(upperdirs)
|
os.makedirs(upperdirs)
|
||||||
|
|
||||||
source = self.open(member, pwd=pwd)
|
source = self.open(member, pwd=pwd)
|
||||||
if not os.path.exists(targetpath): # Could be a previously automatically created directory
|
if not os.path.exists(targetpath): # Could be a previously automatically created directory
|
||||||
target = open(targetpath, "wb")
|
try:
|
||||||
|
target = open(targetpath, "wb")
|
||||||
|
except IOError:
|
||||||
|
targetpath = sanitize_file_name(targetpath)
|
||||||
|
target = open(targetpath, "wb")
|
||||||
shutil.copyfileobj(source, target)
|
shutil.copyfileobj(source, target)
|
||||||
source.close()
|
source.close()
|
||||||
target.close()
|
target.close()
|
||||||
@ -1179,7 +1184,7 @@ class ZipFile:
|
|||||||
zinfo.file_size))
|
zinfo.file_size))
|
||||||
self.filelist.append(zinfo)
|
self.filelist.append(zinfo)
|
||||||
self.NameToInfo[zinfo.filename] = zinfo
|
self.NameToInfo[zinfo.filename] = zinfo
|
||||||
|
|
||||||
def add_dir(self, path, prefix=''):
|
def add_dir(self, path, prefix=''):
|
||||||
'''
|
'''
|
||||||
Add a directory recursively to the zip file with an optional prefix.
|
Add a directory recursively to the zip file with an optional prefix.
|
||||||
@ -1195,10 +1200,10 @@ class ZipFile:
|
|||||||
if os.path.isdir(f):
|
if os.path.isdir(f):
|
||||||
self.add_dir(f, prefix=arcname)
|
self.add_dir(f, prefix=arcname)
|
||||||
else:
|
else:
|
||||||
self.write(f, arcname)
|
self.write(f, arcname)
|
||||||
finally:
|
finally:
|
||||||
os.chdir(cwd)
|
os.chdir(cwd)
|
||||||
|
|
||||||
|
|
||||||
def __del__(self):
|
def __del__(self):
|
||||||
"""Call the "close()" method in case the user forgot."""
|
"""Call the "close()" method in case the user forgot."""
|
||||||
@ -1294,7 +1299,7 @@ class ZipFile:
|
|||||||
if self.debug > 0:
|
if self.debug > 0:
|
||||||
msg = 'Archive comment is too long; truncating to %d bytes' \
|
msg = 'Archive comment is too long; truncating to %d bytes' \
|
||||||
% ZIP_MAX_COMMENT
|
% ZIP_MAX_COMMENT
|
||||||
print msg
|
print msg
|
||||||
self.comment = self.comment[:ZIP_MAX_COMMENT]
|
self.comment = self.comment[:ZIP_MAX_COMMENT]
|
||||||
|
|
||||||
endrec = struct.pack(structEndArchive, stringEndArchive,
|
endrec = struct.pack(structEndArchive, stringEndArchive,
|
||||||
@ -1314,7 +1319,7 @@ def safe_replace(zipstream, name, datastream):
|
|||||||
Replace a file in a zip file in a safe manner. This proceeds by extracting
|
Replace a file in a zip file in a safe manner. This proceeds by extracting
|
||||||
and re-creating the zipfile. This is neccessary because :method:`ZipFile.replace`
|
and re-creating the zipfile. This is neccessary because :method:`ZipFile.replace`
|
||||||
sometimes created corrupted zip files.
|
sometimes created corrupted zip files.
|
||||||
|
|
||||||
:param zipstream: Stream from a zip file
|
:param zipstream: Stream from a zip file
|
||||||
:param name: The name of the file to replace
|
:param name: The name of the file to replace
|
||||||
:param datastream: The data to replace the file with.
|
:param datastream: The data to replace the file with.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user