]> xmof Git - DeDRM.git/commitdiff
Starting on Version 7.0 using the work done by others. Completely untested. I will...
authorApprentice Harper <apprenticeharper@gmail.com>
Sat, 26 Sep 2020 20:22:47 +0000 (21:22 +0100)
committerApprentice Harper <apprenticeharper@gmail.com>
Sat, 26 Sep 2020 20:22:47 +0000 (21:22 +0100)
THIS IS ON THE MASTER BRANCH. The Master branch will be Python 3.0 from now on. While Python 2.7 support will not be deliberately broken, all efforts should now focus on Python 3.0 compatibility.

I can see a lot of work has been done. There's more to do. I've bumped the version number of everything I came across to the next major number for Python 3.0 compatibility indication.

Thanks everyone. I hope to update here at least once a week until we have a stable 7.0 release for calibre 5.0

40 files changed:
DeDRM_plugin/__init__.py
DeDRM_plugin/adobekey.py
DeDRM_plugin/aescbc.py
DeDRM_plugin/alfcrypto.py
DeDRM_plugin/androidkindlekey.py
DeDRM_plugin/argv_utils.py
DeDRM_plugin/askfolder_ed.py
DeDRM_plugin/config.py
DeDRM_plugin/convert2xml.py
DeDRM_plugin/epubtest.py
DeDRM_plugin/erdr2pml.py
DeDRM_plugin/genbook.py
DeDRM_plugin/ignobleepub.py
DeDRM_plugin/ignoblekey.py
DeDRM_plugin/ignoblekeyfetch.py
DeDRM_plugin/ignoblekeygen.py
DeDRM_plugin/ineptepub.py
DeDRM_plugin/ineptpdf.py
DeDRM_plugin/ion.py
DeDRM_plugin/k4mobidedrm.py
DeDRM_plugin/kfxdedrm.py
DeDRM_plugin/kgenpids.py
DeDRM_plugin/kindlekey.py
DeDRM_plugin/kindlepid.py
DeDRM_plugin/mobidedrm.py
DeDRM_plugin/prefs.py
DeDRM_plugin/scriptinterface.py
DeDRM_plugin/simpleprefs.py
DeDRM_plugin/topazextract.py
DeDRM_plugin/utilities.py
DeDRM_plugin/wineutils.py
DeDRM_plugin/zipfilerugged.py
DeDRM_plugin/zipfix.py
Obok_plugin/action.py
Obok_plugin/common_utils.py
Obok_plugin/config.py
Obok_plugin/obok/obok.py
Obok_plugin/utilities.py
Other_Tools/DRM_Key_Scripts/Barnes_and_Noble_ePubs/ignoblekey.pyw
make_release.py

index d2cbc2bb6c3b0a5139bd28bbb96a66b8ad07f96d..00640d1b53683ff7d07bd3a7f471cd07764c89c0 100644 (file)
@@ -7,7 +7,7 @@ from __future__ import with_statement
 # Copyright © 2008-2020 Apprentice Harper et al.
 
 __license__   = 'GPL v3'
-__version__ = '6.8.0'
+__version__ = '7.0.0'
 __docformat__ = 'restructuredtext en'
 
 
@@ -71,17 +71,19 @@ __docformat__ = 'restructuredtext en'
 #   6.6.3 - More cleanup of kindle book names and start of support for .kinf2018
 #   6.7.0 - Handle new library in calibre.
 #   6.8.0 - Full support for .kinf2018 and new KFX encryption (Kindle for PC/Mac 2.5+)
+#   7.0.0 - Switched to Python 3 for calibre 5.0. Thanks to all who comtibuted
 
 """
 Decrypt DRMed ebooks.
 """
 
 PLUGIN_NAME = u"DeDRM"
-PLUGIN_VERSION_TUPLE = (6, 8, 0)
+PLUGIN_VERSION_TUPLE = (7, 0, 0)
 PLUGIN_VERSION = u".".join([unicode(str(x)) for x in PLUGIN_VERSION_TUPLE])
 # Include an html helpfile in the plugin's zipfile with the following name.
 RESOURCE_NAME = PLUGIN_NAME + '_Help.htm'
 
+import codecs
 import sys, os, re
 import time
 import zipfile
@@ -107,7 +109,7 @@ class SafeUnbuffered:
         if self.encoding == None:
             self.encoding = "utf-8"
     def write(self, data):
-        if isinstance(data,unicode):
+        if isinstance(data,bytes):
             data = data.encode(self.encoding,"replace")
         try:
             self.stream.write(data)
@@ -165,7 +167,7 @@ class DeDRM(FileTypePlugin):
                 else:
                     names = [u"libalfcrypto32.so",u"libalfcrypto64.so",u"kindlekey.py",u"adobekey.py",u"subasyncio.py"]
                 lib_dict = self.load_resources(names)
-                print u"{0} v{1}: Copying needed library files from plugin's zip".format(PLUGIN_NAME, PLUGIN_VERSION)
+                print("{0} v{1}: Copying needed library files from plugin's zip".format(PLUGIN_NAME, PLUGIN_VERSION))
 
                 for entry, data in lib_dict.items():
                     file_path = os.path.join(self.alfdir, entry)
@@ -177,7 +179,7 @@ class DeDRM(FileTypePlugin):
                     try:
                         open(file_path,'wb').write(data)
                     except:
-                        print u"{0} v{1}: Exception when copying needed library files".format(PLUGIN_NAME, PLUGIN_VERSION)
+                        print("{0} v{1}: Exception when copying needed library files".format(PLUGIN_NAME, PLUGIN_VERSION))
                         traceback.print_exc()
                         pass
 
@@ -187,7 +189,7 @@ class DeDRM(FileTypePlugin):
 
                 # mark that this version has been initialized
                 os.mkdir(self.verdir)
-        except Exception, e:
+        except Exception as e:
             traceback.print_exc()
             raise
 
@@ -198,11 +200,11 @@ class DeDRM(FileTypePlugin):
 
         inf = self.temporary_file(u".epub")
         try:
-            print u"{0} v{1}: Verifying zip archive integrity".format(PLUGIN_NAME, PLUGIN_VERSION)
+            print("{0} v{1}: Verifying zip archive integrity".format(PLUGIN_NAME, PLUGIN_VERSION))
             fr = zipfix.fixZip(path_to_ebook, inf.name)
             fr.fix()
-        except Exception, e:
-            print u"{0} v{1}: Error \'{2}\' when checking zip archive".format(PLUGIN_NAME, PLUGIN_VERSION, e.args[0])
+        except Exception as e:
+            print(u"{0} v{1}: Error \'{2}\' when checking zip archive".format(PLUGIN_NAME, PLUGIN_VERSION, e.args[0]))
             raise Exception(e)
 
         # import the decryption keys
@@ -215,19 +217,19 @@ class DeDRM(FileTypePlugin):
 
         #check the book
         if  ignobleepub.ignobleBook(inf.name):
-            print u"{0} v{1}: “{2}” is a secure Barnes & Noble ePub".format(PLUGIN_NAME, PLUGIN_VERSION, os.path.basename(path_to_ebook))
+            print("{0} v{1}: “{2}” is a secure Barnes & Noble ePub".format(PLUGIN_NAME, PLUGIN_VERSION, os.path.basename(path_to_ebook)))
 
             # Attempt to decrypt epub with each encryption key (generated or provided).
             for keyname, userkey in dedrmprefs['bandnkeys'].items():
                 keyname_masked = u"".join((u'X' if (x.isdigit()) else x) for x in keyname)
-                print u"{0} v{1}: Trying Encryption key {2:s}".format(PLUGIN_NAME, PLUGIN_VERSION, keyname_masked)
+                print("{0} v{1}: Trying Encryption key {2:s}".format(PLUGIN_NAME, PLUGIN_VERSION, keyname_masked))
                 of = self.temporary_file(u".epub")
 
                 # Give the user key, ebook and TemporaryPersistent file to the decryption function.
                 try:
                     result = ignobleepub.decryptBook(userkey, inf.name, of.name)
                 except:
-                    print u"{0} v{1}: Exception when trying to decrypt after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION, time.time()-self.starttime)
+                    print("{0} v{1}: Exception when trying to decrypt after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION, time.time()-self.starttime))
                     traceback.print_exc()
                     result = 1
 
@@ -238,10 +240,10 @@ class DeDRM(FileTypePlugin):
                     # Return the modified PersistentTemporary file to calibre.
                     return of.name
 
-                print u"{0} v{1}: Failed to decrypt with key {2:s} after {3:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION,keyname_masked,time.time()-self.starttime)
+                print("{0} v{1}: Failed to decrypt with key {2:s} after {3:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION,keyname_masked,time.time()-self.starttime))
 
             # perhaps we should see if we can get a key from a log file
-            print u"{0} v{1}: Looking for new NOOK Study Keys after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION, time.time()-self.starttime)
+            print("{0} v{1}: Looking for new NOOK Study Keys after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION, time.time()-self.starttime))
 
             # get the default NOOK Study keys
             defaultkeys = []
@@ -258,7 +260,7 @@ class DeDRM(FileTypePlugin):
                     defaultkeys = WineGetKeys(scriptpath, u".b64",dedrmprefs['adobewineprefix'])
 
             except:
-                print u"{0} v{1}: Exception when getting default NOOK Study Key after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION, time.time()-self.starttime)
+                print("{0} v{1}: Exception when getting default NOOK Study Key after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION, time.time()-self.starttime))
                 traceback.print_exc()
 
             newkeys = []
@@ -269,7 +271,7 @@ class DeDRM(FileTypePlugin):
             if len(newkeys) > 0:
                 try:
                     for i,userkey in enumerate(newkeys):
-                        print u"{0} v{1}: Trying a new default key".format(PLUGIN_NAME, PLUGIN_VERSION)
+                        print("{0} v{1}: Trying a new default key".format(PLUGIN_NAME, PLUGIN_VERSION))
 
                         of = self.temporary_file(u".epub")
 
@@ -277,7 +279,7 @@ class DeDRM(FileTypePlugin):
                         try:
                             result = ignobleepub.decryptBook(userkey, inf.name, of.name)
                         except:
-                           print u"{0} v{1}: Exception when trying to decrypt after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION, time.time()-self.starttime)
+                           print("{0} v{1}: Exception when trying to decrypt after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION, time.time()-self.starttime))
                            traceback.print_exc()
                            result = 1
 
@@ -286,59 +288,59 @@ class DeDRM(FileTypePlugin):
                         if result == 0:
                             # Decryption was a success
                             # Store the new successful key in the defaults
-                            print u"{0} v{1}: Saving a new default key".format(PLUGIN_NAME, PLUGIN_VERSION)
+                            print("{0} v{1}: Saving a new default key".format(PLUGIN_NAME, PLUGIN_VERSION))
                             try:
                                 dedrmprefs.addnamedvaluetoprefs('bandnkeys','nook_Study_key',keyvalue)
                                 dedrmprefs.writeprefs()
-                                print u"{0} v{1}: Saved a new default key after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION,time.time()-self.starttime)
+                                print("{0} v{1}: Saved a new default key after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION,time.time()-self.starttime))
                             except:
-                                print u"{0} v{1}: Exception saving a new default key after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION, time.time()-self.starttime)
+                                print("{0} v{1}: Exception saving a new default key after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION, time.time()-self.starttime))
                                 traceback.print_exc()
                             # Return the modified PersistentTemporary file to calibre.
                             return of.name
 
-                        print u"{0} v{1}: Failed to decrypt with new default key after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION,time.time()-self.starttime)
-                except Exception, e:
+                        print(u"{0} v{1}: Failed to decrypt with new default key after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION,time.time()-self.starttime))
+                except Exception as e:
                     pass
 
-            print u"{0} v{1}: Ultimately failed to decrypt after {2:.1f} seconds. Read the FAQs at Harper's repository: https://github.com/apprenticeharper/DeDRM_tools/blob/master/FAQs.md".format(PLUGIN_NAME, PLUGIN_VERSION,time.time()-self.starttime)
+            print("{0} v{1}: Ultimately failed to decrypt after {2:.1f} seconds. Read the FAQs at Harper's repository: https://github.com/apprenticeharper/DeDRM_tools/blob/master/FAQs.md".format(PLUGIN_NAME, PLUGIN_VERSION,time.time()-self.starttime))
             raise DeDRMError(u"{0} v{1}: Ultimately failed to decrypt after {2:.1f} seconds. Read the FAQs at Harper's repository: https://github.com/apprenticeharper/DeDRM_tools/blob/master/FAQs.md".format(PLUGIN_NAME, PLUGIN_VERSION, time.time()-self.starttime))
 
         # import the Adobe Adept ePub handler
         import calibre_plugins.dedrm.ineptepub as ineptepub
 
         if ineptepub.adeptBook(inf.name):
-            print u"{0} v{1}: {2} is a secure Adobe Adept ePub".format(PLUGIN_NAME, PLUGIN_VERSION, os.path.basename(path_to_ebook))
+            print("{0} v{1}: {2} is a secure Adobe Adept ePub".format(PLUGIN_NAME, PLUGIN_VERSION, os.path.basename(path_to_ebook)))
 
             # Attempt to decrypt epub with each encryption key (generated or provided).
             for keyname, userkeyhex in dedrmprefs['adeptkeys'].items():
-                userkey = userkeyhex.decode('hex')
-                print u"{0} v{1}: Trying Encryption key {2:s}".format(PLUGIN_NAME, PLUGIN_VERSION, keyname)
+                userkey = codecs.decode(userkeyhex, 'hex')
+                print(u"{0} v{1}: Trying Encryption key {2:s}".format(PLUGIN_NAME, PLUGIN_VERSION, keyname))
                 of = self.temporary_file(u".epub")
 
                 # Give the user key, ebook and TemporaryPersistent file to the decryption function.
                 try:
                     result = ineptepub.decryptBook(userkey, inf.name, of.name)
                 except:
-                    print u"{0} v{1}: Exception when decrypting after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION, time.time()-self.starttime)
+                    print("{0} v{1}: Exception when decrypting after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION, time.time()-self.starttime))
                     traceback.print_exc()
                     result = 1
 
                 try:
                     of.close()
                 except:
-                    print u"{0} v{1}: Exception closing temporary file after {2:.1f} seconds. Ignored.".format(PLUGIN_NAME, PLUGIN_VERSION, time.time()-self.starttime)
+                    print("{0} v{1}: Exception closing temporary file after {2:.1f} seconds. Ignored.".format(PLUGIN_NAME, PLUGIN_VERSION, time.time()-self.starttime))
 
                 if  result == 0:
                     # Decryption was successful.
                     # Return the modified PersistentTemporary file to calibre.
-                    print u"{0} v{1}: Decrypted with key {2:s} after {3:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION,keyname,time.time()-self.starttime)
+                    print("{0} v{1}: Decrypted with key {2:s} after {3:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION,keyname,time.time()-self.starttime))
                     return of.name
 
-                print u"{0} v{1}: Failed to decrypt with key {2:s} after {3:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION,keyname,time.time()-self.starttime)
+                print("{0} v{1}: Failed to decrypt with key {2:s} after {3:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION,keyname,time.time()-self.starttime))
 
             # perhaps we need to get a new default ADE key
-            print u"{0} v{1}: Looking for new default Adobe Digital Editions Keys after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION, time.time()-self.starttime)
+            print("{0} v{1}: Looking for new default Adobe Digital Editions Keys after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION, time.time()-self.starttime))
 
             # get the default Adobe keys
             defaultkeys = []
@@ -356,7 +358,7 @@ class DeDRM(FileTypePlugin):
 
                 self.default_key = defaultkeys[0]
             except:
-                print u"{0} v{1}: Exception when getting default Adobe Key after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION, time.time()-self.starttime)
+                print("{0} v{1}: Exception when getting default Adobe Key after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION, time.time()-self.starttime))
                 traceback.print_exc()
                 self.default_key = u""
 
@@ -368,14 +370,14 @@ class DeDRM(FileTypePlugin):
             if len(newkeys) > 0:
                 try:
                     for i,userkey in enumerate(newkeys):
-                        print u"{0} v{1}: Trying a new default key".format(PLUGIN_NAME, PLUGIN_VERSION)
+                        print("{0} v{1}: Trying a new default key".format(PLUGIN_NAME, PLUGIN_VERSION))
                         of = self.temporary_file(u".epub")
 
                         # Give the user key, ebook and TemporaryPersistent file to the decryption function.
                         try:
                             result = ineptepub.decryptBook(userkey, inf.name, of.name)
                         except:
-                            print u"{0} v{1}: Exception when decrypting after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION, time.time()-self.starttime)
+                            print("{0} v{1}: Exception when decrypting after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION, time.time()-self.starttime))
                             traceback.print_exc()
                             result = 1
 
@@ -384,31 +386,31 @@ class DeDRM(FileTypePlugin):
                         if  result == 0:
                             # Decryption was a success
                             # Store the new successful key in the defaults
-                            print u"{0} v{1}: Saving a new default key".format(PLUGIN_NAME, PLUGIN_VERSION)
+                            print("{0} v{1}: Saving a new default key".format(PLUGIN_NAME, PLUGIN_VERSION))
                             try:
                                 dedrmprefs.addnamedvaluetoprefs('adeptkeys','default_key',keyvalue.encode('hex'))
                                 dedrmprefs.writeprefs()
-                                print u"{0} v{1}: Saved a new default key after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION,time.time()-self.starttime)
+                                print("{0} v{1}: Saved a new default key after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION,time.time()-self.starttime))
                             except:
-                                print u"{0} v{1}: Exception when saving a new default key after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION, time.time()-self.starttime)
+                                print("{0} v{1}: Exception when saving a new default key after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION, time.time()-self.starttime))
                                 traceback.print_exc()
-                            print u"{0} v{1}: Decrypted with new default key after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION,time.time()-self.starttime)
+                            print("{0} v{1}: Decrypted with new default key after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION,time.time()-self.starttime))
                             # Return the modified PersistentTemporary file to calibre.
                             return of.name
 
-                        print u"{0} v{1}: Failed to decrypt with new default key after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION,time.time()-self.starttime)
-                except Exception, e:
-                    print u"{0} v{1}: Unexpected Exception trying a new default key after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION, time.time()-self.starttime)
+                        print(u"{0} v{1}: Failed to decrypt with new default key after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION,time.time()-self.starttime))
+                except Exception as e:
+                    print(u"{0} v{1}: Unexpected Exception trying a new default key after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION, time.time()-self.starttime))
                     traceback.print_exc()
                     pass
 
             # Something went wrong with decryption.
-            print u"{0} v{1}: Ultimately failed to decrypt after {2:.1f} seconds. Read the FAQs at Harper's repository: https://github.com/apprenticeharper/DeDRM_tools/blob/master/FAQs.md".format(PLUGIN_NAME, PLUGIN_VERSION,time.time()-self.starttime)
+            print("{0} v{1}: Ultimately failed to decrypt after {2:.1f} seconds. Read the FAQs at Harper's repository: https://github.com/apprenticeharper/DeDRM_tools/blob/master/FAQs.md".format(PLUGIN_NAME, PLUGIN_VERSION,time.time()-self.starttime))
             raise DeDRMError(u"{0} v{1}: Ultimately failed to decrypt after {2:.1f} seconds. Read the FAQs at Harper's repository: https://github.com/apprenticeharper/DeDRM_tools/blob/master/FAQs.md".format(PLUGIN_NAME, PLUGIN_VERSION,time.time()-self.starttime))
 
         # Not a Barnes & Noble nor an Adobe Adept
         # Import the fixed epub.
-        print u"{0} v{1}: “{2}” is neither an Adobe Adept nor a Barnes & Noble encrypted ePub".format(PLUGIN_NAME, PLUGIN_VERSION, os.path.basename(path_to_ebook))
+        print("{0} v{1}: “{2}” is neither an Adobe Adept nor a Barnes & Noble encrypted ePub".format(PLUGIN_NAME, PLUGIN_VERSION, os.path.basename(path_to_ebook)))
         raise DeDRMError(u"{0} v{1}: Couldn't decrypt after {2:.1f} seconds. DRM free perhaps?".format(PLUGIN_NAME, PLUGIN_VERSION,time.time()-self.starttime))
 
     def PDFDecrypt(self,path_to_ebook):
@@ -417,17 +419,17 @@ class DeDRM(FileTypePlugin):
 
         dedrmprefs = prefs.DeDRM_Prefs()
         # Attempt to decrypt epub with each encryption key (generated or provided).
-        print u"{0} v{1}: {2} is a PDF ebook".format(PLUGIN_NAME, PLUGIN_VERSION, os.path.basename(path_to_ebook))
+        print("{0} v{1}: {2} is a PDF ebook".format(PLUGIN_NAME, PLUGIN_VERSION, os.path.basename(path_to_ebook)))
         for keyname, userkeyhex in dedrmprefs['adeptkeys'].items():
-            userkey = userkeyhex.decode('hex')
-            print u"{0} v{1}: Trying Encryption key {2:s}".format(PLUGIN_NAME, PLUGIN_VERSION, keyname)
+            userkey = codecs.decode(userkeyhex, 'hex')
+            print("{0} v{1}: Trying Encryption key {2:s}".format(PLUGIN_NAME, PLUGIN_VERSION, keyname))
             of = self.temporary_file(u".pdf")
 
             # Give the user key, ebook and TemporaryPersistent file to the decryption function.
             try:
                 result = ineptpdf.decryptBook(userkey, path_to_ebook, of.name)
             except:
-                print u"{0} v{1}: Exception when decrypting after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION, time.time()-self.starttime)
+                print("{0} v{1}: Exception when decrypting after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION, time.time()-self.starttime))
                 traceback.print_exc()
                 result = 1
 
@@ -438,10 +440,10 @@ class DeDRM(FileTypePlugin):
                 # Return the modified PersistentTemporary file to calibre.
                 return of.name
 
-            print u"{0} v{1}: Failed to decrypt with key {2:s} after {3:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION,keyname,time.time()-self.starttime)
+            print("{0} v{1}: Failed to decrypt with key {2:s} after {3:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION,keyname,time.time()-self.starttime))
 
         # perhaps we need to get a new default ADE key
-        print u"{0} v{1}: Looking for new default Adobe Digital Editions Keys after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION, time.time()-self.starttime)
+        print("{0} v{1}: Looking for new default Adobe Digital Editions Keys after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION, time.time()-self.starttime))
 
         # get the default Adobe keys
         defaultkeys = []
@@ -459,7 +461,7 @@ class DeDRM(FileTypePlugin):
 
             self.default_key = defaultkeys[0]
         except:
-            print u"{0} v{1}: Exception when getting default Adobe Key after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION, time.time()-self.starttime)
+            print("{0} v{1}: Exception when getting default Adobe Key after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION, time.time()-self.starttime))
             traceback.print_exc()
             self.default_key = u""
 
@@ -471,14 +473,14 @@ class DeDRM(FileTypePlugin):
         if len(newkeys) > 0:
             try:
                 for i,userkey in enumerate(newkeys):
-                    print u"{0} v{1}: Trying a new default key".format(PLUGIN_NAME, PLUGIN_VERSION)
+                    print("{0} v{1}: Trying a new default key".format(PLUGIN_NAME, PLUGIN_VERSION))
                     of = self.temporary_file(u".pdf")
 
                     # Give the user key, ebook and TemporaryPersistent file to the decryption function.
                     try:
                         result = ineptpdf.decryptBook(userkey, path_to_ebook, of.name)
                     except:
-                        print u"{0} v{1}: Exception when decrypting after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION, time.time()-self.starttime)
+                        print("{0} v{1}: Exception when decrypting after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION, time.time()-self.starttime))
                         traceback.print_exc()
                         result = 1
 
@@ -487,23 +489,23 @@ class DeDRM(FileTypePlugin):
                     if  result == 0:
                         # Decryption was a success
                         # Store the new successful key in the defaults
-                        print u"{0} v{1}: Saving a new default key".format(PLUGIN_NAME, PLUGIN_VERSION)
+                        print("{0} v{1}: Saving a new default key".format(PLUGIN_NAME, PLUGIN_VERSION))
                         try:
                             dedrmprefs.addnamedvaluetoprefs('adeptkeys','default_key',keyvalue.encode('hex'))
                             dedrmprefs.writeprefs()
-                            print u"{0} v{1}: Saved a new default key after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION,time.time()-self.starttime)
+                            print("{0} v{1}: Saved a new default key after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION,time.time()-self.starttime))
                         except:
-                            print u"{0} v{1}: Exception when saving a new default key after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION, time.time()-self.starttime)
+                            print("{0} v{1}: Exception when saving a new default key after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION, time.time()-self.starttime))
                             traceback.print_exc()
                         # Return the modified PersistentTemporary file to calibre.
                         return of.name
 
-                    print u"{0} v{1}: Failed to decrypt with new default key after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION,time.time()-self.starttime)
-            except Exception, e:
+                    print(u"{0} v{1}: Failed to decrypt with new default key after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION,time.time()-self.starttime))
+            except Exception as e:
                 pass
 
         # Something went wrong with decryption.
-        print u"{0} v{1}: Ultimately failed to decrypt after {2:.1f} seconds. Read the FAQs at Harper's repository: https://github.com/apprenticeharper/DeDRM_tools/blob/master/FAQs.md".format(PLUGIN_NAME, PLUGIN_VERSION,time.time()-self.starttime)
+        print("{0} v{1}: Ultimately failed to decrypt after {2:.1f} seconds. Read the FAQs at Harper's repository: https://github.com/apprenticeharper/DeDRM_tools/blob/master/FAQs.md".format(PLUGIN_NAME, PLUGIN_VERSION,time.time()-self.starttime))
         raise DeDRMError(u"{0} v{1}: Ultimately failed to decrypt after {2:.1f} seconds. Read the FAQs at Harper's repository: https://github.com/apprenticeharper/DeDRM_tools/blob/master/FAQs.md".format(PLUGIN_NAME, PLUGIN_VERSION, time.time()-self.starttime))
 
 
@@ -530,12 +532,12 @@ class DeDRM(FileTypePlugin):
 
         try:
             book = k4mobidedrm.GetDecryptedBook(path_to_ebook,kindleDatabases,androidFiles,serials,pids,self.starttime)
-        except Exception, e:
+        except Exception as e:
             decoded = False
             # perhaps we need to get a new default Kindle for Mac/PC key
             defaultkeys = []
-            print u"{0} v{1}: Failed to decrypt with error: {2}".format(PLUGIN_NAME, PLUGIN_VERSION,e.args[0])
-            print u"{0} v{1}: Looking for new default Kindle Key after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION, time.time()-self.starttime)
+            print("{0} v{1}: Failed to decrypt with error: {2}".format(PLUGIN_NAME, PLUGIN_VERSION,e.args[0]))
+            print("{0} v{1}: Looking for new default Kindle Key after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION, time.time()-self.starttime))
 
             try:
                 if iswindows or isosx:
@@ -548,7 +550,7 @@ class DeDRM(FileTypePlugin):
                     scriptpath = os.path.join(self.alfdir,u"kindlekey.py")
                     defaultkeys = WineGetKeys(scriptpath, u".k4i",dedrmprefs['kindlewineprefix'])
             except:
-                print u"{0} v{1}: Exception when getting default Kindle Key after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION, time.time()-self.starttime)
+                print("{0} v{1}: Exception when getting default Kindle Key after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION, time.time()-self.starttime))
                 traceback.print_exc()
                 pass
 
@@ -558,20 +560,20 @@ class DeDRM(FileTypePlugin):
                 if keyvalue not in dedrmprefs['kindlekeys'].values():
                     newkeys[keyname] = keyvalue
             if len(newkeys) > 0:
-                print u"{0} v{1}: Found {2} new {3}".format(PLUGIN_NAME, PLUGIN_VERSION, len(newkeys), u"key" if len(newkeys)==1 else u"keys")
+                print("{0} v{1}: Found {2} new {3}".format(PLUGIN_NAME, PLUGIN_VERSION, len(newkeys), u"key" if len(newkeys)==1 else u"keys"))
                 try:
                     book = k4mobidedrm.GetDecryptedBook(path_to_ebook,newkeys.items(),[],[],[],self.starttime)
                     decoded = True
                     # store the new successful keys in the defaults
-                    print u"{0} v{1}: Saving {2} new {3}".format(PLUGIN_NAME, PLUGIN_VERSION, len(newkeys), u"key" if len(newkeys)==1 else u"keys")
+                    print("{0} v{1}: Saving {2} new {3}".format(PLUGIN_NAME, PLUGIN_VERSION, len(newkeys), u"key" if len(newkeys)==1 else u"keys"))
                     for keyvalue in newkeys.values():
                         dedrmprefs.addnamedvaluetoprefs('kindlekeys','default_key',keyvalue)
                     dedrmprefs.writeprefs()
-                except Exception, e:
+                except Exception as e:
                     pass
             if not decoded:
                 #if you reached here then no luck raise and exception
-                print u"{0} v{1}: Ultimately failed to decrypt after {2:.1f} seconds. Read the FAQs at Harper's repository: https://github.com/apprenticeharper/DeDRM_tools/blob/master/FAQs.md".format(PLUGIN_NAME, PLUGIN_VERSION,time.time()-self.starttime)
+                print("{0} v{1}: Ultimately failed to decrypt after {2:.1f} seconds. Read the FAQs at Harper's repository: https://github.com/apprenticeharper/DeDRM_tools/blob/master/FAQs.md".format(PLUGIN_NAME, PLUGIN_VERSION,time.time()-self.starttime))
                 raise DeDRMError(u"{0} v{1}: Ultimately failed to decrypt after {2:.1f} seconds. Read the FAQs at Harper's repository: https://github.com/apprenticeharper/DeDRM_tools/blob/master/FAQs.md".format(PLUGIN_NAME, PLUGIN_VERSION,time.time()-self.starttime))
 
         of = self.temporary_file(book.getBookExtension())
@@ -590,7 +592,7 @@ class DeDRM(FileTypePlugin):
         # Attempt to decrypt epub with each encryption key (generated or provided).
         for keyname, userkey in dedrmprefs['ereaderkeys'].items():
             keyname_masked = u"".join((u'X' if (x.isdigit()) else x) for x in keyname)
-            print u"{0} v{1}: Trying Encryption key {2:s}".format(PLUGIN_NAME, PLUGIN_VERSION, keyname_masked)
+            print("{0} v{1}: Trying Encryption key {2:s}".format(PLUGIN_NAME, PLUGIN_VERSION, keyname_masked))
             of = self.temporary_file(u".pmlz")
 
             # Give the userkey, ebook and TemporaryPersistent file to the decryption function.
@@ -601,12 +603,12 @@ class DeDRM(FileTypePlugin):
             # Decryption was successful return the modified PersistentTemporary
             # file to Calibre's import process.
             if  result == 0:
-                print u"{0} v{1}: Successfully decrypted with key {2:s} after {3:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION,keyname_masked,time.time()-self.starttime)
+                print("{0} v{1}: Successfully decrypted with key {2:s} after {3:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION,keyname_masked,time.time()-self.starttime))
                 return of.name
 
-            print u"{0} v{1}: Failed to decrypt with key {2:s} after {3:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION,keyname_masked,time.time()-self.starttime)
+            print("{0} v{1}: Failed to decrypt with key {2:s} after {3:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION,keyname_masked,time.time()-self.starttime))
 
-        print u"{0} v{1}: Ultimately failed to decrypt after {2:.1f} seconds. Read the FAQs at Harper's repository: https://github.com/apprenticeharper/DeDRM_tools/blob/master/FAQs.md".format(PLUGIN_NAME, PLUGIN_VERSION,time.time()-self.starttime)
+        print("{0} v{1}: Ultimately failed to decrypt after {2:.1f} seconds. Read the FAQs at Harper's repository: https://github.com/apprenticeharper/DeDRM_tools/blob/master/FAQs.md".format(PLUGIN_NAME, PLUGIN_VERSION,time.time()-self.starttime))
         raise DeDRMError(u"{0} v{1}: Ultimately failed to decrypt after {2:.1f} seconds. Read the FAQs at Harper's repository: https://github.com/apprenticeharper/DeDRM_tools/blob/master/FAQs.md".format(PLUGIN_NAME, PLUGIN_VERSION, time.time()-self.starttime))
 
 
@@ -616,7 +618,7 @@ class DeDRM(FileTypePlugin):
         sys.stdout=SafeUnbuffered(sys.stdout)
         sys.stderr=SafeUnbuffered(sys.stderr)
 
-        print u"{0} v{1}: Trying to decrypt {2}".format(PLUGIN_NAME, PLUGIN_VERSION, os.path.basename(path_to_ebook))
+        print("{0} v{1}: Trying to decrypt {2}".format(PLUGIN_NAME, PLUGIN_VERSION, os.path.basename(path_to_ebook)))
         self.starttime = time.time()
 
         booktype = os.path.splitext(path_to_ebook)[1].lower()[1:]
@@ -635,9 +637,9 @@ class DeDRM(FileTypePlugin):
             # Adobe Adept or B&N ePub
             decrypted_ebook = self.ePubDecrypt(path_to_ebook)
         else:
-            print u"Unknown booktype {0}. Passing back to calibre unchanged".format(booktype)
+            print("Unknown booktype {0}. Passing back to calibre unchanged".format(booktype))
             return path_to_ebook
-        print u"{0} v{1}: Finished after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION,time.time()-self.starttime)
+        print("{0} v{1}: Finished after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION,time.time()-self.starttime))
         return decrypted_ebook
 
     def is_customizable(self):
index 2ede7c2cc562a55cc6c31e66f76d70606808dee0..fb5dccaf5ea79353c261b63c7e29aa6b1baef5cd 100644 (file)
@@ -48,14 +48,16 @@ from __future__ import with_statement
 #   5.8 - Added getkey interface for Windows DeDRM application
 #   5.9 - moved unicode_argv call inside main for Windows DeDRM compatibility
 #   6.0 - Work if TkInter is missing
+#   7.0 - Python 3 compatible for calibre 5
+
+from __future__ import print_function
 
 """
 Retrieve Adobe ADEPT user key.
 """
-from __future__ import print_function
 
 __license__ = 'GPL v3'
-__version__ = '6.0'
+__version__ = '7.0'
 
 import sys, os, struct, getopt
 
@@ -118,7 +120,7 @@ def unicode_argv():
         argvencoding = sys.stdin.encoding
         if argvencoding == None:
             argvencoding = "utf-8"
-        return [arg if (type(arg) == unicode) else unicode(arg,argvencoding) for arg in sys.argv]
+        return arg
 
 class ADEPTError(Exception):
     pass
@@ -497,7 +499,7 @@ def cli_main():
 
     try:
         opts, args = getopt.getopt(argv[1:], "h")
-    except getopt.GetoptError, err:
+    except getopt.GetoptError as err:
         print(u"Error in options or arguments: {0}".format(err.args[0]))
         usage(progname)
         sys.exit(2)
@@ -586,7 +588,7 @@ def gui_main():
                 keyfileout.write(key)
             success = True
             tkMessageBox.showinfo(progname, u"Key successfully retrieved to {0}".format(outfile))
-    except ADEPTError, e:
+    except ADEPTError as e:
         tkMessageBox.showerror(progname, u"Error: {0}".format(str(e)))
     except Exception:
         root.wm_state('normal')
index 566751130103e0bd0b7a6961c8a9638c8fc3a5be..28e1843d8a0ef08ab5568dfc7000e7f557555af8 100644 (file)
@@ -13,6 +13,8 @@
     CryptoPy Artisitic License Version 1.0
     See the wonderful pure python package cryptopy-1.2.5
     and read its LICENSE.txt for complete license details.
+
+    Adjusted for Python 3 compatibility, September 2020
 """
 
 class CryptoError(Exception):
@@ -101,7 +103,7 @@ class BlockCipher:
         numBlocks, numExtraBytes = divmod(len(self.bytesToDecrypt), self.blockSize)
         if more == None:  # no more calls to decrypt, should have all the data
             if numExtraBytes  != 0:
-                raise DecryptNotBlockAlignedError, 'Data not block aligned on decrypt'
+                raise DecryptNotBlockAlignedError('Data not block aligned on decrypt')
 
         # hold back some bytes in case last decrypt has zero len
         if (more != None) and (numExtraBytes == 0) and (numBlocks >0) :
@@ -143,7 +145,7 @@ class padWithPadLen(Pad):
     def removePad(self, paddedBinaryString, blockSize):
         """ Remove padding from a binary string """
         if not(0<len(paddedBinaryString)):
-            raise DecryptNotBlockAlignedError, 'Expected More Data'
+            raise DecryptNotBlockAlignedError('Expected More Data')
         return paddedBinaryString[:-ord(paddedBinaryString[-1])]
 
 class noPadding(Pad):
@@ -451,7 +453,7 @@ class AES(Rijndael):
     def __init__(self, key = None, padding = padWithPadLen(), keySize=16):
         """ Initialize AES, keySize is in bytes """
         if  not (keySize == 16 or keySize == 24 or keySize == 32) :
-            raise BadKeySizeError, 'Illegal AES key size, must be 16, 24, or 32 bytes'
+            raise BadKeySizeError('Illegal AES key size, must be 16, 24, or 32 bytes')
 
         Rijndael.__init__( self, key, padding=padding, keySize=keySize, blockSize=16 )
 
index 5d5e5082bab4fa35b86e0cc77b5c653f92ebc376..a5a11a5a9306df4a7b512fd16673ae0ba7d7f7d4 100644 (file)
@@ -178,13 +178,13 @@ def _load_python_alfcrypto():
             if len(key)!=16:
                 raise Exception('Pukall_Cipher: Bad key length.')
             wkey = []
-            for i in xrange(8):
+            for i in range(8):
                 wkey.append(ord(key[i*2])<<8 | ord(key[i*2+1]))
             dst = ""
-            for i in xrange(len(src)):
+            for i in range(len(src)):
                 temp1 = 0;
                 byteXorVal = 0;
-                for j in xrange(8):
+                for j in range(8):
                     temp1 ^= wkey[j]
                     sum2  = (sum2+j)*20021 + sum1
                     sum1  = (temp1*346)&0xFFFF
@@ -197,7 +197,7 @@ def _load_python_alfcrypto():
                 curByte = ((curByte ^ (byteXorVal >> 8)) ^ byteXorVal) & 0xFF
                 if decryption:
                     keyXorVal = curByte * 257;
-                for j in xrange(8):
+                for j in range(8):
                     wkey[j] ^= keyXorVal;
                 dst+=chr(curByte)
             return dst
index a264c7d2e60ad3e9e65102e769be85f3a79a3e94..f05c4682b57945b14c8f351d85051faca8876bfe 100644 (file)
@@ -2,6 +2,7 @@
 # -*- coding: utf-8 -*-
 
 from __future__ import with_statement
+from __future__ import print_function
 
 # androidkindlekey.py
 # Copyright © 2013-15 by Thom and Apprentice Harper
@@ -17,14 +18,14 @@ from __future__ import with_statement
 #  1.3   - added in TkInter interface, output to a file
 #  1.4   - Fix some problems identified by Aldo Bleeker
 #  1.5   - Fix another problem identified by Aldo Bleeker
+#  2.0   - Add Python 3 compatibility
 
 """
 Retrieve Kindle for Android Serial Number.
 """
-from __future__ import print_function
 
 __license__ = 'GPL v3'
-__version__ = '1.5'
+__version__ = '2.0'
 
 import os
 import sys
@@ -34,7 +35,10 @@ import tempfile
 import zlib
 import tarfile
 from hashlib import md5
-from cStringIO import StringIO
+try:
+    from cStringIO import StringIO
+except ImportError:
+    from io import BytesIO as StringIO
 from binascii import a2b_hex, b2a_hex
 
 # Routines common to Mac and PC
@@ -49,7 +53,7 @@ class SafeUnbuffered:
         if self.encoding == None:
             self.encoding = "utf-8"
     def write(self, data):
-        if isinstance(data,unicode):
+        if isinstance(data,bytes):
             data = data.encode(self.encoding,"replace")
         self.stream.write(data)
         self.stream.flush()
@@ -90,7 +94,7 @@ def unicode_argv():
             # Remove Python executable and commands if present
             start = argc.value - len(sys.argv)
             return [argv[i] for i in
-                    xrange(start, argc.value)]
+                    range(start, argc.value)]
         # if we don't have any arguments at all, just pass back script name
         # this should never happen
         return [u"kindlekey.py"]
@@ -98,7 +102,7 @@ def unicode_argv():
         argvencoding = sys.stdin.encoding
         if argvencoding == None:
             argvencoding = "utf-8"
-        return [arg if (type(arg) == unicode) else unicode(arg,argvencoding) for arg in sys.argv]
+        return argv
 
 class DrmException(Exception):
     pass
@@ -248,7 +252,7 @@ def get_serials2(path=STORAGE2):
             traceback.print_exc()
             pass
     tokens = list(set(tokens))
+
     serials = []
     for x in dsns:
         serials.append(x)
@@ -313,7 +317,7 @@ __all__ = [ 'get_serials', 'getkey']
 def getkey(outfile, inpath):
     keys = get_serials(inpath)
     if len(keys) > 0:
-        with file(outfile, 'w') as keyfileout:
+        with open(outfile, 'w') as keyfileout:
             for key in keys:
                 keyfileout.write(key)
                 keyfileout.write("\n")
@@ -340,7 +344,7 @@ def cli_main():
 
     try:
         opts, args = getopt.getopt(argv[1:], "hb:")
-    except getopt.GetoptError, err:
+    except getopt.GetoptError as err:
         usage(progname)
         print(u"\nError in options or arguments: {0}".format(err.args[0]))
         return 2
@@ -444,11 +448,11 @@ def gui_main():
                         if not os.path.exists(outfile):
                             break
 
-                    with file(outfile, 'w') as keyfileout:
+                    with open(outfile, 'w') as keyfileout:
                         keyfileout.write(key)
                     success = True
                     tkMessageBox.showinfo(progname, u"Key successfully retrieved to {0}".format(outfile))
-            except Exception, e:
+            except Exception as e:
                 self.status['text'] = u"Error: {0}".format(e.args[0])
                 return
             self.status['text'] = u"Select backup.ab file"
index 85ffaa4738c9cdb71a8917c0f3c90d8218492a10..71f079491a670b7c36cdf2a9ed098e66a2ca1fcd 100644 (file)
@@ -42,7 +42,7 @@ def unicode_argv():
         argvencoding = sys.stdin.encoding
         if argvencoding == None:
             argvencoding = "utf-8"
-        return [arg if (type(arg) == unicode) else unicode(arg,argvencoding) for arg in sys.argv]
+        return arg
 
 
 def add_cp65001_codec():
index a4a2ae02e346cbae9ac798bec2435b00bea63cc3..1a85513f0abbaee185661eecdf4ab8c6a6d4f517 100644 (file)
@@ -29,6 +29,8 @@
 # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 # DEALINGS IN THE SOFTWARE.
 
+# Adjusted for Python 3, September 2020
+
 """
 AskFolder(...) -- Ask the user to select a folder Windows specific
 """
@@ -164,15 +166,15 @@ def AskFolder(
     def BrowseCallback(hwnd, uMsg, lParam, lpData):
         if uMsg == BFFM_INITIALIZED:
             if actionButtonLabel:
-                label = unicode(actionButtonLabel, errors='replace')
+                label = actionButtonLabel.decode('utf-8', 'replace')
                 user32.SendMessageW(hwnd, BFFM_SETOKTEXT, 0, label)
             if cancelButtonLabel:
-                label = unicode(cancelButtonLabel, errors='replace')
+                label = cancelButtonLabel.decode('utf-8', 'replace')
                 cancelButton = user32.GetDlgItem(hwnd, IDCANCEL)
                 if cancelButton:
                     user32.SetWindowTextW(cancelButton, label)
             if windowTitle:
-                title = unicode(windowTitle, erros='replace')
+                title = windowTitle.decode('utf-8', 'replace')
                 user32.SetWindowTextW(hwnd, title)
             if defaultLocation:
                 user32.SendMessageW(hwnd, BFFM_SETSELECTIONW, 1, defaultLocation.replace('/', '\\'))
index 9bfae68def8c00bb55691e93bc6e6b489e259cbd..2ef3223d844d0ecc663bb46a2eb2362f781c426a 100644 (file)
@@ -6,6 +6,8 @@ from __future__ import print_function
 
 __license__ = 'GPL v3'
 
+# Added Python 3 compatibility, September 2020
+
 # Standard Python modules.
 import os, traceback, json
 
@@ -22,7 +24,7 @@ try:
     from PyQt5 import Qt as QtGui
 except ImportError:
     from PyQt4 import QtGui
-    
+
 from zipfile import ZipFile
 
 # calibre modules and constants.
@@ -123,7 +125,7 @@ class ConfigWidget(QWidget):
     def kindle_serials(self):
         d = ManageKeysDialog(self,u"EInk Kindle Serial Number",self.tempdedrmprefs['serials'], AddSerialDialog)
         d.exec_()
-        
+
     def kindle_android(self):
         d = ManageKeysDialog(self,u"Kindle for Android Key",self.tempdedrmprefs['androidkeys'], AddAndroidDialog, 'k4a')
         d.exec_()
@@ -289,7 +291,7 @@ class ManageKeysDialog(QDialog):
 
     def getwineprefix(self):
         if self.wineprefix is not None:
-            return unicode(self.wp_lineedit.text()).strip()
+            return self.wp_lineedit.text().strip()
         return u""
 
     def populate_list(self):
@@ -338,7 +340,7 @@ class ManageKeysDialog(QDialog):
         if d.result() != d.Accepted:
             # rename cancelled or moot.
             return
-        keyname = unicode(self.listy.currentItem().text())
+        keyname = self.listy.currentItem().text()
         if not question_dialog(self, "{0} {1}: Confirm Rename".format(PLUGIN_NAME, PLUGIN_VERSION), u"Do you really want to rename the {2} named <strong>{0}</strong> to <strong>{1}</strong>?".format(keyname,d.key_name,self.key_type_name), show_copy_button=False, default_yes=False):
             return
         self.plugin_keys[d.key_name] = self.plugin_keys[keyname]
@@ -350,7 +352,7 @@ class ManageKeysDialog(QDialog):
     def delete_key(self):
         if not self.listy.currentItem():
             return
-        keyname = unicode(self.listy.currentItem().text())
+        keyname = self.listy.currentItem().text()
         if not question_dialog(self, "{0} {1}: Confirm Delete".format(PLUGIN_NAME, PLUGIN_VERSION), u"Do you really want to delete the {1} <strong>{0}</strong>?".format(keyname, self.key_type_name), show_copy_button=False, default_yes=False):
             return
         if type(self.plugin_keys) == dict:
@@ -388,7 +390,7 @@ class ManageKeysDialog(QDialog):
                 with open(fpath,'rb') as keyfile:
                     new_key_value = keyfile.read()
                 if self.binary_file:
-                    new_key_value = new_key_value.encode('hex')
+                    new_key_value = new_key_value.hex()
                 elif self.json_file:
                     new_key_value = json.loads(new_key_value)
                 elif self.android_file:
@@ -412,7 +414,7 @@ class ManageKeysDialog(QDialog):
                     else:
                         counter += 1
                         self.plugin_keys[new_key_name] = new_key_value
-                            
+
             msg = u""
             if counter+skipped > 1:
                 if counter > 0:
@@ -434,7 +436,7 @@ class ManageKeysDialog(QDialog):
             r = error_dialog(None, "{0} {1}".format(PLUGIN_NAME, PLUGIN_VERSION),
                                     _(errmsg), show=True, show_copy_button=False)
             return
-        keyname = unicode(self.listy.currentItem().text())
+        keyname = self.listy.currentItem().text()
         unique_dlg_name = PLUGIN_NAME + u"export {0} keys".format(self.key_type_name).replace(' ', '_') #takes care of automatically remembering last directory
         caption = u"Save {0} File as...".format(self.key_type_name)
         filters = [(u"{0} Files".format(self.key_type_name), [u"{0}".format(self.keyfile_ext)])]
@@ -485,7 +487,7 @@ class RenameKeyDialog(QDialog):
         self.resize(self.sizeHint())
 
     def accept(self):
-        if not unicode(self.key_ledit.text()) or unicode(self.key_ledit.text()).isspace():
+        if not self.key_ledit.text() or self.key_ledit.text().isspace():
             errmsg = u"Key name field cannot be empty!"
             return error_dialog(None, "{0} {1}".format(PLUGIN_NAME, PLUGIN_VERSION),
                                     _(errmsg), show=True, show_copy_button=False)
@@ -506,7 +508,7 @@ class RenameKeyDialog(QDialog):
 
     @property
     def key_name(self):
-        return unicode(self.key_ledit.text()).strip()
+        return self.key_ledit.text().strip()
 
 
 
@@ -589,19 +591,19 @@ class AddBandNKeyDialog(QDialog):
 
     @property
     def key_name(self):
-        return unicode(self.key_ledit.text()).strip()
+        return self.key_ledit.text().strip()
 
     @property
     def key_value(self):
-        return unicode(self.key_display.text()).strip()
+        return self.key_display.text().strip()
 
     @property
     def user_name(self):
-        return unicode(self.name_ledit.text()).strip().lower().replace(' ','')
+        return self.name_ledit.text().strip().lower().replace(' ','')
 
     @property
     def cc_number(self):
-        return unicode(self.cc_ledit.text()).strip()
+        return self.cc_ledit.text().strip()
 
     def retrieve_key(self):
         from calibre_plugins.dedrm.ignoblekeyfetch import fetch_key as fetch_bandn_key
@@ -675,7 +677,7 @@ class AddEReaderDialog(QDialog):
 
     @property
     def key_name(self):
-        return unicode(self.key_ledit.text()).strip()
+        return self.key_ledit.text().strip()
 
     @property
     def key_value(self):
@@ -684,11 +686,11 @@ class AddEReaderDialog(QDialog):
 
     @property
     def user_name(self):
-        return unicode(self.name_ledit.text()).strip().lower().replace(' ','')
+        return self.name_ledit.text().strip().lower().replace(' ','')
 
     @property
     def cc_number(self):
-        return unicode(self.cc_ledit.text()).strip().replace(' ', '').replace('-','')
+        return self.cc_ledit.text().strip().replace(' ', '').replace('-','')
 
 
     def accept(self):
@@ -758,7 +760,7 @@ class AddAdeptDialog(QDialog):
 
     @property
     def key_name(self):
-        return unicode(self.key_ledit.text()).strip()
+        return self.key_ledit.text().strip()
 
     @property
     def key_value(self):
@@ -819,7 +821,7 @@ class AddKindleDialog(QDialog):
             default_key_error = QLabel(u"The default encryption key for Kindle for Mac/PC could not be found.", self)
             default_key_error.setAlignment(Qt.AlignHCenter)
             layout.addWidget(default_key_error)
-            
+
             # if no default, both buttons do the same
             self.button_box.accepted.connect(self.reject)
 
@@ -830,7 +832,7 @@ class AddKindleDialog(QDialog):
 
     @property
     def key_name(self):
-        return unicode(self.key_ledit.text()).strip()
+        return self.key_ledit.text().strip()
 
     @property
     def key_value(self):
@@ -876,11 +878,11 @@ class AddSerialDialog(QDialog):
 
     @property
     def key_name(self):
-        return unicode(self.key_ledit.text()).strip()
+        return self.key_ledit.text().strip()
 
     @property
     def key_value(self):
-        return unicode(self.key_ledit.text()).replace(' ', '')
+        return self.key_ledit.text().replace(' ', '')
 
     def accept(self):
         if len(self.key_name) == 0 or self.key_name.isspace():
@@ -916,7 +918,7 @@ class AddAndroidDialog(QDialog):
         self.selected_file_name = QLabel(u"",self)
         self.selected_file_name.setAlignment(Qt.AlignHCenter)
         file_group.addWidget(self.selected_file_name)
-        
+
         key_group = QHBoxLayout()
         data_group_box_layout.addLayout(key_group)
         key_group.addWidget(QLabel(u"Unique Key Name:", self))
@@ -926,7 +928,7 @@ class AddAndroidDialog(QDialog):
         #key_label = QLabel(_(''), self)
         #key_label.setAlignment(Qt.AlignHCenter)
         #data_group_box_layout.addWidget(key_label)
-        
+
         self.button_box.accepted.connect(self.accept)
         self.button_box.rejected.connect(self.reject)
         layout.addWidget(self.button_box)
@@ -934,16 +936,16 @@ class AddAndroidDialog(QDialog):
 
     @property
     def key_name(self):
-        return unicode(self.key_ledit.text()).strip()
+        return self.key_ledit.text().strip()
 
     @property
     def file_name(self):
-        return unicode(self.selected_file_name.text()).strip()
+        return self.selected_file_name.text().strip()
 
     @property
     def key_value(self):
         return self.serials_from_file
-        
+
     def get_android_file(self):
         unique_dlg_name = PLUGIN_NAME + u"Import Kindle for Android backup file" #takes care of automatically remembering last directory
         caption = u"Select Kindle for Android backup file to add"
@@ -961,7 +963,7 @@ class AddAndroidDialog(QDialog):
                     file_name = os.path.basename(self.filename)
                     self.serials_from_file.extend(file_serials)
         self.selected_file_name.setText(file_name)
-    
+
 
     def accept(self):
         if len(self.file_name) == 0 or len(self.key_value) == 0:
@@ -1004,11 +1006,11 @@ class AddPIDDialog(QDialog):
 
     @property
     def key_name(self):
-        return unicode(self.key_ledit.text()).strip()
+        return self.key_ledit.text().strip()
 
     @property
     def key_value(self):
-        return unicode(self.key_ledit.text()).strip()
+        return self.key_ledit.text().strip()
 
     def accept(self):
         if len(self.key_name) == 0 or self.key_name.isspace():
index 3b65c54538a74d03ee67a348785938338ebd2bd4..aa794b1f68175256a57cf1b4da10345259692d19 100644 (file)
@@ -1,6 +1,7 @@
 #! /usr/bin/python
 # vim:ts=4:sw=4:softtabstop=4:smarttab:expandtab
 # For use with Topaz Scripts Version 2.6
+# Added Python 3 compatibility, September 2020
 
 from __future__ import print_function
 class Unbuffered:
@@ -107,7 +108,7 @@ def readString(file):
 def convert(i):
     result = ''
     val = encodeNumber(i)
-    for j in xrange(len(val)):
+    for j in range(len(val)):
         c = ord(val[j:j+1])
         result += '%02x' % c
     return result
@@ -121,10 +122,10 @@ class Dictionary(object):
     def __init__(self, dictFile):
         self.filename = dictFile
         self.size = 0
-        self.fo = file(dictFile,'rb')
+        self.fo = open(dictFile,'rb')
         self.stable = []
         self.size = readEncodedNumber(self.fo)
-        for i in xrange(self.size):
+        for i in range(self.size):
             self.stable.append(self.escapestr(readString(self.fo)))
         self.pos = 0
 
@@ -151,7 +152,7 @@ class Dictionary(object):
         return self.pos
 
     def dumpDict(self):
-        for i in xrange(self.size):
+        for i in range(self.size):
             print("%d %s %s" % (i, convert(i), self.stable[i]))
         return
 
@@ -161,7 +162,7 @@ class Dictionary(object):
 
 class PageParser(object):
     def __init__(self, filename, dict, debug, flat_xml):
-        self.fo = file(filename,'rb')
+        self.fo = open(filename,'rb')
         self.id = os.path.basename(filename).replace('.dat','')
         self.dict = dict
         self.debug = debug
@@ -392,7 +393,7 @@ class PageParser(object):
         'startID'      : (0, 'number', 1, 1),
         'startID.page' : (1, 'number', 0, 0),
         'startID.id'   : (1, 'number', 0, 0),
-    
+
         'median_d'          : (1, 'number', 0, 0),
         'median_h'          : (1, 'number', 0, 0),
         'median_firsty'     : (1, 'number', 0, 0),
@@ -420,7 +421,7 @@ class PageParser(object):
     def get_tagpath(self, i):
         cnt = len(self.tagpath)
         if i < cnt : result = self.tagpath[i]
-        for j in xrange(i+1, cnt) :
+        for j in range(i+1, cnt) :
             result += '.' + self.tagpath[j]
         return result
 
@@ -472,7 +473,7 @@ class PageParser(object):
 
         if self.debug : print('Processing: ', self.get_tagpath(0))
         cnt = self.tagpath_len()
-        for j in xrange(cnt):
+        for j in range(cnt):
             tkn = self.get_tagpath(j)
             if tkn in self.token_tags :
                 num_args = self.token_tags[tkn][0]
@@ -497,7 +498,7 @@ class PageParser(object):
             if (subtags == 1):
                 ntags = readEncodedNumber(self.fo)
                 if self.debug : print('subtags: ' + token + ' has ' + str(ntags))
-                for j in xrange(ntags):
+                for j in range(ntags):
                     val = readEncodedNumber(self.fo)
                     subtagres.append(self.procToken(self.dict.lookup(val)))
 
@@ -511,7 +512,7 @@ class PageParser(object):
                     argres = self.decodeCMD(arg,argtype)
                 else :
                     # num_arg scalar arguments
-                    for i in xrange(num_args):
+                    for i in range(num_args):
                         argres.append(self.formatArg(readEncodedNumber(self.fo), argtype))
 
             # build the return tag
@@ -546,7 +547,7 @@ class PageParser(object):
             result += 'of the document is indicated by snippet number sets at the\n'
             result += 'end of each snippet. \n'
             print(result)
-        for i in xrange(cnt):
+        for i in range(cnt):
             if self.debug: print('Snippet:',str(i))
             snippet = []
             snippet.append(i)
@@ -565,12 +566,12 @@ class PageParser(object):
             adj = readEncodedNumber(self.fo)
         mode = mode >> 1
         x = []
-        for i in xrange(cnt):
+        for i in range(cnt):
             x.append(readEncodedNumber(self.fo) - adj)
-        for i in xrange(mode):
-            for j in xrange(1, cnt):
+        for i in range(mode):
+            for j in range(1, cnt):
                 x[j] = x[j] + x[j - 1]
-        for i in xrange(cnt):
+        for i in range(cnt):
             result.append(self.formatArg(x[i],argtype))
         return result
 
@@ -844,7 +845,7 @@ def main(argv):
     try:
         opts, args = getopt.getopt(argv[1:], "hd", ["flat-xml"])
 
-    except getopt.GetoptError, err:
+    except getopt.GetoptError as err:
 
         # print help information and exit:
         print(str(err)) # will print something like "option -a not recognized"
index 2b00fe7df1b13491ac7d376b750d5f587a6cfce8..1a08e61acc2f2afd6f64137d0d0711438a897f5a 100644 (file)
@@ -10,6 +10,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
 #
 # Written in 2011 by Paul Durrant
 # Released with unlicense. See http://unlicense.org/
@@ -47,7 +48,7 @@
 from __future__ import with_statement
 from __future__ import print_function
 
-__version__ = '1.01'
+__version__ = '2.0'
 
 import sys, struct, os, traceback
 import zlib
@@ -116,7 +117,7 @@ def unicode_argv():
         argvencoding = sys.stdin.encoding
         if argvencoding == None:
             argvencoding = "utf-8"
-        return [arg if (type(arg) == unicode) else unicode(arg,argvencoding) for arg in sys.argv]
+        return arg
 
 _FILENAME_LEN_OFFSET = 26
 _EXTRA_LEN_OFFSET = 28
index 1dfef423c1995fcb11bfad16e1cd1f718b2100d9..b02a494a6994e3bfddc1490bbce224275ebbc6fa 100644 (file)
@@ -67,8 +67,9 @@
 #       - Ignore sidebars for dictionaries (different format?)
 #  0.22 - Unicode and plugin support, different image folders for PMLZ and source
 #  0.23 - moved unicode_argv call inside main for Windows DeDRM compatibility
+#  1.00 - Added Python 3 compatibility for calibre 5.0
 
-__version__='0.23'
+__version__='1.00'
 
 import sys, re
 import struct, binascii, getopt, zlib, os, os.path, urllib, tempfile, traceback
@@ -88,7 +89,7 @@ class SafeUnbuffered:
         if self.encoding == None:
             self.encoding = "utf-8"
     def write(self, data):
-        if isinstance(data,unicode):
+        if isinstance(data,bytes):
             data = data.encode(self.encoding,"replace")
         self.stream.write(data)
         self.stream.flush()
@@ -126,7 +127,7 @@ def unicode_argv():
             # Remove Python executable and commands if present
             start = argc.value - len(sys.argv)
             return [argv[i] for i in
-                    xrange(start, argc.value)]
+                    range(start, argc.value)]
         # if we don't have any arguments at all, just pass back script name
         # this should never happen
         return [u"mobidedrm.py"]
@@ -134,7 +135,7 @@ def unicode_argv():
         argvencoding = sys.stdin.encoding
         if argvencoding == None:
             argvencoding = "utf-8"
-        return [arg if (type(arg) == unicode) else unicode(arg,argvencoding) for arg in sys.argv]
+        return argv
 
 Des = None
 if iswindows:
@@ -200,7 +201,7 @@ class Sectionizer(object):
     bkType = "Book"
 
     def __init__(self, filename, ident):
-        self.contents = file(filename, 'rb').read()
+        self.contents = open(filename, 'rb').read()
         self.header = self.contents[0:72]
         self.num_sections, = struct.unpack('>H', self.contents[76:78])
         # Dictionary or normal content (TODO: Not hard-coded)
@@ -210,7 +211,7 @@ class Sectionizer(object):
             else:
                 raise ValueError('Invalid file format')
         self.sections = []
-        for i in xrange(self.num_sections):
+        for i in range(self.num_sections):
             offset, a1,a2,a3,a4 = struct.unpack('>LBBBB', self.contents[78+i*8:78+i*8+8])
             flags, val = a1, a2<<16|a3<<8|a4
             self.sections.append( (offset, flags, val) )
@@ -233,7 +234,7 @@ def sanitizeFileName(name):
     # delete control characters
     name = u"".join(char for char in name if ord(char)>=32)
     # white space to single space, delete leading and trailing while space
-    name = re.sub(ur"\s", u" ", name).strip()
+    name = re.sub(r"\s", u" ", name).strip()
     # remove leading dots
     while len(name)>0 and name[0] == u".":
         name = name[1:]
@@ -250,7 +251,7 @@ def fixKey(key):
 def deXOR(text, sp, table):
     r=''
     j = sp
-    for i in xrange(len(text)):
+    for i in range(len(text)):
         r += chr(ord(table[j]) ^ ord(text[i]))
         j = j + 1
         if j == len(table):
@@ -276,7 +277,7 @@ class EreaderProcessor(object):
         def unshuff(data, shuf):
             r = [''] * len(data)
             j = 0
-            for i in xrange(len(data)):
+            for i in range(len(data)):
                 j = (j + shuf) % len(data)
                 r[j] = data[i]
             assert    len("".join(r)) == len(data)
@@ -330,7 +331,7 @@ class EreaderProcessor(object):
         self.flags = struct.unpack('>L', r[4:8])[0]
         reqd_flags = (1<<9) | (1<<7) | (1<<10)
         if (self.flags & reqd_flags) != reqd_flags:
-            print "Flags: 0x%X" % self.flags
+            print("Flags: 0x%X" % self.flags)
             raise ValueError('incompatible eReader file')
         des = Des(fixKey(user_key))
         if version == 259:
@@ -361,7 +362,7 @@ class EreaderProcessor(object):
         sect = self.section_reader(self.first_image_page + i)
         name = sect[4:4+32].strip('\0')
         data = sect[62:]
-        return sanitizeFileName(unicode(name,'windows-1252')), data
+        return sanitizeFileName(name.decode('windows-1252')), data
 
 
     # def getChapterNamePMLOffsetData(self):
@@ -410,7 +411,7 @@ class EreaderProcessor(object):
     def getText(self):
         des = Des(fixKey(self.content_key))
         r = ''
-        for i in xrange(self.num_text_pages):
+        for i in range(self.num_text_pages):
             logging.debug('get page %d', i)
             r += zlib.decompress(des.decrypt(self.section_reader(1 + i)))
 
@@ -422,7 +423,7 @@ class EreaderProcessor(object):
             fnote_ids = deXOR(sect, 0, self.xortable)
             # the remaining records of the footnote sections need to be decoded with the content_key and zlib inflated
             des = Des(fixKey(self.content_key))
-            for i in xrange(1,self.num_footnote_pages):
+            for i in range(1,self.num_footnote_pages):
                 logging.debug('get footnotepage %d', i)
                 id_len = ord(fnote_ids[2])
                 id = fnote_ids[3:3+id_len]
@@ -446,7 +447,7 @@ class EreaderProcessor(object):
             sbar_ids = deXOR(sect, 0, self.xortable)
             # the remaining records of the sidebar sections need to be decoded with the content_key and zlib inflated
             des = Des(fixKey(self.content_key))
-            for i in xrange(1,self.num_sidebar_pages):
+            for i in range(1,self.num_sidebar_pages):
                 id_len = ord(sbar_ids[2])
                 id = sbar_ids[3:3+id_len]
                 smarker = '<sidebar id="%s">\n' % id
@@ -460,7 +461,7 @@ class EreaderProcessor(object):
 def cleanPML(pml):
     # Convert special characters to proper PML code.  High ASCII start at (\x80, \a128) and go up to (\xff, \a255)
     pml2 = pml
-    for k in xrange(128,256):
+    for k in range(128,256):
         badChar = chr(k)
         pml2 = pml2.replace(badChar, '\\a%03d' % k)
     return pml2
@@ -480,26 +481,26 @@ def decryptBook(infile, outpath, make_pmlz, user_key):
     try:
         if not os.path.exists(outdir):
             os.makedirs(outdir)
-        print u"Decoding File"
+        print(u"Decoding File")
         sect = Sectionizer(infile, 'PNRdPPrs')
         er = EreaderProcessor(sect, user_key)
 
         if er.getNumImages() > 0:
-            print u"Extracting images"
+            print(u"Extracting images")
             if not os.path.exists(imagedirpath):
                 os.makedirs(imagedirpath)
-            for i in xrange(er.getNumImages()):
+            for i in range(er.getNumImages()):
                 name, contents = er.getImage(i)
-                file(os.path.join(imagedirpath, name), 'wb').write(contents)
+                open(os.path.join(imagedirpath, name), 'wb').write(contents)
 
-        print u"Extracting pml"
+        print(u"Extracting pml")
         pml_string = er.getText()
         pmlfilename = bookname + ".pml"
-        file(os.path.join(outdir, pmlfilename),'wb').write(cleanPML(pml_string))
+        open(os.path.join(outdir, pmlfilename),'wb').write(cleanPML(pml_string))
         if pmlzname is not None:
             import zipfile
             import shutil
-            print u"Creating PMLZ file {0}".format(os.path.basename(pmlzname))
+            print(u"Creating PMLZ file {0}".format(os.path.basename(pmlzname)))
             myZipFile = zipfile.ZipFile(pmlzname,'w',zipfile.ZIP_STORED, False)
             list = os.listdir(outdir)
             for filename in list:
@@ -518,33 +519,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)
+            print(u"Output is {0}".format(pmlzname))
         else :
-            print u"Output is in {0}".format(outdir)
-        print "done"
-    except ValueError, e:
-        print u"Error: {0}".format(e)
+            print(u"Output is in {0}".format(outdir))
+        print("done")
+    except ValueError as e:
+        print(u"Error: {0}".format(e))
         traceback.print_exc()
         return 1
     return 0
 
 
 def usage():
-    print u"Converts DRMed eReader books to PML Source"
-    print u"Usage:"
-    print u"  erdr2pml [options] infile.pdb [outpath] \"your name\" credit_card_number"
-    print u" "
-    print u"Options: "
-    print u"  -h             prints this message"
-    print u"  -p             create PMLZ instead of source folder"
-    print u"  --make-pmlz    create PMLZ instead of source folder"
-    print u" "
-    print u"Note:"
-    print u"  if outpath is ommitted, creates source in 'infile_Source' folder"
-    print u"  if outpath is ommitted and pmlz option, creates PMLZ 'infile.pmlz'"
-    print u"  if source folder created, images are in infile_img folder"
-    print u"  if pmlz file created, images are in images folder"
-    print u"  It's enough to enter the last 8 digits of the credit card number"
+    print(u"Converts DRMed eReader books to PML Source")
+    print(u"Usage:")
+    print(u"  erdr2pml [options] infile.pdb [outpath] \"your name\" credit_card_number")
+    print(u" ")
+    print(u"Options: ")
+    print(u"  -h             prints this message")
+    print(u"  -p             create PMLZ instead of source folder")
+    print(u"  --make-pmlz    create PMLZ instead of source folder")
+    print(u" ")
+    print(u"Note:")
+    print(u"  if outpath is ommitted, creates source in 'infile_Source' folder")
+    print(u"  if outpath is ommitted and pmlz option, creates PMLZ 'infile.pmlz'")
+    print(u"  if source folder created, images are in infile_img folder")
+    print(u"  if pmlz file created, images are in images folder")
+    print(u"  It's enough to enter the last 8 digits of the credit card number")
     return
 
 def getuser_key(name,cc):
@@ -553,13 +554,13 @@ def getuser_key(name,cc):
     return struct.pack('>LL', binascii.crc32(newname) & 0xffffffff,binascii.crc32(cc[-8:])& 0xffffffff)
 
 def cli_main():
-    print u"eRdr2Pml v{0}. Copyright © 2009–2012 The Dark Reverser et al.".format(__version__)
+    print(u"eRdr2Pml v{0}. Copyright © 2009–2012 The Dark Reverser et al.".format(__version__))
 
     argv=unicode_argv()
     try:
         opts, args = getopt.getopt(argv[1:], "hp", ["make-pmlz"])
-    except getopt.GetoptError, err:
-        print err.args[0]
+    except getopt.GetoptError as err:
+        print(err.args[0])
         usage()
         return 1
     make_pmlz = False
@@ -585,7 +586,7 @@ def cli_main():
     elif len(args)==4:
         infile, outpath, name, cc = args
 
-    print getuser_key(name,cc).encode('hex')
+    print(getuser_key(name,cc).encode('hex'))
 
     return decryptBook(infile, outpath, make_pmlz, getuser_key(name,cc))
 
index a7512afe9b10b62cd8cf8790a35bff0096b701c6..6667e4937c35385297351c607d87ee64636bf608 100644 (file)
@@ -1,5 +1,6 @@
 #! /usr/bin/python
 # vim:ts=4:sw=4:softtabstop=4:smarttab:expandtab
+# Added Python 3 compatibility for calibre 5.0
 
 from __future__ import print_function
 from .convert2xml import encodeNumber
@@ -87,9 +88,9 @@ def readString(file):
 def getMetaArray(metaFile):
     # parse the meta file
     result = {}
-    fo = file(metaFile,'rb')
+    fo = open(metaFile,'rb')
     size = readEncodedNumber(fo)
-    for i in xrange(size):
+    for i in range(size):
         tag = readString(fo)
         value = readString(fo)
         result[tag] = value
@@ -103,10 +104,10 @@ class Dictionary(object):
     def __init__(self, dictFile):
         self.filename = dictFile
         self.size = 0
-        self.fo = file(dictFile,'rb')
+        self.fo = open(dictFile,'rb')
         self.stable = []
         self.size = readEncodedNumber(self.fo)
-        for i in xrange(self.size):
+        for i in range(self.size):
             self.stable.append(self.escapestr(readString(self.fo)))
         self.pos = 0
     def escapestr(self, str):
@@ -142,7 +143,7 @@ class PageDimParser(object):
         else:
             end = min(cnt,end)
         foundat = -1
-        for j in xrange(pos, end):
+        for j in range(pos, end):
             item = docList[j]
             if item.find('=') >= 0:
                 (name, argres) = item.split('=')
@@ -195,7 +196,7 @@ class GParser(object):
     def getData(self, path):
         result = None
         cnt = len(self.flatdoc)
-        for j in xrange(cnt):
+        for j in range(cnt):
             item = self.flatdoc[j]
             if item.find('=') >= 0:
                 (name, argt) = item.split('=')
@@ -207,7 +208,7 @@ class GParser(object):
                 result = argres
                 break
         if (len(argres) > 0) :
-            for j in xrange(0,len(argres)):
+            for j in range(0,len(argres)):
                 argres[j] = int(argres[j])
         return result
     def getGlyphDim(self, gly):
@@ -223,7 +224,7 @@ class GParser(object):
         tx = self.vx[self.gvtx[gly]:self.gvtx[gly+1]]
         ty = self.vy[self.gvtx[gly]:self.gvtx[gly+1]]
         p = 0
-        for k in xrange(self.glen[gly], self.glen[gly+1]):
+        for k in range(self.glen[gly], self.glen[gly+1]):
             if (p == 0):
                 zx = tx[0:self.vlen[k]+1]
                 zy = ty[0:self.vlen[k]+1]
@@ -322,17 +323,17 @@ def generateBook(bookDir, raw, fixedimage):
         imgname = filename.replace('color','img')
         sfile = os.path.join(spath,filename)
         dfile = os.path.join(dpath,imgname)
-        imgdata = file(sfile,'rb').read()
-        file(dfile,'wb').write(imgdata)
+        imgdata = open(sfile,'rb').read()
+        open(dfile,'wb').write(imgdata)
 
     print("Creating cover.jpg")
     isCover = False
     cpath = os.path.join(bookDir,'img')
     cpath = os.path.join(cpath,'img0000.jpg')
     if os.path.isfile(cpath):
-        cover = file(cpath, 'rb').read()
+        cover = open(cpath, 'rb').read()
         cpath = os.path.join(bookDir,'cover.jpg')
-        file(cpath, 'wb').write(cover)
+        open(cpath, 'wb').write(cover)
         isCover = True
 
 
@@ -361,7 +362,7 @@ def generateBook(bookDir, raw, fixedimage):
             mlst.append('<meta name="' + key + '" content="' + meta_array[key] + '" />\n')
         metastr = "".join(mlst)
         mlst = None
-        file(xname, 'wb').write(metastr)
+        open(xname, 'wb').write(metastr)
 
     print('Processing StyleSheet')
 
@@ -424,10 +425,10 @@ def generateBook(bookDir, raw, fixedimage):
 
     # now get the css info
     cssstr , classlst = stylexml2css.convert2CSS(flat_xml, fontsize, ph, pw)
-    file(xname, 'wb').write(cssstr)
+    open(xname, 'wb').write(cssstr)
     if buildXML:
         xname = os.path.join(xmlDir, 'other0000.xml')
-        file(xname, 'wb').write(convert2xml.getXML(dict, otherFile))
+        open(xname, 'wb').write(convert2xml.getXML(dict, otherFile))
 
     print('Processing Glyphs')
     gd = GlyphDict()
@@ -449,10 +450,10 @@ def generateBook(bookDir, raw, fixedimage):
 
         if buildXML:
             xname = os.path.join(xmlDir, filename.replace('.dat','.xml'))
-            file(xname, 'wb').write(convert2xml.getXML(dict, fname))
+            open(xname, 'wb').write(convert2xml.getXML(dict, fname))
 
         gp = GParser(flat_xml)
-        for i in xrange(0, gp.count):
+        for i in range(0, gp.count):
             path = gp.getPath(i)
             maxh, maxw = gp.getGlyphDim(i)
             fullpath = '<path id="gl%d" d="%s" fill="black" /><!-- width=%d height=%d -->\n' % (counter * 256 + i, path, maxw, maxh)
@@ -507,7 +508,7 @@ def generateBook(bookDir, raw, fixedimage):
 
         if buildXML:
             xname = os.path.join(xmlDir, filename.replace('.dat','.xml'))
-            file(xname, 'wb').write(convert2xml.getXML(dict, fname))
+            open(xname, 'wb').write(convert2xml.getXML(dict, fname))
 
         # first get the html
         pagehtml, tocinfo = flatxml2html.convert2HTML(flat_xml, classlst, fname, bookDir, gd, fixedimage)
@@ -518,7 +519,7 @@ def generateBook(bookDir, raw, fixedimage):
     hlst.append('</body>\n</html>\n')
     htmlstr = "".join(hlst)
     hlst = None
-    file(os.path.join(bookDir, htmlFileName), 'wb').write(htmlstr)
+    open(os.path.join(bookDir, htmlFileName), 'wb').write(htmlstr)
 
     print(" ")
     print('Extracting Table of Contents from Amazon OCR')
@@ -564,7 +565,7 @@ def generateBook(bookDir, raw, fixedimage):
     tlst.append('</body>\n')
     tlst.append('</html>\n')
     tochtml = "".join(tlst)
-    file(os.path.join(svgDir, 'toc.xhtml'), 'wb').write(tochtml)
+    open(os.path.join(svgDir, 'toc.xhtml'), 'wb').write(tochtml)
 
 
     # now create index_svg.xhtml that points to all required files
@@ -619,7 +620,7 @@ def generateBook(bookDir, raw, fixedimage):
     slst.append('</body>\n</html>\n')
     svgindex = "".join(slst)
     slst = None
-    file(os.path.join(bookDir, 'index_svg.xhtml'), 'wb').write(svgindex)
+    open(os.path.join(bookDir, 'index_svg.xhtml'), 'wb').write(svgindex)
 
     print(" ")
 
@@ -668,7 +669,7 @@ def generateBook(bookDir, raw, fixedimage):
     olst.append('</package>\n')
     opfstr = "".join(olst)
     olst = None
-    file(opfname, 'wb').write(opfstr)
+    open(opfname, 'wb').write(opfstr)
 
     print('Processing Complete')
 
@@ -694,7 +695,7 @@ def main(argv):
     try:
         opts, args = getopt.getopt(argv[1:], "rh:",["fixed-image"])
 
-    except getopt.GetoptError, err:
+    except getopt.GetoptError as err:
         print(str(err))
         usage()
         return 1
index c16d88bccb2f7e20301caada746a32e71dbbb308..9ec9459f96c0ebb36712b2c65c4bf8faeea4f671 100644 (file)
@@ -2,6 +2,7 @@
 # -*- coding: utf-8 -*-
 
 from __future__ import with_statement
+from __future__ import print_function
 
 # ignobleepub.pyw, version 4.1
 # Copyright © 2009-2010 by i♥cabbages
@@ -37,14 +38,14 @@ from __future__ import with_statement
 #   3.9 - moved unicode_argv call inside main for Windows DeDRM compatibility
 #   4.0 - Work if TkInter is missing
 #   4.1 - Import tkFileDialog, don't assume something else will import it.
+#   5.0 - Added Python 3 compatibility for calibre 5.0
 
 """
 Decrypt Barnes & Noble encrypted ePub books.
 """
-from __future__ import print_function
 
 __license__ = 'GPL v3'
-__version__ = "4.1"
+__version__ = "5.0"
 
 import sys
 import os
@@ -65,7 +66,7 @@ class SafeUnbuffered:
         if self.encoding == None:
             self.encoding = "utf-8"
     def write(self, data):
-        if isinstance(data,unicode):
+        if isinstance(data,bytes):
             data = data.encode(self.encoding,"replace")
         self.stream.write(data)
         self.stream.flush()
@@ -106,13 +107,13 @@ def unicode_argv():
             # Remove Python executable and commands if present
             start = argc.value - len(sys.argv)
             return [argv[i] for i in
-                    xrange(start, argc.value)]
+                    range(start, argc.value)]
         return [u"ineptepub.py"]
     else:
         argvencoding = sys.stdin.encoding
         if argvencoding == None:
             argvencoding = "utf-8"
-        return [arg if (type(arg) == unicode) else unicode(arg,argvencoding) for arg in sys.argv]
+        return argv
 
 
 class IGNOBLEError(Exception):
@@ -434,7 +435,7 @@ def gui_main():
             self.status['text'] = u"Decrypting..."
             try:
                 decrypt_status = decryptBook(userkey, inpath, outpath)
-            except Exception, e:
+            except Exception as e:
                 self.status['text'] = u"Error: {0}".format(e.args[0])
                 return
             if decrypt_status == 0:
index fb7c6a84f44f09a6354b7cdf64b148c85c9f68a2..0b3e60f2379167ada7bfc380e9e74f045311ba58 100644 (file)
@@ -2,9 +2,10 @@
 # -*- coding: utf-8 -*-
 
 from __future__ import with_statement
+from __future__ import print_function
 
 # ignoblekey.py
-# Copyright © 2015 Apprentice Alf and Apprentice Harper
+# Copyright © 2015-2020 Apprentice Alf, Apprentice Harper et al.
 
 # Based on kindlekey.py, Copyright © 2010-2013 by some_updates and Apprentice Alf
 
@@ -14,11 +15,11 @@ from __future__ import with_statement
 # Revision history:
 #   1.0 - Initial release
 #   1.1 - remove duplicates and return last key as single key
+#   2.0 - Added Python 3 compatibility for calibre 5.0
 
 """
 Get Barnes & Noble EPUB user key from nook Studio log file
 """
-from __future__ import print_function
 
 __license__ = 'GPL v3'
 __version__ = "1.1"
@@ -39,7 +40,7 @@ class SafeUnbuffered:
         if self.encoding == None:
             self.encoding = "utf-8"
     def write(self, data):
-        if isinstance(data,unicode):
+        if isinstance(data,bytes):
             data = data.encode(self.encoding,"replace")
         self.stream.write(data)
         self.stream.flush()
@@ -80,7 +81,7 @@ def unicode_argv():
             # Remove Python executable and commands if present
             start = argc.value - len(sys.argv)
             return [argv[i] for i in
-                    xrange(start, argc.value)]
+                    range(start, argc.value)]
         # if we don't have any arguments at all, just pass back script name
         # this should never happen
         return [u"ignoblekey.py"]
@@ -88,7 +89,7 @@ def unicode_argv():
         argvencoding = sys.stdin.encoding
         if argvencoding == None:
             argvencoding = "utf-8"
-        return [arg if (type(arg) == unicode) else unicode(arg,argvencoding) for arg in sys.argv]
+        return argv
 
 class DrmException(Exception):
     pass
@@ -210,7 +211,7 @@ def getkey(outpath, files=[]):
     if len(keys) > 0:
         if not os.path.isdir(outpath):
             outfile = outpath
-            with file(outfile, 'w') as keyfileout:
+            with open(outfile, 'w') as keyfileout:
                 keyfileout.write(keys[-1])
             print(u"Saved a key to {0}".format(outfile))
         else:
@@ -221,7 +222,7 @@ def getkey(outpath, files=[]):
                     outfile = os.path.join(outpath,u"nookkey{0:d}.b64".format(keycount))
                     if not os.path.exists(outfile):
                         break
-                with file(outfile, 'w') as keyfileout:
+                with open(outfile, 'w') as keyfileout:
                     keyfileout.write(key)
                 print(u"Saved a key to {0}".format(outfile))
         return True
@@ -244,7 +245,7 @@ def cli_main():
 
     try:
         opts, args = getopt.getopt(argv[1:], "hk:")
-    except getopt.GetoptError, err:
+    except getopt.GetoptError as err:
         print(u"Error in options or arguments: {0}".format(err.args[0]))
         usage(progname)
         sys.exit(2)
@@ -315,11 +316,11 @@ def gui_main():
                 if not os.path.exists(outfile):
                     break
 
-            with file(outfile, 'w') as keyfileout:
+            with open(outfile, 'w') as keyfileout:
                 keyfileout.write(key)
             success = True
             tkMessageBox.showinfo(progname, u"Key successfully retrieved to {0}".format(outfile))
-    except DrmException, e:
+    except DrmException as e:
         tkMessageBox.showerror(progname, u"Error: {0}".format(str(e)))
     except Exception:
         root.wm_state('normal')
index dd714fcf40210852a2f88bbcec3eeab4c1cca626..ffaf153c625a2fcceade586e7d46feb96d402d3b 100644 (file)
@@ -2,9 +2,10 @@
 # -*- coding: utf-8 -*-
 
 from __future__ import with_statement
+from __future__ import print_function
 
-# ignoblekeyfetch.pyw, version 1.1
-# Copyright © 2015 Apprentice Harper
+# ignoblekeyfetch.pyw, version 2.0
+# Copyright © 2015-2020 Apprentice Harper et al.
 
 # Released under the terms of the GNU General Public Licence, version 3
 # <http://www.gnu.org/licenses/>
@@ -24,11 +25,11 @@ from __future__ import with_statement
 # Revision history:
 #   1.0 - Initial  version
 #   1.1 - Try second URL if first one fails
+#   2.0 - Added Python 3 compatibility for calibre 5.0
 
 """
 Fetch Barnes & Noble EPUB user key from B&N servers using email and password
 """
-from __future__ import print_function
 
 __license__ = 'GPL v3'
 __version__ = "1.1"
@@ -46,7 +47,7 @@ class SafeUnbuffered:
         if self.encoding == None:
             self.encoding = "utf-8"
     def write(self, data):
-        if isinstance(data,unicode):
+        if isinstance(data,bytes):
             data = data.encode(self.encoding,"replace")
         self.stream.write(data)
         self.stream.flush()
@@ -87,7 +88,7 @@ def unicode_argv():
             # Remove Python executable and commands if present
             start = argc.value - len(sys.argv)
             return [argv[i] for i in
-                    xrange(start, argc.value)]
+                    range(start, argc.value)]
         # if we don't have any arguments at all, just pass back script name
         # this should never happen
         return [u"ignoblekeyfetch.py"]
@@ -95,7 +96,7 @@ def unicode_argv():
         argvencoding = sys.stdin.encoding
         if argvencoding == None:
             argvencoding = "utf-8"
-        return [arg if (type(arg) == unicode) else unicode(arg,argvencoding) for arg in sys.argv]
+        return argv
 
 
 class IGNOBLEError(Exception):
@@ -103,9 +104,9 @@ class IGNOBLEError(Exception):
 
 def fetch_key(email, password):
     # change email and password to utf-8 if unicode
-    if type(email)==unicode:
+    if type(email)==bytes:
         email = email.encode('utf-8')
-    if type(password)==unicode:
+    if type(password)==bytes:
         password = password.encode('utf-8')
 
     import random
@@ -236,7 +237,7 @@ def gui_main():
             self.status['text'] = u"Fetching..."
             try:
                 userkey = fetch_key(email, password)
-            except Exception, e:
+            except Exception as e:
                 self.status['text'] = u"Error: {0}".format(e.args[0])
                 return
             if len(userkey) == 28:
index 5ddcd80bfea54324aafcf321c0a5f1d580018d04..3582f247681facdda85be853a4e2bd9a97eac729 100644 (file)
@@ -2,15 +2,14 @@
 # -*- coding: utf-8 -*-
 
 from __future__ import with_statement
+from __future__ import print_function
 
-# ignoblekeygen.pyw, version 2.5
-# Copyright © 2009-2010 i♥cabbages
+# ignoblekeygen.pyw
+# Copyright © 2009-2020 i♥cabbages, Apprentice Harper et al.
 
 # Released under the terms of the GNU General Public Licence, version 3
 # <http://www.gnu.org/licenses/>
 
-# Modified 2010–2013 by some_updates, DiapDealer and Apprentice Alf
-
 # Windows users: Before running this program, you must first install Python.
 #   We recommend ActiveState Python 2.7.X for Windows (x86) from
 #   http://www.activestate.com/activepython/downloads.
@@ -34,14 +33,14 @@ from __future__ import with_statement
 #   2.6 - moved unicode_argv call inside main for Windows DeDRM compatibility
 #   2.7 - Work if TkInter is missing
 #   2.8 - Fix bug in stand-alone use (import tkFileDialog)
+#   3.0 - Added Python 3 compatibility for calibre 5.0
 
 """
 Generate Barnes & Noble EPUB user key from name and credit card number.
 """
-from __future__ import print_function
 
 __license__ = 'GPL v3'
-__version__ = "2.8"
+__version__ = "3.0"
 
 import sys
 import os
@@ -57,7 +56,7 @@ class SafeUnbuffered:
         if self.encoding == None:
             self.encoding = "utf-8"
     def write(self, data):
-        if isinstance(data,unicode):
+        if isinstance(data,bytes):
             data = data.encode(self.encoding,"replace")
         self.stream.write(data)
         self.stream.flush()
@@ -98,7 +97,7 @@ def unicode_argv():
             # Remove Python executable and commands if present
             start = argc.value - len(sys.argv)
             return [argv[i] for i in
-                    xrange(start, argc.value)]
+                    range(start, argc.value)]
         # if we don't have any arguments at all, just pass back script name
         # this should never happen
         return [u"ignoblekeygen.py"]
@@ -106,7 +105,7 @@ def unicode_argv():
         argvencoding = sys.stdin.encoding
         if argvencoding == None:
             argvencoding = "utf-8"
-        return [arg if (type(arg) == unicode) else unicode(arg,argvencoding) for arg in sys.argv]
+        return argv
 
 
 class IGNOBLEError(Exception):
@@ -199,9 +198,9 @@ def normalize_name(name):
 
 def generate_key(name, ccn):
     # remove spaces and case from name and CC numbers.
-    if type(name)==unicode:
+    if type(name)==bytes:
         name = name.encode('utf-8')
-    if type(ccn)==unicode:
+    if type(ccn)==bytes:
         ccn = ccn.encode('utf-8')
 
     name = normalize_name(name) + '\x00'
@@ -306,7 +305,7 @@ def gui_main():
             self.status['text'] = u"Generating..."
             try:
                 userkey = generate_key(name, ccn)
-            except Exception, e:
+            except Exception as e:
                 self.status['text'] = u"Error: (0}".format(e.args[0])
                 return
             open(keypath,'wb').write(userkey)
index cfbd0846a5da01e6c6c84c9a1e18294ad521e338..c0e4c393bd878bd2fe3ffaf0b2b249197dcd7e0a 100644 (file)
@@ -2,18 +2,13 @@
 # -*- coding: utf-8 -*-
 
 from __future__ import with_statement
-from __future__ import absolute_import
-from __future__ import print_function
 
-# ineptepub.pyw, version 6.6
-# Copyright © 2009-2020 by Apprentice Harper et al.
+# ineptepub.pyw
+# Copyright © 2009-2020 by i♥cabbages, Apprentice Harper et al.
 
 # Released under the terms of the GNU General Public Licence, version 3
 # <http://www.gnu.org/licenses/>
 
-# Original script by i♥cabbages
-# Modified 2010–2013 by some_updates, DiapDealer and Apprentice Alf
-# Modified 2015–2020 by Apprentice Harper et al.
 
 # Revision history:
 #   1 - Initial release
@@ -36,17 +31,16 @@ from __future__ import print_function
 #   6.4 - Remove erroneous check on DER file sanity
 #   6.5 - Completely remove erroneous check on DER file sanity
 #   6.6 - Import tkFileDialog, don't assume something else will import it.
-#   6.7 - Add Python 3 compatibility while still working with Python 2.7
+#   7.0 - Add Python 3 compatibility for calibre 5.0
 
 """
 Decrypt Adobe Digital Editions encrypted ePub books.
 """
 
 __license__ = 'GPL v3'
-__version__ = "6.7"
+__version__ = "7.0"
 
-import six
-from six.moves import range
+import codecs
 import sys
 import os
 import traceback
@@ -55,7 +49,6 @@ import zipfile
 from zipfile import ZipInfo, ZipFile, ZIP_STORED, ZIP_DEFLATED
 from contextlib import closing
 import xml.etree.ElementTree as etree
-import base64
 
 # Wrap a stream so that output gets flushed immediately
 # and also make sure that any unicode strings get
@@ -67,7 +60,7 @@ class SafeUnbuffered:
         if self.encoding == None:
             self.encoding = "utf-8"
     def write(self, data):
-        if isinstance(data,six.text_type):
+        if isinstance(data,bytes):
             data = data.encode(self.encoding,"replace")
         self.stream.write(data)
         self.stream.flush()
@@ -114,7 +107,7 @@ def unicode_argv():
         argvencoding = sys.stdin.encoding
         if argvencoding == None:
             argvencoding = "utf-8"
-        return [arg if (type(arg) == six.text_type) else six.text_type(arg,argvencoding) for arg in sys.argv]
+        return argv
 
 
 class ADEPTError(Exception):
@@ -418,9 +411,7 @@ def decryptBook(userkey, inpath, outpath):
             if len(bookkey) != 172:
                 print(u"{0:s} is not a secure Adobe Adept ePub.".format(os.path.basename(inpath)))
                 return 1
-            bookkey = bookkey.encode('ascii')
-            bookkey = base64.b64decode(bookkey)
-            bookkey = rsa.decrypt(bookkey)
+            bookkey = rsa.decrypt(codecs.decode(bookkey.encode('ascii'), 'base64'))
             # Padded as per RSAES-PKCS1-v1_5
             if bookkey[-17] != '\x00' and bookkey[-17] != 0:
                 print(u"Could not decrypt {0:s}. Wrong key".format(os.path.basename(inpath)))
@@ -486,79 +477,79 @@ def cli_main():
 
 def gui_main():
     try:
-        import six.moves.tkinter
-        import six.moves.tkinter_constants
-        import six.moves.tkinter_filedialog
-        import six.moves.tkinter_messagebox
+        import Tkinter
+        import Tkconstants
+        import tkFileDialog
+        import tkMessageBox
         import traceback
     except:
         return cli_main()
 
-    class DecryptionDialog(six.moves.tkinter.Frame):
+    class DecryptionDialog(Tkinter.Frame):
         def __init__(self, root):
-            six.moves.tkinter.Frame.__init__(self, root, border=5)
-            self.status = six.moves.tkinter.Label(self, text=u"Select files for decryption")
-            self.status.pack(fill=six.moves.tkinter_constants.X, expand=1)
-            body = six.moves.tkinter.Frame(self)
-            body.pack(fill=six.moves.tkinter_constants.X, expand=1)
-            sticky = six.moves.tkinter_constants.E + six.moves.tkinter_constants.W
+            Tkinter.Frame.__init__(self, root, border=5)
+            self.status = Tkinter.Label(self, text=u"Select files for decryption")
+            self.status.pack(fill=Tkconstants.X, expand=1)
+            body = Tkinter.Frame(self)
+            body.pack(fill=Tkconstants.X, expand=1)
+            sticky = Tkconstants.E + Tkconstants.W
             body.grid_columnconfigure(1, weight=2)
-            six.moves.tkinter.Label(body, text=u"Key file").grid(row=0)
-            self.keypath = six.moves.tkinter.Entry(body, width=30)
+            Tkinter.Label(body, text=u"Key file").grid(row=0)
+            self.keypath = Tkinter.Entry(body, width=30)
             self.keypath.grid(row=0, column=1, sticky=sticky)
             if os.path.exists(u"adeptkey.der"):
                 self.keypath.insert(0, u"adeptkey.der")
-            button = six.moves.tkinter.Button(body, text=u"...", command=self.get_keypath)
+            button = Tkinter.Button(body, text=u"...", command=self.get_keypath)
             button.grid(row=0, column=2)
-            six.moves.tkinter.Label(body, text=u"Input file").grid(row=1)
-            self.inpath = six.moves.tkinter.Entry(body, width=30)
+            Tkinter.Label(body, text=u"Input file").grid(row=1)
+            self.inpath = Tkinter.Entry(body, width=30)
             self.inpath.grid(row=1, column=1, sticky=sticky)
-            button = six.moves.tkinter.Button(body, text=u"...", command=self.get_inpath)
+            button = Tkinter.Button(body, text=u"...", command=self.get_inpath)
             button.grid(row=1, column=2)
-            six.moves.tkinter.Label(body, text=u"Output file").grid(row=2)
-            self.outpath = six.moves.tkinter.Entry(body, width=30)
+            Tkinter.Label(body, text=u"Output file").grid(row=2)
+            self.outpath = Tkinter.Entry(body, width=30)
             self.outpath.grid(row=2, column=1, sticky=sticky)
-            button = six.moves.tkinter.Button(body, text=u"...", command=self.get_outpath)
+            button = Tkinter.Button(body, text=u"...", command=self.get_outpath)
             button.grid(row=2, column=2)
-            buttons = six.moves.tkinter.Frame(self)
+            buttons = Tkinter.Frame(self)
             buttons.pack()
-            botton = six.moves.tkinter.Button(
+            botton = Tkinter.Button(
                 buttons, text=u"Decrypt", width=10, command=self.decrypt)
-            botton.pack(side=six.moves.tkinter_constants.LEFT)
-            six.moves.tkinter.Frame(buttons, width=10).pack(side=six.moves.tkinter_constants.LEFT)
-            button = six.moves.tkinter.Button(
+            botton.pack(side=Tkconstants.LEFT)
+            Tkinter.Frame(buttons, width=10).pack(side=Tkconstants.LEFT)
+            button = Tkinter.Button(
                 buttons, text=u"Quit", width=10, command=self.quit)
-            button.pack(side=six.moves.tkinter_constants.RIGHT)
+            button.pack(side=Tkconstants.RIGHT)
 
         def get_keypath(self):
-            keypath = six.moves.tkinter_filedialog.askopenfilename(
+            keypath = tkFileDialog.askopenfilename(
                 parent=None, title=u"Select Adobe Adept \'.der\' key file",
                 defaultextension=u".der",
                 filetypes=[('Adobe Adept DER-encoded files', '.der'),
                            ('All Files', '.*')])
             if keypath:
                 keypath = os.path.normpath(keypath)
-                self.keypath.delete(0, six.moves.tkinter_constants.END)
+                self.keypath.delete(0, Tkconstants.END)
                 self.keypath.insert(0, keypath)
             return
 
         def get_inpath(self):
-            inpath = six.moves.tkinter_filedialog.askopenfilename(
+            inpath = tkFileDialog.askopenfilename(
                 parent=None, title=u"Select ADEPT-encrypted ePub file to decrypt",
                 defaultextension=u".epub", filetypes=[('ePub files', '.epub')])
             if inpath:
                 inpath = os.path.normpath(inpath)
-                self.inpath.delete(0, six.moves.tkinter_constants.END)
+                self.inpath.delete(0, Tkconstants.END)
                 self.inpath.insert(0, inpath)
             return
 
         def get_outpath(self):
-            outpath = six.moves.tkinter_filedialog.asksaveasfilename(
+            outpath = tkFileDialog.asksaveasfilename(
                 parent=None, title=u"Select unencrypted ePub file to produce",
                 defaultextension=u".epub", filetypes=[('ePub files', '.epub')])
             if outpath:
                 outpath = os.path.normpath(outpath)
-                self.outpath.delete(0, six.moves.tkinter_constants.END)
+                self.outpath.delete(0, Tkconstants.END)
                 self.outpath.insert(0, outpath)
             return
 
@@ -590,11 +581,11 @@ def gui_main():
             else:
                 self.status['text'] = u"The was an error decrypting the file."
 
-    root = six.moves.tkinter.Tk()
+    root = Tkinter.Tk()
     root.title(u"Adobe Adept ePub Decrypter v.{0}".format(__version__))
     root.resizable(True, False)
     root.minsize(300, 0)
-    DecryptionDialog(root).pack(fill=six.moves.tkinter_constants.X, expand=1)
+    DecryptionDialog(root).pack(fill=Tkconstants.X, expand=1)
     root.mainloop()
     return 0
 
index 17db7e9a5a7ff40d07826dc03baa504648e331a2..c29a536752bb10945380261e8656e6ceafe4b9cf 100644 (file)
@@ -4,13 +4,11 @@
 from __future__ import with_statement
 
 # ineptpdf.py
-# Copyright © 2009-2017 by Apprentice Harper et al.
+# Copyright © 2009-2020 by i♥cabbages, Apprentice Harper et al.
 
 # Released under the terms of the GNU General Public Licence, version 3
 # <http://www.gnu.org/licenses/>
 
-# Modified 2010–2012 by some_updates, DiapDealer and Apprentice Alf
-# Modified 2015-2017 by Apprentice Harper et al.
 
 # Revision history:
 #   1 - Initial release
@@ -49,14 +47,14 @@ from __future__ import with_statement
 #   8.0.4 - Completely remove erroneous check on DER file sanity
 #   8.0.5 - Do not process DRM-free documents
 #   8.0.6 - Replace use of float by Decimal for greater precision, and import tkFileDialog
-
+#   9.0.0 - Add Python 3 compatibility for calibre 5
 
 """
 Decrypts Adobe ADEPT-encrypted PDF files.
 """
 
 __license__ = 'GPL v3'
-__version__ = "8.0.6"
+__version__ = "9.0.0"
 
 import sys
 import os
@@ -64,8 +62,8 @@ import re
 import zlib
 import struct
 import hashlib
-from decimal import *
-from itertools import chain, islice
+from decimal import Decimal
+import itertools
 import xml.etree.ElementTree as etree
 
 # Wrap a stream so that output gets flushed immediately
@@ -75,10 +73,10 @@ class SafeUnbuffered:
     def __init__(self, stream):
         self.stream = stream
         self.encoding = stream.encoding
-        if self.encoding == None:
+        if self.encoding is None:
             self.encoding = "utf-8"
     def write(self, data):
-        if isinstance(data,unicode):
+        if isinstance(data,bytes):
             data = data.encode(self.encoding,"replace")
         self.stream.write(data)
         self.stream.flush()
@@ -116,13 +114,13 @@ def unicode_argv():
             # Remove Python executable and commands if present
             start = argc.value - len(sys.argv)
             return [argv[i] for i in
-                    xrange(start, argc.value)]
+                    range(start, argc.value)]
         return [u"ineptpdf.py"]
     else:
         argvencoding = sys.stdin.encoding
-        if argvencoding == None:
+        if argvencoding is None:
             argvencoding = "utf-8"
-        return [arg if (type(arg) == unicode) else unicode(arg,argvencoding) for arg in sys.argv]
+        return sys.argv
 
 
 class ADEPTError(Exception):
@@ -378,12 +376,12 @@ def _load_crypto_pycrypto():
     class RSA(object):
         def __init__(self, der):
             key = ASN1Parser([ord(x) for x in der])
-            key = [key.getChild(x).value for x in xrange(1, 4)]
+            key = [key.getChild(x).value for x in range(1, 4)]
             key = [self.bytesToNumber(v) for v in key]
             self._rsa = _RSA.construct(key)
 
         def bytesToNumber(self, bytes):
-            total = 0L
+            total = 0
             for byte in bytes:
                 total = (total << 8) + byte
             return total
@@ -411,7 +409,10 @@ ARC4, RSA, AES = _load_crypto()
 try:
     from cStringIO import StringIO
 except ImportError:
-    from StringIO import StringIO
+    try:
+        from StringIO import StringIO
+    except ImportError:
+        from io import StringIO
 
 
 # Do we generate cross reference streams on output?
@@ -444,11 +445,11 @@ def nunpack(s, default=0):
     if not l:
         return default
     elif l == 1:
-        return ord(s)
+        return s
     elif l == 2:
         return struct.unpack('>H', s)[0]
     elif l == 3:
-        return struct.unpack('>L', '\x00'+s)[0]
+        return struct.unpack('>L', b'\x00'+s)[0]
     elif l == 4:
         return struct.unpack('>L', s)[0]
     else:
@@ -487,9 +488,9 @@ class PSLiteral(PSObject):
         name = []
         for char in self.name:
             if not char.isalnum():
-                char = '#%02x' % ord(char)
+                char = b'#%02x' % char
             name.append(char)
-        return '/%s' % ''.join(name)
+        return b'/%s' % ''.join(name)
 
 # PSKeyword
 class PSKeyword(PSObject):
@@ -716,7 +717,7 @@ class PSBaseParser(object):
         except ValueError:
             pass
         return (self.parse_main, j)
-        
+
     def parse_decimal(self, s, i):
         m = END_NUMBER.search(s, i)
         if not m:
@@ -949,7 +950,7 @@ class PSStackParser(PSBaseParser):
                 try:
                     (pos, objs) = self.end_type('d')
                     if len(objs) % 2 != 0:
-                        print "Incomplete dictionary construct"
+                        print("Incomplete dictionary construct")
                         objs.append("") # this isn't necessary.
                         # temporary fix. is this due to rental books?
                         # raise PSSyntaxError(
@@ -1106,18 +1107,18 @@ def stream_value(x):
 # ascii85decode(data)
 def ascii85decode(data):
     n = b = 0
-    out = ''
+    out = b''
     for c in data:
-        if '!' <= c and c <= 'u':
+        if b'!' <= c and c <= b'u':
             n += 1
-            b = b*85+(ord(c)-33)
+            b = b*85+(c-33)
             if n == 5:
                 out += struct.pack('>L',b)
                 n = b = 0
-        elif c == 'z':
+        elif c == b'z':
             assert n == 0
-            out += '\0\0\0\0'
-        elif c == '~':
+            out += b'\0\0\0\0'
+        elif c == b'~':
             if n:
                 for _ in range(5-n):
                     b = b*85+84
@@ -1138,7 +1139,7 @@ class PDFStream(PDFObject):
                 cutdiv = len(rawdata) // 16
                 rawdata = rawdata[:16*cutdiv]
         else:
-            if eol in ('\r', '\n', '\r\n'):
+            if eol in (b'\r', b'\n', b'\r\n'):
                 rawdata = rawdata[:length]
 
         self.dic = dic
@@ -1206,14 +1207,14 @@ class PDFStream(PDFObject):
                         raise PDFValueError(
                             'Columns undefined for predictor=12')
                     columns = int_value(params['Columns'])
-                    buf = ''
-                    ent0 = '\x00' * columns
-                    for i in xrange(0, len(data), columns+1):
+                    buf = b''
+                    ent0 = b'\x00' * columns
+                    for i in range(0, len(data), columns+1):
                         pred = data[i]
                         ent1 = data[i+1:i+1+columns]
-                        if pred == '\x02':
-                            ent1 = ''.join(chr((ord(a)+ord(b)) & 255) \
-                                               for (a,b) in zip(ent0,ent1))
+                        if pred == b'\x02':
+                            ent1 = ''.join(bytes([(a+b) & 255]) \
+                                           for (a,b) in zip(ent0,ent1))
                         buf += ent1
                         ent0 = ent1
                     data = buf
@@ -1290,7 +1291,7 @@ class PDFXRef(object):
                 (start, nobjs) = map(int, f)
             except ValueError:
                 raise PDFNoValidXRef('Invalid line: %r: line=%r' % (parser, line))
-            for objid in xrange(start, start+nobjs):
+            for objid in range(start, start+nobjs):
                 try:
                     (_, line) = parser.nextline()
                 except PSEOF:
@@ -1342,7 +1343,7 @@ class PDFXRefStream(object):
 
     def objids(self):
         for first, size in self.index:
-            for objid in xrange(first, first + size):
+            for objid in range(first, first + size):
                 yield objid
 
     def load(self, parser, debug=0):
@@ -1355,8 +1356,8 @@ class PDFXRefStream(object):
             raise PDFNoValidXRef('Invalid PDF stream spec.')
         size = stream.dic['Size']
         index = stream.dic.get('Index', (0,size))
-        self.index = zip(islice(index, 0, None, 2),
-                         islice(index, 1, None, 2))
+        self.index = zip(itertools.islice(index, 0, None, 2),
+                         itertools.islice(index, 1, None, 2))
         (self.fl1, self.fl2, self.fl3) = stream.dic['W']
         self.data = stream.get_data()
         self.entlen = self.fl1+self.fl2+self.fl3
@@ -1506,10 +1507,10 @@ class PDFDocument(object):
         if plaintext[-16:] != 16 * chr(16):
             raise ADEPTError('Offlinekey cannot be decrypted, aborting ...')
         pdrlpol = AES.new(plaintext[16:32],AES.MODE_CBC,edclist[2].decode('base64')).decrypt(pdrlpol)
-        if ord(pdrlpol[-1]) < 1 or ord(pdrlpol[-1]) > 16:
+        if pdrlpol[-1] < 1 or pdrlpol[-1] > 16:
             raise ADEPTError('Could not decrypt PDRLPol, aborting ...')
         else:
-            cutter = -1 * ord(pdrlpol[-1])
+            cutter = -1 * pdrlpol[-1]
             pdrlpol = pdrlpol[:cutter]
         return plaintext[:16]
 
@@ -1551,7 +1552,7 @@ class PDFDocument(object):
             hash.update('ffffffff'.decode('hex'))
         if 5 <= R:
             # 8
-            for _ in xrange(50):
+            for _ in range(50):
                 hash = hashlib.md5(hash.digest()[:length/8])
         key = hash.digest()[:length/8]
         if R == 2:
@@ -1562,8 +1563,8 @@ class PDFDocument(object):
             hash = hashlib.md5(self.PASSWORD_PADDING) # 2
             hash.update(docid[0]) # 3
             x = ARC4.new(key).decrypt(hash.digest()[:16]) # 4
-            for i in xrange(1,19+1):
-                k = ''.join( chr(ord(c) ^ i) for c in key )
+            for i in range(1,19+1):
+                k = ''.join(bytes([c ^ i]) for c in key )
                 x = ARC4.new(k).decrypt(x)
             u1 = x+x # 32bytes total
         if R == 2:
@@ -1585,9 +1586,9 @@ class PDFDocument(object):
         if V != 4:
             self.decipher = self.decipher_rc4  # XXX may be AES
         # aes
-        elif V == 4 and Length == 128:
-            elf.decipher = self.decipher_aes
-        elif V == 4 and Length == 256:
+        elif V == 4 and length == 128:
+            self.decipher = self.decipher_aes
+        elif V == 4 and length == 256:
             raise PDFNotImplementedError('AES256 encryption is currently unsupported')
         self.ready = True
         return
@@ -1616,18 +1617,18 @@ class PDFDocument(object):
                 else:
                     V = 2
             elif len(bookkey) == length + 1:
-                V = ord(bookkey[0])
+                V = bookkey[0]
                 bookkey = bookkey[1:]
             else:
-                print "ebx_V is %d  and ebx_type is %d" % (ebx_V, ebx_type)
-                print "length is %d and len(bookkey) is %d" % (length, len(bookkey))
-                print "bookkey[0] is %d" % ord(bookkey[0])
+                print("ebx_V is %d  and ebx_type is %d" % (ebx_V, ebx_type))
+                print("length is %d and len(bookkey) is %d" % (length, len(bookkey)))
+                print("bookkey[0] is %d" % bookkey[0])
                 raise ADEPTError('error decrypting book session key - mismatched length')
         else:
             # proper length unknown try with whatever you have
-            print "ebx_V is %d  and ebx_type is %d" % (ebx_V, ebx_type)
-            print "length is %d and len(bookkey) is %d" % (length, len(bookkey))
-            print "bookkey[0] is %d" % ord(bookkey[0])
+            print("ebx_V is %d  and ebx_type is %d" % (ebx_V, ebx_type))
+            print("length is %d and len(bookkey) is %d" % (length, len(bookkey)))
+            print("bookkey[0] is %d" % bookkey[0])
             if ebx_V == 3:
                 V = 3
             else:
@@ -1671,7 +1672,7 @@ class PDFDocument(object):
         data = data[16:]
         plaintext = AES.new(key,AES.MODE_CBC,ivector).decrypt(data)
         # remove pkcs#5 aes padding
-        cutter = -1 * ord(plaintext[-1])
+        cutter = -1 * plaintext[-1]
         #print cutter
         plaintext = plaintext[:cutter]
         return plaintext
@@ -1682,7 +1683,7 @@ class PDFDocument(object):
         data = data[16:]
         plaintext = AES.new(key,AES.MODE_CBC,ivector).decrypt(data)
         # remove pkcs#5 aes padding
-        cutter = -1 * ord(plaintext[-1])
+        cutter = -1 * plaintext[-1]
         #print cutter
         plaintext = plaintext[:cutter]
         return plaintext
@@ -2026,7 +2027,7 @@ class PDFSerializer(object):
         if not gen_xref_stm:
             self.write('xref\n')
             self.write('0 %d\n' % (maxobj + 1,))
-            for objid in xrange(0, maxobj + 1):
+            for objid in range(0, maxobj + 1):
                 if objid in xrefs:
                     # force the genno to be 0
                     self.write("%010d 00000 n \n" % xrefs[objid][0])
@@ -2135,7 +2136,7 @@ class PDFSerializer(object):
             if self.last.isalnum():
                 self.write(' ')
             self.write(str(obj).lower())
-        elif isinstance(obj, (int, long)):
+        elif isinstance(obj, int):
             if self.last.isalnum():
                 self.write(' ')
             self.write(str(obj))
@@ -2189,8 +2190,8 @@ def decryptBook(userkey, inpath, outpath):
             # help construct to make sure the method runs to the end
             try:
                 serializer.dump(outf)
-            except Exception, e:
-                print u"error writing pdf: {0}".format(e.args[0])
+            except Exception as e:
+                print(u"error writing pdf: {0}".format(e.args[0]))
                 return 2
     return 0
 
@@ -2201,13 +2202,13 @@ def cli_main():
     argv=unicode_argv()
     progname = os.path.basename(argv[0])
     if len(argv) != 4:
-        print u"usage: {0} <keyfile.der> <inbook.pdf> <outbook.pdf>".format(progname)
+        print(u"usage: {0} <keyfile.der> <inbook.pdf> <outbook.pdf>".format(progname))
         return 1
     keypath, inpath, outpath = argv[1:]
     userkey = open(keypath,'rb').read()
     result = decryptBook(userkey, inpath, outpath)
     if result == 0:
-        print u"Successfully decrypted {0:s} as {1:s}".format(os.path.basename(inpath),os.path.basename(outpath))
+        print(u"Successfully decrypted {0:s} as {1:s}".format(os.path.basename(inpath),os.path.basename(outpath)))
     return result
 
 
@@ -2309,7 +2310,7 @@ def gui_main():
             self.status['text'] = u"Decrypting..."
             try:
                 decrypt_status = decryptBook(userkey, inpath, outpath)
-            except Exception, e:
+            except Exception as e:
                 self.status['text'] = u"Error; {0}".format(e.args[0])
                 return
             if decrypt_status == 0:
index 0ea385993dab1d15676e8c74479d64d09e5d4d58..f385cc3cb1ceaa5d23b6922c8f66c55ca000d68a 100644 (file)
@@ -7,7 +7,7 @@ from __future__ import with_statement
 # Copyright © 2013-2020 Apprentice Harper et al.
 
 __license__ = 'GPL v3'
-__version__ = '2.0'
+__version__ = '3.0'
 
 # Revision history:
 #  Pascal implementation by lulzkabulz.
@@ -17,7 +17,7 @@ __version__ = '2.0'
 #  1.2   - Added pylzma import fallback
 #  1.3   - Fixed lzma support for calibre 4.6+
 #  2.0   - VoucherEnvelope v2/v3 support by apprenticesakuya.
-
+#  3.0   - Added Python 3 compatibility for calibre 5.0
 
 """
 Decrypt Kindle KFX files.
@@ -33,7 +33,10 @@ import struct
 try:
     from cStringIO import StringIO
 except ImportError:
-    from StringIO import StringIO
+    try:
+        from StringIO import StringIO
+    except ImportError:
+        from io import StringIO
 
 from Crypto.Cipher import AES
 from Crypto.Util.py3compat import bchr, bord
index 3ac753e41d8dec6f56c95d6156bf8e29beb9e3ab..eaaa43b10c0fe8fa46d918a93c6d957210fd2380 100644 (file)
@@ -4,10 +4,10 @@
 from __future__ import with_statement
 
 # k4mobidedrm.py
-# Copyright © 2008-2019 by Apprentice Harper et al.
+# Copyright © 2008-2020 by Apprentice Harper et al.
 
 __license__ = 'GPL v3'
-__version__ = '5.7'
+__version__ = '6.0'
 
 # Engine to remove drm from Kindle and Mobipocket ebooks
 # for personal use for archiving and converting your ebooks
@@ -62,6 +62,8 @@ __version__ = '5.7'
 #  5.5 - Added GPL v3 licence explicitly.
 #  5.6 - Invoke KFXZipBook to handle zipped KFX files
 #  5.7 - Revamp cleanup_name
+#  6.0 - Added Python 3 compatibility for calibre 5.0
+
 
 import sys, os, re
 import csv
@@ -69,7 +71,7 @@ import getopt
 import re
 import traceback
 import time
-import htmlentitydefs
+import html.entities
 import json
 
 class DrmException(Exception):
@@ -103,7 +105,7 @@ class SafeUnbuffered:
         if self.encoding == None:
             self.encoding = "utf-8"
     def write(self, data):
-        if isinstance(data,unicode):
+        if isinstance(data,bytes):
             data = data.encode(self.encoding,"replace")
         self.stream.write(data)
         self.stream.flush()
@@ -141,7 +143,7 @@ def unicode_argv():
             # Remove Python executable and commands if present
             start = argc.value - len(sys.argv)
             return [argv[i] for i in
-                    xrange(start, argc.value)]
+                    range(start, argc.value)]
         # if we don't have any arguments at all, just pass back script name
         # this should never happen
         return [u"mobidedrm.py"]
@@ -149,7 +151,7 @@ def unicode_argv():
         argvencoding = sys.stdin.encoding
         if argvencoding == None:
             argvencoding = "utf-8"
-        return [arg if (type(arg) == unicode) else unicode(arg,argvencoding) for arg in sys.argv]
+        return argv
 
 # cleanup unicode filenames
 # borrowed from calibre from calibre/src/calibre/__init__.py
@@ -161,7 +163,7 @@ 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"")
     # white space to single space, delete leading and trailing while space
-    name = re.sub(ur"\s", u" ", name).strip()
+    name = re.sub(r"\s", u" ", name).strip()
     # delete control characters
     name = u"".join(char for char in name if ord(char)>=32)
     # delete non-ascii characters
@@ -184,19 +186,19 @@ def unescape(text):
             # character reference
             try:
                 if text[:3] == u"&#x":
-                    return unichr(int(text[3:-1], 16))
+                    return chr(int(text[3:-1], 16))
                 else:
-                    return unichr(int(text[2:-1]))
+                    return chr(int(text[2:-1]))
             except ValueError:
                 pass
         else:
             # named entity
             try:
-                text = unichr(htmlentitydefs.name2codepoint[text[1:-1]])
+                text = chr(htmlentitydefs.name2codepoint[text[1:-1]])
             except KeyError:
                 pass
         return text # leave as is
-    return re.sub(u"&#?\w+;", fixup, text)
+    return re.sub(u"&#?\\w+;", fixup, text)
 
 def GetDecryptedBook(infile, kDatabases, androidFiles, serials, pids, starttime = time.time()):
     # handle the obvious cases at the beginning
@@ -220,7 +222,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(u"Decrypting {1} ebook: {0}".format(bookname, mb.getBookType()))
 
     # copy list of pids
     totalpids = list(pids)
@@ -232,7 +234,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(u"Found {1:d} keys to try after {0:.1f} seconds".format(time.time()-starttime, len(totalpids)))
     #print totalpids
 
     try:
@@ -241,7 +243,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(u"Decryption succeeded after {0:.1f} seconds".format(time.time()-starttime))
     return mb
 
 
@@ -255,16 +257,16 @@ def decryptBook(infile, outdir, kDatabaseFiles, androidFiles, serials, pids):
             with open(dbfile, 'r') as keyfilein:
                 kindleDatabase = json.loads(keyfilein.read())
             kDatabases.append([dbfile,kindleDatabase])
-        except Exception, e:
-            print u"Error getting database from file {0:s}: {1:s}".format(dbfile,e)
+        except Exception as e:
+            print(u"Error getting database from file {0:s}: {1:s}".format(dbfile,e))
             traceback.print_exc()
 
 
 
     try:
         book = GetDecryptedBook(infile, kDatabases, androidFiles, serials, pids, starttime)
-    except Exception, e:
-        print u"Error decrypting book after {1:.1f} seconds: {0}".format(e.args[0],time.time()-starttime)
+    except Exception as e:
+        print(u"Error decrypting book after {1:.1f} seconds: {0}".format(e.args[0],time.time()-starttime))
         traceback.print_exc()
         return 1
 
@@ -287,12 +289,12 @@ def decryptBook(infile, outdir, kDatabaseFiles, androidFiles, serials, pids):
     outfile = os.path.join(outdir, outfilename + book.getBookExtension())
 
     book.getFile(outfile)
-    print u"Saved decrypted book {1:s} after {0:.1f} seconds".format(time.time()-starttime, outfilename)
+    print(u"Saved decrypted book {1:s} after {0:.1f} seconds".format(time.time()-starttime, outfilename))
 
     if book.getBookType()==u"Topaz":
         zipname = os.path.join(outdir, outfilename + u"_SVG.zip")
         book.getSVGZip(zipname)
-        print u"Saved SVG ZIP Archive for {1:s} after {0:.1f} seconds".format(time.time()-starttime, outfilename)
+        print(u"Saved SVG ZIP Archive for {1:s} after {0:.1f} seconds".format(time.time()-starttime, outfilename))
 
     # remove internal temporary directory of Topaz pieces
     book.cleanup()
@@ -300,9 +302,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 <kindle.k4i>] [-p <comma separated PIDs>] [-s <comma separated Kindle serial numbers>] [ -a <AmazonSecureStorage.xml|backup.ab> ] <infile> <outdir>".format(progname)
+    print(u"Removes DRM protection from Mobipocket, Amazon KF8, Amazon Print Replica and Amazon Topaz ebooks")
+    print(u"Usage:")
+    print(u"    {0} [-k <kindle.k4i>] [-p <comma separated PIDs>] [-s <comma separated Kindle serial numbers>] [ -a <AmazonSecureStorage.xml|backup.ab> ] <infile> <outdir>".format(progname))
 
 #
 # Main
@@ -310,12 +312,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(u"K4MobiDeDrm v{0}.\nCopyright © 2008-2017 Apprentice Harper et al.".format(__version__))
 
     try:
         opts, args = getopt.getopt(argv[1:], "k:p:s:a:")
-    except getopt.GetoptError, err:
-        print u"Error in options or arguments: {0}".format(err.args[0])
+    except getopt.GetoptError as err:
+        print(u"Error in options or arguments: {0}".format(err.args[0]))
         usage(progname)
         sys.exit(2)
     if len(args)<2:
index 1520f79180d81f57699f209f61a2c57b35d557f8..d3c0f7e76929e0b5ebd2657cbff08c8d506af63d 100644 (file)
@@ -6,6 +6,9 @@ from __future__ import print_function
 
 # Engine to remove drm from Kindle KFX ebooks
 
+#  2.0   - Added Python 3 compatibility for calibre 5.0
+
+
 import os
 import shutil
 import zipfile
@@ -13,7 +16,10 @@ import zipfile
 try:
     from cStringIO import StringIO
 except ImportError:
-    from StringIO import StringIO
+    try:
+        from StringIO import StringIO
+    except ImportError:
+        from io import StringIO
 
 try:
     from calibre_plugins.dedrm import ion
@@ -22,7 +28,7 @@ except ImportError:
 
 
 __license__ = 'GPL v3'
-__version__ = '1.0'
+__version__ = '2.0'
 
 
 class KFXZipBook:
index 529d13d7570868f82b693509d754e29ad8dd09a9..a3efee0cf8d4bd1287458280c7b913c1d6b94b06 100644 (file)
@@ -5,15 +5,17 @@ from __future__ import with_statement
 from __future__ import print_function
 
 # kgenpids.py
-# Copyright © 2008-2017 Apprentice Harper et al.
+# Copyright © 2008-2020 Apprentice Harper et al.
 
 __license__ = 'GPL v3'
-__version__ = '2.1'
+__version__ = '3.0'
 
 # Revision history:
 #  2.0   - Fix for non-ascii Windows user names
 #  2.1   - Actual fix for non-ascii WIndows user names.
-#  x.x   - Return information needed for KFX decryption
+#  2.2   - Return information needed for KFX decryption
+#  3.0   - Added Python 3 compatibility for calibre 5.0
+
 
 import sys
 import os, csv
@@ -31,9 +33,9 @@ global charMap3
 global charMap4
 
 
-charMap1 = 'n5Pr6St7Uv8Wx9YzAb0Cd1Ef2Gh3Jk4M'
-charMap3 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
-charMap4 = 'ABCDEFGHIJKLMNPQRSTUVWXYZ123456789'
+charMap1 = b'n5Pr6St7Uv8Wx9YzAb0Cd1Ef2Gh3Jk4M'
+charMap3 = b'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
+charMap4 = b'ABCDEFGHIJKLMNPQRSTUVWXYZ123456789'
 
 # crypto digestroutines
 import hashlib
@@ -84,7 +86,7 @@ def decode(data,map):
 def getTwoBitsFromBitField(bitField,offset):
     byteNumber = offset // 4
     bitPosition = 6 - 2*(offset % 4)
-    return ord(bitField[byteNumber]) >> bitPosition & 3
+    return bitField[byteNumber] >> bitPosition & 3
 
 # Returns the six bits at offset from a bit field
 def getSixBitsFromBitField(bitField,offset):
@@ -95,9 +97,9 @@ def getSixBitsFromBitField(bitField,offset):
 # 8 bits to six bits encoding from hash to generate PID string
 def encodePID(hash):
     global charMap3
-    PID = ''
+    PID = b''
     for position in range (0,8):
-        PID += charMap3[getSixBitsFromBitField(hash,position)]
+        PID += bytes([charMap3[getSixBitsFromBitField(hash,position)]])
     return PID
 
 # Encryption table used to generate the device PID
@@ -126,7 +128,7 @@ def generatePidSeed(table,dsn) :
 def generateDevicePID(table,dsn,nbRoll):
     global charMap4
     seed = generatePidSeed(table,dsn)
-    pidAscii = ''
+    pidAscii = b''
     pid = [(seed >>24) &0xFF,(seed >> 16) &0xff,(seed >> 8) &0xFF,(seed) & 0xFF,(seed>>24) & 0xFF,(seed >> 16) &0xff,(seed >> 8) &0xFF,(seed) & 0xFF]
     index = 0
     for counter in range (0,nbRoll):
@@ -134,7 +136,7 @@ def generateDevicePID(table,dsn,nbRoll):
         index = (index+1) %8
     for counter in range (0,8):
         index = ((((pid[counter] >>5) & 3) ^ pid[counter]) & 0x1f) + (pid[counter] >> 7)
-        pidAscii += charMap4[index]
+        pidAscii += bytes([charMap4[index]])
     return pidAscii
 
 def crc32(s):
@@ -150,7 +152,7 @@ def checksumPid(s):
     for i in (0,1):
         b = crc & 0xff
         pos = (b // l) ^ (b % l)
-        res += charMap4[pos%l]
+        res += bytes([charMap4[pos%l]])
         crc >>= 8
     return res
 
@@ -160,15 +162,15 @@ def pidFromSerial(s, l):
     global charMap4
     crc = crc32(s)
     arr1 = [0]*l
-    for i in xrange(len(s)):
-        arr1[i%l] ^= ord(s[i])
+    for i in range(len(s)):
+        arr1[i%l] ^= s[i]
     crc_bytes = [crc >> 24 & 0xff, crc >> 16 & 0xff, crc >> 8 & 0xff, crc & 0xff]
-    for i in xrange(l):
+    for i in range(l):
         arr1[i] ^= crc_bytes[i&3]
-    pid = ""
-    for i in xrange(l):
+    pid = b""
+    for i in range(l):
         b = arr1[i] & 0xff
-        pid+=charMap4[(b >> 7) + ((b >> 5 & 3) ^ (b & 0x1f))]
+        pid += bytes([charMap4[(b >> 7) + ((b >> 5 & 3) ^ (b & 0x1f))]])
     return pid
 
 
@@ -179,7 +181,7 @@ def getKindlePids(rec209, token, serialnum):
 
     pids=[]
 
-    if isinstance(serialnum,unicode):
+    if isinstance(serialnum,str):
         serialnum = serialnum.encode('utf-8')
 
     # Compute book PID
@@ -189,7 +191,7 @@ def getKindlePids(rec209, token, serialnum):
     pids.append(bookPID)
 
     # compute fixed pid for old pre 2.5 firmware update pid as well
-    kindlePID = pidFromSerial(serialnum, 7) + "*"
+    kindlePID = pidFromSerial(serialnum, 7) + b"*"
     kindlePID = checksumPid(kindlePID)
     pids.append(kindlePID)
 
@@ -206,7 +208,7 @@ def getK4Pids(rec209, token, kindleDatabase):
 
     try:
         # Get the kindle account token, if present
-        kindleAccountToken = (kindleDatabase[1])['kindle.account.tokens'].decode('hex')
+        kindleAccountToken = bytearray.fromhex((kindleDatabase[1])['kindle.account.tokens']).decode()
 
     except KeyError:
         kindleAccountToken=""
@@ -214,30 +216,30 @@ def getK4Pids(rec209, token, kindleDatabase):
 
     try:
         # Get the DSN token, if present
-        DSN = (kindleDatabase[1])['DSN'].decode('hex')
+        DSN = bytearray.fromhex((kindleDatabase[1])['DSN']).decode()
         print(u"Got DSN key from database {0}".format(kindleDatabase[0]))
     except KeyError:
         # See if we have the info to generate the DSN
         try:
             # Get the Mazama Random number
-            MazamaRandomNumber = (kindleDatabase[1])['MazamaRandomNumber'].decode('hex')
+            MazamaRandomNumber = bytearray.fromhex((kindleDatabase[1])['MazamaRandomNumber']).decode()
             #print u"Got MazamaRandomNumber from database {0}".format(kindleDatabase[0])
 
             try:
                 # Get the SerialNumber token, if present
-                IDString = (kindleDatabase[1])['SerialNumber'].decode('hex')
+                IDString = bytearray.fromhex((kindleDatabase[1])['SerialNumber']).decode()
                 print(u"Got SerialNumber from database {0}".format(kindleDatabase[0]))
             except KeyError:
                  # Get the IDString we added
-                IDString = (kindleDatabase[1])['IDString'].decode('hex')
+                IDString = bytearray.fromhex((kindleDatabase[1])['IDString']).decode()
 
             try:
                 # Get the UsernameHash token, if present
-                encodedUsername = (kindleDatabase[1])['UsernameHash'].decode('hex')
+                encodedUsername = bytearray.fromhex((kindleDatabase[1])['UsernameHash']).decode()
                 print(u"Got UsernameHash from database {0}".format(kindleDatabase[0]))
             except KeyError:
                 # Get the UserName we added
-                UserName = (kindleDatabase[1])['UserName'].decode('hex')
+                UserName = bytearray.fromhex((kindleDatabase[1])['UserName']).decode()
                 # encode it
                 encodedUsername = encodeHash(UserName,charMap1)
                 #print u"encodedUsername",encodedUsername.encode('hex')
@@ -267,19 +269,19 @@ def getK4Pids(rec209, token, kindleDatabase):
     # Compute book PIDs
 
     # book pid
-    pidHash = SHA1(DSN+kindleAccountToken+rec209+token)
+    pidHash = SHA1(DSN.encode()+kindleAccountToken.encode()+rec209+token)
     bookPID = encodePID(pidHash)
     bookPID = checksumPid(bookPID)
     pids.append(bookPID)
 
     # variant 1
-    pidHash = SHA1(kindleAccountToken+rec209+token)
+    pidHash = SHA1(kindleAccountToken.encode()+rec209+token)
     bookPID = encodePID(pidHash)
     bookPID = checksumPid(bookPID)
     pids.append(bookPID)
 
     # variant 2
-    pidHash = SHA1(DSN+rec209+token)
+    pidHash = SHA1(DSN.encode()+rec209+token)
     bookPID = encodePID(pidHash)
     bookPID = checksumPid(bookPID)
     pids.append(bookPID)
@@ -297,14 +299,14 @@ def getPidList(md1, md2, serials=[], kDatabases=[]):
     for kDatabase in kDatabases:
         try:
             pidlst.extend(getK4Pids(md1, md2, kDatabase))
-        except Exception, e:
+        except Exception as e:
             print(u"Error getting PIDs from database {0}: {1}".format(kDatabase[0],e.args[0]))
             traceback.print_exc()
 
     for serialnum in serials:
         try:
             pidlst.extend(getKindlePids(md1, md2, serialnum))
-        except Exception, e:
+        except Exception as e:
             print(u"Error getting PIDs from serial number {0}: {1}".format(serialnum ,e.args[0]))
             traceback.print_exc()
 
index 90862cb6e2f6069d3b69693d7cb6641b7ec40896..5e9f5c8f4af11aed7a91744e315a3abd9a8eff4c 100644 (file)
@@ -7,7 +7,7 @@ from __future__ import with_statement
 # Copyright © 2008-2020 Apprentice Harper et al.
 
 __license__ = 'GPL v3'
-__version__ = '2.7'
+__version__ = '3.0'
 
 # Revision history:
 #  1.0   - Kindle info file decryption, extracted from k4mobidedrm, etc.
@@ -30,6 +30,7 @@ __version__ = '2.7'
 #  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
 
 
 """
@@ -37,7 +38,7 @@ Retrieve Kindle for PC/Mac user key.
 """
 
 import sys, os, re
-from struct import pack, unpack
+from struct import pack, unpack, unpack_from
 import json
 import getopt
 
@@ -108,7 +109,7 @@ def unicode_argv():
         argvencoding = sys.stdin.encoding
         if argvencoding == None:
             argvencoding = "utf-8"
-        return [arg if (type(arg) == unicode) else unicode(arg,argvencoding) for arg in sys.argv]
+        return arg
 
 class DrmException(Exception):
     pass
@@ -299,7 +300,7 @@ if iswindows:
                 numBlocks, numExtraBytes = divmod(len(self.bytesToDecrypt), self.blockSize)
                 if more == None:  # no more calls to decrypt, should have all the data
                     if numExtraBytes  != 0:
-                        raise DecryptNotBlockAlignedError, 'Data not block aligned on decrypt'
+                        raise DecryptNotBlockAlignedError('Data not block aligned on decrypt')
 
                 # hold back some bytes in case last decrypt has zero len
                 if (more != None) and (numExtraBytes == 0) and (numBlocks >0) :
@@ -341,7 +342,7 @@ if iswindows:
             def removePad(self, paddedBinaryString, blockSize):
                 """ Remove padding from a binary string """
                 if not(0<len(paddedBinaryString)):
-                    raise DecryptNotBlockAlignedError, 'Expected More Data'
+                    raise DecryptNotBlockAlignedError('Expected More Data')
                 return paddedBinaryString[:-ord(paddedBinaryString[-1])]
 
         class noPadding(Pad):
@@ -649,7 +650,7 @@ if iswindows:
             def __init__(self, key = None, padding = padWithPadLen(), keySize=16):
                 """ Initialize AES, keySize is in bytes """
                 if  not (keySize == 16 or keySize == 24 or keySize == 32) :
-                    raise BadKeySizeError, 'Illegal AES key size, must be 16, 24, or 32 bytes'
+                    raise BadKeySizeError('Illegal AES key size, must be 16, 24, or 32 bytes')
 
                 Rijndael.__init__( self, key, padding=padding, keySize=keySize, blockSize=16 )
 
@@ -935,7 +936,6 @@ if iswindows:
     # Returns Environmental Variables that contain unicode
     def getEnvironmentVariable(name):
         import ctypes
-        name = unicode(name) # make sure string argument is unicode
         n = ctypes.windll.kernel32.GetEnvironmentVariableW(name, None, 0)
         if n == 0:
             return None
@@ -1166,9 +1166,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(u"Decrypted key file using IDString '{0:s}' and UserName '{1:s}'".format(GetIDString(), GetUserName().encode('hex')))
         else:
-            print u"Couldn't decrypt file."
+            print(u"Couldn't decrypt file.")
             DB = {}
         return DB
 elif isosx:
@@ -1649,11 +1649,11 @@ elif isosx:
                 pass
         if len(DB)>6:
             # store values used in decryption
-            print u"Decrypted key file using IDString '{0:s}' and UserName '{1:s}'".format(IDString, GetUserName())
+            print(u"Decrypted key file using IDString '{0:s}' and UserName '{1:s}'".format(IDString, GetUserName()))
             DB['IDString'] = IDString
             DB['UserName'] = GetUserName()
         else:
-            print u"Couldn't decrypt file."
+            print(u"Couldn't decrypt file.")
             DB = {}
         return DB
 else:
@@ -1683,7 +1683,7 @@ 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(u"Saved a key to {0}".format(outfile))
         else:
             keycount = 0
             for key in keys:
@@ -1694,16 +1694,16 @@ def getkey(outpath, files=[]):
                         break
                 with file(outfile, 'w') as keyfileout:
                     keyfileout.write(json.dumps(key))
-                print u"Saved a key to {0}".format(outfile)
+                print(u"Saved a key to {0}".format(outfile))
         return True
     return False
 
 def usage(progname):
-    print u"Finds, decrypts and saves the default Kindle For Mac/PC encryption keys."
-    print u"Keys are saved to the current directory, or a specified output directory."
-    print u"If a file name is passed instead of a directory, only the first key is saved, in that file."
-    print u"Usage:"
-    print u"    {0:s} [-h] [-k <kindle.info>] [<outpath>]".format(progname)
+    print(u"Finds, decrypts and saves the default Kindle For Mac/PC encryption keys.")
+    print(u"Keys are saved to the current directory, or a specified output directory.")
+    print(u"If a file name is passed instead of a directory, only the first key is saved, in that file.")
+    print(u"Usage:")
+    print(u"    {0:s} [-h] [-k <kindle.info>] [<outpath>]".format(progname))
 
 
 def cli_main():
@@ -1711,12 +1711,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(u"{0} v{1}\nCopyright © 2010-2016 by some_updates, Apprentice Alf and Apprentice Harper".format(progname,__version__))
 
     try:
         opts, args = getopt.getopt(argv[1:], "hk:")
-    except getopt.GetoptError, err:
-        print u"Error in options or arguments: {0}".format(err.args[0])
+    except getopt.GetoptError as err:
+        print(u"Error in options or arguments: {0}".format(err.args[0]))
         usage(progname)
         sys.exit(2)
 
@@ -1745,7 +1745,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(u"Could not retrieve Kindle for Mac/PC key.")
     return 0
 
 
@@ -1789,7 +1789,7 @@ def gui_main():
                 keyfileout.write(json.dumps(key))
             success = True
             tkMessageBox.showinfo(progname, u"Key successfully retrieved to {0}".format(outfile))
-    except DrmException, e:
+    except DrmException as e:
         tkMessageBox.showerror(progname, u"Error: {0}".format(str(e)))
     except Exception:
         root.wm_state('normal')
index 726554fcb467470fa242d390e9f2c7e1476b0cf2..069fdc00aa523e1e633fb491e912496c4f79a7cb 100644 (file)
@@ -10,6 +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
 
 from __future__ import print_function
 import sys
@@ -25,7 +26,7 @@ class SafeUnbuffered:
         if self.encoding == None:
             self.encoding = "utf-8"
     def write(self, data):
-        if isinstance(data,unicode):
+        if isinstance(data,bytes):
             data = data.encode(self.encoding,"replace")
         self.stream.write(data)
         self.stream.flush()
@@ -63,7 +64,7 @@ def unicode_argv():
             # Remove Python executable and commands if present
             start = argc.value - len(sys.argv)
             return [argv[i] for i in
-                    xrange(start, argc.value)]
+                    range(start, argc.value)]
         # if we don't have any arguments at all, just pass back script name
         # this should never happen
         return [u"kindlepid.py"]
@@ -71,11 +72,7 @@ def unicode_argv():
         argvencoding = sys.stdin.encoding
         if argvencoding == None:
             argvencoding = "utf-8"
-        return [arg if (type(arg) == unicode) else unicode(arg,argvencoding) for arg in sys.argv]
-
-if sys.hexversion >= 0x3000000:
-    print('This script is incompatible with Python 3.x. Please install Python 2.7.x.')
-    sys.exit(2)
+        return sys.argv
 
 letters = 'ABCDEFGHIJKLMNPQRSTUVWXYZ123456789'
 
@@ -83,7 +80,7 @@ def crc32(s):
     return (~binascii.crc32(s,-1))&0xFFFFFFFF
 
 def checksumPid(s):
-    crc = crc32(s)
+    crc = crc32(s.encode('ascii'))
     crc = crc ^ (crc >> 16)
     res = s
     l = len(letters)
@@ -99,15 +96,15 @@ def pidFromSerial(s, l):
     crc = crc32(s)
 
     arr1 = [0]*l
-    for i in xrange(len(s)):
-        arr1[i%l] ^= ord(s[i])
+    for i in range(len(s)):
+        arr1[i%l] ^= s[i]
 
     crc_bytes = [crc >> 24 & 0xff, crc >> 16 & 0xff, crc >> 8 & 0xff, crc & 0xff]
-    for i in xrange(l):
+    for i in range(l):
         arr1[i] ^= crc_bytes[i&3]
 
     pid = ''
-    for i in xrange(l):
+    for i in range(l):
         b = arr1[i] & 0xff
         pid+=letters[(b >> 7) + ((b >> 5 & 3) ^ (b & 0x1f))]
 
@@ -140,6 +137,6 @@ def cli_main():
 
 
 if __name__ == "__main__":
-    sys.stdout=SafeUnbuffered(sys.stdout)
-    sys.stderr=SafeUnbuffered(sys.stderr)
+    #sys.stdout=SafeUnbuffered(sys.stdout)
+    #sys.stderr=SafeUnbuffered(sys.stderr)
     sys.exit(cli_main())
index e8400e4097db7bb61e483a6b1f918b4c5d0c1c4a..cfd8a81343af5fe976ef130cd5326f270696440d 100644 (file)
@@ -3,11 +3,11 @@
 
 # mobidedrm.py
 # Copyright © 2008 The Dark Reverser
-# Portions © 2008–2017 Apprentice Harper et al.
+# Portions © 2008–2020 Apprentice Harper et al.
 
 from __future__ import print_function
 __license__ = 'GPL v3'
-__version__ = u"0.42"
+__version__ = u"1.00"
 
 # This is a python script. You need a Python interpreter to run it.
 # For example, ActiveState Python, which exists for windows.
@@ -73,6 +73,7 @@ __version__ = u"0.42"
 #  0.40 - moved unicode_argv call inside main for Windows DeDRM compatibility
 #  0.41 - Fixed potential unicode problem in command line calls
 #  0.42 - Added GPL v3 licence. updated/removed some print statements
+#  3.00 - Added Python 3 compatibility for calibre 5.0
 
 import sys
 import os
@@ -93,7 +94,7 @@ class SafeUnbuffered:
         if self.encoding == None:
             self.encoding = "utf-8"
     def write(self, data):
-        if isinstance(data,unicode):
+        if isinstance(data,bytes):
             data = data.encode(self.encoding,"replace")
         self.stream.write(data)
         self.stream.flush()
@@ -131,7 +132,7 @@ def unicode_argv():
             # Remove Python executable and commands if present
             start = argc.value - len(sys.argv)
             return [argv[i] for i in
-                    xrange(start, argc.value)]
+                    range(start, argc.value)]
         # if we don't have any arguments at all, just pass back script name
         # this should never happen
         return [u"mobidedrm.py"]
@@ -139,7 +140,7 @@ def unicode_argv():
         argvencoding = sys.stdin.encoding
         if argvencoding == None:
             argvencoding = 'utf-8'
-        return [arg if (type(arg) == unicode) else unicode(arg,argvencoding) for arg in sys.argv]
+        return sys.argv
 
 
 class DrmException(Exception):
@@ -153,12 +154,12 @@ class DrmException(Exception):
 # Implementation of Pukall Cipher 1
 def PC1(key, src, decryption=True):
     # if we can get it from alfcrypto, use that
-    try:
-        return Pukall_Cipher().PC1(key,src,decryption)
-    except NameError:
-        pass
-    except TypeError:
-        pass
+    #try:
+    #    return Pukall_Cipher().PC1(key,src,decryption)
+    #except NameError:
+    #    pass
+    #except TypeError:
+    #    pass
 
     # use slow python version, since Pukall_Cipher didn't load
     sum1 = 0;
@@ -167,28 +168,28 @@ def PC1(key, src, decryption=True):
     if len(key)!=16:
          DrmException (u"PC1: Bad key length")
     wkey = []
-    for i in xrange(8):
-        wkey.append(ord(key[i*2])<<8 | ord(key[i*2+1]))
-    dst = ""
-    for i in xrange(len(src)):
+    for i in range(8):
+        wkey.append(key[i*2]<<8 | key[i*2+1])
+    dst = b''
+    for i in range(len(src)):
         temp1 = 0;
         byteXorVal = 0;
-        for j in xrange(8):
+        for j in range(8):
             temp1 ^= wkey[j]
             sum2  = (sum2+j)*20021 + sum1
             sum1  = (temp1*346)&0xFFFF
             sum2  = (sum2+sum1)&0xFFFF
             temp1 = (temp1*20021+1)&0xFFFF
             byteXorVal ^= temp1 ^ sum2
-        curByte = ord(src[i])
+        curByte = src[i]
         if not decryption:
             keyXorVal = curByte * 257;
         curByte = ((curByte ^ (byteXorVal >> 8)) ^ byteXorVal) & 0xFF
         if decryption:
             keyXorVal = curByte * 257;
-        for j in xrange(8):
+        for j in range(8):
             wkey[j] ^= keyXorVal;
-        dst+=chr(curByte)
+        dst+=bytes([curByte])
     return dst
 
 def checksumPid(s):
@@ -200,7 +201,7 @@ def checksumPid(s):
     for i in (0,1):
         b = crc & 0xff
         pos = (b // l) ^ (b % l)
-        res += letters[pos%l]
+        res += letters[pos%l].encode('ascii')
         crc >>= 8
     return res
 
@@ -210,7 +211,7 @@ def getSizeOfTrailingDataEntries(ptr, size, flags):
         if size <= 0:
             return result
         while True:
-            v = ord(ptr[size-1])
+            v = ptr[size-1]
             result |= (v & 0x7F) << bitpos
             bitpos += 7
             size -= 1
@@ -226,7 +227,7 @@ def getSizeOfTrailingDataEntries(ptr, size, flags):
     # if multibyte data is included in the encryped data, we'll
     # have already cleared this flag.
     if flags & 1:
-        num += (ord(ptr[size - num - 1]) & 0x3) + 1
+        num += (ptr[size - num - 1] & 0x3) + 1
     return num
 
 
@@ -253,10 +254,10 @@ class MobiBook:
             print(u"AlfCrypto not found. Using python PC1 implementation.")
 
         # initial sanity check on file
-        self.data_file = file(infile, 'rb').read()
+        self.data_file = open(infile, 'rb').read()
         self.mobi_data = ''
         self.header = self.data_file[0:78]
-        if self.header[0x3C:0x3C+8] != 'BOOKMOBI' and self.header[0x3C:0x3C+8] != 'TEXtREAd':
+        if self.header[0x3C:0x3C+8] != b'BOOKMOBI' and self.header[0x3C:0x3C+8] != b'TEXtREAd':
             raise DrmException(u"Invalid file format")
         self.magic = self.header[0x3C:0x3C+8]
         self.crypto_type = -1
@@ -264,7 +265,7 @@ class MobiBook:
         # build up section offset and flag info
         self.num_sections, = struct.unpack('>H', self.header[76:78])
         self.sections = []
-        for i in xrange(self.num_sections):
+        for i in range(self.num_sections):
             offset, a1,a2,a3,a4 = struct.unpack('>LBBBB', self.data_file[78+i*8:78+i*8+8])
             flags, val = a1, a2<<16|a3<<8|a4
             self.sections.append( (offset, flags, val) )
@@ -304,24 +305,24 @@ class MobiBook:
             exth = ''
             if exth_flag & 0x40:
                 exth = self.sect[16 + self.mobi_length:]
-            if (len(exth) >= 12) and (exth[:4] == 'EXTH'):
+            if (len(exth) >= 12) and (exth[:4] == b'EXTH'):
                 nitems, = struct.unpack('>I', exth[8:12])
                 pos = 12
-                for i in xrange(nitems):
+                for i in range(nitems):
                     type, size = struct.unpack('>II', exth[pos: pos + 8])
                     content = exth[pos + 8: pos + size]
                     self.meta_array[type] = content
                     # reset the text to speech flag and clipping limit, if present
                     if type == 401 and size == 9:
                         # set clipping limit to 100%
-                        self.patchSection(0, '\144', 16 + self.mobi_length + pos + 8)
+                        self.patchSection(0, b'\144', 16 + self.mobi_length + pos + 8)
                     elif type == 404 and size == 9:
                         # make sure text to speech is enabled
-                        self.patchSection(0, '\0', 16 + self.mobi_length + pos + 8)
+                        self.patchSection(0, b'\0', 16 + self.mobi_length + pos + 8)
                     # print type, size, content, content.encode('hex')
                     pos += size
-        except:
-            pass
+        except Exception as e:
+            print(u"Cannot set meta_array: Error: {:s}".format(e.args[0]))
 
     def getBookTitle(self):
         codec_map = {
@@ -341,19 +342,19 @@ class MobiBook:
                 codec = codec_map[self.mobi_codepage]
         if title == '':
             title = self.header[:32]
-            title = title.split('\0')[0]
-        return unicode(title, codec)
+            title = title.split(b'\0')[0]
+        return title.decode(codec)
 
     def getPIDMetaInfo(self):
-        rec209 = ''
-        token = ''
+        rec209 = b''
+        token = b''
         if 209 in self.meta_array:
             rec209 = self.meta_array[209]
             data = rec209
             # The 209 data comes in five byte groups. Interpret the last four bytes
             # of each group as a big endian unsigned integer to get a key value
             # if that key exists in the meta_array, append its contents to the token
-            for i in xrange(0,len(data),5):
+            for i in range(0,len(data),5):
                 val,  = struct.unpack('>I',data[i+1:i+5])
                 sval = self.meta_array.get(val,'')
                 token += sval
@@ -373,13 +374,14 @@ class MobiBook:
 
     def parseDRM(self, data, count, pidlist):
         found_key = None
-        keyvec1 = '\x72\x38\x33\xB0\xB4\xF2\xE3\xCA\xDF\x09\x01\xD6\xE2\xE0\x3F\x96'
+        keyvec1 = b'\x72\x38\x33\xB0\xB4\xF2\xE3\xCA\xDF\x09\x01\xD6\xE2\xE0\x3F\x96'
         for pid in pidlist:
-            bigpid = pid.ljust(16,'\0')
+            bigpid = pid.ljust(16,b'\0')
+            bigpid = bigpid
             temp_key = PC1(keyvec1, bigpid, False)
-            temp_key_sum = sum(map(ord,temp_key)) & 0xff
+            temp_key_sum = sum(temp_key) & 0xff
             found_key = None
-            for i in xrange(count):
+            for i in range(count):
                 verification, size, type, cksum, cookie = struct.unpack('>LLLBxxx32s', data[i*0x30:i*0x30+0x30])
                 if cksum == temp_key_sum:
                     cookie = PC1(temp_key, cookie)
@@ -393,8 +395,8 @@ class MobiBook:
             # Then try the default encoding that doesn't require a PID
             pid = '00000000'
             temp_key = keyvec1
-            temp_key_sum = sum(map(ord,temp_key)) & 0xff
-            for i in xrange(count):
+            temp_key_sum = sum(temp_key) & 0xff
+            for i in range(count):
                 verification, size, type, cksum, cookie = struct.unpack('>LLLBxxx32s', data[i*0x30:i*0x30+0x30])
                 if cksum == temp_key_sum:
                     cookie = PC1(temp_key, cookie)
@@ -405,7 +407,7 @@ class MobiBook:
         return [found_key,pid]
 
     def getFile(self, outpath):
-        file(outpath,'wb').write(self.mobi_data)
+        open(outpath,'wb').write(self.mobi_data)
 
     def getBookType(self):
         if self.print_replica:
@@ -442,6 +444,7 @@ class MobiBook:
                 raise DrmException(u"Cannot decode library or rented ebooks.")
 
         goodpids = []
+        # print("DEBUG ==== pidlist = ", pidlist)
         for pid in pidlist:
             if len(pid)==10:
                 if checksumPid(pid[0:-2]) != pid:
@@ -452,6 +455,8 @@ class MobiBook:
             else:
                 print(u"Warning: PID {0} has wrong number of digits".format(pid))
 
+        # print(u"======= DEBUG good pids = ", goodpids)
+
         if self.crypto_type == 1:
             t1_keyvec = 'QDCVEPMU675RUBSZ'
             if self.magic == 'TEXtREAd':
@@ -471,9 +476,9 @@ class MobiBook:
             if not found_key:
                 raise DrmException(u"No key found in {0:d} keys tried.".format(len(goodpids)))
             # kill the drm keys
-            self.patchSection(0, '\0' * drm_size, drm_ptr)
+            self.patchSection(0, b'\0' * drm_size, drm_ptr)
             # kill the drm pointers
-            self.patchSection(0, '\xff' * 4 + '\0' * 12, 0xA8)
+            self.patchSection(0, b'\xff' * 4 + b'\0' * 12, 0xA8)
 
         if pid=='00000000':
             print(u"File has default encryption, no specific key needed.")
@@ -481,13 +486,13 @@ class MobiBook:
             print(u"File is encoded with PID {0}.".format(checksumPid(pid)))
 
         # clear the crypto type
-        self.patchSection(0, "\0" * 2, 0xC)
+        self.patchSection(0, b'\0' * 2, 0xC)
 
         # decrypt sections
         print(u"Decrypting. Please wait . . .", end=' ')
         mobidataList = []
         mobidataList.append(self.data_file[:self.sections[1][0]])
-        for i in xrange(1, self.records+1):
+        for i in range(1, self.records+1):
             data = self.loadSection(i)
             extra_size = getSizeOfTrailingDataEntries(data, len(data), self.extra_data_flags)
             if i%100 == 0:
@@ -501,7 +506,7 @@ class MobiBook:
                 mobidataList.append(data[-extra_size:])
         if self.num_sections > self.records+1:
             mobidataList.append(self.data_file[self.sections[self.records+1][0]:])
-        self.mobi_data = "".join(mobidataList)
+        self.mobi_data = b''.join(mobidataList)
         print(u"done")
         return
 
@@ -531,8 +536,8 @@ def cli_main():
             pidlist = []
         try:
             stripped_file = getUnencryptedBook(infile, pidlist)
-            file(outfile, 'wb').write(stripped_file)
-        except DrmException, e:
+            open(outfile, 'wb').write(stripped_file)
+        except DrmException as e:
             print(u"MobiDeDRM v{0} Error: {1:s}".format(__version__,e.args[0]))
             return 1
     return 0
index c1bfcb9a2936bc17add82eded8621df859a5161f..cdbb1893f54a4023c533f52c41401fd1f6745dd2 100644 (file)
@@ -101,9 +101,9 @@ def convertprefs(always = False):
                 keyname = u"{0}_{1}".format(name.strip(),ccn.strip()[-4:])
                 keyvalue = generate_key(name, ccn)
                 userkeys.append([keyname,keyvalue])
-            except Exception, e:
+            except Exception as e:
                 traceback.print_exc()
-                print e.args[0]
+                print(e.args[0])
                 pass
         return userkeys
 
@@ -118,9 +118,9 @@ def convertprefs(always = False):
                 keyname = u"{0}_{1}".format(name.strip(),cc.strip()[-4:])
                 keyvalue = getuser_key(name,cc).encode('hex')
                 userkeys.append([keyname,keyvalue])
-            except Exception, e:
+            except Exception as e:
                 traceback.print_exc()
-                print e.args[0]
+                print(e.args[0])
                 pass
         return userkeys
 
@@ -161,7 +161,7 @@ def convertprefs(always = False):
         return
 
 
-    print u"{0} v{1}: Importing configuration data from old DeDRM plugins".format(PLUGIN_NAME, PLUGIN_VERSION)
+    print(u"{0} v{1}: Importing configuration data from old DeDRM plugins".format(PLUGIN_NAME, PLUGIN_VERSION))
 
     IGNOBLEPLUGINNAME = "Ignoble Epub DeDRM"
     EREADERPLUGINNAME = "eReader PDB 2 PML"
@@ -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(u"{0} v{1}: Converting old Ignoble plugin configuration string.".format(PLUGIN_NAME, PLUGIN_VERSION))
         priorkeycount = len(dedrmprefs['bandnkeys'])
         userkeys = parseIgnobleString(str(val))
         for keypair in userkeys:
@@ -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(u"{0} v{1}: {2:d} Barnes and Noble {3} imported from old Ignoble plugin configuration string".format(PLUGIN_NAME, PLUGIN_VERSION, addedkeycount, u"key" if addedkeycount==1 else u"keys"))
     # Make the json write all the prefs to disk
     dedrmprefs.writeprefs(False)
 
@@ -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(u"{0} v{1}: Converting old eReader plugin configuration string.".format(PLUGIN_NAME, PLUGIN_VERSION))
         priorkeycount = len(dedrmprefs['ereaderkeys'])
         userkeys = parseeReaderString(str(val))
         for keypair in userkeys:
@@ -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(u"{0} v{1}: {2:d} eReader {3} imported from old eReader plugin configuration string".format(PLUGIN_NAME, PLUGIN_VERSION, addedkeycount, u"key" if addedkeycount==1 else u"keys"))
     # Make the json write all the prefs to disk
     dedrmprefs.writeprefs(False)
 
     # get old Kindle plugin configuration string
     val = sc.pop(OLDKINDLEPLUGINNAME, None)
     if val is not None:
-        print u"{0} v{1}: Converting old Kindle plugin configuration string.".format(PLUGIN_NAME, PLUGIN_VERSION)
+        print(u"{0} v{1}: Converting old Kindle plugin configuration string.".format(PLUGIN_NAME, PLUGIN_VERSION))
         priorpidcount = len(dedrmprefs['pids'])
         priorserialcount = len(dedrmprefs['serials'])
         pids, serials = parseKindleString(val)
@@ -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(u"{0} v{1}: {2:d} {3} and {4:d} {5} imported from old Kindle plugin configuration string.".format(PLUGIN_NAME, PLUGIN_VERSION, addedpidcount, u"PID" if addedpidcount==1 else u"PIDs", addedserialcount, u"serial number" if addedserialcount==1 else u"serial numbers"))
     # Make the json write all the prefs to disk
     dedrmprefs.writeprefs(False)
 
@@ -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(u"{0} v{1}: {2:d} Barnes and Noble {3} imported from config folder.".format(PLUGIN_NAME, PLUGIN_VERSION, addedkeycount, u"key file" if addedkeycount==1 else u"key files"))
     # Make the json write all the prefs to disk
     dedrmprefs.writeprefs(False)
 
@@ -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(u"{0} v{1}: {2:d} Adobe Adept {3} imported from config folder.".format(PLUGIN_NAME, PLUGIN_VERSION, addedkeycount, u"keyfile" if addedkeycount==1 else u"keyfiles"))
     # Make the json write all the prefs to disk
     dedrmprefs.writeprefs(False)
 
@@ -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(u"{0} v{1}: {2:d} Barnes and Noble {3} imported from Ignoble plugin preferences.".format(PLUGIN_NAME, PLUGIN_VERSION, addedkeycount, u"key" if addedkeycount==1 else u"keys"))
     # Make the json write all the prefs to disk
     dedrmprefs.writeprefs(False)
 
@@ -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(u"{0} v{1}: {2:d} {3} imported from Kindle plugin preferences".format(PLUGIN_NAME, PLUGIN_VERSION, addedpidcount, u"PID" if addedpidcount==1 else u"PIDs"))
     addedserialcount = len(dedrmprefs['serials']) - priorserialcount
     if addedserialcount > 0:
-        print u"{0} v{1}: {2:d} {3} imported from Kindle plugin preferences".format(PLUGIN_NAME, PLUGIN_VERSION, addedserialcount, u"serial number" if addedserialcount==1 else u"serial numbers")
+        print(u"{0} v{1}: {2:d} {3} imported from Kindle plugin preferences".format(PLUGIN_NAME, PLUGIN_VERSION, addedserialcount, u"serial number" if addedserialcount==1 else u"serial numbers"))
     try:
         if 'wineprefix' in kindleprefs and kindleprefs['wineprefix'] != "":
             dedrmprefs.set('adobewineprefix',kindleprefs['wineprefix'])
             dedrmprefs.set('kindlewineprefix',kindleprefs['wineprefix'])
-            print u"{0} v{1}: WINEPREFIX ‘(2)’ imported from Kindle plugin preferences".format(PLUGIN_NAME, PLUGIN_VERSION, kindleprefs['wineprefix'])
+            print(u"{0} v{1}: WINEPREFIX ‘(2)’ imported from Kindle plugin preferences".format(PLUGIN_NAME, PLUGIN_VERSION, kindleprefs['wineprefix']))
     except:
         traceback.print_exc()
 
 
     # Make the json write all the prefs to disk
     dedrmprefs.writeprefs()
-    print u"{0} v{1}: Finished setting up configuration data.".format(PLUGIN_NAME, PLUGIN_VERSION)
+    print(u"{0} v{1}: Finished setting up configuration data.".format(PLUGIN_NAME, PLUGIN_VERSION))
index bcdf0a7eb85e8599e3ea009f3adb7129aed63473..25f23ad29d903d4f4a7c9d428c6af0a8fdd99070 100644 (file)
@@ -6,13 +6,13 @@ from __future__ import print_function
 import sys
 import os
 import re
-import ineptepub
-import ignobleepub
-import epubtest
-import zipfix
-import ineptpdf
-import erdr2pml
-import k4mobidedrm
+import calibre_plugins.dedrm.ineptepub
+import calibre_plugins.dedrm.ignobleepub
+import calibre_plugins.dedrm.epubtest
+import calibre_plugins.dedrm.zipfix
+import calibre_plugins.dedrm.ineptpdf
+import calibre_plugins.dedrm.erdr2pml
+import calibre_plugins.dedrm.k4mobidedrm
 import traceback
 
 def decryptepub(infile, outdir, rscpath):
@@ -46,7 +46,7 @@ def decryptepub(infile, outdir, rscpath):
                     if rv == 0:
                         print("Decrypted Adobe ePub with key file {0}".format(filename))
                         break
-                except Exception, e:
+                except Exception as e:
                     errlog += traceback.format_exc()
                     errlog += str(e)
                     rv = 1
@@ -66,7 +66,7 @@ def decryptepub(infile, outdir, rscpath):
                     if rv == 0:
                         print("Decrypted B&N ePub with key file {0}".format(filename))
                         break
-                except Exception, e:
+                except Exception as e:
                     errlog += traceback.format_exc()
                     errlog += str(e)
                     rv = 1
@@ -104,7 +104,7 @@ def decryptpdf(infile, outdir, rscpath):
                 rv = ineptpdf.decryptBook(userkey, infile, outfile)
                 if rv == 0:
                     break
-            except Exception, e:
+            except Exception as e:
                 errlog += traceback.format_exc()
                 errlog += str(e)
                 rv = 1
@@ -132,7 +132,7 @@ def decryptpdb(infile, outdir, rscpath):
                 return 1
             try:
                 rv = erdr2pml.decryptBook(infile, outpath, True, erdr2pml.getuser_key(name, cc8))
-            except Exception, e:
+            except Exception as e:
                 errlog += traceback.format_exc()
                 errlog += str(e)
                 rv = 1
@@ -193,7 +193,7 @@ def decryptk4mobi(infile, outdir, rscpath):
             androidFiles.append(dpath)
     try:
         rv = k4mobidedrm.decryptBook(infile, outdir, kDatabaseFiles, androidFiles, serialnums, pidnums)
-    except Exception, e:
+    except Exception as e:
         errlog += traceback.format_exc()
         errlog += str(e)
         rv = 1
index 08099444ab2bd8da6b44781a4849d5f8b0989662..55246639eabac52f20be09ba18b261362d15a9f5 100644 (file)
@@ -46,7 +46,7 @@ class SimplePrefs(object):
                     try :
                         data = file(filepath,'rb').read()
                         self.prefs[key] = data
-                    except Exception, e:
+                    except Exception as e:
                         pass
 
     def getPreferences(self):
@@ -71,7 +71,7 @@ class SimplePrefs(object):
                     else:
                         try:
                             file(filepath,'wb').write(data)
-                        except Exception, e:
+                        except Exception as e:
                             pass
         self.prefs = newprefs
         return
index 7d91f2cf5cc36327bc167114c7ff731136038662..ca0101d46bb04c1c4caafab68d0c03f98bab638b 100644 (file)
@@ -7,9 +7,10 @@
 # Changelog
 #  4.9  - moved unicode_argv call inside main for Windows DeDRM compatibility
 #  5.0  - Fixed potential unicode problem with command line interface
+#  6.0  - Added Python 3 compatibility for calibre 5.0
 
 from __future__ import print_function
-__version__ = '5.0'
+__version__ = '6.0'
 
 import sys
 import os, csv, getopt
@@ -17,7 +18,7 @@ import zlib, zipfile, tempfile, shutil
 import traceback
 from struct import pack
 from struct import unpack
-from alfcrypto import Topaz_Cipher
+from calibre_plugins.dedrm.alfcrypto import Topaz_Cipher
 
 class SafeUnbuffered:
     def __init__(self, stream):
@@ -26,7 +27,7 @@ class SafeUnbuffered:
         if self.encoding == None:
             self.encoding = "utf-8"
     def write(self, data):
-        if isinstance(data,unicode):
+        if isinstance(data,bytes):
             data = data.encode(self.encoding,"replace")
         self.stream.write(data)
         self.stream.flush()
@@ -64,7 +65,7 @@ def unicode_argv():
             # Remove Python executable and commands if present
             start = argc.value - len(sys.argv)
             return [argv[i] for i in
-                    xrange(start, argc.value)]
+                    range(start, argc.value)]
         # if we don't have any arguments at all, just pass back script name
         # this should never happen
         return [u"mobidedrm.py"]
@@ -72,7 +73,7 @@ def unicode_argv():
         argvencoding = sys.stdin.encoding
         if argvencoding == None:
             argvencoding = 'utf-8'
-        return [arg if (type(arg) == unicode) else unicode(arg,argvencoding) for arg in sys.argv]
+        return argv
 
 #global switch
 debug = False
@@ -196,7 +197,7 @@ def decryptDkeyRecords(data,PID):
 
 class TopazBook:
     def __init__(self, filename):
-        self.fo = file(filename, 'rb')
+        self.fo = open(filename, 'rb')
         self.outdir = tempfile.mkdtemp()
         # self.outdir = 'rawdat'
         self.bookPayloadOffset = 0
@@ -319,7 +320,7 @@ class TopazBook:
         fixedimage=True
         try:
             keydata = self.getBookPayloadRecord('dkey', 0)
-        except DrmException, e:
+        except DrmException as e:
             print(u"no dkey record found, book may not be encrypted")
             print(u"attempting to extrct files without a book key")
             self.createBookDirectory()
@@ -345,7 +346,7 @@ class TopazBook:
             data = keydata
             try:
                 bookKeys+=decryptDkeyRecords(data,pid)
-            except DrmException, e:
+            except DrmException as e:
                 pass
             else:
                 bookKey = bookKeys[0]
@@ -357,7 +358,7 @@ class TopazBook:
 
         self.setBookKey(bookKey)
         self.createBookDirectory()
-        self.extractFiles() 
+        self.extractFiles()
         print(u"Successfully Extracted Topaz contents")
         if inCalibre:
             from calibre_plugins.dedrm import genbook
@@ -411,7 +412,7 @@ class TopazBook:
                     print(u".", end=' ')
                     record = self.getBookPayloadRecord(name,index)
                     if record != '':
-                        file(outputFile, 'wb').write(record)
+                        open(outputFile, 'wb').write(record)
                 print(u" ")
 
     def getFile(self, zipname):
@@ -454,7 +455,7 @@ def cli_main():
 
     try:
         opts, args = getopt.getopt(argv[1:], "k:p:s:x")
-    except getopt.GetoptError, err:
+    except getopt.GetoptError as err:
         print(u"Error in options or arguments: {0}".format(err.args[0]))
         usage(progname)
         return 1
@@ -513,7 +514,7 @@ def cli_main():
         # removing internal temporary directory of pieces
         tb.cleanup()
 
-    except DrmException, e:
+    except DrmException as e:
         print(u"Decryption failed\n{0}".format(traceback.format_exc()))
 
         try:
@@ -522,8 +523,8 @@ def cli_main():
             pass
         return 1
 
-    except Exception, e:
-        print(u"Decryption failed\m{0}".format(traceback.format_exc()))
+    except Exception as e:
+        print(u"Decryption failed\n{0}".format(traceback.format_exc()))
         try:
             tb.cleanup()
         except:
index c62b0436b8c05fe33ba5a7a352bcb2e886e8fb9d..56c64fd1effb1cdce202b228d0579a3f04869e00 100644 (file)
@@ -3,7 +3,7 @@
 
 from __future__ import with_statement
 
-from ignoblekeygen import generate_key
+from calibre_plugins.dedrm.ignoblekeygen import generate_key
 
 __license__ = 'GPL v3'
 
@@ -21,8 +21,8 @@ DETAILED_MESSAGE = \
 
 def uStrCmp (s1, s2, caseless=False):
     import unicodedata as ud
-    str1 = s1 if isinstance(s1, unicode) else unicode(s1)
-    str2 = s2 if isinstance(s2, unicode) else unicode(s2)
+    str1 = s1 if isinstance(s1, unicode) else s1.decode('utf-8')
+    str2 = s2 if isinstance(s2, unicode) else s2.decode('utf-8')
     if caseless:
         return ud.normalize('NFC', str1.lower()) == ud.normalize('NFC', str2.lower())
     else:
index 65435a26735663ddbd59ffd00ebc00175be744c5..c5d4dee6317dff252ce2bd38dcec0f2bf79bfc76 100644 (file)
@@ -40,7 +40,7 @@ def WineGetKeys(scriptpath, extension, wineprefix=""):
         cmdline = cmdline.encode(sys.getfilesystemencoding())
         p2 = Process(cmdline, shell=True, bufsize=1, stdin=None, stdout=sys.stdout, stderr=STDOUT, close_fds=False)
         result = p2.wait("wait")
-    except Exception, e:
+    except Exception as e:
         print(u"{0} v{1}: Wine subprocess call error: {2}".format(PLUGIN_NAME, PLUGIN_VERSION, e.args[0]))
         if wineprefix != "" and os.path.exists(wineprefix):
             cmdline = u"WINEPREFIX=\"{2}\" wine C:\\Python27\\python.exe \"{0}\" \"{1}\"".format(scriptpath,outdirpath,wineprefix)
@@ -52,7 +52,7 @@ def WineGetKeys(scriptpath, extension, wineprefix=""):
            cmdline = cmdline.encode(sys.getfilesystemencoding())
            p2 = Process(cmdline, shell=True, bufsize=1, stdin=None, stdout=sys.stdout, stderr=STDOUT, close_fds=False)
            result = p2.wait("wait")
-        except Exception, e:
+        except Exception as e:
            print(u"{0} v{1}: Wine subprocess call error: {2}".format(PLUGIN_NAME, PLUGIN_VERSION, e.args[0]))
 
     # try finding winekeys anyway, even if above code errored
index 4a55a69c7ee42b1f590d42335b633664740eb707..d20dab6d8427f19cb7dc0017c6de345574bcd49d 100644 (file)
@@ -2,10 +2,12 @@
 Read and write ZIP files.
 """
 import struct, os, time, sys, shutil
-import binascii, cStringIO, stat
+import binascii, stat
 import io
 import re
 
+from io import BytesIO
+
 try:
     import zlib # We may need its compression method
     crc32 = zlib.crc32
@@ -45,8 +47,8 @@ ZIP_DEFLATED = 8
 
 # The "end of central directory" structure, magic number, size, and indices
 # (section V.I in the format document)
-structEndArchive = "<4s4H2LH"
-stringEndArchive = "PK\005\006"
+structEndArchive = b"<4s4H2LH"
+stringEndArchive = b"PK\005\006"
 sizeEndCentDir = struct.calcsize(structEndArchive)
 
 _ECD_SIGNATURE = 0
@@ -64,8 +66,8 @@ _ECD_LOCATION = 9
 
 # The "central directory" structure, magic number, size, and indices
 # of entries in the structure (section V.F in the format document)
-structCentralDir = "<4s4B4HL2L5H2L"
-stringCentralDir = "PK\001\002"
+structCentralDir = b"<4s4B4HL2L5H2L"
+stringCentralDir = b"PK\001\002"
 sizeCentralDir = struct.calcsize(structCentralDir)
 
 # indexes of entries in the central directory structure
@@ -91,8 +93,8 @@ _CD_LOCAL_HEADER_OFFSET = 18
 
 # The "local file header" structure, magic number, size, and indices
 # (section V.A in the format document)
-structFileHeader = "<4s2B4HL2L2H"
-stringFileHeader = "PK\003\004"
+structFileHeader = b"<4s2B4HL2L2H"
+stringFileHeader = b"PK\003\004"
 sizeFileHeader = struct.calcsize(structFileHeader)
 
 _FH_SIGNATURE = 0
@@ -109,14 +111,14 @@ _FH_FILENAME_LENGTH = 10
 _FH_EXTRA_FIELD_LENGTH = 11
 
 # The "Zip64 end of central directory locator" structure, magic number, and size
-structEndArchive64Locator = "<4sLQL"
-stringEndArchive64Locator = "PK\x06\x07"
+structEndArchive64Locator = b"<4sLQL"
+stringEndArchive64Locator = b"PK\x06\x07"
 sizeEndCentDir64Locator = struct.calcsize(structEndArchive64Locator)
 
 # The "Zip64 end of central directory" record, magic number, size, and indices
 # (section V.G in the format document)
-structEndArchive64 = "<4sQ2H2L4Q"
-stringEndArchive64 = "PK\x06\x06"
+structEndArchive64 = b"<4sQ2H2L4Q"
+stringEndArchive64 = b"PK\x06\x06"
 sizeEndCentDir64 = struct.calcsize(structEndArchive64)
 
 _CD64_SIGNATURE = 0
@@ -275,7 +277,7 @@ class ZipInfo (object):
 
         # Terminate the file name at the first null byte.  Null bytes in file
         # names are used as tricks by viruses in archives.
-        null_byte = filename.find(chr(0))
+        null_byte = filename.find(b"\0")
         if null_byte >= 0:
             filename = filename[0:null_byte]
         # This is used to ensure paths in generated ZIP files always use
@@ -288,8 +290,8 @@ class ZipInfo (object):
         self.date_time = date_time      # year, month, day, hour, min, sec
         # Standard values:
         self.compress_type = ZIP_STORED # Type of compression for the file
-        self.comment = ""               # Comment for each file
-        self.extra = ""                 # ZIP extra data
+        self.comment = b""               # Comment for each file
+        self.extra = b""                 # ZIP extra data
         if sys.platform == 'win32':
             self.create_system = 0          # System which created ZIP archive
         else:
@@ -343,23 +345,13 @@ class ZipInfo (object):
         return header + filename + extra
 
     def _encodeFilenameFlags(self):
-        if isinstance(self.filename, unicode):
+        if isinstance(self.filename, bytes):
+            return self.filename, self.flag_bits
+        else:
             try:
                 return self.filename.encode('ascii'), self.flag_bits
             except UnicodeEncodeError:
                 return self.filename.encode('utf-8'), self.flag_bits | 0x800
-        else:
-            return self.filename, self.flag_bits
-
-    def _decodeFilename(self):
-        if self.flag_bits & 0x800:
-            try:
-                #print "decoding filename",self.filename
-                return self.filename.decode('utf-8')
-            except:
-                return self.filename
-        else:
-            return self.filename
 
     def _decodeExtra(self):
         # Try to decode the extra field.
@@ -377,20 +369,20 @@ class ZipInfo (object):
                 elif ln == 0:
                     counts = ()
                 else:
-                    raise RuntimeError, "Corrupt extra field %s"%(ln,)
+                    raise RuntimeError("Corrupt extra field %s"%(ln,))
 
                 idx = 0
 
                 # ZIP64 extension (large files and/or large archives)
-                if self.file_size in (0xffffffffffffffffL, 0xffffffffL):
+                if self.file_size in (0xffffffffffffffff, 0xffffffff):
                     self.file_size = counts[idx]
                     idx += 1
 
-                if self.compress_size == 0xFFFFFFFFL:
+                if self.compress_size == 0xFFFFFFFF:
                     self.compress_size = counts[idx]
                     idx += 1
 
-                if self.header_offset == 0xffffffffL:
+                if self.header_offset == 0xffffffff:
                     old = self.header_offset
                     self.header_offset = counts[idx]
                     idx+=1
@@ -481,9 +473,9 @@ class ZipExtFile(io.BufferedIOBase):
 
         if self._compress_type == ZIP_DEFLATED:
             self._decompressor = zlib.decompressobj(-15)
-        self._unconsumed = ''
+        self._unconsumed = b''
 
-        self._readbuffer = ''
+        self._readbuffer = b''
         self._offset = 0
 
         self._universal = 'U' in mode
@@ -514,10 +506,10 @@ class ZipExtFile(io.BufferedIOBase):
         if not self._universal:
             return io.BufferedIOBase.readline(self, limit)
 
-        line = ''
+        line = b''
         while limit < 0 or len(line) < limit:
             readahead = self.peek(2)
-            if readahead == '':
+            if readahead == b'':
                 return line
 
             #
@@ -564,7 +556,7 @@ class ZipExtFile(io.BufferedIOBase):
         If the argument is omitted, None, or negative, data is read and returned until EOF is reached..
         """
 
-        buf = ''
+        buf = b''
         while n < 0 or n is None or n > len(buf):
             data = self.read1(n)
             if len(data) == 0:
@@ -594,7 +586,7 @@ class ZipExtFile(io.BufferedIOBase):
             self._compress_left -= len(data)
 
             if data and self._decrypter is not None:
-                data = ''.join(map(self._decrypter, data))
+                data = b''.join(map(self._decrypter, data))
 
             if self._compress_type == ZIP_STORED:
                 self._readbuffer = self._readbuffer[self._offset:] + data
@@ -651,10 +643,10 @@ class ZipFile:
             pass
         elif compression == ZIP_DEFLATED:
             if not zlib:
-                raise RuntimeError,\
-                      "Compression requires the (missing) zlib module"
+                raise RuntimeError(
+                      "Compression requires the (missing) zlib module")
         else:
-            raise RuntimeError, "That compression method is not supported"
+            raise RuntimeError("That compression method is not supported")
 
         self._allowZip64 = allowZip64
         self._didModify = False
@@ -664,10 +656,10 @@ class ZipFile:
         self.compression = compression  # Method of compression
         self.mode = key = mode.replace('b', '')[0]
         self.pwd = None
-        self.comment = ''
+        self.comment = b''
 
         # Check if we were passed a file-like object
-        if isinstance(file, basestring):
+        if isinstance(file, str):
             self._filePassed = 0
             self.filename = file
             modeDict = {'r' : 'rb', 'w': 'wb', 'a' : 'r+b'}
@@ -699,7 +691,7 @@ class ZipFile:
             if not self._filePassed:
                 self.fp.close()
                 self.fp = None
-            raise RuntimeError, 'Mode must be "r", "w" or "a"'
+            raise RuntimeError('Mode must be "r", "w" or "a"')
 
     def __enter__(self):
         return self
@@ -723,9 +715,9 @@ class ZipFile:
         fp = self.fp
         endrec = _EndRecData(fp)
         if not endrec:
-            raise BadZipfile, "File is not a zip file"
+            raise BadZipfile("File is not a zip file")
         if self.debug > 1:
-            print endrec
+            print(endrec)
         size_cd = endrec[_ECD_SIZE]             # bytes in central directory
         offset_cd = endrec[_ECD_OFFSET]         # offset of central directory
         self.comment = endrec[_ECD_COMMENT]     # archive comment
@@ -738,20 +730,20 @@ class ZipFile:
 
         if self.debug > 2:
             inferred = concat + offset_cd
-            print "given, inferred, offset", offset_cd, inferred, concat
+            print("given, inferred, offset", offset_cd, inferred, concat)
         # self.start_dir:  Position of start of central directory
         self.start_dir = offset_cd + concat
         fp.seek(self.start_dir, 0)
         data = fp.read(size_cd)
-        fp = cStringIO.StringIO(data)
+        fp = BytesIO(data)
         total = 0
         while total < size_cd:
             centdir = fp.read(sizeCentralDir)
             if centdir[0:4] != stringCentralDir:
-                raise BadZipfile, "Bad magic number for central directory"
+                raise BadZipfile("Bad magic number for central directory")
             centdir = struct.unpack(structCentralDir, centdir)
             if self.debug > 2:
-                print centdir
+                print(centdir)
             filename = fp.read(centdir[_CD_FILENAME_LENGTH])
             # Create ZipInfo instance to store file information
             x = ZipInfo(filename)
@@ -769,7 +761,6 @@ class ZipFile:
 
             x._decodeExtra()
             x.header_offset = x.header_offset + concat
-            x.filename = x._decodeFilename()
             self.filelist.append(x)
             self.NameToInfo[x.filename] = x
 
@@ -779,7 +770,7 @@ class ZipFile:
                      + centdir[_CD_COMMENT_LENGTH])
 
             if self.debug > 2:
-                print "total", total
+                print("total", total)
 
 
     def namelist(self):
@@ -796,10 +787,10 @@ class ZipFile:
 
     def printdir(self):
         """Print a table of contents for the zip file."""
-        print "%-46s %19s %12s" % ("File Name", "Modified    ", "Size")
+        print("%-46s %19s %12s" % ("File Name", "Modified    ", "Size"))
         for zinfo in self.filelist:
             date = "%d-%02d-%02d %02d:%02d:%02d" % zinfo.date_time[:6]
-            print "%-46s %s %12d" % (zinfo.filename, date, zinfo.file_size)
+            print("%-46s %s %12d" % (zinfo.filename, date, zinfo.file_size))
 
     def testzip(self):
         """Read all the files and check the CRC."""
@@ -834,10 +825,10 @@ 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"'
+            raise RuntimeError('open() requires mode "r", "U", or "rU"')
         if not self.fp:
-            raise RuntimeError, \
-                  "Attempt to read ZIP archive that was already closed"
+            raise RuntimeError(
+                  "Attempt to read ZIP archive that was already closed")
 
         # Only open a new file for instances where we were not
         # given a file object in the constructor
@@ -859,7 +850,7 @@ class ZipFile:
         # Skip the file header:
         fheader = zef_file.read(sizeFileHeader)
         if fheader[0:4] != stringFileHeader:
-            raise BadZipfile, "Bad magic number for file header"
+            raise BadZipfile("Bad magic number for file header")
 
         fheader = struct.unpack(structFileHeader, fheader)
         fname = zef_file.read(fheader[_FH_FILENAME_LENGTH])
@@ -867,9 +858,9 @@ class ZipFile:
             zef_file.read(fheader[_FH_EXTRA_FIELD_LENGTH])
 
         if fname != zinfo.orig_filename:
-            raise BadZipfile, \
+            raise BadZipfile(
                       'File name in directory "%s" and header "%s" differ.' % (
-                          zinfo.orig_filename, fname)
+                          zinfo.orig_filename, fname))
 
         # check for encrypted flag & handle password
         is_encrypted = zinfo.flag_bits & 0x1
@@ -878,8 +869,8 @@ class ZipFile:
             if not pwd:
                 pwd = self.pwd
             if not pwd:
-                raise RuntimeError"File %s is encrypted, " \
-                      "password required for extraction" % name
+                raise RuntimeError("File %s is encrypted, " \
+                      "password required for extraction" % name)
 
             zd = _ZipDecrypter(pwd)
             # The first 12 bytes in the cypher stream is an encryption header
@@ -956,7 +947,7 @@ class ZipFile:
             return targetpath
 
         source = self.open(member, pwd=pwd)
-        target = file(targetpath, "wb")
+        target = open(targetpath, "wb")
         shutil.copyfileobj(source, target)
         source.close()
         target.close()
@@ -967,18 +958,18 @@ class ZipFile:
         """Check for errors before writing a file to the archive."""
         if zinfo.filename in self.NameToInfo:
             if self.debug:      # Warning for duplicate names
-                print "Duplicate name:", zinfo.filename
+                print("Duplicate name:", zinfo.filename)
         if self.mode not in ("w", "a"):
-            raise RuntimeError, 'write() requires mode "w" or "a"'
+            raise RuntimeError('write() requires mode "w" or "a"')
         if not self.fp:
-            raise RuntimeError, \
-                  "Attempt to write ZIP archive that was already closed"
+            raise RuntimeError(
+                  "Attempt to write ZIP archive that was already closed")
         if zinfo.compress_type == ZIP_DEFLATED and not zlib:
-            raise RuntimeError, \
-                  "Compression requires the (missing) zlib module"
+            raise RuntimeError(
+                  "Compression requires the (missing) zlib module")
         if zinfo.compress_type not in (ZIP_STORED, ZIP_DEFLATED):
-            raise RuntimeError, \
-                  "That compression method is not supported"
+            raise RuntimeError(
+                  "That compression method is not supported")
         if zinfo.file_size > ZIP64_LIMIT:
             if not self._allowZip64:
                 raise LargeZipFile("Filesize would require ZIP64 extensions")
@@ -1006,7 +997,7 @@ class ZipFile:
         if isdir:
             arcname += '/'
         zinfo = ZipInfo(arcname, date_time)
-        zinfo.external_attr = (st[0] & 0xFFFF) << 16L      # Unix attributes
+        zinfo.external_attr = (st[0] & 0xFFFF) << 16      # Unix attributes
         if compress_type is None:
             zinfo.compress_type = self.compression
         else:
@@ -1076,7 +1067,7 @@ class ZipFile:
                             date_time=time.localtime(time.time())[:6])
 
             zinfo.compress_type = self.compression
-            zinfo.external_attr = 0600 << 16
+            zinfo.external_attr = 0x0600 << 16
         else:
             zinfo = zinfo_or_arcname
 
@@ -1141,7 +1132,7 @@ class ZipFile:
 
                 if zinfo.header_offset > ZIP64_LIMIT:
                     extra.append(zinfo.header_offset)
-                    header_offset = 0xffffffffL
+                    header_offset = 0xffffffff
                 else:
                     header_offset = zinfo.header_offset
 
@@ -1169,14 +1160,14 @@ class ZipFile:
                      0, zinfo.internal_attr, zinfo.external_attr,
                      header_offset)
                 except DeprecationWarning:
-                    print >>sys.stderr, (structCentralDir,
+                    print(structCentralDir,
                      stringCentralDir, create_version,
                      zinfo.create_system, extract_version, zinfo.reserved,
                      zinfo.flag_bits, zinfo.compress_type, dostime, dosdate,
                      zinfo.CRC, compress_size, file_size,
                      len(zinfo.filename), len(extra_data), len(zinfo.comment),
                      0, zinfo.internal_attr, zinfo.external_attr,
-                     header_offset)
+                     header_offset, sys.stderr)
                     raise
                 self.fp.write(centdir)
                 self.fp.write(filename)
@@ -1250,10 +1241,10 @@ class PyZipFile(ZipFile):
                 else:
                     basename = name
                 if self.debug:
-                    print "Adding package in", pathname, "as", basename
+                    print("Adding package in", pathname, "as", basename)
                 fname, arcname = self._get_codename(initname[0:-3], basename)
                 if self.debug:
-                    print "Adding", arcname
+                    print("Adding", arcname)
                 self.write(fname, arcname)
                 dirlist = os.listdir(pathname)
                 dirlist.remove("__init__.py")
@@ -1269,12 +1260,12 @@ class PyZipFile(ZipFile):
                         fname, arcname = self._get_codename(path[0:-3],
                                          basename)
                         if self.debug:
-                            print "Adding", arcname
+                            print("Adding", arcname)
                         self.write(fname, arcname)
             else:
                 # This is NOT a package directory, add its files at top level
                 if self.debug:
-                    print "Adding files from directory", pathname
+                    print("Adding files from directory", pathname)
                 for filename in os.listdir(pathname):
                     path = os.path.join(pathname, filename)
                     root, ext = os.path.splitext(filename)
@@ -1282,15 +1273,15 @@ class PyZipFile(ZipFile):
                         fname, arcname = self._get_codename(path[0:-3],
                                          basename)
                         if self.debug:
-                            print "Adding", arcname
+                            print("Adding", arcname)
                         self.write(fname, arcname)
         else:
             if pathname[-3:] != ".py":
-                raise RuntimeError, \
-                      'Files added with writepy() must end with ".py"'
+                raise RuntimeError(
+                      'Files added with writepy() must end with ".py"')
             fname, arcname = self._get_codename(pathname[0:-3], basename)
             if self.debug:
-                print "Adding file", arcname
+                print("Adding file", arcname)
             self.write(fname, arcname)
 
     def _get_codename(self, pathname, basename):
@@ -1310,11 +1301,11 @@ class PyZipFile(ZipFile):
              os.stat(file_pyc).st_mtime < os.stat(file_py).st_mtime:
             import py_compile
             if self.debug:
-                print "Compiling", file_py
+                print("Compiling", file_py)
             try:
                 py_compile.compile(file_py, file_pyc, None, True)
-            except py_compile.PyCompileError,err:
-                print err.msg
+            except py_compile.PyCompileError as err:
+                print(err.msg)
             fname = file_pyc
         else:
             fname = file_pyc
@@ -1337,12 +1328,12 @@ def main(args = None):
         args = sys.argv[1:]
 
     if not args or args[0] not in ('-l', '-c', '-e', '-t'):
-        print USAGE
+        print(USAGE)
         sys.exit(1)
 
     if args[0] == '-l':
         if len(args) != 2:
-            print USAGE
+            print(USAGE)
             sys.exit(1)
         zf = ZipFile(args[1], 'r')
         zf.printdir()
@@ -1350,15 +1341,15 @@ def main(args = None):
 
     elif args[0] == '-t':
         if len(args) != 2:
-            print USAGE
+            print(USAGE)
             sys.exit(1)
         zf = ZipFile(args[1], 'r')
         zf.testzip()
-        print "Done testing"
+        print("Done testing")
 
     elif args[0] == '-e':
         if len(args) != 3:
-            print USAGE
+            print(USAGE)
             sys.exit(1)
 
         zf = ZipFile(args[1], 'r')
@@ -1378,7 +1369,7 @@ def main(args = None):
 
     elif args[0] == '-c':
         if len(args) < 3:
-            print USAGE
+            print(USAGE)
             sys.exit(1)
 
         def addToZip(zf, path, zippath):
index d3371f2cbf4e5a1268fb623dd3acb22dee4e3666..190cf443a8a4fdaa185a0357c5bc3e625f9939a7 100644 (file)
@@ -1,8 +1,8 @@
 #!/usr/bin/env python
 # -*- coding: utf-8 -*-
 
-# zipfix.py, version 1.1
-# Copyright © 2010-2013 by some_updates, DiapDealer and Apprentice Alf
+# zipfix.py
+# Copyright © 2010-2020 by some_updates, DiapDealer and Apprentice Alf
 
 # Released under the terms of the GNU General Public Licence, version 3
 # <http://www.gnu.org/licenses/>
@@ -10,6 +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
 
 """
 Re-write zip (or ePub) fixing problems with file names (and mimetype entry).
@@ -21,7 +22,7 @@ __version__ = "1.1"
 
 import sys
 import zlib
-import zipfilerugged
+import calibre_plugins.dedrm.zipfilerugged as zipfilerugged
 import os
 import os.path
 import getopt
@@ -49,7 +50,7 @@ class fixZip:
         self.inzip = zipfilerugged.ZipFile(zinput,'r')
         self.outzip = zipfilerugged.ZipFile(zoutput,'w')
         # open the input zip for reading only as a raw file
-        self.bzf = file(zinput,'rb')
+        self.bzf = open(zinput,'rb')
 
     def getlocalname(self, zi):
         local_header_offset = zi.header_offset
@@ -115,7 +116,7 @@ class fixZip:
         # if epub write mimetype file first, with no compression
         if self.ztype == 'epub':
             # first get a ZipInfo with current time and no compression
-            mimeinfo = ZipInfo('mimetype',compress_type=zipfilerugged.ZIP_STORED)
+            mimeinfo = ZipInfo(b'mimetype',compress_type=zipfilerugged.ZIP_STORED)
             mimeinfo.internal_attr = 1 # text file
             try:
                 # if the mimetype is present, get its info, including time-stamp
@@ -129,7 +130,7 @@ class fixZip:
                 mimeinfo.create_system = oldmimeinfo.create_system
             except:
                 pass
-            self.outzip.writestr(mimeinfo, _MIMETYPE)
+            self.outzip.writestr(mimeinfo, _MIMETYPE.encode('ascii'))
 
         # write the rest of the files
         for zinfo in self.inzip.infolist():
@@ -171,7 +172,7 @@ def repairBook(infile, outfile):
         fr = fixZip(infile, outfile)
         fr.fix()
         return 0
-    except Exception, e:
+    except Exception as e:
         print("Error Occurred ", e)
         return 2
 
index a0b63a6df4bb888e6d7f6765d77f7a7e90b3e486..802a833a5e6737b0093655920c57dd1bfee66b3e 100644 (file)
@@ -6,13 +6,14 @@ __license__   = 'GPL v3'
 __docformat__ = 'restructuredtext en'
 
 
+import codecs
 import os, traceback, zipfile
 
 try:
     from PyQt5.Qt import QToolButton, QUrl
 except ImportError:
     from PyQt4.Qt import QToolButton, QUrl
+
 from calibre.gui2 import open_url, question_dialog
 from calibre.gui2.actions import InterfaceAction
 from calibre.utils.config import config_dir
@@ -24,7 +25,7 @@ from calibre.ebooks.metadata.meta import get_metadata
 from calibre_plugins.obok_dedrm.dialogs import (SelectionDialog, DecryptAddProgressDialog,
                                                 AddEpubFormatsProgressDialog, ResultsSummaryDialog)
 from calibre_plugins.obok_dedrm.config import plugin_prefs as cfg
-from calibre_plugins.obok_dedrm.__init__ import (PLUGIN_NAME, PLUGIN_SAFE_NAME, 
+from calibre_plugins.obok_dedrm.__init__ import (PLUGIN_NAME, PLUGIN_SAFE_NAME,
                                 PLUGIN_VERSION, PLUGIN_DESCRIPTION, HELPFILE_NAME)
 from calibre_plugins.obok_dedrm.utilities import (
                             get_icon, set_plugin_icon_resources, showErrorDlg, format_plural,
@@ -53,7 +54,7 @@ class InterfacePluginAction(InterfaceAction):
     def genesis(self):
         icon_resources = self.load_resources(PLUGIN_ICONS)
         set_plugin_icon_resources(PLUGIN_NAME, icon_resources)
-        
+
         self.qaction.setIcon(get_icon(PLUGIN_ICONS[0]))
         self.qaction.triggered.connect(self.launchObok)
         self.gui.keyboard.finalize()
@@ -106,10 +107,10 @@ class InterfacePluginAction(InterfaceAction):
         # Get a list of Kobo titles
         books = self.build_book_list()
         if len(books) < 1:
-            msg = _('<p>No books found in Kobo Library\nAre you sure it\'s installed\configured\synchronized?')
+            msg = _('<p>No books found in Kobo Library\nAre you sure it\'s installed/configured/synchronized?')
             showErrorDlg(msg, None)
             return
-        
+
         # Check to see if a key can be retrieved using the legacy obok method.
         legacy_key = legacy_obok().get_legacy_cookie_id
         if legacy_key is not None:
@@ -154,7 +155,7 @@ class InterfacePluginAction(InterfaceAction):
         # Close Kobo Library object
         self.library.close()
 
-        # If we have decrypted books to work with, feed the list of decrypted books details 
+        # If we have decrypted books to work with, feed the list of decrypted books details
         # and the callback function (self.add_new_books) to the ProgressDialog dispatcher.
         if len(self.books_to_add):
             d = DecryptAddProgressDialog(self.gui, self.books_to_add, self.add_new_books, self.db, 'calibre',
@@ -212,7 +213,7 @@ class InterfacePluginAction(InterfaceAction):
     def get_decrypted_kobo_books(self, book):
         '''
         This method is a call-back function used by DecryptAddProgressDialog in dialogs.py to decrypt Kobo books
-        
+
         :param book: A KoboBook object that is to be decrypted.
         '''
         print (_('{0} - Decrypting {1}').format(PLUGIN_NAME + ' v' + PLUGIN_VERSION, book.title))
@@ -233,7 +234,7 @@ class InterfacePluginAction(InterfaceAction):
         '''
         This method is a call-back function used by DecryptAddProgressDialog in dialogs.py to add books to calibre
         (It's set up to handle multiple books, but will only be fed books one at a time by DecryptAddProgressDialog)
-        
+
         :param books_to_add: List of calibre bookmaps (created in get_decrypted_kobo_books)
         '''
         added = self.db.add_books(books_to_add, add_duplicates=False, run_hooks=False)
@@ -253,7 +254,7 @@ class InterfacePluginAction(InterfaceAction):
     def add_epub_format(self, book_id, mi, path):
         '''
         This method is a call-back function used by AddEpubFormatsProgressDialog in dialogs.py
-        
+
         :param book_id: calibre ID of the book to add the encrypted epub to.
         :param mi: calibre metadata object
         :param path: path to the decrypted epub (temp file)
@@ -281,7 +282,7 @@ class InterfacePluginAction(InterfaceAction):
                 self.formats_to_add.append((home_id, mi, tmp_file))
             else:
                 self.no_home_for_book.append(mi)
-        # If we found homes for decrypted epubs in existing calibre entries, feed the list of decrypted book 
+        # If we found homes for decrypted epubs in existing calibre entries, feed the list of decrypted book
         # details and the callback function (self.add_epub_format) to the ProgressDialog dispatcher.
         if self.formats_to_add:
             d = AddEpubFormatsProgressDialog(self.gui, self.formats_to_add, self.add_epub_format)
@@ -306,10 +307,10 @@ class InterfacePluginAction(InterfaceAction):
         sd = ResultsSummaryDialog(self.gui, caption, msg, log)
         sd.exec_()
         return
-    
+
     def ask_about_inserting_epubs(self):
         '''
-        Build question dialog with details about kobo books 
+        Build question dialog with details about kobo books
         that couldn't be added to calibre as new books.
         '''
         ''' Terisa: Improve the message
@@ -327,13 +328,13 @@ class InterfacePluginAction(InterfaceAction):
             msg = _('<p><b>{0}</b> -- not added because of {1} in your library.<br /><br />').format(self.duplicate_book_list[0][0].title, self.duplicate_book_list[0][2])
             msg += _('Would you like to try and add the EPUB format to an available calibre duplicate?<br /><br />')
             msg += _('NOTE: no pre-existing EPUB will be overwritten.')
-            
+
         return question_dialog(self.gui, caption, msg, det_msg)
 
     def find_a_home(self, ids):
         '''
         Find the ID of the first EPUB-Free duplicate available
-        
+
         :param ids: List of calibre IDs that might serve as a home.
         '''
         for id in ids:
@@ -373,7 +374,7 @@ class InterfacePluginAction(InterfaceAction):
         zin = zipfile.ZipFile(book.filename, 'r')
         #print ('Kobo library filename: {0}'.format(book.filename))
         for userkey in self.userkeys:
-            print (_('Trying key: '), userkey.encode('hex_codec'))
+            print (_('Trying key: '), codecs.encode(userkey, 'hex'))
             check = True
             try:
                 fileout = PersistentTemporaryFile('.epub', dir=self.tdir)
@@ -455,7 +456,7 @@ class InterfacePluginAction(InterfaceAction):
                     if cancelled_count > 0:
                         log += _('<p><b>Book imports cancelled by user:</b> {}</p>\n').format(cancelled_count)
                     return (msg, log)
-                log += _('<p><b>New EPUB formats inserted in existing calibre books:</b> {0}</p>\n').format(len(self.successful_format_adds))        
+                log += _('<p><b>New EPUB formats inserted in existing calibre books:</b> {0}</p>\n').format(len(self.successful_format_adds))
                 if self.successful_format_adds:
                     log += '<ul>\n'
                     for id, mi in self.successful_format_adds:
@@ -474,7 +475,7 @@ class InterfacePluginAction(InterfaceAction):
                         log += _('<p><b>Format imports cancelled by user:</b> {}</p>\n').format(cancelled_count)
                 return (msg, log)
             else:
-                
+
                 # Single book ... don't get fancy.
                 if self.ids_of_new_books:
                     title = self.ids_of_new_books[0][1].title
@@ -494,4 +495,4 @@ class InterfacePluginAction(InterfaceAction):
                     reason = _('of unknown reasons. Gosh I\'m embarrassed!')
                 msg = _('<p>{0} not added because {1}').format(title, reason)
                 return (msg, log)
+
index 0f2164a04c5d04ea09c73966ded2a0d7a44b426e..babad1c5df5570087b3f949a6f0b3822d2fbec43 100644 (file)
@@ -427,7 +427,7 @@ class KeyValueComboBox(QComboBox):
 
     def selected_key(self):
         for key, value in self.values.iteritems():
-            if value == unicode(self.currentText()).strip():
+            if value == self.currentText().strip():
                 return key
 
 
@@ -450,7 +450,7 @@ class KeyComboBox(QComboBox):
 
     def selected_key(self):
         for key, value in self.values.iteritems():
-            if key == unicode(self.currentText()).strip():
+            if key == self.currentText().strip():
                 return key
 
 
index 8244b91f40585fb94305983b51bc45ef67f58ea6..522b86a403ad7fa7d6407bf0f974dc825ee3f6b4 100644 (file)
@@ -75,7 +75,7 @@ class ConfigWidget(QWidget):
 
 
     def save_settings(self):
-        plugin_prefs['finding_homes_for_formats'] = unicode(self.find_homes.currentText())
+        plugin_prefs['finding_homes_for_formats'] = self.find_homes.currentText()
         plugin_prefs['kobo_serials'] = self.tmpserials
         plugin_prefs['kobo_directory'] = self.kobodirectory
 
@@ -165,7 +165,7 @@ class ManageKeysDialog(QDialog):
     def delete_key(self):
         if not self.listy.currentItem():
             return
-        keyname = unicode(self.listy.currentItem().text())
+        keyname = self.listy.currentItem().text()
         if not question_dialog(self, "{0} {1}: Confirm Delete".format(PLUGIN_NAME, PLUGIN_VERSION), u"Do you really want to delete the {1} <strong>{0}</strong>?".format(keyname, self.key_type_name), show_copy_button=False, default_yes=False):
             return
         self.plugin_keys.remove(keyname)
@@ -202,11 +202,11 @@ class AddSerialDialog(QDialog):
 
     @property
     def key_name(self):
-        return unicode(self.key_ledit.text()).strip()
+        return self.key_ledit.text().strip()
 
     @property
     def key_value(self):
-        return unicode(self.key_ledit.text()).strip()
+        return self.key_ledit.text().strip()
 
     def accept(self):
         if len(self.key_name) == 0 or self.key_name.isspace():
index 21ef14aef37cfff1a634b8524781da7487029f14..80bc058f7e34342cfc4e34045360928c4606ecf0 100644 (file)
@@ -1,6 +1,9 @@
 #!/usr/bin/env python
 # -*- coding: utf-8 -*-
 
+# Version 4.0.0 September 2020
+# Python 3.0
+#
 # Version 3.2.5 December 2016
 # Improve detection of good text decryption.
 #
 """Manage all Kobo books, either encrypted or DRM-free."""
 from __future__ import print_function
 
-__version__ = '3.2.4'
-__about__ =  u"Obok v{0}\nCopyright © 2012-2016 Physisticated et al.".format(__version__)
+__version__ = '4.0.0'
+__about__ =  u"Obok v{0}\nCopyright © 2012-2020 Physisticated et al.".format(__version__)
 
 import sys
 import os
@@ -231,7 +234,7 @@ def _load_crypto_libcrypto():
                 raise ENCRYPTIONError(_('Failed to initialize AES key'))
 
         def decrypt(self, data):
-            clear = ''
+            clear = b''
             for i in range(0, len(data), 16):
                 out = create_string_buffer(16)
                 rv = AES_ecb_encrypt(data[i:i+16], out, self._key, 0)
@@ -276,7 +279,7 @@ class SafeUnbuffered:
         if self.encoding == None:
             self.encoding = "utf-8"
     def write(self, data):
-        if isinstance(data,unicode):
+        if isinstance(data,bytes):
             data = data.encode(self.encoding,"replace")
         self.stream.write(data)
         self.stream.flush()
@@ -381,7 +384,7 @@ class KoboLibrary(object):
             print(self.newdb.name)
             olddb = open(kobodb, 'rb')
             self.newdb.write(olddb.read(18))
-            self.newdb.write('\x01\x01')
+            self.newdb.write(b'\x01\x01')
             olddb.read(2)
             self.newdb.write(olddb.read())
             olddb.close()
@@ -488,14 +491,14 @@ class KoboLibrary(object):
                 pass
             row = cursor.fetchone()
         return userids
-               
+
     def __getuserkeys (self, macaddr):
         userids = self.__getuserids()
         userkeys = []
         for hash in KOBO_HASH_KEYS:
-            deviceid = hashlib.sha256(hash + macaddr).hexdigest()
+            deviceid = hashlib.sha256((hash + macaddr).encode('ascii')).hexdigest()
             for userid in userids:
-                userkey = hashlib.sha256(deviceid + userid).hexdigest()
+                userkey = hashlib.sha256((deviceid + userid).encode('ascii')).hexdigest()
                 userkeys.append(binascii.a2b_hex(userkey[32:]))
         return userkeys
 
@@ -556,7 +559,7 @@ class KoboBook(object):
             # Convert relative URIs
             href = item.attrib['href']
             if not c.match(href):
-                href = string.join((basedir, href), '')
+                href = ''.join((basedir, href))
 
             # Update books we've found from the DB.
             if href in self._encryptedfiles:
@@ -606,57 +609,57 @@ class KoboFile(object):
             stride = 1
             print(u"Checking text:{0}:".format(contents[:10]))
             # check for byte order mark
-            if contents[:3]=="\xef\xbb\xbf":
+            if contents[:3]==b"\xef\xbb\xbf":
                 # seems to be utf-8 with BOM
                 print(u"Could be utf-8 with BOM")
                 textoffset = 3
-            elif contents[:2]=="\xfe\xff":
+            elif contents[:2]==b"\xfe\xff":
                 # seems to be utf-16BE
                 print(u"Could be  utf-16BE")
                 textoffset = 3
                 stride = 2
-            elif contents[:2]=="\xff\xfe":
+            elif contents[:2]==b"\xff\xfe":
                 # seems to be utf-16LE
                 print(u"Could be  utf-16LE")
                 textoffset = 2
                 stride = 2
             else:
                 print(u"Perhaps utf-8 without BOM")
-                
+
             # now check that the first few characters are in the ASCII range
-            for i in xrange(textoffset,textoffset+5*stride,stride):
-                if ord(contents[i])<32 or ord(contents[i])>127:
+            for i in range(textoffset,textoffset+5*stride,stride):
+                if contents[i]<32 or contents[i]>127:
                     # Non-ascii, so decryption probably failed
-                    print(u"Bad character at {0}, value {1}".format(i,ord(contents[i])))
+                    print(u"Bad character at {0}, value {1}".format(i,contents[i]))
                     raise ValueError
             print(u"Seems to be good text")
             return True
-            if contents[:5]=="<?xml" or contents[:8]=="\xef\xbb\xbf<?xml":
+            if contents[:5]==b"<?xml" or contents[:8]==b"\xef\xbb\xbf<?xml":
                 # utf-8
                 return True
-            elif contents[:14]=="\xfe\xff\x00<\x00?\x00x\x00m\x00l":
+            elif contents[:14]==b"\xfe\xff\x00<\x00?\x00x\x00m\x00l":
                 # utf-16BE
                 return True
-            elif contents[:14]=="\xff\xfe<\x00?\x00x\x00m\x00l\x00":
+            elif contents[:14]==b"\xff\xfe<\x00?\x00x\x00m\x00l\x00":
                 # utf-16LE
                 return True
-            elif contents[:9]=="<!DOCTYPE" or contents[:12]=="\xef\xbb\xbf<!DOCTYPE":
+            elif contents[:9]==b"<!DOCTYPE" or contents[:12]==b"\xef\xbb\xbf<!DOCTYPE":
                 # utf-8 of weird <!DOCTYPE start
                 return True
-            elif contents[:22]=="\xfe\xff\x00<\x00!\x00D\x00O\x00C\x00T\x00Y\x00P\x00E":
+            elif contents[:22]==b"\xfe\xff\x00<\x00!\x00D\x00O\x00C\x00T\x00Y\x00P\x00E":
                 # utf-16BE of weird <!DOCTYPE start
                 return True
-            elif contents[:22]=="\xff\xfe<\x00!\x00D\x00O\x00C\x00T\x00Y\x00P\x00E\x00":
+            elif contents[:22]==b"\xff\xfe<\x00!\x00D\x00O\x00C\x00T\x00Y\x00P\x00E\x00":
                 # utf-16LE of weird <!DOCTYPE start
                 return True
             else:
                 print(u"Bad XML: {0}".format(contents[:8]))
                 raise ValueError
         elif self.mimetype == 'image/jpeg':
-            if contents[:3] == '\xff\xd8\xff':
+            if contents[:3] == b'\xff\xd8\xff':
                 return True
             else:
-                print(u"Bad JPEG: {0}".format(contents[:3].encode('hex')))
+                print(u"Bad JPEG: {0}".format(contents[:3].hex()))
                 raise ValueError()
         return False
 
@@ -690,7 +693,7 @@ def decrypt_book(book, lib):
         return 0
     result = 1
     for userkey in lib.userkeys:
-        print(u"Trying key: {0}".format(userkey.encode('hex_codec')))
+        print(u"Trying key: {0}".format(userkey.hex()))
         try:
             zout = zipfile.ZipFile(outname, "w", zipfile.ZIP_DEFLATED)
             for filename in zin.namelist():
@@ -735,7 +738,7 @@ def cli_main():
             print(u"{0}: {1}".format(i + 1, book.title))
         print(u"Or 'all'")
 
-        choice = raw_input(u"Convert book number... ")
+        choice = input(u"Convert book number... ")
         if choice == u'all':
             books = list(lib.books)
         else:
index 62e305c4df08dd76c0c748e690d9fd58da8d467d..f81bd1c91f3bd99ffa5439e47b8c9eb3795e766c 100644 (file)
@@ -7,21 +7,24 @@ __docformat__ = 'restructuredtext en'
 
 
 import os, struct, time
-from StringIO import StringIO
+try:
+    from StringIO import StringIO
+except ImportError:
+    from io import StringIO
 from traceback import print_exc
 
 try:
     from PyQt5.Qt import (Qt, QDialog, QPixmap, QIcon, QLabel, QHBoxLayout, QFont, QTableWidgetItem)
 except ImportError:
     from PyQt4.Qt import (Qt, QDialog, QPixmap, QIcon, QLabel, QHBoxLayout, QFont, QTableWidgetItem)
-    
+
 from calibre.utils.config import config_dir
 from calibre.constants import iswindows, DEBUG
 from calibre import prints
 from calibre.gui2 import (error_dialog, gprefs)
 from calibre.gui2.actions import menu_action_unique_name
 
-from calibre_plugins.obok_dedrm.__init__ import (PLUGIN_NAME,  
+from calibre_plugins.obok_dedrm.__init__ import (PLUGIN_NAME,
                     PLUGIN_SAFE_NAME, PLUGIN_VERSION, PLUGIN_DESCRIPTION)
 
 plugin_ID = None
@@ -39,7 +42,7 @@ else:
     def convert_qvariant(x):
         vt = x.type()
         if vt == x.String:
-            return unicode(x.toString())
+            return x.toString()
         if vt == x.List:
             return [convert_qvariant(i) for i in x.toList()]
         return x.toPyObject()
@@ -62,7 +65,7 @@ except NameError:
 def format_plural(number, possessive=False):
     '''
     Cosmetic ditty to provide the proper string formatting variable to handle singular/plural situations
-    
+
     :param: number: variable that represents the count/len of something
     '''
     if not possessive:
@@ -141,7 +144,7 @@ def showErrorDlg(errmsg, parent, trcbk=False):
     '''
     if trcbk:
         error= ''
-        f=StringIO()     
+        f=StringIO()
         print_exc(file=f)
         error_mess = f.getvalue().splitlines()
         for line in error_mess:
index dbadc5d06a80b4beee9c12d0b169bc50fd161c03..98ae209eef38619f3f97c43f9e7c5ed14abe12be 100644 (file)
@@ -4,7 +4,7 @@
 from __future__ import with_statement
 
 # ignoblekey.py
-# Copyright © 2015 Apprentice Alf and Apprentice Harper
+# Copyright © 2015-2020 Apprentice Harper et al.
 
 # Based on kindlekey.py, Copyright © 2010-2013 by some_updates and Apprentice Alf
 
@@ -14,13 +14,14 @@ from __future__ import with_statement
 # Revision history:
 #   1.0 - Initial release
 #   1.1 - remove duplicates and return last key as single key
+#   2.0 - Python 3
 
 """
 Get Barnes & Noble EPUB user key from nook Studio log file
 """
 
 __license__ = 'GPL v3'
-__version__ = "1.1"
+__version__ = "2.0"
 
 import sys
 import os
@@ -318,7 +319,7 @@ def gui_main():
                 keyfileout.write(key)
             success = True
             tkMessageBox.showinfo(progname, u"Key successfully retrieved to {0}".format(outfile))
-    except DrmException, e:
+    except DrmException as e:
         tkMessageBox.showerror(progname, u"Error: {0}".format(str(e)))
     except Exception:
         root.wm_state('normal')
index e88c3cbbfdfbf8c37a0830f1755c5f448149f25b..bedb956297e8194957e28ebd8535b09878fbfca1 100755 (executable)
@@ -13,10 +13,10 @@ import os
 import shutil
 
 
-DEDRM_SRC_DIR = 'DeDRM_Plugin'
-DEDRM_README= 'DeDRM_Plugin_ReadMe.txt'
+DEDRM_SRC_DIR = 'DeDRM_plugin'
+DEDRM_README= 'DeDRM_plugin_ReadMe.txt'
 OBOK_SRC_DIR = 'Obok_plugin'
-OBOK_README = 'Obok_plugin_ReadMe.txt'
+OBOK_README = 'obok_plugin_ReadMe.txt'
 RELEASE_DIR = 'release'
 
 def make_calibre_plugin():