diff options
author | Apprentice Harper <[email protected]> | 2020-02-16 10:12:25 +0000 |
---|---|---|
committer | Apprentice Harper <[email protected]> | 2020-02-16 10:12:25 +0000 |
commit | 92bf51bc8f201a2d5b1e8b90b8dc033606dbcfb0 (patch) | |
tree | 037f09f49a7d93d6b71dc36db4fba96fa31c5603 /DeDRM_plugin/kfxdedrm.py | |
parent | ef3c7f261c9049e7ee861c62f308ed82c5d757fb (diff) |
Remove stand-alone apps. Only support the two plugins.
Diffstat (limited to 'DeDRM_plugin/kfxdedrm.py')
-rw-r--r-- | DeDRM_plugin/kfxdedrm.py | 116 |
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))) |