mirror of
				https://github.com/noDRM/DeDRM_tools.git
				synced 2025-10-23 23:07:47 -04:00 
			
		
		
		
	
		
			
				
	
	
		
			637 lines
		
	
	
	
		
			32 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			637 lines
		
	
	
	
		
			32 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| #!/usr/bin/env python
 | |
| # -*- coding: utf-8 -*-
 | |
| 
 | |
| from __future__ import with_statement
 | |
| __license__   = 'GPL v3'
 | |
| __docformat__ = 'restructuredtext en'
 | |
| 
 | |
| 
 | |
| # Released under the terms of the GNU General Public Licence, version 3
 | |
| # <http://www.gnu.org/licenses/>
 | |
| #
 | |
| # All credit given to i♥cabbages and The Dark Reverser for the original standalone scripts.
 | |
| # We had the much easier job of converting them to a calibre plugin.
 | |
| #
 | |
| # This plugin is meant to decrypt eReader PDBs, Adobe Adept ePubs, Barnes & Noble ePubs,
 | |
| # Adobe Adept PDFs, Amazon Kindle and Mobipocket files without having to
 | |
| # install any dependencies... other than having calibre installed, of course.
 | |
| #
 | |
| # Configuration:
 | |
| # Check out the plugin's configuration settings by clicking the "Customize plugin"
 | |
| # button when you have the "DeDRM" plugin highlighted (under Preferences->
 | |
| # Plugins->File type plugins). Once you have the configuration dialog open, you'll
 | |
| # see a Help link on the top right-hand side.
 | |
| #
 | |
| # Revision history:
 | |
| #   6.0.0 - Initial release
 | |
| #   6.0.1 - Bug Fixes for Windows App, Kindle for Mac and Windows Adobe Digital Editions
 | |
| #   6.0.2 - Restored call to Wine to get Kindle for PC keys, added for ADE
 | |
| #   6.0.3 - Fixes for Kindle for Mac and Windows non-ascii user names
 | |
| #   6.0.4 - Fixes for stand-alone scripts and applications
 | |
| #           and pdb files in plugin and initial conversion of prefs.
 | |
| #   6.0.5 - Fix a key issue
 | |
| #   6.0.6 - Fix up an incorrect function call
 | |
| #   6.0.7 - Error handling for incomplete PDF metadata
 | |
| #   6.0.8 - Fixes a Wine key issue and topaz support
 | |
| #   6.0.9 - Ported to work with newer versions of Calibre (moved to Qt5). Still supports older Qt4 versions.
 | |
| #   6.1.0 - Fixed multiple books import problem and PDF import with no key problem
 | |
| #   6.2.0 - Support for getting B&N key from nook Study log. Fix for UTF-8 filenames in Adobe ePubs.
 | |
| #           Fix for not copying needed files. Fix for getting default Adobe key for PDFs
 | |
| #   6.2.1 - Fix for non-ascii Windows user names
 | |
| #   6.2.2 - Added URL method for B&N/nook books
 | |
| #   6.3.0 - Added in Kindle for Android serial number solution
 | |
| #   6.3.1 - Version number bump for clarity
 | |
| #   6.3.2 - Fixed Kindle for Android help file
 | |
| #   6.3.3 - Bug fix for Kindle for PC support
 | |
| #   6.3.4 - Fixes for Kindle for Android, Linux, and Kobo 3.17
 | |
| #   6.3.5 - Fixes for Linux, and Kobo 3.19 and more logging
 | |
| #   6.3.6 - Fixes for ADE ePub and PDF introduced in 6.3.5
 | |
| #   6.4.0 - Updated for new Kindle for PC encryption
 | |
| #   6.4.1 - Fix for some new tags in Topaz ebooks.
 | |
| #   6.4.2 - Fix for more new tags in Topaz ebooks and very small Topaz ebooks
 | |
| #   6.4.3 - Fix for error that only appears when not in debug mode
 | |
| #           Also includes fix for Macs with bonded ethernet ports
 | |
| #   6.5.0 - Big update to Macintosh app
 | |
| #           Fix for some more 'new' tags in Topaz ebooks.
 | |
| #           Fix an error in wineutils.py
 | |
| #   6.5.1 - Updated version number, added PDF check for DRM-free documents
 | |
| #   6.5.2 - Another Topaz fix
 | |
| #   6.5.3 - Warn about KFX files explicitly
 | |
| 
 | |
| 
 | |
| """
 | |
| Decrypt DRMed ebooks.
 | |
| """
 | |
| 
 | |
| PLUGIN_NAME = u"DeDRM"
 | |
| PLUGIN_VERSION_TUPLE = (6, 5, 3)
 | |
| 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 sys, os, re
 | |
| import time
 | |
| import zipfile
 | |
| import traceback
 | |
| from zipfile import ZipFile
 | |
| 
 | |
| class DeDRMError(Exception):
 | |
|     pass
 | |
| 
 | |
| from calibre.customize import FileTypePlugin
 | |
| from calibre.constants import iswindows, isosx
 | |
| from calibre.gui2 import is_ok_to_use_qt
 | |
| from calibre.utils.config import config_dir
 | |
| 
 | |
| 
 | |
| # Wrap a stream so that output gets flushed immediately
 | |
| # and also make sure that any unicode strings get safely
 | |
| # encoded using "replace" before writing them.
 | |
| class SafeUnbuffered:
 | |
|     def __init__(self, stream):
 | |
|         self.stream = stream
 | |
|         self.encoding = stream.encoding
 | |
|         if self.encoding == None:
 | |
|             self.encoding = "utf-8"
 | |
|     def write(self, data):
 | |
|         if isinstance(data,unicode):
 | |
|             data = data.encode(self.encoding,"replace")
 | |
|         try:
 | |
|             self.stream.write(data)
 | |
|             self.stream.flush()
 | |
|         except:
 | |
|             # We can do nothing if a write fails
 | |
|             pass
 | |
|     def __getattr__(self, attr):
 | |
|         return getattr(self.stream, attr)
 | |
| 
 | |
| class DeDRM(FileTypePlugin):
 | |
|     name                    = PLUGIN_NAME
 | |
|     description             = u"Removes DRM from Amazon Kindle, Adobe Adept (including Kobo), Barnes & Noble, Mobipocket and eReader ebooks. Credit given to i♥cabbages and The Dark Reverser for the original stand-alone scripts."
 | |
|     supported_platforms     = ['linux', 'osx', 'windows']
 | |
|     author                  = u"Apprentice Alf, Aprentice Harper, The Dark Reverser and i♥cabbages"
 | |
|     version                 = PLUGIN_VERSION_TUPLE
 | |
|     minimum_calibre_version = (1, 0, 0)  # Compiled python libraries cannot be imported in earlier versions.
 | |
|     file_types              = set(['epub','pdf','pdb','prc','mobi','pobi','azw','azw1','azw3','azw4','tpz'])
 | |
|     on_import               = True
 | |
|     priority                = 600
 | |
| 
 | |
| 
 | |
|     def initialize(self):
 | |
|         """
 | |
|         Dynamic modules can't be imported/loaded from a zipfile.
 | |
|         So this routine will extract the appropriate
 | |
|         library for the target OS and copy it to the 'alfcrypto' subdirectory of
 | |
|         calibre's configuration directory. That 'alfcrypto' directory is then
 | |
|         inserted into the syspath (as the very first entry) in the run function
 | |
|         so the CDLL stuff will work in the alfcrypto.py script.
 | |
| 
 | |
|         The extraction only happens once per version of the plugin
 | |
|         Also perform upgrade of preferences once per version
 | |
|         """
 | |
|         try:
 | |
|             self.pluginsdir = os.path.join(config_dir,u"plugins")
 | |
|             if not os.path.exists(self.pluginsdir):
 | |
|                 os.mkdir(self.pluginsdir)
 | |
|             self.maindir = os.path.join(self.pluginsdir,u"DeDRM")
 | |
|             if not os.path.exists(self.maindir):
 | |
|                 os.mkdir(self.maindir)
 | |
|             self.helpdir = os.path.join(self.maindir,u"help")
 | |
|             if not os.path.exists(self.helpdir):
 | |
|                 os.mkdir(self.helpdir)
 | |
|             self.alfdir = os.path.join(self.maindir,u"libraryfiles")
 | |
|             if not os.path.exists(self.alfdir):
 | |
|                 os.mkdir(self.alfdir)
 | |
|             # only continue if we've never run this version of the plugin before
 | |
|             self.verdir = os.path.join(self.maindir,PLUGIN_VERSION)
 | |
|             if not os.path.exists(self.verdir):
 | |
|                 if iswindows:
 | |
|                     names = [u"alfcrypto.dll",u"alfcrypto64.dll"]
 | |
|                 elif isosx:
 | |
|                     names = [u"libalfcrypto.dylib"]
 | |
|                 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)
 | |
| 
 | |
|                 for entry, data in lib_dict.items():
 | |
|                     file_path = os.path.join(self.alfdir, entry)
 | |
|                     try:
 | |
|                         os.remove(file_path)
 | |
|                     except:
 | |
|                         pass
 | |
| 
 | |
|                     try:
 | |
|                         open(file_path,'wb').write(data)
 | |
|                     except:
 | |
|                         print u"{0} v{1}: Exception when copying needed library files".format(PLUGIN_NAME, PLUGIN_VERSION)
 | |
|                         traceback.print_exc()
 | |
|                         pass
 | |
| 
 | |
|                 # convert old preferences, if necessary.
 | |
|                 from calibre_plugins.dedrm.prefs import convertprefs
 | |
|                 convertprefs()
 | |
| 
 | |
|                 # mark that this version has been initialized
 | |
|                 os.mkdir(self.verdir)
 | |
|         except Exception, e:
 | |
|             traceback.print_exc()
 | |
|             raise
 | |
| 
 | |
|     def ePubDecrypt(self,path_to_ebook):
 | |
|         # Create a TemporaryPersistent file to work with.
 | |
|         # Check original epub archive for zip errors.
 | |
|         import calibre_plugins.dedrm.zipfix
 | |
| 
 | |
|         inf = self.temporary_file(u".epub")
 | |
|         try:
 | |
|             print u"{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])
 | |
|             raise Exception(e)
 | |
| 
 | |
|         # import the decryption keys
 | |
|         import calibre_plugins.dedrm.prefs as prefs
 | |
|         dedrmprefs = prefs.DeDRM_Prefs()
 | |
| 
 | |
|         # import the Barnes & Noble ePub handler
 | |
|         import calibre_plugins.dedrm.ignobleepub as ignobleepub
 | |
| 
 | |
| 
 | |
|         #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))
 | |
| 
 | |
|             # 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)
 | |
|                 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)
 | |
|                     traceback.print_exc()
 | |
|                     result = 1
 | |
| 
 | |
|                 of.close()
 | |
| 
 | |
|                 if  result == 0:
 | |
|                     # Decryption was successful.
 | |
|                     # 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)
 | |
| 
 | |
|             # 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)
 | |
| 
 | |
|             # get the default NOOK Study keys
 | |
|             defaultkeys = []
 | |
| 
 | |
|             try:
 | |
|                 if iswindows or isosx:
 | |
|                     from calibre_plugins.dedrm.ignoblekey import nookkeys
 | |
| 
 | |
|                     defaultkeys = nookkeys()
 | |
|                 else: # linux
 | |
|                     from wineutils import WineGetKeys
 | |
| 
 | |
|                     scriptpath = os.path.join(self.alfdir,u"ignoblekey.py")
 | |
|                     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)
 | |
|                 traceback.print_exc()
 | |
| 
 | |
|             newkeys = []
 | |
|             for keyvalue in defaultkeys:
 | |
|                 if keyvalue not in dedrmprefs['bandnkeys'].values():
 | |
|                     newkeys.append(keyvalue)
 | |
| 
 | |
|             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)
 | |
| 
 | |
|                         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)
 | |
|                            traceback.print_exc()
 | |
|                            result = 1
 | |
| 
 | |
|                         of.close()
 | |
| 
 | |
|                         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)
 | |
|                             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)
 | |
|                             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)
 | |
|                                 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:
 | |
|                     pass
 | |
| 
 | |
|             print u"{0} v{1}: Ultimately failed to decrypt after {2:.1f} seconds.\nRead 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.".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))
 | |
| 
 | |
|             # 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)
 | |
|                 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)
 | |
|                     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)
 | |
| 
 | |
|                 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)
 | |
|                     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)
 | |
| 
 | |
|             # 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)
 | |
| 
 | |
|             # get the default Adobe keys
 | |
|             defaultkeys = []
 | |
| 
 | |
|             try:
 | |
|                 if iswindows or isosx:
 | |
|                     from calibre_plugins.dedrm.adobekey import adeptkeys
 | |
| 
 | |
|                     defaultkeys = adeptkeys()
 | |
|                 else: # linux
 | |
|                     from wineutils import WineGetKeys
 | |
| 
 | |
|                     scriptpath = os.path.join(self.alfdir,u"adobekey.py")
 | |
|                     defaultkeys = WineGetKeys(scriptpath, u".der",dedrmprefs['adobewineprefix'])
 | |
| 
 | |
|                 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)
 | |
|                 traceback.print_exc()
 | |
|                 self.default_key = u""
 | |
| 
 | |
|             newkeys = []
 | |
|             for keyvalue in defaultkeys:
 | |
|                 if keyvalue.encode('hex') not in dedrmprefs['adeptkeys'].values():
 | |
|                     newkeys.append(keyvalue)
 | |
| 
 | |
|             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)
 | |
|                         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)
 | |
|                             traceback.print_exc()
 | |
|                             result = 1
 | |
| 
 | |
|                         of.close()
 | |
| 
 | |
|                         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)
 | |
|                             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)
 | |
|                             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)
 | |
|                                 traceback.print_exc()
 | |
|                             print u"{0} v{1}: Decrypted with new default key after {3:.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:
 | |
|                     pass
 | |
| 
 | |
|             # Something went wrong with decryption.
 | |
|             print u"{0} v{1}: Ultimately failed to decrypt after {2:.1f} seconds.\nRead 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.".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))
 | |
|         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):
 | |
|         import calibre_plugins.dedrm.prefs as prefs
 | |
|         import calibre_plugins.dedrm.ineptpdf
 | |
| 
 | |
|         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))
 | |
|         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)
 | |
|             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)
 | |
|                 traceback.print_exc()
 | |
|                 result = 1
 | |
| 
 | |
|             of.close()
 | |
| 
 | |
|             if  result == 0:
 | |
|                 # Decryption was successful.
 | |
|                 # 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)
 | |
| 
 | |
|         # 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)
 | |
| 
 | |
|         # get the default Adobe keys
 | |
|         defaultkeys = []
 | |
| 
 | |
|         try:
 | |
|             if iswindows or isosx:
 | |
|                 from calibre_plugins.dedrm.adobekey import adeptkeys
 | |
| 
 | |
|                 defaultkeys = adeptkeys()
 | |
|             else: # linux
 | |
|                 from wineutils import WineGetKeys
 | |
| 
 | |
|                 scriptpath = os.path.join(self.alfdir,u"adobekey.py")
 | |
|                 defaultkeys = WineGetKeys(scriptpath, u".der",dedrmprefs['adobewineprefix'])
 | |
| 
 | |
|             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)
 | |
|             traceback.print_exc()
 | |
|             self.default_key = u""
 | |
| 
 | |
|         newkeys = []
 | |
|         for keyvalue in defaultkeys:
 | |
|             if keyvalue.encode('hex') not in dedrmprefs['adeptkeys'].values():
 | |
|                 newkeys.append(keyvalue)
 | |
| 
 | |
|         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)
 | |
|                     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)
 | |
|                         traceback.print_exc()
 | |
|                         result = 1
 | |
| 
 | |
|                     of.close()
 | |
| 
 | |
|                     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)
 | |
|                         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)
 | |
|                         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)
 | |
|                             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:
 | |
|                 pass
 | |
| 
 | |
|         # Something went wrong with decryption.
 | |
|         print u"{0} v{1}: Ultimately failed to decrypt after {2:.1f} seconds.\nRead 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.".format(PLUGIN_NAME, PLUGIN_VERSION, time.time()-self.starttime))
 | |
| 
 | |
| 
 | |
|     def KindleMobiDecrypt(self,path_to_ebook):
 | |
| 
 | |
|         # add the alfcrypto directory to sys.path so alfcrypto.py
 | |
|         # will be able to locate the custom lib(s) for CDLL import.
 | |
|         sys.path.insert(0, self.alfdir)
 | |
|         # Had to move this import here so the custom libs can be
 | |
|         # extracted to the appropriate places beforehand these routines
 | |
|         # look for them.
 | |
|         import calibre_plugins.dedrm.prefs as prefs
 | |
|         import calibre_plugins.dedrm.k4mobidedrm
 | |
| 
 | |
|         dedrmprefs = prefs.DeDRM_Prefs()
 | |
|         pids = dedrmprefs['pids']
 | |
|         serials = dedrmprefs['serials']
 | |
|         for android_serials_list in dedrmprefs['androidkeys'].values():
 | |
|             #print android_serials_list
 | |
|             serials.extend(android_serials_list)
 | |
|         #print serials
 | |
|         androidFiles = []
 | |
|         kindleDatabases = dedrmprefs['kindlekeys'].items()
 | |
| 
 | |
|         try:
 | |
|             book = k4mobidedrm.GetDecryptedBook(path_to_ebook,kindleDatabases,androidFiles,serials,pids,self.starttime)
 | |
|         except Exception, 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)
 | |
| 
 | |
|             try:
 | |
|                 if iswindows or isosx:
 | |
|                     from calibre_plugins.dedrm.kindlekey import kindlekeys
 | |
| 
 | |
|                     defaultkeys = kindlekeys()
 | |
|                 else: # linux
 | |
|                     from wineutils import WineGetKeys
 | |
| 
 | |
|                     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)
 | |
|                 traceback.print_exc()
 | |
|                 pass
 | |
| 
 | |
|             newkeys = {}
 | |
|             for i,keyvalue in enumerate(defaultkeys):
 | |
|                 keyname = u"default_key_{0:d}".format(i+1)
 | |
|                 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")
 | |
|                 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")
 | |
|                     for keyvalue in newkeys.values():
 | |
|                         dedrmprefs.addnamedvaluetoprefs('kindlekeys','default_key',keyvalue)
 | |
|                     dedrmprefs.writeprefs()
 | |
|                 except Exception, 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.\nRead 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".format(PLUGIN_NAME, PLUGIN_VERSION,time.time()-self.starttime))
 | |
| 
 | |
|         of = self.temporary_file(book.getBookExtension())
 | |
|         book.getFile(of.name)
 | |
|         of.close()
 | |
|         book.cleanup()
 | |
|         return of.name
 | |
| 
 | |
| 
 | |
|     def eReaderDecrypt(self,path_to_ebook):
 | |
| 
 | |
|         import calibre_plugins.dedrm.prefs as prefs
 | |
|         import calibre_plugins.dedrm.erdr2pml
 | |
| 
 | |
|         dedrmprefs = prefs.DeDRM_Prefs()
 | |
|         # Attempt to decrypt epub with each encryption key (generated or provided).
 | |
|         for keyname, userkey in dedrmprefs['ereaderkeys'].items():
 | |
|             keyname_masked = u"".join((u'X' if (x.isdigit()) else x) for x in keyname)
 | |
|             print u"{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.
 | |
|             result = erdr2pml.decryptBook(path_to_ebook, of.name, True, userkey.decode('hex'))
 | |
| 
 | |
|             of.close()
 | |
| 
 | |
|             # 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)
 | |
|                 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 u"{0} v{1}: Ultimately failed to decrypt after {2:.1f} seconds.\nRead 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.".format(PLUGIN_NAME, PLUGIN_VERSION, time.time()-self.starttime))
 | |
| 
 | |
| 
 | |
|     def run(self, path_to_ebook):
 | |
| 
 | |
|         # make sure any unicode output gets converted safely with 'replace'
 | |
|         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))
 | |
|         self.starttime = time.time()
 | |
| 
 | |
|         booktype = os.path.splitext(path_to_ebook)[1].lower()[1:]
 | |
|         if booktype in ['prc','mobi','pobi','azw','azw1','azw3','azw4','tpz']:
 | |
|             # Kindle/Mobipocket
 | |
|             decrypted_ebook = self.KindleMobiDecrypt(path_to_ebook)
 | |
|         elif booktype == 'pdb':
 | |
|             # eReader
 | |
|             decrypted_ebook = self.eReaderDecrypt(path_to_ebook)
 | |
|             pass
 | |
|         elif booktype == 'pdf':
 | |
|             # Adobe Adept PDF (hopefully)
 | |
|             decrypted_ebook = self.PDFDecrypt(path_to_ebook)
 | |
|             pass
 | |
|         elif booktype == 'epub':
 | |
|             # 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)
 | |
|             return path_to_ebook
 | |
|         print u"{0} v{1}: Finished after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION,time.time()-self.starttime)
 | |
|         return decrypted_ebook
 | |
| 
 | |
|     def is_customizable(self):
 | |
|         # return true to allow customization via the Plugin->Preferences.
 | |
|         return True
 | |
| 
 | |
|     def config_widget(self):
 | |
|         import calibre_plugins.dedrm.config as config
 | |
|         return config.ConfigWidget(self.plugin_path, self.alfdir)
 | |
| 
 | |
|     def save_settings(self, config_widget):
 | |
|         config_widget.save_settings()
 | 
