2020-09-27 12:54:49 +02:00
#!/usr/bin/env python3
2013-03-20 11:23:54 +01:00
# -*- coding: utf-8 -*-
2017-07-04 08:05:51 +02:00
# __init__.py for DeDRM_plugin
2020-06-18 08:42:41 +02:00
# Copyright © 2008-2020 Apprentice Harper et al.
2021-11-15 11:00:06 +01:00
# Copyright © 2021 NoDRM
2017-07-04 08:05:51 +02:00
2013-03-20 11:23:54 +01:00
__license__ = ' GPL v3 '
2021-11-29 17:06:18 +01:00
__version__ = ' 10.0.2 '
2013-03-20 11:23:54 +01:00
__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,
2020-06-18 08:42:41 +02:00
# Adobe Adept PDFs, Amazon Kindle and Mobipocket files without having
# to install any dependencies... other than having calibre installed, of course.
2013-03-20 11:23:54 +01:00
#
# 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
2013-03-26 17:38:18 +01:00
# 6.0.1 - Bug Fixes for Windows App, Kindle for Mac and Windows Adobe Digital Editions
2013-04-08 17:47:22 +02:00
# 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
2013-04-10 12:50:10 +02:00
# 6.0.4 - Fixes for stand-alone scripts and applications
# and pdb files in plugin and initial conversion of prefs.
2013-10-02 20:59:40 +02:00
# 6.0.5 - Fix a key issue
2013-05-18 20:37:05 +02:00
# 6.0.6 - Fix up an incorrect function call
2013-10-02 20:59:40 +02:00
# 6.0.7 - Error handling for incomplete PDF metadata
# 6.0.8 - Fixes a Wine key issue and topaz support
2015-03-07 22:18:50 +01:00
# 6.0.9 - Ported to work with newer versions of Calibre (moved to Qt5). Still supports older Qt4 versions.
2014-12-08 22:23:07 +01:00
# 6.1.0 - Fixed multiple books import problem and PDF import with no key problem
2015-03-09 08:38:31 +01:00
# 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
2015-03-25 08:26:33 +01:00
# 6.2.1 - Fix for non-ascii Windows user names
2015-06-30 18:27:33 +02:00
# 6.2.2 - Added URL method for B&N/nook books
2015-03-17 18:49:30 +01:00
# 6.3.0 - Added in Kindle for Android serial number solution
2015-08-02 12:09:35 +02:00
# 6.3.1 - Version number bump for clarity
2015-08-04 08:18:33 +02:00
# 6.3.2 - Fixed Kindle for Android help file
2015-08-12 19:35:21 +02:00
# 6.3.3 - Bug fix for Kindle for PC support
2015-09-03 08:51:10 +02:00
# 6.3.4 - Fixes for Kindle for Android, Linux, and Kobo 3.17
2016-01-11 07:44:44 +01:00
# 6.3.5 - Fixes for Linux, and Kobo 3.19 and more logging
2016-01-14 18:15:43 +01:00
# 6.3.6 - Fixes for ADE ePub and PDF introduced in 6.3.5
2016-03-13 13:00:57 +01:00
# 6.4.0 - Updated for new Kindle for PC encryption
2016-04-13 19:39:13 +02:00
# 6.4.1 - Fix for some new tags in Topaz ebooks.
2016-04-18 18:39:17 +02:00
# 6.4.2 - Fix for more new tags in Topaz ebooks and very small Topaz ebooks
2016-04-25 18:49:06 +02:00
# 6.4.3 - Fix for error that only appears when not in debug mode
# Also includes fix for Macs with bonded ethernet ports
2016-08-05 18:34:52 +02:00
# 6.5.0 - Big update to Macintosh app
# Fix for some more 'new' tags in Topaz ebooks.
# Fix an error in wineutils.py
2016-08-10 07:40:48 +02:00
# 6.5.1 - Updated version number, added PDF check for DRM-free documents
2016-09-29 08:00:11 +02:00
# 6.5.2 - Another Topaz fix
2017-01-12 08:24:42 +01:00
# 6.5.3 - Warn about KFX files explicitly
2017-06-27 07:50:24 +02:00
# 6.5.4 - Mac App Fix, improve PDF decryption, handle latest tcl changes in ActivePython
2017-07-04 08:05:51 +02:00
# 6.5.5 - Finally a fix for the Windows non-ASCII user names.
2018-04-05 19:30:37 +02:00
# 6.6.0 - Add kfx and kfx-zip as supported file types (also invoke this plugin if the original
2018-03-13 01:32:41 +01:00
# imported format was azw8 since that may be converted to kfx)
2018-06-02 17:47:00 +02:00
# 6.6.1 - Thanks to wzyboy for a fix for stand-alone tools, and the new folder structure.
2018-12-02 12:51:51 +01:00
# 6.6.2 - revamp of folders to get Mac OS X app working. Updated to 64-bit app. Various fixes.
2019-03-30 16:02:40 +01:00
# 6.6.3 - More cleanup of kindle book names and start of support for .kinf2018
2020-02-16 11:12:25 +01:00
# 6.7.0 - Handle new library in calibre.
2020-06-18 08:42:41 +02:00
# 6.8.0 - Full support for .kinf2018 and new KFX encryption (Kindle for PC/Mac 2.5+)
2020-12-30 13:14:04 +01:00
# 6.8.1 - Kindle key fix for Mac OS X Big Sur
# 7.0.0 - Switched to Python 3 for calibre 5.0. Thanks to all who contributed
# 7.0.1 - More Python 3 changes. Adobe PDF decryption should now work in some cases
2021-01-03 17:01:14 +01:00
# 7.0.2 - More Python 3 changes. Adobe PDF decryption should now work on PC too.
2021-01-03 17:11:02 +01:00
# 7.0.3 - More Python 3 changes. Integer division in ineptpdf.py
2021-02-21 15:35:49 +01:00
# 7.1.0 - Full release for calibre 5.x
2021-04-11 16:28:33 +02:00
# 7.2.0 - Update for latest KFX changes, and Python 3 Obok fixes.
2021-04-11 17:43:16 +02:00
# 7.2.1 - Whitespace!
2021-11-17 21:53:24 +01:00
# 10.0.0 - First forked version by NoDRM. See CHANGELOG.md for details.
2021-11-19 12:42:29 +01:00
# 10.0.1 - Fixes a bug in the watermark code.
2021-11-29 17:06:18 +01:00
# 10.0.2 - Fix Kindle for Mac & update Adobe key retrieval
2013-03-20 11:23:54 +01:00
"""
Decrypt DRMed ebooks .
"""
2020-09-27 12:54:49 +02:00
PLUGIN_NAME = " DeDRM "
2020-11-29 16:54:45 +01:00
PLUGIN_VERSION_TUPLE = tuple ( [ int ( x ) for x in __version__ . split ( " . " ) ] )
2020-09-27 12:54:49 +02:00
PLUGIN_VERSION = " . " . join ( [ str ( x ) for x in PLUGIN_VERSION_TUPLE ] )
2013-03-20 11:23:54 +01:00
# Include an html helpfile in the plugin's zipfile with the following name.
RESOURCE_NAME = PLUGIN_NAME + ' _Help.htm '
2020-09-26 22:22:47 +02:00
import codecs
2021-11-17 16:17:30 +01:00
import sys , os
2013-03-20 11:23:54 +01:00
import time
import traceback
2021-11-16 15:23:54 +01:00
2021-12-29 09:26:29 +01:00
# Calibre stuff - so we can import from our ZIP without absolute module name
sys . path . insert ( 0 , os . path . dirname ( os . path . abspath ( __file__ ) ) )
2013-03-20 11:23:54 +01:00
class DeDRMError ( Exception ) :
pass
2021-12-29 09:26:29 +01:00
try :
from calibre . customize import FileTypePlugin
except :
# Allow import without Calibre.
class FileTypePlugin :
pass
2021-12-27 10:35:02 +01:00
try :
from calibre . constants import iswindows , isosx
except :
iswindows = sys . platform . startswith ( ' win ' )
isosx = sys . platform . startswith ( ' darwin ' )
2021-12-29 09:26:29 +01:00
try :
from calibre . utils . config import config_dir
except :
config_dir = " "
2013-03-20 11:23:54 +01:00
# 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 ) :
2021-11-16 11:09:03 +01:00
if isinstance ( data , str ) or isinstance ( data , unicode ) :
# str for Python3, unicode for Python2
2013-03-20 11:23:54 +01:00
data = data . encode ( self . encoding , " replace " )
2016-04-25 18:49:06 +02:00
try :
2021-11-16 11:09:03 +01:00
buffer = getattr ( self . stream , ' buffer ' , self . stream )
# self.stream.buffer for Python3, self.stream for Python2
buffer . write ( data )
buffer . flush ( )
2016-04-25 18:49:06 +02:00
except :
# We can do nothing if a write fails
2021-11-16 11:09:03 +01:00
raise
2013-03-20 11:23:54 +01:00
def __getattr__ ( self , attr ) :
return getattr ( self . stream , attr )
class DeDRM ( FileTypePlugin ) :
name = PLUGIN_NAME
2021-11-17 21:53:24 +01:00
description = " Removes DRM from Amazon Kindle, Adobe Adept (including Kobo), Readium LCP, Barnes & Noble, Mobipocket and eReader ebooks. Credit given to i♥cabbages and The Dark Reverser for the original stand-alone scripts. "
2013-03-20 11:23:54 +01:00
supported_platforms = [ ' linux ' , ' osx ' , ' windows ' ]
2021-11-15 11:00:06 +01:00
author = " Apprentice Alf, Apprentice Harper, NoDRM, The Dark Reverser and i♥cabbages "
2013-03-20 11:23:54 +01:00
version = PLUGIN_VERSION_TUPLE
2021-11-16 11:09:03 +01:00
#minimum_calibre_version = (5, 0, 0) # Python 3.
minimum_calibre_version = ( 2 , 0 , 0 ) # Needs Calibre 1.0 minimum. 1.X untested.
2018-03-13 01:32:41 +01:00
file_types = set ( [ ' epub ' , ' pdf ' , ' pdb ' , ' prc ' , ' mobi ' , ' pobi ' , ' azw ' , ' azw1 ' , ' azw3 ' , ' azw4 ' , ' azw8 ' , ' tpz ' , ' kfx ' , ' kfx-zip ' ] )
2013-03-20 11:23:54 +01:00
on_import = True
2020-09-20 22:43:23 +02:00
on_preprocess = True
2013-03-20 11:23:54 +01:00
priority = 600
2021-12-29 09:26:29 +01:00
def cli_main ( self , data ) :
from standalone import main
main ( data )
2015-03-09 08:38:31 +01:00
def initialize ( self ) :
2013-03-20 11:23:54 +01:00
"""
2014-12-08 22:23:07 +01:00
Dynamic modules can ' t be imported/loaded from a zipfile.
So this routine will extract the appropriate
2013-03-20 11:23:54 +01:00
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 .
2014-12-08 22:23:07 +01:00
The extraction only happens once per version of the plugin
2015-03-09 08:38:31 +01:00
Also perform upgrade of preferences once per version
2013-03-20 11:23:54 +01:00
"""
2021-11-16 11:09:03 +01:00
2013-03-20 11:23:54 +01:00
try :
2020-09-27 12:54:49 +02:00
self . pluginsdir = os . path . join ( config_dir , " plugins " )
2013-03-20 11:23:54 +01:00
if not os . path . exists ( self . pluginsdir ) :
os . mkdir ( self . pluginsdir )
2020-09-27 12:54:49 +02:00
self . maindir = os . path . join ( self . pluginsdir , " DeDRM " )
2013-03-20 11:23:54 +01:00
if not os . path . exists ( self . maindir ) :
os . mkdir ( self . maindir )
2020-09-27 12:54:49 +02:00
self . helpdir = os . path . join ( self . maindir , " help " )
2013-03-20 11:23:54 +01:00
if not os . path . exists ( self . helpdir ) :
os . mkdir ( self . helpdir )
2020-09-27 12:54:49 +02:00
self . alfdir = os . path . join ( self . maindir , " libraryfiles " )
2013-03-20 11:23:54 +01:00
if not os . path . exists ( self . alfdir ) :
os . mkdir ( self . alfdir )
2014-12-08 22:23:07 +01:00
# 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 ) :
2015-03-09 08:38:31 +01:00
if iswindows :
2020-09-27 12:54:49 +02:00
names = [ " alfcrypto.dll " , " alfcrypto64.dll " ]
2015-03-09 08:38:31 +01:00
elif isosx :
2020-09-27 12:54:49 +02:00
names = [ " libalfcrypto.dylib " ]
2015-03-09 08:38:31 +01:00
else :
2020-09-27 12:54:49 +02:00
names = [ " libalfcrypto32.so " , " libalfcrypto64.so " , " kindlekey.py " , " adobekey.py " , " subasyncio.py " ]
2015-03-09 08:38:31 +01:00
lib_dict = self . load_resources ( names )
2020-09-26 22:22:47 +02:00
print ( " {0} v {1} : Copying needed library files from plugin ' s zip " . format ( PLUGIN_NAME , PLUGIN_VERSION ) )
2015-03-09 08:38:31 +01:00
2014-12-08 22:23:07 +01:00
for entry , data in lib_dict . items ( ) :
file_path = os . path . join ( self . alfdir , entry )
2015-03-09 08:38:31 +01:00
try :
os . remove ( file_path )
except :
pass
2013-03-20 11:23:54 +01:00
2015-03-09 08:38:31 +01:00
try :
open ( file_path , ' wb ' ) . write ( data )
except :
2020-09-26 22:22:47 +02:00
print ( " {0} v {1} : Exception when copying needed library files " . format ( PLUGIN_NAME , PLUGIN_VERSION ) )
2015-03-09 08:38:31 +01:00
traceback . print_exc ( )
pass
2014-12-08 22:23:07 +01:00
2015-03-09 08:38:31 +01:00
# mark that this version has been initialized
os . mkdir ( self . verdir )
2020-09-26 22:22:47 +02:00
except Exception as e :
2014-12-08 22:23:07 +01:00
traceback . print_exc ( )
2015-03-09 08:38:31 +01:00
raise
2014-12-08 22:23:07 +01:00
2021-11-16 15:23:54 +01:00
def postProcessEPUB ( self , path_to_ebook ) :
# This is called after the DRM is removed (or if no DRM was present)
# It does stuff like de-obfuscating fonts (by calling checkFonts)
# or removing watermarks.
2021-11-19 12:42:29 +01:00
postProcessStart = time . time ( )
2021-11-16 15:23:54 +01:00
try :
2021-12-29 09:26:29 +01:00
import prefs
2021-11-17 16:17:30 +01:00
dedrmprefs = prefs . DeDRM_Prefs ( )
2021-11-16 15:23:54 +01:00
2021-11-17 16:17:30 +01:00
if dedrmprefs [ " deobfuscate_fonts " ] is True :
# Deobfuscate fonts
path_to_ebook = self . checkFonts ( path_to_ebook ) or path_to_ebook
2021-11-16 15:23:54 +01:00
2021-11-17 16:17:30 +01:00
if dedrmprefs [ " remove_watermarks " ] is True :
2021-12-29 09:26:29 +01:00
import epubwatermark as watermark
2021-11-16 15:23:54 +01:00
2021-11-17 16:17:30 +01:00
# Remove Tolino's CDP watermark file
path_to_ebook = watermark . removeCDPwatermark ( self , path_to_ebook ) or path_to_ebook
2021-11-16 15:23:54 +01:00
2021-11-19 12:42:29 +01:00
# Remove watermarks (Amazon or LemonInk) from the OPF file
2021-11-17 16:17:30 +01:00
path_to_ebook = watermark . removeOPFwatermarks ( self , path_to_ebook ) or path_to_ebook
2021-11-17 21:53:24 +01:00
2021-12-27 10:39:41 +01:00
# Remove watermarks (Adobe, Pocketbook or LemonInk) from all HTML and XHTML files
2021-11-17 16:17:30 +01:00
path_to_ebook = watermark . removeHTMLwatermarks ( self , path_to_ebook ) or path_to_ebook
2021-11-17 21:53:24 +01:00
2021-12-20 21:06:50 +01:00
postProcessEnd = time . time ( )
print ( " {0} v {1} : Post-processing took {2:.1f} seconds " . format ( PLUGIN_NAME , PLUGIN_VERSION , postProcessEnd - postProcessStart ) )
2021-11-19 12:42:29 +01:00
return path_to_ebook
2021-11-16 15:23:54 +01:00
except :
2021-11-17 16:17:30 +01:00
print ( " Error while checking settings " )
2021-11-16 15:23:54 +01:00
return path_to_ebook
2021-11-15 17:59:48 +01:00
def checkFonts ( self , path_to_ebook ) :
# This is called after the normal DRM removal is done.
# It checks if there's fonts that need to be deobfuscated
2021-11-16 15:23:54 +01:00
try :
2021-12-29 09:26:29 +01:00
import epubfontdecrypt
2021-11-15 17:59:48 +01:00
2021-11-17 21:53:24 +01:00
output = self . temporary_file ( " .epub " ) . name
ret = epubfontdecrypt . decryptFontsBook ( path_to_ebook , output )
2021-11-15 17:59:48 +01:00
2021-11-17 21:53:24 +01:00
if ( ret == 0 ) :
return output
elif ( ret == 1 ) :
return path_to_ebook
else :
print ( " {0} v {1} : Error during font deobfuscation " . format ( PLUGIN_NAME , PLUGIN_VERSION ) )
raise DeDRMError ( " Font deobfuscation failed " )
2021-11-17 16:17:30 +01:00
2021-11-16 15:23:54 +01:00
except :
print ( " {0} v {1} : Error during font deobfuscation " . format ( PLUGIN_NAME , PLUGIN_VERSION ) )
2021-11-17 16:17:30 +01:00
traceback . print_exc ( )
2021-11-15 17:59:48 +01:00
return path_to_ebook
2013-03-20 11:23:54 +01:00
def ePubDecrypt ( self , path_to_ebook ) :
# Create a TemporaryPersistent file to work with.
# Check original epub archive for zip errors.
2021-12-29 09:26:29 +01:00
import zipfix
2013-03-20 11:23:54 +01:00
2020-09-27 12:54:49 +02:00
inf = self . temporary_file ( " .epub " )
2013-03-20 11:23:54 +01:00
try :
2020-09-26 22:22:47 +02:00
print ( " {0} v {1} : Verifying zip archive integrity " . format ( PLUGIN_NAME , PLUGIN_VERSION ) )
2013-03-20 11:23:54 +01:00
fr = zipfix . fixZip ( path_to_ebook , inf . name )
fr . fix ( )
2020-09-26 22:22:47 +02:00
except Exception as e :
2020-09-27 12:54:49 +02:00
print ( " {0} v {1} : Error \' {2} \' when checking zip archive " . format ( PLUGIN_NAME , PLUGIN_VERSION , e . args [ 0 ] ) )
2021-11-16 11:09:03 +01:00
raise
2013-03-20 11:23:54 +01:00
# import the decryption keys
2021-12-29 09:26:29 +01:00
import prefs
2013-04-05 18:44:48 +02:00
dedrmprefs = prefs . DeDRM_Prefs ( )
2013-03-20 11:23:54 +01:00
2021-11-17 21:53:24 +01:00
# import the LCP handler
2021-12-29 09:26:29 +01:00
import lcpdedrm
2021-11-17 21:53:24 +01:00
if ( lcpdedrm . isLCPbook ( path_to_ebook ) ) :
try :
retval = lcpdedrm . decryptLCPbook ( path_to_ebook , dedrmprefs [ ' lcp_passphrases ' ] , self )
except :
print ( " Looks like that didn ' t work: " )
raise
return self . postProcessEPUB ( retval )
# Not an LCP book, do the normal EPUB (Adobe) handling.
2021-12-23 11:29:58 +01:00
# import the Adobe ePub handler
2021-12-29 09:26:29 +01:00
import ineptepub
2013-03-20 11:23:54 +01:00
2021-12-23 11:29:58 +01:00
if ineptepub . adeptBook ( inf . name ) :
2013-04-05 18:44:48 +02:00
2021-12-23 11:29:58 +01:00
if ineptepub . isPassHashBook ( inf . name ) :
# This is an Adobe PassHash / B&N encrypted eBook
print ( " {0} v {1} : “ {2} ” is a secure PassHash-protected (B&N) ePub " . format ( PLUGIN_NAME , PLUGIN_VERSION , os . path . basename ( path_to_ebook ) ) )
2013-03-20 11:23:54 +01:00
2021-12-23 11:29:58 +01:00
# Attempt to decrypt epub with each encryption key (generated or provided).
for keyname , userkey in dedrmprefs [ ' bandnkeys ' ] . items ( ) :
keyname_masked = " " . join ( ( " X " if ( x . isdigit ( ) ) else x ) for x in keyname )
print ( " {0} v {1} : Trying Encryption key {2:s} " . format ( PLUGIN_NAME , PLUGIN_VERSION , keyname_masked ) )
of = self . temporary_file ( " .epub " )
2013-03-20 11:23:54 +01:00
2021-12-23 11:29:58 +01:00
# Give the user key, ebook and TemporaryPersistent file to the decryption function.
try :
result = ineptepub . decryptBook ( userkey , inf . name , of . name )
except :
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
2013-03-20 11:23:54 +01:00
2021-12-23 11:29:58 +01:00
of . close ( )
2013-03-20 11:23:54 +01:00
2021-12-23 11:29:58 +01:00
if result == 0 :
# Decryption was successful.
# Return the modified PersistentTemporary file to calibre.
return self . postProcessEPUB ( of . name )
2013-03-20 11:23:54 +01:00
2021-12-23 11:29:58 +01:00
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 ) )
2013-03-20 11:23:54 +01:00
2021-12-23 11:29:58 +01:00
# perhaps we should see if we can get a key from a log file
print ( " {0} v {1} : Looking for new NOOK Keys after {2:.1f} seconds " . format ( PLUGIN_NAME , PLUGIN_VERSION , time . time ( ) - self . starttime ) )
2015-03-09 08:38:31 +01:00
2021-12-23 11:29:58 +01:00
# get the default NOOK keys
defaultkeys = [ ]
2015-03-09 08:38:31 +01:00
2021-12-23 11:29:58 +01:00
###### Add keys from the NOOK Study application (ignoblekeyNookStudy.py)
2015-03-09 08:38:31 +01:00
2021-12-23 11:29:58 +01:00
try :
if iswindows or isosx :
2021-12-29 09:26:29 +01:00
from ignoblekeyNookStudy import nookkeys
2015-03-09 08:38:31 +01:00
2021-12-23 11:29:58 +01:00
defaultkeys_study = nookkeys ( )
else : # linux
2021-12-29 09:26:29 +01:00
from wineutils import WineGetKeys
2015-03-09 08:38:31 +01:00
2021-12-23 11:29:58 +01:00
scriptpath = os . path . join ( self . alfdir , " ignoblekeyNookStudy.py " )
defaultkeys_study = WineGetKeys ( scriptpath , " .b64 " , dedrmprefs [ ' adobewineprefix ' ] )
except :
print ( " {0} v {1} : Exception when getting default NOOK Study Key after {2:.1f} seconds " . format ( PLUGIN_NAME , PLUGIN_VERSION , time . time ( ) - self . starttime ) )
traceback . print_exc ( )
2015-03-09 08:38:31 +01:00
2021-12-23 11:29:58 +01:00
###### Add keys from the NOOK Microsoft Store application (ignoblekeyNookStudy.py)
2015-03-09 08:38:31 +01:00
try :
2021-12-23 11:29:58 +01:00
if iswindows :
# That's a Windows store app, it won't run on Linux or MacOS anyways.
# No need to waste time running Wine.
2021-12-29 09:26:29 +01:00
from ignoblekeyWindowsStore import dump_keys as dump_nook_keys
2021-12-23 11:29:58 +01:00
defaultkeys_store = dump_nook_keys ( False )
2015-03-09 08:38:31 +01:00
2021-12-23 11:29:58 +01:00
except :
print ( " {0} v {1} : Exception when getting default NOOK Microsoft App keys after {2:.1f} seconds " . format ( PLUGIN_NAME , PLUGIN_VERSION , time . time ( ) - self . starttime ) )
traceback . print_exc ( )
2015-03-09 08:38:31 +01:00
2021-12-24 14:35:53 +01:00
###### Add keys from Adobe PassHash ADE activation data (adobekey_get_passhash.py)
try :
if iswindows :
# Right now this is only implemented for Windows. MacOS support still needs to be added.
2021-12-29 09:26:29 +01:00
from adobekey_get_passhash import passhash_keys
2021-12-24 14:35:53 +01:00
defaultkeys_ade , names = passhash_keys ( )
if isosx :
print ( " {0} v {1} : Dumping ADE PassHash data is not yet supported on MacOS. " . format ( PLUGIN_NAME , PLUGIN_VERSION ) )
except :
print ( " {0} v {1} : Exception when getting PassHashes from ADE after {2:.1f} seconds " . format ( PLUGIN_NAME , PLUGIN_VERSION , time . time ( ) - self . starttime ) )
traceback . print_exc ( )
2015-03-09 08:38:31 +01:00
2021-12-23 11:29:58 +01:00
###### Check if one of the new keys decrypts the book:
2015-03-09 08:38:31 +01:00
2021-12-23 11:29:58 +01:00
newkeys = [ ]
for keyvalue in defaultkeys_study :
if keyvalue not in dedrmprefs [ ' bandnkeys ' ] . values ( ) and keyvalue not in newkeys :
newkeys . append ( keyvalue )
if iswindows :
for keyvalue in defaultkeys_store :
if keyvalue not in dedrmprefs [ ' bandnkeys ' ] . values ( ) and keyvalue not in newkeys :
newkeys . append ( keyvalue )
2021-12-24 14:35:53 +01:00
for keyvalue in defaultkeys_ade :
if keyvalue not in dedrmprefs [ ' bandnkeys ' ] . values ( ) and keyvalue not in newkeys :
newkeys . append ( keyvalue )
2021-12-23 11:29:58 +01:00
if len ( newkeys ) > 0 :
try :
for i , userkey in enumerate ( newkeys ) :
print ( " {0} v {1} : Trying a new default key " . format ( PLUGIN_NAME , PLUGIN_VERSION ) )
of = self . temporary_file ( " .epub " )
# Give the user key, ebook and TemporaryPersistent file to the decryption function.
2015-03-09 08:38:31 +01:00
try :
2021-12-23 11:29:58 +01:00
result = ineptepub . decryptBook ( userkey , inf . name , of . name )
2015-03-09 08:38:31 +01:00
except :
2021-12-23 11:29:58 +01:00
print ( " {0} v {1} : Exception when trying to decrypt after {2:.1f} seconds " . format ( PLUGIN_NAME , PLUGIN_VERSION , time . time ( ) - self . starttime ) )
2015-03-09 08:38:31 +01:00
traceback . print_exc ( )
2021-12-23 11:29:58 +01:00
result = 1
of . close ( )
if result == 0 :
# Decryption was a success
# Store the new successful key in the defaults
print ( " {0} v {1} : Saving a new default key " . format ( PLUGIN_NAME , PLUGIN_VERSION ) )
try :
2021-12-24 14:35:53 +01:00
if userkey in defaultkeys_ade :
dedrmprefs . addnamedvaluetoprefs ( ' bandnkeys ' , ' ade_passhash_ ' + str ( int ( time . time ( ) ) ) , keyvalue )
else :
dedrmprefs . addnamedvaluetoprefs ( ' bandnkeys ' , ' nook_key_ ' + str ( int ( time . time ( ) ) ) , keyvalue )
2021-12-23 11:29:58 +01:00
dedrmprefs . writeprefs ( )
print ( " {0} v {1} : Saved a new default key after {2:.1f} seconds " . format ( PLUGIN_NAME , PLUGIN_VERSION , time . time ( ) - self . starttime ) )
except :
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 self . postProcessEPUB ( of . name )
print ( " {0} v {1} : Failed to decrypt with new default key after {2:.1f} seconds " . format ( PLUGIN_NAME , PLUGIN_VERSION , time . time ( ) - self . starttime ) )
except :
pass
2015-03-09 08:38:31 +01:00
2021-12-23 11:29:58 +01:00
else :
# This is a "normal" Adobe eBook.
book_uuid = None
try :
# This tries to figure out which Adobe account UUID the book is licensed for.
# If we know that we can directly use the correct key instead of having to
# try them all.
book_uuid = ineptepub . adeptGetUserUUID ( inf . name )
except :
2015-03-09 08:38:31 +01:00
pass
2021-12-23 11:29:58 +01:00
if book_uuid is None :
print ( " {0} v {1} : {2} is a secure Adobe Adept ePub " . format ( PLUGIN_NAME , PLUGIN_VERSION , os . path . basename ( path_to_ebook ) ) )
else :
print ( " {0} v {1} : {2} is a secure Adobe Adept ePub for UUID {3} " . format ( PLUGIN_NAME , PLUGIN_VERSION , os . path . basename ( path_to_ebook ) , book_uuid ) )
2013-03-20 11:23:54 +01:00
2021-12-23 11:29:58 +01:00
if book_uuid is not None :
# Check if we have a key with that UUID in its name:
for keyname , userkeyhex in dedrmprefs [ ' adeptkeys ' ] . items ( ) :
if not book_uuid . lower ( ) in keyname . lower ( ) :
continue
# Found matching key
userkey = codecs . decode ( userkeyhex , ' hex ' )
print ( " {0} v {1} : Trying UUID-matched encryption key {2:s} " . format ( PLUGIN_NAME , PLUGIN_VERSION , keyname ) )
of = self . temporary_file ( " .epub " )
try :
result = ineptepub . decryptBook ( userkey , inf . name , of . name )
of . close ( )
if result == 0 :
print ( " {0} v {1} : Decrypted with key {2:s} after {3:.1f} seconds " . format ( PLUGIN_NAME , PLUGIN_VERSION , keyname , time . time ( ) - self . starttime ) )
return self . postProcessEPUB ( of . name )
except ineptepub . ADEPTNewVersionError :
print ( " {0} v {1} : Book uses unsupported (too new) Adobe DRM. " . format ( PLUGIN_NAME , PLUGIN_VERSION , time . time ( ) - self . starttime ) )
return self . postProcessEPUB ( path_to_ebook )
2021-11-15 11:59:56 +01:00
2021-12-23 11:29:58 +01:00
except :
print ( " {0} v {1} : Exception when decrypting after {2:.1f} seconds - trying other keys " . format ( PLUGIN_NAME , PLUGIN_VERSION , time . time ( ) - self . starttime ) )
traceback . print_exc ( )
2021-11-15 11:59:56 +01:00
2021-12-23 11:29:58 +01:00
# Attempt to decrypt epub with each encryption key (generated or provided).
for keyname , userkeyhex in dedrmprefs [ ' adeptkeys ' ] . items ( ) :
2021-11-15 11:59:56 +01:00
userkey = codecs . decode ( userkeyhex , ' hex ' )
2021-12-23 11:29:58 +01:00
print ( " {0} v {1} : Trying Encryption key {2:s} " . format ( PLUGIN_NAME , PLUGIN_VERSION , keyname ) )
2021-11-15 11:59:56 +01:00
of = self . temporary_file ( " .epub " )
2021-12-23 11:29:58 +01:00
# Give the user key, ebook and TemporaryPersistent file to the decryption function.
try :
2021-11-15 11:59:56 +01:00
result = ineptepub . decryptBook ( userkey , inf . name , of . name )
2021-11-15 19:51:36 +01:00
except ineptepub . ADEPTNewVersionError :
print ( " {0} v {1} : Book uses unsupported (too new) Adobe DRM. " . format ( PLUGIN_NAME , PLUGIN_VERSION , time . time ( ) - self . starttime ) )
2021-11-16 15:23:54 +01:00
return self . postProcessEPUB ( path_to_ebook )
2021-11-15 11:59:56 +01:00
except :
2021-12-23 11:29:58 +01:00
print ( " {0} v {1} : Exception when decrypting after {2:.1f} seconds " . format ( PLUGIN_NAME , PLUGIN_VERSION , time . time ( ) - self . starttime ) )
2021-11-15 11:59:56 +01:00
traceback . print_exc ( )
2021-12-23 11:29:58 +01:00
result = 1
2021-11-15 11:59:56 +01:00
2021-12-23 11:29:58 +01:00
try :
of . close ( )
except :
print ( " {0} v {1} : Exception closing temporary file after {2:.1f} seconds. Ignored. " . format ( PLUGIN_NAME , PLUGIN_VERSION , time . time ( ) - self . starttime ) )
2013-03-20 11:23:54 +01:00
2021-12-23 11:29:58 +01:00
if result == 0 :
# Decryption was successful.
# Return the modified PersistentTemporary file to calibre.
print ( " {0} v {1} : Decrypted with key {2:s} after {3:.1f} seconds " . format ( PLUGIN_NAME , PLUGIN_VERSION , keyname , time . time ( ) - self . starttime ) )
return self . postProcessEPUB ( of . name )
2013-03-20 11:23:54 +01:00
2021-12-23 11:29:58 +01:00
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 ) )
2013-04-05 18:44:48 +02:00
2021-12-23 11:29:58 +01:00
# perhaps we need to get a new default ADE key
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 ) )
2013-03-20 11:23:54 +01:00
2021-12-23 11:29:58 +01:00
# get the default Adobe keys
defaultkeys = [ ]
2013-03-20 11:23:54 +01:00
2021-12-23 11:29:58 +01:00
try :
if iswindows or isosx :
2021-12-29 09:26:29 +01:00
from adobekey import adeptkeys
2021-11-16 17:14:03 +01:00
2021-12-23 11:29:58 +01:00
defaultkeys , defaultnames = adeptkeys ( )
else : # linux
2021-12-29 09:26:29 +01:00
from wineutils import WineGetKeys
2021-11-16 17:14:03 +01:00
2021-12-23 11:29:58 +01:00
scriptpath = os . path . join ( self . alfdir , " adobekey.py " )
defaultkeys , defaultnames = WineGetKeys ( scriptpath , " .der " , dedrmprefs [ ' adobewineprefix ' ] )
2021-11-16 17:14:03 +01:00
2021-12-23 11:29:58 +01:00
except :
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 ( )
2013-04-05 18:44:48 +02:00
2021-12-23 11:29:58 +01:00
newkeys = [ ]
newnames = [ ]
idx = 0
for keyvalue in defaultkeys :
if codecs . encode ( keyvalue , ' hex ' ) . decode ( ' ascii ' ) not in dedrmprefs [ ' adeptkeys ' ] . values ( ) :
newkeys . append ( keyvalue )
newnames . append ( " default_ade_key_uuid_ " + defaultnames [ idx ] )
idx + = 1
# Check for DeACSM keys:
try :
2021-12-29 09:26:29 +01:00
from config import checkForDeACSMkeys
2021-12-23 11:29:58 +01:00
newkey , newname = checkForDeACSMkeys ( )
if newkey is not None :
if codecs . encode ( newkey , ' hex ' ) . decode ( ' ascii ' ) not in dedrmprefs [ ' adeptkeys ' ] . values ( ) :
print ( " {0} v {1} : Found new key ' {2} ' in DeACSM plugin " . format ( PLUGIN_NAME , PLUGIN_VERSION , newname ) )
newkeys . append ( newkey )
newnames . append ( newname )
except :
traceback . print_exc ( )
pass
2013-04-05 18:44:48 +02:00
2021-12-23 11:29:58 +01:00
if len ( newkeys ) > 0 :
try :
for i , userkey in enumerate ( newkeys ) :
print ( " {0} v {1} : Trying a new default key " . format ( PLUGIN_NAME , PLUGIN_VERSION ) )
of = self . temporary_file ( " .epub " )
2013-04-05 18:44:48 +02:00
2021-12-23 11:29:58 +01:00
# Give the user key, ebook and TemporaryPersistent file to the decryption function.
2013-03-20 11:23:54 +01:00
try :
2021-12-23 11:29:58 +01:00
result = ineptepub . decryptBook ( userkey , inf . name , of . name )
2013-03-20 11:23:54 +01:00
except :
2021-12-23 11:29:58 +01:00
print ( " {0} v {1} : Exception when decrypting after {2:.1f} seconds " . format ( PLUGIN_NAME , PLUGIN_VERSION , time . time ( ) - self . starttime ) )
2013-04-05 18:44:48 +02:00
traceback . print_exc ( )
2021-12-23 11:29:58 +01:00
result = 1
of . close ( )
if result == 0 :
# Decryption was a success
# Store the new successful key in the defaults
print ( " {0} v {1} : Saving a new default key " . format ( PLUGIN_NAME , PLUGIN_VERSION ) )
try :
dedrmprefs . addnamedvaluetoprefs ( ' adeptkeys ' , newnames [ i ] , codecs . encode ( userkey , ' hex ' ) . decode ( ' ascii ' ) )
dedrmprefs . writeprefs ( )
print ( " {0} v {1} : Saved a new default key after {2:.1f} seconds " . format ( PLUGIN_NAME , PLUGIN_VERSION , time . time ( ) - self . starttime ) )
except :
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 ( " {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 self . postProcessEPUB ( of . name )
print ( " {0} v {1} : Failed to decrypt with new default key after {2:.1f} seconds " . format ( PLUGIN_NAME , PLUGIN_VERSION , time . time ( ) - self . starttime ) )
except Exception as e :
print ( " {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
2013-03-20 11:23:54 +01:00
2021-12-23 11:29:58 +01:00
# Something went wrong with decryption.
print ( " {0} v {1} : Ultimately failed to decrypt after {2:.1f} seconds. Read the FAQs at Harper ' s repository: https://github.com/apprenticeharper/DeDRM_tools/blob/master/FAQs.md " . format ( PLUGIN_NAME , PLUGIN_VERSION , time . time ( ) - self . starttime ) )
raise DeDRMError ( " {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 ) )
2013-03-20 11:23:54 +01:00
2021-12-23 11:29:58 +01:00
# Not a Barnes & Noble nor an Adobe Adept
# Probably a DRM-free EPUB, but we should still check for fonts.
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 ) ) )
return self . postProcessEPUB ( inf . name )
#raise DeDRMError("{0} v{1}: Couldn't decrypt after {2:.1f} seconds. DRM free perhaps?".format(PLUGIN_NAME, PLUGIN_VERSION,time.time()-self.starttime))
2013-03-20 11:23:54 +01:00
2021-12-27 10:39:41 +01:00
# No DRM?
return self . postProcessEPUB ( inf . name )
2021-12-27 10:45:12 +01:00
def PDFIneptDecrypt ( self , path_to_ebook ) :
# Sub function to prevent PDFDecrypt from becoming too large ...
2021-12-29 09:26:29 +01:00
import prefs
import ineptpdf
2013-04-05 18:44:48 +02:00
dedrmprefs = prefs . DeDRM_Prefs ( )
2021-11-15 13:38:39 +01:00
book_uuid = None
try :
# Try to figure out which Adobe account this book is licensed for.
book_uuid = ineptpdf . adeptGetUserUUID ( path_to_ebook )
except :
pass
2021-12-27 10:45:12 +01:00
if book_uuid is not None :
print ( " {0} v {1} : {2} is a PDF ebook (EBX) for UUID {3} " . format ( PLUGIN_NAME , PLUGIN_VERSION , os . path . basename ( path_to_ebook ) , book_uuid ) )
2021-11-15 13:38:39 +01:00
# Check if we have a key for that UUID
for keyname , userkeyhex in dedrmprefs [ ' adeptkeys ' ] . items ( ) :
if not book_uuid . lower ( ) in keyname . lower ( ) :
continue
# Found matching key
userkey = codecs . decode ( userkeyhex , ' hex ' )
print ( " {0} v {1} : Trying UUID-matched encryption key {2:s} " . format ( PLUGIN_NAME , PLUGIN_VERSION , keyname ) )
of = self . temporary_file ( " .pdf " )
try :
result = ineptpdf . decryptBook ( userkey , path_to_ebook , of . name )
of . close ( )
if result == 0 :
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
2021-11-15 19:51:36 +01:00
except ineptpdf . ADEPTNewVersionError :
print ( " {0} v {1} : Book uses unsupported (too new) Adobe DRM. " . format ( PLUGIN_NAME , PLUGIN_VERSION , time . time ( ) - self . starttime ) )
return path_to_ebook
2021-11-15 13:38:39 +01:00
except :
print ( " {0} v {1} : Exception when decrypting after {2:.1f} seconds - trying other keys " . format ( PLUGIN_NAME , PLUGIN_VERSION , time . time ( ) - self . starttime ) )
traceback . print_exc ( )
# If we end up here, we didn't find a key with a matching UUID, so lets just try all of them.
2021-11-15 17:59:48 +01:00
2021-11-16 11:48:53 +01:00
# Attempt to decrypt PDF with each encryption key (generated or provided).
2013-04-05 18:44:48 +02:00
for keyname , userkeyhex in dedrmprefs [ ' adeptkeys ' ] . items ( ) :
2020-11-29 13:39:04 +01:00
userkey = codecs . decode ( userkeyhex , ' hex ' )
2021-11-15 13:38:39 +01:00
print ( " {0} v {1} : Trying encryption key {2:s} " . format ( PLUGIN_NAME , PLUGIN_VERSION , keyname ) )
2020-09-27 12:54:49 +02:00
of = self . temporary_file ( " .pdf " )
2013-03-20 11:23:54 +01:00
# Give the user key, ebook and TemporaryPersistent file to the decryption function.
try :
result = ineptpdf . decryptBook ( userkey , path_to_ebook , of . name )
2021-11-15 19:51:36 +01:00
except ineptpdf . ADEPTNewVersionError :
print ( " {0} v {1} : Book uses unsupported (too new) Adobe DRM. " . format ( PLUGIN_NAME , PLUGIN_VERSION , time . time ( ) - self . starttime ) )
return path_to_ebook
2013-03-20 11:23:54 +01:00
except :
2020-09-26 22:22:47 +02:00
print ( " {0} v {1} : Exception when decrypting after {2:.1f} seconds " . format ( PLUGIN_NAME , PLUGIN_VERSION , time . time ( ) - self . starttime ) )
2015-03-09 08:38:31 +01:00
traceback . print_exc ( )
2013-03-20 11:23:54 +01:00
result = 1
of . close ( )
if result == 0 :
# Decryption was successful.
# Return the modified PersistentTemporary file to calibre.
2021-11-15 13:38:39 +01:00
print ( " {0} v {1} : Decrypted with key {2:s} after {3:.1f} seconds " . format ( PLUGIN_NAME , PLUGIN_VERSION , keyname , time . time ( ) - self . starttime ) )
2013-03-20 11:23:54 +01:00
return of . name
2020-09-26 22:22:47 +02:00
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 ) )
2015-03-09 08:38:31 +01:00
2014-12-08 22:23:07 +01:00
# perhaps we need to get a new default ADE key
2020-09-26 22:22:47 +02:00
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 ) )
2013-03-20 11:23:54 +01:00
2014-12-08 22:23:07 +01:00
# get the default Adobe keys
defaultkeys = [ ]
2013-03-20 11:23:54 +01:00
2015-03-09 08:38:31 +01:00
try :
if iswindows or isosx :
2021-12-29 09:26:29 +01:00
from adobekey import adeptkeys
2013-03-20 11:23:54 +01:00
2021-11-15 10:47:09 +01:00
defaultkeys , defaultnames = adeptkeys ( )
2015-03-09 08:38:31 +01:00
else : # linux
2021-12-29 09:26:29 +01:00
from wineutils import WineGetKeys
2013-03-20 11:23:54 +01:00
2020-09-27 12:54:49 +02:00
scriptpath = os . path . join ( self . alfdir , " adobekey.py " )
2021-11-15 10:47:09 +01:00
defaultkeys , defaultnames = WineGetKeys ( scriptpath , " .der " , dedrmprefs [ ' adobewineprefix ' ] )
2015-03-09 08:38:31 +01:00
except :
2020-09-26 22:22:47 +02:00
print ( " {0} v {1} : Exception when getting default Adobe Key after {2:.1f} seconds " . format ( PLUGIN_NAME , PLUGIN_VERSION , time . time ( ) - self . starttime ) )
2015-03-09 08:38:31 +01:00
traceback . print_exc ( )
2013-03-20 11:23:54 +01:00
2014-12-08 22:23:07 +01:00
newkeys = [ ]
2021-11-15 10:47:09 +01:00
newnames = [ ]
idx = 0
2014-12-08 22:23:07 +01:00
for keyvalue in defaultkeys :
2020-11-29 13:39:04 +01:00
if codecs . encode ( keyvalue , ' hex ' ) not in dedrmprefs [ ' adeptkeys ' ] . values ( ) :
2014-12-08 22:23:07 +01:00
newkeys . append ( keyvalue )
2021-11-16 17:14:03 +01:00
newnames . append ( " default_ade_key_uuid_ " + defaultnames [ idx ] )
2021-11-15 10:47:09 +01:00
idx + = 1
2013-04-05 18:44:48 +02:00
2021-11-16 17:14:03 +01:00
# Check for DeACSM keys:
try :
2021-12-29 09:26:29 +01:00
from config import checkForDeACSMkeys
2021-11-16 17:14:03 +01:00
newkey , newname = checkForDeACSMkeys ( )
if newkey is not None :
if codecs . encode ( newkey , ' hex ' ) . decode ( ' ascii ' ) not in dedrmprefs [ ' adeptkeys ' ] . values ( ) :
print ( " {0} v {1} : Found new key ' {2} ' in DeACSM plugin " . format ( PLUGIN_NAME , PLUGIN_VERSION , newname ) )
newkeys . append ( keyvalue )
newnames . append ( newname )
except :
pass
2014-12-08 22:23:07 +01:00
if len ( newkeys ) > 0 :
try :
for i , userkey in enumerate ( newkeys ) :
2020-09-26 22:22:47 +02:00
print ( " {0} v {1} : Trying a new default key " . format ( PLUGIN_NAME , PLUGIN_VERSION ) )
2020-09-27 12:54:49 +02:00
of = self . temporary_file ( " .pdf " )
2014-12-08 22:23:07 +01:00
# Give the user key, ebook and TemporaryPersistent file to the decryption function.
try :
2015-03-09 08:38:31 +01:00
result = ineptpdf . decryptBook ( userkey , path_to_ebook , of . name )
2014-12-08 22:23:07 +01:00
except :
2020-09-26 22:22:47 +02:00
print ( " {0} v {1} : Exception when decrypting after {2:.1f} seconds " . format ( PLUGIN_NAME , PLUGIN_VERSION , time . time ( ) - self . starttime ) )
2015-03-09 08:38:31 +01:00
traceback . print_exc ( )
2014-12-08 22:23:07 +01:00
result = 1
of . close ( )
if result == 0 :
# Decryption was a success
# Store the new successful key in the defaults
2020-09-26 22:22:47 +02:00
print ( " {0} v {1} : Saving a new default key " . format ( PLUGIN_NAME , PLUGIN_VERSION ) )
2013-04-05 18:44:48 +02:00
try :
2021-11-16 17:14:03 +01:00
dedrmprefs . addnamedvaluetoprefs ( ' adeptkeys ' , newnames [ i ] , codecs . encode ( userkey , ' hex ' ) . decode ( ' ascii ' ) )
2014-12-08 22:23:07 +01:00
dedrmprefs . writeprefs ( )
2020-09-26 22:22:47 +02:00
print ( " {0} v {1} : Saved a new default key after {2:.1f} seconds " . format ( PLUGIN_NAME , PLUGIN_VERSION , time . time ( ) - self . starttime ) )
2013-04-05 18:44:48 +02:00
except :
2020-09-26 22:22:47 +02:00
print ( " {0} v {1} : Exception when saving a new default key after {2:.1f} seconds " . format ( PLUGIN_NAME , PLUGIN_VERSION , time . time ( ) - self . starttime ) )
2014-12-08 22:23:07 +01:00
traceback . print_exc ( )
# Return the modified PersistentTemporary file to calibre.
return of . name
2013-04-05 18:44:48 +02:00
2020-09-27 12:54:49 +02:00
print ( " {0} v {1} : Failed to decrypt with new default key after {2:.1f} seconds " . format ( PLUGIN_NAME , PLUGIN_VERSION , time . time ( ) - self . starttime ) )
2020-09-26 22:22:47 +02:00
except Exception as e :
2014-12-08 22:23:07 +01:00
pass
2013-03-20 11:23:54 +01:00
2021-11-16 11:48:53 +01:00
# Unable to decrypt the PDF with any of the existing keys. Is it a B&N PDF?
# Attempt to decrypt PDF with each encryption key (generated or provided).
for keyname , userkey in dedrmprefs [ ' bandnkeys ' ] . items ( ) :
keyname_masked = " " . join ( ( " X " if ( x . isdigit ( ) ) else x ) for x in keyname )
print ( " {0} v {1} : Trying Encryption key {2:s} " . format ( PLUGIN_NAME , PLUGIN_VERSION , keyname_masked ) )
of = self . temporary_file ( " .pdf " )
# Give the user key, ebook and TemporaryPersistent file to the decryption function.
try :
result = ineptpdf . decryptBook ( userkey , path_to_ebook , of . name , False )
except ineptpdf . ADEPTNewVersionError :
print ( " {0} v {1} : Book uses unsupported (too new) Adobe DRM. " . format ( PLUGIN_NAME , PLUGIN_VERSION , time . time ( ) - self . starttime ) )
return path_to_ebook
except :
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
of . close ( )
if result == 0 :
# Decryption was successful.
# Return the modified PersistentTemporary file to calibre.
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 ( " {0} v {1} : Failed to decrypt with key {2:s} after {3:.1f} seconds " . format ( PLUGIN_NAME , PLUGIN_VERSION , keyname , time . time ( ) - self . starttime ) )
2021-12-27 10:45:12 +01:00
def PDFStandardDecrypt ( self , path_to_ebook ) :
# Sub function to prevent PDFDecrypt from becoming too large ...
2021-12-29 09:26:29 +01:00
import prefs
import ineptpdf
2021-12-27 10:45:12 +01:00
dedrmprefs = prefs . DeDRM_Prefs ( )
2021-11-16 11:48:53 +01:00
2021-12-27 10:45:12 +01:00
# Attempt to decrypt PDF with each encryption key (generated or provided).
i = - 1
for userpassword in [ " " ] + dedrmprefs [ ' adobe_pdf_passphrases ' ] :
# Try the empty password, too.
i = i + 1
userpassword = bytearray ( userpassword , " utf-8 " )
if i == 0 :
print ( " {0} v {1} : Trying empty password ... " . format ( PLUGIN_NAME , PLUGIN_VERSION ) , end = " " )
else :
print ( " {0} v {1} : Trying password {2} ... " . format ( PLUGIN_NAME , PLUGIN_VERSION , i ) , end = " " )
of = self . temporary_file ( " .pdf " )
# Give the user password, ebook and TemporaryPersistent file to the decryption function.
msg = False
try :
result = ineptpdf . decryptBook ( userpassword , path_to_ebook , of . name )
print ( " done " )
msg = True
except ineptpdf . ADEPTInvalidPasswordError :
print ( " invalid password " . format ( PLUGIN_NAME , PLUGIN_VERSION ) )
msg = True
result = 1
except :
print ( " exception \n {0} v {1} : Exception when decrypting after {2:.1f} seconds " . format ( PLUGIN_NAME , PLUGIN_VERSION , time . time ( ) - self . starttime ) )
msg = True
traceback . print_exc ( )
result = 1
if not msg :
print ( " error \n {0} v {1} : Failed to decrypt after {2:.1f} seconds " . format ( PLUGIN_NAME , PLUGIN_VERSION , time . time ( ) - self . starttime ) )
of . close ( )
if result == 0 :
# Decryption was successful.
# Return the modified PersistentTemporary file to calibre.
print ( " {0} v {1} : Successfully decrypted with password {3} after {2:.1f} seconds " . format ( PLUGIN_NAME , PLUGIN_VERSION , time . time ( ) - self . starttime , i ) )
return of . name
print ( " {0} v {1} : Didn ' t manage to decrypt PDF. Make sure the correct password is entered in the settings. " . format ( PLUGIN_NAME , PLUGIN_VERSION ) )
def PDFDecrypt ( self , path_to_ebook ) :
2021-12-29 09:26:29 +01:00
import prefs
import ineptpdf
import lcpdedrm
2021-12-27 10:45:12 +01:00
dedrmprefs = prefs . DeDRM_Prefs ( )
if ( lcpdedrm . isLCPbook ( path_to_ebook ) ) :
try :
retval = lcpdedrm . decryptLCPbook ( path_to_ebook , dedrmprefs [ ' lcp_passphrases ' ] , self )
except :
print ( " Looks like that didn ' t work: " )
raise
return retval
# Not an LCP book, do the normal Adobe handling.
pdf_encryption = ineptpdf . getPDFencryptionType ( path_to_ebook )
if pdf_encryption is None :
print ( " {0} v {1} : {2} is an unencrypted PDF file - returning as is. " . format ( PLUGIN_NAME , PLUGIN_VERSION , os . path . basename ( path_to_ebook ) ) )
return path_to_ebook
print ( " {0} v {1} : {2} is a PDF ebook with encryption {3} " . format ( PLUGIN_NAME , PLUGIN_VERSION , os . path . basename ( path_to_ebook ) , pdf_encryption ) )
if pdf_encryption == " EBX_HANDLER " :
# Adobe eBook / ADEPT (normal or B&N)
return self . PDFIneptDecrypt ( path_to_ebook )
elif pdf_encryption == " Standard " or pdf_encryption == " Adobe.APS " :
return self . PDFStandardDecrypt ( path_to_ebook )
elif pdf_encryption == " FOPN_fLock " or pdf_encryption == " FOPN_foweb " :
print ( " {0} v {1} : FileOpen encryption ' {2} ' is unsupported. " . format ( PLUGIN_NAME , PLUGIN_VERSION , pdf_encryption ) )
print ( " {0} v {1} : Try the standalone script from the ' Tetrachroma_FileOpen_ineptpdf ' folder in the Github repo. " . format ( PLUGIN_NAME , PLUGIN_VERSION ) )
2021-12-27 14:23:26 +01:00
return path_to_ebook
2021-12-27 10:45:12 +01:00
else :
print ( " {0} v {1} : Encryption ' {2} ' is unsupported. " . format ( PLUGIN_NAME , PLUGIN_VERSION , pdf_encryption ) )
return path_to_ebook
2013-03-20 11:23:54 +01:00
2013-04-05 18:44:48 +02:00
2013-03-20 11:23:54 +01:00
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.
2021-12-29 09:26:29 +01:00
import prefs
import k4mobidedrm
2013-03-20 11:23:54 +01:00
2013-04-05 18:44:48 +02:00
dedrmprefs = prefs . DeDRM_Prefs ( )
pids = dedrmprefs [ ' pids ' ]
serials = dedrmprefs [ ' serials ' ]
2015-07-29 19:11:19 +02:00
for android_serials_list in dedrmprefs [ ' androidkeys ' ] . values ( ) :
#print android_serials_list
serials . extend ( android_serials_list )
#print serials
androidFiles = [ ]
2020-09-27 12:54:49 +02:00
kindleDatabases = list ( dedrmprefs [ ' kindlekeys ' ] . items ( ) )
2013-03-20 11:23:54 +01:00
try :
2015-07-29 19:11:19 +02:00
book = k4mobidedrm . GetDecryptedBook ( path_to_ebook , kindleDatabases , androidFiles , serials , pids , self . starttime )
2020-09-26 22:22:47 +02:00
except Exception as e :
2013-03-20 11:23:54 +01:00
decoded = False
# perhaps we need to get a new default Kindle for Mac/PC key
2013-04-05 18:44:48 +02:00
defaultkeys = [ ]
2020-09-26 22:22:47 +02:00
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 ) )
2013-04-05 18:44:48 +02:00
try :
if iswindows or isosx :
2021-12-29 09:26:29 +01:00
from kindlekey import kindlekeys
2013-03-20 11:23:54 +01:00
2013-04-05 18:44:48 +02:00
defaultkeys = kindlekeys ( )
else : # linux
2021-12-29 09:26:29 +01:00
from wineutils import WineGetKeys
2013-04-05 18:44:48 +02:00
2020-09-27 12:54:49 +02:00
scriptpath = os . path . join ( self . alfdir , " kindlekey.py " )
defaultkeys = WineGetKeys ( scriptpath , " .k4i " , dedrmprefs [ ' kindlewineprefix ' ] )
2013-04-05 18:44:48 +02:00
except :
2020-09-26 22:22:47 +02:00
print ( " {0} v {1} : Exception when getting default Kindle Key after {2:.1f} seconds " . format ( PLUGIN_NAME , PLUGIN_VERSION , time . time ( ) - self . starttime ) )
2015-03-09 08:38:31 +01:00
traceback . print_exc ( )
2013-04-05 18:44:48 +02:00
pass
newkeys = { }
for i , keyvalue in enumerate ( defaultkeys ) :
2020-09-27 12:54:49 +02:00
keyname = " default_key_ {0:d} " . format ( i + 1 )
2013-04-05 18:44:48 +02:00
if keyvalue not in dedrmprefs [ ' kindlekeys ' ] . values ( ) :
newkeys [ keyname ] = keyvalue
if len ( newkeys ) > 0 :
2020-09-27 12:54:49 +02:00
print ( " {0} v {1} : Found {2} new {3} " . format ( PLUGIN_NAME , PLUGIN_VERSION , len ( newkeys ) , " key " if len ( newkeys ) == 1 else " keys " ) )
2013-03-20 11:23:54 +01:00
try :
2020-09-27 12:54:49 +02:00
book = k4mobidedrm . GetDecryptedBook ( path_to_ebook , list ( newkeys . items ( ) ) , [ ] , [ ] , [ ] , self . starttime )
2013-04-05 18:44:48 +02:00
decoded = True
# store the new successful keys in the defaults
2020-09-27 12:54:49 +02:00
print ( " {0} v {1} : Saving {2} new {3} " . format ( PLUGIN_NAME , PLUGIN_VERSION , len ( newkeys ) , " key " if len ( newkeys ) == 1 else " keys " ) )
2013-04-05 18:44:48 +02:00
for keyvalue in newkeys . values ( ) :
dedrmprefs . addnamedvaluetoprefs ( ' kindlekeys ' , ' default_key ' , keyvalue )
dedrmprefs . writeprefs ( )
2020-09-26 22:22:47 +02:00
except Exception as e :
2013-04-05 18:44:48 +02:00
pass
2013-03-20 11:23:54 +01:00
if not decoded :
#if you reached here then no luck raise and exception
2020-09-26 22:22:47 +02:00
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 ) )
2020-09-27 12:54:49 +02:00
raise DeDRMError ( " {0} v {1} : Ultimately failed to decrypt after {2:.1f} seconds. Read the FAQs at Harper ' s repository: https://github.com/apprenticeharper/DeDRM_tools/blob/master/FAQs.md " . format ( PLUGIN_NAME , PLUGIN_VERSION , time . time ( ) - self . starttime ) )
2013-03-20 11:23:54 +01:00
of = self . temporary_file ( book . getBookExtension ( ) )
book . getFile ( of . name )
of . close ( )
book . cleanup ( )
return of . name
def eReaderDecrypt ( self , path_to_ebook ) :
2021-12-29 09:26:29 +01:00
import prefs
import erdr2pml
2013-03-20 11:23:54 +01:00
2013-04-10 12:50:10 +02:00
dedrmprefs = prefs . DeDRM_Prefs ( )
2013-03-20 11:23:54 +01:00
# Attempt to decrypt epub with each encryption key (generated or provided).
2013-04-05 18:44:48 +02:00
for keyname , userkey in dedrmprefs [ ' ereaderkeys ' ] . items ( ) :
2020-10-14 17:23:49 +02:00
keyname_masked = " " . join ( ( " X " if ( x . isdigit ( ) ) else x ) for x in keyname )
2020-09-26 22:22:47 +02:00
print ( " {0} v {1} : Trying Encryption key {2:s} " . format ( PLUGIN_NAME , PLUGIN_VERSION , keyname_masked ) )
2020-09-27 12:54:49 +02:00
of = self . temporary_file ( " .pmlz " )
2013-03-20 11:23:54 +01:00
# Give the userkey, ebook and TemporaryPersistent file to the decryption function.
2020-11-29 13:39:04 +01:00
result = erdr2pml . decryptBook ( path_to_ebook , of . name , True , codecs . decode ( userkey , ' hex ' ) )
2013-03-20 11:23:54 +01:00
of . close ( )
# Decryption was successful return the modified PersistentTemporary
# file to Calibre's import process.
if result == 0 :
2020-09-26 22:22:47 +02:00
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 ) )
2013-03-20 11:23:54 +01:00
return of . name
2020-09-26 22:22:47 +02:00
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 ) )
2013-03-20 11:23:54 +01:00
2020-09-26 22:22:47 +02:00
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 ) )
2020-09-27 12:54:49 +02:00
raise DeDRMError ( " {0} v {1} : Ultimately failed to decrypt after {2:.1f} seconds. Read the FAQs at Harper ' s repository: https://github.com/apprenticeharper/DeDRM_tools/blob/master/FAQs.md " . format ( PLUGIN_NAME , PLUGIN_VERSION , time . time ( ) - self . starttime ) )
2013-03-20 11:23:54 +01:00
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 )
2020-09-26 22:22:47 +02:00
print ( " {0} v {1} : Trying to decrypt {2} " . format ( PLUGIN_NAME , PLUGIN_VERSION , os . path . basename ( path_to_ebook ) ) )
2013-03-20 11:23:54 +01:00
self . starttime = time . time ( )
booktype = os . path . splitext ( path_to_ebook ) [ 1 ] . lower ( ) [ 1 : ]
2018-03-13 01:32:41 +01:00
if booktype in [ ' prc ' , ' mobi ' , ' pobi ' , ' azw ' , ' azw1 ' , ' azw3 ' , ' azw4 ' , ' tpz ' , ' kfx-zip ' ] :
2013-03-20 11:23:54 +01:00
# Kindle/Mobipocket
decrypted_ebook = self . KindleMobiDecrypt ( path_to_ebook )
elif booktype == ' pdb ' :
# eReader
decrypted_ebook = self . eReaderDecrypt ( path_to_ebook )
pass
elif booktype == ' pdf ' :
2021-12-29 09:14:35 +01:00
# Adobe PDF (hopefully) or LCP PDF
2013-03-20 11:23:54 +01:00
decrypted_ebook = self . PDFDecrypt ( path_to_ebook )
pass
elif booktype == ' epub ' :
2021-12-29 09:14:35 +01:00
# Adobe Adept, PassHash (B&N) or LCP ePub
2013-03-20 11:23:54 +01:00
decrypted_ebook = self . ePubDecrypt ( path_to_ebook )
else :
2020-09-26 22:22:47 +02:00
print ( " Unknown booktype {0} . Passing back to calibre unchanged " . format ( booktype ) )
2013-03-20 11:23:54 +01:00
return path_to_ebook
2020-09-26 22:22:47 +02:00
print ( " {0} v {1} : Finished after {2:.1f} seconds " . format ( PLUGIN_NAME , PLUGIN_VERSION , time . time ( ) - self . starttime ) )
2013-03-20 11:23:54 +01:00
return decrypted_ebook
def is_customizable ( self ) :
# return true to allow customization via the Plugin->Preferences.
return True
def config_widget ( self ) :
2021-12-29 09:26:29 +01:00
import config
2013-04-05 18:44:48 +02:00
return config . ConfigWidget ( self . plugin_path , self . alfdir )
2013-03-20 11:23:54 +01:00
def save_settings ( self , config_widget ) :
config_widget . save_settings ( )