mirror of
https://github.com/noDRM/DeDRM_tools.git
synced 2026-03-31 00:38:58 +00:00
Compare commits
5 Commits
v10.0.3
...
41df9ecda0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
41df9ecda0 | ||
|
|
80cbaa4841 | ||
|
|
9a11f480b5 | ||
|
|
59839ae5c7 | ||
|
|
c15135b12f |
@@ -69,4 +69,8 @@ List of changes since the fork of Apprentice Harper's repository:
|
|||||||
|
|
||||||
## Fixes on master (not yet released):
|
## Fixes on master (not yet released):
|
||||||
|
|
||||||
- (None)
|
- 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.
|
||||||
|
- Fix PDF decryption issues on Calibre 4 (hopefully fixes #104).
|
||||||
@@ -511,10 +511,10 @@ class DeDRM(FileTypePlugin):
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
# Found matching key
|
# Found matching key
|
||||||
userkey = codecs.decode(userkeyhex, 'hex')
|
|
||||||
print("{0} v{1}: Trying UUID-matched encryption key {2:s}".format(PLUGIN_NAME, PLUGIN_VERSION, keyname))
|
print("{0} v{1}: Trying UUID-matched encryption key {2:s}".format(PLUGIN_NAME, PLUGIN_VERSION, keyname))
|
||||||
of = self.temporary_file(".epub")
|
of = self.temporary_file(".epub")
|
||||||
try:
|
try:
|
||||||
|
userkey = codecs.decode(userkeyhex, 'hex')
|
||||||
result = ineptepub.decryptBook(userkey, inf.name, of.name)
|
result = ineptepub.decryptBook(userkey, inf.name, of.name)
|
||||||
of.close()
|
of.close()
|
||||||
if result == 0:
|
if result == 0:
|
||||||
@@ -531,12 +531,13 @@ class DeDRM(FileTypePlugin):
|
|||||||
|
|
||||||
# Attempt to decrypt epub with each encryption key (generated or provided).
|
# Attempt to decrypt epub with each encryption key (generated or provided).
|
||||||
for keyname, userkeyhex in dedrmprefs['adeptkeys'].items():
|
for keyname, userkeyhex in dedrmprefs['adeptkeys'].items():
|
||||||
userkey = codecs.decode(userkeyhex, 'hex')
|
|
||||||
print("{0} v{1}: Trying Encryption key {2:s}".format(PLUGIN_NAME, PLUGIN_VERSION, keyname))
|
print("{0} v{1}: Trying Encryption key {2:s}".format(PLUGIN_NAME, PLUGIN_VERSION, keyname))
|
||||||
of = self.temporary_file(".epub")
|
of = self.temporary_file(".epub")
|
||||||
|
|
||||||
# Give the user key, ebook and TemporaryPersistent file to the decryption function.
|
# Give the user key, ebook and TemporaryPersistent file to the decryption function.
|
||||||
try:
|
try:
|
||||||
|
userkey = codecs.decode(userkeyhex, 'hex')
|
||||||
result = ineptepub.decryptBook(userkey, inf.name, of.name)
|
result = ineptepub.decryptBook(userkey, inf.name, of.name)
|
||||||
except ineptepub.ADEPTNewVersionError:
|
except ineptepub.ADEPTNewVersionError:
|
||||||
print("{0} v{1}: Book uses unsupported (too new) Adobe DRM.".format(PLUGIN_NAME, PLUGIN_VERSION, time.time()-self.starttime))
|
print("{0} v{1}: Book uses unsupported (too new) Adobe DRM.".format(PLUGIN_NAME, PLUGIN_VERSION, time.time()-self.starttime))
|
||||||
@@ -673,11 +674,11 @@ class DeDRM(FileTypePlugin):
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
# Found matching key
|
# Found matching key
|
||||||
userkey = codecs.decode(userkeyhex, 'hex')
|
|
||||||
print("{0} v{1}: Trying UUID-matched encryption key {2:s}".format(PLUGIN_NAME, PLUGIN_VERSION, keyname))
|
print("{0} v{1}: Trying UUID-matched encryption key {2:s}".format(PLUGIN_NAME, PLUGIN_VERSION, keyname))
|
||||||
of = self.temporary_file(".pdf")
|
of = self.temporary_file(".pdf")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
userkey = codecs.decode(userkeyhex, 'hex')
|
||||||
result = ineptpdf.decryptBook(userkey, path_to_ebook, of.name)
|
result = ineptpdf.decryptBook(userkey, path_to_ebook, of.name)
|
||||||
of.close()
|
of.close()
|
||||||
if result == 0:
|
if result == 0:
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import traceback
|
|||||||
import zlib
|
import zlib
|
||||||
import zipfile
|
import zipfile
|
||||||
from zipfile import ZipInfo, ZipFile, ZIP_STORED, ZIP_DEFLATED
|
from zipfile import ZipInfo, ZipFile, ZIP_STORED, ZIP_DEFLATED
|
||||||
|
from zeroedzipinfo import ZeroedZipInfo
|
||||||
from contextlib import closing
|
from contextlib import closing
|
||||||
from lxml import etree
|
from lxml import etree
|
||||||
import itertools
|
import itertools
|
||||||
@@ -298,13 +299,21 @@ def decryptFontsBook(inpath, outpath):
|
|||||||
zi.internal_attr = oldzi.internal_attr
|
zi.internal_attr = oldzi.internal_attr
|
||||||
# external attributes are dependent on the create system, so copy both.
|
# external attributes are dependent on the create system, so copy both.
|
||||||
zi.external_attr = oldzi.external_attr
|
zi.external_attr = oldzi.external_attr
|
||||||
|
zi.volume = oldzi.volume
|
||||||
zi.create_system = oldzi.create_system
|
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 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
|
# If the file name or the comment contains any non-ASCII char, set the UTF8-flag
|
||||||
zi.flag_bits |= 0x800
|
zi.flag_bits |= 0x800
|
||||||
except:
|
except:
|
||||||
pass
|
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":
|
if path == "mimetype":
|
||||||
outf.writestr(zi, inf.read('mimetype'))
|
outf.writestr(zi, inf.read('mimetype'))
|
||||||
elif path == "META-INF/encryption.xml":
|
elif path == "META-INF/encryption.xml":
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ Removes various watermarks from EPUB files
|
|||||||
|
|
||||||
import traceback
|
import traceback
|
||||||
from zipfile import ZipInfo, ZipFile, ZIP_STORED, ZIP_DEFLATED
|
from zipfile import ZipInfo, ZipFile, ZIP_STORED, ZIP_DEFLATED
|
||||||
|
from zeroedzipinfo import ZeroedZipInfo
|
||||||
from contextlib import closing
|
from contextlib import closing
|
||||||
from lxml import etree
|
from lxml import etree
|
||||||
import re
|
import re
|
||||||
@@ -133,13 +134,22 @@ def removeHTMLwatermarks(object, path_to_ebook):
|
|||||||
zi.extra = oldzi.extra
|
zi.extra = oldzi.extra
|
||||||
zi.internal_attr = oldzi.internal_attr
|
zi.internal_attr = oldzi.internal_attr
|
||||||
zi.external_attr = oldzi.external_attr
|
zi.external_attr = oldzi.external_attr
|
||||||
|
zi.volume = oldzi.volume
|
||||||
zi.create_system = oldzi.create_system
|
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 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
|
# If the file name or the comment contains any non-ASCII char, set the UTF8-flag
|
||||||
zi.flag_bits |= 0x800
|
zi.flag_bits |= 0x800
|
||||||
except:
|
except:
|
||||||
pass
|
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)
|
outf.writestr(zi, data)
|
||||||
except:
|
except:
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
@@ -249,13 +259,21 @@ def removeOPFwatermarks(object, path_to_ebook):
|
|||||||
zi.extra = oldzi.extra
|
zi.extra = oldzi.extra
|
||||||
zi.internal_attr = oldzi.internal_attr
|
zi.internal_attr = oldzi.internal_attr
|
||||||
zi.external_attr = oldzi.external_attr
|
zi.external_attr = oldzi.external_attr
|
||||||
|
zi.volume = oldzi.volume
|
||||||
zi.create_system = oldzi.create_system
|
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 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
|
# If the file name or the comment contains any non-ASCII char, set the UTF8-flag
|
||||||
zi.flag_bits |= 0x800
|
zi.flag_bits |= 0x800
|
||||||
except:
|
except:
|
||||||
pass
|
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)
|
outf.writestr(zi, data)
|
||||||
except:
|
except:
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
@@ -301,13 +319,21 @@ def removeCDPwatermark(object, path_to_ebook):
|
|||||||
zi.extra = oldzi.extra
|
zi.extra = oldzi.extra
|
||||||
zi.internal_attr = oldzi.internal_attr
|
zi.internal_attr = oldzi.internal_attr
|
||||||
zi.external_attr = oldzi.external_attr
|
zi.external_attr = oldzi.external_attr
|
||||||
|
zi.volume = oldzi.volume
|
||||||
zi.create_system = oldzi.create_system
|
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 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
|
# If the file name or the comment contains any non-ASCII char, set the UTF8-flag
|
||||||
zi.flag_bits |= 0x800
|
zi.flag_bits |= 0x800
|
||||||
except:
|
except:
|
||||||
pass
|
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)
|
outf.writestr(zi, data)
|
||||||
|
|
||||||
print("Watermark: Successfully removed cdp.info watermark")
|
print("Watermark: Successfully removed cdp.info watermark")
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ import base64
|
|||||||
import zlib
|
import zlib
|
||||||
import zipfile
|
import zipfile
|
||||||
from zipfile import ZipInfo, ZipFile, ZIP_STORED, ZIP_DEFLATED
|
from zipfile import ZipInfo, ZipFile, ZIP_STORED, ZIP_DEFLATED
|
||||||
|
from zeroedzipinfo import ZeroedZipInfo
|
||||||
from contextlib import closing
|
from contextlib import closing
|
||||||
from lxml import etree
|
from lxml import etree
|
||||||
from uuid import UUID
|
from uuid import UUID
|
||||||
@@ -302,7 +303,7 @@ def decryptBook(userkey, inpath, outpath):
|
|||||||
|
|
||||||
if len(bookkey) != 64:
|
if len(bookkey) != 64:
|
||||||
# Normal or "hardened" Adobe ADEPT
|
# Normal or "hardened" Adobe ADEPT
|
||||||
rsakey = RSA.import_key(userkey) # parses the ASN1 structure
|
rsakey = RSA.importKey(userkey) # parses the ASN1 structure
|
||||||
bookkey = base64.b64decode(bookkey)
|
bookkey = base64.b64decode(bookkey)
|
||||||
if int(keytype, 10) > 2:
|
if int(keytype, 10) > 2:
|
||||||
bookkey = removeHardening(rights, keytype, bookkey)
|
bookkey = removeHardening(rights, keytype, bookkey)
|
||||||
@@ -356,12 +357,23 @@ def decryptBook(userkey, inpath, outpath):
|
|||||||
zi.internal_attr = oldzi.internal_attr
|
zi.internal_attr = oldzi.internal_attr
|
||||||
# external attributes are dependent on the create system, so copy both.
|
# external attributes are dependent on the create system, so copy both.
|
||||||
zi.external_attr = oldzi.external_attr
|
zi.external_attr = oldzi.external_attr
|
||||||
|
|
||||||
|
zi.volume = oldzi.volume
|
||||||
zi.create_system = oldzi.create_system
|
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 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
|
# If the file name or the comment contains any non-ASCII char, set the UTF8-flag
|
||||||
zi.flag_bits |= 0x800
|
zi.flag_bits |= 0x800
|
||||||
except:
|
except:
|
||||||
pass
|
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":
|
if path == "META-INF/encryption.xml":
|
||||||
outf.writestr(zi, data)
|
outf.writestr(zi, data)
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -51,13 +51,14 @@
|
|||||||
# 9.1.1 - Only support PyCryptodome; clean up the code
|
# 9.1.1 - Only support PyCryptodome; clean up the code
|
||||||
# 10.0.0 - Add support for "hardened" Adobe DRM (RMSDK >= 10)
|
# 10.0.0 - Add support for "hardened" Adobe DRM (RMSDK >= 10)
|
||||||
# 10.0.2 - Fix some Python2 stuff
|
# 10.0.2 - Fix some Python2 stuff
|
||||||
|
# 10.0.4 - Fix more Python2 stuff
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Decrypts Adobe ADEPT-encrypted PDF files.
|
Decrypts Adobe ADEPT-encrypted PDF files.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
__license__ = 'GPL v3'
|
__license__ = 'GPL v3'
|
||||||
__version__ = "10.0.2"
|
__version__ = "10.0.4"
|
||||||
|
|
||||||
import codecs
|
import codecs
|
||||||
import hashlib
|
import hashlib
|
||||||
@@ -200,7 +201,10 @@ def nunpack(s, default=0):
|
|||||||
elif l == 2:
|
elif l == 2:
|
||||||
return struct.unpack('>H', s)[0]
|
return struct.unpack('>H', s)[0]
|
||||||
elif l == 3:
|
elif l == 3:
|
||||||
return struct.unpack('>L', bytes([0]) + s)[0]
|
if sys.version_info[0] == 2:
|
||||||
|
return struct.unpack('>L', '\x00'+s)[0]
|
||||||
|
else:
|
||||||
|
return struct.unpack('>L', bytes([0]) + s)[0]
|
||||||
elif l == 4:
|
elif l == 4:
|
||||||
return struct.unpack('>L', s)[0]
|
return struct.unpack('>L', s)[0]
|
||||||
else:
|
else:
|
||||||
@@ -459,7 +463,10 @@ class PSBaseParser(object):
|
|||||||
self.hex += c
|
self.hex += c
|
||||||
return (self.parse_literal_hex, i+1)
|
return (self.parse_literal_hex, i+1)
|
||||||
if self.hex:
|
if self.hex:
|
||||||
self.token += bytes([int(self.hex, 16)])
|
if sys.version_info[0] == 2:
|
||||||
|
self.token += chr(int(self.hex, 16))
|
||||||
|
else:
|
||||||
|
self.token += bytes([int(self.hex, 16)])
|
||||||
return (self.parse_literal, i)
|
return (self.parse_literal, i)
|
||||||
|
|
||||||
def parse_number(self, s, i):
|
def parse_number(self, s, i):
|
||||||
@@ -543,10 +550,18 @@ class PSBaseParser(object):
|
|||||||
self.oct += c
|
self.oct += c
|
||||||
return (self.parse_string_1, i+1)
|
return (self.parse_string_1, i+1)
|
||||||
if self.oct:
|
if self.oct:
|
||||||
self.token += bytes([int(self.oct, 8)])
|
if sys.version_info[0] == 2:
|
||||||
|
self.token += chr(int(self.oct, 8))
|
||||||
|
else:
|
||||||
|
self.token += bytes([int(self.oct, 8)])
|
||||||
return (self.parse_string, i)
|
return (self.parse_string, i)
|
||||||
if c in ESC_STRING:
|
if c in ESC_STRING:
|
||||||
self.token += bytes([ESC_STRING[c]])
|
|
||||||
|
if sys.version_info[0] == 2:
|
||||||
|
self.token += chr(ESC_STRING[c])
|
||||||
|
else:
|
||||||
|
self.token += bytes([ESC_STRING[c]])
|
||||||
|
|
||||||
return (self.parse_string, i+1)
|
return (self.parse_string, i+1)
|
||||||
|
|
||||||
def parse_wopen(self, s, i):
|
def parse_wopen(self, s, i):
|
||||||
@@ -572,14 +587,19 @@ class PSBaseParser(object):
|
|||||||
return (self.parse_main, i)
|
return (self.parse_main, i)
|
||||||
|
|
||||||
def parse_hexstring(self, s, i):
|
def parse_hexstring(self, s, i):
|
||||||
m1 = END_HEX_STRING.search(s, i)
|
m = END_HEX_STRING.search(s, i)
|
||||||
if not m1:
|
if not m:
|
||||||
self.token += s[i:]
|
self.token += s[i:]
|
||||||
return (self.parse_hexstring, len(s))
|
return (self.parse_hexstring, len(s))
|
||||||
j = m1.start(0)
|
j = m.start(0)
|
||||||
self.token += s[i:j]
|
self.token += s[i:j]
|
||||||
token = HEX_PAIR.sub(lambda m2: bytes([int(m2.group(0), 16)]),
|
if sys.version_info[0] == 2:
|
||||||
|
token = HEX_PAIR.sub(lambda m: chr(int(m.group(0), 16)),
|
||||||
|
SPC.sub('', self.token))
|
||||||
|
else:
|
||||||
|
token = HEX_PAIR.sub(lambda m: bytes([int(m.group(0), 16)]),
|
||||||
SPC.sub(b'', self.token))
|
SPC.sub(b'', self.token))
|
||||||
|
|
||||||
self.add_token(token)
|
self.add_token(token)
|
||||||
return (self.parse_main, j)
|
return (self.parse_main, j)
|
||||||
|
|
||||||
@@ -1669,7 +1689,7 @@ class PDFDocument(object):
|
|||||||
|
|
||||||
def initialize_ebx_inept(self, password, docid, param):
|
def initialize_ebx_inept(self, password, docid, param):
|
||||||
self.is_printable = self.is_modifiable = self.is_extractable = True
|
self.is_printable = self.is_modifiable = self.is_extractable = True
|
||||||
rsakey = RSA.import_key(password) # parses the ASN1 structure
|
rsakey = RSA.importKey(password) # parses the ASN1 structure
|
||||||
length = int_value(param.get('Length', 0)) // 8
|
length = int_value(param.get('Length', 0)) // 8
|
||||||
rights = codecs.decode(param.get('ADEPT_LICENSE'), 'base64')
|
rights = codecs.decode(param.get('ADEPT_LICENSE'), 'base64')
|
||||||
rights = zlib.decompress(rights, -15)
|
rights = zlib.decompress(rights, -15)
|
||||||
|
|||||||
30
DeDRM_plugin/zeroedzipinfo.py
Normal file
30
DeDRM_plugin/zeroedzipinfo.py
Normal file
@@ -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:]
|
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 _ZipDecrypter:
|
||||||
"""Class to handle decryption of files stored within a ZIP archive.
|
"""Class to handle decryption of files stored within a ZIP archive.
|
||||||
|
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import sys, os
|
|||||||
|
|
||||||
import zlib
|
import zlib
|
||||||
import zipfilerugged
|
import zipfilerugged
|
||||||
|
from zipfilerugged import ZipInfo, ZeroedZipInfo
|
||||||
import getopt
|
import getopt
|
||||||
from struct import unpack
|
from struct import unpack
|
||||||
|
|
||||||
@@ -36,12 +37,6 @@ _FILENAME_OFFSET = 30
|
|||||||
_MAX_SIZE = 64 * 1024
|
_MAX_SIZE = 64 * 1024
|
||||||
_MIMETYPE = 'application/epub+zip'
|
_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:
|
class fixZip:
|
||||||
def __init__(self, zinput, zoutput):
|
def __init__(self, zinput, zoutput):
|
||||||
@@ -117,7 +112,8 @@ class fixZip:
|
|||||||
# if epub write mimetype file first, with no compression
|
# if epub write mimetype file first, with no compression
|
||||||
if self.ztype == 'epub':
|
if self.ztype == 'epub':
|
||||||
# first get a ZipInfo with current time and no compression
|
# 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
|
mimeinfo.internal_attr = 1 # text file
|
||||||
try:
|
try:
|
||||||
# if the mimetype is present, get its info, including time-stamp
|
# if the mimetype is present, get its info, including time-stamp
|
||||||
@@ -129,8 +125,16 @@ class fixZip:
|
|||||||
mimeinfo.internal_attr = oldmimeinfo.internal_attr
|
mimeinfo.internal_attr = oldmimeinfo.internal_attr
|
||||||
mimeinfo.external_attr = oldmimeinfo.external_attr
|
mimeinfo.external_attr = oldmimeinfo.external_attr
|
||||||
mimeinfo.create_system = oldmimeinfo.create_system
|
mimeinfo.create_system = oldmimeinfo.create_system
|
||||||
|
mimeinfo.create_version = oldmimeinfo.create_version
|
||||||
|
mimeinfo.volume = oldmimeinfo.volume
|
||||||
except:
|
except:
|
||||||
pass
|
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'))
|
self.outzip.writestr(mimeinfo, _MIMETYPE.encode('ascii'))
|
||||||
|
|
||||||
# write the rest of the files
|
# write the rest of the files
|
||||||
@@ -145,13 +149,23 @@ class fixZip:
|
|||||||
zinfo.filename = local_name
|
zinfo.filename = local_name
|
||||||
|
|
||||||
# create new ZipInfo with only the useful attributes from the old info
|
# 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.comment=zinfo.comment
|
||||||
nzinfo.extra=zinfo.extra
|
nzinfo.extra=zinfo.extra
|
||||||
nzinfo.internal_attr=zinfo.internal_attr
|
nzinfo.internal_attr=zinfo.internal_attr
|
||||||
nzinfo.external_attr=zinfo.external_attr
|
nzinfo.external_attr=zinfo.external_attr
|
||||||
nzinfo.create_system=zinfo.create_system
|
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
|
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.outzip.writestr(nzinfo,data)
|
||||||
|
|
||||||
self.bzf.close()
|
self.bzf.close()
|
||||||
|
|||||||
@@ -265,13 +265,13 @@ class ReadOnlyTableWidgetItem(QTableWidgetItem):
|
|||||||
def __init__(self, text):
|
def __init__(self, text):
|
||||||
if text is None:
|
if text is None:
|
||||||
text = ''
|
text = ''
|
||||||
QTableWidgetItem.__init__(self, text, QTableWidgetItem.UserType)
|
QTableWidgetItem.__init__(self, text, QTableWidgetItem.ItemType.UserType)
|
||||||
self.setFlags(Qt.ItemIsSelectable|Qt.ItemIsEnabled)
|
self.setFlags(Qt.ItemIsSelectable|Qt.ItemIsEnabled)
|
||||||
|
|
||||||
class RatingTableWidgetItem(QTableWidgetItem):
|
class RatingTableWidgetItem(QTableWidgetItem):
|
||||||
|
|
||||||
def __init__(self, rating, is_read_only=False):
|
def __init__(self, rating, is_read_only=False):
|
||||||
QTableWidgetItem.__init__(self, '', QTableWidgetItem.UserType)
|
QTableWidgetItem.__init__(self, '', QTableWidgetItem.ItemType.UserType)
|
||||||
self.setData(Qt.DisplayRole, rating)
|
self.setData(Qt.DisplayRole, rating)
|
||||||
if is_read_only:
|
if is_read_only:
|
||||||
self.setFlags(Qt.ItemIsSelectable|Qt.ItemIsEnabled)
|
self.setFlags(Qt.ItemIsSelectable|Qt.ItemIsEnabled)
|
||||||
@@ -284,11 +284,11 @@ class DateTableWidgetItem(QTableWidgetItem):
|
|||||||
if date_read is None or date_read == UNDEFINED_DATE and default_to_today:
|
if date_read is None or date_read == UNDEFINED_DATE and default_to_today:
|
||||||
date_read = now()
|
date_read = now()
|
||||||
if is_read_only:
|
if is_read_only:
|
||||||
QTableWidgetItem.__init__(self, format_date(date_read, fmt), QTableWidgetItem.UserType)
|
QTableWidgetItem.__init__(self, format_date(date_read, fmt), QTableWidgetItem.ItemType.UserType)
|
||||||
self.setFlags(Qt.ItemIsSelectable|Qt.ItemIsEnabled)
|
self.setFlags(Qt.ItemIsSelectable|Qt.ItemIsEnabled)
|
||||||
self.setData(Qt.DisplayRole, QDateTime(date_read))
|
self.setData(Qt.DisplayRole, QDateTime(date_read))
|
||||||
else:
|
else:
|
||||||
QTableWidgetItem.__init__(self, '', QTableWidgetItem.UserType)
|
QTableWidgetItem.__init__(self, '', QTableWidgetItem.ItemType.UserType)
|
||||||
self.setData(Qt.DisplayRole, QDateTime(date_read))
|
self.setData(Qt.DisplayRole, QDateTime(date_read))
|
||||||
|
|
||||||
from calibre.gui2.library.delegates import DateDelegate as _DateDelegate
|
from calibre.gui2.library.delegates import DateDelegate as _DateDelegate
|
||||||
|
|||||||
@@ -224,5 +224,5 @@ class ReadOnlyTableWidgetItem(QTableWidgetItem):
|
|||||||
def __init__(self, text):
|
def __init__(self, text):
|
||||||
if text is None:
|
if text is None:
|
||||||
text = ''
|
text = ''
|
||||||
QTableWidgetItem.__init__(self, text, QTableWidgetItem.UserType)
|
QTableWidgetItem.__init__(self, text, QTableWidgetItem.ItemType.UserType)
|
||||||
self.setFlags(Qt.ItemIsSelectable|Qt.ItemIsEnabled)
|
self.setFlags(Qt.ItemIsSelectable|Qt.ItemIsEnabled)
|
||||||
|
|||||||
Reference in New Issue
Block a user