From de50a02af92ef2bdd142dfb0bfb8e288ebf3bb67 Mon Sep 17 00:00:00 2001 From: Apprentice Harper Date: Sun, 27 Sep 2020 11:54:49 +0100 Subject: [PATCH] More generic 3.0 changes, to be tested. --- DeDRM_plugin/__init__.py | 118 +++++++------ DeDRM_plugin/adobekey.py | 24 +-- DeDRM_plugin/aescbc.py | 5 +- DeDRM_plugin/alfcrypto.py | 7 +- DeDRM_plugin/androidkindlekey.py | 76 ++++---- DeDRM_plugin/argv_utils.py | 4 +- DeDRM_plugin/askfolder_ed.py | 4 +- DeDRM_plugin/config.py | 290 +++++++++++++++---------------- DeDRM_plugin/convert2xml.py | 13 +- DeDRM_plugin/encodebase64.py | 46 ----- DeDRM_plugin/epubtest.py | 10 +- DeDRM_plugin/erdr2pml.py | 84 +++++---- DeDRM_plugin/genbook.py | 12 +- DeDRM_plugin/ignobleepub.py | 95 +++++----- DeDRM_plugin/ignoblekey.py | 53 +++--- DeDRM_plugin/ignoblekeyfetch.py | 53 +++--- DeDRM_plugin/ignoblekeygen.py | 45 +++-- DeDRM_plugin/ignoblepdf.py | 68 ++++---- DeDRM_plugin/ineptepub.py | 74 ++++---- DeDRM_plugin/ineptpdf.py | 68 ++++---- DeDRM_plugin/ion.py | 4 +- DeDRM_plugin/k4mobidedrm.py | 58 +++---- DeDRM_plugin/kfxdedrm.py | 21 +-- DeDRM_plugin/kgenpids.py | 27 ++- DeDRM_plugin/kindlekey.py | 70 ++++---- DeDRM_plugin/kindlepid.py | 22 +-- DeDRM_plugin/mobidedrm.py | 78 ++++----- DeDRM_plugin/prefs.py | 42 ++--- DeDRM_plugin/scriptinterface.py | 2 +- DeDRM_plugin/simpleprefs.py | 3 +- DeDRM_plugin/topazextract.py | 120 ++++++------- DeDRM_plugin/utilities.py | 4 +- DeDRM_plugin/wineutils.py | 33 ++-- DeDRM_plugin/zipfilerugged.py | 7 +- DeDRM_plugin/zipfix.py | 6 +- Obok_plugin/action.py | 6 +- Obok_plugin/common_utils.py | 13 +- Obok_plugin/config.py | 52 +++--- Obok_plugin/obok/obok.py | 94 +++++----- Obok_plugin/utilities.py | 9 +- Other_Tools/Kobo/obok.py | 86 ++++----- make_release.py | 4 +- 42 files changed, 882 insertions(+), 1028 deletions(-) delete mode 100644 DeDRM_plugin/encodebase64.py diff --git a/DeDRM_plugin/__init__.py b/DeDRM_plugin/__init__.py index c20dd1e..1fe97a1 100644 --- a/DeDRM_plugin/__init__.py +++ b/DeDRM_plugin/__init__.py @@ -1,8 +1,6 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # -*- coding: utf-8 -*- -from __future__ import with_statement - # __init__.py for DeDRM_plugin # Copyright © 2008-2020 Apprentice Harper et al. @@ -77,9 +75,9 @@ __docformat__ = 'restructuredtext en' Decrypt DRMed ebooks. """ -PLUGIN_NAME = u"DeDRM" +PLUGIN_NAME = "DeDRM" PLUGIN_VERSION_TUPLE = (7, 0, 0) -PLUGIN_VERSION = u".".join([unicode(str(x)) for x in PLUGIN_VERSION_TUPLE]) +PLUGIN_VERSION = ".".join([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' @@ -109,11 +107,11 @@ class SafeUnbuffered: if self.encoding == None: self.encoding = "utf-8" def write(self, data): - if isinstance(data,bytes): + if isinstance(data,str): data = data.encode(self.encoding,"replace") try: - self.stream.write(data) - self.stream.flush() + self.stream.buffer.write(data) + self.stream.buffer.flush() except: # We can do nothing if a write fails pass @@ -122,11 +120,11 @@ class SafeUnbuffered: class DeDRM(FileTypePlugin): name = PLUGIN_NAME - description = u"Removes DRM from Amazon Kindle, Adobe Adept (including Kobo), Barnes & Noble, Mobipocket and eReader ebooks. Credit given to i♥cabbages and The Dark Reverser for the original stand-alone scripts." + description = "Removes DRM from Amazon Kindle, Adobe Adept (including Kobo), Barnes & Noble, Mobipocket and eReader ebooks. Credit given to i♥cabbages and The Dark Reverser for the original stand-alone scripts." supported_platforms = ['linux', 'osx', 'windows'] - author = u"Apprentice Alf, Aprentice Harper, The Dark Reverser and i♥cabbages" + author = "Apprentice Alf, Aprentice Harper, The Dark Reverser and i♥cabbages" version = PLUGIN_VERSION_TUPLE - minimum_calibre_version = (1, 0, 0) # Compiled python libraries cannot be imported in earlier versions. + minimum_calibre_version = (5, 0, 0) # Python 3. file_types = set(['epub','pdf','pdb','prc','mobi','pobi','azw','azw1','azw3','azw4','azw8','tpz','kfx','kfx-zip']) on_import = True on_preprocess = True @@ -146,27 +144,27 @@ class DeDRM(FileTypePlugin): Also perform upgrade of preferences once per version """ try: - self.pluginsdir = os.path.join(config_dir,u"plugins") + self.pluginsdir = os.path.join(config_dir,"plugins") if not os.path.exists(self.pluginsdir): os.mkdir(self.pluginsdir) - self.maindir = os.path.join(self.pluginsdir,u"DeDRM") + self.maindir = os.path.join(self.pluginsdir,"DeDRM") if not os.path.exists(self.maindir): os.mkdir(self.maindir) - self.helpdir = os.path.join(self.maindir,u"help") + self.helpdir = os.path.join(self.maindir,"help") if not os.path.exists(self.helpdir): os.mkdir(self.helpdir) - self.alfdir = os.path.join(self.maindir,u"libraryfiles") + self.alfdir = os.path.join(self.maindir,"libraryfiles") if not os.path.exists(self.alfdir): os.mkdir(self.alfdir) # only continue if we've never run this version of the plugin before self.verdir = os.path.join(self.maindir,PLUGIN_VERSION) if not os.path.exists(self.verdir): if iswindows: - names = [u"alfcrypto.dll",u"alfcrypto64.dll"] + names = ["alfcrypto.dll","alfcrypto64.dll"] elif isosx: - names = [u"libalfcrypto.dylib"] + names = ["libalfcrypto.dylib"] else: - names = [u"libalfcrypto32.so",u"libalfcrypto64.so",u"kindlekey.py",u"adobekey.py",u"subasyncio.py"] + names = ["libalfcrypto32.so","libalfcrypto64.so","kindlekey.py","adobekey.py","subasyncio.py"] lib_dict = self.load_resources(names) print("{0} v{1}: Copying needed library files from plugin's zip".format(PLUGIN_NAME, PLUGIN_VERSION)) @@ -199,13 +197,13 @@ class DeDRM(FileTypePlugin): # Check original epub archive for zip errors. import calibre_plugins.dedrm.zipfix - inf = self.temporary_file(u".epub") + inf = self.temporary_file(".epub") try: 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 as e: - print(u"{0} v{1}: Error \'{2}\' when checking zip archive".format(PLUGIN_NAME, PLUGIN_VERSION, e.args[0])) + print("{0} v{1}: Error \'{2}\' when checking zip archive".format(PLUGIN_NAME, PLUGIN_VERSION, e.args[0])) raise Exception(e) # import the decryption keys @@ -222,9 +220,9 @@ class DeDRM(FileTypePlugin): # 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) + keyname_masked = u"".join(("X" if (x.isdigit()) else x) for x in keyname) print("{0} v{1}: Trying Encryption key {2:s}".format(PLUGIN_NAME, PLUGIN_VERSION, keyname_masked)) - of = self.temporary_file(u".epub") + of = self.temporary_file(".epub") # Give the user key, ebook and TemporaryPersistent file to the decryption function. try: @@ -255,10 +253,10 @@ class DeDRM(FileTypePlugin): defaultkeys = nookkeys() else: # linux - from wineutils import WineGetKeys + from .wineutils import WineGetKeys - scriptpath = os.path.join(self.alfdir,u"ignoblekey.py") - defaultkeys = WineGetKeys(scriptpath, u".b64",dedrmprefs['adobewineprefix']) + scriptpath = os.path.join(self.alfdir,"ignoblekey.py") + defaultkeys = WineGetKeys(scriptpath, ".b64",dedrmprefs['adobewineprefix']) except: print("{0} v{1}: Exception when getting default NOOK Study Key after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION, time.time()-self.starttime)) @@ -274,7 +272,7 @@ class DeDRM(FileTypePlugin): for i,userkey in enumerate(newkeys): print("{0} v{1}: Trying a new default key".format(PLUGIN_NAME, PLUGIN_VERSION)) - of = self.temporary_file(u".epub") + of = self.temporary_file(".epub") # Give the user key, ebook and TemporaryPersistent file to the decryption function. try: @@ -300,12 +298,12 @@ class DeDRM(FileTypePlugin): # 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)) + print("{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("{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)) + raise DeDRMError("{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 @@ -316,8 +314,8 @@ class DeDRM(FileTypePlugin): # Attempt to decrypt epub with each encryption key (generated or provided). for keyname, userkeyhex in dedrmprefs['adeptkeys'].items(): 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") + print("{0} v{1}: Trying Encryption key {2:s}".format(PLUGIN_NAME, PLUGIN_VERSION, keyname)) + of = self.temporary_file(".epub") # Give the user key, ebook and TemporaryPersistent file to the decryption function. try: @@ -352,10 +350,10 @@ class DeDRM(FileTypePlugin): defaultkeys = adeptkeys() else: # linux - from wineutils import WineGetKeys + from .wineutils import WineGetKeys - scriptpath = os.path.join(self.alfdir,u"adobekey.py") - defaultkeys = WineGetKeys(scriptpath, u".der",dedrmprefs['adobewineprefix']) + scriptpath = os.path.join(self.alfdir,"adobekey.py") + defaultkeys = WineGetKeys(scriptpath, ".der",dedrmprefs['adobewineprefix']) self.default_key = defaultkeys[0] except: @@ -365,14 +363,14 @@ class DeDRM(FileTypePlugin): newkeys = [] for keyvalue in defaultkeys: - if keyvalue.encode('hex') not in dedrmprefs['adeptkeys'].values(): + if codecs.encode(keyvalue, 'hex').decode('ascii') not in dedrmprefs['adeptkeys'].values(): newkeys.append(keyvalue) if len(newkeys) > 0: try: for i,userkey in enumerate(newkeys): print("{0} v{1}: Trying a new default key".format(PLUGIN_NAME, PLUGIN_VERSION)) - of = self.temporary_file(u".epub") + of = self.temporary_file(".epub") # Give the user key, ebook and TemporaryPersistent file to the decryption function. try: @@ -389,7 +387,7 @@ class DeDRM(FileTypePlugin): # Store the new successful key in the defaults print("{0} v{1}: Saving a new default key".format(PLUGIN_NAME, PLUGIN_VERSION)) try: - dedrmprefs.addnamedvaluetoprefs('adeptkeys','default_key',keyvalue.encode('hex')) + dedrmprefs.addnamedvaluetoprefs('adeptkeys','default_key',codecs.encode(keyvalue, 'hex').decode('ascii')) dedrmprefs.writeprefs() print("{0} v{1}: Saved a new default key after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION,time.time()-self.starttime)) except: @@ -399,20 +397,20 @@ class DeDRM(FileTypePlugin): # 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)) + print("{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)) + print("{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("{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)) + raise DeDRMError("{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("{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)) + raise DeDRMError("{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): import calibre_plugins.dedrm.prefs as prefs @@ -422,9 +420,9 @@ class DeDRM(FileTypePlugin): # Attempt to decrypt epub with each encryption key (generated or provided). 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 = codecs.decode(userkeyhex, 'hex') + userkey = userkeyhex.decode('hex') print("{0} v{1}: Trying Encryption key {2:s}".format(PLUGIN_NAME, PLUGIN_VERSION, keyname)) - of = self.temporary_file(u".pdf") + of = self.temporary_file(".pdf") # Give the user key, ebook and TemporaryPersistent file to the decryption function. try: @@ -455,10 +453,10 @@ class DeDRM(FileTypePlugin): defaultkeys = adeptkeys() else: # linux - from wineutils import WineGetKeys + from .wineutils import WineGetKeys - scriptpath = os.path.join(self.alfdir,u"adobekey.py") - defaultkeys = WineGetKeys(scriptpath, u".der",dedrmprefs['adobewineprefix']) + scriptpath = os.path.join(self.alfdir,"adobekey.py") + defaultkeys = WineGetKeys(scriptpath, ".der",dedrmprefs['adobewineprefix']) self.default_key = defaultkeys[0] except: @@ -475,7 +473,7 @@ class DeDRM(FileTypePlugin): try: for i,userkey in enumerate(newkeys): print("{0} v{1}: Trying a new default key".format(PLUGIN_NAME, PLUGIN_VERSION)) - of = self.temporary_file(u".pdf") + of = self.temporary_file(".pdf") # Give the user key, ebook and TemporaryPersistent file to the decryption function. try: @@ -501,13 +499,13 @@ class DeDRM(FileTypePlugin): # 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)) + print("{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("{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)) + raise DeDRMError("{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)) def KindleMobiDecrypt(self,path_to_ebook): @@ -529,7 +527,7 @@ class DeDRM(FileTypePlugin): serials.extend(android_serials_list) #print serials androidFiles = [] - kindleDatabases = dedrmprefs['kindlekeys'].items() + kindleDatabases = list(dedrmprefs['kindlekeys'].items()) try: book = k4mobidedrm.GetDecryptedBook(path_to_ebook,kindleDatabases,androidFiles,serials,pids,self.starttime) @@ -546,10 +544,10 @@ class DeDRM(FileTypePlugin): defaultkeys = kindlekeys() else: # linux - from wineutils import WineGetKeys + from .wineutils import WineGetKeys - scriptpath = os.path.join(self.alfdir,u"kindlekey.py") - defaultkeys = WineGetKeys(scriptpath, u".k4i",dedrmprefs['kindlewineprefix']) + scriptpath = os.path.join(self.alfdir,"kindlekey.py") + defaultkeys = WineGetKeys(scriptpath, ".k4i",dedrmprefs['kindlewineprefix']) except: 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() @@ -557,16 +555,16 @@ class DeDRM(FileTypePlugin): newkeys = {} for i,keyvalue in enumerate(defaultkeys): - keyname = u"default_key_{0:d}".format(i+1) + keyname = "default_key_{0:d}".format(i+1) if keyvalue not in dedrmprefs['kindlekeys'].values(): newkeys[keyname] = keyvalue if len(newkeys) > 0: - print("{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), "key" if len(newkeys)==1 else "keys")) try: - book = k4mobidedrm.GetDecryptedBook(path_to_ebook,newkeys.items(),[],[],[],self.starttime) + book = k4mobidedrm.GetDecryptedBook(path_to_ebook,list(newkeys.items()),[],[],[],self.starttime) decoded = True # store the new successful keys in the defaults - print("{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), "key" if len(newkeys)==1 else "keys")) for keyvalue in newkeys.values(): dedrmprefs.addnamedvaluetoprefs('kindlekeys','default_key',keyvalue) dedrmprefs.writeprefs() @@ -575,7 +573,7 @@ class DeDRM(FileTypePlugin): if not decoded: #if you reached here then no luck raise and exception 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)) + raise DeDRMError("{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()) book.getFile(of.name) @@ -592,9 +590,9 @@ class DeDRM(FileTypePlugin): dedrmprefs = prefs.DeDRM_Prefs() # 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) + keyname_masked = u"".join(("X" if (x.isdigit()) else x) for x in keyname) print("{0} v{1}: Trying Encryption key {2:s}".format(PLUGIN_NAME, PLUGIN_VERSION, keyname_masked)) - of = self.temporary_file(u".pmlz") + of = self.temporary_file(".pmlz") # Give the userkey, ebook and TemporaryPersistent file to the decryption function. result = erdr2pml.decryptBook(path_to_ebook, of.name, True, userkey.decode('hex')) @@ -610,7 +608,7 @@ class DeDRM(FileTypePlugin): 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("{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)) + raise DeDRMError("{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)) def run(self, path_to_ebook): diff --git a/DeDRM_plugin/adobekey.py b/DeDRM_plugin/adobekey.py index 5a329dc..e462d3d 100644 --- a/DeDRM_plugin/adobekey.py +++ b/DeDRM_plugin/adobekey.py @@ -1,30 +1,12 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # -*- coding: utf-8 -*- # adobekey.pyw, version 6.0 -# Copyright © 2009-2010 i♥cabbages +# Copyright © 2009-2020 i♥cabbages, Apprentice Harper et al. # Released under the terms of the GNU General Public Licence, version 3 # -# Modified 2010–2016 by several people - -# 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. -# You must also install PyCrypto from -# http://www.voidspace.org.uk/python/modules.shtml#pycrypto -# (make certain to install the version for Python 2.7). -# Then save this script file as adobekey.pyw and double-click on it to run it. -# It will create a file named adobekey_1.der in in the same directory as the script. -# This is your Adobe Digital Editions user key. -# -# Mac OS X users: Save this script file as adobekey.pyw. You can run this -# program from the command line (python adobekey.pyw) or by double-clicking -# it when it has been associated with PythonLauncher. It will create a file -# named adobekey_1.der in the same directory as the script. -# This is your Adobe Digital Editions user key. - # Revision history: # 1 - Initial release, for Adobe Digital Editions 1.7 # 2 - Better algorithm for finding pLK; improved error handling @@ -46,7 +28,7 @@ # 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 +# 7.0 - Python 3 for calibre 5 """ Retrieve Adobe ADEPT user key. diff --git a/DeDRM_plugin/aescbc.py b/DeDRM_plugin/aescbc.py index 28e1843..59c763e 100644 --- a/DeDRM_plugin/aescbc.py +++ b/DeDRM_plugin/aescbc.py @@ -1,4 +1,5 @@ -#! /usr/bin/env python +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- """ Routines for doing AES CBC in one file @@ -14,7 +15,7 @@ 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 + Adjusted for Python 3, September 2020 """ class CryptoError(Exception): diff --git a/DeDRM_plugin/alfcrypto.py b/DeDRM_plugin/alfcrypto.py index a5a11a5..818ec68 100644 --- a/DeDRM_plugin/alfcrypto.py +++ b/DeDRM_plugin/alfcrypto.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # -*- coding: utf-8 -*- # crypto library mainly by some_updates @@ -8,7 +8,6 @@ # pbkdf2.py Copyright © 2009 Daniel Holth # pbkdf2.py This code may be freely used and modified for any purpose. -from __future__ import print_function import sys, os import hmac from struct import pack @@ -159,7 +158,7 @@ def _load_libalfcrypto(): topazCryptoDecrypt(ctx, data, out, len(data)) return out.raw - print(u"Using Library AlfCrypto DLL/DYLIB/SO") + print("Using Library AlfCrypto DLL/DYLIB/SO") return (AES_CBC, Pukall_Cipher, Topaz_Cipher) @@ -245,7 +244,7 @@ def _load_python_alfcrypto(): cleartext = self.aes.decrypt(iv + data) return cleartext - print(u"Using Library AlfCrypto Python") + print("Using Library AlfCrypto Python") return (AES_CBC, Pukall_Cipher, Topaz_Cipher) diff --git a/DeDRM_plugin/androidkindlekey.py b/DeDRM_plugin/androidkindlekey.py index f05c468..4759e1b 100644 --- a/DeDRM_plugin/androidkindlekey.py +++ b/DeDRM_plugin/androidkindlekey.py @@ -1,13 +1,8 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # -*- coding: utf-8 -*- -from __future__ import with_statement -from __future__ import print_function - # androidkindlekey.py -# Copyright © 2013-15 by Thom and Apprentice Harper -# Some portions Copyright © 2010-15 by some_updates and Apprentice Alf -# +# Copyright © 2010-20 by Thom, Apprentice et al. # Revision history: # 1.0 - AmazonSecureStorage.xml decryption to serial number @@ -18,7 +13,7 @@ from __future__ import print_function # 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 +# 2.0 - Python 3 compatibility """ Retrieve Kindle for Android Serial Number. @@ -53,10 +48,11 @@ class SafeUnbuffered: if self.encoding == None: self.encoding = "utf-8" def write(self, data): - if isinstance(data,bytes): + if isinstance(data,str): data = data.encode(self.encoding,"replace") - self.stream.write(data) - self.stream.flush() + self.stream.buffer.write(data) + self.stream.buffer.flush() + def __getattr__(self, attr): return getattr(self.stream, attr) @@ -97,7 +93,7 @@ def unicode_argv(): 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"] + return ["kindlekey.py"] else: argvencoding = sys.stdin.encoding if argvencoding == None: @@ -107,9 +103,9 @@ def unicode_argv(): class DrmException(Exception): pass -STORAGE = u"backup.ab" -STORAGE1 = u"AmazonSecureStorage.xml" -STORAGE2 = u"map_data_storage.db" +STORAGE = "backup.ab" +STORAGE1 = "AmazonSecureStorage.xml" +STORAGE2 = "map_data_storage.db" class AndroidObfuscation(object): '''AndroidObfuscation @@ -326,13 +322,13 @@ def getkey(outfile, inpath): def usage(progname): - print(u"Decrypts the serial number(s) of Kindle For Android from Android backup or file") - print(u"Get backup.ab file using adb backup com.amazon.kindle for Android 4.0+.") - print(u"Otherwise extract AmazonSecureStorage.xml from /data/data/com.amazon.kindle/shared_prefs/AmazonSecureStorage.xml") - print(u"Or map_data_storage.db from /data/data/com.amazon.kindle/databases/map_data_storage.db") + print("Decrypts the serial number(s) of Kindle For Android from Android backup or file") + print("Get backup.ab file using adb backup com.amazon.kindle for Android 4.0+.") + print("Otherwise extract AmazonSecureStorage.xml from /data/data/com.amazon.kindle/shared_prefs/AmazonSecureStorage.xml") + print("Or map_data_storage.db from /data/data/com.amazon.kindle/databases/map_data_storage.db") print(u"") - print(u"Usage:") - print(u" {0:s} [-h] [-b ] []".format(progname)) + print("Usage:") + print(" {0:s} [-h] [-b ] []".format(progname)) def cli_main(): @@ -340,13 +336,13 @@ def cli_main(): sys.stderr=SafeUnbuffered(sys.stderr) argv=unicode_argv() progname = os.path.basename(argv[0]) - print(u"{0} v{1}\nCopyright © 2010-2015 Thom, some_updates, Apprentice Alf and Apprentice Harper".format(progname,__version__)) + print("{0} v{1}\nCopyright © 2010-2015 Thom, some_updates, Apprentice Alf and Apprentice Harper".format(progname,__version__)) try: opts, args = getopt.getopt(argv[1:], "hb:") except getopt.GetoptError as err: usage(progname) - print(u"\nError in options or arguments: {0}".format(err.args[0])) + print("\nError in options or arguments: {0}".format(err.args[0])) return 2 inpath = "" @@ -378,13 +374,13 @@ def cli_main(): if not os.path.isfile(inpath): usage(progname) - print(u"\n{0:s} file not found".format(inpath)) + print("\n{0:s} file not found".format(inpath)) return 2 if getkey(outfile, inpath): - print(u"\nSaved Kindle for Android key to {0}".format(outfile)) + print("\nSaved Kindle for Android key to {0}".format(outfile)) else: - print(u"\nCould not retrieve Kindle for Android key.") + print("\nCould not retrieve Kindle for Android key.") return 0 @@ -401,32 +397,32 @@ def gui_main(): class DecryptionDialog(Tkinter.Frame): def __init__(self, root): Tkinter.Frame.__init__(self, root, border=5) - self.status = Tkinter.Label(self, text=u"Select backup.ab file") + self.status = Tkinter.Label(self, text="Select backup.ab file") 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) - Tkinter.Label(body, text=u"Backup file").grid(row=0, column=0) + Tkinter.Label(body, text="Backup file").grid(row=0, column=0) self.keypath = Tkinter.Entry(body, width=40) self.keypath.grid(row=0, column=1, sticky=sticky) - self.keypath.insert(2, u"backup.ab") - button = Tkinter.Button(body, text=u"...", command=self.get_keypath) + self.keypath.insert(2, "backup.ab") + button = Tkinter.Button(body, text="...", command=self.get_keypath) button.grid(row=0, column=2) buttons = Tkinter.Frame(self) buttons.pack() button2 = Tkinter.Button( - buttons, text=u"Extract", width=10, command=self.generate) + buttons, text="Extract", width=10, command=self.generate) button2.pack(side=Tkconstants.LEFT) Tkinter.Frame(buttons, width=10).pack(side=Tkconstants.LEFT) button3 = Tkinter.Button( - buttons, text=u"Quit", width=10, command=self.quit) + buttons, text="Quit", width=10, command=self.quit) button3.pack(side=Tkconstants.RIGHT) def get_keypath(self): keypath = tkFileDialog.askopenfilename( - parent=None, title=u"Select backup.ab file", - defaultextension=u".ab", + parent=None, title="Select backup.ab file", + defaultextension=".ab", filetypes=[('adb backup com.amazon.kindle', '.ab'), ('All Files', '.*')]) if keypath: @@ -437,30 +433,30 @@ def gui_main(): def generate(self): inpath = self.keypath.get() - self.status['text'] = u"Getting key..." + self.status['text'] = "Getting key..." try: keys = get_serials(inpath) keycount = 0 for key in keys: while True: keycount += 1 - outfile = os.path.join(progpath,u"kindlekey{0:d}.k4a".format(keycount)) + outfile = os.path.join(progpath,"kindlekey{0:d}.k4a".format(keycount)) if not os.path.exists(outfile): break with open(outfile, 'w') as keyfileout: keyfileout.write(key) success = True - tkMessageBox.showinfo(progname, u"Key successfully retrieved to {0}".format(outfile)) + tkMessageBox.showinfo(progname, "Key successfully retrieved to {0}".format(outfile)) except Exception as e: - self.status['text'] = u"Error: {0}".format(e.args[0]) + self.status['text'] = "Error: {0}".format(e.args[0]) return - self.status['text'] = u"Select backup.ab file" + self.status['text'] = "Select backup.ab file" argv=unicode_argv() progpath, progname = os.path.split(argv[0]) root = Tkinter.Tk() - root.title(u"Kindle for Android Key Extraction v.{0}".format(__version__)) + root.title("Kindle for Android Key Extraction v.{0}".format(__version__)) root.resizable(True, False) root.minsize(300, 0) DecryptionDialog(root).pack(fill=Tkconstants.X, expand=1) diff --git a/DeDRM_plugin/argv_utils.py b/DeDRM_plugin/argv_utils.py index 71f0794..b904903 100644 --- a/DeDRM_plugin/argv_utils.py +++ b/DeDRM_plugin/argv_utils.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # -*- coding: utf-8 -*- import sys, os @@ -37,7 +37,7 @@ def unicode_argv(): xrange(start, argc.value)] # if we don't have any arguments at all, just pass back script name # this should never happen - return [u"DeDRM.py"] + return ["DeDRM.py"] else: argvencoding = sys.stdin.encoding if argvencoding == None: diff --git a/DeDRM_plugin/askfolder_ed.py b/DeDRM_plugin/askfolder_ed.py index 1a85513..4f64c1f 100644 --- a/DeDRM_plugin/askfolder_ed.py +++ b/DeDRM_plugin/askfolder_ed.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # -*- coding: utf-8 -*- # vim:ts=4:sw=4:softtabstop=4:smarttab:expandtab @@ -202,7 +202,7 @@ def AskFolder( if not pidl: result = None else: - path = LPCWSTR(u" " * (MAX_PATH+1)) + path = LPCWSTR(" " * (MAX_PATH+1)) shell32.SHGetPathFromIDListW(pidl, path) ole32.CoTaskMemFree(pidl) result = path.value diff --git a/DeDRM_plugin/config.py b/DeDRM_plugin/config.py index 2ef3223..93f69ae 100644 --- a/DeDRM_plugin/config.py +++ b/DeDRM_plugin/config.py @@ -1,30 +1,18 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # -*- coding: utf-8 -*- -from __future__ import with_statement -from __future__ import print_function - __license__ = 'GPL v3' -# Added Python 3 compatibility, September 2020 +# Python 3, September 2020 # Standard Python modules. import os, traceback, json -# PyQT4 modules (part of calibre). -try: - from PyQt5.Qt import (Qt, QWidget, QHBoxLayout, QVBoxLayout, QLabel, QLineEdit, +from PyQt5.Qt import (Qt, QWidget, QHBoxLayout, QVBoxLayout, QLabel, QLineEdit, QGroupBox, QPushButton, QListWidget, QListWidgetItem, QAbstractItemView, QIcon, QDialog, QDialogButtonBox, QUrl) -except ImportError: - from PyQt4.Qt import (Qt, QWidget, QHBoxLayout, QVBoxLayout, QLabel, QLineEdit, - QGroupBox, QPushButton, QListWidget, QListWidgetItem, - QAbstractItemView, QIcon, QDialog, QDialogButtonBox, QUrl) -try: - from PyQt5 import Qt as QtGui -except ImportError: - from PyQt4 import QtGui +from PyQt5 import Qt as QtGui from zipfile import ZipFile # calibre modules and constants. @@ -85,32 +73,32 @@ class ConfigWidget(QWidget): button_layout = QVBoxLayout() keys_group_box_layout.addLayout(button_layout) self.bandn_button = QtGui.QPushButton(self) - self.bandn_button.setToolTip(_(u"Click to manage keys for Barnes and Noble ebooks")) - self.bandn_button.setText(u"Barnes and Noble ebooks") + self.bandn_button.setToolTip(_("Click to manage keys for Barnes and Noble ebooks")) + self.bandn_button.setText("Barnes and Noble ebooks") self.bandn_button.clicked.connect(self.bandn_keys) self.kindle_android_button = QtGui.QPushButton(self) - self.kindle_android_button.setToolTip(_(u"Click to manage keys for Kindle for Android ebooks")) - self.kindle_android_button.setText(u"Kindle for Android ebooks") + self.kindle_android_button.setToolTip(_("Click to manage keys for Kindle for Android ebooks")) + self.kindle_android_button.setText("Kindle for Android ebooks") self.kindle_android_button.clicked.connect(self.kindle_android) self.kindle_serial_button = QtGui.QPushButton(self) - self.kindle_serial_button.setToolTip(_(u"Click to manage eInk Kindle serial numbers for Kindle ebooks")) - self.kindle_serial_button.setText(u"eInk Kindle ebooks") + self.kindle_serial_button.setToolTip(_("Click to manage eInk Kindle serial numbers for Kindle ebooks")) + self.kindle_serial_button.setText("eInk Kindle ebooks") self.kindle_serial_button.clicked.connect(self.kindle_serials) self.kindle_key_button = QtGui.QPushButton(self) - self.kindle_key_button.setToolTip(_(u"Click to manage keys for Kindle for Mac/PC ebooks")) - self.kindle_key_button.setText(u"Kindle for Mac/PC ebooks") + self.kindle_key_button.setToolTip(_("Click to manage keys for Kindle for Mac/PC ebooks")) + self.kindle_key_button.setText("Kindle for Mac/PC ebooks") self.kindle_key_button.clicked.connect(self.kindle_keys) self.adept_button = QtGui.QPushButton(self) - self.adept_button.setToolTip(_(u"Click to manage keys for Adobe Digital Editions ebooks")) - self.adept_button.setText(u"Adobe Digital Editions ebooks") + self.adept_button.setToolTip(_("Click to manage keys for Adobe Digital Editions ebooks")) + self.adept_button.setText("Adobe Digital Editions ebooks") self.adept_button.clicked.connect(self.adept_keys) self.mobi_button = QtGui.QPushButton(self) - self.mobi_button.setToolTip(_(u"Click to manage PIDs for Mobipocket ebooks")) - self.mobi_button.setText(u"Mobipocket ebooks") + self.mobi_button.setToolTip(_("Click to manage PIDs for Mobipocket ebooks")) + self.mobi_button.setText("Mobipocket ebooks") self.mobi_button.clicked.connect(self.mobi_keys) self.ereader_button = QtGui.QPushButton(self) - self.ereader_button.setToolTip(_(u"Click to manage keys for eReader ebooks")) - self.ereader_button.setText(u"eReader ebooks") + self.ereader_button.setToolTip(_("Click to manage keys for eReader ebooks")) + self.ereader_button.setText("eReader ebooks") self.ereader_button.clicked.connect(self.ereader_keys) button_layout.addWidget(self.kindle_serial_button) button_layout.addWidget(self.kindle_android_button) @@ -123,48 +111,48 @@ class ConfigWidget(QWidget): self.resize(self.sizeHint()) def kindle_serials(self): - d = ManageKeysDialog(self,u"EInk Kindle Serial Number",self.tempdedrmprefs['serials'], AddSerialDialog) + d = ManageKeysDialog(self,"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 = ManageKeysDialog(self,"Kindle for Android Key",self.tempdedrmprefs['androidkeys'], AddAndroidDialog, 'k4a') d.exec_() def kindle_keys(self): if isosx or iswindows: - d = ManageKeysDialog(self,u"Kindle for Mac and PC Key",self.tempdedrmprefs['kindlekeys'], AddKindleDialog, 'k4i') + d = ManageKeysDialog(self,"Kindle for Mac and PC Key",self.tempdedrmprefs['kindlekeys'], AddKindleDialog, 'k4i') else: # linux - d = ManageKeysDialog(self,u"Kindle for Mac and PC Key",self.tempdedrmprefs['kindlekeys'], AddKindleDialog, 'k4i', self.tempdedrmprefs['kindlewineprefix']) + d = ManageKeysDialog(self,"Kindle for Mac and PC Key",self.tempdedrmprefs['kindlekeys'], AddKindleDialog, 'k4i', self.tempdedrmprefs['kindlewineprefix']) d.exec_() self.tempdedrmprefs['kindlewineprefix'] = d.getwineprefix() def adept_keys(self): if isosx or iswindows: - d = ManageKeysDialog(self,u"Adobe Digital Editions Key",self.tempdedrmprefs['adeptkeys'], AddAdeptDialog, 'der') + d = ManageKeysDialog(self,"Adobe Digital Editions Key",self.tempdedrmprefs['adeptkeys'], AddAdeptDialog, 'der') else: # linux - d = ManageKeysDialog(self,u"Adobe Digital Editions Key",self.tempdedrmprefs['adeptkeys'], AddAdeptDialog, 'der', self.tempdedrmprefs['adobewineprefix']) + d = ManageKeysDialog(self,"Adobe Digital Editions Key",self.tempdedrmprefs['adeptkeys'], AddAdeptDialog, 'der', self.tempdedrmprefs['adobewineprefix']) d.exec_() self.tempdedrmprefs['adobewineprefix'] = d.getwineprefix() def mobi_keys(self): - d = ManageKeysDialog(self,u"Mobipocket PID",self.tempdedrmprefs['pids'], AddPIDDialog) + d = ManageKeysDialog(self,"Mobipocket PID",self.tempdedrmprefs['pids'], AddPIDDialog) d.exec_() def bandn_keys(self): - d = ManageKeysDialog(self,u"Barnes and Noble Key",self.tempdedrmprefs['bandnkeys'], AddBandNKeyDialog, 'b64') + d = ManageKeysDialog(self,"Barnes and Noble Key",self.tempdedrmprefs['bandnkeys'], AddBandNKeyDialog, 'b64') d.exec_() def ereader_keys(self): - d = ManageKeysDialog(self,u"eReader Key",self.tempdedrmprefs['ereaderkeys'], AddEReaderDialog, 'b63') + d = ManageKeysDialog(self,"eReader Key",self.tempdedrmprefs['ereaderkeys'], AddEReaderDialog, 'b63') d.exec_() def help_link_activated(self, url): def get_help_file_resource(): # Copy the HTML helpfile to the plugin directory each time the # link is clicked in case the helpfile is updated in newer plugins. - file_path = os.path.join(config_dir, u"plugins", u"DeDRM", u"help", help_file_name) + file_path = os.path.join(config_dir, "plugins", "DeDRM", "help", help_file_name) with open(file_path,'w') as f: f.write(self.load_resource(help_file_name)) return file_path @@ -201,9 +189,9 @@ class ManageKeysDialog(QDialog): self.create_key = create_key self.keyfile_ext = keyfile_ext self.import_key = (keyfile_ext != u"") - self.binary_file = (keyfile_ext == u"der") - self.json_file = (keyfile_ext == u"k4i") - self.android_file = (keyfile_ext == u"k4a") + self.binary_file = (keyfile_ext == "der") + self.json_file = (keyfile_ext == "k4i") + self.android_file = (keyfile_ext == "k4a") self.wineprefix = wineprefix self.setWindowTitle("{0} {1}: Manage {2}s".format(PLUGIN_NAME, PLUGIN_VERSION, self.key_type_name)) @@ -221,13 +209,13 @@ class ManageKeysDialog(QDialog): help_label.linkActivated.connect(self.help_link_activated) help_layout.addWidget(help_label) - keys_group_box = QGroupBox(_(u"{0}s".format(self.key_type_name)), self) + keys_group_box = QGroupBox(_("{0}s".format(self.key_type_name)), self) layout.addWidget(keys_group_box) keys_group_box_layout = QHBoxLayout() keys_group_box.setLayout(keys_group_box_layout) self.listy = QListWidget(self) - self.listy.setToolTip(u"{0}s that will be used to decrypt ebooks".format(self.key_type_name)) + self.listy.setToolTip("{0}s that will be used to decrypt ebooks".format(self.key_type_name)) self.listy.setSelectionMode(QAbstractItemView.SingleSelection) self.populate_list() keys_group_box_layout.addWidget(self.listy) @@ -236,25 +224,25 @@ class ManageKeysDialog(QDialog): keys_group_box_layout.addLayout(button_layout) self._add_key_button = QtGui.QToolButton(self) self._add_key_button.setIcon(QIcon(I('plus.png'))) - self._add_key_button.setToolTip(u"Create new {0}".format(self.key_type_name)) + self._add_key_button.setToolTip("Create new {0}".format(self.key_type_name)) self._add_key_button.clicked.connect(self.add_key) button_layout.addWidget(self._add_key_button) self._delete_key_button = QtGui.QToolButton(self) - self._delete_key_button.setToolTip(_(u"Delete highlighted key")) + self._delete_key_button.setToolTip(_("Delete highlighted key")) self._delete_key_button.setIcon(QIcon(I('list_remove.png'))) self._delete_key_button.clicked.connect(self.delete_key) button_layout.addWidget(self._delete_key_button) if type(self.plugin_keys) == dict and self.import_key: self._rename_key_button = QtGui.QToolButton(self) - self._rename_key_button.setToolTip(_(u"Rename highlighted key")) + self._rename_key_button.setToolTip(_("Rename highlighted key")) self._rename_key_button.setIcon(QIcon(I('edit-select-all.png'))) self._rename_key_button.clicked.connect(self.rename_key) button_layout.addWidget(self._rename_key_button) self.export_key_button = QtGui.QToolButton(self) - self.export_key_button.setToolTip(u"Save highlighted key to a .{0} file".format(self.keyfile_ext)) + self.export_key_button.setToolTip("Save highlighted key to a .{0} file".format(self.keyfile_ext)) self.export_key_button.setIcon(QIcon(I('save.png'))) self.export_key_button.clicked.connect(self.export_key) button_layout.addWidget(self.export_key_button) @@ -266,7 +254,7 @@ class ManageKeysDialog(QDialog): wineprefix_layout = QHBoxLayout() layout.addLayout(wineprefix_layout) wineprefix_layout.setAlignment(Qt.AlignCenter) - self.wp_label = QLabel(u"WINEPREFIX:") + self.wp_label = QLabel("WINEPREFIX:") wineprefix_layout.addWidget(self.wp_label) self.wp_lineedit = QLineEdit(self) wineprefix_layout.addWidget(self.wp_lineedit) @@ -278,8 +266,8 @@ class ManageKeysDialog(QDialog): layout.addLayout(migrate_layout) if self.import_key: migrate_layout.setAlignment(Qt.AlignJustify) - self.migrate_btn = QPushButton(u"Import Existing Keyfiles", self) - self.migrate_btn.setToolTip(u"Import *.{0} files (created using other tools).".format(self.keyfile_ext)) + self.migrate_btn = QPushButton("Import Existing Keyfiles", self) + self.migrate_btn.setToolTip("Import *.{0} files (created using other tools).".format(self.keyfile_ext)) self.migrate_btn.clicked.connect(self.migrate_wrapper) migrate_layout.addWidget(self.migrate_btn) migrate_layout.addStretch() @@ -314,13 +302,13 @@ class ManageKeysDialog(QDialog): if new_key_value in self.plugin_keys.values(): old_key_name = [name for name, value in self.plugin_keys.iteritems() if value == new_key_value][0] info_dialog(None, "{0} {1}: Duplicate {2}".format(PLUGIN_NAME, PLUGIN_VERSION,self.key_type_name), - u"The new {1} is the same as the existing {1} named {0} and has not been added.".format(old_key_name,self.key_type_name), show=True) + "The new {1} is the same as the existing {1} named {0} and has not been added.".format(old_key_name,self.key_type_name), show=True) return self.plugin_keys[d.key_name] = new_key_value else: if new_key_value in self.plugin_keys: info_dialog(None, "{0} {1}: Duplicate {2}".format(PLUGIN_NAME, PLUGIN_VERSION,self.key_type_name), - u"This {0} is already in the list of {0}s has not been added.".format(self.key_type_name), show=True) + "This {0} is already in the list of {0}s has not been added.".format(self.key_type_name), show=True) return self.plugin_keys.append(d.key_value) @@ -329,7 +317,7 @@ class ManageKeysDialog(QDialog): def rename_key(self): if not self.listy.currentItem(): - errmsg = u"No {0} selected to rename. Highlight a keyfile first.".format(self.key_type_name) + errmsg = "No {0} selected to rename. Highlight a keyfile first.".format(self.key_type_name) r = error_dialog(None, "{0} {1}".format(PLUGIN_NAME, PLUGIN_VERSION), _(errmsg), show=True, show_copy_button=False) return @@ -341,7 +329,7 @@ class ManageKeysDialog(QDialog): # rename cancelled or moot. return 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 {0} to {1}?".format(keyname,d.key_name,self.key_type_name), show_copy_button=False, default_yes=False): + if not question_dialog(self, "{0} {1}: Confirm Rename".format(PLUGIN_NAME, PLUGIN_VERSION), "Do you really want to rename the {2} named {0} to {1}?".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] del self.plugin_keys[keyname] @@ -353,7 +341,7 @@ class ManageKeysDialog(QDialog): if not self.listy.currentItem(): return 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} {0}?".format(keyname, self.key_type_name), show_copy_button=False, default_yes=False): + if not question_dialog(self, "{0} {1}: Confirm Delete".format(PLUGIN_NAME, PLUGIN_VERSION), "Do you really want to delete the {1} {0}?".format(keyname, self.key_type_name), show_copy_button=False, default_yes=False): return if type(self.plugin_keys) == dict: del self.plugin_keys[keyname] @@ -367,8 +355,8 @@ class ManageKeysDialog(QDialog): def get_help_file_resource(): # Copy the HTML helpfile to the plugin directory each time the # link is clicked in case the helpfile is updated in newer plugins. - help_file_name = u"{0}_{1}_Help.htm".format(PLUGIN_NAME, self.key_type_name) - file_path = os.path.join(config_dir, u"plugins", u"DeDRM", u"help", help_file_name) + help_file_name = "{0}_{1}_Help.htm".format(PLUGIN_NAME, self.key_type_name) + file_path = os.path.join(config_dir, "plugins", "DeDRM", "help", help_file_name) with open(file_path,'w') as f: f.write(self.parent.load_resource(help_file_name)) return file_path @@ -376,9 +364,9 @@ class ManageKeysDialog(QDialog): open_url(QUrl(url)) def migrate_files(self): - unique_dlg_name = PLUGIN_NAME + u"import {0} keys".format(self.key_type_name).replace(' ', '_') #takes care of automatically remembering last directory - caption = u"Select {0} files to import".format(self.key_type_name) - filters = [(u"{0} files".format(self.key_type_name), [self.keyfile_ext])] + unique_dlg_name = PLUGIN_NAME + "import {0} keys".format(self.key_type_name).replace(' ', '_') #takes care of automatically remembering last directory + caption = "Select {0} files to import".format(self.key_type_name) + filters = [("{0} files".format(self.key_type_name), [self.keyfile_ext])] files = choose_files(self, unique_dlg_name, caption, filters, all_files=False) counter = 0 skipped = 0 @@ -400,7 +388,7 @@ class ManageKeysDialog(QDialog): for key in self.plugin_keys.keys(): if uStrCmp(new_key_name, key, True): skipped += 1 - msg = u"A key with the name {0} already exists!\nSkipping key file {1}.\nRename the existing key and import again".format(new_key_name,filename) + msg = "A key with the name {0} already exists!\nSkipping key file {1}.\nRename the existing key and import again".format(new_key_name,filename) inf = info_dialog(None, "{0} {1}".format(PLUGIN_NAME, PLUGIN_VERSION), _(msg), show_copy_button=False, show=True) match = True @@ -410,7 +398,7 @@ class ManageKeysDialog(QDialog): old_key_name = [name for name, value in self.plugin_keys.iteritems() if value == new_key_value][0] skipped += 1 info_dialog(None, "{0} {1}".format(PLUGIN_NAME, PLUGIN_VERSION), - u"The key in file {0} is the same as the existing key {1} and has been skipped.".format(filename,old_key_name), show_copy_button=False, show=True) + "The key in file {0} is the same as the existing key {1} and has been skipped.".format(filename,old_key_name), show_copy_button=False, show=True) else: counter += 1 self.plugin_keys[new_key_name] = new_key_value @@ -418,9 +406,9 @@ class ManageKeysDialog(QDialog): msg = u"" if counter+skipped > 1: if counter > 0: - msg += u"Imported {0:d} key {1}. ".format(counter, u"file" if counter == 1 else u"files") + msg += "Imported {0:d} key {1}. ".format(counter, "file" if counter == 1 else "files") if skipped > 0: - msg += u"Skipped {0:d} key {1}.".format(skipped, u"file" if counter == 1 else u"files") + msg += "Skipped {0:d} key {1}.".format(skipped, "file" if counter == 1 else "files") inf = info_dialog(None, "{0} {1}".format(PLUGIN_NAME, PLUGIN_VERSION), _(msg), show_copy_button=False, show=True) return counter > 0 @@ -432,15 +420,15 @@ class ManageKeysDialog(QDialog): def export_key(self): if not self.listy.currentItem(): - errmsg = u"No keyfile selected to export. Highlight a keyfile first." + errmsg = "No keyfile selected to export. Highlight a keyfile first." r = error_dialog(None, "{0} {1}".format(PLUGIN_NAME, PLUGIN_VERSION), _(errmsg), show=True, show_copy_button=False) return 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)])] - defaultname = u"{0}.{1}".format(keyname, self.keyfile_ext) + unique_dlg_name = PLUGIN_NAME + "export {0} keys".format(self.key_type_name).replace(' ', '_') #takes care of automatically remembering last directory + caption = "Save {0} File as...".format(self.key_type_name) + filters = [("{0} Files".format(self.key_type_name), ["{0}".format(self.keyfile_ext)])] + defaultname = "{0}.{1}".format(keyname, self.keyfile_ext) filename = choose_save_file(self, unique_dlg_name, caption, filters, all_files=False, initial_filename=defaultname) if filename: with file(filename, 'wb') as fname: @@ -474,7 +462,7 @@ class RenameKeyDialog(QDialog): data_group_box_layout.addWidget(QLabel('New Key Name:', self)) self.key_ledit = QLineEdit(self.parent.listy.currentItem().text(), self) - self.key_ledit.setToolTip(u"Enter a new name for this existing {0}.".format(parent.key_type_name)) + self.key_ledit.setToolTip("Enter a new name for this existing {0}.".format(parent.key_type_name)) data_group_box_layout.addWidget(self.key_ledit) layout.addSpacing(20) @@ -488,11 +476,11 @@ class RenameKeyDialog(QDialog): def accept(self): if not self.key_ledit.text() or self.key_ledit.text().isspace(): - errmsg = u"Key name field cannot be empty!" + errmsg = "Key name field cannot be empty!" return error_dialog(None, "{0} {1}".format(PLUGIN_NAME, PLUGIN_VERSION), _(errmsg), show=True, show_copy_button=False) if len(self.key_ledit.text()) < 4: - errmsg = u"Key name must be at least 4 characters long!" + errmsg = "Key name must be at least 4 characters long!" return error_dialog(None, "{0} {1}".format(PLUGIN_NAME, PLUGIN_VERSION), _(errmsg), show=True, show_copy_button=False) if uStrCmp(self.key_ledit.text(), self.parent.listy.currentItem().text()): @@ -501,7 +489,7 @@ class RenameKeyDialog(QDialog): for k in self.parent.plugin_keys.keys(): if (uStrCmp(self.key_ledit.text(), k, True) and not uStrCmp(k, self.parent.listy.currentItem().text(), True)): - errmsg = u"The key name {0} is already being used.".format(self.key_ledit.text()) + errmsg = "The key name {0} is already being used.".format(self.key_ledit.text()) return error_dialog(None, "{0} {1}".format(PLUGIN_NAME, PLUGIN_VERSION), _(errmsg), show=True, show_copy_button=False) QDialog.accept(self) @@ -521,7 +509,7 @@ class AddBandNKeyDialog(QDialog): def __init__(self, parent=None,): QDialog.__init__(self, parent) self.parent = parent - self.setWindowTitle(u"{0} {1}: Create New Barnes & Noble Key".format(PLUGIN_NAME, PLUGIN_VERSION)) + self.setWindowTitle("{0} {1}: Create New Barnes & Noble Key".format(PLUGIN_NAME, PLUGIN_VERSION)) layout = QVBoxLayout(self) self.setLayout(layout) @@ -532,37 +520,37 @@ class AddBandNKeyDialog(QDialog): key_group = QHBoxLayout() data_group_box_layout.addLayout(key_group) - key_group.addWidget(QLabel(u"Unique Key Name:", self)) + key_group.addWidget(QLabel("Unique Key Name:", self)) self.key_ledit = QLineEdit("", self) - self.key_ledit.setToolTip(_(u"

Enter an identifying name for this new key.

" + - u"

It should be something that will help you remember " + - u"what personal information was used to create it.")) + self.key_ledit.setToolTip(_("

Enter an identifying name for this new key.

" + + "

It should be something that will help you remember " + + "what personal information was used to create it.")) key_group.addWidget(self.key_ledit) name_group = QHBoxLayout() data_group_box_layout.addLayout(name_group) - name_group.addWidget(QLabel(u"B&N/nook account email address:", self)) + name_group.addWidget(QLabel("B&N/nook account email address:", self)) self.name_ledit = QLineEdit(u"", self) - self.name_ledit.setToolTip(_(u"

Enter your email address as it appears in your B&N " + - u"account.

" + - u"

It will only be used to generate this " + - u"key and won\'t be stored anywhere " + - u"in calibre or on your computer.

" + - u"

eg: apprenticeharper@gmail.com

")) + self.name_ledit.setToolTip(_("

Enter your email address as it appears in your B&N " + + "account.

" + + "

It will only be used to generate this " + + "key and won\'t be stored anywhere " + + "in calibre or on your computer.

" + + "

eg: apprenticeharper@gmail.com

")) name_group.addWidget(self.name_ledit) - name_disclaimer_label = QLabel(_(u"(Will not be saved in configuration data)"), self) + name_disclaimer_label = QLabel(_("(Will not be saved in configuration data)"), self) name_disclaimer_label.setAlignment(Qt.AlignHCenter) data_group_box_layout.addWidget(name_disclaimer_label) ccn_group = QHBoxLayout() data_group_box_layout.addLayout(ccn_group) - ccn_group.addWidget(QLabel(u"B&N/nook account password:", self)) + ccn_group.addWidget(QLabel("B&N/nook account password:", self)) self.cc_ledit = QLineEdit(u"", self) - self.cc_ledit.setToolTip(_(u"

Enter the password " + - u"for your B&N account.

" + - u"

The password will only be used to generate this " + - u"key and won\'t be stored anywhere in " + - u"calibre or on your computer.")) + self.cc_ledit.setToolTip(_("

Enter the password " + + "for your B&N account.

" + + "

The password will only be used to generate this " + + "key and won\'t be stored anywhere in " + + "calibre or on your computer.")) ccn_group.addWidget(self.cc_ledit) ccn_disclaimer_label = QLabel(_('(Will not be saved in configuration data)'), self) ccn_disclaimer_label.setAlignment(Qt.AlignHCenter) @@ -571,13 +559,13 @@ class AddBandNKeyDialog(QDialog): key_group = QHBoxLayout() data_group_box_layout.addLayout(key_group) - key_group.addWidget(QLabel(u"Retrieved key:", self)) + key_group.addWidget(QLabel("Retrieved key:", self)) self.key_display = QLabel(u"", self) - self.key_display.setToolTip(_(u"Click the Retrieve Key button to fetch your B&N encryption key from the B&N servers")) + self.key_display.setToolTip(_("Click the Retrieve Key button to fetch your B&N encryption key from the B&N servers")) key_group.addWidget(self.key_display) self.retrieve_button = QtGui.QPushButton(self) - self.retrieve_button.setToolTip(_(u"Click to retrieve your B&N encryption key from the B&N servers")) - self.retrieve_button.setText(u"Retrieve Key") + self.retrieve_button.setToolTip(_("Click to retrieve your B&N encryption key from the B&N servers")) + self.retrieve_button.setText("Retrieve Key") self.retrieve_button.clicked.connect(self.retrieve_key) key_group.addWidget(self.retrieve_button) layout.addSpacing(10) @@ -609,17 +597,17 @@ class AddBandNKeyDialog(QDialog): from calibre_plugins.dedrm.ignoblekeyfetch import fetch_key as fetch_bandn_key fetched_key = fetch_bandn_key(self.user_name,self.cc_number) if fetched_key == "": - errmsg = u"Could not retrieve key. Check username, password and intenet connectivity and try again." + errmsg = "Could not retrieve key. Check username, password and intenet connectivity and try again." error_dialog(None, "{0} {1}".format(PLUGIN_NAME, PLUGIN_VERSION), errmsg, show=True, show_copy_button=False) else: self.key_display.setText(fetched_key) def accept(self): if len(self.key_name) == 0 or len(self.user_name) == 0 or len(self.cc_number) == 0 or self.key_name.isspace() or self.user_name.isspace() or self.cc_number.isspace(): - errmsg = u"All fields are required!" + errmsg = "All fields are required!" return error_dialog(None, "{0} {1}".format(PLUGIN_NAME, PLUGIN_VERSION), errmsg, show=True, show_copy_button=False) if len(self.key_name) < 4: - errmsg = u"Key name must be at least 4 characters long!" + errmsg = "Key name must be at least 4 characters long!" return error_dialog(None, "{0} {1}".format(PLUGIN_NAME, PLUGIN_VERSION), errmsg, show=True, show_copy_button=False) if len(self.key_value) == 0: self.retrieve_key() @@ -631,7 +619,7 @@ class AddEReaderDialog(QDialog): def __init__(self, parent=None,): QDialog.__init__(self, parent) self.parent = parent - self.setWindowTitle(u"{0} {1}: Create New eReader Key".format(PLUGIN_NAME, PLUGIN_VERSION)) + self.setWindowTitle("{0} {1}: Create New eReader Key".format(PLUGIN_NAME, PLUGIN_VERSION)) layout = QVBoxLayout(self) self.setLayout(layout) @@ -642,26 +630,26 @@ class AddEReaderDialog(QDialog): key_group = QHBoxLayout() data_group_box_layout.addLayout(key_group) - key_group.addWidget(QLabel(u"Unique Key Name:", self)) + key_group.addWidget(QLabel("Unique Key Name:", self)) self.key_ledit = QLineEdit("", self) - self.key_ledit.setToolTip(u"

Enter an identifying name for this new key.\nIt should be something that will help you remember what personal information was used to create it.") + self.key_ledit.setToolTip("

Enter an identifying name for this new key.\nIt should be something that will help you remember what personal information was used to create it.") key_group.addWidget(self.key_ledit) name_group = QHBoxLayout() data_group_box_layout.addLayout(name_group) - name_group.addWidget(QLabel(u"Your Name:", self)) + name_group.addWidget(QLabel("Your Name:", self)) self.name_ledit = QLineEdit(u"", self) - self.name_ledit.setToolTip(u"Enter the name for this eReader key, usually the name on your credit card.\nIt will only be used to generate this one-time key and won\'t be stored anywhere in calibre or on your computer.\n(ex: Mr Jonathan Q Smith)") + self.name_ledit.setToolTip("Enter the name for this eReader key, usually the name on your credit card.\nIt will only be used to generate this one-time key and won\'t be stored anywhere in calibre or on your computer.\n(ex: Mr Jonathan Q Smith)") name_group.addWidget(self.name_ledit) - name_disclaimer_label = QLabel(_(u"(Will not be saved in configuration data)"), self) + name_disclaimer_label = QLabel(_("(Will not be saved in configuration data)"), self) name_disclaimer_label.setAlignment(Qt.AlignHCenter) data_group_box_layout.addWidget(name_disclaimer_label) ccn_group = QHBoxLayout() data_group_box_layout.addLayout(ccn_group) - ccn_group.addWidget(QLabel(u"Credit Card#:", self)) + ccn_group.addWidget(QLabel("Credit Card#:", self)) self.cc_ledit = QLineEdit(u"", self) - self.cc_ledit.setToolTip(u"

Enter the last 8 digits of credit card number for this eReader key.\nThey will only be used to generate this one-time key and won\'t be stored anywhere in calibre or on your computer.") + self.cc_ledit.setToolTip("

Enter the last 8 digits of credit card number for this eReader key.\nThey will only be used to generate this one-time key and won\'t be stored anywhere in calibre or on your computer.") ccn_group.addWidget(self.cc_ledit) ccn_disclaimer_label = QLabel(_('(Will not be saved in configuration data)'), self) ccn_disclaimer_label.setAlignment(Qt.AlignHCenter) @@ -695,13 +683,13 @@ class AddEReaderDialog(QDialog): def accept(self): if len(self.key_name) == 0 or len(self.user_name) == 0 or len(self.cc_number) == 0 or self.key_name.isspace() or self.user_name.isspace() or self.cc_number.isspace(): - errmsg = u"All fields are required!" + errmsg = "All fields are required!" return error_dialog(None, "{0} {1}".format(PLUGIN_NAME, PLUGIN_VERSION), errmsg, show=True, show_copy_button=False) if not self.cc_number.isdigit(): - errmsg = u"Numbers only in the credit card number field!" + errmsg = "Numbers only in the credit card number field!" return error_dialog(None, "{0} {1}".format(PLUGIN_NAME, PLUGIN_VERSION), errmsg, show=True, show_copy_button=False) if len(self.key_name) < 4: - errmsg = u"Key name must be at least 4 characters long!" + errmsg = "Key name must be at least 4 characters long!" return error_dialog(None, "{0} {1}".format(PLUGIN_NAME, PLUGIN_VERSION), errmsg, show=True, show_copy_button=False) QDialog.accept(self) @@ -710,7 +698,7 @@ class AddAdeptDialog(QDialog): def __init__(self, parent=None,): QDialog.__init__(self, parent) self.parent = parent - self.setWindowTitle(u"{0} {1}: Getting Default Adobe Digital Editions Key".format(PLUGIN_NAME, PLUGIN_VERSION)) + self.setWindowTitle("{0} {1}: Getting Default Adobe Digital Editions Key".format(PLUGIN_NAME, PLUGIN_VERSION)) layout = QVBoxLayout(self) self.setLayout(layout) @@ -722,8 +710,8 @@ class AddAdeptDialog(QDialog): else: # linux from wineutils import WineGetKeys - scriptpath = os.path.join(parent.parent.alfdir,u"adobekey.py") - defaultkeys = WineGetKeys(scriptpath, u".der",parent.getwineprefix()) + scriptpath = os.path.join(parent.parent.alfdir,"adobekey.py") + defaultkeys = WineGetKeys(scriptpath, ".der",parent.getwineprefix()) self.default_key = defaultkeys[0] except: @@ -740,14 +728,14 @@ class AddAdeptDialog(QDialog): key_group = QHBoxLayout() data_group_box_layout.addLayout(key_group) - key_group.addWidget(QLabel(u"Unique Key Name:", self)) - self.key_ledit = QLineEdit(u"default_key", self) - self.key_ledit.setToolTip(u"

Enter an identifying name for the current default Adobe Digital Editions key.") + key_group.addWidget(QLabel("Unique Key Name:", self)) + self.key_ledit = QLineEdit("default_key", self) + self.key_ledit.setToolTip("

Enter an identifying name for the current default Adobe Digital Editions key.") key_group.addWidget(self.key_ledit) self.button_box.accepted.connect(self.accept) else: - default_key_error = QLabel(u"The default encryption key for Adobe Digital Editions could not be found.", self) + default_key_error = QLabel("The default encryption key for Adobe Digital Editions could not be found.", self) default_key_error.setAlignment(Qt.AlignHCenter) layout.addWidget(default_key_error) # if no default, bot buttons do the same @@ -769,10 +757,10 @@ class AddAdeptDialog(QDialog): def accept(self): if len(self.key_name) == 0 or self.key_name.isspace(): - errmsg = u"All fields are required!" + errmsg = "All fields are required!" return error_dialog(None, "{0} {1}".format(PLUGIN_NAME, PLUGIN_VERSION), errmsg, show=True, show_copy_button=False) if len(self.key_name) < 4: - errmsg = u"Key name must be at least 4 characters long!" + errmsg = "Key name must be at least 4 characters long!" return error_dialog(None, "{0} {1}".format(PLUGIN_NAME, PLUGIN_VERSION), errmsg, show=True, show_copy_button=False) QDialog.accept(self) @@ -781,7 +769,7 @@ class AddKindleDialog(QDialog): def __init__(self, parent=None,): QDialog.__init__(self, parent) self.parent = parent - self.setWindowTitle(u"{0} {1}: Getting Default Kindle for Mac/PC Key".format(PLUGIN_NAME, PLUGIN_VERSION)) + self.setWindowTitle("{0} {1}: Getting Default Kindle for Mac/PC Key".format(PLUGIN_NAME, PLUGIN_VERSION)) layout = QVBoxLayout(self) self.setLayout(layout) @@ -793,8 +781,8 @@ class AddKindleDialog(QDialog): else: # linux from wineutils import WineGetKeys - scriptpath = os.path.join(parent.parent.alfdir,u"kindlekey.py") - defaultkeys = WineGetKeys(scriptpath, u".k4i",parent.getwineprefix()) + scriptpath = os.path.join(parent.parent.alfdir,"kindlekey.py") + defaultkeys = WineGetKeys(scriptpath, ".k4i",parent.getwineprefix()) self.default_key = defaultkeys[0] except: @@ -811,14 +799,14 @@ class AddKindleDialog(QDialog): key_group = QHBoxLayout() data_group_box_layout.addLayout(key_group) - key_group.addWidget(QLabel(u"Unique Key Name:", self)) - self.key_ledit = QLineEdit(u"default_key", self) - self.key_ledit.setToolTip(u"

Enter an identifying name for the current default Kindle for Mac/PC key.") + key_group.addWidget(QLabel("Unique Key Name:", self)) + self.key_ledit = QLineEdit("default_key", self) + self.key_ledit.setToolTip("

Enter an identifying name for the current default Kindle for Mac/PC key.") key_group.addWidget(self.key_ledit) self.button_box.accepted.connect(self.accept) else: - default_key_error = QLabel(u"The default encryption key for Kindle for Mac/PC could not be found.", self) + default_key_error = QLabel("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) @@ -841,10 +829,10 @@ class AddKindleDialog(QDialog): def accept(self): if len(self.key_name) == 0 or self.key_name.isspace(): - errmsg = u"All fields are required!" + errmsg = "All fields are required!" return error_dialog(None, "{0} {1}".format(PLUGIN_NAME, PLUGIN_VERSION), errmsg, show=True, show_copy_button=False) if len(self.key_name) < 4: - errmsg = u"Key name must be at least 4 characters long!" + errmsg = "Key name must be at least 4 characters long!" return error_dialog(None, "{0} {1}".format(PLUGIN_NAME, PLUGIN_VERSION), errmsg, show=True, show_copy_button=False) QDialog.accept(self) @@ -853,7 +841,7 @@ class AddSerialDialog(QDialog): def __init__(self, parent=None,): QDialog.__init__(self, parent) self.parent = parent - self.setWindowTitle(u"{0} {1}: Add New EInk Kindle Serial Number".format(PLUGIN_NAME, PLUGIN_VERSION)) + self.setWindowTitle("{0} {1}: Add New EInk Kindle Serial Number".format(PLUGIN_NAME, PLUGIN_VERSION)) layout = QVBoxLayout(self) self.setLayout(layout) @@ -864,9 +852,9 @@ class AddSerialDialog(QDialog): key_group = QHBoxLayout() data_group_box_layout.addLayout(key_group) - key_group.addWidget(QLabel(u"EInk Kindle Serial Number:", self)) + key_group.addWidget(QLabel("EInk Kindle Serial Number:", self)) self.key_ledit = QLineEdit("", self) - self.key_ledit.setToolTip(u"Enter an eInk Kindle serial number. EInk Kindle serial numbers are 16 characters long and usually start with a 'B' or a '9'. Kindle Serial Numbers are case-sensitive, so be sure to enter the upper and lower case letters unchanged.") + self.key_ledit.setToolTip("Enter an eInk Kindle serial number. EInk Kindle serial numbers are 16 characters long and usually start with a 'B' or a '9'. Kindle Serial Numbers are case-sensitive, so be sure to enter the upper and lower case letters unchanged.") key_group.addWidget(self.key_ledit) self.button_box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) @@ -886,10 +874,10 @@ class AddSerialDialog(QDialog): def accept(self): if len(self.key_name) == 0 or self.key_name.isspace(): - errmsg = u"Please enter an eInk Kindle Serial Number or click Cancel in the dialog." + errmsg = "Please enter an eInk Kindle Serial Number or click Cancel in the dialog." return error_dialog(None, "{0} {1}".format(PLUGIN_NAME, PLUGIN_VERSION), errmsg, show=True, show_copy_button=False) if len(self.key_name) != 16: - errmsg = u"EInk Kindle Serial Numbers must be 16 characters long. This is {0:d} characters long.".format(len(self.key_name)) + errmsg = "EInk Kindle Serial Numbers must be 16 characters long. This is {0:d} characters long.".format(len(self.key_name)) return error_dialog(None, "{0} {1}".format(PLUGIN_NAME, PLUGIN_VERSION), errmsg, show=True, show_copy_button=False) QDialog.accept(self) @@ -899,7 +887,7 @@ class AddAndroidDialog(QDialog): QDialog.__init__(self, parent) self.parent = parent - self.setWindowTitle(u"{0} {1}: Add new Kindle for Android Key".format(PLUGIN_NAME, PLUGIN_VERSION)) + self.setWindowTitle("{0} {1}: Add new Kindle for Android Key".format(PLUGIN_NAME, PLUGIN_VERSION)) layout = QVBoxLayout(self) self.setLayout(layout) self.button_box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) @@ -911,8 +899,8 @@ class AddAndroidDialog(QDialog): file_group = QHBoxLayout() data_group_box_layout.addLayout(file_group) - add_btn = QPushButton(u"Choose Backup File", self) - add_btn.setToolTip(u"Import Kindle for Android backup file.") + add_btn = QPushButton("Choose Backup File", self) + add_btn.setToolTip("Import Kindle for Android backup file.") add_btn.clicked.connect(self.get_android_file) file_group.addWidget(add_btn) self.selected_file_name = QLabel(u"",self) @@ -921,9 +909,9 @@ class AddAndroidDialog(QDialog): key_group = QHBoxLayout() data_group_box_layout.addLayout(key_group) - key_group.addWidget(QLabel(u"Unique Key Name:", self)) + key_group.addWidget(QLabel("Unique Key Name:", self)) self.key_ledit = QLineEdit(u"", self) - self.key_ledit.setToolTip(u"

Enter an identifying name for the Android for Kindle key.") + self.key_ledit.setToolTip("

Enter an identifying name for the Android for Kindle key.") key_group.addWidget(self.key_ledit) #key_label = QLabel(_(''), self) #key_label.setAlignment(Qt.AlignHCenter) @@ -947,9 +935,9 @@ class AddAndroidDialog(QDialog): 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" - filters = [(u"Kindle for Android backup files", ['db','ab','xml'])] + unique_dlg_name = PLUGIN_NAME + "Import Kindle for Android backup file" #takes care of automatically remembering last directory + caption = "Select Kindle for Android backup file to add" + filters = [("Kindle for Android backup files", ['db','ab','xml'])] files = choose_files(self, unique_dlg_name, caption, filters, all_files=False) self.serials_from_file = [] file_name = u"" @@ -967,13 +955,13 @@ class AddAndroidDialog(QDialog): def accept(self): if len(self.file_name) == 0 or len(self.key_value) == 0: - errmsg = u"Please choose a Kindle for Android backup file." + errmsg = "Please choose a Kindle for Android backup file." return error_dialog(None, "{0} {1}".format(PLUGIN_NAME, PLUGIN_VERSION), errmsg, show=True, show_copy_button=False) if len(self.key_name) == 0 or self.key_name.isspace(): - errmsg = u"Please enter a key name." + errmsg = "Please enter a key name." return error_dialog(None, "{0} {1}".format(PLUGIN_NAME, PLUGIN_VERSION), errmsg, show=True, show_copy_button=False) if len(self.key_name) < 4: - errmsg = u"Key name must be at least 4 characters long!" + errmsg = "Key name must be at least 4 characters long!" return error_dialog(None, "{0} {1}".format(PLUGIN_NAME, PLUGIN_VERSION), errmsg, show=True, show_copy_button=False) QDialog.accept(self) @@ -981,7 +969,7 @@ class AddPIDDialog(QDialog): def __init__(self, parent=None,): QDialog.__init__(self, parent) self.parent = parent - self.setWindowTitle(u"{0} {1}: Add New Mobipocket PID".format(PLUGIN_NAME, PLUGIN_VERSION)) + self.setWindowTitle("{0} {1}: Add New Mobipocket PID".format(PLUGIN_NAME, PLUGIN_VERSION)) layout = QVBoxLayout(self) self.setLayout(layout) @@ -992,9 +980,9 @@ class AddPIDDialog(QDialog): key_group = QHBoxLayout() data_group_box_layout.addLayout(key_group) - key_group.addWidget(QLabel(u"PID:", self)) + key_group.addWidget(QLabel("PID:", self)) self.key_ledit = QLineEdit("", self) - self.key_ledit.setToolTip(u"Enter a Mobipocket PID. Mobipocket PIDs are 8 or 10 characters long. Mobipocket PIDs are case-sensitive, so be sure to enter the upper and lower case letters unchanged.") + self.key_ledit.setToolTip("Enter a Mobipocket PID. Mobipocket PIDs are 8 or 10 characters long. Mobipocket PIDs are case-sensitive, so be sure to enter the upper and lower case letters unchanged.") key_group.addWidget(self.key_ledit) self.button_box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) @@ -1014,10 +1002,10 @@ class AddPIDDialog(QDialog): def accept(self): if len(self.key_name) == 0 or self.key_name.isspace(): - errmsg = u"Please enter a Mobipocket PID or click Cancel in the dialog." + errmsg = "Please enter a Mobipocket PID or click Cancel in the dialog." return error_dialog(None, "{0} {1}".format(PLUGIN_NAME, PLUGIN_VERSION), errmsg, show=True, show_copy_button=False) if len(self.key_name) != 8 and len(self.key_name) != 10: - errmsg = u"Mobipocket PIDs must be 8 or 10 characters long. This is {0:d} characters long.".format(len(self.key_name)) + errmsg = "Mobipocket PIDs must be 8 or 10 characters long. This is {0:d} characters long.".format(len(self.key_name)) return error_dialog(None, "{0} {1}".format(PLUGIN_NAME, PLUGIN_VERSION), errmsg, show=True, show_copy_button=False) QDialog.accept(self) diff --git a/DeDRM_plugin/convert2xml.py b/DeDRM_plugin/convert2xml.py index aa794b1..a0bf188 100644 --- a/DeDRM_plugin/convert2xml.py +++ b/DeDRM_plugin/convert2xml.py @@ -1,15 +1,16 @@ -#! /usr/bin/python +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- # 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 +# For use with Topaz Scripts Version 2.6 +# Python 3, September 2020 + class Unbuffered: def __init__(self, stream): self.stream = stream def write(self, data): - self.stream.write(data) - self.stream.flush() + self.stream.buffer.write(data) + self.stream.buffer.flush() def __getattr__(self, attr): return getattr(self.stream, attr) diff --git a/DeDRM_plugin/encodebase64.py b/DeDRM_plugin/encodebase64.py deleted file mode 100644 index cfe0d26..0000000 --- a/DeDRM_plugin/encodebase64.py +++ /dev/null @@ -1,46 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -# base64.py, version 1.0 -# Copyright © 2010 Apprentice Alf - -# Released under the terms of the GNU General Public Licence, version 3 or -# later. - -# Revision history: -# 1 - Initial release. To allow Applescript to do base64 encoding - -""" -Provide base64 encoding. -""" - -from __future__ import with_statement -from __future__ import print_function - -__license__ = 'GPL v3' - -import sys -import os -import base64 - -def usage(progname): - print("Applies base64 encoding to the supplied file, sending to standard output") - print("Usage:") - print(" %s " % progname) - -def cli_main(argv=sys.argv): - progname = os.path.basename(argv[0]) - - if len(argv)<2: - usage(progname) - sys.exit(2) - - keypath = argv[1] - with open(keypath, 'rb') as f: - keyder = f.read() - print(keyder.encode('base64')) - return 0 - - -if __name__ == '__main__': - sys.exit(cli_main()) diff --git a/DeDRM_plugin/epubtest.py b/DeDRM_plugin/epubtest.py index 1a08e61..baede6a 100644 --- a/DeDRM_plugin/epubtest.py +++ b/DeDRM_plugin/epubtest.py @@ -1,4 +1,5 @@ -#!/usr/bin/python +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- # # This is a python script. You need a Python interpreter to run it. # For example, ActiveState Python, which exists for windows. @@ -10,7 +11,7 @@ # 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 +# 2.00 - Python 3, September 2020 # # Written in 2011 by Paul Durrant # Released with unlicense. See http://unlicense.org/ @@ -45,9 +46,6 @@ # It's still polite to give attribution if you do reuse this code. # -from __future__ import with_statement -from __future__ import print_function - __version__ = '2.0' import sys, struct, os, traceback @@ -112,7 +110,7 @@ def unicode_argv(): xrange(start, argc.value)] # if we don't have any arguments at all, just pass back script name # this should never happen - return [u"epubtest.py"] + return ["epubtest.py"] else: argvencoding = sys.stdin.encoding if argvencoding == None: diff --git a/DeDRM_plugin/erdr2pml.py b/DeDRM_plugin/erdr2pml.py index b02a494..401fecc 100644 --- a/DeDRM_plugin/erdr2pml.py +++ b/DeDRM_plugin/erdr2pml.py @@ -1,13 +1,9 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # -*- coding: utf-8 -*- # erdr2pml.py -# Copyright © 2008 The Dark Reverser +# Copyright © 2008-2020 The Dark Reverser, Apprentice Harper et al. # -# Modified 2008–2012 by some_updates, DiapDealer and Apprentice Alf - -# This is a python script. You need a Python interpreter to run it. -# For example, ActiveState Python, which exists for windows. # Changelog # # Based on ereader2html version 0.08 plus some later small fixes @@ -89,10 +85,10 @@ class SafeUnbuffered: if self.encoding == None: self.encoding = "utf-8" def write(self, data): - if isinstance(data,bytes): + if isinstance(data,str): data = data.encode(self.encoding,"replace") - self.stream.write(data) - self.stream.flush() + self.stream.buffer.write(data) + self.stream.buffer.flush() def __getattr__(self, attr): return getattr(self.stream, attr) @@ -130,7 +126,7 @@ def unicode_argv(): 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"] + return ["mobidedrm.py"] else: argvencoding = sys.stdin.encoding if argvencoding == None: @@ -230,16 +226,16 @@ class Sectionizer(object): # and with some (heavily edited) code from Paul Durrant's kindlenamer.py def sanitizeFileName(name): # 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"\'") + name = name.replace("<","[").replace(">","]").replace(" : "," – ").replace(": "," – ").replace(":","—").replace("/","_").replace("\\","_").replace("|","_").replace("\"","\'") # delete control characters - name = u"".join(char for char in name if ord(char)>=32) + name = "".join(char for char in name if ord(char)>=32) # white space to single space, delete leading and trailing while space - name = re.sub(r"\s", u" ", name).strip() + name = re.sub(r"\s", " ", name).strip() # remove leading dots - while len(name)>0 and name[0] == u".": + while len(name)>0 and name[0] == ".": name = name[1:] # remove trailing dots (Windows doesn't like them) - if name.endswith(u'.'): + if name.endswith("."): name = name[:-1] return name @@ -472,35 +468,35 @@ def decryptBook(infile, outpath, make_pmlz, user_key): # outpath is actually pmlz name pmlzname = outpath outdir = tempfile.mkdtemp() - imagedirpath = os.path.join(outdir,u"images") + imagedirpath = os.path.join(outdir,"images") else: pmlzname = None outdir = outpath - imagedirpath = os.path.join(outdir,bookname + u"_img") + imagedirpath = os.path.join(outdir,bookname + "_img") try: if not os.path.exists(outdir): os.makedirs(outdir) - print(u"Decoding File") - sect = Sectionizer(infile, 'PNRdPPrs') + print("Decoding File") + sect =Sectionizer(infile, 'PNRdPPrs') er = EreaderProcessor(sect, user_key) if er.getNumImages() > 0: - print(u"Extracting images") + print("Extracting images") if not os.path.exists(imagedirpath): os.makedirs(imagedirpath) for i in range(er.getNumImages()): name, contents = er.getImage(i) open(os.path.join(imagedirpath, name), 'wb').write(contents) - print(u"Extracting pml") + print("Extracting pml") pml_string = er.getText() pmlfilename = bookname + ".pml" 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("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: @@ -519,33 +515,33 @@ def decryptBook(infile, outpath, make_pmlz, user_key): myZipFile.close() # remove temporary directory shutil.rmtree(outdir, True) - print(u"Output is {0}".format(pmlzname)) - else : - print(u"Output is in {0}".format(outdir)) + print("Output is {0}".format(pmlzname)) + else + print("Output is in {0}".format(outdir)) print("done") except ValueError as e: - print(u"Error: {0}".format(e)) + print("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("Converts DRMed eReader books to PML Source") + print("Usage:") + print(" erdr2pml [options] infile.pdb [outpath] \"your name\" credit_card_number") + print(" ") + print("Options: ") + print(" -h prints this message") + print(" -p create PMLZ instead of source folder") + print(" --make-pmlz create PMLZ instead of source folder") + print(" ") + print("Note:") + print(" if outpath is ommitted, creates source in 'infile_Source' folder") + print(" if outpath is ommitted and pmlz option, creates PMLZ 'infile.pmlz'") + print(" if source folder created, images are in infile_img folder") + print(" if pmlz file created, images are in images folder") + print(" It's enough to enter the last 8 digits of the credit card number") return def getuser_key(name,cc): @@ -554,7 +550,7 @@ 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("eRdr2Pml v{0}. Copyright © 2009–2020 The Dark Reverser et al.".format(__version__)) argv=unicode_argv() try: @@ -580,9 +576,9 @@ def cli_main(): if len(args)==3: infile, name, cc = args if make_pmlz: - outpath = os.path.splitext(infile)[0] + u".pmlz" + outpath = os.path.splitext(infile)[0] + ".pmlz" else: - outpath = os.path.splitext(infile)[0] + u"_Source" + outpath = os.path.splitext(infile)[0] + "_Source" elif len(args)==4: infile, outpath, name, cc = args diff --git a/DeDRM_plugin/genbook.py b/DeDRM_plugin/genbook.py index 6667e49..791bc5e 100644 --- a/DeDRM_plugin/genbook.py +++ b/DeDRM_plugin/genbook.py @@ -1,16 +1,14 @@ -#! /usr/bin/python +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- # 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 +# Python 3 for calibre 5.0 class Unbuffered: def __init__(self, stream): self.stream = stream def write(self, data): - self.stream.write(data) - self.stream.flush() + self.stream.buffer.write(data) + self.stream.buffer.flush() def __getattr__(self, attr): return getattr(self.stream, attr) diff --git a/DeDRM_plugin/ignobleepub.py b/DeDRM_plugin/ignobleepub.py index 9ec9459..d10b209 100644 --- a/DeDRM_plugin/ignobleepub.py +++ b/DeDRM_plugin/ignobleepub.py @@ -1,28 +1,13 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # -*- coding: utf-8 -*- -from __future__ import with_statement -from __future__ import print_function - -# ignobleepub.pyw, version 4.1 -# Copyright © 2009-2010 by i♥cabbages +# ignobleepub.py +# Copyright © 2009-2020 by i♥cabbages, Apprentice Harper et al. # Released under the terms of the GNU General Public Licence, version 3 # -# Modified 2010–2013 by some_updates, DiapDealer and Apprentice Alf -# Modified 2015–2017 by Apprentice Harper - -# Windows users: Before running this program, you must first install Python 2.6 -# from and PyCrypto from -# (make sure to -# install the version for Python 2.6). Save this script file as -# ineptepub.pyw and double-click on it to run it. # -# Mac OS X users: Save this script file as ineptepub.pyw. You can run this -# program from the command line (pythonw ineptepub.pyw) or by double-clicking -# it when it has been associated with PythonLauncher. - # Revision history: # 1 - Initial release # 2 - Added OS X support by using OpenSSL when available @@ -38,7 +23,7 @@ from __future__ import print_function # 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 +# 5.0 - Python 3 for calibre 5.0 """ Decrypt Barnes & Noble encrypted ePub books. @@ -66,10 +51,10 @@ class SafeUnbuffered: if self.encoding == None: self.encoding = "utf-8" def write(self, data): - if isinstance(data,bytes): + if isinstance(data,str): data = data.encode(self.encoding,"replace") - self.stream.write(data) - self.stream.flush() + self.stream.buffer.write(data) + self.stream.buffer.flush() def __getattr__(self, attr): return getattr(self.stream, attr) @@ -108,7 +93,7 @@ def unicode_argv(): start = argc.value - len(sys.argv) return [argv[i] for i in range(start, argc.value)] - return [u"ineptepub.py"] + return ["ineptepub.py"] else: argvencoding = sys.stdin.encoding if argvencoding == None: @@ -257,14 +242,14 @@ def ignobleBook(inpath): def decryptBook(keyb64, inpath, outpath): if AES is None: - raise IGNOBLEError(u"PyCrypto or OpenSSL must be installed.") + raise IGNOBLEError("PyCrypto or OpenSSL must be installed.") key = keyb64.decode('base64')[:16] aes = AES(key) with closing(ZipFile(open(inpath, 'rb'))) as inf: namelist = set(inf.namelist()) if 'META-INF/rights.xml' not in namelist or \ 'META-INF/encryption.xml' not in namelist: - print(u"{0:s} is DRM-free.".format(os.path.basename(inpath))) + print("{0:s} is DRM-free.".format(os.path.basename(inpath))) return 1 for name in META_NAMES: namelist.remove(name) @@ -274,7 +259,7 @@ def decryptBook(keyb64, inpath, outpath): expr = './/%s' % (adept('encryptedKey'),) bookkey = ''.join(rights.findtext(expr)) if len(bookkey) != 64: - print(u"{0:s} is not a secure Barnes & Noble ePub.".format(os.path.basename(inpath))) + print("{0:s} is not a secure Barnes & Noble ePub.".format(os.path.basename(inpath))) return 1 bookkey = aes.decrypt(bookkey.decode('base64')) bookkey = bookkey[:-ord(bookkey[-1])] @@ -317,7 +302,7 @@ def decryptBook(keyb64, inpath, outpath): pass outf.writestr(zi, decryptor.decrypt(path, data)) except: - print(u"Could not decrypt {0:s} because of an exception:\n{1:s}".format(os.path.basename(inpath), traceback.format_exc())) + print("Could not decrypt {0:s} because of an exception:\n{1:s}".format(os.path.basename(inpath), traceback.format_exc())) return 2 return 0 @@ -328,13 +313,13 @@ def cli_main(): argv=unicode_argv() progname = os.path.basename(argv[0]) if len(argv) != 4: - print(u"usage: {0} ".format(progname)) + print("usage: {0} ".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("Successfully decrypted {0:s} as {1:s}".format(os.path.basename(inpath),os.path.basename(outpath))) return result def gui_main(): @@ -350,43 +335,43 @@ def gui_main(): class DecryptionDialog(Tkinter.Frame): def __init__(self, root): Tkinter.Frame.__init__(self, root, border=5) - self.status = Tkinter.Label(self, text=u"Select files for decryption") + self.status = Tkinter.Label(self, text="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) - Tkinter.Label(body, text=u"Key file").grid(row=0) + Tkinter.Label(body, text="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"bnepubkey.b64"): - self.keypath.insert(0, u"bnepubkey.b64") - button = Tkinter.Button(body, text=u"...", command=self.get_keypath) + if os.path.exists("bnepubkey.b64"): + self.keypath.insert(0, "bnepubkey.b64") + button = Tkinter.Button(body, text="...", command=self.get_keypath) button.grid(row=0, column=2) - Tkinter.Label(body, text=u"Input file").grid(row=1) + Tkinter.Label(body, text="Input file").grid(row=1) self.inpath = Tkinter.Entry(body, width=30) self.inpath.grid(row=1, column=1, sticky=sticky) - button = Tkinter.Button(body, text=u"...", command=self.get_inpath) + button = Tkinter.Button(body, text="...", command=self.get_inpath) button.grid(row=1, column=2) - Tkinter.Label(body, text=u"Output file").grid(row=2) + Tkinter.Label(body, text="Output file").grid(row=2) self.outpath = Tkinter.Entry(body, width=30) self.outpath.grid(row=2, column=1, sticky=sticky) - button = Tkinter.Button(body, text=u"...", command=self.get_outpath) + button = Tkinter.Button(body, text="...", command=self.get_outpath) button.grid(row=2, column=2) buttons = Tkinter.Frame(self) buttons.pack() botton = Tkinter.Button( - buttons, text=u"Decrypt", width=10, command=self.decrypt) + buttons, text="Decrypt", width=10, command=self.decrypt) 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) + buttons, text="Quit", width=10, command=self.quit) button.pack(side=Tkconstants.RIGHT) def get_keypath(self): keypath = tkFileDialog.askopenfilename( - parent=None, title=u"Select Barnes & Noble \'.b64\' key file", - defaultextension=u".b64", + parent=None, title="Select Barnes & Noble \'.b64\' key file", + defaultextension=".b64", filetypes=[('base64-encoded files', '.b64'), ('All Files', '.*')]) if keypath: @@ -397,8 +382,8 @@ def gui_main(): def get_inpath(self): inpath = tkFileDialog.askopenfilename( - parent=None, title=u"Select B&N-encrypted ePub file to decrypt", - defaultextension=u".epub", filetypes=[('ePub files', '.epub')]) + parent=None, title="Select B&N-encrypted ePub file to decrypt", + defaultextension=".epub", filetypes=[('ePub files', '.epub')]) if inpath: inpath = os.path.normpath(inpath) self.inpath.delete(0, Tkconstants.END) @@ -407,8 +392,8 @@ def gui_main(): def get_outpath(self): outpath = tkFileDialog.asksaveasfilename( - parent=None, title=u"Select unencrypted ePub file to produce", - defaultextension=u".epub", filetypes=[('ePub files', '.epub')]) + parent=None, title="Select unencrypted ePub file to produce", + defaultextension=".epub", filetypes=[('ePub files', '.epub')]) if outpath: outpath = os.path.normpath(outpath) self.outpath.delete(0, Tkconstants.END) @@ -420,31 +405,31 @@ def gui_main(): inpath = self.inpath.get() outpath = self.outpath.get() if not keypath or not os.path.exists(keypath): - self.status['text'] = u"Specified key file does not exist" + self.status['text'] = "Specified key file does not exist" return if not inpath or not os.path.exists(inpath): - self.status['text'] = u"Specified input file does not exist" + self.status['text'] = "Specified input file does not exist" return if not outpath: - self.status['text'] = u"Output file not specified" + self.status['text'] = "Output file not specified" return if inpath == outpath: - self.status['text'] = u"Must have different input and output files" + self.status['text'] = "Must have different input and output files" return userkey = open(keypath,'rb').read() - self.status['text'] = u"Decrypting..." + self.status['text'] = "Decrypting..." try: decrypt_status = decryptBook(userkey, inpath, outpath) except Exception as e: - self.status['text'] = u"Error: {0}".format(e.args[0]) + self.status['text'] = "Error: {0}".format(e.args[0]) return if decrypt_status == 0: - self.status['text'] = u"File successfully decrypted" + self.status['text'] = "File successfully decrypted" else: - self.status['text'] = u"The was an error decrypting the file." + self.status['text'] = "The was an error decrypting the file." root = Tkinter.Tk() - root.title(u"Barnes & Noble ePub Decrypter v.{0}".format(__version__)) + root.title("Barnes & Noble ePub Decrypter v.{0}".format(__version__)) root.resizable(True, False) root.minsize(300, 0) DecryptionDialog(root).pack(fill=Tkconstants.X, expand=1) diff --git a/DeDRM_plugin/ignoblekey.py b/DeDRM_plugin/ignoblekey.py index 0b3e60f..b47a07a 100644 --- a/DeDRM_plugin/ignoblekey.py +++ b/DeDRM_plugin/ignoblekey.py @@ -1,9 +1,6 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # -*- coding: utf-8 -*- -from __future__ import with_statement -from __future__ import print_function - # ignoblekey.py # Copyright © 2015-2020 Apprentice Alf, Apprentice Harper et al. @@ -15,7 +12,7 @@ from __future__ import print_function # 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 +# 2.0 - Python 3 for calibre 5.0 """ Get Barnes & Noble EPUB user key from nook Studio log file @@ -40,10 +37,10 @@ class SafeUnbuffered: if self.encoding == None: self.encoding = "utf-8" def write(self, data): - if isinstance(data,bytes): + if isinstance(data,str): data = data.encode(self.encoding,"replace") - self.stream.write(data) - self.stream.flush() + self.stream.buffer.write(data) + self.stream.buffer.flush() def __getattr__(self, attr): return getattr(self.stream, attr) @@ -84,7 +81,7 @@ def unicode_argv(): 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"] + return ["ignoblekey.py"] else: argvencoding = sys.stdin.encoding if argvencoding == None: @@ -106,15 +103,15 @@ def getNookLogFiles(): paths = set() if 'LOCALAPPDATA' in os.environ.keys(): # Python 2.x does not return unicode env. Use Python 3.x - path = winreg.ExpandEnvironmentStrings(u"%LOCALAPPDATA%") + path = winreg.ExpandEnvironmentStrings("%LOCALAPPDATA%") if os.path.isdir(path): paths.add(path) if 'USERPROFILE' in os.environ.keys(): # Python 2.x does not return unicode env. Use Python 3.x - path = winreg.ExpandEnvironmentStrings(u"%USERPROFILE%")+u"\\AppData\\Local" + path = winreg.ExpandEnvironmentStrings("%USERPROFILE%")+"\\AppData\\Local" if os.path.isdir(path): paths.add(path) - path = winreg.ExpandEnvironmentStrings(u"%USERPROFILE%")+u"\\AppData\\Roaming" + path = winreg.ExpandEnvironmentStrings("%USERPROFILE%")+"\\AppData\\Roaming" if os.path.isdir(path): paths.add(path) # User Shell Folders show take precedent over Shell Folders if present @@ -200,7 +197,7 @@ def nookkeys(files = []): for file in files: fileKeys = getKeysFromLog(file) if fileKeys: - print(u"Found {0} keys in the Nook Study log files".format(len(fileKeys))) + print("Found {0} keys in the Nook Study log files".format(len(fileKeys))) keys.extend(fileKeys) return list(set(keys)) @@ -213,27 +210,27 @@ def getkey(outpath, files=[]): outfile = outpath with open(outfile, 'w') as keyfileout: keyfileout.write(keys[-1]) - print(u"Saved a key to {0}".format(outfile)) + print("Saved a key to {0}".format(outfile)) else: keycount = 0 for key in keys: while True: keycount += 1 - outfile = os.path.join(outpath,u"nookkey{0:d}.b64".format(keycount)) + outfile = os.path.join(outpath,"nookkey{0:d}.b64".format(keycount)) if not os.path.exists(outfile): break with open(outfile, 'w') as keyfileout: keyfileout.write(key) - print(u"Saved a key to {0}".format(outfile)) + print("Saved a key to {0}".format(outfile)) return True return False def usage(progname): - print(u"Finds the nook Study 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 ] []".format(progname)) + print("Finds the nook Study encryption keys.") + print("Keys are saved to the current directory, or a specified output directory.") + print("If a file name is passed instead of a directory, only the first key is saved, in that file.") + print("Usage:") + print(" {0:s} [-h] [-k ] []".format(progname)) def cli_main(): @@ -241,12 +238,12 @@ def cli_main(): sys.stderr=SafeUnbuffered(sys.stderr) argv=unicode_argv() progname = os.path.basename(argv[0]) - print(u"{0} v{1}\nCopyright © 2015 Apprentice Alf".format(progname,__version__)) + print("{0} v{1}\nCopyright © 2015 Apprentice Alf".format(progname,__version__)) try: opts, args = getopt.getopt(argv[1:], "hk:") except getopt.GetoptError as err: - print(u"Error in options or arguments: {0}".format(err.args[0])) + print("Error in options or arguments: {0}".format(err.args[0])) usage(progname) sys.exit(2) @@ -275,7 +272,7 @@ def cli_main(): outpath = os.path.realpath(os.path.normpath(outpath)) if not getkey(outpath, files): - print(u"Could not retrieve nook Study key.") + print("Could not retrieve nook Study key.") return 0 @@ -291,7 +288,7 @@ def gui_main(): class ExceptionDialog(Tkinter.Frame): def __init__(self, root, text): Tkinter.Frame.__init__(self, root, border=5) - label = Tkinter.Label(self, text=u"Unexpected error:", + label = Tkinter.Label(self, text="Unexpected error:", anchor=Tkconstants.W, justify=Tkconstants.LEFT) label.pack(fill=Tkconstants.X, expand=0) self.text = Tkinter.Text(self) @@ -312,16 +309,16 @@ def gui_main(): print(key) while True: keycount += 1 - outfile = os.path.join(progpath,u"nookkey{0:d}.b64".format(keycount)) + outfile = os.path.join(progpath,"nookkey{0:d}.b64".format(keycount)) if not os.path.exists(outfile): break with open(outfile, 'w') as keyfileout: keyfileout.write(key) success = True - tkMessageBox.showinfo(progname, u"Key successfully retrieved to {0}".format(outfile)) + tkMessageBox.showinfo(progname, "Key successfully retrieved to {0}".format(outfile)) except DrmException as e: - tkMessageBox.showerror(progname, u"Error: {0}".format(str(e))) + tkMessageBox.showerror(progname, "Error: {0}".format(str(e))) except Exception: root.wm_state('normal') root.title(progname) diff --git a/DeDRM_plugin/ignoblekeyfetch.py b/DeDRM_plugin/ignoblekeyfetch.py index ffaf153..a844fd8 100644 --- a/DeDRM_plugin/ignoblekeyfetch.py +++ b/DeDRM_plugin/ignoblekeyfetch.py @@ -1,10 +1,7 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # -*- coding: utf-8 -*- -from __future__ import with_statement -from __future__ import print_function - -# ignoblekeyfetch.pyw, version 2.0 +# ignoblekeyfetch.py # Copyright © 2015-2020 Apprentice Harper et al. # Released under the terms of the GNU General Public Licence, version 3 @@ -25,14 +22,14 @@ from __future__ import print_function # 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 +# 2.0 - Python 3 for calibre 5.0 """ Fetch Barnes & Noble EPUB user key from B&N servers using email and password """ __license__ = 'GPL v3' -__version__ = "1.1" +__version__ = "2.0" import sys import os @@ -91,7 +88,7 @@ def unicode_argv(): 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"] + return ["ignoblekeyfetch.py"] else: argvencoding = sys.stdin.encoding if argvencoding == None: @@ -157,14 +154,14 @@ def cli_main(): argv=unicode_argv() progname = os.path.basename(argv[0]) if len(argv) != 4: - print(u"usage: {0} ".format(progname)) + print("usage: {0} ".format(progname)) return 1 email, password, keypath = argv[1:] userkey = fetch_key(email, password) if len(userkey) == 28: open(keypath,'wb').write(userkey) return 0 - print(u"Failed to fetch key.") + print("Failed to fetch key.") return 1 @@ -181,38 +178,38 @@ def gui_main(): class DecryptionDialog(Tkinter.Frame): def __init__(self, root): Tkinter.Frame.__init__(self, root, border=5) - self.status = Tkinter.Label(self, text=u"Enter parameters") + self.status = Tkinter.Label(self, text="Enter parameters") 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) - Tkinter.Label(body, text=u"Account email address").grid(row=0) + Tkinter.Label(body, text="Account email address").grid(row=0) self.name = Tkinter.Entry(body, width=40) self.name.grid(row=0, column=1, sticky=sticky) - Tkinter.Label(body, text=u"Account password").grid(row=1) + Tkinter.Label(body, text="Account password").grid(row=1) self.ccn = Tkinter.Entry(body, width=40) self.ccn.grid(row=1, column=1, sticky=sticky) - Tkinter.Label(body, text=u"Output file").grid(row=2) + Tkinter.Label(body, text="Output file").grid(row=2) self.keypath = Tkinter.Entry(body, width=40) self.keypath.grid(row=2, column=1, sticky=sticky) - self.keypath.insert(2, u"bnepubkey.b64") - button = Tkinter.Button(body, text=u"...", command=self.get_keypath) + self.keypath.insert(2, "bnepubkey.b64") + button = Tkinter.Button(body, text="...", command=self.get_keypath) button.grid(row=2, column=2) buttons = Tkinter.Frame(self) buttons.pack() botton = Tkinter.Button( - buttons, text=u"Fetch", width=10, command=self.generate) + buttons, text="Fetch", width=10, command=self.generate) 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) + buttons, text="Quit", width=10, command=self.quit) button.pack(side=Tkconstants.RIGHT) def get_keypath(self): keypath = tkFileDialog.asksaveasfilename( - parent=None, title=u"Select B&N ePub key file to produce", - defaultextension=u".b64", + parent=None, title="Select B&N ePub key file to produce", + defaultextension=".b64", filetypes=[('base64-encoded files', '.b64'), ('All Files', '.*')]) if keypath: @@ -226,28 +223,28 @@ def gui_main(): password = self.ccn.get() keypath = self.keypath.get() if not email: - self.status['text'] = u"Email address not given" + self.status['text'] = "Email address not given" return if not password: - self.status['text'] = u"Account password not given" + self.status['text'] = "Account password not given" return if not keypath: - self.status['text'] = u"Output keyfile path not set" + self.status['text'] = "Output keyfile path not set" return - self.status['text'] = u"Fetching..." + self.status['text'] = "Fetching..." try: userkey = fetch_key(email, password) except Exception as e: - self.status['text'] = u"Error: {0}".format(e.args[0]) + self.status['text'] = "Error: {0}".format(e.args[0]) return if len(userkey) == 28: open(keypath,'wb').write(userkey) - self.status['text'] = u"Keyfile fetched successfully" + self.status['text'] = "Keyfile fetched successfully" else: - self.status['text'] = u"Keyfile fetch failed." + self.status['text'] = "Keyfile fetch failed." root = Tkinter.Tk() - root.title(u"Barnes & Noble ePub Keyfile Fetch v.{0}".format(__version__)) + root.title("Barnes & Noble ePub Keyfile Fetch v.{0}".format(__version__)) root.resizable(True, False) root.minsize(300, 0) DecryptionDialog(root).pack(fill=Tkconstants.X, expand=1) diff --git a/DeDRM_plugin/ignoblekeygen.py b/DeDRM_plugin/ignoblekeygen.py index 3582f24..489f2b9 100644 --- a/DeDRM_plugin/ignoblekeygen.py +++ b/DeDRM_plugin/ignoblekeygen.py @@ -1,10 +1,7 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # -*- coding: utf-8 -*- -from __future__ import with_statement -from __future__ import print_function - -# ignoblekeygen.pyw +# ignoblekeygen.py # Copyright © 2009-2020 i♥cabbages, Apprentice Harper et al. # Released under the terms of the GNU General Public Licence, version 3 @@ -100,7 +97,7 @@ def unicode_argv(): 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"] + return ["ignoblekeygen.py"] else: argvencoding = sys.stdin.encoding if argvencoding == None: @@ -228,7 +225,7 @@ def cli_main(): (progname,)) return 1 if len(argv) != 4: - print(u"usage: {0} ".format(progname)) + print("usage: {0} ".format(progname)) return 1 name, ccn, keypath = argv[1:] userkey = generate_key(name, ccn) @@ -249,38 +246,38 @@ def gui_main(): class DecryptionDialog(Tkinter.Frame): def __init__(self, root): Tkinter.Frame.__init__(self, root, border=5) - self.status = Tkinter.Label(self, text=u"Enter parameters") + self.status = Tkinter.Label(self, text="Enter parameters") 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) - Tkinter.Label(body, text=u"Account Name").grid(row=0) + Tkinter.Label(body, text="Account Name").grid(row=0) self.name = Tkinter.Entry(body, width=40) self.name.grid(row=0, column=1, sticky=sticky) - Tkinter.Label(body, text=u"CC#").grid(row=1) + Tkinter.Label(body, text="CC#").grid(row=1) self.ccn = Tkinter.Entry(body, width=40) self.ccn.grid(row=1, column=1, sticky=sticky) - Tkinter.Label(body, text=u"Output file").grid(row=2) + Tkinter.Label(body, text="Output file").grid(row=2) self.keypath = Tkinter.Entry(body, width=40) self.keypath.grid(row=2, column=1, sticky=sticky) - self.keypath.insert(2, u"bnepubkey.b64") - button = Tkinter.Button(body, text=u"...", command=self.get_keypath) + self.keypath.insert(2, "bnepubkey.b64") + button = Tkinter.Button(body, text="...", command=self.get_keypath) button.grid(row=2, column=2) buttons = Tkinter.Frame(self) buttons.pack() botton = Tkinter.Button( - buttons, text=u"Generate", width=10, command=self.generate) + buttons, text="Generate", width=10, command=self.generate) 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) + buttons, text="Quit", width=10, command=self.quit) button.pack(side=Tkconstants.RIGHT) def get_keypath(self): keypath = tkFileDialog.asksaveasfilename( - parent=None, title=u"Select B&N ePub key file to produce", - defaultextension=u".b64", + parent=None, title="Select B&N ePub key file to produce", + defaultextension=".b64", filetypes=[('base64-encoded files', '.b64'), ('All Files', '.*')]) if keypath: @@ -294,22 +291,22 @@ def gui_main(): ccn = self.ccn.get() keypath = self.keypath.get() if not name: - self.status['text'] = u"Name not specified" + self.status['text'] = "Name not specified" return if not ccn: - self.status['text'] = u"Credit card number not specified" + self.status['text'] = "Credit card number not specified" return if not keypath: - self.status['text'] = u"Output keyfile path not specified" + self.status['text'] = "Output keyfile path not specified" return - self.status['text'] = u"Generating..." + self.status['text'] = "Generating..." try: userkey = generate_key(name, ccn) except Exception as e: - self.status['text'] = u"Error: (0}".format(e.args[0]) + self.status['text'] = "Error: (0}".format(e.args[0]) return open(keypath,'wb').write(userkey) - self.status['text'] = u"Keyfile successfully generated" + self.status['text'] = "Keyfile successfully generated" root = Tkinter.Tk() if AES is None: @@ -319,7 +316,7 @@ def gui_main(): "This script requires OpenSSL or PyCrypto, which must be installed " "separately. Read the top-of-script comment for details.") return 1 - root.title(u"Barnes & Noble ePub Keyfile Generator v.{0}".format(__version__)) + root.title("Barnes & Noble ePub Keyfile Generator v.{0}".format(__version__)) root.resizable(True, False) root.minsize(300, 0) DecryptionDialog(root).pack(fill=Tkconstants.X, expand=1) diff --git a/DeDRM_plugin/ignoblepdf.py b/DeDRM_plugin/ignoblepdf.py index 5232b1d..1546fa1 100644 --- a/DeDRM_plugin/ignoblepdf.py +++ b/DeDRM_plugin/ignoblepdf.py @@ -1,7 +1,6 @@ -#! /usr/bin/python +#!/usr/bin/env python3 # -*- coding: utf-8 -*- -from __future__ import with_statement # ignoblepdf.py # Copyright © 2009-2020 by Apprentice Harper et al. @@ -14,6 +13,7 @@ from __future__ import with_statement # Revision history: # 0.1 - Initial alpha testing release 2020 by Pu D. Pud +# 0.2 - Python 3 for calibre 5.0 (in testing) """ @@ -82,7 +82,7 @@ def unicode_argv(): start = argc.value - len(sys.argv) return [argv[i] for i in xrange(start, argc.value)] - return [u"ignoblepdf.py"] + return ["ignoblepdf.py"] else: argvencoding = sys.stdin.encoding if argvencoding == None: @@ -2005,12 +2005,12 @@ class PDFSerializer(object): def decryptBook(userkey, inpath, outpath): if AES is None: - raise IGNOBLEError(u"PyCrypto or OpenSSL must be installed.") + raise IGNOBLEError("PyCrypto or OpenSSL must be installed.") with open(inpath, 'rb') as inf: #try: serializer = PDFSerializer(inf, userkey) #except: - # print u"Error serializing pdf {0}. Probably wrong key.".format(os.path.basename(inpath)) + # print "Error serializing pdf {0}. Probably wrong key.".format(os.path.basename(inpath)) # return 2 # hope this will fix the 'bad file descriptor' problem with open(outpath, 'wb') as outf: @@ -2018,7 +2018,7 @@ def decryptBook(userkey, inpath, outpath): try: serializer.dump(outf) except Exception, e: - print u"error writing pdf: {0}".format(e.args[0]) + print "error writing pdf: {0}".format(e.args[0]) return 2 return 0 @@ -2029,13 +2029,13 @@ def cli_main(): argv=unicode_argv() progname = os.path.basename(argv[0]) if len(argv) != 4: - print u"usage: {0} ".format(progname) + print "usage: {0} ".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 "Successfully decrypted {0:s} as {1:s}".format(os.path.basename(inpath),os.path.basename(outpath)) return result @@ -2052,43 +2052,43 @@ def gui_main(): class DecryptionDialog(Tkinter.Frame): def __init__(self, root): Tkinter.Frame.__init__(self, root, border=5) - self.status = Tkinter.Label(self, text=u"Select files for decryption") + self.status = Tkinter.Label(self, text="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) - Tkinter.Label(body, text=u"Key file").grid(row=0) + Tkinter.Label(body, text="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"bnpdfkey.b64"): - self.keypath.insert(0, u"bnpdfkey.b64") - button = Tkinter.Button(body, text=u"...", command=self.get_keypath) + if os.path.exists("bnpdfkey.b64"): + self.keypath.insert(0, "bnpdfkey.b64") + button = Tkinter.Button(body, text="...", command=self.get_keypath) button.grid(row=0, column=2) - Tkinter.Label(body, text=u"Input file").grid(row=1) + Tkinter.Label(body, text="Input file").grid(row=1) self.inpath = Tkinter.Entry(body, width=30) self.inpath.grid(row=1, column=1, sticky=sticky) - button = Tkinter.Button(body, text=u"...", command=self.get_inpath) + button = Tkinter.Button(body, text="...", command=self.get_inpath) button.grid(row=1, column=2) - Tkinter.Label(body, text=u"Output file").grid(row=2) + Tkinter.Label(body, text="Output file").grid(row=2) self.outpath = Tkinter.Entry(body, width=30) self.outpath.grid(row=2, column=1, sticky=sticky) - button = Tkinter.Button(body, text=u"...", command=self.get_outpath) + button = Tkinter.Button(body, text="...", command=self.get_outpath) button.grid(row=2, column=2) buttons = Tkinter.Frame(self) buttons.pack() botton = Tkinter.Button( - buttons, text=u"Decrypt", width=10, command=self.decrypt) + buttons, text="Decrypt", width=10, command=self.decrypt) 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) + buttons, text="Quit", width=10, command=self.quit) button.pack(side=Tkconstants.RIGHT) def get_keypath(self): keypath = tkFileDialog.askopenfilename( - parent=None, title=u"Select Barnes & Noble \'.b64\' key file", - defaultextension=u".b64", + parent=None, title="Select Barnes & Noble \'.b64\' key file", + defaultextension=".b64", filetypes=[('base64-encoded files', '.b64'), ('All Files', '.*')]) if keypath: @@ -2099,8 +2099,8 @@ def gui_main(): def get_inpath(self): inpath = tkFileDialog.askopenfilename( - parent=None, title=u"Select B&N-encrypted PDF file to decrypt", - defaultextension=u".pdf", filetypes=[('PDF files', '.pdf')]) + parent=None, title="Select B&N-encrypted PDF file to decrypt", + defaultextension=".pdf", filetypes=[('PDF files', '.pdf')]) if inpath: inpath = os.path.normpath(inpath) self.inpath.delete(0, Tkconstants.END) @@ -2109,8 +2109,8 @@ def gui_main(): def get_outpath(self): outpath = tkFileDialog.asksaveasfilename( - parent=None, title=u"Select unencrypted PDF file to produce", - defaultextension=u".pdf", filetypes=[('PDF files', '.pdf')]) + parent=None, title="Select unencrypted PDF file to produce", + defaultextension=".pdf", filetypes=[('PDF files', '.pdf')]) if outpath: outpath = os.path.normpath(outpath) self.outpath.delete(0, Tkconstants.END) @@ -2122,28 +2122,28 @@ def gui_main(): inpath = self.inpath.get() outpath = self.outpath.get() if not keypath or not os.path.exists(keypath): - self.status['text'] = u"Specified key file does not exist" + self.status['text'] = "Specified key file does not exist" return if not inpath or not os.path.exists(inpath): - self.status['text'] = u"Specified input file does not exist" + self.status['text'] = "Specified input file does not exist" return if not outpath: - self.status['text'] = u"Output file not specified" + self.status['text'] = "Output file not specified" return if inpath == outpath: - self.status['text'] = u"Must have different input and output files" + self.status['text'] = "Must have different input and output files" return userkey = open(keypath,'rb').read() - self.status['text'] = u"Decrypting..." + self.status['text'] = "Decrypting..." try: decrypt_status = decryptBook(userkey, inpath, outpath) except Exception, e: - self.status['text'] = u"Error; {0}".format(e.args[0]) + self.status['text'] = "Error; {0}".format(e.args[0]) return if decrypt_status == 0: - self.status['text'] = u"File successfully decrypted" + self.status['text'] = "File successfully decrypted" else: - self.status['text'] = u"The was an error decrypting the file." + self.status['text'] = "The was an error decrypting the file." root = Tkinter.Tk() @@ -2154,7 +2154,7 @@ def gui_main(): "This script requires OpenSSL or PyCrypto, which must be installed " "separately. Read the top-of-script comment for details.") return 1 - root.title(u"Barnes & Noble PDF Decrypter v.{0}".format(__version__)) + root.title("Barnes & Noble PDF Decrypter v.{0}".format(__version__)) root.resizable(True, False) root.minsize(370, 0) DecryptionDialog(root).pack(fill=Tkconstants.X, expand=1) diff --git a/DeDRM_plugin/ineptepub.py b/DeDRM_plugin/ineptepub.py index c0e4c39..f4b9ca3 100644 --- a/DeDRM_plugin/ineptepub.py +++ b/DeDRM_plugin/ineptepub.py @@ -1,9 +1,7 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # -*- coding: utf-8 -*- -from __future__ import with_statement - -# ineptepub.pyw +# ineptepub.py # Copyright © 2009-2020 by i♥cabbages, Apprentice Harper et al. # Released under the terms of the GNU General Public Licence, version 3 @@ -102,7 +100,7 @@ def unicode_argv(): start = argc.value - len(sys.argv) return [argv[i] for i in range(start, argc.value)] - return [u"ineptepub.py"] + return ["ineptepub.py"] else: argvencoding = sys.stdin.encoding if argvencoding == None: @@ -393,13 +391,13 @@ def adeptBook(inpath): def decryptBook(userkey, inpath, outpath): if AES is None: - raise ADEPTError(u"PyCrypto or OpenSSL must be installed.") + raise ADEPTError("PyCrypto or OpenSSL must be installed.") rsa = RSA(userkey) with closing(ZipFile(open(inpath, 'rb'))) as inf: namelist = set(inf.namelist()) if 'META-INF/rights.xml' not in namelist or \ 'META-INF/encryption.xml' not in namelist: - print(u"{0:s} is DRM-free.".format(os.path.basename(inpath))) + print("{0:s} is DRM-free.".format(os.path.basename(inpath))) return 1 for name in META_NAMES: namelist.remove(name) @@ -409,12 +407,12 @@ def decryptBook(userkey, inpath, outpath): expr = './/%s' % (adept('encryptedKey'),) bookkey = ''.join(rights.findtext(expr)) if len(bookkey) != 172: - print(u"{0:s} is not a secure Adobe Adept ePub.".format(os.path.basename(inpath))) + print("{0:s} is not a secure Adobe Adept ePub.".format(os.path.basename(inpath))) return 1 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))) + print("Could not decrypt {0:s}. Wrong key".format(os.path.basename(inpath))) return 2 encryption = inf.read('META-INF/encryption.xml') decryptor = Decryptor(bookkey[-16:], encryption) @@ -455,7 +453,7 @@ def decryptBook(userkey, inpath, outpath): pass outf.writestr(zi, decryptor.decrypt(path, data)) except: - print(u"Could not decrypt {0:s} because of an exception:\n{1:s}".format(os.path.basename(inpath), traceback.format_exc())) + print("Could not decrypt {0:s} because of an exception:\n{1:s}".format(os.path.basename(inpath), traceback.format_exc())) return 2 return 0 @@ -466,13 +464,13 @@ def cli_main(): argv=unicode_argv() progname = os.path.basename(argv[0]) if len(argv) != 4: - print(u"usage: {0} ".format(progname)) + print("usage: {0} ".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("Successfully decrypted {0:s} as {1:s}".format(os.path.basename(inpath),os.path.basename(outpath))) return result def gui_main(): @@ -488,43 +486,43 @@ def gui_main(): class DecryptionDialog(Tkinter.Frame): def __init__(self, root): Tkinter.Frame.__init__(self, root, border=5) - self.status = Tkinter.Label(self, text=u"Select files for decryption") + self.status = Tkinter.Label(self, text="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) - Tkinter.Label(body, text=u"Key file").grid(row=0) + Tkinter.Label(body, text="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 = Tkinter.Button(body, text=u"...", command=self.get_keypath) + if os.path.exists("adeptkey.der"): + self.keypath.insert(0, "adeptkey.der") + button = Tkinter.Button(body, text="...", command=self.get_keypath) button.grid(row=0, column=2) - Tkinter.Label(body, text=u"Input file").grid(row=1) + Tkinter.Label(body, text="Input file").grid(row=1) self.inpath = Tkinter.Entry(body, width=30) self.inpath.grid(row=1, column=1, sticky=sticky) - button = Tkinter.Button(body, text=u"...", command=self.get_inpath) + button = Tkinter.Button(body, text="...", command=self.get_inpath) button.grid(row=1, column=2) - Tkinter.Label(body, text=u"Output file").grid(row=2) + Tkinter.Label(body, text="Output file").grid(row=2) self.outpath = Tkinter.Entry(body, width=30) self.outpath.grid(row=2, column=1, sticky=sticky) - button = Tkinter.Button(body, text=u"...", command=self.get_outpath) + button = Tkinter.Button(body, text="...", command=self.get_outpath) button.grid(row=2, column=2) buttons = Tkinter.Frame(self) buttons.pack() botton = Tkinter.Button( - buttons, text=u"Decrypt", width=10, command=self.decrypt) + buttons, text="Decrypt", width=10, command=self.decrypt) 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) + buttons, text="Quit", width=10, command=self.quit) button.pack(side=Tkconstants.RIGHT) def get_keypath(self): keypath = tkFileDialog.askopenfilename( - parent=None, title=u"Select Adobe Adept \'.der\' key file", - defaultextension=u".der", + parent=None, title="Select Adobe Adept \'.der\' key file", + defaultextension=".der", filetypes=[('Adobe Adept DER-encoded files', '.der'), ('All Files', '.*')]) if keypath: @@ -535,8 +533,8 @@ def gui_main(): def get_inpath(self): inpath = tkFileDialog.askopenfilename( - parent=None, title=u"Select ADEPT-encrypted ePub file to decrypt", - defaultextension=u".epub", filetypes=[('ePub files', '.epub')]) + parent=None, title="Select ADEPT-encrypted ePub file to decrypt", + defaultextension=".epub", filetypes=[('ePub files', '.epub')]) if inpath: inpath = os.path.normpath(inpath) self.inpath.delete(0, Tkconstants.END) @@ -545,8 +543,8 @@ def gui_main(): def get_outpath(self): outpath = tkFileDialog.asksaveasfilename( - parent=None, title=u"Select unencrypted ePub file to produce", - defaultextension=u".epub", filetypes=[('ePub files', '.epub')]) + parent=None, title="Select unencrypted ePub file to produce", + defaultextension=".epub", filetypes=[('ePub files', '.epub')]) if outpath: outpath = os.path.normpath(outpath) self.outpath.delete(0, Tkconstants.END) @@ -558,31 +556,31 @@ def gui_main(): inpath = self.inpath.get() outpath = self.outpath.get() if not keypath or not os.path.exists(keypath): - self.status['text'] = u"Specified key file does not exist" + self.status['text'] = "Specified key file does not exist" return if not inpath or not os.path.exists(inpath): - self.status['text'] = u"Specified input file does not exist" + self.status['text'] = "Specified input file does not exist" return if not outpath: - self.status['text'] = u"Output file not specified" + self.status['text'] = "Output file not specified" return if inpath == outpath: - self.status['text'] = u"Must have different input and output files" + self.status['text'] = "Must have different input and output files" return userkey = open(keypath,'rb').read() - self.status['text'] = u"Decrypting..." + self.status['text'] = "Decrypting..." try: decrypt_status = decryptBook(userkey, inpath, outpath) except Exception as e: - self.status['text'] = u"Error: {0}".format(e.args[0]) + self.status['text'] = "Error: {0}".format(e.args[0]) return if decrypt_status == 0: - self.status['text'] = u"File successfully decrypted" + self.status['text'] = "File successfully decrypted" else: - self.status['text'] = u"The was an error decrypting the file." + self.status['text'] = "The was an error decrypting the file." root = Tkinter.Tk() - root.title(u"Adobe Adept ePub Decrypter v.{0}".format(__version__)) + root.title("Adobe Adept ePub Decrypter v.{0}".format(__version__)) root.resizable(True, False) root.minsize(300, 0) DecryptionDialog(root).pack(fill=Tkconstants.X, expand=1) diff --git a/DeDRM_plugin/ineptpdf.py b/DeDRM_plugin/ineptpdf.py index c29a536..5604fae 100644 --- a/DeDRM_plugin/ineptpdf.py +++ b/DeDRM_plugin/ineptpdf.py @@ -1,8 +1,6 @@ -#! /usr/bin/python +#!/usr/bin/env python3 # -*- coding: utf-8 -*- -from __future__ import with_statement - # ineptpdf.py # Copyright © 2009-2020 by i♥cabbages, Apprentice Harper et al. @@ -115,7 +113,7 @@ def unicode_argv(): start = argc.value - len(sys.argv) return [argv[i] for i in range(start, argc.value)] - return [u"ineptpdf.py"] + return ["ineptpdf.py"] else: argvencoding = sys.stdin.encoding if argvencoding is None: @@ -2178,12 +2176,12 @@ class PDFSerializer(object): def decryptBook(userkey, inpath, outpath): if RSA is None: - raise ADEPTError(u"PyCrypto or OpenSSL must be installed.") + raise ADEPTError("PyCrypto or OpenSSL must be installed.") with open(inpath, 'rb') as inf: #try: serializer = PDFSerializer(inf, userkey) #except: - # print u"Error serializing pdf {0}. Probably wrong key.".format(os.path.basename(inpath)) + # print "Error serializing pdf {0}. Probably wrong key.".format(os.path.basename(inpath)) # return 2 # hope this will fix the 'bad file descriptor' problem with open(outpath, 'wb') as outf: @@ -2191,7 +2189,7 @@ def decryptBook(userkey, inpath, outpath): try: serializer.dump(outf) except Exception as e: - print(u"error writing pdf: {0}".format(e.args[0])) + print("error writing pdf: {0}".format(e.args[0])) return 2 return 0 @@ -2202,13 +2200,13 @@ def cli_main(): argv=unicode_argv() progname = os.path.basename(argv[0]) if len(argv) != 4: - print(u"usage: {0} ".format(progname)) + print("usage: {0} ".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("Successfully decrypted {0:s} as {1:s}".format(os.path.basename(inpath),os.path.basename(outpath))) return result @@ -2225,43 +2223,43 @@ def gui_main(): class DecryptionDialog(Tkinter.Frame): def __init__(self, root): Tkinter.Frame.__init__(self, root, border=5) - self.status = Tkinter.Label(self, text=u"Select files for decryption") + self.status = Tkinter.Label(self, text="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) - Tkinter.Label(body, text=u"Key file").grid(row=0) + Tkinter.Label(body, text="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 = Tkinter.Button(body, text=u"...", command=self.get_keypath) + if os.path.exists("adeptkey.der"): + self.keypath.insert(0, "adeptkey.der") + button = Tkinter.Button(body, text="...", command=self.get_keypath) button.grid(row=0, column=2) - Tkinter.Label(body, text=u"Input file").grid(row=1) + Tkinter.Label(body, text="Input file").grid(row=1) self.inpath = Tkinter.Entry(body, width=30) self.inpath.grid(row=1, column=1, sticky=sticky) - button = Tkinter.Button(body, text=u"...", command=self.get_inpath) + button = Tkinter.Button(body, text="...", command=self.get_inpath) button.grid(row=1, column=2) - Tkinter.Label(body, text=u"Output file").grid(row=2) + Tkinter.Label(body, text="Output file").grid(row=2) self.outpath = Tkinter.Entry(body, width=30) self.outpath.grid(row=2, column=1, sticky=sticky) - button = Tkinter.Button(body, text=u"...", command=self.get_outpath) + button = Tkinter.Button(body, text="...", command=self.get_outpath) button.grid(row=2, column=2) buttons = Tkinter.Frame(self) buttons.pack() botton = Tkinter.Button( - buttons, text=u"Decrypt", width=10, command=self.decrypt) + buttons, text="Decrypt", width=10, command=self.decrypt) 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) + buttons, text="Quit", width=10, command=self.quit) button.pack(side=Tkconstants.RIGHT) def get_keypath(self): keypath = tkFileDialog.askopenfilename( - parent=None, title=u"Select Adobe Adept \'.der\' key file", - defaultextension=u".der", + parent=None, title="Select Adobe Adept \'.der\' key file", + defaultextension=".der", filetypes=[('Adobe Adept DER-encoded files', '.der'), ('All Files', '.*')]) if keypath: @@ -2272,8 +2270,8 @@ def gui_main(): def get_inpath(self): inpath = tkFileDialog.askopenfilename( - parent=None, title=u"Select ADEPT-encrypted PDF file to decrypt", - defaultextension=u".pdf", filetypes=[('PDF files', '.pdf')]) + parent=None, title="Select ADEPT-encrypted PDF file to decrypt", + defaultextension=".pdf", filetypes=[('PDF files', '.pdf')]) if inpath: inpath = os.path.normpath(inpath) self.inpath.delete(0, Tkconstants.END) @@ -2282,8 +2280,8 @@ def gui_main(): def get_outpath(self): outpath = tkFileDialog.asksaveasfilename( - parent=None, title=u"Select unencrypted PDF file to produce", - defaultextension=u".pdf", filetypes=[('PDF files', '.pdf')]) + parent=None, title="Select unencrypted PDF file to produce", + defaultextension=".pdf", filetypes=[('PDF files', '.pdf')]) if outpath: outpath = os.path.normpath(outpath) self.outpath.delete(0, Tkconstants.END) @@ -2295,28 +2293,28 @@ def gui_main(): inpath = self.inpath.get() outpath = self.outpath.get() if not keypath or not os.path.exists(keypath): - self.status['text'] = u"Specified key file does not exist" + self.status['text'] = "Specified key file does not exist" return if not inpath or not os.path.exists(inpath): - self.status['text'] = u"Specified input file does not exist" + self.status['text'] = "Specified input file does not exist" return if not outpath: - self.status['text'] = u"Output file not specified" + self.status['text'] = "Output file not specified" return if inpath == outpath: - self.status['text'] = u"Must have different input and output files" + self.status['text'] = "Must have different input and output files" return userkey = open(keypath,'rb').read() - self.status['text'] = u"Decrypting..." + self.status['text'] = "Decrypting..." try: decrypt_status = decryptBook(userkey, inpath, outpath) except Exception as e: - self.status['text'] = u"Error; {0}".format(e.args[0]) + self.status['text'] = "Error; {0}".format(e.args[0]) return if decrypt_status == 0: - self.status['text'] = u"File successfully decrypted" + self.status['text'] = "File successfully decrypted" else: - self.status['text'] = u"The was an error decrypting the file." + self.status['text'] = "The was an error decrypting the file." root = Tkinter.Tk() @@ -2327,7 +2325,7 @@ def gui_main(): "This script requires OpenSSL or PyCrypto, which must be installed " "separately. Read the top-of-script comment for details.") return 1 - root.title(u"Adobe Adept PDF Decrypter v.{0}".format(__version__)) + root.title("Adobe Adept PDF Decrypter v.{0}".format(__version__)) root.resizable(True, False) root.minsize(370, 0) DecryptionDialog(root).pack(fill=Tkconstants.X, expand=1) diff --git a/DeDRM_plugin/ion.py b/DeDRM_plugin/ion.py index f385cc3..ac1b6ad 100644 --- a/DeDRM_plugin/ion.py +++ b/DeDRM_plugin/ion.py @@ -1,8 +1,6 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # -*- coding: utf-8 -*- -from __future__ import with_statement - # ion.py # Copyright © 2013-2020 Apprentice Harper et al. diff --git a/DeDRM_plugin/k4mobidedrm.py b/DeDRM_plugin/k4mobidedrm.py index eaaa43b..18addc2 100644 --- a/DeDRM_plugin/k4mobidedrm.py +++ b/DeDRM_plugin/k4mobidedrm.py @@ -1,8 +1,6 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # -*- coding: utf-8 -*- -from __future__ import with_statement - # k4mobidedrm.py # Copyright © 2008-2020 by Apprentice Harper et al. @@ -146,7 +144,7 @@ def unicode_argv(): 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"] + return ["mobidedrm.py"] else: argvencoding = sys.stdin.encoding if argvencoding == None: @@ -161,31 +159,31 @@ def unicode_argv(): # and some improvements suggested by jhaisley def cleanup_name(name): # 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"") + name = name.replace("<","[").replace(">","]").replace(" : "," – ").replace(": "," – ").replace(":","—").replace("/","_").replace("\\","_").replace("|","_").replace("\"","\'").replace("*","_").replace("?",u"") # white space to single space, delete leading and trailing while space - name = re.sub(r"\s", u" ", name).strip() + name = re.sub(r"\s", " ", name).strip() # delete control characters name = u"".join(char for char in name if ord(char)>=32) # delete non-ascii characters name = u"".join(char for char in name if ord(char)<=126) # remove leading dots - while len(name)>0 and name[0] == u".": + while len(name)>0 and name[0] == ".": name = name[1:] # remove trailing dots (Windows doesn't like them) - while name.endswith(u'.'): + while name.endswith("."): name = name[:-1] if len(name)==0: - name=u"DecryptedBook" + name="DecryptedBook" return name # must be passed unicode def unescape(text): def fixup(m): text = m.group(0) - if text[:2] == u"&#": + if text[:2] == "&#": # character reference try: - if text[:3] == u"&#x": + if text[:3] == "&#x": return chr(int(text[3:-1], 16)) else: return chr(int(text[2:-1])) @@ -198,17 +196,17 @@ def unescape(text): except KeyError: pass return text # leave as is - return re.sub(u"&#?\\w+;", fixup, text) + return re.sub("&#?\\w+;", fixup, text) def GetDecryptedBook(infile, kDatabases, androidFiles, serials, pids, starttime = time.time()): # handle the obvious cases at the beginning if not os.path.isfile(infile): - raise DrmException(u"Input file does not exist.") + raise DrmException("Input file does not exist.") mobi = True magic8 = open(infile,'rb').read(8) if magic8 == '\xeaDRMION\xee': - raise DrmException(u"The .kfx DRMION file cannot be decrypted by itself. A .kfx-zip archive containing a DRM voucher is required.") + raise DrmException("The .kfx DRMION file cannot be decrypted by itself. A .kfx-zip archive containing a DRM voucher is required.") magic3 = magic8[:3] if magic3 == 'TPZ': @@ -222,7 +220,7 @@ def GetDecryptedBook(infile, kDatabases, androidFiles, serials, pids, starttime mb = topazextract.TopazBook(infile) bookname = unescape(mb.getBookTitle()) - print(u"Decrypting {1} ebook: {0}".format(bookname, mb.getBookType())) + print("Decrypting {1} ebook: {0}".format(bookname, mb.getBookType())) # copy list of pids totalpids = list(pids) @@ -234,7 +232,7 @@ def GetDecryptedBook(infile, kDatabases, androidFiles, serials, pids, starttime 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("Found {1:d} keys to try after {0:.1f} seconds".format(time.time()-starttime, len(totalpids))) #print totalpids try: @@ -243,7 +241,7 @@ def GetDecryptedBook(infile, kDatabases, androidFiles, serials, pids, starttime mb.cleanup raise - print(u"Decryption succeeded after {0:.1f} seconds".format(time.time()-starttime)) + print("Decryption succeeded after {0:.1f} seconds".format(time.time()-starttime)) return mb @@ -258,7 +256,7 @@ def decryptBook(infile, outdir, kDatabaseFiles, androidFiles, serials, pids): kindleDatabase = json.loads(keyfilein.read()) kDatabases.append([dbfile,kindleDatabase]) except Exception as e: - print(u"Error getting database from file {0:s}: {1:s}".format(dbfile,e)) + print("Error getting database from file {0:s}: {1:s}".format(dbfile,e)) traceback.print_exc() @@ -266,7 +264,7 @@ def decryptBook(infile, outdir, kDatabaseFiles, androidFiles, serials, pids): try: book = GetDecryptedBook(infile, kDatabases, androidFiles, serials, pids, starttime) except Exception as e: - print(u"Error decrypting book after {1:.1f} seconds: {0}".format(e.args[0],time.time()-starttime)) + print("Error decrypting book after {1:.1f} seconds: {0}".format(e.args[0],time.time()-starttime)) traceback.print_exc() return 1 @@ -277,7 +275,7 @@ def decryptBook(infile, outdir, kDatabaseFiles, androidFiles, serials, pids): re.match('^{0-9A-F-}{36}$', orig_fn_root) ): # Kindle for PC / Mac / Android / Fire / iOS clean_title = cleanup_name(book.getBookTitle()) - outfilename = u'{}_{}'.format(orig_fn_root, clean_title) + outfilename = "{}_{}".format(orig_fn_root, clean_title) else: # E Ink Kindle, which already uses a reasonable name outfilename = orig_fn_root @@ -285,16 +283,16 @@ def decryptBook(infile, outdir, kDatabaseFiles, androidFiles, serials, pids): if len(outfilename)>150: outfilename = outfilename[:99]+"--"+outfilename[-49:] - outfilename = outfilename+u"_nodrm" + outfilename = outfilename+"_nodrm" 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("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") + if book.getBookType()=="Topaz": + zipname = os.path.join(outdir, outfilename + "_SVG.zip") book.getSVGZip(zipname) - print(u"Saved SVG ZIP Archive for {1:s} after {0:.1f} seconds".format(time.time()-starttime, outfilename)) + print("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() @@ -302,9 +300,9 @@ def decryptBook(infile, outdir, kDatabaseFiles, androidFiles, serials, pids): 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 ] [-p ] [-s ] [ -a ] ".format(progname)) + print("Removes DRM protection from Mobipocket, Amazon KF8, Amazon Print Replica and Amazon Topaz ebooks") + print("Usage:") + print(" {0} [-k ] [-p ] [-s ] [ -a ] ".format(progname)) # # Main @@ -312,12 +310,12 @@ def usage(progname): 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("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 as err: - print(u"Error in options or arguments: {0}".format(err.args[0])) + print("Error in options or arguments: {0}".format(err.args[0])) usage(progname) sys.exit(2) if len(args)<2: diff --git a/DeDRM_plugin/kfxdedrm.py b/DeDRM_plugin/kfxdedrm.py index d3c0f7e..875c4a1 100644 --- a/DeDRM_plugin/kfxdedrm.py +++ b/DeDRM_plugin/kfxdedrm.py @@ -1,12 +1,9 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # -*- coding: utf-8 -*- -from __future__ import with_statement -from __future__ import print_function - # Engine to remove drm from Kindle KFX ebooks -# 2.0 - Added Python 3 compatibility for calibre 5.0 +# 2.0 - Python 3 for calibre 5.0 import os @@ -50,13 +47,13 @@ class KFXZipBook: data += fh.read() if self.voucher is None: self.decrypt_voucher(totalpids) - print(u'Decrypting KFX DRMION: {0}'.format(filename)) + print("Decrypting KFX DRMION: {0}".format(filename)) outfile = StringIO() ion.DrmIon(StringIO(data[8:-8]), lambda name: self.voucher).parse(outfile) self.decrypted[filename] = outfile.getvalue() if not self.decrypted: - print(u'The .kfx-zip archive does not contain an encrypted DRMION file') + print("The .kfx-zip archive does not contain an encrypted DRMION file") def decrypt_voucher(self, totalpids): with zipfile.ZipFile(self.infile, 'r') as zf: @@ -70,9 +67,9 @@ class KFXZipBook: if 'ProtectedData' in data: break # found DRM voucher else: - raise Exception(u'The .kfx-zip archive contains an encrypted DRMION file without a DRM voucher') + raise Exception("The .kfx-zip archive contains an encrypted DRMION file without a DRM voucher") - print(u'Decrypting KFX DRM voucher: {0}'.format(info.filename)) + print("Decrypting KFX DRM voucher: {0}".format(info.filename)) for pid in [''] + totalpids: for dsn_len,secret_len in [(0,0), (16,0), (16,40), (32,40), (40,0), (40,40)]: @@ -89,13 +86,13 @@ class KFXZipBook: except: pass else: - raise Exception(u'Failed to decrypt KFX DRM voucher with any key') + raise Exception("Failed to decrypt KFX DRM voucher with any key") - print(u'KFX DRM voucher successfully decrypted') + print("KFX DRM voucher successfully decrypted") license_type = voucher.getlicensetype() if license_type != "Purchase": - raise Exception((u'This book is licensed as {0}. ' + raise Exception(("This book is licensed as {0}. " 'These tools are intended for use on purchased books.').format(license_type)) self.voucher = voucher diff --git a/DeDRM_plugin/kgenpids.py b/DeDRM_plugin/kgenpids.py index a3efee0..3e80598 100644 --- a/DeDRM_plugin/kgenpids.py +++ b/DeDRM_plugin/kgenpids.py @@ -1,9 +1,6 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # -*- coding: utf-8 -*- -from __future__ import with_statement -from __future__ import print_function - # kgenpids.py # Copyright © 2008-2020 Apprentice Harper et al. @@ -14,7 +11,7 @@ __version__ = '3.0' # 2.0 - Fix for non-ascii Windows user names # 2.1 - Actual fix for non-ascii WIndows user names. # 2.2 - Return information needed for KFX decryption -# 3.0 - Added Python 3 compatibility for calibre 5.0 +# 3.0 - Python 3 for calibre 5.0 import sys @@ -217,18 +214,18 @@ def getK4Pids(rec209, token, kindleDatabase): try: # Get the DSN token, if present DSN = bytearray.fromhex((kindleDatabase[1])['DSN']).decode() - print(u"Got DSN key from database {0}".format(kindleDatabase[0])) + print("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 = bytearray.fromhex((kindleDatabase[1])['MazamaRandomNumber']).decode() - #print u"Got MazamaRandomNumber from database {0}".format(kindleDatabase[0]) + #print "Got MazamaRandomNumber from database {0}".format(kindleDatabase[0]) try: # Get the SerialNumber token, if present IDString = bytearray.fromhex((kindleDatabase[1])['SerialNumber']).decode() - print(u"Got SerialNumber from database {0}".format(kindleDatabase[0])) + print("Got SerialNumber from database {0}".format(kindleDatabase[0])) except KeyError: # Get the IDString we added IDString = bytearray.fromhex((kindleDatabase[1])['IDString']).decode() @@ -236,24 +233,24 @@ def getK4Pids(rec209, token, kindleDatabase): try: # Get the UsernameHash token, if present encodedUsername = bytearray.fromhex((kindleDatabase[1])['UsernameHash']).decode() - print(u"Got UsernameHash from database {0}".format(kindleDatabase[0])) + print("Got UsernameHash from database {0}".format(kindleDatabase[0])) except KeyError: # Get the UserName we added UserName = bytearray.fromhex((kindleDatabase[1])['UserName']).decode() # encode it encodedUsername = encodeHash(UserName,charMap1) - #print u"encodedUsername",encodedUsername.encode('hex') + #print "encodedUsername",encodedUsername.encode('hex') except KeyError: - print(u"Keys not found in the database {0}.".format(kindleDatabase[0])) + print("Keys not found in the database {0}.".format(kindleDatabase[0])) return pids # Get the ID string used encodedIDString = encodeHash(IDString,charMap1) - #print u"encodedIDString",encodedIDString.encode('hex') + #print "encodedIDString",encodedIDString.encode('hex') # concat, hash and encode to calculate the DSN DSN = encode(SHA1(MazamaRandomNumber+encodedIDString+encodedUsername),charMap1) - #print u"DSN",DSN.encode('hex') + #print "DSN",DSN.encode('hex') pass if rec209 is None: @@ -300,14 +297,14 @@ def getPidList(md1, md2, serials=[], kDatabases=[]): try: pidlst.extend(getK4Pids(md1, md2, kDatabase)) except Exception as e: - print(u"Error getting PIDs from database {0}: {1}".format(kDatabase[0],e.args[0])) + print("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 as e: - print(u"Error getting PIDs from serial number {0}: {1}".format(serialnum ,e.args[0])) + print("Error getting PIDs from serial number {0}: {1}".format(serialnum ,e.args[0])) traceback.print_exc() return pidlst diff --git a/DeDRM_plugin/kindlekey.py b/DeDRM_plugin/kindlekey.py index 5e9f5c8..05b1142 100644 --- a/DeDRM_plugin/kindlekey.py +++ b/DeDRM_plugin/kindlekey.py @@ -1,8 +1,6 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # -*- coding: utf-8 -*- -from __future__ import with_statement - # kindlekey.py # Copyright © 2008-2020 Apprentice Harper et al. @@ -30,7 +28,7 @@ __version__ = '3.0' # 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 +# 3.0 - Python 3 for calibre 5.0 """ @@ -104,7 +102,7 @@ def unicode_argv(): xrange(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"] + return ["kindlekey.py"] else: argvencoding = sys.stdin.encoding if argvencoding == None: @@ -905,11 +903,11 @@ if iswindows: # replace any non-ASCII values with 0xfffd for i in xrange(0,len(buffer)): - if buffer[i]>u"\u007f": - #print u"swapping char "+str(i)+" ("+buffer[i]+")" - buffer[i] = u"\ufffd" + if buffer[i]>"\u007f": + #print "swapping char "+str(i)+" ("+buffer[i]+")" + buffer[i] = "\ufffd" # return utf-8 encoding of modified username - #print u"modified username:"+buffer.value + #print "modified username:"+buffer.value return buffer.value.encode('utf-8') return GetUserName GetUserName = GetUserName() @@ -939,7 +937,7 @@ if iswindows: n = ctypes.windll.kernel32.GetEnvironmentVariableW(name, None, 0) if n == 0: return None - buf = ctypes.create_unicode_buffer(u'\0'*n) + buf = ctypes.create_unicode_buffer("\0"*n) ctypes.windll.kernel32.GetEnvironmentVariableW(name, buf, n) return buf.value @@ -951,7 +949,7 @@ if iswindows: path = "" if 'LOCALAPPDATA' in os.environ.keys(): # Python 2.x does not return unicode env. Use Python 3.x - path = winreg.ExpandEnvironmentStrings(u"%LOCALAPPDATA%") + path = winreg.ExpandEnvironmentStrings("%LOCALAPPDATA%") # this is just another alternative. # path = getEnvironmentVariable('LOCALAPPDATA') if not os.path.isdir(path): @@ -979,7 +977,7 @@ if iswindows: print ('Could not find the folder in which to look for kinfoFiles.') else: # Probably not the best. To Fix (shouldn't ignore in encoding) or use utf-8 - print(u'searching for kinfoFiles in ' + path.encode('ascii', 'ignore')) + print("searching for kinfoFiles in " + path.encode('ascii', 'ignore')) # look for (K4PC 1.25.1 and later) .kinf2018 file kinfopath = path +'\\Amazon\\Kindle\\storage\\.kinf2018' @@ -1166,9 +1164,9 @@ if iswindows: # 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("Decrypted key file using IDString '{0:s}' and UserName '{1:s}'".format(GetIDString(), GetUserName().encode('hex'))) else: - print(u"Couldn't decrypt file.") + print("Couldn't decrypt file.") DB = {} return DB elif isosx: @@ -1183,7 +1181,7 @@ elif isosx: libcrypto = find_library('crypto') if libcrypto is None: - raise DrmException(u"libcrypto not found") + raise DrmException("libcrypto not found") libcrypto = CDLL(libcrypto) # From OpenSSL's crypto aes header @@ -1241,14 +1239,14 @@ elif isosx: def set_decrypt_key(self, userkey, iv): self._blocksize = len(userkey) if (self._blocksize != 16) and (self._blocksize != 24) and (self._blocksize != 32) : - raise DrmException(u"AES improper key used") + raise DrmException("AES improper key used") return keyctx = self._keyctx = AES_KEY() self._iv = iv self._userkey = userkey rv = AES_set_decrypt_key(userkey, len(userkey) * 8, keyctx) if rv < 0: - raise DrmException(u"Failed to initialize AES key") + raise DrmException("Failed to initialize AES key") def decrypt(self, data): out = create_string_buffer(len(data)) @@ -1256,7 +1254,7 @@ elif isosx: keyctx = self._keyctx rv = AES_cbc_encrypt(data, out, len(data), keyctx, mutable_iv, 0) if rv == 0: - raise DrmException(u"AES decryption failed") + raise DrmException("AES decryption failed") return out.raw def keyivgen(self, passwd, salt, iter, keylen): @@ -1649,16 +1647,16 @@ 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("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("Couldn't decrypt file.") DB = {} return DB else: def getDBfromFile(kInfoFile): - raise DrmException(u"This script only runs under Windows or Mac OS X.") + raise DrmException("This script only runs under Windows or Mac OS X.") return {} def kindlekeys(files = []): @@ -1683,27 +1681,27 @@ def getkey(outpath, files=[]): outfile = outpath with file(outfile, 'w') as keyfileout: keyfileout.write(json.dumps(keys[0])) - print(u"Saved a key to {0}".format(outfile)) + print("Saved a key to {0}".format(outfile)) else: keycount = 0 for key in keys: while True: keycount += 1 - outfile = os.path.join(outpath,u"kindlekey{0:d}.k4i".format(keycount)) + outfile = os.path.join(outpath,"kindlekey{0:d}.k4i".format(keycount)) if not os.path.exists(outfile): break with file(outfile, 'w') as keyfileout: keyfileout.write(json.dumps(key)) - print(u"Saved a key to {0}".format(outfile)) + print("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 ] []".format(progname)) + print("Finds, decrypts and saves the default Kindle For Mac/PC encryption keys.") + print("Keys are saved to the current directory, or a specified output directory.") + print("If a file name is passed instead of a directory, only the first key is saved, in that file.") + print("Usage:") + print(" {0:s} [-h] [-k ] []".format(progname)) def cli_main(): @@ -1711,12 +1709,12 @@ 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("{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 as err: - print(u"Error in options or arguments: {0}".format(err.args[0])) + print("Error in options or arguments: {0}".format(err.args[0])) usage(progname) sys.exit(2) @@ -1745,7 +1743,7 @@ def cli_main(): outpath = os.path.realpath(os.path.normpath(outpath)) if not getkey(outpath, files): - print(u"Could not retrieve Kindle for Mac/PC key.") + print("Could not retrieve Kindle for Mac/PC key.") return 0 @@ -1761,7 +1759,7 @@ def gui_main(): class ExceptionDialog(Tkinter.Frame): def __init__(self, root, text): Tkinter.Frame.__init__(self, root, border=5) - label = Tkinter.Label(self, text=u"Unexpected error:", + label = Tkinter.Label(self, text="Unexpected error:", anchor=Tkconstants.W, justify=Tkconstants.LEFT) label.pack(fill=Tkconstants.X, expand=0) self.text = Tkinter.Text(self) @@ -1781,16 +1779,16 @@ def gui_main(): for key in keys: while True: keycount += 1 - outfile = os.path.join(progpath,u"kindlekey{0:d}.k4i".format(keycount)) + outfile = os.path.join(progpath,"kindlekey{0:d}.k4i".format(keycount)) if not os.path.exists(outfile): break with file(outfile, 'w') as keyfileout: keyfileout.write(json.dumps(key)) success = True - tkMessageBox.showinfo(progname, u"Key successfully retrieved to {0}".format(outfile)) + tkMessageBox.showinfo(progname, "Key successfully retrieved to {0}".format(outfile)) except DrmException as e: - tkMessageBox.showerror(progname, u"Error: {0}".format(str(e))) + tkMessageBox.showerror(progname, "Error: {0}".format(str(e))) except Exception: root.wm_state('normal') root.title(progname) diff --git a/DeDRM_plugin/kindlepid.py b/DeDRM_plugin/kindlepid.py index 069fdc0..b021e97 100644 --- a/DeDRM_plugin/kindlepid.py +++ b/DeDRM_plugin/kindlepid.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python3 # -*- coding: utf-8 -*- # Mobipocket PID calculator v0.4 for Amazon Kindle. @@ -10,7 +10,7 @@ # 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 +# 1.0 Python 3 for calibre 5.0 from __future__ import print_function import sys @@ -67,7 +67,7 @@ def unicode_argv(): 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"] + return ["kindlepid.py"] else: argvencoding = sys.stdin.encoding if argvencoding == None: @@ -111,28 +111,28 @@ def pidFromSerial(s, l): return pid def cli_main(): - print(u"Mobipocket PID calculator for Amazon Kindle. Copyright © 2007, 2009 Igor Skochinsky") + print("Mobipocket PID calculator for Amazon Kindle. Copyright © 2007, 2009 Igor Skochinsky") argv=unicode_argv() if len(argv)==2: serial = argv[1] else: - print(u"Usage: kindlepid.py /") + print("Usage: kindlepid.py /") return 1 if len(serial)==16: if serial.startswith("B") or serial.startswith("9"): - print(u"Kindle serial number detected") + print("Kindle serial number detected") else: - print(u"Warning: unrecognized serial number. Please recheck input.") + print("Warning: unrecognized serial number. Please recheck input.") return 1 pid = pidFromSerial(serial.encode("utf-8"),7)+'*' - print(u"Mobipocket PID for Kindle serial#{0} is {1}".format(serial,checksumPid(pid))) + print("Mobipocket PID for Kindle serial#{0} is {1}".format(serial,checksumPid(pid))) return 0 elif len(serial)==40: - print(u"iPhone serial number (UDID) detected") + print("iPhone serial number (UDID) detected") pid = pidFromSerial(serial.encode("utf-8"),8) - print(u"Mobipocket PID for iPhone serial#{0} is {1}".format(serial,checksumPid(pid))) + print("Mobipocket PID for iPhone serial#{0} is {1}".format(serial,checksumPid(pid))) return 0 - print(u"Warning: unrecognized serial number. Please recheck input.") + print("Warning: unrecognized serial number. Please recheck input.") return 1 diff --git a/DeDRM_plugin/mobidedrm.py b/DeDRM_plugin/mobidedrm.py index cfd8a81..05c7b07 100644 --- a/DeDRM_plugin/mobidedrm.py +++ b/DeDRM_plugin/mobidedrm.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # -*- coding: utf-8 -*- # mobidedrm.py @@ -7,7 +7,7 @@ from __future__ import print_function __license__ = 'GPL v3' -__version__ = u"1.00" +__version__ = "1.00" # This is a python script. You need a Python interpreter to run it. # For example, ActiveState Python, which exists for windows. @@ -82,7 +82,7 @@ import binascii try: from alfcrypto import Pukall_Cipher except: - print(u"AlfCrypto not found. Using python PC1 implementation.") + print("AlfCrypto not found. Using python PC1 implementation.") # Wrap a stream so that output gets flushed immediately # and also make sure that any unicode strings get @@ -135,7 +135,7 @@ def unicode_argv(): 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"] + return ["mobidedrm.py"] else: argvencoding = sys.stdin.encoding if argvencoding == None: @@ -166,7 +166,7 @@ def PC1(key, src, decryption=True): sum2 = 0; keyXorVal = 0; if len(key)!=16: - DrmException (u"PC1: Bad key length") + DrmException ("PC1: Bad key length") wkey = [] for i in range(8): wkey.append(key[i*2]<<8 | key[i*2+1]) @@ -246,19 +246,19 @@ class MobiBook: pass def __init__(self, infile): - print(u"MobiDeDrm v{0:s}.\nCopyright © 2008-2017 The Dark Reverser, Apprentice Harper et al.".format(__version__)) + print("MobiDeDrm v{0:s}.\nCopyright © 2008-2017 The Dark Reverser, Apprentice Harper et al.".format(__version__)) try: from alfcrypto import Pukall_Cipher except: - print(u"AlfCrypto not found. Using python PC1 implementation.") + print("AlfCrypto not found. Using python PC1 implementation.") # initial sanity check on file self.data_file = open(infile, 'rb').read() self.mobi_data = '' self.header = self.data_file[0:78] if self.header[0x3C:0x3C+8] != b'BOOKMOBI' and self.header[0x3C:0x3C+8] != b'TEXtREAd': - raise DrmException(u"Invalid file format") + raise DrmException("Invalid file format") self.magic = self.header[0x3C:0x3C+8] self.crypto_type = -1 @@ -284,16 +284,16 @@ class MobiBook: self.mobi_version = -1 if self.magic == 'TEXtREAd': - print(u"PalmDoc format book detected.") + print("PalmDoc format book detected.") return self.mobi_length, = struct.unpack('>L',self.sect[0x14:0x18]) self.mobi_codepage, = struct.unpack('>L',self.sect[0x1c:0x20]) self.mobi_version, = struct.unpack('>L',self.sect[0x68:0x6C]) - #print u"MOBI header version {0:d}, header length {1:d}".format(self.mobi_version, self.mobi_length) + #print "MOBI header version {0:d}, header length {1:d}".format(self.mobi_version, self.mobi_length) if (self.mobi_length >= 0xE4) and (self.mobi_version >= 5): self.extra_data_flags, = struct.unpack('>H', self.sect[0xF2:0xF4]) - #print u"Extra Data Flags: {0:d}".format(self.extra_data_flags) + #print "Extra Data Flags: {0:d}".format(self.extra_data_flags) if (self.compression != 17480): # multibyte utf8 data is included in the encryption for PalmDoc compression # so clear that byte so that we leave it to be decrypted. @@ -322,7 +322,7 @@ class MobiBook: # print type, size, content, content.encode('hex') pos += size except Exception as e: - print(u"Cannot set meta_array: Error: {:s}".format(e.args[0])) + print("Cannot set meta_array: Error: {:s}".format(e.args[0])) def getBookTitle(self): codec_map = { @@ -411,51 +411,51 @@ class MobiBook: def getBookType(self): if self.print_replica: - return u"Print Replica" + return "Print Replica" if self.mobi_version >= 8: - return u"Kindle Format 8" + return "Kindle Format 8" if self.mobi_version >= 0: - return u"Mobipocket {0:d}".format(self.mobi_version) - return u"PalmDoc" + return "Mobipocket {0:d}".format(self.mobi_version) + return "PalmDoc" def getBookExtension(self): if self.print_replica: - return u".azw4" + return ".azw4" if self.mobi_version >= 8: - return u".azw3" - return u".mobi" + return ".azw3" + return ".mobi" def processBook(self, pidlist): crypto_type, = struct.unpack('>H', self.sect[0xC:0xC+2]) - print(u"Crypto Type is: {0:d}".format(crypto_type)) + print("Crypto Type is: {0:d}".format(crypto_type)) self.crypto_type = crypto_type if crypto_type == 0: - print(u"This book is not encrypted.") + print("This book is not encrypted.") # we must still check for Print Replica self.print_replica = (self.loadSection(1)[0:4] == '%MOP') self.mobi_data = self.data_file return if crypto_type != 2 and crypto_type != 1: - raise DrmException(u"Cannot decode unknown Mobipocket encryption type {0:d}".format(crypto_type)) + raise DrmException("Cannot decode unknown Mobipocket encryption type {0:d}".format(crypto_type)) if 406 in self.meta_array: data406 = self.meta_array[406] val406, = struct.unpack('>Q',data406) if val406 != 0: - raise DrmException(u"Cannot decode library or rented ebooks.") + raise DrmException("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: - print(u"Warning: PID {0} has incorrect checksum, should have been {1}".format(pid,checksumPid(pid[0:-2]))) + print("Warning: PID {0} has incorrect checksum, should have been {1}".format(pid,checksumPid(pid[0:-2]))) goodpids.append(pid[0:-2]) elif len(pid)==8: goodpids.append(pid) else: - print(u"Warning: PID {0} has wrong number of digits".format(pid)) + print("Warning: PID {0} has wrong number of digits".format(pid)) - # print(u"======= DEBUG good pids = ", goodpids) + # print("======= DEBUG good pids = ", goodpids) if self.crypto_type == 1: t1_keyvec = 'QDCVEPMU675RUBSZ' @@ -471,32 +471,32 @@ class MobiBook: # calculate the keys drm_ptr, drm_count, drm_size, drm_flags = struct.unpack('>LLLL', self.sect[0xA8:0xA8+16]) if drm_count == 0: - raise DrmException(u"Encryption not initialised. Must be opened with Mobipocket Reader first.") + raise DrmException("Encryption not initialised. Must be opened with Mobipocket Reader first.") found_key, pid = self.parseDRM(self.sect[drm_ptr:drm_ptr+drm_size], drm_count, goodpids) if not found_key: - raise DrmException(u"No key found in {0:d} keys tried.".format(len(goodpids))) + raise DrmException("No key found in {0:d} keys tried.".format(len(goodpids))) # kill the drm keys self.patchSection(0, b'\0' * drm_size, drm_ptr) # kill the drm pointers self.patchSection(0, b'\xff' * 4 + b'\0' * 12, 0xA8) if pid=='00000000': - print(u"File has default encryption, no specific key needed.") + print("File has default encryption, no specific key needed.") else: - print(u"File is encoded with PID {0}.".format(checksumPid(pid))) + print("File is encoded with PID {0}.".format(checksumPid(pid))) # clear the crypto type self.patchSection(0, b'\0' * 2, 0xC) # decrypt sections - print(u"Decrypting. Please wait . . .", end=' ') + print("Decrypting. Please wait . . .", end=' ') mobidataList = [] mobidataList.append(self.data_file[:self.sections[1][0]]) 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: - print(u".", end=' ') + print(".", end=' ') # print "record %d, extra_size %d" %(i,extra_size) decoded_data = PC1(found_key, data[0:len(data) - extra_size]) if i==1: @@ -507,12 +507,12 @@ class MobiBook: if self.num_sections > self.records+1: mobidataList.append(self.data_file[self.sections[self.records+1][0]:]) self.mobi_data = b''.join(mobidataList) - print(u"done") + print("done") return def getUnencryptedBook(infile,pidlist): if not os.path.isfile(infile): - raise DrmException(u"Input File Not Found.") + raise DrmException("Input File Not Found.") book = MobiBook(infile) book.processBook(pidlist) return book.mobi_data @@ -522,10 +522,10 @@ def cli_main(): argv=unicode_argv() progname = os.path.basename(argv[0]) if len(argv)<3 or len(argv)>4: - print(u"MobiDeDrm v{0:s}.\nCopyright © 2008-2017 The Dark Reverser, Apprentice Harper et al.".format(__version__)) - print(u"Removes protection from Kindle/Mobipocket, Kindle/KF8 and Kindle/Print Replica ebooks") - print(u"Usage:") - print(u" {0} []".format(progname)) + print("MobiDeDrm v{0:s}.\nCopyright © 2008-2017 The Dark Reverser, Apprentice Harper et al.".format(__version__)) + print("Removes protection from Kindle/Mobipocket, Kindle/KF8 and Kindle/Print Replica ebooks") + print("Usage:") + print(" {0} []".format(progname)) return 1 else: infile = argv[1] @@ -538,7 +538,7 @@ def cli_main(): stripped_file = getUnencryptedBook(infile, pidlist) open(outfile, 'wb').write(stripped_file) except DrmException as e: - print(u"MobiDeDRM v{0} Error: {1:s}".format(__version__,e.args[0])) + print("MobiDeDRM v{0} Error: {1:s}".format(__version__,e.args[0])) return 1 return 0 diff --git a/DeDRM_plugin/prefs.py b/DeDRM_plugin/prefs.py index cdbb189..ee16a30 100644 --- a/DeDRM_plugin/prefs.py +++ b/DeDRM_plugin/prefs.py @@ -1,7 +1,7 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- # vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai -from __future__ import with_statement __license__ = 'GPL v3' # Standard Python modules. @@ -15,7 +15,7 @@ from calibre.constants import iswindows, isosx class DeDRM_Prefs(): def __init__(self): - JSON_PATH = os.path.join(u"plugins", PLUGIN_NAME.strip().lower().replace(' ', '_') + '.json') + JSON_PATH = os.path.join("plugins", PLUGIN_NAME.strip().lower().replace(' ', '_') + '.json') self.dedrmprefs = JSONConfig(JSON_PATH) self.dedrmprefs.defaults['configured'] = False @@ -98,7 +98,7 @@ def convertprefs(always = False): try: name, ccn = keystring.split(',') # Generate Barnes & Noble EPUB user key from name and credit card number. - keyname = u"{0}_{1}".format(name.strip(),ccn.strip()[-4:]) + keyname = "{0}_{1}".format(name.strip(),ccn.strip()[-4:]) keyvalue = generate_key(name, ccn) userkeys.append([keyname,keyvalue]) except Exception as e: @@ -115,7 +115,7 @@ def convertprefs(always = False): try: name, cc = keystring.split(',') # Generate eReader user key from name and credit card number. - keyname = u"{0}_{1}".format(name.strip(),cc.strip()[-4:]) + keyname = "{0}_{1}".format(name.strip(),cc.strip()[-4:]) keyvalue = getuser_key(name,cc).encode('hex') userkeys.append([keyname,keyvalue]) except Exception as e: @@ -161,15 +161,15 @@ def convertprefs(always = False): return - print(u"{0} v{1}: Importing configuration data from old DeDRM plugins".format(PLUGIN_NAME, PLUGIN_VERSION)) + print("{0} v{1}: Importing configuration data from old DeDRM plugins".format(PLUGIN_NAME, PLUGIN_VERSION)) IGNOBLEPLUGINNAME = "Ignoble Epub DeDRM" EREADERPLUGINNAME = "eReader PDB 2 PML" OLDKINDLEPLUGINNAME = "K4PC, K4Mac, Kindle Mobi and Topaz DeDRM" # get prefs from older tools - kindleprefs = JSONConfig(os.path.join(u"plugins", u"K4MobiDeDRM")) - ignobleprefs = JSONConfig(os.path.join(u"plugins", u"ignoble_epub_dedrm")) + kindleprefs = JSONConfig(os.path.join("plugins", "K4MobiDeDRM")) + ignobleprefs = JSONConfig(os.path.join("plugins", "ignoble_epub_dedrm")) # Handle the old ignoble plugin's customization string by converting the # old string to stored keys... get that personal data out of plain sight. @@ -177,7 +177,7 @@ def convertprefs(always = False): 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("{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: @@ -185,7 +185,7 @@ def convertprefs(always = False): 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("{0} v{1}: {2:d} Barnes and Noble {3} imported from old Ignoble plugin configuration string".format(PLUGIN_NAME, PLUGIN_VERSION, addedkeycount, "key" if addedkeycount==1 else "keys")) # Make the json write all the prefs to disk dedrmprefs.writeprefs(False) @@ -193,7 +193,7 @@ def convertprefs(always = 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("{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: @@ -201,14 +201,14 @@ def convertprefs(always = False): 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("{0} v{1}: {2:d} eReader {3} imported from old eReader plugin configuration string".format(PLUGIN_NAME, PLUGIN_VERSION, addedkeycount, "key" if addedkeycount==1 else "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("{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) @@ -218,7 +218,7 @@ def convertprefs(always = False): 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("{0} v{1}: {2:d} {3} and {4:d} {5} imported from old Kindle plugin configuration string.".format(PLUGIN_NAME, PLUGIN_VERSION, addedpidcount, "PID" if addedpidcount==1 else "PIDs", addedserialcount, "serial number" if addedserialcount==1 else "serial numbers")) # Make the json write all the prefs to disk dedrmprefs.writeprefs(False) @@ -234,7 +234,7 @@ def convertprefs(always = 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("{0} v{1}: {2:d} Barnes and Noble {3} imported from config folder.".format(PLUGIN_NAME, PLUGIN_VERSION, addedkeycount, "key file" if addedkeycount==1 else "key files")) # Make the json write all the prefs to disk dedrmprefs.writeprefs(False) @@ -247,7 +247,7 @@ def convertprefs(always = 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("{0} v{1}: {2:d} Adobe Adept {3} imported from config folder.".format(PLUGIN_NAME, PLUGIN_VERSION, addedkeycount, "keyfile" if addedkeycount==1 else "keyfiles")) # Make the json write all the prefs to disk dedrmprefs.writeprefs(False) @@ -260,7 +260,7 @@ def convertprefs(always = 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("{0} v{1}: {2:d} Barnes and Noble {3} imported from Ignoble plugin preferences.".format(PLUGIN_NAME, PLUGIN_VERSION, addedkeycount, "key" if addedkeycount==1 else "keys")) # Make the json write all the prefs to disk dedrmprefs.writeprefs(False) @@ -277,19 +277,19 @@ def convertprefs(always = 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("{0} v{1}: {2:d} {3} imported from Kindle plugin preferences".format(PLUGIN_NAME, PLUGIN_VERSION, addedpidcount, "PID" if addedpidcount==1 else "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("{0} v{1}: {2:d} {3} imported from Kindle plugin preferences".format(PLUGIN_NAME, PLUGIN_VERSION, addedserialcount, "serial number" if addedserialcount==1 else "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("{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("{0} v{1}: Finished setting up configuration data.".format(PLUGIN_NAME, PLUGIN_VERSION)) diff --git a/DeDRM_plugin/scriptinterface.py b/DeDRM_plugin/scriptinterface.py index 25f23ad..2b70266 100644 --- a/DeDRM_plugin/scriptinterface.py +++ b/DeDRM_plugin/scriptinterface.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # -*- coding: utf-8 -*- # vim:ts=4:sw=4:softtabstop=4:smarttab:expandtab diff --git a/DeDRM_plugin/simpleprefs.py b/DeDRM_plugin/simpleprefs.py index 5524663..34aa6dc 100644 --- a/DeDRM_plugin/simpleprefs.py +++ b/DeDRM_plugin/simpleprefs.py @@ -1,4 +1,5 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- # vim:ts=4:sw=4:softtabstop=4:smarttab:expandtab import sys diff --git a/DeDRM_plugin/topazextract.py b/DeDRM_plugin/topazextract.py index ca0101d..e1b97e0 100644 --- a/DeDRM_plugin/topazextract.py +++ b/DeDRM_plugin/topazextract.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # -*- coding: utf-8 -*- # topazextract.py @@ -68,7 +68,7 @@ def unicode_argv(): 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"] + return ["mobidedrm.py"] else: argvencoding = sys.stdin.encoding if argvencoding == None: @@ -170,11 +170,11 @@ def decryptDkeyRecord(data,PID): record = decryptRecord(data,PID) fields = unpack('3sB8sB8s3s',record) if fields[0] != 'PID' or fields[5] != 'pid' : - raise DrmException(u"Didn't find PID magic numbers in record") + raise DrmException("Didn't find PID magic numbers in record") elif fields[1] != 8 or fields[3] != 8 : - raise DrmException(u"Record didn't contain correct length fields") + raise DrmException("Record didn't contain correct length fields") elif fields[2] != PID : - raise DrmException(u"Record didn't contain PID") + raise DrmException("Record didn't contain PID") return fields[4] # Decrypt all dkey records (contain the book PID) @@ -191,7 +191,7 @@ def decryptDkeyRecords(data,PID): pass data = data[1+length:] if len(records) == 0: - raise DrmException(u"BookKey Not Found") + raise DrmException("BookKey Not Found") return records @@ -206,7 +206,7 @@ class TopazBook: self.bookKey = None magic = unpack('4s',self.fo.read(4))[0] if magic != 'TPZ0': - raise DrmException(u"Parse Error : Invalid Header, not a Topaz file") + raise DrmException("Parse Error : Invalid Header, not a Topaz file") self.parseTopazHeaders() self.parseMetadata() @@ -224,7 +224,7 @@ class TopazBook: # Read and parse one header record at the current book file position and return the associated data # [[offset,decompressedLength,compressedLength],...] if ord(self.fo.read(1)) != 0x63: - raise DrmException(u"Parse Error : Invalid Header") + raise DrmException("Parse Error : Invalid Header") tag = bookReadString(self.fo) record = bookReadHeaderRecordData() return [tag,record] @@ -235,7 +235,7 @@ class TopazBook: if debug: print(result[0], ": ", result[1]) self.bookHeaderRecords[result[0]] = result[1] if ord(self.fo.read(1)) != 0x64 : - raise DrmException(u"Parse Error : Invalid Header") + raise DrmException("Parse Error : Invalid Header") self.bookPayloadOffset = self.fo.tell() def parseMetadata(self): @@ -243,7 +243,7 @@ class TopazBook: self.fo.seek(self.bookPayloadOffset + self.bookHeaderRecords['metadata'][0][0]) tag = bookReadString(self.fo) if tag != 'metadata' : - raise DrmException(u"Parse Error : Record Names Don't Match") + raise DrmException("Parse Error : Record Names Don't Match") flags = ord(self.fo.read(1)) nbRecords = ord(self.fo.read(1)) if debug: print("Metadata Records: %d" % nbRecords) @@ -321,11 +321,11 @@ class TopazBook: try: keydata = self.getBookPayloadRecord('dkey', 0) 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") + print("no dkey record found, book may not be encrypted") + print("attempting to extrct files without a book key") self.createBookDirectory() self.extractFiles() - print(u"Successfully Extracted Topaz contents") + print("Successfully Extracted Topaz contents") if inCalibre: from calibre_plugins.dedrm import genbook else: @@ -333,7 +333,7 @@ class TopazBook: rv = genbook.generateBook(self.outdir, raw, fixedimage) if rv == 0: - print(u"Book Successfully generated.") + print("Book Successfully generated.") return rv # try each pid to decode the file @@ -341,7 +341,7 @@ class TopazBook: for pid in pidlst: # use 8 digit pids here pid = pid[0:8] - print(u"Trying: {0}".format(pid)) + print("Trying: {0}".format(pid)) bookKeys = [] data = keydata try: @@ -350,16 +350,16 @@ class TopazBook: pass else: bookKey = bookKeys[0] - print(u"Book Key Found! ({0})".format(bookKey.encode('hex'))) + print("Book Key Found! ({0})".format(bookKey.encode('hex'))) break if not bookKey: - raise DrmException(u"No key found in {0:d} keys tried. Read the FAQs at Harper's repository: https://github.com/apprenticeharper/DeDRM_tools/blob/master/FAQs.md".format(len(pidlst))) + raise DrmException("No key found in {0:d} keys tried. Read the FAQs at Harper's repository: https://github.com/apprenticeharper/DeDRM_tools/blob/master/FAQs.md".format(len(pidlst))) self.setBookKey(bookKey) self.createBookDirectory() self.extractFiles() - print(u"Successfully Extracted Topaz contents") + print("Successfully Extracted Topaz contents") if inCalibre: from calibre_plugins.dedrm import genbook else: @@ -367,7 +367,7 @@ class TopazBook: rv = genbook.generateBook(self.outdir, raw, fixedimage) if rv == 0: - print(u"Book Successfully generated") + print("Book Successfully generated") return rv def createBookDirectory(self): @@ -375,16 +375,16 @@ class TopazBook: # create output directory structure if not os.path.exists(outdir): os.makedirs(outdir) - destdir = os.path.join(outdir,u"img") + destdir = os.path.join(outdir,"img") if not os.path.exists(destdir): os.makedirs(destdir) - destdir = os.path.join(outdir,u"color_img") + destdir = os.path.join(outdir,"color_img") if not os.path.exists(destdir): os.makedirs(destdir) - destdir = os.path.join(outdir,u"page") + destdir = os.path.join(outdir,"page") if not os.path.exists(destdir): os.makedirs(destdir) - destdir = os.path.join(outdir,u"glyphs") + destdir = os.path.join(outdir,"glyphs") if not os.path.exists(destdir): os.makedirs(destdir) @@ -393,49 +393,49 @@ class TopazBook: for headerRecord in self.bookHeaderRecords: name = headerRecord if name != 'dkey': - ext = u".dat" - if name == 'img': ext = u".jpg" - if name == 'color' : ext = u".jpg" - print(u"Processing Section: {0}\n. . .".format(name), end=' ') + ext = ".dat" + if name == 'img': ext = ".jpg" + if name == 'color' : ext = ".jpg" + print("Processing Section: {0}\n. . .".format(name), end=' ') for index in range (0,len(self.bookHeaderRecords[name])) : - fname = u"{0}{1:04d}{2}".format(name,index,ext) + fname = "{0}{1:04d}{2}".format(name,index,ext) destdir = outdir if name == 'img': - destdir = os.path.join(outdir,u"img") + destdir = os.path.join(outdir,"img") if name == 'color': - destdir = os.path.join(outdir,u"color_img") + destdir = os.path.join(outdir,"color_img") if name == 'page': - destdir = os.path.join(outdir,u"page") + destdir = os.path.join(outdir,"page") if name == 'glyphs': - destdir = os.path.join(outdir,u"glyphs") + destdir = os.path.join(outdir,"glyphs") outputFile = os.path.join(destdir,fname) - print(u".", end=' ') + print(".", end=' ') record = self.getBookPayloadRecord(name,index) if record != '': open(outputFile, 'wb').write(record) - print(u" ") + print(" ") def getFile(self, zipname): htmlzip = zipfile.ZipFile(zipname,'w',zipfile.ZIP_DEFLATED, False) - htmlzip.write(os.path.join(self.outdir,u"book.html"),u"book.html") - htmlzip.write(os.path.join(self.outdir,u"book.opf"),u"book.opf") - if os.path.isfile(os.path.join(self.outdir,u"cover.jpg")): - htmlzip.write(os.path.join(self.outdir,u"cover.jpg"),u"cover.jpg") - htmlzip.write(os.path.join(self.outdir,u"style.css"),u"style.css") - zipUpDir(htmlzip, self.outdir, u"img") + htmlzip.write(os.path.join(self.outdir,"book.html"),"book.html") + htmlzip.write(os.path.join(self.outdir,"book.opf"),"book.opf") + if os.path.isfile(os.path.join(self.outdir,"cover.jpg")): + htmlzip.write(os.path.join(self.outdir,"cover.jpg"),"cover.jpg") + htmlzip.write(os.path.join(self.outdir,"style.css"),"style.css") + zipUpDir(htmlzip, self.outdir, "img") htmlzip.close() def getBookType(self): - return u"Topaz" + return "Topaz" def getBookExtension(self): - return u".htmlz" + return ".htmlz" def getSVGZip(self, zipname): svgzip = zipfile.ZipFile(zipname,'w',zipfile.ZIP_DEFLATED, False) - svgzip.write(os.path.join(self.outdir,u"index_svg.xhtml"),u"index_svg.xhtml") - zipUpDir(svgzip, self.outdir, u"svg") - zipUpDir(svgzip, self.outdir, u"img") + svgzip.write(os.path.join(self.outdir,"index_svg.xhtml"),"index_svg.xhtml") + zipUpDir(svgzip, self.outdir, "svg") + zipUpDir(svgzip, self.outdir, "img") svgzip.close() def cleanup(self): @@ -443,20 +443,20 @@ class TopazBook: shutil.rmtree(self.outdir, True) def usage(progname): - print(u"Removes DRM protection from Topaz ebooks and extracts the contents") - print(u"Usage:") - print(u" {0} [-k ] [-p ] [-s ] ".format(progname)) + print("Removes DRM protection from Topaz ebooks and extracts the contents") + print("Usage:") + print(" {0} [-k ] [-p ] [-s ] ".format(progname)) # Main def cli_main(): argv=unicode_argv() progname = os.path.basename(argv[0]) - print(u"TopazExtract v{0}.".format(__version__)) + print("TopazExtract v{0}.".format(__version__)) try: opts, args = getopt.getopt(argv[1:], "k:p:s:x") except getopt.GetoptError as err: - print(u"Error in options or arguments: {0}".format(err.args[0])) + print("Error in options or arguments: {0}".format(err.args[0])) usage(progname) return 1 if len(args)<2: @@ -466,11 +466,11 @@ def cli_main(): infile = args[0] outdir = args[1] if not os.path.isfile(infile): - print(u"Input File {0} Does Not Exist.".format(infile)) + print("Input File {0} Does Not Exist.".format(infile)) return 1 if not os.path.exists(outdir): - print(u"Output Directory {0} Does Not Exist.".format(outdir)) + print("Output Directory {0} Does Not Exist.".format(outdir)) return 1 kDatabaseFiles = [] @@ -495,27 +495,27 @@ def cli_main(): tb = TopazBook(infile) title = tb.getBookTitle() - print(u"Processing Book: {0}".format(title)) + print("Processing Book: {0}".format(title)) md1, md2 = tb.getPIDMetaInfo() pids.extend(kgenpids.getPidList(md1, md2, serials, kDatabaseFiles)) try: - print(u"Decrypting Book") + print("Decrypting Book") tb.processBook(pids) - print(u" Creating HTML ZIP Archive") - zipname = os.path.join(outdir, bookname + u"_nodrm.htmlz") + print(" Creating HTML ZIP Archive") + zipname = os.path.join(outdir, bookname + "_nodrm.htmlz") tb.getFile(zipname) - print(u" Creating SVG ZIP Archive") - zipname = os.path.join(outdir, bookname + u"_SVG.zip") + print(" Creating SVG ZIP Archive") + zipname = os.path.join(outdir, bookname + "_SVG.zip") tb.getSVGZip(zipname) # removing internal temporary directory of pieces tb.cleanup() except DrmException as e: - print(u"Decryption failed\n{0}".format(traceback.format_exc())) + print("Decryption failed\n{0}".format(traceback.format_exc())) try: tb.cleanup() @@ -524,7 +524,7 @@ def cli_main(): return 1 except Exception as e: - print(u"Decryption failed\n{0}".format(traceback.format_exc())) + print("Decryption failed\n{0}".format(traceback.format_exc())) try: tb.cleanup() except: diff --git a/DeDRM_plugin/utilities.py b/DeDRM_plugin/utilities.py index 56c64fd..8472283 100644 --- a/DeDRM_plugin/utilities.py +++ b/DeDRM_plugin/utilities.py @@ -1,8 +1,6 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # -*- coding: utf-8 -*- -from __future__ import with_statement - from calibre_plugins.dedrm.ignoblekeygen import generate_key __license__ = 'GPL v3' diff --git a/DeDRM_plugin/wineutils.py b/DeDRM_plugin/wineutils.py index c5d4dee..390e233 100644 --- a/DeDRM_plugin/wineutils.py +++ b/DeDRM_plugin/wineutils.py @@ -1,9 +1,6 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # -*- coding: utf-8 -*- -from __future__ import with_statement -from __future__ import print_function - __license__ = 'GPL v3' # Standard Python modules. @@ -17,13 +14,13 @@ def WineGetKeys(scriptpath, extension, wineprefix=""): import subasyncio from subasyncio import Process - if extension == u".k4i": + if extension == ".k4i": import json basepath, script = os.path.split(scriptpath) - print(u"{0} v{1}: Running {2} under Wine".format(PLUGIN_NAME, PLUGIN_VERSION, script)) + print("{0} v{1}: Running {2} under Wine".format(PLUGIN_NAME, PLUGIN_VERSION, script)) - outdirpath = os.path.join(basepath, u"winekeysdir") + outdirpath = os.path.join(basepath, "winekeysdir") if not os.path.exists(outdirpath): os.makedirs(outdirpath) @@ -31,29 +28,29 @@ def WineGetKeys(scriptpath, extension, wineprefix=""): wineprefix = os.path.abspath(os.path.expanduser(os.path.expandvars(wineprefix))) if wineprefix != "" and os.path.exists(wineprefix): - cmdline = u"WINEPREFIX=\"{2}\" wine python.exe \"{0}\" \"{1}\"".format(scriptpath,outdirpath,wineprefix) + cmdline = "WINEPREFIX=\"{2}\" wine python.exe \"{0}\" \"{1}\"".format(scriptpath,outdirpath,wineprefix) else: - cmdline = u"wine python.exe \"{0}\" \"{1}\"".format(scriptpath,outdirpath) - print(u"{0} v{1}: Command line: '{2}'".format(PLUGIN_NAME, PLUGIN_VERSION, cmdline)) + cmdline = "wine python.exe \"{0}\" \"{1}\"".format(scriptpath,outdirpath) + print("{0} v{1}: Command line: '{2}'".format(PLUGIN_NAME, PLUGIN_VERSION, cmdline)) try: 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 as e: - print(u"{0} v{1}: Wine subprocess call error: {2}".format(PLUGIN_NAME, PLUGIN_VERSION, e.args[0])) + print("{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 = "WINEPREFIX=\"{2}\" wine C:\\Python27\\python.exe \"{0}\" \"{1}\"".format(scriptpath,outdirpath,wineprefix) else: - cmdline = u"wine C:\\Python27\\python.exe \"{0}\" \"{1}\"".format(scriptpath,outdirpath) - print(u"{0} v{1}: Command line: “{2}”".format(PLUGIN_NAME, PLUGIN_VERSION, cmdline)) + cmdline = "wine C:\\Python27\\python.exe \"{0}\" \"{1}\"".format(scriptpath,outdirpath) + print("{0} v{1}: Command line: “{2}”".format(PLUGIN_NAME, PLUGIN_VERSION, cmdline)) try: 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 as e: - print(u"{0} v{1}: Wine subprocess call error: {2}".format(PLUGIN_NAME, PLUGIN_VERSION, e.args[0])) + print("{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 winekeys = [] @@ -63,14 +60,14 @@ def WineGetKeys(scriptpath, extension, wineprefix=""): try: fpath = os.path.join(outdirpath, filename) with open(fpath, 'rb') as keyfile: - if extension == u".k4i": + if extension == ".k4i": new_key_value = json.loads(keyfile.read()) else: new_key_value = keyfile.read() winekeys.append(new_key_value) except: - print(u"{0} v{1}: Error loading file {2}".format(PLUGIN_NAME, PLUGIN_VERSION, filename)) + print("{0} v{1}: Error loading file {2}".format(PLUGIN_NAME, PLUGIN_VERSION, filename)) traceback.print_exc() os.remove(fpath) - print(u"{0} v{1}: Found and decrypted {2} {3}".format(PLUGIN_NAME, PLUGIN_VERSION, len(winekeys), u"key file" if len(winekeys) == 1 else u"key files")) + print("{0} v{1}: Found and decrypted {2} {3}".format(PLUGIN_NAME, PLUGIN_VERSION, len(winekeys), "key file" if len(winekeys) == 1 else "key files")) return winekeys diff --git a/DeDRM_plugin/zipfilerugged.py b/DeDRM_plugin/zipfilerugged.py index d20dab6..25aed8a 100644 --- a/DeDRM_plugin/zipfilerugged.py +++ b/DeDRM_plugin/zipfilerugged.py @@ -1,3 +1,6 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + """ Read and write ZIP files. """ @@ -824,8 +827,8 @@ class ZipFile: 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"') + if mode not in ("r", "", "rU"): + raise RuntimeError('open() requires mode "r", "", or "rU"') if not self.fp: raise RuntimeError( "Attempt to read ZIP archive that was already closed") diff --git a/DeDRM_plugin/zipfix.py b/DeDRM_plugin/zipfix.py index 190cf44..9745495 100644 --- a/DeDRM_plugin/zipfix.py +++ b/DeDRM_plugin/zipfix.py @@ -1,8 +1,8 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # -*- coding: utf-8 -*- # zipfix.py -# Copyright © 2010-2020 by some_updates, DiapDealer and Apprentice Alf +# Copyright © 2010-2020 by Apprentice Harper et al. # Released under the terms of the GNU General Public Licence, version 3 # @@ -10,7 +10,7 @@ # 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 +# 2.0 - Python 3 for calibre 5.0 """ Re-write zip (or ePub) fixing problems with file names (and mimetype entry). diff --git a/Obok_plugin/action.py b/Obok_plugin/action.py index 802a833..e94204f 100644 --- a/Obok_plugin/action.py +++ b/Obok_plugin/action.py @@ -1,8 +1,8 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- # vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai -from __future__ import (unicode_literals, division, absolute_import, - print_function) -__license__ = 'GPL v3' +_license__ = 'GPL v3' __docformat__ = 'restructuredtext en' diff --git a/Obok_plugin/common_utils.py b/Obok_plugin/common_utils.py index babad1c..21b7f19 100644 --- a/Obok_plugin/common_utils.py +++ b/Obok_plugin/common_utils.py @@ -1,7 +1,6 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- # vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai -from __future__ import (unicode_literals, division, absolute_import, - print_function) __license__ = 'GPL v3' __copyright__ = '2012, David Forrester ' @@ -9,13 +8,7 @@ __docformat__ = 'restructuredtext en' import os, time, re, sys from datetime import datetime -try: - from PyQt5.Qt import (Qt, QIcon, QPixmap, QLabel, QDialog, QHBoxLayout, QProgressBar, - QTableWidgetItem, QFont, QLineEdit, QComboBox, - QVBoxLayout, QDialogButtonBox, QStyledItemDelegate, QDateTime, - QRegExpValidator, QRegExp, QDate, QDateEdit) -except ImportError: - from PyQt4.Qt import (Qt, QIcon, QPixmap, QLabel, QDialog, QHBoxLayout, QProgressBar, +from PyQt5.Qt import (Qt, QIcon, QPixmap, QLabel, QDialog, QHBoxLayout, QProgressBar, QTableWidgetItem, QFont, QLineEdit, QComboBox, QVBoxLayout, QDialogButtonBox, QStyledItemDelegate, QDateTime, QRegExpValidator, QRegExp, QDate, QDateEdit) diff --git a/Obok_plugin/config.py b/Obok_plugin/config.py index 522b86a..2830dad 100644 --- a/Obok_plugin/config.py +++ b/Obok_plugin/config.py @@ -1,16 +1,10 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + # vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai -from __future__ import (unicode_literals, division, absolute_import, - print_function) -try: - from PyQt5.Qt import (Qt, QGroupBox, QListWidget, QLineEdit, QDialogButtonBox, QWidget, QLabel, QDialog, QVBoxLayout, QAbstractItemView, QIcon, QHBoxLayout, QComboBox, QListWidgetItem, QFileDialog) -except ImportError: - from PyQt4.Qt import (Qt, QGroupBox, QListWidget, QLineEdit, QDialogButtonBox, QWidget, QLabel, QDialog, QVBoxLayout, QAbstractItemView, QIcon, QHBoxLayout, QComboBox, QListWidgetItem, QFileDialog) - -try: - from PyQt5 import Qt as QtGui -except ImportError: - from PyQt4 import QtGui +from PyQt5.Qt import (Qt, QGroupBox, QListWidget, QLineEdit, QDialogButtonBox, QWidget, QLabel, QDialog, QVBoxLayout, QAbstractItemView, QIcon, QHBoxLayout, QComboBox, QListWidgetItem, QFileDialog) +from PyQt5 import Qt as QtGui from calibre.gui2 import (error_dialog, question_dialog, info_dialog, open_url) from calibre.utils.config import JSONConfig, config_dir @@ -50,25 +44,25 @@ class ConfigWidget(QWidget): self.find_homes.setCurrentIndex(index) self.serials_button = QtGui.QPushButton(self) - self.serials_button.setToolTip(_(u"Click to manage Kobo serial numbers for Kobo ebooks")) - self.serials_button.setText(u"Kobo devices serials") + self.serials_button.setToolTip(_("Click to manage Kobo serial numbers for Kobo ebooks")) + self.serials_button.setText("Kobo devices serials") self.serials_button.clicked.connect(self.edit_serials) layout.addWidget(self.serials_button) self.kobo_directory_button = QtGui.QPushButton(self) - self.kobo_directory_button.setToolTip(_(u"Click to specify the Kobo directory")) - self.kobo_directory_button.setText(u"Kobo directory") + self.kobo_directory_button.setToolTip(_("Click to specify the Kobo directory")) + self.kobo_directory_button.setText("Kobo directory") self.kobo_directory_button.clicked.connect(self.edit_kobo_directory) layout.addWidget(self.kobo_directory_button) def edit_serials(self): - d = ManageKeysDialog(self,u"Kobo device serial number",self.tmpserials, AddSerialDialog) + d = ManageKeysDialog(self,"Kobo device serial number",self.tmpserials, AddSerialDialog) d.exec_() def edit_kobo_directory(self): - tmpkobodirectory = QFileDialog.getExistingDirectory(self, u"Select Kobo directory", self.kobodirectory or "/home", QFileDialog.ShowDirsOnly) + tmpkobodirectory = QFileDialog.getExistingDirectory(self, "Select Kobo directory", self.kobodirectory or "/home", QFileDialog.ShowDirsOnly) if tmpkobodirectory != u"" and tmpkobodirectory is not None: self.kobodirectory = tmpkobodirectory @@ -91,7 +85,7 @@ class ManageKeysDialog(QDialog): self.plugin_keys = plugin_keys self.create_key = create_key self.keyfile_ext = keyfile_ext - self.json_file = (keyfile_ext == u"k4i") + self.json_file = (keyfile_ext == "k4i") self.setWindowTitle("{0} {1}: Manage {2}s".format(PLUGIN_NAME, PLUGIN_VERSION, self.key_type_name)) @@ -99,13 +93,13 @@ class ManageKeysDialog(QDialog): layout = QVBoxLayout(self) self.setLayout(layout) - keys_group_box = QGroupBox(_(u"{0}s".format(self.key_type_name)), self) + keys_group_box = QGroupBox(_("{0}s".format(self.key_type_name)), self) layout.addWidget(keys_group_box) keys_group_box_layout = QHBoxLayout() keys_group_box.setLayout(keys_group_box_layout) self.listy = QListWidget(self) - self.listy.setToolTip(u"{0}s that will be used to decrypt ebooks".format(self.key_type_name)) + self.listy.setToolTip("{0}s that will be used to decrypt ebooks".format(self.key_type_name)) self.listy.setSelectionMode(QAbstractItemView.SingleSelection) self.populate_list() keys_group_box_layout.addWidget(self.listy) @@ -114,12 +108,12 @@ class ManageKeysDialog(QDialog): keys_group_box_layout.addLayout(button_layout) self._add_key_button = QtGui.QToolButton(self) self._add_key_button.setIcon(QIcon(I('plus.png'))) - self._add_key_button.setToolTip(u"Create new {0}".format(self.key_type_name)) + self._add_key_button.setToolTip("Create new {0}".format(self.key_type_name)) self._add_key_button.clicked.connect(self.add_key) button_layout.addWidget(self._add_key_button) self._delete_key_button = QtGui.QToolButton(self) - self._delete_key_button.setToolTip(_(u"Delete highlighted key")) + self._delete_key_button.setToolTip(_("Delete highlighted key")) self._delete_key_button.setIcon(QIcon(I('list_remove.png'))) self._delete_key_button.clicked.connect(self.delete_key) button_layout.addWidget(self._delete_key_button) @@ -155,7 +149,7 @@ class ManageKeysDialog(QDialog): new_key_value = d.key_value if new_key_value in self.plugin_keys: info_dialog(None, "{0} {1}: Duplicate {2}".format(PLUGIN_NAME, PLUGIN_VERSION,self.key_type_name), - u"This {0} is already in the list of {0}s has not been added.".format(self.key_type_name), show=True) + "This {0} is already in the list of {0}s has not been added.".format(self.key_type_name), show=True) return self.plugin_keys.append(d.key_value) @@ -166,7 +160,7 @@ class ManageKeysDialog(QDialog): if not self.listy.currentItem(): return 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} {0}?".format(keyname, self.key_type_name), show_copy_button=False, default_yes=False): + if not question_dialog(self, "{0} {1}: Confirm Delete".format(PLUGIN_NAME, PLUGIN_VERSION), "Do you really want to delete the {1} {0}?".format(keyname, self.key_type_name), show_copy_button=False, default_yes=False): return self.plugin_keys.remove(keyname) @@ -177,7 +171,7 @@ class AddSerialDialog(QDialog): def __init__(self, parent=None,): QDialog.__init__(self, parent) self.parent = parent - self.setWindowTitle(u"{0} {1}: Add New eInk Kobo Serial Number".format(PLUGIN_NAME, PLUGIN_VERSION)) + self.setWindowTitle("{0} {1}: Add New eInk Kobo Serial Number".format(PLUGIN_NAME, PLUGIN_VERSION)) layout = QVBoxLayout(self) self.setLayout(layout) @@ -188,9 +182,9 @@ class AddSerialDialog(QDialog): key_group = QHBoxLayout() data_group_box_layout.addLayout(key_group) - key_group.addWidget(QLabel(u"EInk Kobo Serial Number:", self)) + key_group.addWidget(QLabel("EInk Kobo Serial Number:", self)) self.key_ledit = QLineEdit("", self) - self.key_ledit.setToolTip(u"Enter an eInk Kobo serial number. EInk Kobo serial numbers are 13 characters long and usually start with a 'N'. Kobo Serial Numbers are case-sensitive, so be sure to enter the upper and lower case letters unchanged.") + self.key_ledit.setToolTip("Enter an eInk Kobo serial number. EInk Kobo serial numbers are 13 characters long and usually start with a 'N'. Kobo Serial Numbers are case-sensitive, so be sure to enter the upper and lower case letters unchanged.") key_group.addWidget(self.key_ledit) self.button_box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) @@ -210,9 +204,9 @@ class AddSerialDialog(QDialog): def accept(self): if len(self.key_name) == 0 or self.key_name.isspace(): - errmsg = u"Please enter an eInk Kindle Serial Number or click Cancel in the dialog." + errmsg = "Please enter an eInk Kindle Serial Number or click Cancel in the dialog." return error_dialog(None, "{0} {1}".format(PLUGIN_NAME, PLUGIN_VERSION), errmsg, show=True, show_copy_button=False) if len(self.key_name) != 13: - errmsg = u"EInk Kobo Serial Numbers must be 13 characters long. This is {0:d} characters long.".format(len(self.key_name)) + errmsg = "EInk Kobo Serial Numbers must be 13 characters long. This is {0:d} characters long.".format(len(self.key_name)) return error_dialog(None, "{0} {1}".format(PLUGIN_NAME, PLUGIN_VERSION), errmsg, show=True, show_copy_button=False) QDialog.accept(self) diff --git a/Obok_plugin/obok/obok.py b/Obok_plugin/obok/obok.py index 80bc058..d20f6dc 100644 --- a/Obok_plugin/obok/obok.py +++ b/Obok_plugin/obok/obok.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # -*- coding: utf-8 -*- # Version 4.0.0 September 2020 @@ -156,7 +156,7 @@ from __future__ import print_function __version__ = '4.0.0' -__about__ = u"Obok v{0}\nCopyright © 2012-2020 Physisticated et al.".format(__version__) +__about__ = "Obok v{0}\nCopyright © 2012-2020 Physisticated et al.".format(__version__) import sys import os @@ -176,10 +176,10 @@ import tempfile can_parse_xml = True try: from xml.etree import ElementTree as ET - # print u"using xml.etree for xml parsing" + # print "using xml.etree for xml parsing" except ImportError: can_parse_xml = False - # print u"Cannot find xml.etree, disabling extraction of serial numbers" + # print "Cannot find xml.etree, disabling extraction of serial numbers" # List of all known hash keys KOBO_HASH_KEYS = ['88b3a2e13', 'XzUhGYdFp', 'NoCanLook','QJhwzAtXL'] @@ -279,10 +279,10 @@ class SafeUnbuffered: if self.encoding == None: self.encoding = "utf-8" def write(self, data): - if isinstance(data,bytes): + if isinstance(data,str): data = data.encode(self.encoding,"replace") - self.stream.write(data) - self.stream.flush() + self.stream.buffer.write(data) + self.stream.buffer.flush() def __getattr__(self, attr): return getattr(self.stream, attr) @@ -312,9 +312,9 @@ class KoboLibrary(object): # step 1. check whether this looks like a real device if (device_path): # we got a device path - self.kobodir = os.path.join(device_path, u".kobo") + self.kobodir = os.path.join(device_path, ".kobo") # devices use KoboReader.sqlite - kobodb = os.path.join(self.kobodir, u"KoboReader.sqlite") + kobodb = os.path.join(self.kobodir, "KoboReader.sqlite") if (not(os.path.isfile(kobodb))): # device path seems to be wrong, unset it device_path = u"" @@ -326,22 +326,22 @@ class KoboLibrary(object): if (len(serials) == 0): # we got a device path but no saved serial # try to get the serial from the device - # print u"get_device_settings - device_path = {0}".format(device_path) + # print "get_device_settings - device_path = {0}".format(device_path) # get serial from device_path/.adobe-digital-editions/device.xml if can_parse_xml: devicexml = os.path.join(device_path, '.adobe-digital-editions', 'device.xml') - # print u"trying to load {0}".format(devicexml) + # print "trying to load {0}".format(devicexml) if (os.path.exists(devicexml)): - # print u"trying to parse {0}".format(devicexml) + # print "trying to parse {0}".format(devicexml) xmltree = ET.parse(devicexml) for node in xmltree.iter(): if "deviceSerial" in node.tag: serial = node.text - # print u"found serial {0}".format(serial) + # print "found serial {0}".format(serial) serials.append(serial) break else: - # print u"cannot get serials from device." + # print "cannot get serials from device." device_path = u"" self.kobodir = u"" kobodb = u"" @@ -357,19 +357,19 @@ class KoboLibrary(object): if sys.getwindowsversion().major > 5: if 'LOCALAPPDATA' in os.environ.keys(): # Python 2.x does not return unicode env. Use Python 3.x - self.kobodir = winreg.ExpandEnvironmentStrings(u"%LOCALAPPDATA%") + self.kobodir = winreg.ExpandEnvironmentStrings("%LOCALAPPDATA%") if (self.kobodir == u""): if 'USERPROFILE' in os.environ.keys(): # Python 2.x does not return unicode env. Use Python 3.x - self.kobodir = os.path.join(winreg.ExpandEnvironmentStrings(u"%USERPROFILE%"), u"Local Settings", u"Application Data") - self.kobodir = os.path.join(self.kobodir, u"Kobo", u"Kobo Desktop Edition") + self.kobodir = os.path.join(winreg.ExpandEnvironmentStrings("%USERPROFILE%"), "Local Settings", "Application Data") + self.kobodir = os.path.join(self.kobodir, "Kobo", "Kobo Desktop Edition") elif sys.platform.startswith('darwin'): - self.kobodir = os.path.join(os.environ['HOME'], u"Library", u"Application Support", u"Kobo", u"Kobo Desktop Edition") + self.kobodir = os.path.join(os.environ['HOME'], "Library", "Application Support", "Kobo", "Kobo Desktop Edition") #elif linux_path != None: # Probably Linux, let's get the wine prefix and path to Kobo. - # self.kobodir = os.path.join(linux_path, u"Local Settings", u"Application Data", u"Kobo", u"Kobo Desktop Edition") + # self.kobodir = os.path.join(linux_path, "Local Settings", "Application Data", "Kobo", "Kobo Desktop Edition") # desktop versions use Kobo.sqlite - kobodb = os.path.join(self.kobodir, u"Kobo.sqlite") + kobodb = os.path.join(self.kobodir, "Kobo.sqlite") # check for existence of file if (not(os.path.isfile(kobodb))): # give up here, we haven't found anything useful @@ -377,7 +377,7 @@ class KoboLibrary(object): kobodb = u"" if (self.kobodir != u""): - self.bookdir = os.path.join(self.kobodir, u"kepub") + self.bookdir = os.path.join(self.kobodir, "kepub") # make a copy of the database in a temporary file # so we can ensure it's not using WAL logging which sqlite3 can't do. self.newdb = tempfile.NamedTemporaryFile(mode='wb', delete=False) @@ -437,7 +437,7 @@ class KoboLibrary(object): def __bookfile (self, volumeid): """The filename needed to open a given book.""" - return os.path.join(self.kobodir, u"kepub", volumeid) + return os.path.join(self.kobodir, "kepub", volumeid) def __getmacaddrs (self): """The list of all MAC addresses on this machine.""" @@ -454,7 +454,7 @@ class KoboLibrary(object): output = subprocess.check_output('/sbin/ifconfig -a', shell=True) matches = c.findall(output) for m in matches: - # print u"m:{0}".format(m[0]) + # print "m:{0}".format(m[0]) macaddrs.append(m[0].upper()) else: # probably linux @@ -607,32 +607,32 @@ class KoboFile(object): # assume utf-8 with no BOM textoffset = 0 stride = 1 - print(u"Checking text:{0}:".format(contents[:10])) + print("Checking text:{0}:".format(contents[:10])) # check for byte order mark if contents[:3]==b"\xef\xbb\xbf": # seems to be utf-8 with BOM - print(u"Could be utf-8 with BOM") + print("Could be utf-8 with BOM") textoffset = 3 elif contents[:2]==b"\xfe\xff": # seems to be utf-16BE - print(u"Could be utf-16BE") + print("Could be utf-16BE") textoffset = 3 stride = 2 elif contents[:2]==b"\xff\xfe": # seems to be utf-16LE - print(u"Could be utf-16LE") + print("Could be utf-16LE") textoffset = 2 stride = 2 else: - print(u"Perhaps utf-8 without BOM") + print("Perhaps utf-8 without BOM") # now check that the first few characters are in the ASCII range 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,contents[i])) + print("Bad character at {0}, value {1}".format(i,contents[i])) raise ValueError - print(u"Seems to be good text") + print("Seems to be good text") return True if contents[:5]==b" 5: if 'LOCALAPPDATA' in os.environ.keys(): # Python 2.x does not return unicode env. Use Python 3.x - self.kobodir = winreg.ExpandEnvironmentStrings(u"%LOCALAPPDATA%") + self.kobodir = winreg.ExpandEnvironmentStrings("%LOCALAPPDATA%") if (self.kobodir == u""): if 'USERPROFILE' in os.environ.keys(): # Python 2.x does not return unicode env. Use Python 3.x - self.kobodir = os.path.join(winreg.ExpandEnvironmentStrings(u"%USERPROFILE%"), u"Local Settings", u"Application Data") - self.kobodir = os.path.join(self.kobodir, u"Kobo", u"Kobo Desktop Edition") + self.kobodir = os.path.join(winreg.ExpandEnvironmentStrings("%USERPROFILE%"), "Local Settings", "Application Data") + self.kobodir = os.path.join(self.kobodir, "Kobo", "Kobo Desktop Edition") elif sys.platform.startswith('darwin'): - self.kobodir = os.path.join(os.environ['HOME'], u"Library", u"Application Support", u"Kobo", u"Kobo Desktop Edition") + self.kobodir = os.path.join(os.environ['HOME'], "Library", "Application Support", "Kobo", "Kobo Desktop Edition") #elif linux_path != None: # Probably Linux, let's get the wine prefix and path to Kobo. - # self.kobodir = os.path.join(linux_path, u"Local Settings", u"Application Data", u"Kobo", u"Kobo Desktop Edition") + # self.kobodir = os.path.join(linux_path, "Local Settings", "Application Data", "Kobo", "Kobo Desktop Edition") # desktop versions use Kobo.sqlite - kobodb = os.path.join(self.kobodir, u"Kobo.sqlite") + kobodb = os.path.join(self.kobodir, "Kobo.sqlite") # check for existence of file if (not(os.path.isfile(kobodb))): # give up here, we haven't found anything useful @@ -371,7 +371,7 @@ class KoboLibrary(object): if (self.kobodir != u""): - self.bookdir = os.path.join(self.kobodir, u"kepub") + self.bookdir = os.path.join(self.kobodir, "kepub") # make a copy of the database in a temporary file # so we can ensure it's not using WAL logging which sqlite3 can't do. self.newdb = tempfile.NamedTemporaryFile(mode='wb', delete=False) @@ -431,7 +431,7 @@ class KoboLibrary(object): def __bookfile (self, volumeid): """The filename needed to open a given book.""" - return os.path.join(self.kobodir, u"kepub", volumeid) + return os.path.join(self.kobodir, "kepub", volumeid) def __getmacaddrs (self): """The list of all MAC addresses on this machine.""" @@ -448,7 +448,7 @@ class KoboLibrary(object): output = subprocess.check_output('/sbin/ifconfig -a', shell=True) matches = c.findall(output) for m in matches: - # print u"m:{0}".format(m[0]) + # print "m:{0}".format(m[0]) macaddrs.append(m[0].upper()) elif sys.platform.startswith('linux'): p_out = subprocess.check_output("ip -br link show | awk '{print $3}'", shell=True) @@ -596,32 +596,32 @@ class KoboFile(object): # assume utf-8 with no BOM textoffset = 0 stride = 1 - print(u"Checking text:{0}:".format(contents[:10])) + print("Checking text:{0}:".format(contents[:10])) # check for byte order mark if contents[:3]=="\xef\xbb\xbf": # seems to be utf-8 with BOM - print(u"Could be utf-8 with BOM") + print("Could be utf-8 with BOM") textoffset = 3 elif contents[:2]=="\xfe\xff": # seems to be utf-16BE - print(u"Could be utf-16BE") + print("Could be utf-16BE") textoffset = 3 stride = 2 elif contents[:2]=="\xff\xfe": # seems to be utf-16LE - print(u"Could be utf-16LE") + print("Could be utf-16LE") textoffset = 2 stride = 2 else: - print(u"Perhaps utf-8 without BOM") + print("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: # Non-ascii, so decryption probably failed - print(u"Bad character at {0}, value {1}".format(i,ord(contents[i]))) + print("Bad character at {0}, value {1}".format(i,ord(contents[i]))) raise ValueError - print(u"Seems to be good text") + print("Seems to be good text") return True if contents[:5]=="