THIS IS ON THE MASTER BRANCH. The Master branch will be Python 3.0 from now on. While Python 2.7 support will not be deliberately broken, all efforts should now focus on Python 3.0 compatibility.
I can see a lot of work has been done. There's more to do. I've bumped the version number of everything I came across to the next major number for Python 3.0 compatibility indication.
Thanks everyone. I hope to update here at least once a week until we have a stable 7.0 release for calibre 5.0
# Copyright © 2008-2020 Apprentice Harper et al.
__license__ = 'GPL v3'
-__version__ = '6.8.0'
+__version__ = '7.0.0'
__docformat__ = 'restructuredtext en'
# 6.6.3 - More cleanup of kindle book names and start of support for .kinf2018
# 6.7.0 - Handle new library in calibre.
# 6.8.0 - Full support for .kinf2018 and new KFX encryption (Kindle for PC/Mac 2.5+)
+# 7.0.0 - Switched to Python 3 for calibre 5.0. Thanks to all who comtibuted
"""
Decrypt DRMed ebooks.
"""
PLUGIN_NAME = u"DeDRM"
-PLUGIN_VERSION_TUPLE = (6, 8, 0)
+PLUGIN_VERSION_TUPLE = (7, 0, 0)
PLUGIN_VERSION = u".".join([unicode(str(x)) for x in PLUGIN_VERSION_TUPLE])
# Include an html helpfile in the plugin's zipfile with the following name.
RESOURCE_NAME = PLUGIN_NAME + '_Help.htm'
+import codecs
import sys, os, re
import time
import zipfile
if self.encoding == None:
self.encoding = "utf-8"
def write(self, data):
- if isinstance(data,unicode):
+ if isinstance(data,bytes):
data = data.encode(self.encoding,"replace")
try:
self.stream.write(data)
else:
names = [u"libalfcrypto32.so",u"libalfcrypto64.so",u"kindlekey.py",u"adobekey.py",u"subasyncio.py"]
lib_dict = self.load_resources(names)
- print u"{0} v{1}: Copying needed library files from plugin's zip".format(PLUGIN_NAME, PLUGIN_VERSION)
+ print("{0} v{1}: Copying needed library files from plugin's zip".format(PLUGIN_NAME, PLUGIN_VERSION))
for entry, data in lib_dict.items():
file_path = os.path.join(self.alfdir, entry)
try:
open(file_path,'wb').write(data)
except:
- print u"{0} v{1}: Exception when copying needed library files".format(PLUGIN_NAME, PLUGIN_VERSION)
+ print("{0} v{1}: Exception when copying needed library files".format(PLUGIN_NAME, PLUGIN_VERSION))
traceback.print_exc()
pass
# mark that this version has been initialized
os.mkdir(self.verdir)
- except Exception, e:
+ except Exception as e:
traceback.print_exc()
raise
inf = self.temporary_file(u".epub")
try:
- print u"{0} v{1}: Verifying zip archive integrity".format(PLUGIN_NAME, PLUGIN_VERSION)
+ print("{0} v{1}: Verifying zip archive integrity".format(PLUGIN_NAME, PLUGIN_VERSION))
fr = zipfix.fixZip(path_to_ebook, inf.name)
fr.fix()
- except Exception, e:
- print u"{0} v{1}: Error \'{2}\' when checking zip archive".format(PLUGIN_NAME, PLUGIN_VERSION, e.args[0])
+ except Exception as e:
+ print(u"{0} v{1}: Error \'{2}\' when checking zip archive".format(PLUGIN_NAME, PLUGIN_VERSION, e.args[0]))
raise Exception(e)
# import the decryption keys
#check the book
if ignobleepub.ignobleBook(inf.name):
- print u"{0} v{1}: “{2}” is a secure Barnes & Noble ePub".format(PLUGIN_NAME, PLUGIN_VERSION, os.path.basename(path_to_ebook))
+ print("{0} v{1}: “{2}” is a secure Barnes & Noble ePub".format(PLUGIN_NAME, PLUGIN_VERSION, os.path.basename(path_to_ebook)))
# Attempt to decrypt epub with each encryption key (generated or provided).
for keyname, userkey in dedrmprefs['bandnkeys'].items():
keyname_masked = u"".join((u'X' if (x.isdigit()) else x) for x in keyname)
- print u"{0} v{1}: Trying Encryption key {2:s}".format(PLUGIN_NAME, PLUGIN_VERSION, keyname_masked)
+ print("{0} v{1}: Trying Encryption key {2:s}".format(PLUGIN_NAME, PLUGIN_VERSION, keyname_masked))
of = self.temporary_file(u".epub")
# Give the user key, ebook and TemporaryPersistent file to the decryption function.
try:
result = ignobleepub.decryptBook(userkey, inf.name, of.name)
except:
- print u"{0} v{1}: Exception when trying to decrypt after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION, time.time()-self.starttime)
+ print("{0} v{1}: Exception when trying to decrypt after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION, time.time()-self.starttime))
traceback.print_exc()
result = 1
# Return the modified PersistentTemporary file to calibre.
return of.name
- print u"{0} v{1}: Failed to decrypt with key {2:s} after {3:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION,keyname_masked,time.time()-self.starttime)
+ print("{0} v{1}: Failed to decrypt with key {2:s} after {3:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION,keyname_masked,time.time()-self.starttime))
# perhaps we should see if we can get a key from a log file
- print u"{0} v{1}: Looking for new NOOK Study Keys after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION, time.time()-self.starttime)
+ print("{0} v{1}: Looking for new NOOK Study Keys after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION, time.time()-self.starttime))
# get the default NOOK Study keys
defaultkeys = []
defaultkeys = WineGetKeys(scriptpath, u".b64",dedrmprefs['adobewineprefix'])
except:
- print u"{0} v{1}: Exception when getting default NOOK Study Key after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION, time.time()-self.starttime)
+ print("{0} v{1}: Exception when getting default NOOK Study Key after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION, time.time()-self.starttime))
traceback.print_exc()
newkeys = []
if len(newkeys) > 0:
try:
for i,userkey in enumerate(newkeys):
- print u"{0} v{1}: Trying a new default key".format(PLUGIN_NAME, PLUGIN_VERSION)
+ print("{0} v{1}: Trying a new default key".format(PLUGIN_NAME, PLUGIN_VERSION))
of = self.temporary_file(u".epub")
try:
result = ignobleepub.decryptBook(userkey, inf.name, of.name)
except:
- print u"{0} v{1}: Exception when trying to decrypt after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION, time.time()-self.starttime)
+ print("{0} v{1}: Exception when trying to decrypt after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION, time.time()-self.starttime))
traceback.print_exc()
result = 1
if result == 0:
# Decryption was a success
# Store the new successful key in the defaults
- print u"{0} v{1}: Saving a new default key".format(PLUGIN_NAME, PLUGIN_VERSION)
+ print("{0} v{1}: Saving a new default key".format(PLUGIN_NAME, PLUGIN_VERSION))
try:
dedrmprefs.addnamedvaluetoprefs('bandnkeys','nook_Study_key',keyvalue)
dedrmprefs.writeprefs()
- print u"{0} v{1}: Saved a new default key after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION,time.time()-self.starttime)
+ print("{0} v{1}: Saved a new default key after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION,time.time()-self.starttime))
except:
- print u"{0} v{1}: Exception saving a new default key after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION, time.time()-self.starttime)
+ print("{0} v{1}: Exception saving a new default key after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION, time.time()-self.starttime))
traceback.print_exc()
# Return the modified PersistentTemporary file to calibre.
return of.name
- print u"{0} v{1}: Failed to decrypt with new default key after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION,time.time()-self.starttime)
- except Exception, e:
+ print(u"{0} v{1}: Failed to decrypt with new default key after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION,time.time()-self.starttime))
+ except Exception as e:
pass
- print u"{0} v{1}: Ultimately failed to decrypt after {2:.1f} seconds. Read the FAQs at Harper's repository: https://github.com/apprenticeharper/DeDRM_tools/blob/master/FAQs.md".format(PLUGIN_NAME, PLUGIN_VERSION,time.time()-self.starttime)
+ print("{0} v{1}: Ultimately failed to decrypt after {2:.1f} seconds. Read the FAQs at Harper's repository: https://github.com/apprenticeharper/DeDRM_tools/blob/master/FAQs.md".format(PLUGIN_NAME, PLUGIN_VERSION,time.time()-self.starttime))
raise DeDRMError(u"{0} v{1}: Ultimately failed to decrypt after {2:.1f} seconds. Read the FAQs at Harper's repository: https://github.com/apprenticeharper/DeDRM_tools/blob/master/FAQs.md".format(PLUGIN_NAME, PLUGIN_VERSION, time.time()-self.starttime))
# import the Adobe Adept ePub handler
import calibre_plugins.dedrm.ineptepub as ineptepub
if ineptepub.adeptBook(inf.name):
- print u"{0} v{1}: {2} is a secure Adobe Adept ePub".format(PLUGIN_NAME, PLUGIN_VERSION, os.path.basename(path_to_ebook))
+ print("{0} v{1}: {2} is a secure Adobe Adept ePub".format(PLUGIN_NAME, PLUGIN_VERSION, os.path.basename(path_to_ebook)))
# Attempt to decrypt epub with each encryption key (generated or provided).
for keyname, userkeyhex in dedrmprefs['adeptkeys'].items():
- userkey = userkeyhex.decode('hex')
- print u"{0} v{1}: Trying Encryption key {2:s}".format(PLUGIN_NAME, PLUGIN_VERSION, keyname)
+ userkey = codecs.decode(userkeyhex, 'hex')
+ print(u"{0} v{1}: Trying Encryption key {2:s}".format(PLUGIN_NAME, PLUGIN_VERSION, keyname))
of = self.temporary_file(u".epub")
# Give the user key, ebook and TemporaryPersistent file to the decryption function.
try:
result = ineptepub.decryptBook(userkey, inf.name, of.name)
except:
- print u"{0} v{1}: Exception when decrypting after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION, time.time()-self.starttime)
+ print("{0} v{1}: Exception when decrypting after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION, time.time()-self.starttime))
traceback.print_exc()
result = 1
try:
of.close()
except:
- print u"{0} v{1}: Exception closing temporary file after {2:.1f} seconds. Ignored.".format(PLUGIN_NAME, PLUGIN_VERSION, time.time()-self.starttime)
+ print("{0} v{1}: Exception closing temporary file after {2:.1f} seconds. Ignored.".format(PLUGIN_NAME, PLUGIN_VERSION, time.time()-self.starttime))
if result == 0:
# Decryption was successful.
# Return the modified PersistentTemporary file to calibre.
- print u"{0} v{1}: Decrypted with key {2:s} after {3:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION,keyname,time.time()-self.starttime)
+ print("{0} v{1}: Decrypted with key {2:s} after {3:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION,keyname,time.time()-self.starttime))
return of.name
- print u"{0} v{1}: Failed to decrypt with key {2:s} after {3:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION,keyname,time.time()-self.starttime)
+ print("{0} v{1}: Failed to decrypt with key {2:s} after {3:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION,keyname,time.time()-self.starttime))
# perhaps we need to get a new default ADE key
- print u"{0} v{1}: Looking for new default Adobe Digital Editions Keys after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION, time.time()-self.starttime)
+ print("{0} v{1}: Looking for new default Adobe Digital Editions Keys after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION, time.time()-self.starttime))
# get the default Adobe keys
defaultkeys = []
self.default_key = defaultkeys[0]
except:
- print u"{0} v{1}: Exception when getting default Adobe Key after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION, time.time()-self.starttime)
+ print("{0} v{1}: Exception when getting default Adobe Key after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION, time.time()-self.starttime))
traceback.print_exc()
self.default_key = u""
if len(newkeys) > 0:
try:
for i,userkey in enumerate(newkeys):
- print u"{0} v{1}: Trying a new default key".format(PLUGIN_NAME, PLUGIN_VERSION)
+ print("{0} v{1}: Trying a new default key".format(PLUGIN_NAME, PLUGIN_VERSION))
of = self.temporary_file(u".epub")
# Give the user key, ebook and TemporaryPersistent file to the decryption function.
try:
result = ineptepub.decryptBook(userkey, inf.name, of.name)
except:
- print u"{0} v{1}: Exception when decrypting after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION, time.time()-self.starttime)
+ print("{0} v{1}: Exception when decrypting after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION, time.time()-self.starttime))
traceback.print_exc()
result = 1
if result == 0:
# Decryption was a success
# Store the new successful key in the defaults
- print u"{0} v{1}: Saving a new default key".format(PLUGIN_NAME, PLUGIN_VERSION)
+ print("{0} v{1}: Saving a new default key".format(PLUGIN_NAME, PLUGIN_VERSION))
try:
dedrmprefs.addnamedvaluetoprefs('adeptkeys','default_key',keyvalue.encode('hex'))
dedrmprefs.writeprefs()
- print u"{0} v{1}: Saved a new default key after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION,time.time()-self.starttime)
+ print("{0} v{1}: Saved a new default key after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION,time.time()-self.starttime))
except:
- print u"{0} v{1}: Exception when saving a new default key after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION, time.time()-self.starttime)
+ print("{0} v{1}: Exception when saving a new default key after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION, time.time()-self.starttime))
traceback.print_exc()
- print u"{0} v{1}: Decrypted with new default key after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION,time.time()-self.starttime)
+ print("{0} v{1}: Decrypted with new default key after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION,time.time()-self.starttime))
# Return the modified PersistentTemporary file to calibre.
return of.name
- print u"{0} v{1}: Failed to decrypt with new default key after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION,time.time()-self.starttime)
- except Exception, e:
- print u"{0} v{1}: Unexpected Exception trying a new default key after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION, time.time()-self.starttime)
+ print(u"{0} v{1}: Failed to decrypt with new default key after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION,time.time()-self.starttime))
+ except Exception as e:
+ print(u"{0} v{1}: Unexpected Exception trying a new default key after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION, time.time()-self.starttime))
traceback.print_exc()
pass
# Something went wrong with decryption.
- print u"{0} v{1}: Ultimately failed to decrypt after {2:.1f} seconds. Read the FAQs at Harper's repository: https://github.com/apprenticeharper/DeDRM_tools/blob/master/FAQs.md".format(PLUGIN_NAME, PLUGIN_VERSION,time.time()-self.starttime)
+ print("{0} v{1}: Ultimately failed to decrypt after {2:.1f} seconds. Read the FAQs at Harper's repository: https://github.com/apprenticeharper/DeDRM_tools/blob/master/FAQs.md".format(PLUGIN_NAME, PLUGIN_VERSION,time.time()-self.starttime))
raise DeDRMError(u"{0} v{1}: Ultimately failed to decrypt after {2:.1f} seconds. Read the FAQs at Harper's repository: https://github.com/apprenticeharper/DeDRM_tools/blob/master/FAQs.md".format(PLUGIN_NAME, PLUGIN_VERSION,time.time()-self.starttime))
# Not a Barnes & Noble nor an Adobe Adept
# Import the fixed epub.
- print u"{0} v{1}: “{2}” is neither an Adobe Adept nor a Barnes & Noble encrypted ePub".format(PLUGIN_NAME, PLUGIN_VERSION, os.path.basename(path_to_ebook))
+ print("{0} v{1}: “{2}” is neither an Adobe Adept nor a Barnes & Noble encrypted ePub".format(PLUGIN_NAME, PLUGIN_VERSION, os.path.basename(path_to_ebook)))
raise DeDRMError(u"{0} v{1}: Couldn't decrypt after {2:.1f} seconds. DRM free perhaps?".format(PLUGIN_NAME, PLUGIN_VERSION,time.time()-self.starttime))
def PDFDecrypt(self,path_to_ebook):
dedrmprefs = prefs.DeDRM_Prefs()
# Attempt to decrypt epub with each encryption key (generated or provided).
- print u"{0} v{1}: {2} is a PDF ebook".format(PLUGIN_NAME, PLUGIN_VERSION, os.path.basename(path_to_ebook))
+ print("{0} v{1}: {2} is a PDF ebook".format(PLUGIN_NAME, PLUGIN_VERSION, os.path.basename(path_to_ebook)))
for keyname, userkeyhex in dedrmprefs['adeptkeys'].items():
- userkey = userkeyhex.decode('hex')
- print u"{0} v{1}: Trying Encryption key {2:s}".format(PLUGIN_NAME, PLUGIN_VERSION, keyname)
+ userkey = codecs.decode(userkeyhex, 'hex')
+ print("{0} v{1}: Trying Encryption key {2:s}".format(PLUGIN_NAME, PLUGIN_VERSION, keyname))
of = self.temporary_file(u".pdf")
# Give the user key, ebook and TemporaryPersistent file to the decryption function.
try:
result = ineptpdf.decryptBook(userkey, path_to_ebook, of.name)
except:
- print u"{0} v{1}: Exception when decrypting after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION, time.time()-self.starttime)
+ print("{0} v{1}: Exception when decrypting after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION, time.time()-self.starttime))
traceback.print_exc()
result = 1
# Return the modified PersistentTemporary file to calibre.
return of.name
- print u"{0} v{1}: Failed to decrypt with key {2:s} after {3:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION,keyname,time.time()-self.starttime)
+ print("{0} v{1}: Failed to decrypt with key {2:s} after {3:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION,keyname,time.time()-self.starttime))
# perhaps we need to get a new default ADE key
- print u"{0} v{1}: Looking for new default Adobe Digital Editions Keys after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION, time.time()-self.starttime)
+ print("{0} v{1}: Looking for new default Adobe Digital Editions Keys after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION, time.time()-self.starttime))
# get the default Adobe keys
defaultkeys = []
self.default_key = defaultkeys[0]
except:
- print u"{0} v{1}: Exception when getting default Adobe Key after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION, time.time()-self.starttime)
+ print("{0} v{1}: Exception when getting default Adobe Key after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION, time.time()-self.starttime))
traceback.print_exc()
self.default_key = u""
if len(newkeys) > 0:
try:
for i,userkey in enumerate(newkeys):
- print u"{0} v{1}: Trying a new default key".format(PLUGIN_NAME, PLUGIN_VERSION)
+ print("{0} v{1}: Trying a new default key".format(PLUGIN_NAME, PLUGIN_VERSION))
of = self.temporary_file(u".pdf")
# Give the user key, ebook and TemporaryPersistent file to the decryption function.
try:
result = ineptpdf.decryptBook(userkey, path_to_ebook, of.name)
except:
- print u"{0} v{1}: Exception when decrypting after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION, time.time()-self.starttime)
+ print("{0} v{1}: Exception when decrypting after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION, time.time()-self.starttime))
traceback.print_exc()
result = 1
if result == 0:
# Decryption was a success
# Store the new successful key in the defaults
- print u"{0} v{1}: Saving a new default key".format(PLUGIN_NAME, PLUGIN_VERSION)
+ print("{0} v{1}: Saving a new default key".format(PLUGIN_NAME, PLUGIN_VERSION))
try:
dedrmprefs.addnamedvaluetoprefs('adeptkeys','default_key',keyvalue.encode('hex'))
dedrmprefs.writeprefs()
- print u"{0} v{1}: Saved a new default key after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION,time.time()-self.starttime)
+ print("{0} v{1}: Saved a new default key after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION,time.time()-self.starttime))
except:
- print u"{0} v{1}: Exception when saving a new default key after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION, time.time()-self.starttime)
+ print("{0} v{1}: Exception when saving a new default key after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION, time.time()-self.starttime))
traceback.print_exc()
# Return the modified PersistentTemporary file to calibre.
return of.name
- print u"{0} v{1}: Failed to decrypt with new default key after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION,time.time()-self.starttime)
- except Exception, e:
+ print(u"{0} v{1}: Failed to decrypt with new default key after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION,time.time()-self.starttime))
+ except Exception as e:
pass
# Something went wrong with decryption.
- print u"{0} v{1}: Ultimately failed to decrypt after {2:.1f} seconds. Read the FAQs at Harper's repository: https://github.com/apprenticeharper/DeDRM_tools/blob/master/FAQs.md".format(PLUGIN_NAME, PLUGIN_VERSION,time.time()-self.starttime)
+ print("{0} v{1}: Ultimately failed to decrypt after {2:.1f} seconds. Read the FAQs at Harper's repository: https://github.com/apprenticeharper/DeDRM_tools/blob/master/FAQs.md".format(PLUGIN_NAME, PLUGIN_VERSION,time.time()-self.starttime))
raise DeDRMError(u"{0} v{1}: Ultimately failed to decrypt after {2:.1f} seconds. Read the FAQs at Harper's repository: https://github.com/apprenticeharper/DeDRM_tools/blob/master/FAQs.md".format(PLUGIN_NAME, PLUGIN_VERSION, time.time()-self.starttime))
try:
book = k4mobidedrm.GetDecryptedBook(path_to_ebook,kindleDatabases,androidFiles,serials,pids,self.starttime)
- except Exception, e:
+ except Exception as e:
decoded = False
# perhaps we need to get a new default Kindle for Mac/PC key
defaultkeys = []
- print u"{0} v{1}: Failed to decrypt with error: {2}".format(PLUGIN_NAME, PLUGIN_VERSION,e.args[0])
- print u"{0} v{1}: Looking for new default Kindle Key after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION, time.time()-self.starttime)
+ print("{0} v{1}: Failed to decrypt with error: {2}".format(PLUGIN_NAME, PLUGIN_VERSION,e.args[0]))
+ print("{0} v{1}: Looking for new default Kindle Key after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION, time.time()-self.starttime))
try:
if iswindows or isosx:
scriptpath = os.path.join(self.alfdir,u"kindlekey.py")
defaultkeys = WineGetKeys(scriptpath, u".k4i",dedrmprefs['kindlewineprefix'])
except:
- print u"{0} v{1}: Exception when getting default Kindle Key after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION, time.time()-self.starttime)
+ print("{0} v{1}: Exception when getting default Kindle Key after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION, time.time()-self.starttime))
traceback.print_exc()
pass
if keyvalue not in dedrmprefs['kindlekeys'].values():
newkeys[keyname] = keyvalue
if len(newkeys) > 0:
- print u"{0} v{1}: Found {2} new {3}".format(PLUGIN_NAME, PLUGIN_VERSION, len(newkeys), u"key" if len(newkeys)==1 else u"keys")
+ print("{0} v{1}: Found {2} new {3}".format(PLUGIN_NAME, PLUGIN_VERSION, len(newkeys), u"key" if len(newkeys)==1 else u"keys"))
try:
book = k4mobidedrm.GetDecryptedBook(path_to_ebook,newkeys.items(),[],[],[],self.starttime)
decoded = True
# store the new successful keys in the defaults
- print u"{0} v{1}: Saving {2} new {3}".format(PLUGIN_NAME, PLUGIN_VERSION, len(newkeys), u"key" if len(newkeys)==1 else u"keys")
+ print("{0} v{1}: Saving {2} new {3}".format(PLUGIN_NAME, PLUGIN_VERSION, len(newkeys), u"key" if len(newkeys)==1 else u"keys"))
for keyvalue in newkeys.values():
dedrmprefs.addnamedvaluetoprefs('kindlekeys','default_key',keyvalue)
dedrmprefs.writeprefs()
- except Exception, e:
+ except Exception as e:
pass
if not decoded:
#if you reached here then no luck raise and exception
- print u"{0} v{1}: Ultimately failed to decrypt after {2:.1f} seconds. Read the FAQs at Harper's repository: https://github.com/apprenticeharper/DeDRM_tools/blob/master/FAQs.md".format(PLUGIN_NAME, PLUGIN_VERSION,time.time()-self.starttime)
+ print("{0} v{1}: Ultimately failed to decrypt after {2:.1f} seconds. Read the FAQs at Harper's repository: https://github.com/apprenticeharper/DeDRM_tools/blob/master/FAQs.md".format(PLUGIN_NAME, PLUGIN_VERSION,time.time()-self.starttime))
raise DeDRMError(u"{0} v{1}: Ultimately failed to decrypt after {2:.1f} seconds. Read the FAQs at Harper's repository: https://github.com/apprenticeharper/DeDRM_tools/blob/master/FAQs.md".format(PLUGIN_NAME, PLUGIN_VERSION,time.time()-self.starttime))
of = self.temporary_file(book.getBookExtension())
# Attempt to decrypt epub with each encryption key (generated or provided).
for keyname, userkey in dedrmprefs['ereaderkeys'].items():
keyname_masked = u"".join((u'X' if (x.isdigit()) else x) for x in keyname)
- print u"{0} v{1}: Trying Encryption key {2:s}".format(PLUGIN_NAME, PLUGIN_VERSION, keyname_masked)
+ print("{0} v{1}: Trying Encryption key {2:s}".format(PLUGIN_NAME, PLUGIN_VERSION, keyname_masked))
of = self.temporary_file(u".pmlz")
# Give the userkey, ebook and TemporaryPersistent file to the decryption function.
# Decryption was successful return the modified PersistentTemporary
# file to Calibre's import process.
if result == 0:
- print u"{0} v{1}: Successfully decrypted with key {2:s} after {3:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION,keyname_masked,time.time()-self.starttime)
+ print("{0} v{1}: Successfully decrypted with key {2:s} after {3:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION,keyname_masked,time.time()-self.starttime))
return of.name
- print u"{0} v{1}: Failed to decrypt with key {2:s} after {3:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION,keyname_masked,time.time()-self.starttime)
+ print("{0} v{1}: Failed to decrypt with key {2:s} after {3:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION,keyname_masked,time.time()-self.starttime))
- print u"{0} v{1}: Ultimately failed to decrypt after {2:.1f} seconds. Read the FAQs at Harper's repository: https://github.com/apprenticeharper/DeDRM_tools/blob/master/FAQs.md".format(PLUGIN_NAME, PLUGIN_VERSION,time.time()-self.starttime)
+ print("{0} v{1}: Ultimately failed to decrypt after {2:.1f} seconds. Read the FAQs at Harper's repository: https://github.com/apprenticeharper/DeDRM_tools/blob/master/FAQs.md".format(PLUGIN_NAME, PLUGIN_VERSION,time.time()-self.starttime))
raise DeDRMError(u"{0} v{1}: Ultimately failed to decrypt after {2:.1f} seconds. Read the FAQs at Harper's repository: https://github.com/apprenticeharper/DeDRM_tools/blob/master/FAQs.md".format(PLUGIN_NAME, PLUGIN_VERSION, time.time()-self.starttime))
sys.stdout=SafeUnbuffered(sys.stdout)
sys.stderr=SafeUnbuffered(sys.stderr)
- print u"{0} v{1}: Trying to decrypt {2}".format(PLUGIN_NAME, PLUGIN_VERSION, os.path.basename(path_to_ebook))
+ print("{0} v{1}: Trying to decrypt {2}".format(PLUGIN_NAME, PLUGIN_VERSION, os.path.basename(path_to_ebook)))
self.starttime = time.time()
booktype = os.path.splitext(path_to_ebook)[1].lower()[1:]
# Adobe Adept or B&N ePub
decrypted_ebook = self.ePubDecrypt(path_to_ebook)
else:
- print u"Unknown booktype {0}. Passing back to calibre unchanged".format(booktype)
+ print("Unknown booktype {0}. Passing back to calibre unchanged".format(booktype))
return path_to_ebook
- print u"{0} v{1}: Finished after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION,time.time()-self.starttime)
+ print("{0} v{1}: Finished after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION,time.time()-self.starttime))
return decrypted_ebook
def is_customizable(self):
# 5.8 - Added getkey interface for Windows DeDRM application
# 5.9 - moved unicode_argv call inside main for Windows DeDRM compatibility
# 6.0 - Work if TkInter is missing
+# 7.0 - Python 3 compatible for calibre 5
+
+from __future__ import print_function
"""
Retrieve Adobe ADEPT user key.
"""
-from __future__ import print_function
__license__ = 'GPL v3'
-__version__ = '6.0'
+__version__ = '7.0'
import sys, os, struct, getopt
argvencoding = sys.stdin.encoding
if argvencoding == None:
argvencoding = "utf-8"
- return [arg if (type(arg) == unicode) else unicode(arg,argvencoding) for arg in sys.argv]
+ return arg
class ADEPTError(Exception):
pass
try:
opts, args = getopt.getopt(argv[1:], "h")
- except getopt.GetoptError, err:
+ except getopt.GetoptError as err:
print(u"Error in options or arguments: {0}".format(err.args[0]))
usage(progname)
sys.exit(2)
keyfileout.write(key)
success = True
tkMessageBox.showinfo(progname, u"Key successfully retrieved to {0}".format(outfile))
- except ADEPTError, e:
+ except ADEPTError as e:
tkMessageBox.showerror(progname, u"Error: {0}".format(str(e)))
except Exception:
root.wm_state('normal')
CryptoPy Artisitic License Version 1.0
See the wonderful pure python package cryptopy-1.2.5
and read its LICENSE.txt for complete license details.
+
+ Adjusted for Python 3 compatibility, September 2020
"""
class CryptoError(Exception):
numBlocks, numExtraBytes = divmod(len(self.bytesToDecrypt), self.blockSize)
if more == None: # no more calls to decrypt, should have all the data
if numExtraBytes != 0:
- raise DecryptNotBlockAlignedError, 'Data not block aligned on decrypt'
+ raise DecryptNotBlockAlignedError('Data not block aligned on decrypt')
# hold back some bytes in case last decrypt has zero len
if (more != None) and (numExtraBytes == 0) and (numBlocks >0) :
def removePad(self, paddedBinaryString, blockSize):
""" Remove padding from a binary string """
if not(0<len(paddedBinaryString)):
- raise DecryptNotBlockAlignedError, 'Expected More Data'
+ raise DecryptNotBlockAlignedError('Expected More Data')
return paddedBinaryString[:-ord(paddedBinaryString[-1])]
class noPadding(Pad):
def __init__(self, key = None, padding = padWithPadLen(), keySize=16):
""" Initialize AES, keySize is in bytes """
if not (keySize == 16 or keySize == 24 or keySize == 32) :
- raise BadKeySizeError, 'Illegal AES key size, must be 16, 24, or 32 bytes'
+ raise BadKeySizeError('Illegal AES key size, must be 16, 24, or 32 bytes')
Rijndael.__init__( self, key, padding=padding, keySize=keySize, blockSize=16 )
if len(key)!=16:
raise Exception('Pukall_Cipher: Bad key length.')
wkey = []
- for i in xrange(8):
+ for i in range(8):
wkey.append(ord(key[i*2])<<8 | ord(key[i*2+1]))
dst = ""
- for i in xrange(len(src)):
+ for i in range(len(src)):
temp1 = 0;
byteXorVal = 0;
- for j in xrange(8):
+ for j in range(8):
temp1 ^= wkey[j]
sum2 = (sum2+j)*20021 + sum1
sum1 = (temp1*346)&0xFFFF
curByte = ((curByte ^ (byteXorVal >> 8)) ^ byteXorVal) & 0xFF
if decryption:
keyXorVal = curByte * 257;
- for j in xrange(8):
+ for j in range(8):
wkey[j] ^= keyXorVal;
dst+=chr(curByte)
return dst
# -*- coding: utf-8 -*-
from __future__ import with_statement
+from __future__ import print_function
# androidkindlekey.py
# Copyright © 2013-15 by Thom and Apprentice Harper
# 1.3 - added in TkInter interface, output to a file
# 1.4 - Fix some problems identified by Aldo Bleeker
# 1.5 - Fix another problem identified by Aldo Bleeker
+# 2.0 - Add Python 3 compatibility
"""
Retrieve Kindle for Android Serial Number.
"""
-from __future__ import print_function
__license__ = 'GPL v3'
-__version__ = '1.5'
+__version__ = '2.0'
import os
import sys
import zlib
import tarfile
from hashlib import md5
-from cStringIO import StringIO
+try:
+ from cStringIO import StringIO
+except ImportError:
+ from io import BytesIO as StringIO
from binascii import a2b_hex, b2a_hex
# Routines common to Mac and PC
if self.encoding == None:
self.encoding = "utf-8"
def write(self, data):
- if isinstance(data,unicode):
+ if isinstance(data,bytes):
data = data.encode(self.encoding,"replace")
self.stream.write(data)
self.stream.flush()
# Remove Python executable and commands if present
start = argc.value - len(sys.argv)
return [argv[i] for i in
- xrange(start, argc.value)]
+ range(start, argc.value)]
# if we don't have any arguments at all, just pass back script name
# this should never happen
return [u"kindlekey.py"]
argvencoding = sys.stdin.encoding
if argvencoding == None:
argvencoding = "utf-8"
- return [arg if (type(arg) == unicode) else unicode(arg,argvencoding) for arg in sys.argv]
+ return argv
class DrmException(Exception):
pass
traceback.print_exc()
pass
tokens = list(set(tokens))
-
+
serials = []
for x in dsns:
serials.append(x)
def getkey(outfile, inpath):
keys = get_serials(inpath)
if len(keys) > 0:
- with file(outfile, 'w') as keyfileout:
+ with open(outfile, 'w') as keyfileout:
for key in keys:
keyfileout.write(key)
keyfileout.write("\n")
try:
opts, args = getopt.getopt(argv[1:], "hb:")
- except getopt.GetoptError, err:
+ except getopt.GetoptError as err:
usage(progname)
print(u"\nError in options or arguments: {0}".format(err.args[0]))
return 2
if not os.path.exists(outfile):
break
- with file(outfile, 'w') as keyfileout:
+ with open(outfile, 'w') as keyfileout:
keyfileout.write(key)
success = True
tkMessageBox.showinfo(progname, u"Key successfully retrieved to {0}".format(outfile))
- except Exception, e:
+ except Exception as e:
self.status['text'] = u"Error: {0}".format(e.args[0])
return
self.status['text'] = u"Select backup.ab file"
argvencoding = sys.stdin.encoding
if argvencoding == None:
argvencoding = "utf-8"
- return [arg if (type(arg) == unicode) else unicode(arg,argvencoding) for arg in sys.argv]
+ return arg
def add_cp65001_codec():
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
# DEALINGS IN THE SOFTWARE.
+# Adjusted for Python 3, September 2020
+
"""
AskFolder(...) -- Ask the user to select a folder Windows specific
"""
def BrowseCallback(hwnd, uMsg, lParam, lpData):
if uMsg == BFFM_INITIALIZED:
if actionButtonLabel:
- label = unicode(actionButtonLabel, errors='replace')
+ label = actionButtonLabel.decode('utf-8', 'replace')
user32.SendMessageW(hwnd, BFFM_SETOKTEXT, 0, label)
if cancelButtonLabel:
- label = unicode(cancelButtonLabel, errors='replace')
+ label = cancelButtonLabel.decode('utf-8', 'replace')
cancelButton = user32.GetDlgItem(hwnd, IDCANCEL)
if cancelButton:
user32.SetWindowTextW(cancelButton, label)
if windowTitle:
- title = unicode(windowTitle, erros='replace')
+ title = windowTitle.decode('utf-8', 'replace')
user32.SetWindowTextW(hwnd, title)
if defaultLocation:
user32.SendMessageW(hwnd, BFFM_SETSELECTIONW, 1, defaultLocation.replace('/', '\\'))
__license__ = 'GPL v3'
+# Added Python 3 compatibility, September 2020
+
# Standard Python modules.
import os, traceback, json
from PyQt5 import Qt as QtGui
except ImportError:
from PyQt4 import QtGui
-
+
from zipfile import ZipFile
# calibre modules and constants.
def kindle_serials(self):
d = ManageKeysDialog(self,u"EInk Kindle Serial Number",self.tempdedrmprefs['serials'], AddSerialDialog)
d.exec_()
-
+
def kindle_android(self):
d = ManageKeysDialog(self,u"Kindle for Android Key",self.tempdedrmprefs['androidkeys'], AddAndroidDialog, 'k4a')
d.exec_()
def getwineprefix(self):
if self.wineprefix is not None:
- return unicode(self.wp_lineedit.text()).strip()
+ return self.wp_lineedit.text().strip()
return u""
def populate_list(self):
if d.result() != d.Accepted:
# rename cancelled or moot.
return
- keyname = unicode(self.listy.currentItem().text())
+ keyname = self.listy.currentItem().text()
if not question_dialog(self, "{0} {1}: Confirm Rename".format(PLUGIN_NAME, PLUGIN_VERSION), u"Do you really want to rename the {2} named <strong>{0}</strong> to <strong>{1}</strong>?".format(keyname,d.key_name,self.key_type_name), show_copy_button=False, default_yes=False):
return
self.plugin_keys[d.key_name] = self.plugin_keys[keyname]
def delete_key(self):
if not self.listy.currentItem():
return
- keyname = unicode(self.listy.currentItem().text())
+ keyname = self.listy.currentItem().text()
if not question_dialog(self, "{0} {1}: Confirm Delete".format(PLUGIN_NAME, PLUGIN_VERSION), u"Do you really want to delete the {1} <strong>{0}</strong>?".format(keyname, self.key_type_name), show_copy_button=False, default_yes=False):
return
if type(self.plugin_keys) == dict:
with open(fpath,'rb') as keyfile:
new_key_value = keyfile.read()
if self.binary_file:
- new_key_value = new_key_value.encode('hex')
+ new_key_value = new_key_value.hex()
elif self.json_file:
new_key_value = json.loads(new_key_value)
elif self.android_file:
else:
counter += 1
self.plugin_keys[new_key_name] = new_key_value
-
+
msg = u""
if counter+skipped > 1:
if counter > 0:
r = error_dialog(None, "{0} {1}".format(PLUGIN_NAME, PLUGIN_VERSION),
_(errmsg), show=True, show_copy_button=False)
return
- keyname = unicode(self.listy.currentItem().text())
+ keyname = self.listy.currentItem().text()
unique_dlg_name = PLUGIN_NAME + u"export {0} keys".format(self.key_type_name).replace(' ', '_') #takes care of automatically remembering last directory
caption = u"Save {0} File as...".format(self.key_type_name)
filters = [(u"{0} Files".format(self.key_type_name), [u"{0}".format(self.keyfile_ext)])]
self.resize(self.sizeHint())
def accept(self):
- if not unicode(self.key_ledit.text()) or unicode(self.key_ledit.text()).isspace():
+ if not self.key_ledit.text() or self.key_ledit.text().isspace():
errmsg = u"Key name field cannot be empty!"
return error_dialog(None, "{0} {1}".format(PLUGIN_NAME, PLUGIN_VERSION),
_(errmsg), show=True, show_copy_button=False)
@property
def key_name(self):
- return unicode(self.key_ledit.text()).strip()
+ return self.key_ledit.text().strip()
@property
def key_name(self):
- return unicode(self.key_ledit.text()).strip()
+ return self.key_ledit.text().strip()
@property
def key_value(self):
- return unicode(self.key_display.text()).strip()
+ return self.key_display.text().strip()
@property
def user_name(self):
- return unicode(self.name_ledit.text()).strip().lower().replace(' ','')
+ return self.name_ledit.text().strip().lower().replace(' ','')
@property
def cc_number(self):
- return unicode(self.cc_ledit.text()).strip()
+ return self.cc_ledit.text().strip()
def retrieve_key(self):
from calibre_plugins.dedrm.ignoblekeyfetch import fetch_key as fetch_bandn_key
@property
def key_name(self):
- return unicode(self.key_ledit.text()).strip()
+ return self.key_ledit.text().strip()
@property
def key_value(self):
@property
def user_name(self):
- return unicode(self.name_ledit.text()).strip().lower().replace(' ','')
+ return self.name_ledit.text().strip().lower().replace(' ','')
@property
def cc_number(self):
- return unicode(self.cc_ledit.text()).strip().replace(' ', '').replace('-','')
+ return self.cc_ledit.text().strip().replace(' ', '').replace('-','')
def accept(self):
@property
def key_name(self):
- return unicode(self.key_ledit.text()).strip()
+ return self.key_ledit.text().strip()
@property
def key_value(self):
default_key_error = QLabel(u"The default encryption key for Kindle for Mac/PC could not be found.", self)
default_key_error.setAlignment(Qt.AlignHCenter)
layout.addWidget(default_key_error)
-
+
# if no default, both buttons do the same
self.button_box.accepted.connect(self.reject)
@property
def key_name(self):
- return unicode(self.key_ledit.text()).strip()
+ return self.key_ledit.text().strip()
@property
def key_value(self):
@property
def key_name(self):
- return unicode(self.key_ledit.text()).strip()
+ return self.key_ledit.text().strip()
@property
def key_value(self):
- return unicode(self.key_ledit.text()).replace(' ', '')
+ return self.key_ledit.text().replace(' ', '')
def accept(self):
if len(self.key_name) == 0 or self.key_name.isspace():
self.selected_file_name = QLabel(u"",self)
self.selected_file_name.setAlignment(Qt.AlignHCenter)
file_group.addWidget(self.selected_file_name)
-
+
key_group = QHBoxLayout()
data_group_box_layout.addLayout(key_group)
key_group.addWidget(QLabel(u"Unique Key Name:", self))
#key_label = QLabel(_(''), self)
#key_label.setAlignment(Qt.AlignHCenter)
#data_group_box_layout.addWidget(key_label)
-
+
self.button_box.accepted.connect(self.accept)
self.button_box.rejected.connect(self.reject)
layout.addWidget(self.button_box)
@property
def key_name(self):
- return unicode(self.key_ledit.text()).strip()
+ return self.key_ledit.text().strip()
@property
def file_name(self):
- return unicode(self.selected_file_name.text()).strip()
+ return self.selected_file_name.text().strip()
@property
def key_value(self):
return self.serials_from_file
-
+
def get_android_file(self):
unique_dlg_name = PLUGIN_NAME + u"Import Kindle for Android backup file" #takes care of automatically remembering last directory
caption = u"Select Kindle for Android backup file to add"
file_name = os.path.basename(self.filename)
self.serials_from_file.extend(file_serials)
self.selected_file_name.setText(file_name)
-
+
def accept(self):
if len(self.file_name) == 0 or len(self.key_value) == 0:
@property
def key_name(self):
- return unicode(self.key_ledit.text()).strip()
+ return self.key_ledit.text().strip()
@property
def key_value(self):
- return unicode(self.key_ledit.text()).strip()
+ return self.key_ledit.text().strip()
def accept(self):
if len(self.key_name) == 0 or self.key_name.isspace():
#! /usr/bin/python
# vim:ts=4:sw=4:softtabstop=4:smarttab:expandtab
# For use with Topaz Scripts Version 2.6
+# Added Python 3 compatibility, September 2020
from __future__ import print_function
class Unbuffered:
def convert(i):
result = ''
val = encodeNumber(i)
- for j in xrange(len(val)):
+ for j in range(len(val)):
c = ord(val[j:j+1])
result += '%02x' % c
return result
def __init__(self, dictFile):
self.filename = dictFile
self.size = 0
- self.fo = file(dictFile,'rb')
+ self.fo = open(dictFile,'rb')
self.stable = []
self.size = readEncodedNumber(self.fo)
- for i in xrange(self.size):
+ for i in range(self.size):
self.stable.append(self.escapestr(readString(self.fo)))
self.pos = 0
return self.pos
def dumpDict(self):
- for i in xrange(self.size):
+ for i in range(self.size):
print("%d %s %s" % (i, convert(i), self.stable[i]))
return
class PageParser(object):
def __init__(self, filename, dict, debug, flat_xml):
- self.fo = file(filename,'rb')
+ self.fo = open(filename,'rb')
self.id = os.path.basename(filename).replace('.dat','')
self.dict = dict
self.debug = debug
'startID' : (0, 'number', 1, 1),
'startID.page' : (1, 'number', 0, 0),
'startID.id' : (1, 'number', 0, 0),
-
+
'median_d' : (1, 'number', 0, 0),
'median_h' : (1, 'number', 0, 0),
'median_firsty' : (1, 'number', 0, 0),
def get_tagpath(self, i):
cnt = len(self.tagpath)
if i < cnt : result = self.tagpath[i]
- for j in xrange(i+1, cnt) :
+ for j in range(i+1, cnt) :
result += '.' + self.tagpath[j]
return result
if self.debug : print('Processing: ', self.get_tagpath(0))
cnt = self.tagpath_len()
- for j in xrange(cnt):
+ for j in range(cnt):
tkn = self.get_tagpath(j)
if tkn in self.token_tags :
num_args = self.token_tags[tkn][0]
if (subtags == 1):
ntags = readEncodedNumber(self.fo)
if self.debug : print('subtags: ' + token + ' has ' + str(ntags))
- for j in xrange(ntags):
+ for j in range(ntags):
val = readEncodedNumber(self.fo)
subtagres.append(self.procToken(self.dict.lookup(val)))
argres = self.decodeCMD(arg,argtype)
else :
# num_arg scalar arguments
- for i in xrange(num_args):
+ for i in range(num_args):
argres.append(self.formatArg(readEncodedNumber(self.fo), argtype))
# build the return tag
result += 'of the document is indicated by snippet number sets at the\n'
result += 'end of each snippet. \n'
print(result)
- for i in xrange(cnt):
+ for i in range(cnt):
if self.debug: print('Snippet:',str(i))
snippet = []
snippet.append(i)
adj = readEncodedNumber(self.fo)
mode = mode >> 1
x = []
- for i in xrange(cnt):
+ for i in range(cnt):
x.append(readEncodedNumber(self.fo) - adj)
- for i in xrange(mode):
- for j in xrange(1, cnt):
+ for i in range(mode):
+ for j in range(1, cnt):
x[j] = x[j] + x[j - 1]
- for i in xrange(cnt):
+ for i in range(cnt):
result.append(self.formatArg(x[i],argtype))
return result
try:
opts, args = getopt.getopt(argv[1:], "hd", ["flat-xml"])
- except getopt.GetoptError, err:
+ except getopt.GetoptError as err:
# print help information and exit:
print(str(err)) # will print something like "option -a not recognized"
# Changelog epubtest
# 1.00 - Cut to epubtest.py, testing ePub files only by Apprentice Alf
# 1.01 - Added routine for use by Windows DeDRM
+# 2.00 - Added Python 3 compatibility, September 2020
#
# Written in 2011 by Paul Durrant
# Released with unlicense. See http://unlicense.org/
from __future__ import with_statement
from __future__ import print_function
-__version__ = '1.01'
+__version__ = '2.0'
import sys, struct, os, traceback
import zlib
argvencoding = sys.stdin.encoding
if argvencoding == None:
argvencoding = "utf-8"
- return [arg if (type(arg) == unicode) else unicode(arg,argvencoding) for arg in sys.argv]
+ return arg
_FILENAME_LEN_OFFSET = 26
_EXTRA_LEN_OFFSET = 28
# - Ignore sidebars for dictionaries (different format?)
# 0.22 - Unicode and plugin support, different image folders for PMLZ and source
# 0.23 - moved unicode_argv call inside main for Windows DeDRM compatibility
+# 1.00 - Added Python 3 compatibility for calibre 5.0
-__version__='0.23'
+__version__='1.00'
import sys, re
import struct, binascii, getopt, zlib, os, os.path, urllib, tempfile, traceback
if self.encoding == None:
self.encoding = "utf-8"
def write(self, data):
- if isinstance(data,unicode):
+ if isinstance(data,bytes):
data = data.encode(self.encoding,"replace")
self.stream.write(data)
self.stream.flush()
# Remove Python executable and commands if present
start = argc.value - len(sys.argv)
return [argv[i] for i in
- xrange(start, argc.value)]
+ range(start, argc.value)]
# if we don't have any arguments at all, just pass back script name
# this should never happen
return [u"mobidedrm.py"]
argvencoding = sys.stdin.encoding
if argvencoding == None:
argvencoding = "utf-8"
- return [arg if (type(arg) == unicode) else unicode(arg,argvencoding) for arg in sys.argv]
+ return argv
Des = None
if iswindows:
bkType = "Book"
def __init__(self, filename, ident):
- self.contents = file(filename, 'rb').read()
+ self.contents = open(filename, 'rb').read()
self.header = self.contents[0:72]
self.num_sections, = struct.unpack('>H', self.contents[76:78])
# Dictionary or normal content (TODO: Not hard-coded)
else:
raise ValueError('Invalid file format')
self.sections = []
- for i in xrange(self.num_sections):
+ for i in range(self.num_sections):
offset, a1,a2,a3,a4 = struct.unpack('>LBBBB', self.contents[78+i*8:78+i*8+8])
flags, val = a1, a2<<16|a3<<8|a4
self.sections.append( (offset, flags, val) )
# delete control characters
name = u"".join(char for char in name if ord(char)>=32)
# white space to single space, delete leading and trailing while space
- name = re.sub(ur"\s", u" ", name).strip()
+ name = re.sub(r"\s", u" ", name).strip()
# remove leading dots
while len(name)>0 and name[0] == u".":
name = name[1:]
def deXOR(text, sp, table):
r=''
j = sp
- for i in xrange(len(text)):
+ for i in range(len(text)):
r += chr(ord(table[j]) ^ ord(text[i]))
j = j + 1
if j == len(table):
def unshuff(data, shuf):
r = [''] * len(data)
j = 0
- for i in xrange(len(data)):
+ for i in range(len(data)):
j = (j + shuf) % len(data)
r[j] = data[i]
assert len("".join(r)) == len(data)
self.flags = struct.unpack('>L', r[4:8])[0]
reqd_flags = (1<<9) | (1<<7) | (1<<10)
if (self.flags & reqd_flags) != reqd_flags:
- print "Flags: 0x%X" % self.flags
+ print("Flags: 0x%X" % self.flags)
raise ValueError('incompatible eReader file')
des = Des(fixKey(user_key))
if version == 259:
sect = self.section_reader(self.first_image_page + i)
name = sect[4:4+32].strip('\0')
data = sect[62:]
- return sanitizeFileName(unicode(name,'windows-1252')), data
+ return sanitizeFileName(name.decode('windows-1252')), data
# def getChapterNamePMLOffsetData(self):
def getText(self):
des = Des(fixKey(self.content_key))
r = ''
- for i in xrange(self.num_text_pages):
+ for i in range(self.num_text_pages):
logging.debug('get page %d', i)
r += zlib.decompress(des.decrypt(self.section_reader(1 + i)))
fnote_ids = deXOR(sect, 0, self.xortable)
# the remaining records of the footnote sections need to be decoded with the content_key and zlib inflated
des = Des(fixKey(self.content_key))
- for i in xrange(1,self.num_footnote_pages):
+ for i in range(1,self.num_footnote_pages):
logging.debug('get footnotepage %d', i)
id_len = ord(fnote_ids[2])
id = fnote_ids[3:3+id_len]
sbar_ids = deXOR(sect, 0, self.xortable)
# the remaining records of the sidebar sections need to be decoded with the content_key and zlib inflated
des = Des(fixKey(self.content_key))
- for i in xrange(1,self.num_sidebar_pages):
+ for i in range(1,self.num_sidebar_pages):
id_len = ord(sbar_ids[2])
id = sbar_ids[3:3+id_len]
smarker = '<sidebar id="%s">\n' % id
def cleanPML(pml):
# Convert special characters to proper PML code. High ASCII start at (\x80, \a128) and go up to (\xff, \a255)
pml2 = pml
- for k in xrange(128,256):
+ for k in range(128,256):
badChar = chr(k)
pml2 = pml2.replace(badChar, '\\a%03d' % k)
return pml2
try:
if not os.path.exists(outdir):
os.makedirs(outdir)
- print u"Decoding File"
+ print(u"Decoding File")
sect = Sectionizer(infile, 'PNRdPPrs')
er = EreaderProcessor(sect, user_key)
if er.getNumImages() > 0:
- print u"Extracting images"
+ print(u"Extracting images")
if not os.path.exists(imagedirpath):
os.makedirs(imagedirpath)
- for i in xrange(er.getNumImages()):
+ for i in range(er.getNumImages()):
name, contents = er.getImage(i)
- file(os.path.join(imagedirpath, name), 'wb').write(contents)
+ open(os.path.join(imagedirpath, name), 'wb').write(contents)
- print u"Extracting pml"
+ print(u"Extracting pml")
pml_string = er.getText()
pmlfilename = bookname + ".pml"
- file(os.path.join(outdir, pmlfilename),'wb').write(cleanPML(pml_string))
+ open(os.path.join(outdir, pmlfilename),'wb').write(cleanPML(pml_string))
if pmlzname is not None:
import zipfile
import shutil
- print u"Creating PMLZ file {0}".format(os.path.basename(pmlzname))
+ print(u"Creating PMLZ file {0}".format(os.path.basename(pmlzname)))
myZipFile = zipfile.ZipFile(pmlzname,'w',zipfile.ZIP_STORED, False)
list = os.listdir(outdir)
for filename in list:
myZipFile.close()
# remove temporary directory
shutil.rmtree(outdir, True)
- print u"Output is {0}".format(pmlzname)
+ print(u"Output is {0}".format(pmlzname))
else :
- print u"Output is in {0}".format(outdir)
- print "done"
- except ValueError, e:
- print u"Error: {0}".format(e)
+ print(u"Output is in {0}".format(outdir))
+ print("done")
+ except ValueError as e:
+ print(u"Error: {0}".format(e))
traceback.print_exc()
return 1
return 0
def usage():
- print u"Converts DRMed eReader books to PML Source"
- print u"Usage:"
- print u" erdr2pml [options] infile.pdb [outpath] \"your name\" credit_card_number"
- print u" "
- print u"Options: "
- print u" -h prints this message"
- print u" -p create PMLZ instead of source folder"
- print u" --make-pmlz create PMLZ instead of source folder"
- print u" "
- print u"Note:"
- print u" if outpath is ommitted, creates source in 'infile_Source' folder"
- print u" if outpath is ommitted and pmlz option, creates PMLZ 'infile.pmlz'"
- print u" if source folder created, images are in infile_img folder"
- print u" if pmlz file created, images are in images folder"
- print u" It's enough to enter the last 8 digits of the credit card number"
+ print(u"Converts DRMed eReader books to PML Source")
+ print(u"Usage:")
+ print(u" erdr2pml [options] infile.pdb [outpath] \"your name\" credit_card_number")
+ print(u" ")
+ print(u"Options: ")
+ print(u" -h prints this message")
+ print(u" -p create PMLZ instead of source folder")
+ print(u" --make-pmlz create PMLZ instead of source folder")
+ print(u" ")
+ print(u"Note:")
+ print(u" if outpath is ommitted, creates source in 'infile_Source' folder")
+ print(u" if outpath is ommitted and pmlz option, creates PMLZ 'infile.pmlz'")
+ print(u" if source folder created, images are in infile_img folder")
+ print(u" if pmlz file created, images are in images folder")
+ print(u" It's enough to enter the last 8 digits of the credit card number")
return
def getuser_key(name,cc):
return struct.pack('>LL', binascii.crc32(newname) & 0xffffffff,binascii.crc32(cc[-8:])& 0xffffffff)
def cli_main():
- print u"eRdr2Pml v{0}. Copyright © 2009–2012 The Dark Reverser et al.".format(__version__)
+ print(u"eRdr2Pml v{0}. Copyright © 2009–2012 The Dark Reverser et al.".format(__version__))
argv=unicode_argv()
try:
opts, args = getopt.getopt(argv[1:], "hp", ["make-pmlz"])
- except getopt.GetoptError, err:
- print err.args[0]
+ except getopt.GetoptError as err:
+ print(err.args[0])
usage()
return 1
make_pmlz = False
elif len(args)==4:
infile, outpath, name, cc = args
- print getuser_key(name,cc).encode('hex')
+ print(getuser_key(name,cc).encode('hex'))
return decryptBook(infile, outpath, make_pmlz, getuser_key(name,cc))
#! /usr/bin/python
# vim:ts=4:sw=4:softtabstop=4:smarttab:expandtab
+# Added Python 3 compatibility for calibre 5.0
from __future__ import print_function
from .convert2xml import encodeNumber
def getMetaArray(metaFile):
# parse the meta file
result = {}
- fo = file(metaFile,'rb')
+ fo = open(metaFile,'rb')
size = readEncodedNumber(fo)
- for i in xrange(size):
+ for i in range(size):
tag = readString(fo)
value = readString(fo)
result[tag] = value
def __init__(self, dictFile):
self.filename = dictFile
self.size = 0
- self.fo = file(dictFile,'rb')
+ self.fo = open(dictFile,'rb')
self.stable = []
self.size = readEncodedNumber(self.fo)
- for i in xrange(self.size):
+ for i in range(self.size):
self.stable.append(self.escapestr(readString(self.fo)))
self.pos = 0
def escapestr(self, str):
else:
end = min(cnt,end)
foundat = -1
- for j in xrange(pos, end):
+ for j in range(pos, end):
item = docList[j]
if item.find('=') >= 0:
(name, argres) = item.split('=')
def getData(self, path):
result = None
cnt = len(self.flatdoc)
- for j in xrange(cnt):
+ for j in range(cnt):
item = self.flatdoc[j]
if item.find('=') >= 0:
(name, argt) = item.split('=')
result = argres
break
if (len(argres) > 0) :
- for j in xrange(0,len(argres)):
+ for j in range(0,len(argres)):
argres[j] = int(argres[j])
return result
def getGlyphDim(self, gly):
tx = self.vx[self.gvtx[gly]:self.gvtx[gly+1]]
ty = self.vy[self.gvtx[gly]:self.gvtx[gly+1]]
p = 0
- for k in xrange(self.glen[gly], self.glen[gly+1]):
+ for k in range(self.glen[gly], self.glen[gly+1]):
if (p == 0):
zx = tx[0:self.vlen[k]+1]
zy = ty[0:self.vlen[k]+1]
imgname = filename.replace('color','img')
sfile = os.path.join(spath,filename)
dfile = os.path.join(dpath,imgname)
- imgdata = file(sfile,'rb').read()
- file(dfile,'wb').write(imgdata)
+ imgdata = open(sfile,'rb').read()
+ open(dfile,'wb').write(imgdata)
print("Creating cover.jpg")
isCover = False
cpath = os.path.join(bookDir,'img')
cpath = os.path.join(cpath,'img0000.jpg')
if os.path.isfile(cpath):
- cover = file(cpath, 'rb').read()
+ cover = open(cpath, 'rb').read()
cpath = os.path.join(bookDir,'cover.jpg')
- file(cpath, 'wb').write(cover)
+ open(cpath, 'wb').write(cover)
isCover = True
mlst.append('<meta name="' + key + '" content="' + meta_array[key] + '" />\n')
metastr = "".join(mlst)
mlst = None
- file(xname, 'wb').write(metastr)
+ open(xname, 'wb').write(metastr)
print('Processing StyleSheet')
# now get the css info
cssstr , classlst = stylexml2css.convert2CSS(flat_xml, fontsize, ph, pw)
- file(xname, 'wb').write(cssstr)
+ open(xname, 'wb').write(cssstr)
if buildXML:
xname = os.path.join(xmlDir, 'other0000.xml')
- file(xname, 'wb').write(convert2xml.getXML(dict, otherFile))
+ open(xname, 'wb').write(convert2xml.getXML(dict, otherFile))
print('Processing Glyphs')
gd = GlyphDict()
if buildXML:
xname = os.path.join(xmlDir, filename.replace('.dat','.xml'))
- file(xname, 'wb').write(convert2xml.getXML(dict, fname))
+ open(xname, 'wb').write(convert2xml.getXML(dict, fname))
gp = GParser(flat_xml)
- for i in xrange(0, gp.count):
+ for i in range(0, gp.count):
path = gp.getPath(i)
maxh, maxw = gp.getGlyphDim(i)
fullpath = '<path id="gl%d" d="%s" fill="black" /><!-- width=%d height=%d -->\n' % (counter * 256 + i, path, maxw, maxh)
if buildXML:
xname = os.path.join(xmlDir, filename.replace('.dat','.xml'))
- file(xname, 'wb').write(convert2xml.getXML(dict, fname))
+ open(xname, 'wb').write(convert2xml.getXML(dict, fname))
# first get the html
pagehtml, tocinfo = flatxml2html.convert2HTML(flat_xml, classlst, fname, bookDir, gd, fixedimage)
hlst.append('</body>\n</html>\n')
htmlstr = "".join(hlst)
hlst = None
- file(os.path.join(bookDir, htmlFileName), 'wb').write(htmlstr)
+ open(os.path.join(bookDir, htmlFileName), 'wb').write(htmlstr)
print(" ")
print('Extracting Table of Contents from Amazon OCR')
tlst.append('</body>\n')
tlst.append('</html>\n')
tochtml = "".join(tlst)
- file(os.path.join(svgDir, 'toc.xhtml'), 'wb').write(tochtml)
+ open(os.path.join(svgDir, 'toc.xhtml'), 'wb').write(tochtml)
# now create index_svg.xhtml that points to all required files
slst.append('</body>\n</html>\n')
svgindex = "".join(slst)
slst = None
- file(os.path.join(bookDir, 'index_svg.xhtml'), 'wb').write(svgindex)
+ open(os.path.join(bookDir, 'index_svg.xhtml'), 'wb').write(svgindex)
print(" ")
olst.append('</package>\n')
opfstr = "".join(olst)
olst = None
- file(opfname, 'wb').write(opfstr)
+ open(opfname, 'wb').write(opfstr)
print('Processing Complete')
try:
opts, args = getopt.getopt(argv[1:], "rh:",["fixed-image"])
- except getopt.GetoptError, err:
+ except getopt.GetoptError as err:
print(str(err))
usage()
return 1
# -*- coding: utf-8 -*-
from __future__ import with_statement
+from __future__ import print_function
# ignobleepub.pyw, version 4.1
# Copyright © 2009-2010 by i♥cabbages
# 3.9 - moved unicode_argv call inside main for Windows DeDRM compatibility
# 4.0 - Work if TkInter is missing
# 4.1 - Import tkFileDialog, don't assume something else will import it.
+# 5.0 - Added Python 3 compatibility for calibre 5.0
"""
Decrypt Barnes & Noble encrypted ePub books.
"""
-from __future__ import print_function
__license__ = 'GPL v3'
-__version__ = "4.1"
+__version__ = "5.0"
import sys
import os
if self.encoding == None:
self.encoding = "utf-8"
def write(self, data):
- if isinstance(data,unicode):
+ if isinstance(data,bytes):
data = data.encode(self.encoding,"replace")
self.stream.write(data)
self.stream.flush()
# Remove Python executable and commands if present
start = argc.value - len(sys.argv)
return [argv[i] for i in
- xrange(start, argc.value)]
+ range(start, argc.value)]
return [u"ineptepub.py"]
else:
argvencoding = sys.stdin.encoding
if argvencoding == None:
argvencoding = "utf-8"
- return [arg if (type(arg) == unicode) else unicode(arg,argvencoding) for arg in sys.argv]
+ return argv
class IGNOBLEError(Exception):
self.status['text'] = u"Decrypting..."
try:
decrypt_status = decryptBook(userkey, inpath, outpath)
- except Exception, e:
+ except Exception as e:
self.status['text'] = u"Error: {0}".format(e.args[0])
return
if decrypt_status == 0:
# -*- coding: utf-8 -*-
from __future__ import with_statement
+from __future__ import print_function
# ignoblekey.py
-# Copyright © 2015 Apprentice Alf and Apprentice Harper
+# Copyright © 2015-2020 Apprentice Alf, Apprentice Harper et al.
# Based on kindlekey.py, Copyright © 2010-2013 by some_updates and Apprentice Alf
# Revision history:
# 1.0 - Initial release
# 1.1 - remove duplicates and return last key as single key
+# 2.0 - Added Python 3 compatibility for calibre 5.0
"""
Get Barnes & Noble EPUB user key from nook Studio log file
"""
-from __future__ import print_function
__license__ = 'GPL v3'
__version__ = "1.1"
if self.encoding == None:
self.encoding = "utf-8"
def write(self, data):
- if isinstance(data,unicode):
+ if isinstance(data,bytes):
data = data.encode(self.encoding,"replace")
self.stream.write(data)
self.stream.flush()
# Remove Python executable and commands if present
start = argc.value - len(sys.argv)
return [argv[i] for i in
- xrange(start, argc.value)]
+ range(start, argc.value)]
# if we don't have any arguments at all, just pass back script name
# this should never happen
return [u"ignoblekey.py"]
argvencoding = sys.stdin.encoding
if argvencoding == None:
argvencoding = "utf-8"
- return [arg if (type(arg) == unicode) else unicode(arg,argvencoding) for arg in sys.argv]
+ return argv
class DrmException(Exception):
pass
if len(keys) > 0:
if not os.path.isdir(outpath):
outfile = outpath
- with file(outfile, 'w') as keyfileout:
+ with open(outfile, 'w') as keyfileout:
keyfileout.write(keys[-1])
print(u"Saved a key to {0}".format(outfile))
else:
outfile = os.path.join(outpath,u"nookkey{0:d}.b64".format(keycount))
if not os.path.exists(outfile):
break
- with file(outfile, 'w') as keyfileout:
+ with open(outfile, 'w') as keyfileout:
keyfileout.write(key)
print(u"Saved a key to {0}".format(outfile))
return True
try:
opts, args = getopt.getopt(argv[1:], "hk:")
- except getopt.GetoptError, err:
+ except getopt.GetoptError as err:
print(u"Error in options or arguments: {0}".format(err.args[0]))
usage(progname)
sys.exit(2)
if not os.path.exists(outfile):
break
- with file(outfile, 'w') as keyfileout:
+ with open(outfile, 'w') as keyfileout:
keyfileout.write(key)
success = True
tkMessageBox.showinfo(progname, u"Key successfully retrieved to {0}".format(outfile))
- except DrmException, e:
+ except DrmException as e:
tkMessageBox.showerror(progname, u"Error: {0}".format(str(e)))
except Exception:
root.wm_state('normal')
# -*- coding: utf-8 -*-
from __future__ import with_statement
+from __future__ import print_function
-# ignoblekeyfetch.pyw, version 1.1
-# Copyright © 2015 Apprentice Harper
+# ignoblekeyfetch.pyw, version 2.0
+# Copyright © 2015-2020 Apprentice Harper et al.
# Released under the terms of the GNU General Public Licence, version 3
# <http://www.gnu.org/licenses/>
# Revision history:
# 1.0 - Initial version
# 1.1 - Try second URL if first one fails
+# 2.0 - Added Python 3 compatibility for calibre 5.0
"""
Fetch Barnes & Noble EPUB user key from B&N servers using email and password
"""
-from __future__ import print_function
__license__ = 'GPL v3'
__version__ = "1.1"
if self.encoding == None:
self.encoding = "utf-8"
def write(self, data):
- if isinstance(data,unicode):
+ if isinstance(data,bytes):
data = data.encode(self.encoding,"replace")
self.stream.write(data)
self.stream.flush()
# Remove Python executable and commands if present
start = argc.value - len(sys.argv)
return [argv[i] for i in
- xrange(start, argc.value)]
+ range(start, argc.value)]
# if we don't have any arguments at all, just pass back script name
# this should never happen
return [u"ignoblekeyfetch.py"]
argvencoding = sys.stdin.encoding
if argvencoding == None:
argvencoding = "utf-8"
- return [arg if (type(arg) == unicode) else unicode(arg,argvencoding) for arg in sys.argv]
+ return argv
class IGNOBLEError(Exception):
def fetch_key(email, password):
# change email and password to utf-8 if unicode
- if type(email)==unicode:
+ if type(email)==bytes:
email = email.encode('utf-8')
- if type(password)==unicode:
+ if type(password)==bytes:
password = password.encode('utf-8')
import random
self.status['text'] = u"Fetching..."
try:
userkey = fetch_key(email, password)
- except Exception, e:
+ except Exception as e:
self.status['text'] = u"Error: {0}".format(e.args[0])
return
if len(userkey) == 28:
# -*- coding: utf-8 -*-
from __future__ import with_statement
+from __future__ import print_function
-# ignoblekeygen.pyw, version 2.5
-# Copyright © 2009-2010 i♥cabbages
+# ignoblekeygen.pyw
+# Copyright © 2009-2020 i♥cabbages, Apprentice Harper et al.
# Released under the terms of the GNU General Public Licence, version 3
# <http://www.gnu.org/licenses/>
-# Modified 2010–2013 by some_updates, DiapDealer and Apprentice Alf
-
# Windows users: Before running this program, you must first install Python.
# We recommend ActiveState Python 2.7.X for Windows (x86) from
# http://www.activestate.com/activepython/downloads.
# 2.6 - moved unicode_argv call inside main for Windows DeDRM compatibility
# 2.7 - Work if TkInter is missing
# 2.8 - Fix bug in stand-alone use (import tkFileDialog)
+# 3.0 - Added Python 3 compatibility for calibre 5.0
"""
Generate Barnes & Noble EPUB user key from name and credit card number.
"""
-from __future__ import print_function
__license__ = 'GPL v3'
-__version__ = "2.8"
+__version__ = "3.0"
import sys
import os
if self.encoding == None:
self.encoding = "utf-8"
def write(self, data):
- if isinstance(data,unicode):
+ if isinstance(data,bytes):
data = data.encode(self.encoding,"replace")
self.stream.write(data)
self.stream.flush()
# Remove Python executable and commands if present
start = argc.value - len(sys.argv)
return [argv[i] for i in
- xrange(start, argc.value)]
+ range(start, argc.value)]
# if we don't have any arguments at all, just pass back script name
# this should never happen
return [u"ignoblekeygen.py"]
argvencoding = sys.stdin.encoding
if argvencoding == None:
argvencoding = "utf-8"
- return [arg if (type(arg) == unicode) else unicode(arg,argvencoding) for arg in sys.argv]
+ return argv
class IGNOBLEError(Exception):
def generate_key(name, ccn):
# remove spaces and case from name and CC numbers.
- if type(name)==unicode:
+ if type(name)==bytes:
name = name.encode('utf-8')
- if type(ccn)==unicode:
+ if type(ccn)==bytes:
ccn = ccn.encode('utf-8')
name = normalize_name(name) + '\x00'
self.status['text'] = u"Generating..."
try:
userkey = generate_key(name, ccn)
- except Exception, e:
+ except Exception as e:
self.status['text'] = u"Error: (0}".format(e.args[0])
return
open(keypath,'wb').write(userkey)
# -*- coding: utf-8 -*-
from __future__ import with_statement
-from __future__ import absolute_import
-from __future__ import print_function
-# ineptepub.pyw, version 6.6
-# Copyright © 2009-2020 by Apprentice Harper et al.
+# ineptepub.pyw
+# Copyright © 2009-2020 by i♥cabbages, Apprentice Harper et al.
# Released under the terms of the GNU General Public Licence, version 3
# <http://www.gnu.org/licenses/>
-# Original script by i♥cabbages
-# Modified 2010–2013 by some_updates, DiapDealer and Apprentice Alf
-# Modified 2015–2020 by Apprentice Harper et al.
# Revision history:
# 1 - Initial release
# 6.4 - Remove erroneous check on DER file sanity
# 6.5 - Completely remove erroneous check on DER file sanity
# 6.6 - Import tkFileDialog, don't assume something else will import it.
-# 6.7 - Add Python 3 compatibility while still working with Python 2.7
+# 7.0 - Add Python 3 compatibility for calibre 5.0
"""
Decrypt Adobe Digital Editions encrypted ePub books.
"""
__license__ = 'GPL v3'
-__version__ = "6.7"
+__version__ = "7.0"
-import six
-from six.moves import range
+import codecs
import sys
import os
import traceback
from zipfile import ZipInfo, ZipFile, ZIP_STORED, ZIP_DEFLATED
from contextlib import closing
import xml.etree.ElementTree as etree
-import base64
# Wrap a stream so that output gets flushed immediately
# and also make sure that any unicode strings get
if self.encoding == None:
self.encoding = "utf-8"
def write(self, data):
- if isinstance(data,six.text_type):
+ if isinstance(data,bytes):
data = data.encode(self.encoding,"replace")
self.stream.write(data)
self.stream.flush()
argvencoding = sys.stdin.encoding
if argvencoding == None:
argvencoding = "utf-8"
- return [arg if (type(arg) == six.text_type) else six.text_type(arg,argvencoding) for arg in sys.argv]
+ return argv
class ADEPTError(Exception):
if len(bookkey) != 172:
print(u"{0:s} is not a secure Adobe Adept ePub.".format(os.path.basename(inpath)))
return 1
- bookkey = bookkey.encode('ascii')
- bookkey = base64.b64decode(bookkey)
- bookkey = rsa.decrypt(bookkey)
+ bookkey = rsa.decrypt(codecs.decode(bookkey.encode('ascii'), 'base64'))
# Padded as per RSAES-PKCS1-v1_5
if bookkey[-17] != '\x00' and bookkey[-17] != 0:
print(u"Could not decrypt {0:s}. Wrong key".format(os.path.basename(inpath)))
def gui_main():
try:
- import six.moves.tkinter
- import six.moves.tkinter_constants
- import six.moves.tkinter_filedialog
- import six.moves.tkinter_messagebox
+ import Tkinter
+ import Tkconstants
+ import tkFileDialog
+ import tkMessageBox
import traceback
except:
return cli_main()
- class DecryptionDialog(six.moves.tkinter.Frame):
+ class DecryptionDialog(Tkinter.Frame):
def __init__(self, root):
- six.moves.tkinter.Frame.__init__(self, root, border=5)
- self.status = six.moves.tkinter.Label(self, text=u"Select files for decryption")
- self.status.pack(fill=six.moves.tkinter_constants.X, expand=1)
- body = six.moves.tkinter.Frame(self)
- body.pack(fill=six.moves.tkinter_constants.X, expand=1)
- sticky = six.moves.tkinter_constants.E + six.moves.tkinter_constants.W
+ Tkinter.Frame.__init__(self, root, border=5)
+ self.status = Tkinter.Label(self, text=u"Select files for decryption")
+ self.status.pack(fill=Tkconstants.X, expand=1)
+ body = Tkinter.Frame(self)
+ body.pack(fill=Tkconstants.X, expand=1)
+ sticky = Tkconstants.E + Tkconstants.W
body.grid_columnconfigure(1, weight=2)
- six.moves.tkinter.Label(body, text=u"Key file").grid(row=0)
- self.keypath = six.moves.tkinter.Entry(body, width=30)
+ Tkinter.Label(body, text=u"Key file").grid(row=0)
+ self.keypath = Tkinter.Entry(body, width=30)
self.keypath.grid(row=0, column=1, sticky=sticky)
if os.path.exists(u"adeptkey.der"):
self.keypath.insert(0, u"adeptkey.der")
- button = six.moves.tkinter.Button(body, text=u"...", command=self.get_keypath)
+ button = Tkinter.Button(body, text=u"...", command=self.get_keypath)
button.grid(row=0, column=2)
- six.moves.tkinter.Label(body, text=u"Input file").grid(row=1)
- self.inpath = six.moves.tkinter.Entry(body, width=30)
+ Tkinter.Label(body, text=u"Input file").grid(row=1)
+ self.inpath = Tkinter.Entry(body, width=30)
self.inpath.grid(row=1, column=1, sticky=sticky)
- button = six.moves.tkinter.Button(body, text=u"...", command=self.get_inpath)
+ button = Tkinter.Button(body, text=u"...", command=self.get_inpath)
button.grid(row=1, column=2)
- six.moves.tkinter.Label(body, text=u"Output file").grid(row=2)
- self.outpath = six.moves.tkinter.Entry(body, width=30)
+ Tkinter.Label(body, text=u"Output file").grid(row=2)
+ self.outpath = Tkinter.Entry(body, width=30)
self.outpath.grid(row=2, column=1, sticky=sticky)
- button = six.moves.tkinter.Button(body, text=u"...", command=self.get_outpath)
+ button = Tkinter.Button(body, text=u"...", command=self.get_outpath)
button.grid(row=2, column=2)
- buttons = six.moves.tkinter.Frame(self)
+ buttons = Tkinter.Frame(self)
buttons.pack()
- botton = six.moves.tkinter.Button(
+ botton = Tkinter.Button(
buttons, text=u"Decrypt", width=10, command=self.decrypt)
- botton.pack(side=six.moves.tkinter_constants.LEFT)
- six.moves.tkinter.Frame(buttons, width=10).pack(side=six.moves.tkinter_constants.LEFT)
- button = six.moves.tkinter.Button(
+ botton.pack(side=Tkconstants.LEFT)
+ Tkinter.Frame(buttons, width=10).pack(side=Tkconstants.LEFT)
+ button = Tkinter.Button(
buttons, text=u"Quit", width=10, command=self.quit)
- button.pack(side=six.moves.tkinter_constants.RIGHT)
+ button.pack(side=Tkconstants.RIGHT)
def get_keypath(self):
- keypath = six.moves.tkinter_filedialog.askopenfilename(
+ keypath = tkFileDialog.askopenfilename(
parent=None, title=u"Select Adobe Adept \'.der\' key file",
defaultextension=u".der",
filetypes=[('Adobe Adept DER-encoded files', '.der'),
('All Files', '.*')])
if keypath:
keypath = os.path.normpath(keypath)
- self.keypath.delete(0, six.moves.tkinter_constants.END)
+ self.keypath.delete(0, Tkconstants.END)
self.keypath.insert(0, keypath)
return
def get_inpath(self):
- inpath = six.moves.tkinter_filedialog.askopenfilename(
+ inpath = tkFileDialog.askopenfilename(
parent=None, title=u"Select ADEPT-encrypted ePub file to decrypt",
defaultextension=u".epub", filetypes=[('ePub files', '.epub')])
if inpath:
inpath = os.path.normpath(inpath)
- self.inpath.delete(0, six.moves.tkinter_constants.END)
+ self.inpath.delete(0, Tkconstants.END)
self.inpath.insert(0, inpath)
return
def get_outpath(self):
- outpath = six.moves.tkinter_filedialog.asksaveasfilename(
+ outpath = tkFileDialog.asksaveasfilename(
parent=None, title=u"Select unencrypted ePub file to produce",
defaultextension=u".epub", filetypes=[('ePub files', '.epub')])
if outpath:
outpath = os.path.normpath(outpath)
- self.outpath.delete(0, six.moves.tkinter_constants.END)
+ self.outpath.delete(0, Tkconstants.END)
self.outpath.insert(0, outpath)
return
else:
self.status['text'] = u"The was an error decrypting the file."
- root = six.moves.tkinter.Tk()
+ root = Tkinter.Tk()
root.title(u"Adobe Adept ePub Decrypter v.{0}".format(__version__))
root.resizable(True, False)
root.minsize(300, 0)
- DecryptionDialog(root).pack(fill=six.moves.tkinter_constants.X, expand=1)
+ DecryptionDialog(root).pack(fill=Tkconstants.X, expand=1)
root.mainloop()
return 0
from __future__ import with_statement
# ineptpdf.py
-# Copyright © 2009-2017 by Apprentice Harper et al.
+# Copyright © 2009-2020 by i♥cabbages, Apprentice Harper et al.
# Released under the terms of the GNU General Public Licence, version 3
# <http://www.gnu.org/licenses/>
-# Modified 2010–2012 by some_updates, DiapDealer and Apprentice Alf
-# Modified 2015-2017 by Apprentice Harper et al.
# Revision history:
# 1 - Initial release
# 8.0.4 - Completely remove erroneous check on DER file sanity
# 8.0.5 - Do not process DRM-free documents
# 8.0.6 - Replace use of float by Decimal for greater precision, and import tkFileDialog
-
+# 9.0.0 - Add Python 3 compatibility for calibre 5
"""
Decrypts Adobe ADEPT-encrypted PDF files.
"""
__license__ = 'GPL v3'
-__version__ = "8.0.6"
+__version__ = "9.0.0"
import sys
import os
import zlib
import struct
import hashlib
-from decimal import *
-from itertools import chain, islice
+from decimal import Decimal
+import itertools
import xml.etree.ElementTree as etree
# Wrap a stream so that output gets flushed immediately
def __init__(self, stream):
self.stream = stream
self.encoding = stream.encoding
- if self.encoding == None:
+ if self.encoding is None:
self.encoding = "utf-8"
def write(self, data):
- if isinstance(data,unicode):
+ if isinstance(data,bytes):
data = data.encode(self.encoding,"replace")
self.stream.write(data)
self.stream.flush()
# Remove Python executable and commands if present
start = argc.value - len(sys.argv)
return [argv[i] for i in
- xrange(start, argc.value)]
+ range(start, argc.value)]
return [u"ineptpdf.py"]
else:
argvencoding = sys.stdin.encoding
- if argvencoding == None:
+ if argvencoding is None:
argvencoding = "utf-8"
- return [arg if (type(arg) == unicode) else unicode(arg,argvencoding) for arg in sys.argv]
+ return sys.argv
class ADEPTError(Exception):
class RSA(object):
def __init__(self, der):
key = ASN1Parser([ord(x) for x in der])
- key = [key.getChild(x).value for x in xrange(1, 4)]
+ key = [key.getChild(x).value for x in range(1, 4)]
key = [self.bytesToNumber(v) for v in key]
self._rsa = _RSA.construct(key)
def bytesToNumber(self, bytes):
- total = 0L
+ total = 0
for byte in bytes:
total = (total << 8) + byte
return total
try:
from cStringIO import StringIO
except ImportError:
- from StringIO import StringIO
+ try:
+ from StringIO import StringIO
+ except ImportError:
+ from io import StringIO
# Do we generate cross reference streams on output?
if not l:
return default
elif l == 1:
- return ord(s)
+ return s
elif l == 2:
return struct.unpack('>H', s)[0]
elif l == 3:
- return struct.unpack('>L', '\x00'+s)[0]
+ return struct.unpack('>L', b'\x00'+s)[0]
elif l == 4:
return struct.unpack('>L', s)[0]
else:
name = []
for char in self.name:
if not char.isalnum():
- char = '#%02x' % ord(char)
+ char = b'#%02x' % char
name.append(char)
- return '/%s' % ''.join(name)
+ return b'/%s' % ''.join(name)
# PSKeyword
class PSKeyword(PSObject):
except ValueError:
pass
return (self.parse_main, j)
-
+
def parse_decimal(self, s, i):
m = END_NUMBER.search(s, i)
if not m:
try:
(pos, objs) = self.end_type('d')
if len(objs) % 2 != 0:
- print "Incomplete dictionary construct"
+ print("Incomplete dictionary construct")
objs.append("") # this isn't necessary.
# temporary fix. is this due to rental books?
# raise PSSyntaxError(
# ascii85decode(data)
def ascii85decode(data):
n = b = 0
- out = ''
+ out = b''
for c in data:
- if '!' <= c and c <= 'u':
+ if b'!' <= c and c <= b'u':
n += 1
- b = b*85+(ord(c)-33)
+ b = b*85+(c-33)
if n == 5:
out += struct.pack('>L',b)
n = b = 0
- elif c == 'z':
+ elif c == b'z':
assert n == 0
- out += '\0\0\0\0'
- elif c == '~':
+ out += b'\0\0\0\0'
+ elif c == b'~':
if n:
for _ in range(5-n):
b = b*85+84
cutdiv = len(rawdata) // 16
rawdata = rawdata[:16*cutdiv]
else:
- if eol in ('\r', '\n', '\r\n'):
+ if eol in (b'\r', b'\n', b'\r\n'):
rawdata = rawdata[:length]
self.dic = dic
raise PDFValueError(
'Columns undefined for predictor=12')
columns = int_value(params['Columns'])
- buf = ''
- ent0 = '\x00' * columns
- for i in xrange(0, len(data), columns+1):
+ buf = b''
+ ent0 = b'\x00' * columns
+ for i in range(0, len(data), columns+1):
pred = data[i]
ent1 = data[i+1:i+1+columns]
- if pred == '\x02':
- ent1 = ''.join(chr((ord(a)+ord(b)) & 255) \
- for (a,b) in zip(ent0,ent1))
+ if pred == b'\x02':
+ ent1 = ''.join(bytes([(a+b) & 255]) \
+ for (a,b) in zip(ent0,ent1))
buf += ent1
ent0 = ent1
data = buf
(start, nobjs) = map(int, f)
except ValueError:
raise PDFNoValidXRef('Invalid line: %r: line=%r' % (parser, line))
- for objid in xrange(start, start+nobjs):
+ for objid in range(start, start+nobjs):
try:
(_, line) = parser.nextline()
except PSEOF:
def objids(self):
for first, size in self.index:
- for objid in xrange(first, first + size):
+ for objid in range(first, first + size):
yield objid
def load(self, parser, debug=0):
raise PDFNoValidXRef('Invalid PDF stream spec.')
size = stream.dic['Size']
index = stream.dic.get('Index', (0,size))
- self.index = zip(islice(index, 0, None, 2),
- islice(index, 1, None, 2))
+ self.index = zip(itertools.islice(index, 0, None, 2),
+ itertools.islice(index, 1, None, 2))
(self.fl1, self.fl2, self.fl3) = stream.dic['W']
self.data = stream.get_data()
self.entlen = self.fl1+self.fl2+self.fl3
if plaintext[-16:] != 16 * chr(16):
raise ADEPTError('Offlinekey cannot be decrypted, aborting ...')
pdrlpol = AES.new(plaintext[16:32],AES.MODE_CBC,edclist[2].decode('base64')).decrypt(pdrlpol)
- if ord(pdrlpol[-1]) < 1 or ord(pdrlpol[-1]) > 16:
+ if pdrlpol[-1] < 1 or pdrlpol[-1] > 16:
raise ADEPTError('Could not decrypt PDRLPol, aborting ...')
else:
- cutter = -1 * ord(pdrlpol[-1])
+ cutter = -1 * pdrlpol[-1]
pdrlpol = pdrlpol[:cutter]
return plaintext[:16]
hash.update('ffffffff'.decode('hex'))
if 5 <= R:
# 8
- for _ in xrange(50):
+ for _ in range(50):
hash = hashlib.md5(hash.digest()[:length/8])
key = hash.digest()[:length/8]
if R == 2:
hash = hashlib.md5(self.PASSWORD_PADDING) # 2
hash.update(docid[0]) # 3
x = ARC4.new(key).decrypt(hash.digest()[:16]) # 4
- for i in xrange(1,19+1):
- k = ''.join( chr(ord(c) ^ i) for c in key )
+ for i in range(1,19+1):
+ k = ''.join(bytes([c ^ i]) for c in key )
x = ARC4.new(k).decrypt(x)
u1 = x+x # 32bytes total
if R == 2:
if V != 4:
self.decipher = self.decipher_rc4 # XXX may be AES
# aes
- elif V == 4 and Length == 128:
- elf.decipher = self.decipher_aes
- elif V == 4 and Length == 256:
+ elif V == 4 and length == 128:
+ self.decipher = self.decipher_aes
+ elif V == 4 and length == 256:
raise PDFNotImplementedError('AES256 encryption is currently unsupported')
self.ready = True
return
else:
V = 2
elif len(bookkey) == length + 1:
- V = ord(bookkey[0])
+ V = bookkey[0]
bookkey = bookkey[1:]
else:
- print "ebx_V is %d and ebx_type is %d" % (ebx_V, ebx_type)
- print "length is %d and len(bookkey) is %d" % (length, len(bookkey))
- print "bookkey[0] is %d" % ord(bookkey[0])
+ print("ebx_V is %d and ebx_type is %d" % (ebx_V, ebx_type))
+ print("length is %d and len(bookkey) is %d" % (length, len(bookkey)))
+ print("bookkey[0] is %d" % bookkey[0])
raise ADEPTError('error decrypting book session key - mismatched length')
else:
# proper length unknown try with whatever you have
- print "ebx_V is %d and ebx_type is %d" % (ebx_V, ebx_type)
- print "length is %d and len(bookkey) is %d" % (length, len(bookkey))
- print "bookkey[0] is %d" % ord(bookkey[0])
+ print("ebx_V is %d and ebx_type is %d" % (ebx_V, ebx_type))
+ print("length is %d and len(bookkey) is %d" % (length, len(bookkey)))
+ print("bookkey[0] is %d" % bookkey[0])
if ebx_V == 3:
V = 3
else:
data = data[16:]
plaintext = AES.new(key,AES.MODE_CBC,ivector).decrypt(data)
# remove pkcs#5 aes padding
- cutter = -1 * ord(plaintext[-1])
+ cutter = -1 * plaintext[-1]
#print cutter
plaintext = plaintext[:cutter]
return plaintext
data = data[16:]
plaintext = AES.new(key,AES.MODE_CBC,ivector).decrypt(data)
# remove pkcs#5 aes padding
- cutter = -1 * ord(plaintext[-1])
+ cutter = -1 * plaintext[-1]
#print cutter
plaintext = plaintext[:cutter]
return plaintext
if not gen_xref_stm:
self.write('xref\n')
self.write('0 %d\n' % (maxobj + 1,))
- for objid in xrange(0, maxobj + 1):
+ for objid in range(0, maxobj + 1):
if objid in xrefs:
# force the genno to be 0
self.write("%010d 00000 n \n" % xrefs[objid][0])
if self.last.isalnum():
self.write(' ')
self.write(str(obj).lower())
- elif isinstance(obj, (int, long)):
+ elif isinstance(obj, int):
if self.last.isalnum():
self.write(' ')
self.write(str(obj))
# help construct to make sure the method runs to the end
try:
serializer.dump(outf)
- except Exception, e:
- print u"error writing pdf: {0}".format(e.args[0])
+ except Exception as e:
+ print(u"error writing pdf: {0}".format(e.args[0]))
return 2
return 0
argv=unicode_argv()
progname = os.path.basename(argv[0])
if len(argv) != 4:
- print u"usage: {0} <keyfile.der> <inbook.pdf> <outbook.pdf>".format(progname)
+ print(u"usage: {0} <keyfile.der> <inbook.pdf> <outbook.pdf>".format(progname))
return 1
keypath, inpath, outpath = argv[1:]
userkey = open(keypath,'rb').read()
result = decryptBook(userkey, inpath, outpath)
if result == 0:
- print u"Successfully decrypted {0:s} as {1:s}".format(os.path.basename(inpath),os.path.basename(outpath))
+ print(u"Successfully decrypted {0:s} as {1:s}".format(os.path.basename(inpath),os.path.basename(outpath)))
return result
self.status['text'] = u"Decrypting..."
try:
decrypt_status = decryptBook(userkey, inpath, outpath)
- except Exception, e:
+ except Exception as e:
self.status['text'] = u"Error; {0}".format(e.args[0])
return
if decrypt_status == 0:
# Copyright © 2013-2020 Apprentice Harper et al.
__license__ = 'GPL v3'
-__version__ = '2.0'
+__version__ = '3.0'
# Revision history:
# Pascal implementation by lulzkabulz.
# 1.2 - Added pylzma import fallback
# 1.3 - Fixed lzma support for calibre 4.6+
# 2.0 - VoucherEnvelope v2/v3 support by apprenticesakuya.
-
+# 3.0 - Added Python 3 compatibility for calibre 5.0
"""
Decrypt Kindle KFX files.
try:
from cStringIO import StringIO
except ImportError:
- from StringIO import StringIO
+ try:
+ from StringIO import StringIO
+ except ImportError:
+ from io import StringIO
from Crypto.Cipher import AES
from Crypto.Util.py3compat import bchr, bord
from __future__ import with_statement
# k4mobidedrm.py
-# Copyright © 2008-2019 by Apprentice Harper et al.
+# Copyright © 2008-2020 by Apprentice Harper et al.
__license__ = 'GPL v3'
-__version__ = '5.7'
+__version__ = '6.0'
# Engine to remove drm from Kindle and Mobipocket ebooks
# for personal use for archiving and converting your ebooks
# 5.5 - Added GPL v3 licence explicitly.
# 5.6 - Invoke KFXZipBook to handle zipped KFX files
# 5.7 - Revamp cleanup_name
+# 6.0 - Added Python 3 compatibility for calibre 5.0
+
import sys, os, re
import csv
import re
import traceback
import time
-import htmlentitydefs
+import html.entities
import json
class DrmException(Exception):
if self.encoding == None:
self.encoding = "utf-8"
def write(self, data):
- if isinstance(data,unicode):
+ if isinstance(data,bytes):
data = data.encode(self.encoding,"replace")
self.stream.write(data)
self.stream.flush()
# Remove Python executable and commands if present
start = argc.value - len(sys.argv)
return [argv[i] for i in
- xrange(start, argc.value)]
+ range(start, argc.value)]
# if we don't have any arguments at all, just pass back script name
# this should never happen
return [u"mobidedrm.py"]
argvencoding = sys.stdin.encoding
if argvencoding == None:
argvencoding = "utf-8"
- return [arg if (type(arg) == unicode) else unicode(arg,argvencoding) for arg in sys.argv]
+ return argv
# cleanup unicode filenames
# borrowed from calibre from calibre/src/calibre/__init__.py
# substitute filename unfriendly characters
name = name.replace(u"<",u"[").replace(u">",u"]").replace(u" : ",u" – ").replace(u": ",u" – ").replace(u":",u"—").replace(u"/",u"_").replace(u"\\",u"_").replace(u"|",u"_").replace(u"\"",u"\'").replace(u"*",u"_").replace(u"?",u"")
# white space to single space, delete leading and trailing while space
- name = re.sub(ur"\s", u" ", name).strip()
+ name = re.sub(r"\s", u" ", name).strip()
# delete control characters
name = u"".join(char for char in name if ord(char)>=32)
# delete non-ascii characters
# character reference
try:
if text[:3] == u"&#x":
- return unichr(int(text[3:-1], 16))
+ return chr(int(text[3:-1], 16))
else:
- return unichr(int(text[2:-1]))
+ return chr(int(text[2:-1]))
except ValueError:
pass
else:
# named entity
try:
- text = unichr(htmlentitydefs.name2codepoint[text[1:-1]])
+ text = chr(htmlentitydefs.name2codepoint[text[1:-1]])
except KeyError:
pass
return text # leave as is
- return re.sub(u"&#?\w+;", fixup, text)
+ return re.sub(u"&#?\\w+;", fixup, text)
def GetDecryptedBook(infile, kDatabases, androidFiles, serials, pids, starttime = time.time()):
# handle the obvious cases at the beginning
mb = topazextract.TopazBook(infile)
bookname = unescape(mb.getBookTitle())
- print u"Decrypting {1} ebook: {0}".format(bookname, mb.getBookType())
+ print(u"Decrypting {1} ebook: {0}".format(bookname, mb.getBookType()))
# copy list of pids
totalpids = list(pids)
totalpids.extend(kgenpids.getPidList(md1, md2, serials, kDatabases))
# remove any duplicates
totalpids = list(set(totalpids))
- print u"Found {1:d} keys to try after {0:.1f} seconds".format(time.time()-starttime, len(totalpids))
+ print(u"Found {1:d} keys to try after {0:.1f} seconds".format(time.time()-starttime, len(totalpids)))
#print totalpids
try:
mb.cleanup
raise
- print u"Decryption succeeded after {0:.1f} seconds".format(time.time()-starttime)
+ print(u"Decryption succeeded after {0:.1f} seconds".format(time.time()-starttime))
return mb
with open(dbfile, 'r') as keyfilein:
kindleDatabase = json.loads(keyfilein.read())
kDatabases.append([dbfile,kindleDatabase])
- except Exception, e:
- print u"Error getting database from file {0:s}: {1:s}".format(dbfile,e)
+ except Exception as e:
+ print(u"Error getting database from file {0:s}: {1:s}".format(dbfile,e))
traceback.print_exc()
try:
book = GetDecryptedBook(infile, kDatabases, androidFiles, serials, pids, starttime)
- except Exception, e:
- print u"Error decrypting book after {1:.1f} seconds: {0}".format(e.args[0],time.time()-starttime)
+ except Exception as e:
+ print(u"Error decrypting book after {1:.1f} seconds: {0}".format(e.args[0],time.time()-starttime))
traceback.print_exc()
return 1
outfile = os.path.join(outdir, outfilename + book.getBookExtension())
book.getFile(outfile)
- print u"Saved decrypted book {1:s} after {0:.1f} seconds".format(time.time()-starttime, outfilename)
+ print(u"Saved decrypted book {1:s} after {0:.1f} seconds".format(time.time()-starttime, outfilename))
if book.getBookType()==u"Topaz":
zipname = os.path.join(outdir, outfilename + u"_SVG.zip")
book.getSVGZip(zipname)
- print u"Saved SVG ZIP Archive for {1:s} after {0:.1f} seconds".format(time.time()-starttime, outfilename)
+ print(u"Saved SVG ZIP Archive for {1:s} after {0:.1f} seconds".format(time.time()-starttime, outfilename))
# remove internal temporary directory of Topaz pieces
book.cleanup()
def usage(progname):
- print u"Removes DRM protection from Mobipocket, Amazon KF8, Amazon Print Replica and Amazon Topaz ebooks"
- print u"Usage:"
- print u" {0} [-k <kindle.k4i>] [-p <comma separated PIDs>] [-s <comma separated Kindle serial numbers>] [ -a <AmazonSecureStorage.xml|backup.ab> ] <infile> <outdir>".format(progname)
+ print(u"Removes DRM protection from Mobipocket, Amazon KF8, Amazon Print Replica and Amazon Topaz ebooks")
+ print(u"Usage:")
+ print(u" {0} [-k <kindle.k4i>] [-p <comma separated PIDs>] [-s <comma separated Kindle serial numbers>] [ -a <AmazonSecureStorage.xml|backup.ab> ] <infile> <outdir>".format(progname))
#
# Main
def cli_main():
argv=unicode_argv()
progname = os.path.basename(argv[0])
- print u"K4MobiDeDrm v{0}.\nCopyright © 2008-2017 Apprentice Harper et al.".format(__version__)
+ print(u"K4MobiDeDrm v{0}.\nCopyright © 2008-2017 Apprentice Harper et al.".format(__version__))
try:
opts, args = getopt.getopt(argv[1:], "k:p:s:a:")
- except getopt.GetoptError, err:
- print u"Error in options or arguments: {0}".format(err.args[0])
+ except getopt.GetoptError as err:
+ print(u"Error in options or arguments: {0}".format(err.args[0]))
usage(progname)
sys.exit(2)
if len(args)<2:
# Engine to remove drm from Kindle KFX ebooks
+# 2.0 - Added Python 3 compatibility for calibre 5.0
+
+
import os
import shutil
import zipfile
try:
from cStringIO import StringIO
except ImportError:
- from StringIO import StringIO
+ try:
+ from StringIO import StringIO
+ except ImportError:
+ from io import StringIO
try:
from calibre_plugins.dedrm import ion
__license__ = 'GPL v3'
-__version__ = '1.0'
+__version__ = '2.0'
class KFXZipBook:
from __future__ import print_function
# kgenpids.py
-# Copyright © 2008-2017 Apprentice Harper et al.
+# Copyright © 2008-2020 Apprentice Harper et al.
__license__ = 'GPL v3'
-__version__ = '2.1'
+__version__ = '3.0'
# Revision history:
# 2.0 - Fix for non-ascii Windows user names
# 2.1 - Actual fix for non-ascii WIndows user names.
-# x.x - Return information needed for KFX decryption
+# 2.2 - Return information needed for KFX decryption
+# 3.0 - Added Python 3 compatibility for calibre 5.0
+
import sys
import os, csv
global charMap4
-charMap1 = 'n5Pr6St7Uv8Wx9YzAb0Cd1Ef2Gh3Jk4M'
-charMap3 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
-charMap4 = 'ABCDEFGHIJKLMNPQRSTUVWXYZ123456789'
+charMap1 = b'n5Pr6St7Uv8Wx9YzAb0Cd1Ef2Gh3Jk4M'
+charMap3 = b'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
+charMap4 = b'ABCDEFGHIJKLMNPQRSTUVWXYZ123456789'
# crypto digestroutines
import hashlib
def getTwoBitsFromBitField(bitField,offset):
byteNumber = offset // 4
bitPosition = 6 - 2*(offset % 4)
- return ord(bitField[byteNumber]) >> bitPosition & 3
+ return bitField[byteNumber] >> bitPosition & 3
# Returns the six bits at offset from a bit field
def getSixBitsFromBitField(bitField,offset):
# 8 bits to six bits encoding from hash to generate PID string
def encodePID(hash):
global charMap3
- PID = ''
+ PID = b''
for position in range (0,8):
- PID += charMap3[getSixBitsFromBitField(hash,position)]
+ PID += bytes([charMap3[getSixBitsFromBitField(hash,position)]])
return PID
# Encryption table used to generate the device PID
def generateDevicePID(table,dsn,nbRoll):
global charMap4
seed = generatePidSeed(table,dsn)
- pidAscii = ''
+ pidAscii = b''
pid = [(seed >>24) &0xFF,(seed >> 16) &0xff,(seed >> 8) &0xFF,(seed) & 0xFF,(seed>>24) & 0xFF,(seed >> 16) &0xff,(seed >> 8) &0xFF,(seed) & 0xFF]
index = 0
for counter in range (0,nbRoll):
index = (index+1) %8
for counter in range (0,8):
index = ((((pid[counter] >>5) & 3) ^ pid[counter]) & 0x1f) + (pid[counter] >> 7)
- pidAscii += charMap4[index]
+ pidAscii += bytes([charMap4[index]])
return pidAscii
def crc32(s):
for i in (0,1):
b = crc & 0xff
pos = (b // l) ^ (b % l)
- res += charMap4[pos%l]
+ res += bytes([charMap4[pos%l]])
crc >>= 8
return res
global charMap4
crc = crc32(s)
arr1 = [0]*l
- for i in xrange(len(s)):
- arr1[i%l] ^= ord(s[i])
+ for i in range(len(s)):
+ arr1[i%l] ^= s[i]
crc_bytes = [crc >> 24 & 0xff, crc >> 16 & 0xff, crc >> 8 & 0xff, crc & 0xff]
- for i in xrange(l):
+ for i in range(l):
arr1[i] ^= crc_bytes[i&3]
- pid = ""
- for i in xrange(l):
+ pid = b""
+ for i in range(l):
b = arr1[i] & 0xff
- pid+=charMap4[(b >> 7) + ((b >> 5 & 3) ^ (b & 0x1f))]
+ pid += bytes([charMap4[(b >> 7) + ((b >> 5 & 3) ^ (b & 0x1f))]])
return pid
pids=[]
- if isinstance(serialnum,unicode):
+ if isinstance(serialnum,str):
serialnum = serialnum.encode('utf-8')
# Compute book PID
pids.append(bookPID)
# compute fixed pid for old pre 2.5 firmware update pid as well
- kindlePID = pidFromSerial(serialnum, 7) + "*"
+ kindlePID = pidFromSerial(serialnum, 7) + b"*"
kindlePID = checksumPid(kindlePID)
pids.append(kindlePID)
try:
# Get the kindle account token, if present
- kindleAccountToken = (kindleDatabase[1])['kindle.account.tokens'].decode('hex')
+ kindleAccountToken = bytearray.fromhex((kindleDatabase[1])['kindle.account.tokens']).decode()
except KeyError:
kindleAccountToken=""
try:
# Get the DSN token, if present
- DSN = (kindleDatabase[1])['DSN'].decode('hex')
+ DSN = bytearray.fromhex((kindleDatabase[1])['DSN']).decode()
print(u"Got DSN key from database {0}".format(kindleDatabase[0]))
except KeyError:
# See if we have the info to generate the DSN
try:
# Get the Mazama Random number
- MazamaRandomNumber = (kindleDatabase[1])['MazamaRandomNumber'].decode('hex')
+ MazamaRandomNumber = bytearray.fromhex((kindleDatabase[1])['MazamaRandomNumber']).decode()
#print u"Got MazamaRandomNumber from database {0}".format(kindleDatabase[0])
try:
# Get the SerialNumber token, if present
- IDString = (kindleDatabase[1])['SerialNumber'].decode('hex')
+ IDString = bytearray.fromhex((kindleDatabase[1])['SerialNumber']).decode()
print(u"Got SerialNumber from database {0}".format(kindleDatabase[0]))
except KeyError:
# Get the IDString we added
- IDString = (kindleDatabase[1])['IDString'].decode('hex')
+ IDString = bytearray.fromhex((kindleDatabase[1])['IDString']).decode()
try:
# Get the UsernameHash token, if present
- encodedUsername = (kindleDatabase[1])['UsernameHash'].decode('hex')
+ encodedUsername = bytearray.fromhex((kindleDatabase[1])['UsernameHash']).decode()
print(u"Got UsernameHash from database {0}".format(kindleDatabase[0]))
except KeyError:
# Get the UserName we added
- UserName = (kindleDatabase[1])['UserName'].decode('hex')
+ UserName = bytearray.fromhex((kindleDatabase[1])['UserName']).decode()
# encode it
encodedUsername = encodeHash(UserName,charMap1)
#print u"encodedUsername",encodedUsername.encode('hex')
# Compute book PIDs
# book pid
- pidHash = SHA1(DSN+kindleAccountToken+rec209+token)
+ pidHash = SHA1(DSN.encode()+kindleAccountToken.encode()+rec209+token)
bookPID = encodePID(pidHash)
bookPID = checksumPid(bookPID)
pids.append(bookPID)
# variant 1
- pidHash = SHA1(kindleAccountToken+rec209+token)
+ pidHash = SHA1(kindleAccountToken.encode()+rec209+token)
bookPID = encodePID(pidHash)
bookPID = checksumPid(bookPID)
pids.append(bookPID)
# variant 2
- pidHash = SHA1(DSN+rec209+token)
+ pidHash = SHA1(DSN.encode()+rec209+token)
bookPID = encodePID(pidHash)
bookPID = checksumPid(bookPID)
pids.append(bookPID)
for kDatabase in kDatabases:
try:
pidlst.extend(getK4Pids(md1, md2, kDatabase))
- except Exception, e:
+ except Exception as e:
print(u"Error getting PIDs from database {0}: {1}".format(kDatabase[0],e.args[0]))
traceback.print_exc()
for serialnum in serials:
try:
pidlst.extend(getKindlePids(md1, md2, serialnum))
- except Exception, e:
+ except Exception as e:
print(u"Error getting PIDs from serial number {0}: {1}".format(serialnum ,e.args[0]))
traceback.print_exc()
# Copyright © 2008-2020 Apprentice Harper et al.
__license__ = 'GPL v3'
-__version__ = '2.7'
+__version__ = '3.0'
# Revision history:
# 1.0 - Kindle info file decryption, extracted from k4mobidedrm, etc.
# 2.5 - Final Fix for Windows user names with non-ascii characters, thanks to oneofusoneofus
# 2.6 - Start adding support for Kindle 1.25+ .kinf2018 file
# 2.7 - Finish .kinf2018 support, PC & Mac by Apprentice Sakuya
+# 3.0 - Added Python 3 compatibility for calibre 5.0
"""
"""
import sys, os, re
-from struct import pack, unpack
+from struct import pack, unpack, unpack_from
import json
import getopt
argvencoding = sys.stdin.encoding
if argvencoding == None:
argvencoding = "utf-8"
- return [arg if (type(arg) == unicode) else unicode(arg,argvencoding) for arg in sys.argv]
+ return arg
class DrmException(Exception):
pass
numBlocks, numExtraBytes = divmod(len(self.bytesToDecrypt), self.blockSize)
if more == None: # no more calls to decrypt, should have all the data
if numExtraBytes != 0:
- raise DecryptNotBlockAlignedError, 'Data not block aligned on decrypt'
+ raise DecryptNotBlockAlignedError('Data not block aligned on decrypt')
# hold back some bytes in case last decrypt has zero len
if (more != None) and (numExtraBytes == 0) and (numBlocks >0) :
def removePad(self, paddedBinaryString, blockSize):
""" Remove padding from a binary string """
if not(0<len(paddedBinaryString)):
- raise DecryptNotBlockAlignedError, 'Expected More Data'
+ raise DecryptNotBlockAlignedError('Expected More Data')
return paddedBinaryString[:-ord(paddedBinaryString[-1])]
class noPadding(Pad):
def __init__(self, key = None, padding = padWithPadLen(), keySize=16):
""" Initialize AES, keySize is in bytes """
if not (keySize == 16 or keySize == 24 or keySize == 32) :
- raise BadKeySizeError, 'Illegal AES key size, must be 16, 24, or 32 bytes'
+ raise BadKeySizeError('Illegal AES key size, must be 16, 24, or 32 bytes')
Rijndael.__init__( self, key, padding=padding, keySize=keySize, blockSize=16 )
# Returns Environmental Variables that contain unicode
def getEnvironmentVariable(name):
import ctypes
- name = unicode(name) # make sure string argument is unicode
n = ctypes.windll.kernel32.GetEnvironmentVariableW(name, None, 0)
if n == 0:
return None
# store values used in decryption
DB['IDString'] = GetIDString()
DB['UserName'] = GetUserName()
- print u"Decrypted key file using IDString '{0:s}' and UserName '{1:s}'".format(GetIDString(), GetUserName().encode('hex'))
+ print(u"Decrypted key file using IDString '{0:s}' and UserName '{1:s}'".format(GetIDString(), GetUserName().encode('hex')))
else:
- print u"Couldn't decrypt file."
+ print(u"Couldn't decrypt file.")
DB = {}
return DB
elif isosx:
pass
if len(DB)>6:
# store values used in decryption
- print u"Decrypted key file using IDString '{0:s}' and UserName '{1:s}'".format(IDString, GetUserName())
+ print(u"Decrypted key file using IDString '{0:s}' and UserName '{1:s}'".format(IDString, GetUserName()))
DB['IDString'] = IDString
DB['UserName'] = GetUserName()
else:
- print u"Couldn't decrypt file."
+ print(u"Couldn't decrypt file.")
DB = {}
return DB
else:
outfile = outpath
with file(outfile, 'w') as keyfileout:
keyfileout.write(json.dumps(keys[0]))
- print u"Saved a key to {0}".format(outfile)
+ print(u"Saved a key to {0}".format(outfile))
else:
keycount = 0
for key in keys:
break
with file(outfile, 'w') as keyfileout:
keyfileout.write(json.dumps(key))
- print u"Saved a key to {0}".format(outfile)
+ print(u"Saved a key to {0}".format(outfile))
return True
return False
def usage(progname):
- print u"Finds, decrypts and saves the default Kindle For Mac/PC encryption keys."
- print u"Keys are saved to the current directory, or a specified output directory."
- print u"If a file name is passed instead of a directory, only the first key is saved, in that file."
- print u"Usage:"
- print u" {0:s} [-h] [-k <kindle.info>] [<outpath>]".format(progname)
+ print(u"Finds, decrypts and saves the default Kindle For Mac/PC encryption keys.")
+ print(u"Keys are saved to the current directory, or a specified output directory.")
+ print(u"If a file name is passed instead of a directory, only the first key is saved, in that file.")
+ print(u"Usage:")
+ print(u" {0:s} [-h] [-k <kindle.info>] [<outpath>]".format(progname))
def cli_main():
sys.stderr=SafeUnbuffered(sys.stderr)
argv=unicode_argv()
progname = os.path.basename(argv[0])
- print u"{0} v{1}\nCopyright © 2010-2016 by some_updates, Apprentice Alf and Apprentice Harper".format(progname,__version__)
+ print(u"{0} v{1}\nCopyright © 2010-2016 by some_updates, Apprentice Alf and Apprentice Harper".format(progname,__version__))
try:
opts, args = getopt.getopt(argv[1:], "hk:")
- except getopt.GetoptError, err:
- print u"Error in options or arguments: {0}".format(err.args[0])
+ except getopt.GetoptError as err:
+ print(u"Error in options or arguments: {0}".format(err.args[0]))
usage(progname)
sys.exit(2)
outpath = os.path.realpath(os.path.normpath(outpath))
if not getkey(outpath, files):
- print u"Could not retrieve Kindle for Mac/PC key."
+ print(u"Could not retrieve Kindle for Mac/PC key.")
return 0
keyfileout.write(json.dumps(key))
success = True
tkMessageBox.showinfo(progname, u"Key successfully retrieved to {0}".format(outfile))
- except DrmException, e:
+ except DrmException as e:
tkMessageBox.showerror(progname, u"Error: {0}".format(str(e)))
except Exception:
root.wm_state('normal')
# 0.3 updated for unicode
# 0.4 Added support for serial numbers starting with '9', fixed unicode bugs.
# 0.5 moved unicode_argv call inside main for Windows DeDRM compatibility
+# 1.0 Added Python 3 compatibility for calibre 5.0
from __future__ import print_function
import sys
if self.encoding == None:
self.encoding = "utf-8"
def write(self, data):
- if isinstance(data,unicode):
+ if isinstance(data,bytes):
data = data.encode(self.encoding,"replace")
self.stream.write(data)
self.stream.flush()
# Remove Python executable and commands if present
start = argc.value - len(sys.argv)
return [argv[i] for i in
- xrange(start, argc.value)]
+ range(start, argc.value)]
# if we don't have any arguments at all, just pass back script name
# this should never happen
return [u"kindlepid.py"]
argvencoding = sys.stdin.encoding
if argvencoding == None:
argvencoding = "utf-8"
- return [arg if (type(arg) == unicode) else unicode(arg,argvencoding) for arg in sys.argv]
-
-if sys.hexversion >= 0x3000000:
- print('This script is incompatible with Python 3.x. Please install Python 2.7.x.')
- sys.exit(2)
+ return sys.argv
letters = 'ABCDEFGHIJKLMNPQRSTUVWXYZ123456789'
return (~binascii.crc32(s,-1))&0xFFFFFFFF
def checksumPid(s):
- crc = crc32(s)
+ crc = crc32(s.encode('ascii'))
crc = crc ^ (crc >> 16)
res = s
l = len(letters)
crc = crc32(s)
arr1 = [0]*l
- for i in xrange(len(s)):
- arr1[i%l] ^= ord(s[i])
+ for i in range(len(s)):
+ arr1[i%l] ^= s[i]
crc_bytes = [crc >> 24 & 0xff, crc >> 16 & 0xff, crc >> 8 & 0xff, crc & 0xff]
- for i in xrange(l):
+ for i in range(l):
arr1[i] ^= crc_bytes[i&3]
pid = ''
- for i in xrange(l):
+ for i in range(l):
b = arr1[i] & 0xff
pid+=letters[(b >> 7) + ((b >> 5 & 3) ^ (b & 0x1f))]
if __name__ == "__main__":
- sys.stdout=SafeUnbuffered(sys.stdout)
- sys.stderr=SafeUnbuffered(sys.stderr)
+ #sys.stdout=SafeUnbuffered(sys.stdout)
+ #sys.stderr=SafeUnbuffered(sys.stderr)
sys.exit(cli_main())
# mobidedrm.py
# Copyright © 2008 The Dark Reverser
-# Portions © 2008–2017 Apprentice Harper et al.
+# Portions © 2008–2020 Apprentice Harper et al.
from __future__ import print_function
__license__ = 'GPL v3'
-__version__ = u"0.42"
+__version__ = u"1.00"
# This is a python script. You need a Python interpreter to run it.
# For example, ActiveState Python, which exists for windows.
# 0.40 - moved unicode_argv call inside main for Windows DeDRM compatibility
# 0.41 - Fixed potential unicode problem in command line calls
# 0.42 - Added GPL v3 licence. updated/removed some print statements
+# 3.00 - Added Python 3 compatibility for calibre 5.0
import sys
import os
if self.encoding == None:
self.encoding = "utf-8"
def write(self, data):
- if isinstance(data,unicode):
+ if isinstance(data,bytes):
data = data.encode(self.encoding,"replace")
self.stream.write(data)
self.stream.flush()
# Remove Python executable and commands if present
start = argc.value - len(sys.argv)
return [argv[i] for i in
- xrange(start, argc.value)]
+ range(start, argc.value)]
# if we don't have any arguments at all, just pass back script name
# this should never happen
return [u"mobidedrm.py"]
argvencoding = sys.stdin.encoding
if argvencoding == None:
argvencoding = 'utf-8'
- return [arg if (type(arg) == unicode) else unicode(arg,argvencoding) for arg in sys.argv]
+ return sys.argv
class DrmException(Exception):
# Implementation of Pukall Cipher 1
def PC1(key, src, decryption=True):
# if we can get it from alfcrypto, use that
- try:
- return Pukall_Cipher().PC1(key,src,decryption)
- except NameError:
- pass
- except TypeError:
- pass
+ #try:
+ # return Pukall_Cipher().PC1(key,src,decryption)
+ #except NameError:
+ # pass
+ #except TypeError:
+ # pass
# use slow python version, since Pukall_Cipher didn't load
sum1 = 0;
if len(key)!=16:
DrmException (u"PC1: Bad key length")
wkey = []
- for i in xrange(8):
- wkey.append(ord(key[i*2])<<8 | ord(key[i*2+1]))
- dst = ""
- for i in xrange(len(src)):
+ for i in range(8):
+ wkey.append(key[i*2]<<8 | key[i*2+1])
+ dst = b''
+ for i in range(len(src)):
temp1 = 0;
byteXorVal = 0;
- for j in xrange(8):
+ for j in range(8):
temp1 ^= wkey[j]
sum2 = (sum2+j)*20021 + sum1
sum1 = (temp1*346)&0xFFFF
sum2 = (sum2+sum1)&0xFFFF
temp1 = (temp1*20021+1)&0xFFFF
byteXorVal ^= temp1 ^ sum2
- curByte = ord(src[i])
+ curByte = src[i]
if not decryption:
keyXorVal = curByte * 257;
curByte = ((curByte ^ (byteXorVal >> 8)) ^ byteXorVal) & 0xFF
if decryption:
keyXorVal = curByte * 257;
- for j in xrange(8):
+ for j in range(8):
wkey[j] ^= keyXorVal;
- dst+=chr(curByte)
+ dst+=bytes([curByte])
return dst
def checksumPid(s):
for i in (0,1):
b = crc & 0xff
pos = (b // l) ^ (b % l)
- res += letters[pos%l]
+ res += letters[pos%l].encode('ascii')
crc >>= 8
return res
if size <= 0:
return result
while True:
- v = ord(ptr[size-1])
+ v = ptr[size-1]
result |= (v & 0x7F) << bitpos
bitpos += 7
size -= 1
# if multibyte data is included in the encryped data, we'll
# have already cleared this flag.
if flags & 1:
- num += (ord(ptr[size - num - 1]) & 0x3) + 1
+ num += (ptr[size - num - 1] & 0x3) + 1
return num
print(u"AlfCrypto not found. Using python PC1 implementation.")
# initial sanity check on file
- self.data_file = file(infile, 'rb').read()
+ self.data_file = open(infile, 'rb').read()
self.mobi_data = ''
self.header = self.data_file[0:78]
- if self.header[0x3C:0x3C+8] != 'BOOKMOBI' and self.header[0x3C:0x3C+8] != 'TEXtREAd':
+ if self.header[0x3C:0x3C+8] != b'BOOKMOBI' and self.header[0x3C:0x3C+8] != b'TEXtREAd':
raise DrmException(u"Invalid file format")
self.magic = self.header[0x3C:0x3C+8]
self.crypto_type = -1
# build up section offset and flag info
self.num_sections, = struct.unpack('>H', self.header[76:78])
self.sections = []
- for i in xrange(self.num_sections):
+ for i in range(self.num_sections):
offset, a1,a2,a3,a4 = struct.unpack('>LBBBB', self.data_file[78+i*8:78+i*8+8])
flags, val = a1, a2<<16|a3<<8|a4
self.sections.append( (offset, flags, val) )
exth = ''
if exth_flag & 0x40:
exth = self.sect[16 + self.mobi_length:]
- if (len(exth) >= 12) and (exth[:4] == 'EXTH'):
+ if (len(exth) >= 12) and (exth[:4] == b'EXTH'):
nitems, = struct.unpack('>I', exth[8:12])
pos = 12
- for i in xrange(nitems):
+ for i in range(nitems):
type, size = struct.unpack('>II', exth[pos: pos + 8])
content = exth[pos + 8: pos + size]
self.meta_array[type] = content
# reset the text to speech flag and clipping limit, if present
if type == 401 and size == 9:
# set clipping limit to 100%
- self.patchSection(0, '\144', 16 + self.mobi_length + pos + 8)
+ self.patchSection(0, b'\144', 16 + self.mobi_length + pos + 8)
elif type == 404 and size == 9:
# make sure text to speech is enabled
- self.patchSection(0, '\0', 16 + self.mobi_length + pos + 8)
+ self.patchSection(0, b'\0', 16 + self.mobi_length + pos + 8)
# print type, size, content, content.encode('hex')
pos += size
- except:
- pass
+ except Exception as e:
+ print(u"Cannot set meta_array: Error: {:s}".format(e.args[0]))
def getBookTitle(self):
codec_map = {
codec = codec_map[self.mobi_codepage]
if title == '':
title = self.header[:32]
- title = title.split('\0')[0]
- return unicode(title, codec)
+ title = title.split(b'\0')[0]
+ return title.decode(codec)
def getPIDMetaInfo(self):
- rec209 = ''
- token = ''
+ rec209 = b''
+ token = b''
if 209 in self.meta_array:
rec209 = self.meta_array[209]
data = rec209
# The 209 data comes in five byte groups. Interpret the last four bytes
# of each group as a big endian unsigned integer to get a key value
# if that key exists in the meta_array, append its contents to the token
- for i in xrange(0,len(data),5):
+ for i in range(0,len(data),5):
val, = struct.unpack('>I',data[i+1:i+5])
sval = self.meta_array.get(val,'')
token += sval
def parseDRM(self, data, count, pidlist):
found_key = None
- keyvec1 = '\x72\x38\x33\xB0\xB4\xF2\xE3\xCA\xDF\x09\x01\xD6\xE2\xE0\x3F\x96'
+ keyvec1 = b'\x72\x38\x33\xB0\xB4\xF2\xE3\xCA\xDF\x09\x01\xD6\xE2\xE0\x3F\x96'
for pid in pidlist:
- bigpid = pid.ljust(16,'\0')
+ bigpid = pid.ljust(16,b'\0')
+ bigpid = bigpid
temp_key = PC1(keyvec1, bigpid, False)
- temp_key_sum = sum(map(ord,temp_key)) & 0xff
+ temp_key_sum = sum(temp_key) & 0xff
found_key = None
- for i in xrange(count):
+ for i in range(count):
verification, size, type, cksum, cookie = struct.unpack('>LLLBxxx32s', data[i*0x30:i*0x30+0x30])
if cksum == temp_key_sum:
cookie = PC1(temp_key, cookie)
# Then try the default encoding that doesn't require a PID
pid = '00000000'
temp_key = keyvec1
- temp_key_sum = sum(map(ord,temp_key)) & 0xff
- for i in xrange(count):
+ temp_key_sum = sum(temp_key) & 0xff
+ for i in range(count):
verification, size, type, cksum, cookie = struct.unpack('>LLLBxxx32s', data[i*0x30:i*0x30+0x30])
if cksum == temp_key_sum:
cookie = PC1(temp_key, cookie)
return [found_key,pid]
def getFile(self, outpath):
- file(outpath,'wb').write(self.mobi_data)
+ open(outpath,'wb').write(self.mobi_data)
def getBookType(self):
if self.print_replica:
raise DrmException(u"Cannot decode library or rented ebooks.")
goodpids = []
+ # print("DEBUG ==== pidlist = ", pidlist)
for pid in pidlist:
if len(pid)==10:
if checksumPid(pid[0:-2]) != pid:
else:
print(u"Warning: PID {0} has wrong number of digits".format(pid))
+ # print(u"======= DEBUG good pids = ", goodpids)
+
if self.crypto_type == 1:
t1_keyvec = 'QDCVEPMU675RUBSZ'
if self.magic == 'TEXtREAd':
if not found_key:
raise DrmException(u"No key found in {0:d} keys tried.".format(len(goodpids)))
# kill the drm keys
- self.patchSection(0, '\0' * drm_size, drm_ptr)
+ self.patchSection(0, b'\0' * drm_size, drm_ptr)
# kill the drm pointers
- self.patchSection(0, '\xff' * 4 + '\0' * 12, 0xA8)
+ self.patchSection(0, b'\xff' * 4 + b'\0' * 12, 0xA8)
if pid=='00000000':
print(u"File has default encryption, no specific key needed.")
print(u"File is encoded with PID {0}.".format(checksumPid(pid)))
# clear the crypto type
- self.patchSection(0, "\0" * 2, 0xC)
+ self.patchSection(0, b'\0' * 2, 0xC)
# decrypt sections
print(u"Decrypting. Please wait . . .", end=' ')
mobidataList = []
mobidataList.append(self.data_file[:self.sections[1][0]])
- for i in xrange(1, self.records+1):
+ for i in range(1, self.records+1):
data = self.loadSection(i)
extra_size = getSizeOfTrailingDataEntries(data, len(data), self.extra_data_flags)
if i%100 == 0:
mobidataList.append(data[-extra_size:])
if self.num_sections > self.records+1:
mobidataList.append(self.data_file[self.sections[self.records+1][0]:])
- self.mobi_data = "".join(mobidataList)
+ self.mobi_data = b''.join(mobidataList)
print(u"done")
return
pidlist = []
try:
stripped_file = getUnencryptedBook(infile, pidlist)
- file(outfile, 'wb').write(stripped_file)
- except DrmException, e:
+ open(outfile, 'wb').write(stripped_file)
+ except DrmException as e:
print(u"MobiDeDRM v{0} Error: {1:s}".format(__version__,e.args[0]))
return 1
return 0
keyname = u"{0}_{1}".format(name.strip(),ccn.strip()[-4:])
keyvalue = generate_key(name, ccn)
userkeys.append([keyname,keyvalue])
- except Exception, e:
+ except Exception as e:
traceback.print_exc()
- print e.args[0]
+ print(e.args[0])
pass
return userkeys
keyname = u"{0}_{1}".format(name.strip(),cc.strip()[-4:])
keyvalue = getuser_key(name,cc).encode('hex')
userkeys.append([keyname,keyvalue])
- except Exception, e:
+ except Exception as e:
traceback.print_exc()
- print e.args[0]
+ print(e.args[0])
pass
return userkeys
return
- print u"{0} v{1}: Importing configuration data from old DeDRM plugins".format(PLUGIN_NAME, PLUGIN_VERSION)
+ print(u"{0} v{1}: Importing configuration data from old DeDRM plugins".format(PLUGIN_NAME, PLUGIN_VERSION))
IGNOBLEPLUGINNAME = "Ignoble Epub DeDRM"
EREADERPLUGINNAME = "eReader PDB 2 PML"
sc = config['plugin_customization']
val = sc.pop(IGNOBLEPLUGINNAME, None)
if val is not None:
- print u"{0} v{1}: Converting old Ignoble plugin configuration string.".format(PLUGIN_NAME, PLUGIN_VERSION)
+ print(u"{0} v{1}: Converting old Ignoble plugin configuration string.".format(PLUGIN_NAME, PLUGIN_VERSION))
priorkeycount = len(dedrmprefs['bandnkeys'])
userkeys = parseIgnobleString(str(val))
for keypair in userkeys:
value = keypair[1]
dedrmprefs.addnamedvaluetoprefs('bandnkeys', name, value)
addedkeycount = len(dedrmprefs['bandnkeys'])-priorkeycount
- print u"{0} v{1}: {2:d} Barnes and Noble {3} imported from old Ignoble plugin configuration string".format(PLUGIN_NAME, PLUGIN_VERSION, addedkeycount, u"key" if addedkeycount==1 else u"keys")
+ print(u"{0} v{1}: {2:d} Barnes and Noble {3} imported from old Ignoble plugin configuration string".format(PLUGIN_NAME, PLUGIN_VERSION, addedkeycount, u"key" if addedkeycount==1 else u"keys"))
# Make the json write all the prefs to disk
dedrmprefs.writeprefs(False)
# old string to stored keys... get that personal data out of plain sight.
val = sc.pop(EREADERPLUGINNAME, None)
if val is not None:
- print u"{0} v{1}: Converting old eReader plugin configuration string.".format(PLUGIN_NAME, PLUGIN_VERSION)
+ print(u"{0} v{1}: Converting old eReader plugin configuration string.".format(PLUGIN_NAME, PLUGIN_VERSION))
priorkeycount = len(dedrmprefs['ereaderkeys'])
userkeys = parseeReaderString(str(val))
for keypair in userkeys:
value = keypair[1]
dedrmprefs.addnamedvaluetoprefs('ereaderkeys', name, value)
addedkeycount = len(dedrmprefs['ereaderkeys'])-priorkeycount
- print u"{0} v{1}: {2:d} eReader {3} imported from old eReader plugin configuration string".format(PLUGIN_NAME, PLUGIN_VERSION, addedkeycount, u"key" if addedkeycount==1 else u"keys")
+ print(u"{0} v{1}: {2:d} eReader {3} imported from old eReader plugin configuration string".format(PLUGIN_NAME, PLUGIN_VERSION, addedkeycount, u"key" if addedkeycount==1 else u"keys"))
# Make the json write all the prefs to disk
dedrmprefs.writeprefs(False)
# get old Kindle plugin configuration string
val = sc.pop(OLDKINDLEPLUGINNAME, None)
if val is not None:
- print u"{0} v{1}: Converting old Kindle plugin configuration string.".format(PLUGIN_NAME, PLUGIN_VERSION)
+ print(u"{0} v{1}: Converting old Kindle plugin configuration string.".format(PLUGIN_NAME, PLUGIN_VERSION))
priorpidcount = len(dedrmprefs['pids'])
priorserialcount = len(dedrmprefs['serials'])
pids, serials = parseKindleString(val)
dedrmprefs.addvaluetoprefs('serials',serial)
addedpidcount = len(dedrmprefs['pids']) - priorpidcount
addedserialcount = len(dedrmprefs['serials']) - priorserialcount
- print u"{0} v{1}: {2:d} {3} and {4:d} {5} imported from old Kindle plugin configuration string.".format(PLUGIN_NAME, PLUGIN_VERSION, addedpidcount, u"PID" if addedpidcount==1 else u"PIDs", addedserialcount, u"serial number" if addedserialcount==1 else u"serial numbers")
+ print(u"{0} v{1}: {2:d} {3} and {4:d} {5} imported from old Kindle plugin configuration string.".format(PLUGIN_NAME, PLUGIN_VERSION, addedpidcount, u"PID" if addedpidcount==1 else u"PIDs", addedserialcount, u"serial number" if addedserialcount==1 else u"serial numbers"))
# Make the json write all the prefs to disk
dedrmprefs.writeprefs(False)
dedrmprefs.addnamedvaluetoprefs('bandnkeys', name, value)
addedkeycount = len(dedrmprefs['bandnkeys'])-priorkeycount
if addedkeycount > 0:
- print u"{0} v{1}: {2:d} Barnes and Noble {3} imported from config folder.".format(PLUGIN_NAME, PLUGIN_VERSION, addedkeycount, u"key file" if addedkeycount==1 else u"key files")
+ print(u"{0} v{1}: {2:d} Barnes and Noble {3} imported from config folder.".format(PLUGIN_NAME, PLUGIN_VERSION, addedkeycount, u"key file" if addedkeycount==1 else u"key files"))
# Make the json write all the prefs to disk
dedrmprefs.writeprefs(False)
dedrmprefs.addnamedvaluetoprefs('adeptkeys', name, value)
addedkeycount = len(dedrmprefs['adeptkeys'])-priorkeycount
if addedkeycount > 0:
- print u"{0} v{1}: {2:d} Adobe Adept {3} imported from config folder.".format(PLUGIN_NAME, PLUGIN_VERSION, addedkeycount, u"keyfile" if addedkeycount==1 else u"keyfiles")
+ print(u"{0} v{1}: {2:d} Adobe Adept {3} imported from config folder.".format(PLUGIN_NAME, PLUGIN_VERSION, addedkeycount, u"keyfile" if addedkeycount==1 else u"keyfiles"))
# Make the json write all the prefs to disk
dedrmprefs.writeprefs(False)
addedkeycount = len(dedrmprefs['bandnkeys']) - priorkeycount
# no need to delete old prefs, since they contain no recoverable private data
if addedkeycount > 0:
- print u"{0} v{1}: {2:d} Barnes and Noble {3} imported from Ignoble plugin preferences.".format(PLUGIN_NAME, PLUGIN_VERSION, addedkeycount, u"key" if addedkeycount==1 else u"keys")
+ print(u"{0} v{1}: {2:d} Barnes and Noble {3} imported from Ignoble plugin preferences.".format(PLUGIN_NAME, PLUGIN_VERSION, addedkeycount, u"key" if addedkeycount==1 else u"keys"))
# Make the json write all the prefs to disk
dedrmprefs.writeprefs(False)
dedrmprefs.addvaluetoprefs('serials',serial)
addedpidcount = len(dedrmprefs['pids']) - priorpidcount
if addedpidcount > 0:
- print u"{0} v{1}: {2:d} {3} imported from Kindle plugin preferences".format(PLUGIN_NAME, PLUGIN_VERSION, addedpidcount, u"PID" if addedpidcount==1 else u"PIDs")
+ print(u"{0} v{1}: {2:d} {3} imported from Kindle plugin preferences".format(PLUGIN_NAME, PLUGIN_VERSION, addedpidcount, u"PID" if addedpidcount==1 else u"PIDs"))
addedserialcount = len(dedrmprefs['serials']) - priorserialcount
if addedserialcount > 0:
- print u"{0} v{1}: {2:d} {3} imported from Kindle plugin preferences".format(PLUGIN_NAME, PLUGIN_VERSION, addedserialcount, u"serial number" if addedserialcount==1 else u"serial numbers")
+ print(u"{0} v{1}: {2:d} {3} imported from Kindle plugin preferences".format(PLUGIN_NAME, PLUGIN_VERSION, addedserialcount, u"serial number" if addedserialcount==1 else u"serial numbers"))
try:
if 'wineprefix' in kindleprefs and kindleprefs['wineprefix'] != "":
dedrmprefs.set('adobewineprefix',kindleprefs['wineprefix'])
dedrmprefs.set('kindlewineprefix',kindleprefs['wineprefix'])
- print u"{0} v{1}: WINEPREFIX ‘(2)’ imported from Kindle plugin preferences".format(PLUGIN_NAME, PLUGIN_VERSION, kindleprefs['wineprefix'])
+ print(u"{0} v{1}: WINEPREFIX ‘(2)’ imported from Kindle plugin preferences".format(PLUGIN_NAME, PLUGIN_VERSION, kindleprefs['wineprefix']))
except:
traceback.print_exc()
# Make the json write all the prefs to disk
dedrmprefs.writeprefs()
- print u"{0} v{1}: Finished setting up configuration data.".format(PLUGIN_NAME, PLUGIN_VERSION)
+ print(u"{0} v{1}: Finished setting up configuration data.".format(PLUGIN_NAME, PLUGIN_VERSION))
import sys
import os
import re
-import ineptepub
-import ignobleepub
-import epubtest
-import zipfix
-import ineptpdf
-import erdr2pml
-import k4mobidedrm
+import calibre_plugins.dedrm.ineptepub
+import calibre_plugins.dedrm.ignobleepub
+import calibre_plugins.dedrm.epubtest
+import calibre_plugins.dedrm.zipfix
+import calibre_plugins.dedrm.ineptpdf
+import calibre_plugins.dedrm.erdr2pml
+import calibre_plugins.dedrm.k4mobidedrm
import traceback
def decryptepub(infile, outdir, rscpath):
if rv == 0:
print("Decrypted Adobe ePub with key file {0}".format(filename))
break
- except Exception, e:
+ except Exception as e:
errlog += traceback.format_exc()
errlog += str(e)
rv = 1
if rv == 0:
print("Decrypted B&N ePub with key file {0}".format(filename))
break
- except Exception, e:
+ except Exception as e:
errlog += traceback.format_exc()
errlog += str(e)
rv = 1
rv = ineptpdf.decryptBook(userkey, infile, outfile)
if rv == 0:
break
- except Exception, e:
+ except Exception as e:
errlog += traceback.format_exc()
errlog += str(e)
rv = 1
return 1
try:
rv = erdr2pml.decryptBook(infile, outpath, True, erdr2pml.getuser_key(name, cc8))
- except Exception, e:
+ except Exception as e:
errlog += traceback.format_exc()
errlog += str(e)
rv = 1
androidFiles.append(dpath)
try:
rv = k4mobidedrm.decryptBook(infile, outdir, kDatabaseFiles, androidFiles, serialnums, pidnums)
- except Exception, e:
+ except Exception as e:
errlog += traceback.format_exc()
errlog += str(e)
rv = 1
try :
data = file(filepath,'rb').read()
self.prefs[key] = data
- except Exception, e:
+ except Exception as e:
pass
def getPreferences(self):
else:
try:
file(filepath,'wb').write(data)
- except Exception, e:
+ except Exception as e:
pass
self.prefs = newprefs
return
# Changelog
# 4.9 - moved unicode_argv call inside main for Windows DeDRM compatibility
# 5.0 - Fixed potential unicode problem with command line interface
+# 6.0 - Added Python 3 compatibility for calibre 5.0
from __future__ import print_function
-__version__ = '5.0'
+__version__ = '6.0'
import sys
import os, csv, getopt
import traceback
from struct import pack
from struct import unpack
-from alfcrypto import Topaz_Cipher
+from calibre_plugins.dedrm.alfcrypto import Topaz_Cipher
class SafeUnbuffered:
def __init__(self, stream):
if self.encoding == None:
self.encoding = "utf-8"
def write(self, data):
- if isinstance(data,unicode):
+ if isinstance(data,bytes):
data = data.encode(self.encoding,"replace")
self.stream.write(data)
self.stream.flush()
# Remove Python executable and commands if present
start = argc.value - len(sys.argv)
return [argv[i] for i in
- xrange(start, argc.value)]
+ range(start, argc.value)]
# if we don't have any arguments at all, just pass back script name
# this should never happen
return [u"mobidedrm.py"]
argvencoding = sys.stdin.encoding
if argvencoding == None:
argvencoding = 'utf-8'
- return [arg if (type(arg) == unicode) else unicode(arg,argvencoding) for arg in sys.argv]
+ return argv
#global switch
debug = False
class TopazBook:
def __init__(self, filename):
- self.fo = file(filename, 'rb')
+ self.fo = open(filename, 'rb')
self.outdir = tempfile.mkdtemp()
# self.outdir = 'rawdat'
self.bookPayloadOffset = 0
fixedimage=True
try:
keydata = self.getBookPayloadRecord('dkey', 0)
- except DrmException, e:
+ except DrmException as e:
print(u"no dkey record found, book may not be encrypted")
print(u"attempting to extrct files without a book key")
self.createBookDirectory()
data = keydata
try:
bookKeys+=decryptDkeyRecords(data,pid)
- except DrmException, e:
+ except DrmException as e:
pass
else:
bookKey = bookKeys[0]
self.setBookKey(bookKey)
self.createBookDirectory()
- self.extractFiles()
+ self.extractFiles()
print(u"Successfully Extracted Topaz contents")
if inCalibre:
from calibre_plugins.dedrm import genbook
print(u".", end=' ')
record = self.getBookPayloadRecord(name,index)
if record != '':
- file(outputFile, 'wb').write(record)
+ open(outputFile, 'wb').write(record)
print(u" ")
def getFile(self, zipname):
try:
opts, args = getopt.getopt(argv[1:], "k:p:s:x")
- except getopt.GetoptError, err:
+ except getopt.GetoptError as err:
print(u"Error in options or arguments: {0}".format(err.args[0]))
usage(progname)
return 1
# removing internal temporary directory of pieces
tb.cleanup()
- except DrmException, e:
+ except DrmException as e:
print(u"Decryption failed\n{0}".format(traceback.format_exc()))
try:
pass
return 1
- except Exception, e:
- print(u"Decryption failed\m{0}".format(traceback.format_exc()))
+ except Exception as e:
+ print(u"Decryption failed\n{0}".format(traceback.format_exc()))
try:
tb.cleanup()
except:
from __future__ import with_statement
-from ignoblekeygen import generate_key
+from calibre_plugins.dedrm.ignoblekeygen import generate_key
__license__ = 'GPL v3'
def uStrCmp (s1, s2, caseless=False):
import unicodedata as ud
- str1 = s1 if isinstance(s1, unicode) else unicode(s1)
- str2 = s2 if isinstance(s2, unicode) else unicode(s2)
+ str1 = s1 if isinstance(s1, unicode) else s1.decode('utf-8')
+ str2 = s2 if isinstance(s2, unicode) else s2.decode('utf-8')
if caseless:
return ud.normalize('NFC', str1.lower()) == ud.normalize('NFC', str2.lower())
else:
cmdline = cmdline.encode(sys.getfilesystemencoding())
p2 = Process(cmdline, shell=True, bufsize=1, stdin=None, stdout=sys.stdout, stderr=STDOUT, close_fds=False)
result = p2.wait("wait")
- except Exception, e:
+ except Exception as e:
print(u"{0} v{1}: Wine subprocess call error: {2}".format(PLUGIN_NAME, PLUGIN_VERSION, e.args[0]))
if wineprefix != "" and os.path.exists(wineprefix):
cmdline = u"WINEPREFIX=\"{2}\" wine C:\\Python27\\python.exe \"{0}\" \"{1}\"".format(scriptpath,outdirpath,wineprefix)
cmdline = cmdline.encode(sys.getfilesystemencoding())
p2 = Process(cmdline, shell=True, bufsize=1, stdin=None, stdout=sys.stdout, stderr=STDOUT, close_fds=False)
result = p2.wait("wait")
- except Exception, e:
+ except Exception as e:
print(u"{0} v{1}: Wine subprocess call error: {2}".format(PLUGIN_NAME, PLUGIN_VERSION, e.args[0]))
# try finding winekeys anyway, even if above code errored
Read and write ZIP files.
"""
import struct, os, time, sys, shutil
-import binascii, cStringIO, stat
+import binascii, stat
import io
import re
+from io import BytesIO
+
try:
import zlib # We may need its compression method
crc32 = zlib.crc32
# The "end of central directory" structure, magic number, size, and indices
# (section V.I in the format document)
-structEndArchive = "<4s4H2LH"
-stringEndArchive = "PK\005\006"
+structEndArchive = b"<4s4H2LH"
+stringEndArchive = b"PK\005\006"
sizeEndCentDir = struct.calcsize(structEndArchive)
_ECD_SIGNATURE = 0
# The "central directory" structure, magic number, size, and indices
# of entries in the structure (section V.F in the format document)
-structCentralDir = "<4s4B4HL2L5H2L"
-stringCentralDir = "PK\001\002"
+structCentralDir = b"<4s4B4HL2L5H2L"
+stringCentralDir = b"PK\001\002"
sizeCentralDir = struct.calcsize(structCentralDir)
# indexes of entries in the central directory structure
# The "local file header" structure, magic number, size, and indices
# (section V.A in the format document)
-structFileHeader = "<4s2B4HL2L2H"
-stringFileHeader = "PK\003\004"
+structFileHeader = b"<4s2B4HL2L2H"
+stringFileHeader = b"PK\003\004"
sizeFileHeader = struct.calcsize(structFileHeader)
_FH_SIGNATURE = 0
_FH_EXTRA_FIELD_LENGTH = 11
# The "Zip64 end of central directory locator" structure, magic number, and size
-structEndArchive64Locator = "<4sLQL"
-stringEndArchive64Locator = "PK\x06\x07"
+structEndArchive64Locator = b"<4sLQL"
+stringEndArchive64Locator = b"PK\x06\x07"
sizeEndCentDir64Locator = struct.calcsize(structEndArchive64Locator)
# The "Zip64 end of central directory" record, magic number, size, and indices
# (section V.G in the format document)
-structEndArchive64 = "<4sQ2H2L4Q"
-stringEndArchive64 = "PK\x06\x06"
+structEndArchive64 = b"<4sQ2H2L4Q"
+stringEndArchive64 = b"PK\x06\x06"
sizeEndCentDir64 = struct.calcsize(structEndArchive64)
_CD64_SIGNATURE = 0
# Terminate the file name at the first null byte. Null bytes in file
# names are used as tricks by viruses in archives.
- null_byte = filename.find(chr(0))
+ null_byte = filename.find(b"\0")
if null_byte >= 0:
filename = filename[0:null_byte]
# This is used to ensure paths in generated ZIP files always use
self.date_time = date_time # year, month, day, hour, min, sec
# Standard values:
self.compress_type = ZIP_STORED # Type of compression for the file
- self.comment = "" # Comment for each file
- self.extra = "" # ZIP extra data
+ self.comment = b"" # Comment for each file
+ self.extra = b"" # ZIP extra data
if sys.platform == 'win32':
self.create_system = 0 # System which created ZIP archive
else:
return header + filename + extra
def _encodeFilenameFlags(self):
- if isinstance(self.filename, unicode):
+ if isinstance(self.filename, bytes):
+ return self.filename, self.flag_bits
+ else:
try:
return self.filename.encode('ascii'), self.flag_bits
except UnicodeEncodeError:
return self.filename.encode('utf-8'), self.flag_bits | 0x800
- else:
- return self.filename, self.flag_bits
-
- def _decodeFilename(self):
- if self.flag_bits & 0x800:
- try:
- #print "decoding filename",self.filename
- return self.filename.decode('utf-8')
- except:
- return self.filename
- else:
- return self.filename
def _decodeExtra(self):
# Try to decode the extra field.
elif ln == 0:
counts = ()
else:
- raise RuntimeError, "Corrupt extra field %s"%(ln,)
+ raise RuntimeError("Corrupt extra field %s"%(ln,))
idx = 0
# ZIP64 extension (large files and/or large archives)
- if self.file_size in (0xffffffffffffffffL, 0xffffffffL):
+ if self.file_size in (0xffffffffffffffff, 0xffffffff):
self.file_size = counts[idx]
idx += 1
- if self.compress_size == 0xFFFFFFFFL:
+ if self.compress_size == 0xFFFFFFFF:
self.compress_size = counts[idx]
idx += 1
- if self.header_offset == 0xffffffffL:
+ if self.header_offset == 0xffffffff:
old = self.header_offset
self.header_offset = counts[idx]
idx+=1
if self._compress_type == ZIP_DEFLATED:
self._decompressor = zlib.decompressobj(-15)
- self._unconsumed = ''
+ self._unconsumed = b''
- self._readbuffer = ''
+ self._readbuffer = b''
self._offset = 0
self._universal = 'U' in mode
if not self._universal:
return io.BufferedIOBase.readline(self, limit)
- line = ''
+ line = b''
while limit < 0 or len(line) < limit:
readahead = self.peek(2)
- if readahead == '':
+ if readahead == b'':
return line
#
If the argument is omitted, None, or negative, data is read and returned until EOF is reached..
"""
- buf = ''
+ buf = b''
while n < 0 or n is None or n > len(buf):
data = self.read1(n)
if len(data) == 0:
self._compress_left -= len(data)
if data and self._decrypter is not None:
- data = ''.join(map(self._decrypter, data))
+ data = b''.join(map(self._decrypter, data))
if self._compress_type == ZIP_STORED:
self._readbuffer = self._readbuffer[self._offset:] + data
pass
elif compression == ZIP_DEFLATED:
if not zlib:
- raise RuntimeError,\
- "Compression requires the (missing) zlib module"
+ raise RuntimeError(
+ "Compression requires the (missing) zlib module")
else:
- raise RuntimeError, "That compression method is not supported"
+ raise RuntimeError("That compression method is not supported")
self._allowZip64 = allowZip64
self._didModify = False
self.compression = compression # Method of compression
self.mode = key = mode.replace('b', '')[0]
self.pwd = None
- self.comment = ''
+ self.comment = b''
# Check if we were passed a file-like object
- if isinstance(file, basestring):
+ if isinstance(file, str):
self._filePassed = 0
self.filename = file
modeDict = {'r' : 'rb', 'w': 'wb', 'a' : 'r+b'}
if not self._filePassed:
self.fp.close()
self.fp = None
- raise RuntimeError, 'Mode must be "r", "w" or "a"'
+ raise RuntimeError('Mode must be "r", "w" or "a"')
def __enter__(self):
return self
fp = self.fp
endrec = _EndRecData(fp)
if not endrec:
- raise BadZipfile, "File is not a zip file"
+ raise BadZipfile("File is not a zip file")
if self.debug > 1:
- print endrec
+ print(endrec)
size_cd = endrec[_ECD_SIZE] # bytes in central directory
offset_cd = endrec[_ECD_OFFSET] # offset of central directory
self.comment = endrec[_ECD_COMMENT] # archive comment
if self.debug > 2:
inferred = concat + offset_cd
- print "given, inferred, offset", offset_cd, inferred, concat
+ print("given, inferred, offset", offset_cd, inferred, concat)
# self.start_dir: Position of start of central directory
self.start_dir = offset_cd + concat
fp.seek(self.start_dir, 0)
data = fp.read(size_cd)
- fp = cStringIO.StringIO(data)
+ fp = BytesIO(data)
total = 0
while total < size_cd:
centdir = fp.read(sizeCentralDir)
if centdir[0:4] != stringCentralDir:
- raise BadZipfile, "Bad magic number for central directory"
+ raise BadZipfile("Bad magic number for central directory")
centdir = struct.unpack(structCentralDir, centdir)
if self.debug > 2:
- print centdir
+ print(centdir)
filename = fp.read(centdir[_CD_FILENAME_LENGTH])
# Create ZipInfo instance to store file information
x = ZipInfo(filename)
x._decodeExtra()
x.header_offset = x.header_offset + concat
- x.filename = x._decodeFilename()
self.filelist.append(x)
self.NameToInfo[x.filename] = x
+ centdir[_CD_COMMENT_LENGTH])
if self.debug > 2:
- print "total", total
+ print("total", total)
def namelist(self):
def printdir(self):
"""Print a table of contents for the zip file."""
- print "%-46s %19s %12s" % ("File Name", "Modified ", "Size")
+ print("%-46s %19s %12s" % ("File Name", "Modified ", "Size"))
for zinfo in self.filelist:
date = "%d-%02d-%02d %02d:%02d:%02d" % zinfo.date_time[:6]
- print "%-46s %s %12d" % (zinfo.filename, date, zinfo.file_size)
+ print("%-46s %s %12d" % (zinfo.filename, date, zinfo.file_size))
def testzip(self):
"""Read all the files and check the CRC."""
def open(self, name, mode="r", pwd=None):
"""Return file-like object for 'name'."""
if mode not in ("r", "U", "rU"):
- raise RuntimeError, 'open() requires mode "r", "U", or "rU"'
+ raise RuntimeError('open() requires mode "r", "U", or "rU"')
if not self.fp:
- raise RuntimeError, \
- "Attempt to read ZIP archive that was already closed"
+ raise RuntimeError(
+ "Attempt to read ZIP archive that was already closed")
# Only open a new file for instances where we were not
# given a file object in the constructor
# Skip the file header:
fheader = zef_file.read(sizeFileHeader)
if fheader[0:4] != stringFileHeader:
- raise BadZipfile, "Bad magic number for file header"
+ raise BadZipfile("Bad magic number for file header")
fheader = struct.unpack(structFileHeader, fheader)
fname = zef_file.read(fheader[_FH_FILENAME_LENGTH])
zef_file.read(fheader[_FH_EXTRA_FIELD_LENGTH])
if fname != zinfo.orig_filename:
- raise BadZipfile, \
+ raise BadZipfile(
'File name in directory "%s" and header "%s" differ.' % (
- zinfo.orig_filename, fname)
+ zinfo.orig_filename, fname))
# check for encrypted flag & handle password
is_encrypted = zinfo.flag_bits & 0x1
if not pwd:
pwd = self.pwd
if not pwd:
- raise RuntimeError, "File %s is encrypted, " \
- "password required for extraction" % name
+ raise RuntimeError("File %s is encrypted, " \
+ "password required for extraction" % name)
zd = _ZipDecrypter(pwd)
# The first 12 bytes in the cypher stream is an encryption header
return targetpath
source = self.open(member, pwd=pwd)
- target = file(targetpath, "wb")
+ target = open(targetpath, "wb")
shutil.copyfileobj(source, target)
source.close()
target.close()
"""Check for errors before writing a file to the archive."""
if zinfo.filename in self.NameToInfo:
if self.debug: # Warning for duplicate names
- print "Duplicate name:", zinfo.filename
+ print("Duplicate name:", zinfo.filename)
if self.mode not in ("w", "a"):
- raise RuntimeError, 'write() requires mode "w" or "a"'
+ raise RuntimeError('write() requires mode "w" or "a"')
if not self.fp:
- raise RuntimeError, \
- "Attempt to write ZIP archive that was already closed"
+ raise RuntimeError(
+ "Attempt to write ZIP archive that was already closed")
if zinfo.compress_type == ZIP_DEFLATED and not zlib:
- raise RuntimeError, \
- "Compression requires the (missing) zlib module"
+ raise RuntimeError(
+ "Compression requires the (missing) zlib module")
if zinfo.compress_type not in (ZIP_STORED, ZIP_DEFLATED):
- raise RuntimeError, \
- "That compression method is not supported"
+ raise RuntimeError(
+ "That compression method is not supported")
if zinfo.file_size > ZIP64_LIMIT:
if not self._allowZip64:
raise LargeZipFile("Filesize would require ZIP64 extensions")
if isdir:
arcname += '/'
zinfo = ZipInfo(arcname, date_time)
- zinfo.external_attr = (st[0] & 0xFFFF) << 16L # Unix attributes
+ zinfo.external_attr = (st[0] & 0xFFFF) << 16 # Unix attributes
if compress_type is None:
zinfo.compress_type = self.compression
else:
date_time=time.localtime(time.time())[:6])
zinfo.compress_type = self.compression
- zinfo.external_attr = 0600 << 16
+ zinfo.external_attr = 0x0600 << 16
else:
zinfo = zinfo_or_arcname
if zinfo.header_offset > ZIP64_LIMIT:
extra.append(zinfo.header_offset)
- header_offset = 0xffffffffL
+ header_offset = 0xffffffff
else:
header_offset = zinfo.header_offset
0, zinfo.internal_attr, zinfo.external_attr,
header_offset)
except DeprecationWarning:
- print >>sys.stderr, (structCentralDir,
+ print(structCentralDir,
stringCentralDir, create_version,
zinfo.create_system, extract_version, zinfo.reserved,
zinfo.flag_bits, zinfo.compress_type, dostime, dosdate,
zinfo.CRC, compress_size, file_size,
len(zinfo.filename), len(extra_data), len(zinfo.comment),
0, zinfo.internal_attr, zinfo.external_attr,
- header_offset)
+ header_offset, sys.stderr)
raise
self.fp.write(centdir)
self.fp.write(filename)
else:
basename = name
if self.debug:
- print "Adding package in", pathname, "as", basename
+ print("Adding package in", pathname, "as", basename)
fname, arcname = self._get_codename(initname[0:-3], basename)
if self.debug:
- print "Adding", arcname
+ print("Adding", arcname)
self.write(fname, arcname)
dirlist = os.listdir(pathname)
dirlist.remove("__init__.py")
fname, arcname = self._get_codename(path[0:-3],
basename)
if self.debug:
- print "Adding", arcname
+ print("Adding", arcname)
self.write(fname, arcname)
else:
# This is NOT a package directory, add its files at top level
if self.debug:
- print "Adding files from directory", pathname
+ print("Adding files from directory", pathname)
for filename in os.listdir(pathname):
path = os.path.join(pathname, filename)
root, ext = os.path.splitext(filename)
fname, arcname = self._get_codename(path[0:-3],
basename)
if self.debug:
- print "Adding", arcname
+ print("Adding", arcname)
self.write(fname, arcname)
else:
if pathname[-3:] != ".py":
- raise RuntimeError, \
- 'Files added with writepy() must end with ".py"'
+ raise RuntimeError(
+ 'Files added with writepy() must end with ".py"')
fname, arcname = self._get_codename(pathname[0:-3], basename)
if self.debug:
- print "Adding file", arcname
+ print("Adding file", arcname)
self.write(fname, arcname)
def _get_codename(self, pathname, basename):
os.stat(file_pyc).st_mtime < os.stat(file_py).st_mtime:
import py_compile
if self.debug:
- print "Compiling", file_py
+ print("Compiling", file_py)
try:
py_compile.compile(file_py, file_pyc, None, True)
- except py_compile.PyCompileError,err:
- print err.msg
+ except py_compile.PyCompileError as err:
+ print(err.msg)
fname = file_pyc
else:
fname = file_pyc
args = sys.argv[1:]
if not args or args[0] not in ('-l', '-c', '-e', '-t'):
- print USAGE
+ print(USAGE)
sys.exit(1)
if args[0] == '-l':
if len(args) != 2:
- print USAGE
+ print(USAGE)
sys.exit(1)
zf = ZipFile(args[1], 'r')
zf.printdir()
elif args[0] == '-t':
if len(args) != 2:
- print USAGE
+ print(USAGE)
sys.exit(1)
zf = ZipFile(args[1], 'r')
zf.testzip()
- print "Done testing"
+ print("Done testing")
elif args[0] == '-e':
if len(args) != 3:
- print USAGE
+ print(USAGE)
sys.exit(1)
zf = ZipFile(args[1], 'r')
elif args[0] == '-c':
if len(args) < 3:
- print USAGE
+ print(USAGE)
sys.exit(1)
def addToZip(zf, path, zippath):
#!/usr/bin/env python
# -*- coding: utf-8 -*-
-# zipfix.py, version 1.1
-# Copyright © 2010-2013 by some_updates, DiapDealer and Apprentice Alf
+# zipfix.py
+# Copyright © 2010-2020 by some_updates, DiapDealer and Apprentice Alf
# Released under the terms of the GNU General Public Licence, version 3
# <http://www.gnu.org/licenses/>
# Revision history:
# 1.0 - Initial release
# 1.1 - Updated to handle zip file metadata correctly
+# 2.0 - Added Python 3 compatibility for calibre 5.0
"""
Re-write zip (or ePub) fixing problems with file names (and mimetype entry).
import sys
import zlib
-import zipfilerugged
+import calibre_plugins.dedrm.zipfilerugged as zipfilerugged
import os
import os.path
import getopt
self.inzip = zipfilerugged.ZipFile(zinput,'r')
self.outzip = zipfilerugged.ZipFile(zoutput,'w')
# open the input zip for reading only as a raw file
- self.bzf = file(zinput,'rb')
+ self.bzf = open(zinput,'rb')
def getlocalname(self, zi):
local_header_offset = zi.header_offset
# 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('mimetype',compress_type=zipfilerugged.ZIP_STORED)
+ mimeinfo = ZipInfo(b'mimetype',compress_type=zipfilerugged.ZIP_STORED)
mimeinfo.internal_attr = 1 # text file
try:
# if the mimetype is present, get its info, including time-stamp
mimeinfo.create_system = oldmimeinfo.create_system
except:
pass
- self.outzip.writestr(mimeinfo, _MIMETYPE)
+ self.outzip.writestr(mimeinfo, _MIMETYPE.encode('ascii'))
# write the rest of the files
for zinfo in self.inzip.infolist():
fr = fixZip(infile, outfile)
fr.fix()
return 0
- except Exception, e:
+ except Exception as e:
print("Error Occurred ", e)
return 2
__docformat__ = 'restructuredtext en'
+import codecs
import os, traceback, zipfile
try:
from PyQt5.Qt import QToolButton, QUrl
except ImportError:
from PyQt4.Qt import QToolButton, QUrl
-
+
from calibre.gui2 import open_url, question_dialog
from calibre.gui2.actions import InterfaceAction
from calibre.utils.config import config_dir
from calibre_plugins.obok_dedrm.dialogs import (SelectionDialog, DecryptAddProgressDialog,
AddEpubFormatsProgressDialog, ResultsSummaryDialog)
from calibre_plugins.obok_dedrm.config import plugin_prefs as cfg
-from calibre_plugins.obok_dedrm.__init__ import (PLUGIN_NAME, PLUGIN_SAFE_NAME,
+from calibre_plugins.obok_dedrm.__init__ import (PLUGIN_NAME, PLUGIN_SAFE_NAME,
PLUGIN_VERSION, PLUGIN_DESCRIPTION, HELPFILE_NAME)
from calibre_plugins.obok_dedrm.utilities import (
get_icon, set_plugin_icon_resources, showErrorDlg, format_plural,
def genesis(self):
icon_resources = self.load_resources(PLUGIN_ICONS)
set_plugin_icon_resources(PLUGIN_NAME, icon_resources)
-
+
self.qaction.setIcon(get_icon(PLUGIN_ICONS[0]))
self.qaction.triggered.connect(self.launchObok)
self.gui.keyboard.finalize()
# Get a list of Kobo titles
books = self.build_book_list()
if len(books) < 1:
- msg = _('<p>No books found in Kobo Library\nAre you sure it\'s installed\configured\synchronized?')
+ msg = _('<p>No books found in Kobo Library\nAre you sure it\'s installed/configured/synchronized?')
showErrorDlg(msg, None)
return
-
+
# Check to see if a key can be retrieved using the legacy obok method.
legacy_key = legacy_obok().get_legacy_cookie_id
if legacy_key is not None:
# Close Kobo Library object
self.library.close()
- # If we have decrypted books to work with, feed the list of decrypted books details
+ # If we have decrypted books to work with, feed the list of decrypted books details
# and the callback function (self.add_new_books) to the ProgressDialog dispatcher.
if len(self.books_to_add):
d = DecryptAddProgressDialog(self.gui, self.books_to_add, self.add_new_books, self.db, 'calibre',
def get_decrypted_kobo_books(self, book):
'''
This method is a call-back function used by DecryptAddProgressDialog in dialogs.py to decrypt Kobo books
-
+
:param book: A KoboBook object that is to be decrypted.
'''
print (_('{0} - Decrypting {1}').format(PLUGIN_NAME + ' v' + PLUGIN_VERSION, book.title))
'''
This method is a call-back function used by DecryptAddProgressDialog in dialogs.py to add books to calibre
(It's set up to handle multiple books, but will only be fed books one at a time by DecryptAddProgressDialog)
-
+
:param books_to_add: List of calibre bookmaps (created in get_decrypted_kobo_books)
'''
added = self.db.add_books(books_to_add, add_duplicates=False, run_hooks=False)
def add_epub_format(self, book_id, mi, path):
'''
This method is a call-back function used by AddEpubFormatsProgressDialog in dialogs.py
-
+
:param book_id: calibre ID of the book to add the encrypted epub to.
:param mi: calibre metadata object
:param path: path to the decrypted epub (temp file)
self.formats_to_add.append((home_id, mi, tmp_file))
else:
self.no_home_for_book.append(mi)
- # If we found homes for decrypted epubs in existing calibre entries, feed the list of decrypted book
+ # If we found homes for decrypted epubs in existing calibre entries, feed the list of decrypted book
# details and the callback function (self.add_epub_format) to the ProgressDialog dispatcher.
if self.formats_to_add:
d = AddEpubFormatsProgressDialog(self.gui, self.formats_to_add, self.add_epub_format)
sd = ResultsSummaryDialog(self.gui, caption, msg, log)
sd.exec_()
return
-
+
def ask_about_inserting_epubs(self):
'''
- Build question dialog with details about kobo books
+ Build question dialog with details about kobo books
that couldn't be added to calibre as new books.
'''
''' Terisa: Improve the message
msg = _('<p><b>{0}</b> -- not added because of {1} in your library.<br /><br />').format(self.duplicate_book_list[0][0].title, self.duplicate_book_list[0][2])
msg += _('Would you like to try and add the EPUB format to an available calibre duplicate?<br /><br />')
msg += _('NOTE: no pre-existing EPUB will be overwritten.')
-
+
return question_dialog(self.gui, caption, msg, det_msg)
def find_a_home(self, ids):
'''
Find the ID of the first EPUB-Free duplicate available
-
+
:param ids: List of calibre IDs that might serve as a home.
'''
for id in ids:
zin = zipfile.ZipFile(book.filename, 'r')
#print ('Kobo library filename: {0}'.format(book.filename))
for userkey in self.userkeys:
- print (_('Trying key: '), userkey.encode('hex_codec'))
+ print (_('Trying key: '), codecs.encode(userkey, 'hex'))
check = True
try:
fileout = PersistentTemporaryFile('.epub', dir=self.tdir)
if cancelled_count > 0:
log += _('<p><b>Book imports cancelled by user:</b> {}</p>\n').format(cancelled_count)
return (msg, log)
- log += _('<p><b>New EPUB formats inserted in existing calibre books:</b> {0}</p>\n').format(len(self.successful_format_adds))
+ log += _('<p><b>New EPUB formats inserted in existing calibre books:</b> {0}</p>\n').format(len(self.successful_format_adds))
if self.successful_format_adds:
log += '<ul>\n'
for id, mi in self.successful_format_adds:
log += _('<p><b>Format imports cancelled by user:</b> {}</p>\n').format(cancelled_count)
return (msg, log)
else:
-
+
# Single book ... don't get fancy.
if self.ids_of_new_books:
title = self.ids_of_new_books[0][1].title
reason = _('of unknown reasons. Gosh I\'m embarrassed!')
msg = _('<p>{0} not added because {1}').format(title, reason)
return (msg, log)
-
+
def selected_key(self):
for key, value in self.values.iteritems():
- if value == unicode(self.currentText()).strip():
+ if value == self.currentText().strip():
return key
def selected_key(self):
for key, value in self.values.iteritems():
- if key == unicode(self.currentText()).strip():
+ if key == self.currentText().strip():
return key
def save_settings(self):
- plugin_prefs['finding_homes_for_formats'] = unicode(self.find_homes.currentText())
+ plugin_prefs['finding_homes_for_formats'] = self.find_homes.currentText()
plugin_prefs['kobo_serials'] = self.tmpserials
plugin_prefs['kobo_directory'] = self.kobodirectory
def delete_key(self):
if not self.listy.currentItem():
return
- keyname = unicode(self.listy.currentItem().text())
+ keyname = self.listy.currentItem().text()
if not question_dialog(self, "{0} {1}: Confirm Delete".format(PLUGIN_NAME, PLUGIN_VERSION), u"Do you really want to delete the {1} <strong>{0}</strong>?".format(keyname, self.key_type_name), show_copy_button=False, default_yes=False):
return
self.plugin_keys.remove(keyname)
@property
def key_name(self):
- return unicode(self.key_ledit.text()).strip()
+ return self.key_ledit.text().strip()
@property
def key_value(self):
- return unicode(self.key_ledit.text()).strip()
+ return self.key_ledit.text().strip()
def accept(self):
if len(self.key_name) == 0 or self.key_name.isspace():
#!/usr/bin/env python
# -*- coding: utf-8 -*-
+# Version 4.0.0 September 2020
+# Python 3.0
+#
# Version 3.2.5 December 2016
# Improve detection of good text decryption.
#
"""Manage all Kobo books, either encrypted or DRM-free."""
from __future__ import print_function
-__version__ = '3.2.4'
-__about__ = u"Obok v{0}\nCopyright © 2012-2016 Physisticated et al.".format(__version__)
+__version__ = '4.0.0'
+__about__ = u"Obok v{0}\nCopyright © 2012-2020 Physisticated et al.".format(__version__)
import sys
import os
raise ENCRYPTIONError(_('Failed to initialize AES key'))
def decrypt(self, data):
- clear = ''
+ clear = b''
for i in range(0, len(data), 16):
out = create_string_buffer(16)
rv = AES_ecb_encrypt(data[i:i+16], out, self._key, 0)
if self.encoding == None:
self.encoding = "utf-8"
def write(self, data):
- if isinstance(data,unicode):
+ if isinstance(data,bytes):
data = data.encode(self.encoding,"replace")
self.stream.write(data)
self.stream.flush()
print(self.newdb.name)
olddb = open(kobodb, 'rb')
self.newdb.write(olddb.read(18))
- self.newdb.write('\x01\x01')
+ self.newdb.write(b'\x01\x01')
olddb.read(2)
self.newdb.write(olddb.read())
olddb.close()
pass
row = cursor.fetchone()
return userids
-
+
def __getuserkeys (self, macaddr):
userids = self.__getuserids()
userkeys = []
for hash in KOBO_HASH_KEYS:
- deviceid = hashlib.sha256(hash + macaddr).hexdigest()
+ deviceid = hashlib.sha256((hash + macaddr).encode('ascii')).hexdigest()
for userid in userids:
- userkey = hashlib.sha256(deviceid + userid).hexdigest()
+ userkey = hashlib.sha256((deviceid + userid).encode('ascii')).hexdigest()
userkeys.append(binascii.a2b_hex(userkey[32:]))
return userkeys
# Convert relative URIs
href = item.attrib['href']
if not c.match(href):
- href = string.join((basedir, href), '')
+ href = ''.join((basedir, href))
# Update books we've found from the DB.
if href in self._encryptedfiles:
stride = 1
print(u"Checking text:{0}:".format(contents[:10]))
# check for byte order mark
- if contents[:3]=="\xef\xbb\xbf":
+ if contents[:3]==b"\xef\xbb\xbf":
# seems to be utf-8 with BOM
print(u"Could be utf-8 with BOM")
textoffset = 3
- elif contents[:2]=="\xfe\xff":
+ elif contents[:2]==b"\xfe\xff":
# seems to be utf-16BE
print(u"Could be utf-16BE")
textoffset = 3
stride = 2
- elif contents[:2]=="\xff\xfe":
+ elif contents[:2]==b"\xff\xfe":
# seems to be utf-16LE
print(u"Could be utf-16LE")
textoffset = 2
stride = 2
else:
print(u"Perhaps utf-8 without BOM")
-
+
# now check that the first few characters are in the ASCII range
- for i in xrange(textoffset,textoffset+5*stride,stride):
- if ord(contents[i])<32 or ord(contents[i])>127:
+ for i in range(textoffset,textoffset+5*stride,stride):
+ if contents[i]<32 or contents[i]>127:
# Non-ascii, so decryption probably failed
- print(u"Bad character at {0}, value {1}".format(i,ord(contents[i])))
+ print(u"Bad character at {0}, value {1}".format(i,contents[i]))
raise ValueError
print(u"Seems to be good text")
return True
- if contents[:5]=="<?xml" or contents[:8]=="\xef\xbb\xbf<?xml":
+ if contents[:5]==b"<?xml" or contents[:8]==b"\xef\xbb\xbf<?xml":
# utf-8
return True
- elif contents[:14]=="\xfe\xff\x00<\x00?\x00x\x00m\x00l":
+ elif contents[:14]==b"\xfe\xff\x00<\x00?\x00x\x00m\x00l":
# utf-16BE
return True
- elif contents[:14]=="\xff\xfe<\x00?\x00x\x00m\x00l\x00":
+ elif contents[:14]==b"\xff\xfe<\x00?\x00x\x00m\x00l\x00":
# utf-16LE
return True
- elif contents[:9]=="<!DOCTYPE" or contents[:12]=="\xef\xbb\xbf<!DOCTYPE":
+ elif contents[:9]==b"<!DOCTYPE" or contents[:12]==b"\xef\xbb\xbf<!DOCTYPE":
# utf-8 of weird <!DOCTYPE start
return True
- elif contents[:22]=="\xfe\xff\x00<\x00!\x00D\x00O\x00C\x00T\x00Y\x00P\x00E":
+ elif contents[:22]==b"\xfe\xff\x00<\x00!\x00D\x00O\x00C\x00T\x00Y\x00P\x00E":
# utf-16BE of weird <!DOCTYPE start
return True
- elif contents[:22]=="\xff\xfe<\x00!\x00D\x00O\x00C\x00T\x00Y\x00P\x00E\x00":
+ elif contents[:22]==b"\xff\xfe<\x00!\x00D\x00O\x00C\x00T\x00Y\x00P\x00E\x00":
# utf-16LE of weird <!DOCTYPE start
return True
else:
print(u"Bad XML: {0}".format(contents[:8]))
raise ValueError
elif self.mimetype == 'image/jpeg':
- if contents[:3] == '\xff\xd8\xff':
+ if contents[:3] == b'\xff\xd8\xff':
return True
else:
- print(u"Bad JPEG: {0}".format(contents[:3].encode('hex')))
+ print(u"Bad JPEG: {0}".format(contents[:3].hex()))
raise ValueError()
return False
return 0
result = 1
for userkey in lib.userkeys:
- print(u"Trying key: {0}".format(userkey.encode('hex_codec')))
+ print(u"Trying key: {0}".format(userkey.hex()))
try:
zout = zipfile.ZipFile(outname, "w", zipfile.ZIP_DEFLATED)
for filename in zin.namelist():
print(u"{0}: {1}".format(i + 1, book.title))
print(u"Or 'all'")
- choice = raw_input(u"Convert book number... ")
+ choice = input(u"Convert book number... ")
if choice == u'all':
books = list(lib.books)
else:
import os, struct, time
-from StringIO import StringIO
+try:
+ from StringIO import StringIO
+except ImportError:
+ from io import StringIO
from traceback import print_exc
try:
from PyQt5.Qt import (Qt, QDialog, QPixmap, QIcon, QLabel, QHBoxLayout, QFont, QTableWidgetItem)
except ImportError:
from PyQt4.Qt import (Qt, QDialog, QPixmap, QIcon, QLabel, QHBoxLayout, QFont, QTableWidgetItem)
-
+
from calibre.utils.config import config_dir
from calibre.constants import iswindows, DEBUG
from calibre import prints
from calibre.gui2 import (error_dialog, gprefs)
from calibre.gui2.actions import menu_action_unique_name
-from calibre_plugins.obok_dedrm.__init__ import (PLUGIN_NAME,
+from calibre_plugins.obok_dedrm.__init__ import (PLUGIN_NAME,
PLUGIN_SAFE_NAME, PLUGIN_VERSION, PLUGIN_DESCRIPTION)
plugin_ID = None
def convert_qvariant(x):
vt = x.type()
if vt == x.String:
- return unicode(x.toString())
+ return x.toString()
if vt == x.List:
return [convert_qvariant(i) for i in x.toList()]
return x.toPyObject()
def format_plural(number, possessive=False):
'''
Cosmetic ditty to provide the proper string formatting variable to handle singular/plural situations
-
+
:param: number: variable that represents the count/len of something
'''
if not possessive:
'''
if trcbk:
error= ''
- f=StringIO()
+ f=StringIO()
print_exc(file=f)
error_mess = f.getvalue().splitlines()
for line in error_mess:
from __future__ import with_statement
# ignoblekey.py
-# Copyright © 2015 Apprentice Alf and Apprentice Harper
+# Copyright © 2015-2020 Apprentice Harper et al.
# Based on kindlekey.py, Copyright © 2010-2013 by some_updates and Apprentice Alf
# Revision history:
# 1.0 - Initial release
# 1.1 - remove duplicates and return last key as single key
+# 2.0 - Python 3
"""
Get Barnes & Noble EPUB user key from nook Studio log file
"""
__license__ = 'GPL v3'
-__version__ = "1.1"
+__version__ = "2.0"
import sys
import os
keyfileout.write(key)
success = True
tkMessageBox.showinfo(progname, u"Key successfully retrieved to {0}".format(outfile))
- except DrmException, e:
+ except DrmException as e:
tkMessageBox.showerror(progname, u"Error: {0}".format(str(e)))
except Exception:
root.wm_state('normal')
import shutil
-DEDRM_SRC_DIR = 'DeDRM_Plugin'
-DEDRM_README= 'DeDRM_Plugin_ReadMe.txt'
+DEDRM_SRC_DIR = 'DeDRM_plugin'
+DEDRM_README= 'DeDRM_plugin_ReadMe.txt'
OBOK_SRC_DIR = 'Obok_plugin'
-OBOK_README = 'Obok_plugin_ReadMe.txt'
+OBOK_README = 'obok_plugin_ReadMe.txt'
RELEASE_DIR = 'release'
def make_calibre_plugin():