Fix ZIP attribute "external_attr" getting reset
This commit is contained in:
parent
9a11f480b5
commit
80cbaa4841
|
@ -72,3 +72,4 @@ List of changes since the fork of Apprentice Harper's repository:
|
|||
- Fix a bug introduced with #48 that breaks DeDRM'ing on Calibre 4 (fixes #101).
|
||||
- Fix some more Calibre-6 bugs in the Obok plugin (should fix #114).
|
||||
- Fix a bug where invalid Adobe keys could cause the plugin to stop trying subsequent keys (partially fixes #109).
|
||||
- Fix DRM removal sometimes resetting the ZIP's internal "external_attr" value on Calibre 5 and newer.
|
||||
|
|
|
@ -25,6 +25,7 @@ import traceback
|
|||
import zlib
|
||||
import zipfile
|
||||
from zipfile import ZipInfo, ZipFile, ZIP_STORED, ZIP_DEFLATED
|
||||
from zeroedzipinfo import ZeroedZipInfo
|
||||
from contextlib import closing
|
||||
from lxml import etree
|
||||
import itertools
|
||||
|
@ -298,13 +299,21 @@ def decryptFontsBook(inpath, outpath):
|
|||
zi.internal_attr = oldzi.internal_attr
|
||||
# external attributes are dependent on the create system, so copy both.
|
||||
zi.external_attr = oldzi.external_attr
|
||||
zi.volume = oldzi.volume
|
||||
zi.create_system = oldzi.create_system
|
||||
zi.create_version = oldzi.create_version
|
||||
|
||||
if any(ord(c) >= 128 for c in path) or any(ord(c) >= 128 for c in zi.comment):
|
||||
# If the file name or the comment contains any non-ASCII char, set the UTF8-flag
|
||||
zi.flag_bits |= 0x800
|
||||
except:
|
||||
pass
|
||||
|
||||
# Python 3 has a bug where the external_attr is reset to `0o600 << 16`
|
||||
# if it's NULL, so we need a workaround:
|
||||
if zi.external_attr == 0:
|
||||
zi = ZeroedZipInfo(zi)
|
||||
|
||||
if path == "mimetype":
|
||||
outf.writestr(zi, inf.read('mimetype'))
|
||||
elif path == "META-INF/encryption.xml":
|
||||
|
|
|
@ -16,6 +16,7 @@ Removes various watermarks from EPUB files
|
|||
|
||||
import traceback
|
||||
from zipfile import ZipInfo, ZipFile, ZIP_STORED, ZIP_DEFLATED
|
||||
from zeroedzipinfo import ZeroedZipInfo
|
||||
from contextlib import closing
|
||||
from lxml import etree
|
||||
import re
|
||||
|
@ -133,13 +134,22 @@ def removeHTMLwatermarks(object, path_to_ebook):
|
|||
zi.extra = oldzi.extra
|
||||
zi.internal_attr = oldzi.internal_attr
|
||||
zi.external_attr = oldzi.external_attr
|
||||
zi.volume = oldzi.volume
|
||||
zi.create_system = oldzi.create_system
|
||||
zi.create_version = oldzi.create_version
|
||||
|
||||
if any(ord(c) >= 128 for c in path) or any(ord(c) >= 128 for c in zi.comment):
|
||||
# If the file name or the comment contains any non-ASCII char, set the UTF8-flag
|
||||
zi.flag_bits |= 0x800
|
||||
except:
|
||||
pass
|
||||
|
||||
# Python 3 has a bug where the external_attr is reset to `0o600 << 16`
|
||||
# if it's NULL, so we need a workaround:
|
||||
if zi.external_attr == 0:
|
||||
zi = ZeroedZipInfo(zi)
|
||||
|
||||
|
||||
outf.writestr(zi, data)
|
||||
except:
|
||||
traceback.print_exc()
|
||||
|
@ -249,13 +259,21 @@ def removeOPFwatermarks(object, path_to_ebook):
|
|||
zi.extra = oldzi.extra
|
||||
zi.internal_attr = oldzi.internal_attr
|
||||
zi.external_attr = oldzi.external_attr
|
||||
zi.volume = oldzi.volume
|
||||
zi.create_system = oldzi.create_system
|
||||
zi.create_version = oldzi.create_version
|
||||
|
||||
if any(ord(c) >= 128 for c in path) or any(ord(c) >= 128 for c in zi.comment):
|
||||
# If the file name or the comment contains any non-ASCII char, set the UTF8-flag
|
||||
zi.flag_bits |= 0x800
|
||||
except:
|
||||
pass
|
||||
|
||||
# Python 3 has a bug where the external_attr is reset to `0o600 << 16`
|
||||
# if it's NULL, so we need a workaround:
|
||||
if zi.external_attr == 0:
|
||||
zi = ZeroedZipInfo(zi)
|
||||
|
||||
outf.writestr(zi, data)
|
||||
except:
|
||||
traceback.print_exc()
|
||||
|
@ -301,13 +319,21 @@ def removeCDPwatermark(object, path_to_ebook):
|
|||
zi.extra = oldzi.extra
|
||||
zi.internal_attr = oldzi.internal_attr
|
||||
zi.external_attr = oldzi.external_attr
|
||||
zi.volume = oldzi.volume
|
||||
zi.create_system = oldzi.create_system
|
||||
zi.create_version = oldzi.create_version
|
||||
|
||||
if any(ord(c) >= 128 for c in path) or any(ord(c) >= 128 for c in zi.comment):
|
||||
# If the file name or the comment contains any non-ASCII char, set the UTF8-flag
|
||||
zi.flag_bits |= 0x800
|
||||
except:
|
||||
pass
|
||||
|
||||
# Python 3 has a bug where the external_attr is reset to `0o600 << 16`
|
||||
# if it's NULL, so we need a workaround:
|
||||
if zi.external_attr == 0:
|
||||
zi = ZeroedZipInfo(zi)
|
||||
|
||||
outf.writestr(zi, data)
|
||||
|
||||
print("Watermark: Successfully removed cdp.info watermark")
|
||||
|
|
|
@ -48,6 +48,7 @@ import base64
|
|||
import zlib
|
||||
import zipfile
|
||||
from zipfile import ZipInfo, ZipFile, ZIP_STORED, ZIP_DEFLATED
|
||||
from zeroedzipinfo import ZeroedZipInfo
|
||||
from contextlib import closing
|
||||
from lxml import etree
|
||||
from uuid import UUID
|
||||
|
@ -356,12 +357,23 @@ def decryptBook(userkey, inpath, outpath):
|
|||
zi.internal_attr = oldzi.internal_attr
|
||||
# external attributes are dependent on the create system, so copy both.
|
||||
zi.external_attr = oldzi.external_attr
|
||||
|
||||
zi.volume = oldzi.volume
|
||||
zi.create_system = oldzi.create_system
|
||||
zi.create_version = oldzi.create_version
|
||||
|
||||
if any(ord(c) >= 128 for c in path) or any(ord(c) >= 128 for c in zi.comment):
|
||||
# If the file name or the comment contains any non-ASCII char, set the UTF8-flag
|
||||
zi.flag_bits |= 0x800
|
||||
except:
|
||||
pass
|
||||
|
||||
# Python 3 has a bug where the external_attr is reset to `0o600 << 16`
|
||||
# if it's NULL, so we need a workaround:
|
||||
if zi.external_attr == 0:
|
||||
zi = ZeroedZipInfo(zi)
|
||||
|
||||
|
||||
if path == "META-INF/encryption.xml":
|
||||
outf.writestr(zi, data)
|
||||
else:
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
|
||||
"""
|
||||
Python 3's "zipfile" has an annoying bug where the `external_attr` field
|
||||
of a ZIP file cannot be set to 0. However, if the original DRMed ZIP has
|
||||
that set to 0 then we want the DRM-free ZIP to have that as 0, too.
|
||||
See https://github.com/python/cpython/issues/87713
|
||||
|
||||
We cannot just set the "external_attr" to 0 as the code to save the ZIP
|
||||
resets that variable.
|
||||
|
||||
So, here's a class that inherits from ZipInfo and ensures that EVERY
|
||||
read access to that variable will return a 0 ...
|
||||
|
||||
"""
|
||||
|
||||
import zipfile
|
||||
|
||||
class ZeroedZipInfo(zipfile.ZipInfo):
|
||||
def __init__(self, zinfo):
|
||||
for k in self.__slots__:
|
||||
if hasattr(zinfo, k):
|
||||
setattr(self, k, getattr(zinfo, k))
|
||||
|
||||
def __getattribute__(self, name):
|
||||
if name == "external_attr":
|
||||
return 0
|
||||
return object.__getattribute__(self, name)
|
|
@ -394,6 +394,19 @@ class ZipInfo (object):
|
|||
extra = extra[ln+4:]
|
||||
|
||||
|
||||
class ZeroedZipInfo(ZipInfo):
|
||||
def __init__(self, zinfo):
|
||||
for k in self.__slots__:
|
||||
if hasattr(zinfo, k):
|
||||
setattr(self, k, getattr(zinfo, k))
|
||||
|
||||
def __getattribute__(self, name):
|
||||
if name == "external_attr":
|
||||
return 0
|
||||
return object.__getattribute__(self, name)
|
||||
|
||||
|
||||
|
||||
class _ZipDecrypter:
|
||||
"""Class to handle decryption of files stored within a ZIP archive.
|
||||
|
||||
|
|
|
@ -26,6 +26,7 @@ import sys, os
|
|||
|
||||
import zlib
|
||||
import zipfilerugged
|
||||
from zipfilerugged import ZipInfo, ZeroedZipInfo
|
||||
import getopt
|
||||
from struct import unpack
|
||||
|
||||
|
@ -36,12 +37,6 @@ _FILENAME_OFFSET = 30
|
|||
_MAX_SIZE = 64 * 1024
|
||||
_MIMETYPE = 'application/epub+zip'
|
||||
|
||||
class ZipInfo(zipfilerugged.ZipInfo):
|
||||
def __init__(self, *args, **kwargs):
|
||||
if 'compress_type' in kwargs:
|
||||
compress_type = kwargs.pop('compress_type')
|
||||
super(ZipInfo, self).__init__(*args, **kwargs)
|
||||
self.compress_type = compress_type
|
||||
|
||||
class fixZip:
|
||||
def __init__(self, zinput, zoutput):
|
||||
|
@ -117,7 +112,8 @@ class fixZip:
|
|||
# if epub write mimetype file first, with no compression
|
||||
if self.ztype == 'epub':
|
||||
# first get a ZipInfo with current time and no compression
|
||||
mimeinfo = ZipInfo(b'mimetype',compress_type=zipfilerugged.ZIP_STORED)
|
||||
mimeinfo = ZipInfo(b'mimetype')
|
||||
mimeinfo.compress_type = zipfilerugged.ZIP_STORED
|
||||
mimeinfo.internal_attr = 1 # text file
|
||||
try:
|
||||
# if the mimetype is present, get its info, including time-stamp
|
||||
|
@ -129,8 +125,16 @@ class fixZip:
|
|||
mimeinfo.internal_attr = oldmimeinfo.internal_attr
|
||||
mimeinfo.external_attr = oldmimeinfo.external_attr
|
||||
mimeinfo.create_system = oldmimeinfo.create_system
|
||||
mimeinfo.create_version = oldmimeinfo.create_version
|
||||
mimeinfo.volume = oldmimeinfo.volume
|
||||
except:
|
||||
pass
|
||||
|
||||
# Python 3 has a bug where the external_attr is reset to `0o600 << 16`
|
||||
# if it's NULL, so we need a workaround:
|
||||
if mimeinfo.external_attr == 0:
|
||||
mimeinfo = ZeroedZipInfo(mimeinfo)
|
||||
|
||||
self.outzip.writestr(mimeinfo, _MIMETYPE.encode('ascii'))
|
||||
|
||||
# write the rest of the files
|
||||
|
@ -145,13 +149,23 @@ class fixZip:
|
|||
zinfo.filename = local_name
|
||||
|
||||
# create new ZipInfo with only the useful attributes from the old info
|
||||
nzinfo = ZipInfo(zinfo.filename, zinfo.date_time, compress_type=zinfo.compress_type)
|
||||
nzinfo = ZipInfo(zinfo.filename)
|
||||
nzinfo.date_time = zinfo.date_time
|
||||
nzinfo.compress_type = zinfo.compress_type
|
||||
nzinfo.comment=zinfo.comment
|
||||
nzinfo.extra=zinfo.extra
|
||||
nzinfo.internal_attr=zinfo.internal_attr
|
||||
nzinfo.external_attr=zinfo.external_attr
|
||||
nzinfo.create_system=zinfo.create_system
|
||||
nzinfo.create_version = zinfo.create_version
|
||||
nzinfo.volume = zinfo.volume
|
||||
nzinfo.flag_bits = zinfo.flag_bits & 0x800 # preserve UTF-8 flag
|
||||
|
||||
# Python 3 has a bug where the external_attr is reset to `0o600 << 16`
|
||||
# if it's NULL, so we need a workaround:
|
||||
if nzinfo.external_attr == 0:
|
||||
nzinfo = ZeroedZipInfo(nzinfo)
|
||||
|
||||
self.outzip.writestr(nzinfo,data)
|
||||
|
||||
self.bzf.close()
|
||||
|
|
Loading…
Reference in New Issue