summaryrefslogtreecommitdiffstats
path: root/DeDRM_plugin/kfxdedrm.py
diff options
context:
space:
mode:
authorApprentice Harper <[email protected]>2020-02-16 10:12:25 +0000
committerApprentice Harper <[email protected]>2020-02-16 10:12:25 +0000
commit92bf51bc8f201a2d5b1e8b90b8dc033606dbcfb0 (patch)
tree037f09f49a7d93d6b71dc36db4fba96fa31c5603 /DeDRM_plugin/kfxdedrm.py
parentef3c7f261c9049e7ee861c62f308ed82c5d757fb (diff)
Remove stand-alone apps. Only support the two plugins.
Diffstat (limited to 'DeDRM_plugin/kfxdedrm.py')
-rw-r--r--DeDRM_plugin/kfxdedrm.py116
1 files changed, 116 insertions, 0 deletions
diff --git a/DeDRM_plugin/kfxdedrm.py b/DeDRM_plugin/kfxdedrm.py
new file mode 100644
index 0000000..1520f79
--- /dev/null
+++ b/DeDRM_plugin/kfxdedrm.py
@@ -0,0 +1,116 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+from __future__ import with_statement
+from __future__ import print_function
+
+# Engine to remove drm from Kindle KFX ebooks
+
+import os
+import shutil
+import zipfile
+
+try:
+ from cStringIO import StringIO
+except ImportError:
+ from StringIO import StringIO
+
+try:
+ from calibre_plugins.dedrm import ion
+except ImportError:
+ import ion
+
+
+__license__ = 'GPL v3'
+__version__ = '1.0'
+
+
+class KFXZipBook:
+ def __init__(self, infile):
+ self.infile = infile
+ self.voucher = None
+ self.decrypted = {}
+
+ def getPIDMetaInfo(self):
+ return (None, None)
+
+ def processBook(self, totalpids):
+ with zipfile.ZipFile(self.infile, 'r') as zf:
+ for filename in zf.namelist():
+ with zf.open(filename) as fh:
+ data = fh.read(8)
+ if data != '\xeaDRMION\xee':
+ continue
+ data += fh.read()
+ if self.voucher is None:
+ self.decrypt_voucher(totalpids)
+ print(u'Decrypting KFX DRMION: {0}'.format(filename))
+ outfile = StringIO()
+ ion.DrmIon(StringIO(data[8:-8]), lambda name: self.voucher).parse(outfile)
+ self.decrypted[filename] = outfile.getvalue()
+
+ if not self.decrypted:
+ print(u'The .kfx-zip archive does not contain an encrypted DRMION file')
+
+ def decrypt_voucher(self, totalpids):
+ with zipfile.ZipFile(self.infile, 'r') as zf:
+ for info in zf.infolist():
+ with zf.open(info.filename) as fh:
+ data = fh.read(4)
+ if data != '\xe0\x01\x00\xea':
+ continue
+
+ data += fh.read()
+ if 'ProtectedData' in data:
+ break # found DRM voucher
+ else:
+ raise Exception(u'The .kfx-zip archive contains an encrypted DRMION file without a DRM voucher')
+
+ print(u'Decrypting KFX DRM voucher: {0}'.format(info.filename))
+
+ for pid in [''] + totalpids:
+ for dsn_len,secret_len in [(0,0), (16,0), (16,40), (32,40), (40,0), (40,40)]:
+ if len(pid) == dsn_len + secret_len:
+ break # split pid into DSN and account secret
+ else:
+ continue
+
+ try:
+ voucher = ion.DrmIonVoucher(StringIO(data), pid[:dsn_len], pid[dsn_len:])
+ voucher.parse()
+ voucher.decryptvoucher()
+ break
+ except:
+ pass
+ else:
+ raise Exception(u'Failed to decrypt KFX DRM voucher with any key')
+
+ print(u'KFX DRM voucher successfully decrypted')
+
+ license_type = voucher.getlicensetype()
+ if license_type != "Purchase":
+ raise Exception((u'This book is licensed as {0}. '
+ 'These tools are intended for use on purchased books.').format(license_type))
+
+ self.voucher = voucher
+
+ def getBookTitle(self):
+ return os.path.splitext(os.path.split(self.infile)[1])[0]
+
+ def getBookExtension(self):
+ return '.kfx-zip'
+
+ def getBookType(self):
+ return 'KFX-ZIP'
+
+ def cleanup(self):
+ pass
+
+ def getFile(self, outpath):
+ if not self.decrypted:
+ shutil.copyfile(self.infile, outpath)
+ else:
+ with zipfile.ZipFile(self.infile, 'r') as zif:
+ with zipfile.ZipFile(outpath, 'w') as zof:
+ for info in zif.infolist():
+ zof.writestr(info, self.decrypted.get(info.filename, zif.read(info.filename)))