summaryrefslogtreecommitdiffstats
path: root/DeDRM_plugin
diff options
context:
space:
mode:
authorNoDRM <[email protected]>2022-08-06 13:53:03 +0200
committerNoDRM <[email protected]>2022-08-06 13:53:03 +0200
commit80cbaa4841622ffb4605f53107cc0bad0801ef35 (patch)
treeafa8cc11a2c675e78865894ef97f7cbab3957114 /DeDRM_plugin
parent9a11f480b527e3d1064b890b1b5043e1c5363957 (diff)
Fix ZIP attribute "external_attr" getting reset
Diffstat (limited to 'DeDRM_plugin')
-rw-r--r--DeDRM_plugin/epubfontdecrypt.py9
-rw-r--r--DeDRM_plugin/epubwatermark.py26
-rw-r--r--DeDRM_plugin/ineptepub.py12
-rw-r--r--DeDRM_plugin/zeroedzipinfo.py30
-rwxr-xr-xDeDRM_plugin/zipfilerugged.py13
-rw-r--r--DeDRM_plugin/zipfix.py30
6 files changed, 112 insertions, 8 deletions
diff --git a/DeDRM_plugin/epubfontdecrypt.py b/DeDRM_plugin/epubfontdecrypt.py
index ea08175..4baa375 100644
--- a/DeDRM_plugin/epubfontdecrypt.py
+++ b/DeDRM_plugin/epubfontdecrypt.py
@@ -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":
diff --git a/DeDRM_plugin/epubwatermark.py b/DeDRM_plugin/epubwatermark.py
index 176c77f..6719935 100644
--- a/DeDRM_plugin/epubwatermark.py
+++ b/DeDRM_plugin/epubwatermark.py
@@ -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")
diff --git a/DeDRM_plugin/ineptepub.py b/DeDRM_plugin/ineptepub.py
index 6b4b676..094cb8c 100644
--- a/DeDRM_plugin/ineptepub.py
+++ b/DeDRM_plugin/ineptepub.py
@@ -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:
diff --git a/DeDRM_plugin/zeroedzipinfo.py b/DeDRM_plugin/zeroedzipinfo.py
new file mode 100644
index 0000000..08c65d0
--- /dev/null
+++ b/DeDRM_plugin/zeroedzipinfo.py
@@ -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)
diff --git a/DeDRM_plugin/zipfilerugged.py b/DeDRM_plugin/zipfilerugged.py
index aef9ea3..1941cc0 100755
--- a/DeDRM_plugin/zipfilerugged.py
+++ b/DeDRM_plugin/zipfilerugged.py
@@ -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.
diff --git a/DeDRM_plugin/zipfix.py b/DeDRM_plugin/zipfix.py
index 3fbfbce..9cb4ff1 100644
--- a/DeDRM_plugin/zipfix.py
+++ b/DeDRM_plugin/zipfix.py
@@ -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()