-#!/usr/bin/env python
+#!/usr/bin/env python3
# -*- coding: utf-8 -*-
-from __future__ import with_statement
-
# __init__.py for DeDRM_plugin
# Copyright © 2008-2020 Apprentice Harper et al.
Decrypt DRMed ebooks.
"""
-PLUGIN_NAME = u"DeDRM"
+PLUGIN_NAME = "DeDRM"
PLUGIN_VERSION_TUPLE = (7, 0, 0)
-PLUGIN_VERSION = u".".join([unicode(str(x)) for x in PLUGIN_VERSION_TUPLE])
+PLUGIN_VERSION = ".".join([str(x)for x in PLUGIN_VERSION_TUPLE])
# Include an html helpfile in the plugin's zipfile with the following name.
RESOURCE_NAME = PLUGIN_NAME + '_Help.htm'
if self.encoding == None:
self.encoding = "utf-8"
def write(self, data):
- if isinstance(data,bytes):
+ if isinstance(data,str):
data = data.encode(self.encoding,"replace")
try:
- self.stream.write(data)
- self.stream.flush()
+ self.stream.buffer.write(data)
+ self.stream.buffer.flush()
except:
# We can do nothing if a write fails
pass
class DeDRM(FileTypePlugin):
name = PLUGIN_NAME
- description = u"Removes DRM from Amazon Kindle, Adobe Adept (including Kobo), Barnes & Noble, Mobipocket and eReader ebooks. Credit given to i♥cabbages and The Dark Reverser for the original stand-alone scripts."
+ description = "Removes DRM from Amazon Kindle, Adobe Adept (including Kobo), Barnes & Noble, Mobipocket and eReader ebooks. Credit given to i♥cabbages and The Dark Reverser for the original stand-alone scripts."
supported_platforms = ['linux', 'osx', 'windows']
- author = u"Apprentice Alf, Aprentice Harper, The Dark Reverser and i♥cabbages"
+ author = "Apprentice Alf, Aprentice Harper, The Dark Reverser and i♥cabbages"
version = PLUGIN_VERSION_TUPLE
- minimum_calibre_version = (1, 0, 0) # Compiled python libraries cannot be imported in earlier versions.
+ minimum_calibre_version = (5, 0, 0) # Python 3.
file_types = set(['epub','pdf','pdb','prc','mobi','pobi','azw','azw1','azw3','azw4','azw8','tpz','kfx','kfx-zip'])
on_import = True
on_preprocess = True
Also perform upgrade of preferences once per version
"""
try:
- self.pluginsdir = os.path.join(config_dir,u"plugins")
+ self.pluginsdir = os.path.join(config_dir,"plugins")
if not os.path.exists(self.pluginsdir):
os.mkdir(self.pluginsdir)
- self.maindir = os.path.join(self.pluginsdir,u"DeDRM")
+ self.maindir = os.path.join(self.pluginsdir,"DeDRM")
if not os.path.exists(self.maindir):
os.mkdir(self.maindir)
- self.helpdir = os.path.join(self.maindir,u"help")
+ self.helpdir = os.path.join(self.maindir,"help")
if not os.path.exists(self.helpdir):
os.mkdir(self.helpdir)
- self.alfdir = os.path.join(self.maindir,u"libraryfiles")
+ self.alfdir = os.path.join(self.maindir,"libraryfiles")
if not os.path.exists(self.alfdir):
os.mkdir(self.alfdir)
# only continue if we've never run this version of the plugin before
self.verdir = os.path.join(self.maindir,PLUGIN_VERSION)
if not os.path.exists(self.verdir):
if iswindows:
- names = [u"alfcrypto.dll",u"alfcrypto64.dll"]
+ names = ["alfcrypto.dll","alfcrypto64.dll"]
elif isosx:
- names = [u"libalfcrypto.dylib"]
+ names = ["libalfcrypto.dylib"]
else:
- names = [u"libalfcrypto32.so",u"libalfcrypto64.so",u"kindlekey.py",u"adobekey.py",u"subasyncio.py"]
+ names = ["libalfcrypto32.so","libalfcrypto64.so","kindlekey.py","adobekey.py","subasyncio.py"]
lib_dict = self.load_resources(names)
print("{0} v{1}: Copying needed library files from plugin's zip".format(PLUGIN_NAME, PLUGIN_VERSION))
# Check original epub archive for zip errors.
import calibre_plugins.dedrm.zipfix
- inf = self.temporary_file(u".epub")
+ inf = self.temporary_file(".epub")
try:
print("{0} v{1}: Verifying zip archive integrity".format(PLUGIN_NAME, PLUGIN_VERSION))
fr = zipfix.fixZip(path_to_ebook, inf.name)
fr.fix()
except Exception as e:
- print(u"{0} v{1}: Error \'{2}\' when checking zip archive".format(PLUGIN_NAME, PLUGIN_VERSION, e.args[0]))
+ print("{0} v{1}: Error \'{2}\' when checking zip archive".format(PLUGIN_NAME, PLUGIN_VERSION, e.args[0]))
raise Exception(e)
# import the decryption keys
# Attempt to decrypt epub with each encryption key (generated or provided).
for keyname, userkey in dedrmprefs['bandnkeys'].items():
- keyname_masked = u"".join((u'X' if (x.isdigit()) else x) for x in keyname)
+ keyname_masked = u"".join(("X" if (x.isdigit()) else x) for x in keyname)
print("{0} v{1}: Trying Encryption key {2:s}".format(PLUGIN_NAME, PLUGIN_VERSION, keyname_masked))
- of = self.temporary_file(u".epub")
+ of = self.temporary_file(".epub")
# Give the user key, ebook and TemporaryPersistent file to the decryption function.
try:
defaultkeys = nookkeys()
else: # linux
- from wineutils import WineGetKeys
+ from .wineutils import WineGetKeys
- scriptpath = os.path.join(self.alfdir,u"ignoblekey.py")
- defaultkeys = WineGetKeys(scriptpath, u".b64",dedrmprefs['adobewineprefix'])
+ scriptpath = os.path.join(self.alfdir,"ignoblekey.py")
+ defaultkeys = WineGetKeys(scriptpath, ".b64",dedrmprefs['adobewineprefix'])
except:
print("{0} v{1}: Exception when getting default NOOK Study Key after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION, time.time()-self.starttime))
for i,userkey in enumerate(newkeys):
print("{0} v{1}: Trying a new default key".format(PLUGIN_NAME, PLUGIN_VERSION))
- of = self.temporary_file(u".epub")
+ of = self.temporary_file(".epub")
# Give the user key, ebook and TemporaryPersistent file to the decryption function.
try:
# Return the modified PersistentTemporary file to calibre.
return of.name
- print(u"{0} v{1}: Failed to decrypt with new default key after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION,time.time()-self.starttime))
+ print("{0} v{1}: Failed to decrypt with new default key after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION,time.time()-self.starttime))
except Exception as e:
pass
print("{0} v{1}: Ultimately failed to decrypt after {2:.1f} seconds. Read the FAQs at Harper's repository: https://github.com/apprenticeharper/DeDRM_tools/blob/master/FAQs.md".format(PLUGIN_NAME, PLUGIN_VERSION,time.time()-self.starttime))
- raise DeDRMError(u"{0} v{1}: Ultimately failed to decrypt after {2:.1f} seconds. Read the FAQs at Harper's repository: https://github.com/apprenticeharper/DeDRM_tools/blob/master/FAQs.md".format(PLUGIN_NAME, PLUGIN_VERSION, time.time()-self.starttime))
+ raise DeDRMError("{0} v{1}: Ultimately failed to decrypt after {2:.1f} seconds. Read the FAQs at Harper's repository: https://github.com/apprenticeharper/DeDRM_tools/blob/master/FAQs.md".format(PLUGIN_NAME, PLUGIN_VERSION, time.time()-self.starttime))
# import the Adobe Adept ePub handler
import calibre_plugins.dedrm.ineptepub as ineptepub
# Attempt to decrypt epub with each encryption key (generated or provided).
for keyname, userkeyhex in dedrmprefs['adeptkeys'].items():
userkey = codecs.decode(userkeyhex, 'hex')
- print(u"{0} v{1}: Trying Encryption key {2:s}".format(PLUGIN_NAME, PLUGIN_VERSION, keyname))
- of = self.temporary_file(u".epub")
+ print("{0} v{1}: Trying Encryption key {2:s}".format(PLUGIN_NAME, PLUGIN_VERSION, keyname))
+ of = self.temporary_file(".epub")
# Give the user key, ebook and TemporaryPersistent file to the decryption function.
try:
defaultkeys = adeptkeys()
else: # linux
- from wineutils import WineGetKeys
+ from .wineutils import WineGetKeys
- scriptpath = os.path.join(self.alfdir,u"adobekey.py")
- defaultkeys = WineGetKeys(scriptpath, u".der",dedrmprefs['adobewineprefix'])
+ scriptpath = os.path.join(self.alfdir,"adobekey.py")
+ defaultkeys = WineGetKeys(scriptpath, ".der",dedrmprefs['adobewineprefix'])
self.default_key = defaultkeys[0]
except:
newkeys = []
for keyvalue in defaultkeys:
- if keyvalue.encode('hex') not in dedrmprefs['adeptkeys'].values():
+ if codecs.encode(keyvalue, 'hex').decode('ascii') not in dedrmprefs['adeptkeys'].values():
newkeys.append(keyvalue)
if len(newkeys) > 0:
try:
for i,userkey in enumerate(newkeys):
print("{0} v{1}: Trying a new default key".format(PLUGIN_NAME, PLUGIN_VERSION))
- of = self.temporary_file(u".epub")
+ of = self.temporary_file(".epub")
# Give the user key, ebook and TemporaryPersistent file to the decryption function.
try:
# Store the new successful key in the defaults
print("{0} v{1}: Saving a new default key".format(PLUGIN_NAME, PLUGIN_VERSION))
try:
- dedrmprefs.addnamedvaluetoprefs('adeptkeys','default_key',keyvalue.encode('hex'))
+ dedrmprefs.addnamedvaluetoprefs('adeptkeys','default_key',codecs.encode(keyvalue, 'hex').decode('ascii'))
dedrmprefs.writeprefs()
print("{0} v{1}: Saved a new default key after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION,time.time()-self.starttime))
except:
# Return the modified PersistentTemporary file to calibre.
return of.name
- print(u"{0} v{1}: Failed to decrypt with new default key after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION,time.time()-self.starttime))
+ print("{0} v{1}: Failed to decrypt with new default key after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION,time.time()-self.starttime))
except Exception as e:
- print(u"{0} v{1}: Unexpected Exception trying a new default key after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION, time.time()-self.starttime))
+ print("{0} v{1}: Unexpected Exception trying a new default key after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION, time.time()-self.starttime))
traceback.print_exc()
pass
# Something went wrong with decryption.
print("{0} v{1}: Ultimately failed to decrypt after {2:.1f} seconds. Read the FAQs at Harper's repository: https://github.com/apprenticeharper/DeDRM_tools/blob/master/FAQs.md".format(PLUGIN_NAME, PLUGIN_VERSION,time.time()-self.starttime))
- raise DeDRMError(u"{0} v{1}: Ultimately failed to decrypt after {2:.1f} seconds. Read the FAQs at Harper's repository: https://github.com/apprenticeharper/DeDRM_tools/blob/master/FAQs.md".format(PLUGIN_NAME, PLUGIN_VERSION,time.time()-self.starttime))
+ raise DeDRMError("{0} v{1}: Ultimately failed to decrypt after {2:.1f} seconds. Read the FAQs at Harper's repository: https://github.com/apprenticeharper/DeDRM_tools/blob/master/FAQs.md".format(PLUGIN_NAME, PLUGIN_VERSION,time.time()-self.starttime))
# Not a Barnes & Noble nor an Adobe Adept
# Import the fixed epub.
print("{0} v{1}: “{2}” is neither an Adobe Adept nor a Barnes & Noble encrypted ePub".format(PLUGIN_NAME, PLUGIN_VERSION, os.path.basename(path_to_ebook)))
- raise DeDRMError(u"{0} v{1}: Couldn't decrypt after {2:.1f} seconds. DRM free perhaps?".format(PLUGIN_NAME, PLUGIN_VERSION,time.time()-self.starttime))
+ raise DeDRMError("{0} v{1}: Couldn't decrypt after {2:.1f} seconds. DRM free perhaps?".format(PLUGIN_NAME, PLUGIN_VERSION,time.time()-self.starttime))
def PDFDecrypt(self,path_to_ebook):
import calibre_plugins.dedrm.prefs as prefs
# Attempt to decrypt epub with each encryption key (generated or provided).
print("{0} v{1}: {2} is a PDF ebook".format(PLUGIN_NAME, PLUGIN_VERSION, os.path.basename(path_to_ebook)))
for keyname, userkeyhex in dedrmprefs['adeptkeys'].items():
- userkey = codecs.decode(userkeyhex, 'hex')
+ userkey = userkeyhex.decode('hex')
print("{0} v{1}: Trying Encryption key {2:s}".format(PLUGIN_NAME, PLUGIN_VERSION, keyname))
- of = self.temporary_file(u".pdf")
+ of = self.temporary_file(".pdf")
# Give the user key, ebook and TemporaryPersistent file to the decryption function.
try:
defaultkeys = adeptkeys()
else: # linux
- from wineutils import WineGetKeys
+ from .wineutils import WineGetKeys
- scriptpath = os.path.join(self.alfdir,u"adobekey.py")
- defaultkeys = WineGetKeys(scriptpath, u".der",dedrmprefs['adobewineprefix'])
+ scriptpath = os.path.join(self.alfdir,"adobekey.py")
+ defaultkeys = WineGetKeys(scriptpath, ".der",dedrmprefs['adobewineprefix'])
self.default_key = defaultkeys[0]
except:
try:
for i,userkey in enumerate(newkeys):
print("{0} v{1}: Trying a new default key".format(PLUGIN_NAME, PLUGIN_VERSION))
- of = self.temporary_file(u".pdf")
+ of = self.temporary_file(".pdf")
# Give the user key, ebook and TemporaryPersistent file to the decryption function.
try:
# Return the modified PersistentTemporary file to calibre.
return of.name
- print(u"{0} v{1}: Failed to decrypt with new default key after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION,time.time()-self.starttime))
+ print("{0} v{1}: Failed to decrypt with new default key after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION,time.time()-self.starttime))
except Exception as e:
pass
# Something went wrong with decryption.
print("{0} v{1}: Ultimately failed to decrypt after {2:.1f} seconds. Read the FAQs at Harper's repository: https://github.com/apprenticeharper/DeDRM_tools/blob/master/FAQs.md".format(PLUGIN_NAME, PLUGIN_VERSION,time.time()-self.starttime))
- raise DeDRMError(u"{0} v{1}: Ultimately failed to decrypt after {2:.1f} seconds. Read the FAQs at Harper's repository: https://github.com/apprenticeharper/DeDRM_tools/blob/master/FAQs.md".format(PLUGIN_NAME, PLUGIN_VERSION, time.time()-self.starttime))
+ raise DeDRMError("{0} v{1}: Ultimately failed to decrypt after {2:.1f} seconds. Read the FAQs at Harper's repository: https://github.com/apprenticeharper/DeDRM_tools/blob/master/FAQs.md".format(PLUGIN_NAME, PLUGIN_VERSION, time.time()-self.starttime))
def KindleMobiDecrypt(self,path_to_ebook):
serials.extend(android_serials_list)
#print serials
androidFiles = []
- kindleDatabases = dedrmprefs['kindlekeys'].items()
+ kindleDatabases = list(dedrmprefs['kindlekeys'].items())
try:
book = k4mobidedrm.GetDecryptedBook(path_to_ebook,kindleDatabases,androidFiles,serials,pids,self.starttime)
defaultkeys = kindlekeys()
else: # linux
- from wineutils import WineGetKeys
+ from .wineutils import WineGetKeys
- scriptpath = os.path.join(self.alfdir,u"kindlekey.py")
- defaultkeys = WineGetKeys(scriptpath, u".k4i",dedrmprefs['kindlewineprefix'])
+ scriptpath = os.path.join(self.alfdir,"kindlekey.py")
+ defaultkeys = WineGetKeys(scriptpath, ".k4i",dedrmprefs['kindlewineprefix'])
except:
print("{0} v{1}: Exception when getting default Kindle Key after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION, time.time()-self.starttime))
traceback.print_exc()
newkeys = {}
for i,keyvalue in enumerate(defaultkeys):
- keyname = u"default_key_{0:d}".format(i+1)
+ keyname = "default_key_{0:d}".format(i+1)
if keyvalue not in dedrmprefs['kindlekeys'].values():
newkeys[keyname] = keyvalue
if len(newkeys) > 0:
- print("{0} v{1}: Found {2} new {3}".format(PLUGIN_NAME, PLUGIN_VERSION, len(newkeys), u"key" if len(newkeys)==1 else u"keys"))
+ print("{0} v{1}: Found {2} new {3}".format(PLUGIN_NAME, PLUGIN_VERSION, len(newkeys), "key" if len(newkeys)==1 else "keys"))
try:
- book = k4mobidedrm.GetDecryptedBook(path_to_ebook,newkeys.items(),[],[],[],self.starttime)
+ book = k4mobidedrm.GetDecryptedBook(path_to_ebook,list(newkeys.items()),[],[],[],self.starttime)
decoded = True
# store the new successful keys in the defaults
- print("{0} v{1}: Saving {2} new {3}".format(PLUGIN_NAME, PLUGIN_VERSION, len(newkeys), u"key" if len(newkeys)==1 else u"keys"))
+ print("{0} v{1}: Saving {2} new {3}".format(PLUGIN_NAME, PLUGIN_VERSION, len(newkeys), "key" if len(newkeys)==1 else "keys"))
for keyvalue in newkeys.values():
dedrmprefs.addnamedvaluetoprefs('kindlekeys','default_key',keyvalue)
dedrmprefs.writeprefs()
if not decoded:
#if you reached here then no luck raise and exception
print("{0} v{1}: Ultimately failed to decrypt after {2:.1f} seconds. Read the FAQs at Harper's repository: https://github.com/apprenticeharper/DeDRM_tools/blob/master/FAQs.md".format(PLUGIN_NAME, PLUGIN_VERSION,time.time()-self.starttime))
- raise DeDRMError(u"{0} v{1}: Ultimately failed to decrypt after {2:.1f} seconds. Read the FAQs at Harper's repository: https://github.com/apprenticeharper/DeDRM_tools/blob/master/FAQs.md".format(PLUGIN_NAME, PLUGIN_VERSION,time.time()-self.starttime))
+ raise DeDRMError("{0} v{1}: Ultimately failed to decrypt after {2:.1f} seconds. Read the FAQs at Harper's repository: https://github.com/apprenticeharper/DeDRM_tools/blob/master/FAQs.md".format(PLUGIN_NAME, PLUGIN_VERSION,time.time()-self.starttime))
of = self.temporary_file(book.getBookExtension())
book.getFile(of.name)
dedrmprefs = prefs.DeDRM_Prefs()
# Attempt to decrypt epub with each encryption key (generated or provided).
for keyname, userkey in dedrmprefs['ereaderkeys'].items():
- keyname_masked = u"".join((u'X' if (x.isdigit()) else x) for x in keyname)
+ keyname_masked = u"".join(("X" if (x.isdigit()) else x) for x in keyname)
print("{0} v{1}: Trying Encryption key {2:s}".format(PLUGIN_NAME, PLUGIN_VERSION, keyname_masked))
- of = self.temporary_file(u".pmlz")
+ of = self.temporary_file(".pmlz")
# Give the userkey, ebook and TemporaryPersistent file to the decryption function.
result = erdr2pml.decryptBook(path_to_ebook, of.name, True, userkey.decode('hex'))
print("{0} v{1}: Failed to decrypt with key {2:s} after {3:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION,keyname_masked,time.time()-self.starttime))
print("{0} v{1}: Ultimately failed to decrypt after {2:.1f} seconds. Read the FAQs at Harper's repository: https://github.com/apprenticeharper/DeDRM_tools/blob/master/FAQs.md".format(PLUGIN_NAME, PLUGIN_VERSION,time.time()-self.starttime))
- raise DeDRMError(u"{0} v{1}: Ultimately failed to decrypt after {2:.1f} seconds. Read the FAQs at Harper's repository: https://github.com/apprenticeharper/DeDRM_tools/blob/master/FAQs.md".format(PLUGIN_NAME, PLUGIN_VERSION, time.time()-self.starttime))
+ raise DeDRMError("{0} v{1}: Ultimately failed to decrypt after {2:.1f} seconds. Read the FAQs at Harper's repository: https://github.com/apprenticeharper/DeDRM_tools/blob/master/FAQs.md".format(PLUGIN_NAME, PLUGIN_VERSION, time.time()-self.starttime))
def run(self, path_to_ebook):
-#!/usr/bin/env python
+#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# adobekey.pyw, version 6.0
-# Copyright © 2009-2010 i♥cabbages
+# Copyright © 2009-2020 i♥cabbages, Apprentice Harper et al.
# Released under the terms of the GNU General Public Licence, version 3
# <http://www.gnu.org/licenses/>
-# Modified 2010–2016 by several people
-
-# Windows users: Before running this program, you must first install Python.
-# We recommend ActiveState Python 2.7.X for Windows (x86) from
-# http://www.activestate.com/activepython/downloads.
-# You must also install PyCrypto from
-# http://www.voidspace.org.uk/python/modules.shtml#pycrypto
-# (make certain to install the version for Python 2.7).
-# Then save this script file as adobekey.pyw and double-click on it to run it.
-# It will create a file named adobekey_1.der in in the same directory as the script.
-# This is your Adobe Digital Editions user key.
-#
-# Mac OS X users: Save this script file as adobekey.pyw. You can run this
-# program from the command line (python adobekey.pyw) or by double-clicking
-# it when it has been associated with PythonLauncher. It will create a file
-# named adobekey_1.der in the same directory as the script.
-# This is your Adobe Digital Editions user key.
-
# Revision history:
# 1 - Initial release, for Adobe Digital Editions 1.7
# 2 - Better algorithm for finding pLK; improved error handling
# 5.8 - Added getkey interface for Windows DeDRM application
# 5.9 - moved unicode_argv call inside main for Windows DeDRM compatibility
# 6.0 - Work if TkInter is missing
-# 7.0 - Python 3 compatible for calibre 5
+# 7.0 - Python 3 for calibre 5
"""
Retrieve Adobe ADEPT user key.
-#! /usr/bin/env python
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
"""
Routines for doing AES CBC in one file
See the wonderful pure python package cryptopy-1.2.5
and read its LICENSE.txt for complete license details.
- Adjusted for Python 3 compatibility, September 2020
+ Adjusted for Python 3, September 2020
"""
class CryptoError(Exception):
-#!/usr/bin/env python
+#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# crypto library mainly by some_updates
# pbkdf2.py Copyright © 2009 Daniel Holth <dholth@fastmail.fm>
# pbkdf2.py This code may be freely used and modified for any purpose.
-from __future__ import print_function
import sys, os
import hmac
from struct import pack
topazCryptoDecrypt(ctx, data, out, len(data))
return out.raw
- print(u"Using Library AlfCrypto DLL/DYLIB/SO")
+ print("Using Library AlfCrypto DLL/DYLIB/SO")
return (AES_CBC, Pukall_Cipher, Topaz_Cipher)
cleartext = self.aes.decrypt(iv + data)
return cleartext
- print(u"Using Library AlfCrypto Python")
+ print("Using Library AlfCrypto Python")
return (AES_CBC, Pukall_Cipher, Topaz_Cipher)
-#!/usr/bin/env python
+#!/usr/bin/env python3
# -*- coding: utf-8 -*-
-from __future__ import with_statement
-from __future__ import print_function
-
# androidkindlekey.py
-# Copyright © 2013-15 by Thom and Apprentice Harper
-# Some portions Copyright © 2010-15 by some_updates and Apprentice Alf
-#
+# Copyright © 2010-20 by Thom, Apprentice et al.
# Revision history:
# 1.0 - AmazonSecureStorage.xml decryption to serial number
# 1.3 - added in TkInter interface, output to a file
# 1.4 - Fix some problems identified by Aldo Bleeker
# 1.5 - Fix another problem identified by Aldo Bleeker
-# 2.0 - Add Python 3 compatibility
+# 2.0 - Python 3 compatibility
"""
Retrieve Kindle for Android Serial Number.
if self.encoding == None:
self.encoding = "utf-8"
def write(self, data):
- if isinstance(data,bytes):
+ if isinstance(data,str):
data = data.encode(self.encoding,"replace")
- self.stream.write(data)
- self.stream.flush()
+ self.stream.buffer.write(data)
+ self.stream.buffer.flush()
+
def __getattr__(self, attr):
return getattr(self.stream, attr)
range(start, argc.value)]
# if we don't have any arguments at all, just pass back script name
# this should never happen
- return [u"kindlekey.py"]
+ return ["kindlekey.py"]
else:
argvencoding = sys.stdin.encoding
if argvencoding == None:
class DrmException(Exception):
pass
-STORAGE = u"backup.ab"
-STORAGE1 = u"AmazonSecureStorage.xml"
-STORAGE2 = u"map_data_storage.db"
+STORAGE = "backup.ab"
+STORAGE1 = "AmazonSecureStorage.xml"
+STORAGE2 = "map_data_storage.db"
class AndroidObfuscation(object):
'''AndroidObfuscation
def usage(progname):
- print(u"Decrypts the serial number(s) of Kindle For Android from Android backup or file")
- print(u"Get backup.ab file using adb backup com.amazon.kindle for Android 4.0+.")
- print(u"Otherwise extract AmazonSecureStorage.xml from /data/data/com.amazon.kindle/shared_prefs/AmazonSecureStorage.xml")
- print(u"Or map_data_storage.db from /data/data/com.amazon.kindle/databases/map_data_storage.db")
+ print("Decrypts the serial number(s) of Kindle For Android from Android backup or file")
+ print("Get backup.ab file using adb backup com.amazon.kindle for Android 4.0+.")
+ print("Otherwise extract AmazonSecureStorage.xml from /data/data/com.amazon.kindle/shared_prefs/AmazonSecureStorage.xml")
+ print("Or map_data_storage.db from /data/data/com.amazon.kindle/databases/map_data_storage.db")
print(u"")
- print(u"Usage:")
- print(u" {0:s} [-h] [-b <backup.ab>] [<outfile.k4a>]".format(progname))
+ print("Usage:")
+ print(" {0:s} [-h] [-b <backup.ab>] [<outfile.k4a>]".format(progname))
def cli_main():
sys.stderr=SafeUnbuffered(sys.stderr)
argv=unicode_argv()
progname = os.path.basename(argv[0])
- print(u"{0} v{1}\nCopyright © 2010-2015 Thom, some_updates, Apprentice Alf and Apprentice Harper".format(progname,__version__))
+ print("{0} v{1}\nCopyright © 2010-2015 Thom, some_updates, Apprentice Alf and Apprentice Harper".format(progname,__version__))
try:
opts, args = getopt.getopt(argv[1:], "hb:")
except getopt.GetoptError as err:
usage(progname)
- print(u"\nError in options or arguments: {0}".format(err.args[0]))
+ print("\nError in options or arguments: {0}".format(err.args[0]))
return 2
inpath = ""
if not os.path.isfile(inpath):
usage(progname)
- print(u"\n{0:s} file not found".format(inpath))
+ print("\n{0:s} file not found".format(inpath))
return 2
if getkey(outfile, inpath):
- print(u"\nSaved Kindle for Android key to {0}".format(outfile))
+ print("\nSaved Kindle for Android key to {0}".format(outfile))
else:
- print(u"\nCould not retrieve Kindle for Android key.")
+ print("\nCould not retrieve Kindle for Android key.")
return 0
class DecryptionDialog(Tkinter.Frame):
def __init__(self, root):
Tkinter.Frame.__init__(self, root, border=5)
- self.status = Tkinter.Label(self, text=u"Select backup.ab file")
+ self.status = Tkinter.Label(self, text="Select backup.ab file")
self.status.pack(fill=Tkconstants.X, expand=1)
body = Tkinter.Frame(self)
body.pack(fill=Tkconstants.X, expand=1)
sticky = Tkconstants.E + Tkconstants.W
body.grid_columnconfigure(1, weight=2)
- Tkinter.Label(body, text=u"Backup file").grid(row=0, column=0)
+ Tkinter.Label(body, text="Backup file").grid(row=0, column=0)
self.keypath = Tkinter.Entry(body, width=40)
self.keypath.grid(row=0, column=1, sticky=sticky)
- self.keypath.insert(2, u"backup.ab")
- button = Tkinter.Button(body, text=u"...", command=self.get_keypath)
+ self.keypath.insert(2, "backup.ab")
+ button = Tkinter.Button(body, text="...", command=self.get_keypath)
button.grid(row=0, column=2)
buttons = Tkinter.Frame(self)
buttons.pack()
button2 = Tkinter.Button(
- buttons, text=u"Extract", width=10, command=self.generate)
+ buttons, text="Extract", width=10, command=self.generate)
button2.pack(side=Tkconstants.LEFT)
Tkinter.Frame(buttons, width=10).pack(side=Tkconstants.LEFT)
button3 = Tkinter.Button(
- buttons, text=u"Quit", width=10, command=self.quit)
+ buttons, text="Quit", width=10, command=self.quit)
button3.pack(side=Tkconstants.RIGHT)
def get_keypath(self):
keypath = tkFileDialog.askopenfilename(
- parent=None, title=u"Select backup.ab file",
- defaultextension=u".ab",
+ parent=None, title="Select backup.ab file",
+ defaultextension=".ab",
filetypes=[('adb backup com.amazon.kindle', '.ab'),
('All Files', '.*')])
if keypath:
def generate(self):
inpath = self.keypath.get()
- self.status['text'] = u"Getting key..."
+ self.status['text'] = "Getting key..."
try:
keys = get_serials(inpath)
keycount = 0
for key in keys:
while True:
keycount += 1
- outfile = os.path.join(progpath,u"kindlekey{0:d}.k4a".format(keycount))
+ outfile = os.path.join(progpath,"kindlekey{0:d}.k4a".format(keycount))
if not os.path.exists(outfile):
break
with open(outfile, 'w') as keyfileout:
keyfileout.write(key)
success = True
- tkMessageBox.showinfo(progname, u"Key successfully retrieved to {0}".format(outfile))
+ tkMessageBox.showinfo(progname, "Key successfully retrieved to {0}".format(outfile))
except Exception as e:
- self.status['text'] = u"Error: {0}".format(e.args[0])
+ self.status['text'] = "Error: {0}".format(e.args[0])
return
- self.status['text'] = u"Select backup.ab file"
+ self.status['text'] = "Select backup.ab file"
argv=unicode_argv()
progpath, progname = os.path.split(argv[0])
root = Tkinter.Tk()
- root.title(u"Kindle for Android Key Extraction v.{0}".format(__version__))
+ root.title("Kindle for Android Key Extraction v.{0}".format(__version__))
root.resizable(True, False)
root.minsize(300, 0)
DecryptionDialog(root).pack(fill=Tkconstants.X, expand=1)
-#!/usr/bin/env python
+#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import sys, os
xrange(start, argc.value)]
# if we don't have any arguments at all, just pass back script name
# this should never happen
- return [u"DeDRM.py"]
+ return ["DeDRM.py"]
else:
argvencoding = sys.stdin.encoding
if argvencoding == None:
-#!/usr/bin/env python
+#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# vim:ts=4:sw=4:softtabstop=4:smarttab:expandtab
if not pidl:
result = None
else:
- path = LPCWSTR(u" " * (MAX_PATH+1))
+ path = LPCWSTR(" " * (MAX_PATH+1))
shell32.SHGetPathFromIDListW(pidl, path)
ole32.CoTaskMemFree(pidl)
result = path.value
-#!/usr/bin/env python
+#!/usr/bin/env python3
# -*- coding: utf-8 -*-
-from __future__ import with_statement
-from __future__ import print_function
-
__license__ = 'GPL v3'
-# Added Python 3 compatibility, September 2020
+# Python 3, September 2020
# Standard Python modules.
import os, traceback, json
-# PyQT4 modules (part of calibre).
-try:
- from PyQt5.Qt import (Qt, QWidget, QHBoxLayout, QVBoxLayout, QLabel, QLineEdit,
- QGroupBox, QPushButton, QListWidget, QListWidgetItem,
- QAbstractItemView, QIcon, QDialog, QDialogButtonBox, QUrl)
-except ImportError:
- from PyQt4.Qt import (Qt, QWidget, QHBoxLayout, QVBoxLayout, QLabel, QLineEdit,
+from PyQt5.Qt import (Qt, QWidget, QHBoxLayout, QVBoxLayout, QLabel, QLineEdit,
QGroupBox, QPushButton, QListWidget, QListWidgetItem,
QAbstractItemView, QIcon, QDialog, QDialogButtonBox, QUrl)
-try:
- from PyQt5 import Qt as QtGui
-except ImportError:
- from PyQt4 import QtGui
+from PyQt5 import Qt as QtGui
from zipfile import ZipFile
# calibre modules and constants.
button_layout = QVBoxLayout()
keys_group_box_layout.addLayout(button_layout)
self.bandn_button = QtGui.QPushButton(self)
- self.bandn_button.setToolTip(_(u"Click to manage keys for Barnes and Noble ebooks"))
- self.bandn_button.setText(u"Barnes and Noble ebooks")
+ self.bandn_button.setToolTip(_("Click to manage keys for Barnes and Noble ebooks"))
+ self.bandn_button.setText("Barnes and Noble ebooks")
self.bandn_button.clicked.connect(self.bandn_keys)
self.kindle_android_button = QtGui.QPushButton(self)
- self.kindle_android_button.setToolTip(_(u"Click to manage keys for Kindle for Android ebooks"))
- self.kindle_android_button.setText(u"Kindle for Android ebooks")
+ self.kindle_android_button.setToolTip(_("Click to manage keys for Kindle for Android ebooks"))
+ self.kindle_android_button.setText("Kindle for Android ebooks")
self.kindle_android_button.clicked.connect(self.kindle_android)
self.kindle_serial_button = QtGui.QPushButton(self)
- self.kindle_serial_button.setToolTip(_(u"Click to manage eInk Kindle serial numbers for Kindle ebooks"))
- self.kindle_serial_button.setText(u"eInk Kindle ebooks")
+ self.kindle_serial_button.setToolTip(_("Click to manage eInk Kindle serial numbers for Kindle ebooks"))
+ self.kindle_serial_button.setText("eInk Kindle ebooks")
self.kindle_serial_button.clicked.connect(self.kindle_serials)
self.kindle_key_button = QtGui.QPushButton(self)
- self.kindle_key_button.setToolTip(_(u"Click to manage keys for Kindle for Mac/PC ebooks"))
- self.kindle_key_button.setText(u"Kindle for Mac/PC ebooks")
+ self.kindle_key_button.setToolTip(_("Click to manage keys for Kindle for Mac/PC ebooks"))
+ self.kindle_key_button.setText("Kindle for Mac/PC ebooks")
self.kindle_key_button.clicked.connect(self.kindle_keys)
self.adept_button = QtGui.QPushButton(self)
- self.adept_button.setToolTip(_(u"Click to manage keys for Adobe Digital Editions ebooks"))
- self.adept_button.setText(u"Adobe Digital Editions ebooks")
+ self.adept_button.setToolTip(_("Click to manage keys for Adobe Digital Editions ebooks"))
+ self.adept_button.setText("Adobe Digital Editions ebooks")
self.adept_button.clicked.connect(self.adept_keys)
self.mobi_button = QtGui.QPushButton(self)
- self.mobi_button.setToolTip(_(u"Click to manage PIDs for Mobipocket ebooks"))
- self.mobi_button.setText(u"Mobipocket ebooks")
+ self.mobi_button.setToolTip(_("Click to manage PIDs for Mobipocket ebooks"))
+ self.mobi_button.setText("Mobipocket ebooks")
self.mobi_button.clicked.connect(self.mobi_keys)
self.ereader_button = QtGui.QPushButton(self)
- self.ereader_button.setToolTip(_(u"Click to manage keys for eReader ebooks"))
- self.ereader_button.setText(u"eReader ebooks")
+ self.ereader_button.setToolTip(_("Click to manage keys for eReader ebooks"))
+ self.ereader_button.setText("eReader ebooks")
self.ereader_button.clicked.connect(self.ereader_keys)
button_layout.addWidget(self.kindle_serial_button)
button_layout.addWidget(self.kindle_android_button)
self.resize(self.sizeHint())
def kindle_serials(self):
- d = ManageKeysDialog(self,u"EInk Kindle Serial Number",self.tempdedrmprefs['serials'], AddSerialDialog)
+ d = ManageKeysDialog(self,"EInk Kindle Serial Number",self.tempdedrmprefs['serials'], AddSerialDialog)
d.exec_()
def kindle_android(self):
- d = ManageKeysDialog(self,u"Kindle for Android Key",self.tempdedrmprefs['androidkeys'], AddAndroidDialog, 'k4a')
+ d = ManageKeysDialog(self,"Kindle for Android Key",self.tempdedrmprefs['androidkeys'], AddAndroidDialog, 'k4a')
d.exec_()
def kindle_keys(self):
if isosx or iswindows:
- d = ManageKeysDialog(self,u"Kindle for Mac and PC Key",self.tempdedrmprefs['kindlekeys'], AddKindleDialog, 'k4i')
+ d = ManageKeysDialog(self,"Kindle for Mac and PC Key",self.tempdedrmprefs['kindlekeys'], AddKindleDialog, 'k4i')
else:
# linux
- d = ManageKeysDialog(self,u"Kindle for Mac and PC Key",self.tempdedrmprefs['kindlekeys'], AddKindleDialog, 'k4i', self.tempdedrmprefs['kindlewineprefix'])
+ d = ManageKeysDialog(self,"Kindle for Mac and PC Key",self.tempdedrmprefs['kindlekeys'], AddKindleDialog, 'k4i', self.tempdedrmprefs['kindlewineprefix'])
d.exec_()
self.tempdedrmprefs['kindlewineprefix'] = d.getwineprefix()
def adept_keys(self):
if isosx or iswindows:
- d = ManageKeysDialog(self,u"Adobe Digital Editions Key",self.tempdedrmprefs['adeptkeys'], AddAdeptDialog, 'der')
+ d = ManageKeysDialog(self,"Adobe Digital Editions Key",self.tempdedrmprefs['adeptkeys'], AddAdeptDialog, 'der')
else:
# linux
- d = ManageKeysDialog(self,u"Adobe Digital Editions Key",self.tempdedrmprefs['adeptkeys'], AddAdeptDialog, 'der', self.tempdedrmprefs['adobewineprefix'])
+ d = ManageKeysDialog(self,"Adobe Digital Editions Key",self.tempdedrmprefs['adeptkeys'], AddAdeptDialog, 'der', self.tempdedrmprefs['adobewineprefix'])
d.exec_()
self.tempdedrmprefs['adobewineprefix'] = d.getwineprefix()
def mobi_keys(self):
- d = ManageKeysDialog(self,u"Mobipocket PID",self.tempdedrmprefs['pids'], AddPIDDialog)
+ d = ManageKeysDialog(self,"Mobipocket PID",self.tempdedrmprefs['pids'], AddPIDDialog)
d.exec_()
def bandn_keys(self):
- d = ManageKeysDialog(self,u"Barnes and Noble Key",self.tempdedrmprefs['bandnkeys'], AddBandNKeyDialog, 'b64')
+ d = ManageKeysDialog(self,"Barnes and Noble Key",self.tempdedrmprefs['bandnkeys'], AddBandNKeyDialog, 'b64')
d.exec_()
def ereader_keys(self):
- d = ManageKeysDialog(self,u"eReader Key",self.tempdedrmprefs['ereaderkeys'], AddEReaderDialog, 'b63')
+ d = ManageKeysDialog(self,"eReader Key",self.tempdedrmprefs['ereaderkeys'], AddEReaderDialog, 'b63')
d.exec_()
def help_link_activated(self, url):
def get_help_file_resource():
# Copy the HTML helpfile to the plugin directory each time the
# link is clicked in case the helpfile is updated in newer plugins.
- file_path = os.path.join(config_dir, u"plugins", u"DeDRM", u"help", help_file_name)
+ file_path = os.path.join(config_dir, "plugins", "DeDRM", "help", help_file_name)
with open(file_path,'w') as f:
f.write(self.load_resource(help_file_name))
return file_path
self.create_key = create_key
self.keyfile_ext = keyfile_ext
self.import_key = (keyfile_ext != u"")
- self.binary_file = (keyfile_ext == u"der")
- self.json_file = (keyfile_ext == u"k4i")
- self.android_file = (keyfile_ext == u"k4a")
+ self.binary_file = (keyfile_ext == "der")
+ self.json_file = (keyfile_ext == "k4i")
+ self.android_file = (keyfile_ext == "k4a")
self.wineprefix = wineprefix
self.setWindowTitle("{0} {1}: Manage {2}s".format(PLUGIN_NAME, PLUGIN_VERSION, self.key_type_name))
help_label.linkActivated.connect(self.help_link_activated)
help_layout.addWidget(help_label)
- keys_group_box = QGroupBox(_(u"{0}s".format(self.key_type_name)), self)
+ keys_group_box = QGroupBox(_("{0}s".format(self.key_type_name)), self)
layout.addWidget(keys_group_box)
keys_group_box_layout = QHBoxLayout()
keys_group_box.setLayout(keys_group_box_layout)
self.listy = QListWidget(self)
- self.listy.setToolTip(u"{0}s that will be used to decrypt ebooks".format(self.key_type_name))
+ self.listy.setToolTip("{0}s that will be used to decrypt ebooks".format(self.key_type_name))
self.listy.setSelectionMode(QAbstractItemView.SingleSelection)
self.populate_list()
keys_group_box_layout.addWidget(self.listy)
keys_group_box_layout.addLayout(button_layout)
self._add_key_button = QtGui.QToolButton(self)
self._add_key_button.setIcon(QIcon(I('plus.png')))
- self._add_key_button.setToolTip(u"Create new {0}".format(self.key_type_name))
+ self._add_key_button.setToolTip("Create new {0}".format(self.key_type_name))
self._add_key_button.clicked.connect(self.add_key)
button_layout.addWidget(self._add_key_button)
self._delete_key_button = QtGui.QToolButton(self)
- self._delete_key_button.setToolTip(_(u"Delete highlighted key"))
+ self._delete_key_button.setToolTip(_("Delete highlighted key"))
self._delete_key_button.setIcon(QIcon(I('list_remove.png')))
self._delete_key_button.clicked.connect(self.delete_key)
button_layout.addWidget(self._delete_key_button)
if type(self.plugin_keys) == dict and self.import_key:
self._rename_key_button = QtGui.QToolButton(self)
- self._rename_key_button.setToolTip(_(u"Rename highlighted key"))
+ self._rename_key_button.setToolTip(_("Rename highlighted key"))
self._rename_key_button.setIcon(QIcon(I('edit-select-all.png')))
self._rename_key_button.clicked.connect(self.rename_key)
button_layout.addWidget(self._rename_key_button)
self.export_key_button = QtGui.QToolButton(self)
- self.export_key_button.setToolTip(u"Save highlighted key to a .{0} file".format(self.keyfile_ext))
+ self.export_key_button.setToolTip("Save highlighted key to a .{0} file".format(self.keyfile_ext))
self.export_key_button.setIcon(QIcon(I('save.png')))
self.export_key_button.clicked.connect(self.export_key)
button_layout.addWidget(self.export_key_button)
wineprefix_layout = QHBoxLayout()
layout.addLayout(wineprefix_layout)
wineprefix_layout.setAlignment(Qt.AlignCenter)
- self.wp_label = QLabel(u"WINEPREFIX:")
+ self.wp_label = QLabel("WINEPREFIX:")
wineprefix_layout.addWidget(self.wp_label)
self.wp_lineedit = QLineEdit(self)
wineprefix_layout.addWidget(self.wp_lineedit)
layout.addLayout(migrate_layout)
if self.import_key:
migrate_layout.setAlignment(Qt.AlignJustify)
- self.migrate_btn = QPushButton(u"Import Existing Keyfiles", self)
- self.migrate_btn.setToolTip(u"Import *.{0} files (created using other tools).".format(self.keyfile_ext))
+ self.migrate_btn = QPushButton("Import Existing Keyfiles", self)
+ self.migrate_btn.setToolTip("Import *.{0} files (created using other tools).".format(self.keyfile_ext))
self.migrate_btn.clicked.connect(self.migrate_wrapper)
migrate_layout.addWidget(self.migrate_btn)
migrate_layout.addStretch()
if new_key_value in self.plugin_keys.values():
old_key_name = [name for name, value in self.plugin_keys.iteritems() if value == new_key_value][0]
info_dialog(None, "{0} {1}: Duplicate {2}".format(PLUGIN_NAME, PLUGIN_VERSION,self.key_type_name),
- u"The new {1} is the same as the existing {1} named <strong>{0}</strong> and has not been added.".format(old_key_name,self.key_type_name), show=True)
+ "The new {1} is the same as the existing {1} named <strong>{0}</strong> and has not been added.".format(old_key_name,self.key_type_name), show=True)
return
self.plugin_keys[d.key_name] = new_key_value
else:
if new_key_value in self.plugin_keys:
info_dialog(None, "{0} {1}: Duplicate {2}".format(PLUGIN_NAME, PLUGIN_VERSION,self.key_type_name),
- u"This {0} is already in the list of {0}s has not been added.".format(self.key_type_name), show=True)
+ "This {0} is already in the list of {0}s has not been added.".format(self.key_type_name), show=True)
return
self.plugin_keys.append(d.key_value)
def rename_key(self):
if not self.listy.currentItem():
- errmsg = u"No {0} selected to rename. Highlight a keyfile first.".format(self.key_type_name)
+ errmsg = "No {0} selected to rename. Highlight a keyfile first.".format(self.key_type_name)
r = error_dialog(None, "{0} {1}".format(PLUGIN_NAME, PLUGIN_VERSION),
_(errmsg), show=True, show_copy_button=False)
return
# rename cancelled or moot.
return
keyname = self.listy.currentItem().text()
- if not question_dialog(self, "{0} {1}: Confirm Rename".format(PLUGIN_NAME, PLUGIN_VERSION), u"Do you really want to rename the {2} named <strong>{0}</strong> to <strong>{1}</strong>?".format(keyname,d.key_name,self.key_type_name), show_copy_button=False, default_yes=False):
+ if not question_dialog(self, "{0} {1}: Confirm Rename".format(PLUGIN_NAME, PLUGIN_VERSION), "Do you really want to rename the {2} named <strong>{0}</strong> to <strong>{1}</strong>?".format(keyname,d.key_name,self.key_type_name), show_copy_button=False, default_yes=False):
return
self.plugin_keys[d.key_name] = self.plugin_keys[keyname]
del self.plugin_keys[keyname]
if not self.listy.currentItem():
return
keyname = self.listy.currentItem().text()
- if not question_dialog(self, "{0} {1}: Confirm Delete".format(PLUGIN_NAME, PLUGIN_VERSION), u"Do you really want to delete the {1} <strong>{0}</strong>?".format(keyname, self.key_type_name), show_copy_button=False, default_yes=False):
+ if not question_dialog(self, "{0} {1}: Confirm Delete".format(PLUGIN_NAME, PLUGIN_VERSION), "Do you really want to delete the {1} <strong>{0}</strong>?".format(keyname, self.key_type_name), show_copy_button=False, default_yes=False):
return
if type(self.plugin_keys) == dict:
del self.plugin_keys[keyname]
def get_help_file_resource():
# Copy the HTML helpfile to the plugin directory each time the
# link is clicked in case the helpfile is updated in newer plugins.
- help_file_name = u"{0}_{1}_Help.htm".format(PLUGIN_NAME, self.key_type_name)
- file_path = os.path.join(config_dir, u"plugins", u"DeDRM", u"help", help_file_name)
+ help_file_name = "{0}_{1}_Help.htm".format(PLUGIN_NAME, self.key_type_name)
+ file_path = os.path.join(config_dir, "plugins", "DeDRM", "help", help_file_name)
with open(file_path,'w') as f:
f.write(self.parent.load_resource(help_file_name))
return file_path
open_url(QUrl(url))
def migrate_files(self):
- unique_dlg_name = PLUGIN_NAME + u"import {0} keys".format(self.key_type_name).replace(' ', '_') #takes care of automatically remembering last directory
- caption = u"Select {0} files to import".format(self.key_type_name)
- filters = [(u"{0} files".format(self.key_type_name), [self.keyfile_ext])]
+ unique_dlg_name = PLUGIN_NAME + "import {0} keys".format(self.key_type_name).replace(' ', '_') #takes care of automatically remembering last directory
+ caption = "Select {0} files to import".format(self.key_type_name)
+ filters = [("{0} files".format(self.key_type_name), [self.keyfile_ext])]
files = choose_files(self, unique_dlg_name, caption, filters, all_files=False)
counter = 0
skipped = 0
for key in self.plugin_keys.keys():
if uStrCmp(new_key_name, key, True):
skipped += 1
- msg = u"A key with the name <strong>{0}</strong> already exists!\nSkipping key file <strong>{1}</strong>.\nRename the existing key and import again".format(new_key_name,filename)
+ msg = "A key with the name <strong>{0}</strong> already exists!\nSkipping key file <strong>{1}</strong>.\nRename the existing key and import again".format(new_key_name,filename)
inf = info_dialog(None, "{0} {1}".format(PLUGIN_NAME, PLUGIN_VERSION),
_(msg), show_copy_button=False, show=True)
match = True
old_key_name = [name for name, value in self.plugin_keys.iteritems() if value == new_key_value][0]
skipped += 1
info_dialog(None, "{0} {1}".format(PLUGIN_NAME, PLUGIN_VERSION),
- u"The key in file {0} is the same as the existing key <strong>{1}</strong> and has been skipped.".format(filename,old_key_name), show_copy_button=False, show=True)
+ "The key in file {0} is the same as the existing key <strong>{1}</strong> and has been skipped.".format(filename,old_key_name), show_copy_button=False, show=True)
else:
counter += 1
self.plugin_keys[new_key_name] = new_key_value
msg = u""
if counter+skipped > 1:
if counter > 0:
- msg += u"Imported <strong>{0:d}</strong> key {1}. ".format(counter, u"file" if counter == 1 else u"files")
+ msg += "Imported <strong>{0:d}</strong> key {1}. ".format(counter, "file" if counter == 1 else "files")
if skipped > 0:
- msg += u"Skipped <strong>{0:d}</strong> key {1}.".format(skipped, u"file" if counter == 1 else u"files")
+ msg += "Skipped <strong>{0:d}</strong> key {1}.".format(skipped, "file" if counter == 1 else "files")
inf = info_dialog(None, "{0} {1}".format(PLUGIN_NAME, PLUGIN_VERSION),
_(msg), show_copy_button=False, show=True)
return counter > 0
def export_key(self):
if not self.listy.currentItem():
- errmsg = u"No keyfile selected to export. Highlight a keyfile first."
+ errmsg = "No keyfile selected to export. Highlight a keyfile first."
r = error_dialog(None, "{0} {1}".format(PLUGIN_NAME, PLUGIN_VERSION),
_(errmsg), show=True, show_copy_button=False)
return
keyname = self.listy.currentItem().text()
- unique_dlg_name = PLUGIN_NAME + u"export {0} keys".format(self.key_type_name).replace(' ', '_') #takes care of automatically remembering last directory
- caption = u"Save {0} File as...".format(self.key_type_name)
- filters = [(u"{0} Files".format(self.key_type_name), [u"{0}".format(self.keyfile_ext)])]
- defaultname = u"{0}.{1}".format(keyname, self.keyfile_ext)
+ unique_dlg_name = PLUGIN_NAME + "export {0} keys".format(self.key_type_name).replace(' ', '_') #takes care of automatically remembering last directory
+ caption = "Save {0} File as...".format(self.key_type_name)
+ filters = [("{0} Files".format(self.key_type_name), ["{0}".format(self.keyfile_ext)])]
+ defaultname = "{0}.{1}".format(keyname, self.keyfile_ext)
filename = choose_save_file(self, unique_dlg_name, caption, filters, all_files=False, initial_filename=defaultname)
if filename:
with file(filename, 'wb') as fname:
data_group_box_layout.addWidget(QLabel('New Key Name:', self))
self.key_ledit = QLineEdit(self.parent.listy.currentItem().text(), self)
- self.key_ledit.setToolTip(u"Enter a new name for this existing {0}.".format(parent.key_type_name))
+ self.key_ledit.setToolTip("Enter a new name for this existing {0}.".format(parent.key_type_name))
data_group_box_layout.addWidget(self.key_ledit)
layout.addSpacing(20)
def accept(self):
if not self.key_ledit.text() or self.key_ledit.text().isspace():
- errmsg = u"Key name field cannot be empty!"
+ errmsg = "Key name field cannot be empty!"
return error_dialog(None, "{0} {1}".format(PLUGIN_NAME, PLUGIN_VERSION),
_(errmsg), show=True, show_copy_button=False)
if len(self.key_ledit.text()) < 4:
- errmsg = u"Key name must be at <i>least</i> 4 characters long!"
+ errmsg = "Key name must be at <i>least</i> 4 characters long!"
return error_dialog(None, "{0} {1}".format(PLUGIN_NAME, PLUGIN_VERSION),
_(errmsg), show=True, show_copy_button=False)
if uStrCmp(self.key_ledit.text(), self.parent.listy.currentItem().text()):
for k in self.parent.plugin_keys.keys():
if (uStrCmp(self.key_ledit.text(), k, True) and
not uStrCmp(k, self.parent.listy.currentItem().text(), True)):
- errmsg = u"The key name <strong>{0}</strong> is already being used.".format(self.key_ledit.text())
+ errmsg = "The key name <strong>{0}</strong> is already being used.".format(self.key_ledit.text())
return error_dialog(None, "{0} {1}".format(PLUGIN_NAME, PLUGIN_VERSION),
_(errmsg), show=True, show_copy_button=False)
QDialog.accept(self)
def __init__(self, parent=None,):
QDialog.__init__(self, parent)
self.parent = parent
- self.setWindowTitle(u"{0} {1}: Create New Barnes & Noble Key".format(PLUGIN_NAME, PLUGIN_VERSION))
+ self.setWindowTitle("{0} {1}: Create New Barnes & Noble Key".format(PLUGIN_NAME, PLUGIN_VERSION))
layout = QVBoxLayout(self)
self.setLayout(layout)
key_group = QHBoxLayout()
data_group_box_layout.addLayout(key_group)
- key_group.addWidget(QLabel(u"Unique Key Name:", self))
+ key_group.addWidget(QLabel("Unique Key Name:", self))
self.key_ledit = QLineEdit("", self)
- self.key_ledit.setToolTip(_(u"<p>Enter an identifying name for this new key.</p>" +
- u"<p>It should be something that will help you remember " +
- u"what personal information was used to create it."))
+ self.key_ledit.setToolTip(_("<p>Enter an identifying name for this new key.</p>" +
+ "<p>It should be something that will help you remember " +
+ "what personal information was used to create it."))
key_group.addWidget(self.key_ledit)
name_group = QHBoxLayout()
data_group_box_layout.addLayout(name_group)
- name_group.addWidget(QLabel(u"B&N/nook account email address:", self))
+ name_group.addWidget(QLabel("B&N/nook account email address:", self))
self.name_ledit = QLineEdit(u"", self)
- self.name_ledit.setToolTip(_(u"<p>Enter your email address as it appears in your B&N " +
- u"account.</p>" +
- u"<p>It will only be used to generate this " +
- u"key and won\'t be stored anywhere " +
- u"in calibre or on your computer.</p>" +
- u"<p>eg: apprenticeharper@gmail.com</p>"))
+ self.name_ledit.setToolTip(_("<p>Enter your email address as it appears in your B&N " +
+ "account.</p>" +
+ "<p>It will only be used to generate this " +
+ "key and won\'t be stored anywhere " +
+ "in calibre or on your computer.</p>" +
+ "<p>eg: apprenticeharper@gmail.com</p>"))
name_group.addWidget(self.name_ledit)
- name_disclaimer_label = QLabel(_(u"(Will not be saved in configuration data)"), self)
+ name_disclaimer_label = QLabel(_("(Will not be saved in configuration data)"), self)
name_disclaimer_label.setAlignment(Qt.AlignHCenter)
data_group_box_layout.addWidget(name_disclaimer_label)
ccn_group = QHBoxLayout()
data_group_box_layout.addLayout(ccn_group)
- ccn_group.addWidget(QLabel(u"B&N/nook account password:", self))
+ ccn_group.addWidget(QLabel("B&N/nook account password:", self))
self.cc_ledit = QLineEdit(u"", self)
- self.cc_ledit.setToolTip(_(u"<p>Enter the password " +
- u"for your B&N account.</p>" +
- u"<p>The password will only be used to generate this " +
- u"key and won\'t be stored anywhere in " +
- u"calibre or on your computer."))
+ self.cc_ledit.setToolTip(_("<p>Enter the password " +
+ "for your B&N account.</p>" +
+ "<p>The password will only be used to generate this " +
+ "key and won\'t be stored anywhere in " +
+ "calibre or on your computer."))
ccn_group.addWidget(self.cc_ledit)
ccn_disclaimer_label = QLabel(_('(Will not be saved in configuration data)'), self)
ccn_disclaimer_label.setAlignment(Qt.AlignHCenter)
key_group = QHBoxLayout()
data_group_box_layout.addLayout(key_group)
- key_group.addWidget(QLabel(u"Retrieved key:", self))
+ key_group.addWidget(QLabel("Retrieved key:", self))
self.key_display = QLabel(u"", self)
- self.key_display.setToolTip(_(u"Click the Retrieve Key button to fetch your B&N encryption key from the B&N servers"))
+ self.key_display.setToolTip(_("Click the Retrieve Key button to fetch your B&N encryption key from the B&N servers"))
key_group.addWidget(self.key_display)
self.retrieve_button = QtGui.QPushButton(self)
- self.retrieve_button.setToolTip(_(u"Click to retrieve your B&N encryption key from the B&N servers"))
- self.retrieve_button.setText(u"Retrieve Key")
+ self.retrieve_button.setToolTip(_("Click to retrieve your B&N encryption key from the B&N servers"))
+ self.retrieve_button.setText("Retrieve Key")
self.retrieve_button.clicked.connect(self.retrieve_key)
key_group.addWidget(self.retrieve_button)
layout.addSpacing(10)
from calibre_plugins.dedrm.ignoblekeyfetch import fetch_key as fetch_bandn_key
fetched_key = fetch_bandn_key(self.user_name,self.cc_number)
if fetched_key == "":
- errmsg = u"Could not retrieve key. Check username, password and intenet connectivity and try again."
+ errmsg = "Could not retrieve key. Check username, password and intenet connectivity and try again."
error_dialog(None, "{0} {1}".format(PLUGIN_NAME, PLUGIN_VERSION), errmsg, show=True, show_copy_button=False)
else:
self.key_display.setText(fetched_key)
def accept(self):
if len(self.key_name) == 0 or len(self.user_name) == 0 or len(self.cc_number) == 0 or self.key_name.isspace() or self.user_name.isspace() or self.cc_number.isspace():
- errmsg = u"All fields are required!"
+ errmsg = "All fields are required!"
return error_dialog(None, "{0} {1}".format(PLUGIN_NAME, PLUGIN_VERSION), errmsg, show=True, show_copy_button=False)
if len(self.key_name) < 4:
- errmsg = u"Key name must be at <i>least</i> 4 characters long!"
+ errmsg = "Key name must be at <i>least</i> 4 characters long!"
return error_dialog(None, "{0} {1}".format(PLUGIN_NAME, PLUGIN_VERSION), errmsg, show=True, show_copy_button=False)
if len(self.key_value) == 0:
self.retrieve_key()
def __init__(self, parent=None,):
QDialog.__init__(self, parent)
self.parent = parent
- self.setWindowTitle(u"{0} {1}: Create New eReader Key".format(PLUGIN_NAME, PLUGIN_VERSION))
+ self.setWindowTitle("{0} {1}: Create New eReader Key".format(PLUGIN_NAME, PLUGIN_VERSION))
layout = QVBoxLayout(self)
self.setLayout(layout)
key_group = QHBoxLayout()
data_group_box_layout.addLayout(key_group)
- key_group.addWidget(QLabel(u"Unique Key Name:", self))
+ key_group.addWidget(QLabel("Unique Key Name:", self))
self.key_ledit = QLineEdit("", self)
- self.key_ledit.setToolTip(u"<p>Enter an identifying name for this new key.\nIt should be something that will help you remember what personal information was used to create it.")
+ self.key_ledit.setToolTip("<p>Enter an identifying name for this new key.\nIt should be something that will help you remember what personal information was used to create it.")
key_group.addWidget(self.key_ledit)
name_group = QHBoxLayout()
data_group_box_layout.addLayout(name_group)
- name_group.addWidget(QLabel(u"Your Name:", self))
+ name_group.addWidget(QLabel("Your Name:", self))
self.name_ledit = QLineEdit(u"", self)
- self.name_ledit.setToolTip(u"Enter the name for this eReader key, usually the name on your credit card.\nIt will only be used to generate this one-time key and won\'t be stored anywhere in calibre or on your computer.\n(ex: Mr Jonathan Q Smith)")
+ self.name_ledit.setToolTip("Enter the name for this eReader key, usually the name on your credit card.\nIt will only be used to generate this one-time key and won\'t be stored anywhere in calibre or on your computer.\n(ex: Mr Jonathan Q Smith)")
name_group.addWidget(self.name_ledit)
- name_disclaimer_label = QLabel(_(u"(Will not be saved in configuration data)"), self)
+ name_disclaimer_label = QLabel(_("(Will not be saved in configuration data)"), self)
name_disclaimer_label.setAlignment(Qt.AlignHCenter)
data_group_box_layout.addWidget(name_disclaimer_label)
ccn_group = QHBoxLayout()
data_group_box_layout.addLayout(ccn_group)
- ccn_group.addWidget(QLabel(u"Credit Card#:", self))
+ ccn_group.addWidget(QLabel("Credit Card#:", self))
self.cc_ledit = QLineEdit(u"", self)
- self.cc_ledit.setToolTip(u"<p>Enter the last 8 digits of credit card number for this eReader key.\nThey will only be used to generate this one-time key and won\'t be stored anywhere in calibre or on your computer.")
+ self.cc_ledit.setToolTip("<p>Enter the last 8 digits of credit card number for this eReader key.\nThey will only be used to generate this one-time key and won\'t be stored anywhere in calibre or on your computer.")
ccn_group.addWidget(self.cc_ledit)
ccn_disclaimer_label = QLabel(_('(Will not be saved in configuration data)'), self)
ccn_disclaimer_label.setAlignment(Qt.AlignHCenter)
def accept(self):
if len(self.key_name) == 0 or len(self.user_name) == 0 or len(self.cc_number) == 0 or self.key_name.isspace() or self.user_name.isspace() or self.cc_number.isspace():
- errmsg = u"All fields are required!"
+ errmsg = "All fields are required!"
return error_dialog(None, "{0} {1}".format(PLUGIN_NAME, PLUGIN_VERSION), errmsg, show=True, show_copy_button=False)
if not self.cc_number.isdigit():
- errmsg = u"Numbers only in the credit card number field!"
+ errmsg = "Numbers only in the credit card number field!"
return error_dialog(None, "{0} {1}".format(PLUGIN_NAME, PLUGIN_VERSION), errmsg, show=True, show_copy_button=False)
if len(self.key_name) < 4:
- errmsg = u"Key name must be at <i>least</i> 4 characters long!"
+ errmsg = "Key name must be at <i>least</i> 4 characters long!"
return error_dialog(None, "{0} {1}".format(PLUGIN_NAME, PLUGIN_VERSION), errmsg, show=True, show_copy_button=False)
QDialog.accept(self)
def __init__(self, parent=None,):
QDialog.__init__(self, parent)
self.parent = parent
- self.setWindowTitle(u"{0} {1}: Getting Default Adobe Digital Editions Key".format(PLUGIN_NAME, PLUGIN_VERSION))
+ self.setWindowTitle("{0} {1}: Getting Default Adobe Digital Editions Key".format(PLUGIN_NAME, PLUGIN_VERSION))
layout = QVBoxLayout(self)
self.setLayout(layout)
else: # linux
from wineutils import WineGetKeys
- scriptpath = os.path.join(parent.parent.alfdir,u"adobekey.py")
- defaultkeys = WineGetKeys(scriptpath, u".der",parent.getwineprefix())
+ scriptpath = os.path.join(parent.parent.alfdir,"adobekey.py")
+ defaultkeys = WineGetKeys(scriptpath, ".der",parent.getwineprefix())
self.default_key = defaultkeys[0]
except:
key_group = QHBoxLayout()
data_group_box_layout.addLayout(key_group)
- key_group.addWidget(QLabel(u"Unique Key Name:", self))
- self.key_ledit = QLineEdit(u"default_key", self)
- self.key_ledit.setToolTip(u"<p>Enter an identifying name for the current default Adobe Digital Editions key.")
+ key_group.addWidget(QLabel("Unique Key Name:", self))
+ self.key_ledit = QLineEdit("default_key", self)
+ self.key_ledit.setToolTip("<p>Enter an identifying name for the current default Adobe Digital Editions key.")
key_group.addWidget(self.key_ledit)
self.button_box.accepted.connect(self.accept)
else:
- default_key_error = QLabel(u"The default encryption key for Adobe Digital Editions could not be found.", self)
+ default_key_error = QLabel("The default encryption key for Adobe Digital Editions could not be found.", self)
default_key_error.setAlignment(Qt.AlignHCenter)
layout.addWidget(default_key_error)
# if no default, bot buttons do the same
def accept(self):
if len(self.key_name) == 0 or self.key_name.isspace():
- errmsg = u"All fields are required!"
+ errmsg = "All fields are required!"
return error_dialog(None, "{0} {1}".format(PLUGIN_NAME, PLUGIN_VERSION), errmsg, show=True, show_copy_button=False)
if len(self.key_name) < 4:
- errmsg = u"Key name must be at <i>least</i> 4 characters long!"
+ errmsg = "Key name must be at <i>least</i> 4 characters long!"
return error_dialog(None, "{0} {1}".format(PLUGIN_NAME, PLUGIN_VERSION), errmsg, show=True, show_copy_button=False)
QDialog.accept(self)
def __init__(self, parent=None,):
QDialog.__init__(self, parent)
self.parent = parent
- self.setWindowTitle(u"{0} {1}: Getting Default Kindle for Mac/PC Key".format(PLUGIN_NAME, PLUGIN_VERSION))
+ self.setWindowTitle("{0} {1}: Getting Default Kindle for Mac/PC Key".format(PLUGIN_NAME, PLUGIN_VERSION))
layout = QVBoxLayout(self)
self.setLayout(layout)
else: # linux
from wineutils import WineGetKeys
- scriptpath = os.path.join(parent.parent.alfdir,u"kindlekey.py")
- defaultkeys = WineGetKeys(scriptpath, u".k4i",parent.getwineprefix())
+ scriptpath = os.path.join(parent.parent.alfdir,"kindlekey.py")
+ defaultkeys = WineGetKeys(scriptpath, ".k4i",parent.getwineprefix())
self.default_key = defaultkeys[0]
except:
key_group = QHBoxLayout()
data_group_box_layout.addLayout(key_group)
- key_group.addWidget(QLabel(u"Unique Key Name:", self))
- self.key_ledit = QLineEdit(u"default_key", self)
- self.key_ledit.setToolTip(u"<p>Enter an identifying name for the current default Kindle for Mac/PC key.")
+ key_group.addWidget(QLabel("Unique Key Name:", self))
+ self.key_ledit = QLineEdit("default_key", self)
+ self.key_ledit.setToolTip("<p>Enter an identifying name for the current default Kindle for Mac/PC key.")
key_group.addWidget(self.key_ledit)
self.button_box.accepted.connect(self.accept)
else:
- default_key_error = QLabel(u"The default encryption key for Kindle for Mac/PC could not be found.", self)
+ default_key_error = QLabel("The default encryption key for Kindle for Mac/PC could not be found.", self)
default_key_error.setAlignment(Qt.AlignHCenter)
layout.addWidget(default_key_error)
def accept(self):
if len(self.key_name) == 0 or self.key_name.isspace():
- errmsg = u"All fields are required!"
+ errmsg = "All fields are required!"
return error_dialog(None, "{0} {1}".format(PLUGIN_NAME, PLUGIN_VERSION), errmsg, show=True, show_copy_button=False)
if len(self.key_name) < 4:
- errmsg = u"Key name must be at <i>least</i> 4 characters long!"
+ errmsg = "Key name must be at <i>least</i> 4 characters long!"
return error_dialog(None, "{0} {1}".format(PLUGIN_NAME, PLUGIN_VERSION), errmsg, show=True, show_copy_button=False)
QDialog.accept(self)
def __init__(self, parent=None,):
QDialog.__init__(self, parent)
self.parent = parent
- self.setWindowTitle(u"{0} {1}: Add New EInk Kindle Serial Number".format(PLUGIN_NAME, PLUGIN_VERSION))
+ self.setWindowTitle("{0} {1}: Add New EInk Kindle Serial Number".format(PLUGIN_NAME, PLUGIN_VERSION))
layout = QVBoxLayout(self)
self.setLayout(layout)
key_group = QHBoxLayout()
data_group_box_layout.addLayout(key_group)
- key_group.addWidget(QLabel(u"EInk Kindle Serial Number:", self))
+ key_group.addWidget(QLabel("EInk Kindle Serial Number:", self))
self.key_ledit = QLineEdit("", self)
- self.key_ledit.setToolTip(u"Enter an eInk Kindle serial number. EInk Kindle serial numbers are 16 characters long and usually start with a 'B' or a '9'. Kindle Serial Numbers are case-sensitive, so be sure to enter the upper and lower case letters unchanged.")
+ self.key_ledit.setToolTip("Enter an eInk Kindle serial number. EInk Kindle serial numbers are 16 characters long and usually start with a 'B' or a '9'. Kindle Serial Numbers are case-sensitive, so be sure to enter the upper and lower case letters unchanged.")
key_group.addWidget(self.key_ledit)
self.button_box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
def accept(self):
if len(self.key_name) == 0 or self.key_name.isspace():
- errmsg = u"Please enter an eInk Kindle Serial Number or click Cancel in the dialog."
+ errmsg = "Please enter an eInk Kindle Serial Number or click Cancel in the dialog."
return error_dialog(None, "{0} {1}".format(PLUGIN_NAME, PLUGIN_VERSION), errmsg, show=True, show_copy_button=False)
if len(self.key_name) != 16:
- errmsg = u"EInk Kindle Serial Numbers must be 16 characters long. This is {0:d} characters long.".format(len(self.key_name))
+ errmsg = "EInk Kindle Serial Numbers must be 16 characters long. This is {0:d} characters long.".format(len(self.key_name))
return error_dialog(None, "{0} {1}".format(PLUGIN_NAME, PLUGIN_VERSION), errmsg, show=True, show_copy_button=False)
QDialog.accept(self)
QDialog.__init__(self, parent)
self.parent = parent
- self.setWindowTitle(u"{0} {1}: Add new Kindle for Android Key".format(PLUGIN_NAME, PLUGIN_VERSION))
+ self.setWindowTitle("{0} {1}: Add new Kindle for Android Key".format(PLUGIN_NAME, PLUGIN_VERSION))
layout = QVBoxLayout(self)
self.setLayout(layout)
self.button_box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
file_group = QHBoxLayout()
data_group_box_layout.addLayout(file_group)
- add_btn = QPushButton(u"Choose Backup File", self)
- add_btn.setToolTip(u"Import Kindle for Android backup file.")
+ add_btn = QPushButton("Choose Backup File", self)
+ add_btn.setToolTip("Import Kindle for Android backup file.")
add_btn.clicked.connect(self.get_android_file)
file_group.addWidget(add_btn)
self.selected_file_name = QLabel(u"",self)
key_group = QHBoxLayout()
data_group_box_layout.addLayout(key_group)
- key_group.addWidget(QLabel(u"Unique Key Name:", self))
+ key_group.addWidget(QLabel("Unique Key Name:", self))
self.key_ledit = QLineEdit(u"", self)
- self.key_ledit.setToolTip(u"<p>Enter an identifying name for the Android for Kindle key.")
+ self.key_ledit.setToolTip("<p>Enter an identifying name for the Android for Kindle key.")
key_group.addWidget(self.key_ledit)
#key_label = QLabel(_(''), self)
#key_label.setAlignment(Qt.AlignHCenter)
return self.serials_from_file
def get_android_file(self):
- unique_dlg_name = PLUGIN_NAME + u"Import Kindle for Android backup file" #takes care of automatically remembering last directory
- caption = u"Select Kindle for Android backup file to add"
- filters = [(u"Kindle for Android backup files", ['db','ab','xml'])]
+ unique_dlg_name = PLUGIN_NAME + "Import Kindle for Android backup file" #takes care of automatically remembering last directory
+ caption = "Select Kindle for Android backup file to add"
+ filters = [("Kindle for Android backup files", ['db','ab','xml'])]
files = choose_files(self, unique_dlg_name, caption, filters, all_files=False)
self.serials_from_file = []
file_name = u""
def accept(self):
if len(self.file_name) == 0 or len(self.key_value) == 0:
- errmsg = u"Please choose a Kindle for Android backup file."
+ errmsg = "Please choose a Kindle for Android backup file."
return error_dialog(None, "{0} {1}".format(PLUGIN_NAME, PLUGIN_VERSION), errmsg, show=True, show_copy_button=False)
if len(self.key_name) == 0 or self.key_name.isspace():
- errmsg = u"Please enter a key name."
+ errmsg = "Please enter a key name."
return error_dialog(None, "{0} {1}".format(PLUGIN_NAME, PLUGIN_VERSION), errmsg, show=True, show_copy_button=False)
if len(self.key_name) < 4:
- errmsg = u"Key name must be at <i>least</i> 4 characters long!"
+ errmsg = "Key name must be at <i>least</i> 4 characters long!"
return error_dialog(None, "{0} {1}".format(PLUGIN_NAME, PLUGIN_VERSION), errmsg, show=True, show_copy_button=False)
QDialog.accept(self)
def __init__(self, parent=None,):
QDialog.__init__(self, parent)
self.parent = parent
- self.setWindowTitle(u"{0} {1}: Add New Mobipocket PID".format(PLUGIN_NAME, PLUGIN_VERSION))
+ self.setWindowTitle("{0} {1}: Add New Mobipocket PID".format(PLUGIN_NAME, PLUGIN_VERSION))
layout = QVBoxLayout(self)
self.setLayout(layout)
key_group = QHBoxLayout()
data_group_box_layout.addLayout(key_group)
- key_group.addWidget(QLabel(u"PID:", self))
+ key_group.addWidget(QLabel("PID:", self))
self.key_ledit = QLineEdit("", self)
- self.key_ledit.setToolTip(u"Enter a Mobipocket PID. Mobipocket PIDs are 8 or 10 characters long. Mobipocket PIDs are case-sensitive, so be sure to enter the upper and lower case letters unchanged.")
+ self.key_ledit.setToolTip("Enter a Mobipocket PID. Mobipocket PIDs are 8 or 10 characters long. Mobipocket PIDs are case-sensitive, so be sure to enter the upper and lower case letters unchanged.")
key_group.addWidget(self.key_ledit)
self.button_box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
def accept(self):
if len(self.key_name) == 0 or self.key_name.isspace():
- errmsg = u"Please enter a Mobipocket PID or click Cancel in the dialog."
+ errmsg = "Please enter a Mobipocket PID or click Cancel in the dialog."
return error_dialog(None, "{0} {1}".format(PLUGIN_NAME, PLUGIN_VERSION), errmsg, show=True, show_copy_button=False)
if len(self.key_name) != 8 and len(self.key_name) != 10:
- errmsg = u"Mobipocket PIDs must be 8 or 10 characters long. This is {0:d} characters long.".format(len(self.key_name))
+ errmsg = "Mobipocket PIDs must be 8 or 10 characters long. This is {0:d} characters long.".format(len(self.key_name))
return error_dialog(None, "{0} {1}".format(PLUGIN_NAME, PLUGIN_VERSION), errmsg, show=True, show_copy_button=False)
QDialog.accept(self)
-#! /usr/bin/python
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
# vim:ts=4:sw=4:softtabstop=4:smarttab:expandtab
+
# For use with Topaz Scripts Version 2.6
-# Added Python 3 compatibility, September 2020
+# Python 3, September 2020
-from __future__ import print_function
class Unbuffered:
def __init__(self, stream):
self.stream = stream
def write(self, data):
- self.stream.write(data)
- self.stream.flush()
+ self.stream.buffer.write(data)
+ self.stream.buffer.flush()
def __getattr__(self, attr):
return getattr(self.stream, attr)
+++ /dev/null
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-
-# base64.py, version 1.0
-# Copyright © 2010 Apprentice Alf
-
-# Released under the terms of the GNU General Public Licence, version 3 or
-# later. <http://www.gnu.org/licenses/>
-
-# Revision history:
-# 1 - Initial release. To allow Applescript to do base64 encoding
-
-"""
-Provide base64 encoding.
-"""
-
-from __future__ import with_statement
-from __future__ import print_function
-
-__license__ = 'GPL v3'
-
-import sys
-import os
-import base64
-
-def usage(progname):
- print("Applies base64 encoding to the supplied file, sending to standard output")
- print("Usage:")
- print(" %s <infile>" % progname)
-
-def cli_main(argv=sys.argv):
- progname = os.path.basename(argv[0])
-
- if len(argv)<2:
- usage(progname)
- sys.exit(2)
-
- keypath = argv[1]
- with open(keypath, 'rb') as f:
- keyder = f.read()
- print(keyder.encode('base64'))
- return 0
-
-
-if __name__ == '__main__':
- sys.exit(cli_main())
-#!/usr/bin/python
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
#
# This is a python script. You need a Python interpreter to run it.
# For example, ActiveState Python, which exists for windows.
# Changelog epubtest
# 1.00 - Cut to epubtest.py, testing ePub files only by Apprentice Alf
# 1.01 - Added routine for use by Windows DeDRM
-# 2.00 - Added Python 3 compatibility, September 2020
+# 2.00 - Python 3, September 2020
#
# Written in 2011 by Paul Durrant
# Released with unlicense. See http://unlicense.org/
# It's still polite to give attribution if you do reuse this code.
#
-from __future__ import with_statement
-from __future__ import print_function
-
__version__ = '2.0'
import sys, struct, os, traceback
xrange(start, argc.value)]
# if we don't have any arguments at all, just pass back script name
# this should never happen
- return [u"epubtest.py"]
+ return ["epubtest.py"]
else:
argvencoding = sys.stdin.encoding
if argvencoding == None:
-#!/usr/bin/env python
+#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# erdr2pml.py
-# Copyright © 2008 The Dark Reverser
+# Copyright © 2008-2020 The Dark Reverser, Apprentice Harper et al.
#
-# Modified 2008–2012 by some_updates, DiapDealer and Apprentice Alf
-
-# This is a python script. You need a Python interpreter to run it.
-# For example, ActiveState Python, which exists for windows.
# Changelog
#
# Based on ereader2html version 0.08 plus some later small fixes
if self.encoding == None:
self.encoding = "utf-8"
def write(self, data):
- if isinstance(data,bytes):
+ if isinstance(data,str):
data = data.encode(self.encoding,"replace")
- self.stream.write(data)
- self.stream.flush()
+ self.stream.buffer.write(data)
+ self.stream.buffer.flush()
def __getattr__(self, attr):
return getattr(self.stream, attr)
range(start, argc.value)]
# if we don't have any arguments at all, just pass back script name
# this should never happen
- return [u"mobidedrm.py"]
+ return ["mobidedrm.py"]
else:
argvencoding = sys.stdin.encoding
if argvencoding == None:
# and with some (heavily edited) code from Paul Durrant's kindlenamer.py
def sanitizeFileName(name):
# substitute filename unfriendly characters
- name = name.replace(u"<",u"[").replace(u">",u"]").replace(u" : ",u" – ").replace(u": ",u" – ").replace(u":",u"—").replace(u"/",u"_").replace(u"\\",u"_").replace(u"|",u"_").replace(u"\"",u"\'")
+ name = name.replace("<","[").replace(">","]").replace(" : "," – ").replace(": "," – ").replace(":","—").replace("/","_").replace("\\","_").replace("|","_").replace("\"","\'")
# delete control characters
- name = u"".join(char for char in name if ord(char)>=32)
+ name = "".join(char for char in name if ord(char)>=32)
# white space to single space, delete leading and trailing while space
- name = re.sub(r"\s", u" ", name).strip()
+ name = re.sub(r"\s", " ", name).strip()
# remove leading dots
- while len(name)>0 and name[0] == u".":
+ while len(name)>0 and name[0] == ".":
name = name[1:]
# remove trailing dots (Windows doesn't like them)
- if name.endswith(u'.'):
+ if name.endswith("."):
name = name[:-1]
return name
# outpath is actually pmlz name
pmlzname = outpath
outdir = tempfile.mkdtemp()
- imagedirpath = os.path.join(outdir,u"images")
+ imagedirpath = os.path.join(outdir,"images")
else:
pmlzname = None
outdir = outpath
- imagedirpath = os.path.join(outdir,bookname + u"_img")
+ imagedirpath = os.path.join(outdir,bookname + "_img")
try:
if not os.path.exists(outdir):
os.makedirs(outdir)
- print(u"Decoding File")
- sect = Sectionizer(infile, 'PNRdPPrs')
+ print("Decoding File")
+ sect =Sectionizer(infile, 'PNRdPPrs')
er = EreaderProcessor(sect, user_key)
if er.getNumImages() > 0:
- print(u"Extracting images")
+ print("Extracting images")
if not os.path.exists(imagedirpath):
os.makedirs(imagedirpath)
for i in range(er.getNumImages()):
name, contents = er.getImage(i)
open(os.path.join(imagedirpath, name), 'wb').write(contents)
- print(u"Extracting pml")
+ print("Extracting pml")
pml_string = er.getText()
pmlfilename = bookname + ".pml"
open(os.path.join(outdir, pmlfilename),'wb').write(cleanPML(pml_string))
if pmlzname is not None:
import zipfile
import shutil
- print(u"Creating PMLZ file {0}".format(os.path.basename(pmlzname)))
+ print("Creating PMLZ file {0}".format(os.path.basename(pmlzname)))
myZipFile = zipfile.ZipFile(pmlzname,'w',zipfile.ZIP_STORED, False)
list = os.listdir(outdir)
for filename in list:
myZipFile.close()
# remove temporary directory
shutil.rmtree(outdir, True)
- print(u"Output is {0}".format(pmlzname))
- else :
- print(u"Output is in {0}".format(outdir))
+ print("Output is {0}".format(pmlzname))
+ else
+ print("Output is in {0}".format(outdir))
print("done")
except ValueError as e:
- print(u"Error: {0}".format(e))
+ print("Error: {0}".format(e))
traceback.print_exc()
return 1
return 0
def usage():
- print(u"Converts DRMed eReader books to PML Source")
- print(u"Usage:")
- print(u" erdr2pml [options] infile.pdb [outpath] \"your name\" credit_card_number")
- print(u" ")
- print(u"Options: ")
- print(u" -h prints this message")
- print(u" -p create PMLZ instead of source folder")
- print(u" --make-pmlz create PMLZ instead of source folder")
- print(u" ")
- print(u"Note:")
- print(u" if outpath is ommitted, creates source in 'infile_Source' folder")
- print(u" if outpath is ommitted and pmlz option, creates PMLZ 'infile.pmlz'")
- print(u" if source folder created, images are in infile_img folder")
- print(u" if pmlz file created, images are in images folder")
- print(u" It's enough to enter the last 8 digits of the credit card number")
+ print("Converts DRMed eReader books to PML Source")
+ print("Usage:")
+ print(" erdr2pml [options] infile.pdb [outpath] \"your name\" credit_card_number")
+ print(" ")
+ print("Options: ")
+ print(" -h prints this message")
+ print(" -p create PMLZ instead of source folder")
+ print(" --make-pmlz create PMLZ instead of source folder")
+ print(" ")
+ print("Note:")
+ print(" if outpath is ommitted, creates source in 'infile_Source' folder")
+ print(" if outpath is ommitted and pmlz option, creates PMLZ 'infile.pmlz'")
+ print(" if source folder created, images are in infile_img folder")
+ print(" if pmlz file created, images are in images folder")
+ print(" It's enough to enter the last 8 digits of the credit card number")
return
def getuser_key(name,cc):
return struct.pack('>LL', binascii.crc32(newname) & 0xffffffff,binascii.crc32(cc[-8:])& 0xffffffff)
def cli_main():
- print(u"eRdr2Pml v{0}. Copyright © 2009–2012 The Dark Reverser et al.".format(__version__))
+ print("eRdr2Pml v{0}. Copyright © 2009–2020 The Dark Reverser et al.".format(__version__))
argv=unicode_argv()
try:
if len(args)==3:
infile, name, cc = args
if make_pmlz:
- outpath = os.path.splitext(infile)[0] + u".pmlz"
+ outpath = os.path.splitext(infile)[0] + ".pmlz"
else:
- outpath = os.path.splitext(infile)[0] + u"_Source"
+ outpath = os.path.splitext(infile)[0] + "_Source"
elif len(args)==4:
infile, outpath, name, cc = args
-#! /usr/bin/python
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
# vim:ts=4:sw=4:softtabstop=4:smarttab:expandtab
-# Added Python 3 compatibility for calibre 5.0
-
-from __future__ import print_function
-from .convert2xml import encodeNumber
+# Python 3 for calibre 5.0
class Unbuffered:
def __init__(self, stream):
self.stream = stream
def write(self, data):
- self.stream.write(data)
- self.stream.flush()
+ self.stream.buffer.write(data)
+ self.stream.buffer.flush()
def __getattr__(self, attr):
return getattr(self.stream, attr)
-#!/usr/bin/env python
+#!/usr/bin/env python3
# -*- coding: utf-8 -*-
-from __future__ import with_statement
-from __future__ import print_function
-
-# ignobleepub.pyw, version 4.1
-# Copyright © 2009-2010 by i♥cabbages
+# ignobleepub.py
+# Copyright © 2009-2020 by i♥cabbages, Apprentice Harper et al.
# Released under the terms of the GNU General Public Licence, version 3
# <http://www.gnu.org/licenses/>
-# Modified 2010–2013 by some_updates, DiapDealer and Apprentice Alf
-# Modified 2015–2017 by Apprentice Harper
-
-# Windows users: Before running this program, you must first install Python 2.6
-# from <http://www.python.org/download/> and PyCrypto from
-# <http://www.voidspace.org.uk/python/modules.shtml#pycrypto> (make sure to
-# install the version for Python 2.6). Save this script file as
-# ineptepub.pyw and double-click on it to run it.
#
-# Mac OS X users: Save this script file as ineptepub.pyw. You can run this
-# program from the command line (pythonw ineptepub.pyw) or by double-clicking
-# it when it has been associated with PythonLauncher.
-
# Revision history:
# 1 - Initial release
# 2 - Added OS X support by using OpenSSL when available
# 3.9 - moved unicode_argv call inside main for Windows DeDRM compatibility
# 4.0 - Work if TkInter is missing
# 4.1 - Import tkFileDialog, don't assume something else will import it.
-# 5.0 - Added Python 3 compatibility for calibre 5.0
+# 5.0 - Python 3 for calibre 5.0
"""
Decrypt Barnes & Noble encrypted ePub books.
if self.encoding == None:
self.encoding = "utf-8"
def write(self, data):
- if isinstance(data,bytes):
+ if isinstance(data,str):
data = data.encode(self.encoding,"replace")
- self.stream.write(data)
- self.stream.flush()
+ self.stream.buffer.write(data)
+ self.stream.buffer.flush()
def __getattr__(self, attr):
return getattr(self.stream, attr)
start = argc.value - len(sys.argv)
return [argv[i] for i in
range(start, argc.value)]
- return [u"ineptepub.py"]
+ return ["ineptepub.py"]
else:
argvencoding = sys.stdin.encoding
if argvencoding == None:
def decryptBook(keyb64, inpath, outpath):
if AES is None:
- raise IGNOBLEError(u"PyCrypto or OpenSSL must be installed.")
+ raise IGNOBLEError("PyCrypto or OpenSSL must be installed.")
key = keyb64.decode('base64')[:16]
aes = AES(key)
with closing(ZipFile(open(inpath, 'rb'))) as inf:
namelist = set(inf.namelist())
if 'META-INF/rights.xml' not in namelist or \
'META-INF/encryption.xml' not in namelist:
- print(u"{0:s} is DRM-free.".format(os.path.basename(inpath)))
+ print("{0:s} is DRM-free.".format(os.path.basename(inpath)))
return 1
for name in META_NAMES:
namelist.remove(name)
expr = './/%s' % (adept('encryptedKey'),)
bookkey = ''.join(rights.findtext(expr))
if len(bookkey) != 64:
- print(u"{0:s} is not a secure Barnes & Noble ePub.".format(os.path.basename(inpath)))
+ print("{0:s} is not a secure Barnes & Noble ePub.".format(os.path.basename(inpath)))
return 1
bookkey = aes.decrypt(bookkey.decode('base64'))
bookkey = bookkey[:-ord(bookkey[-1])]
pass
outf.writestr(zi, decryptor.decrypt(path, data))
except:
- print(u"Could not decrypt {0:s} because of an exception:\n{1:s}".format(os.path.basename(inpath), traceback.format_exc()))
+ print("Could not decrypt {0:s} because of an exception:\n{1:s}".format(os.path.basename(inpath), traceback.format_exc()))
return 2
return 0
argv=unicode_argv()
progname = os.path.basename(argv[0])
if len(argv) != 4:
- print(u"usage: {0} <keyfile.b64> <inbook.epub> <outbook.epub>".format(progname))
+ print("usage: {0} <keyfile.b64> <inbook.epub> <outbook.epub>".format(progname))
return 1
keypath, inpath, outpath = argv[1:]
userkey = open(keypath,'rb').read()
result = decryptBook(userkey, inpath, outpath)
if result == 0:
- print(u"Successfully decrypted {0:s} as {1:s}".format(os.path.basename(inpath),os.path.basename(outpath)))
+ print("Successfully decrypted {0:s} as {1:s}".format(os.path.basename(inpath),os.path.basename(outpath)))
return result
def gui_main():
class DecryptionDialog(Tkinter.Frame):
def __init__(self, root):
Tkinter.Frame.__init__(self, root, border=5)
- self.status = Tkinter.Label(self, text=u"Select files for decryption")
+ self.status = Tkinter.Label(self, text="Select files for decryption")
self.status.pack(fill=Tkconstants.X, expand=1)
body = Tkinter.Frame(self)
body.pack(fill=Tkconstants.X, expand=1)
sticky = Tkconstants.E + Tkconstants.W
body.grid_columnconfigure(1, weight=2)
- Tkinter.Label(body, text=u"Key file").grid(row=0)
+ Tkinter.Label(body, text="Key file").grid(row=0)
self.keypath = Tkinter.Entry(body, width=30)
self.keypath.grid(row=0, column=1, sticky=sticky)
- if os.path.exists(u"bnepubkey.b64"):
- self.keypath.insert(0, u"bnepubkey.b64")
- button = Tkinter.Button(body, text=u"...", command=self.get_keypath)
+ if os.path.exists("bnepubkey.b64"):
+ self.keypath.insert(0, "bnepubkey.b64")
+ button = Tkinter.Button(body, text="...", command=self.get_keypath)
button.grid(row=0, column=2)
- Tkinter.Label(body, text=u"Input file").grid(row=1)
+ Tkinter.Label(body, text="Input file").grid(row=1)
self.inpath = Tkinter.Entry(body, width=30)
self.inpath.grid(row=1, column=1, sticky=sticky)
- button = Tkinter.Button(body, text=u"...", command=self.get_inpath)
+ button = Tkinter.Button(body, text="...", command=self.get_inpath)
button.grid(row=1, column=2)
- Tkinter.Label(body, text=u"Output file").grid(row=2)
+ Tkinter.Label(body, text="Output file").grid(row=2)
self.outpath = Tkinter.Entry(body, width=30)
self.outpath.grid(row=2, column=1, sticky=sticky)
- button = Tkinter.Button(body, text=u"...", command=self.get_outpath)
+ button = Tkinter.Button(body, text="...", command=self.get_outpath)
button.grid(row=2, column=2)
buttons = Tkinter.Frame(self)
buttons.pack()
botton = Tkinter.Button(
- buttons, text=u"Decrypt", width=10, command=self.decrypt)
+ buttons, text="Decrypt", width=10, command=self.decrypt)
botton.pack(side=Tkconstants.LEFT)
Tkinter.Frame(buttons, width=10).pack(side=Tkconstants.LEFT)
button = Tkinter.Button(
- buttons, text=u"Quit", width=10, command=self.quit)
+ buttons, text="Quit", width=10, command=self.quit)
button.pack(side=Tkconstants.RIGHT)
def get_keypath(self):
keypath = tkFileDialog.askopenfilename(
- parent=None, title=u"Select Barnes & Noble \'.b64\' key file",
- defaultextension=u".b64",
+ parent=None, title="Select Barnes & Noble \'.b64\' key file",
+ defaultextension=".b64",
filetypes=[('base64-encoded files', '.b64'),
('All Files', '.*')])
if keypath:
def get_inpath(self):
inpath = tkFileDialog.askopenfilename(
- parent=None, title=u"Select B&N-encrypted ePub file to decrypt",
- defaultextension=u".epub", filetypes=[('ePub files', '.epub')])
+ parent=None, title="Select B&N-encrypted ePub file to decrypt",
+ defaultextension=".epub", filetypes=[('ePub files', '.epub')])
if inpath:
inpath = os.path.normpath(inpath)
self.inpath.delete(0, Tkconstants.END)
def get_outpath(self):
outpath = tkFileDialog.asksaveasfilename(
- parent=None, title=u"Select unencrypted ePub file to produce",
- defaultextension=u".epub", filetypes=[('ePub files', '.epub')])
+ parent=None, title="Select unencrypted ePub file to produce",
+ defaultextension=".epub", filetypes=[('ePub files', '.epub')])
if outpath:
outpath = os.path.normpath(outpath)
self.outpath.delete(0, Tkconstants.END)
inpath = self.inpath.get()
outpath = self.outpath.get()
if not keypath or not os.path.exists(keypath):
- self.status['text'] = u"Specified key file does not exist"
+ self.status['text'] = "Specified key file does not exist"
return
if not inpath or not os.path.exists(inpath):
- self.status['text'] = u"Specified input file does not exist"
+ self.status['text'] = "Specified input file does not exist"
return
if not outpath:
- self.status['text'] = u"Output file not specified"
+ self.status['text'] = "Output file not specified"
return
if inpath == outpath:
- self.status['text'] = u"Must have different input and output files"
+ self.status['text'] = "Must have different input and output files"
return
userkey = open(keypath,'rb').read()
- self.status['text'] = u"Decrypting..."
+ self.status['text'] = "Decrypting..."
try:
decrypt_status = decryptBook(userkey, inpath, outpath)
except Exception as e:
- self.status['text'] = u"Error: {0}".format(e.args[0])
+ self.status['text'] = "Error: {0}".format(e.args[0])
return
if decrypt_status == 0:
- self.status['text'] = u"File successfully decrypted"
+ self.status['text'] = "File successfully decrypted"
else:
- self.status['text'] = u"The was an error decrypting the file."
+ self.status['text'] = "The was an error decrypting the file."
root = Tkinter.Tk()
- root.title(u"Barnes & Noble ePub Decrypter v.{0}".format(__version__))
+ root.title("Barnes & Noble ePub Decrypter v.{0}".format(__version__))
root.resizable(True, False)
root.minsize(300, 0)
DecryptionDialog(root).pack(fill=Tkconstants.X, expand=1)
-#!/usr/bin/env python
+#!/usr/bin/env python3
# -*- coding: utf-8 -*-
-from __future__ import with_statement
-from __future__ import print_function
-
# ignoblekey.py
# Copyright © 2015-2020 Apprentice Alf, Apprentice Harper et al.
# Revision history:
# 1.0 - Initial release
# 1.1 - remove duplicates and return last key as single key
-# 2.0 - Added Python 3 compatibility for calibre 5.0
+# 2.0 - Python 3 for calibre 5.0
"""
Get Barnes & Noble EPUB user key from nook Studio log file
if self.encoding == None:
self.encoding = "utf-8"
def write(self, data):
- if isinstance(data,bytes):
+ if isinstance(data,str):
data = data.encode(self.encoding,"replace")
- self.stream.write(data)
- self.stream.flush()
+ self.stream.buffer.write(data)
+ self.stream.buffer.flush()
def __getattr__(self, attr):
return getattr(self.stream, attr)
range(start, argc.value)]
# if we don't have any arguments at all, just pass back script name
# this should never happen
- return [u"ignoblekey.py"]
+ return ["ignoblekey.py"]
else:
argvencoding = sys.stdin.encoding
if argvencoding == None:
paths = set()
if 'LOCALAPPDATA' in os.environ.keys():
# Python 2.x does not return unicode env. Use Python 3.x
- path = winreg.ExpandEnvironmentStrings(u"%LOCALAPPDATA%")
+ path = winreg.ExpandEnvironmentStrings("%LOCALAPPDATA%")
if os.path.isdir(path):
paths.add(path)
if 'USERPROFILE' in os.environ.keys():
# Python 2.x does not return unicode env. Use Python 3.x
- path = winreg.ExpandEnvironmentStrings(u"%USERPROFILE%")+u"\\AppData\\Local"
+ path = winreg.ExpandEnvironmentStrings("%USERPROFILE%")+"\\AppData\\Local"
if os.path.isdir(path):
paths.add(path)
- path = winreg.ExpandEnvironmentStrings(u"%USERPROFILE%")+u"\\AppData\\Roaming"
+ path = winreg.ExpandEnvironmentStrings("%USERPROFILE%")+"\\AppData\\Roaming"
if os.path.isdir(path):
paths.add(path)
# User Shell Folders show take precedent over Shell Folders if present
for file in files:
fileKeys = getKeysFromLog(file)
if fileKeys:
- print(u"Found {0} keys in the Nook Study log files".format(len(fileKeys)))
+ print("Found {0} keys in the Nook Study log files".format(len(fileKeys)))
keys.extend(fileKeys)
return list(set(keys))
outfile = outpath
with open(outfile, 'w') as keyfileout:
keyfileout.write(keys[-1])
- print(u"Saved a key to {0}".format(outfile))
+ print("Saved a key to {0}".format(outfile))
else:
keycount = 0
for key in keys:
while True:
keycount += 1
- outfile = os.path.join(outpath,u"nookkey{0:d}.b64".format(keycount))
+ outfile = os.path.join(outpath,"nookkey{0:d}.b64".format(keycount))
if not os.path.exists(outfile):
break
with open(outfile, 'w') as keyfileout:
keyfileout.write(key)
- print(u"Saved a key to {0}".format(outfile))
+ print("Saved a key to {0}".format(outfile))
return True
return False
def usage(progname):
- print(u"Finds the nook Study encryption keys.")
- print(u"Keys are saved to the current directory, or a specified output directory.")
- print(u"If a file name is passed instead of a directory, only the first key is saved, in that file.")
- print(u"Usage:")
- print(u" {0:s} [-h] [-k <logFile>] [<outpath>]".format(progname))
+ print("Finds the nook Study encryption keys.")
+ print("Keys are saved to the current directory, or a specified output directory.")
+ print("If a file name is passed instead of a directory, only the first key is saved, in that file.")
+ print("Usage:")
+ print(" {0:s} [-h] [-k <logFile>] [<outpath>]".format(progname))
def cli_main():
sys.stderr=SafeUnbuffered(sys.stderr)
argv=unicode_argv()
progname = os.path.basename(argv[0])
- print(u"{0} v{1}\nCopyright © 2015 Apprentice Alf".format(progname,__version__))
+ print("{0} v{1}\nCopyright © 2015 Apprentice Alf".format(progname,__version__))
try:
opts, args = getopt.getopt(argv[1:], "hk:")
except getopt.GetoptError as err:
- print(u"Error in options or arguments: {0}".format(err.args[0]))
+ print("Error in options or arguments: {0}".format(err.args[0]))
usage(progname)
sys.exit(2)
outpath = os.path.realpath(os.path.normpath(outpath))
if not getkey(outpath, files):
- print(u"Could not retrieve nook Study key.")
+ print("Could not retrieve nook Study key.")
return 0
class ExceptionDialog(Tkinter.Frame):
def __init__(self, root, text):
Tkinter.Frame.__init__(self, root, border=5)
- label = Tkinter.Label(self, text=u"Unexpected error:",
+ label = Tkinter.Label(self, text="Unexpected error:",
anchor=Tkconstants.W, justify=Tkconstants.LEFT)
label.pack(fill=Tkconstants.X, expand=0)
self.text = Tkinter.Text(self)
print(key)
while True:
keycount += 1
- outfile = os.path.join(progpath,u"nookkey{0:d}.b64".format(keycount))
+ outfile = os.path.join(progpath,"nookkey{0:d}.b64".format(keycount))
if not os.path.exists(outfile):
break
with open(outfile, 'w') as keyfileout:
keyfileout.write(key)
success = True
- tkMessageBox.showinfo(progname, u"Key successfully retrieved to {0}".format(outfile))
+ tkMessageBox.showinfo(progname, "Key successfully retrieved to {0}".format(outfile))
except DrmException as e:
- tkMessageBox.showerror(progname, u"Error: {0}".format(str(e)))
+ tkMessageBox.showerror(progname, "Error: {0}".format(str(e)))
except Exception:
root.wm_state('normal')
root.title(progname)
-#!/usr/bin/env python
+#!/usr/bin/env python3
# -*- coding: utf-8 -*-
-from __future__ import with_statement
-from __future__ import print_function
-
-# ignoblekeyfetch.pyw, version 2.0
+# ignoblekeyfetch.py
# Copyright © 2015-2020 Apprentice Harper et al.
# Released under the terms of the GNU General Public Licence, version 3
# Revision history:
# 1.0 - Initial version
# 1.1 - Try second URL if first one fails
-# 2.0 - Added Python 3 compatibility for calibre 5.0
+# 2.0 - Python 3 for calibre 5.0
"""
Fetch Barnes & Noble EPUB user key from B&N servers using email and password
"""
__license__ = 'GPL v3'
-__version__ = "1.1"
+__version__ = "2.0"
import sys
import os
range(start, argc.value)]
# if we don't have any arguments at all, just pass back script name
# this should never happen
- return [u"ignoblekeyfetch.py"]
+ return ["ignoblekeyfetch.py"]
else:
argvencoding = sys.stdin.encoding
if argvencoding == None:
argv=unicode_argv()
progname = os.path.basename(argv[0])
if len(argv) != 4:
- print(u"usage: {0} <email> <password> <keyfileout.b64>".format(progname))
+ print("usage: {0} <email> <password> <keyfileout.b64>".format(progname))
return 1
email, password, keypath = argv[1:]
userkey = fetch_key(email, password)
if len(userkey) == 28:
open(keypath,'wb').write(userkey)
return 0
- print(u"Failed to fetch key.")
+ print("Failed to fetch key.")
return 1
class DecryptionDialog(Tkinter.Frame):
def __init__(self, root):
Tkinter.Frame.__init__(self, root, border=5)
- self.status = Tkinter.Label(self, text=u"Enter parameters")
+ self.status = Tkinter.Label(self, text="Enter parameters")
self.status.pack(fill=Tkconstants.X, expand=1)
body = Tkinter.Frame(self)
body.pack(fill=Tkconstants.X, expand=1)
sticky = Tkconstants.E + Tkconstants.W
body.grid_columnconfigure(1, weight=2)
- Tkinter.Label(body, text=u"Account email address").grid(row=0)
+ Tkinter.Label(body, text="Account email address").grid(row=0)
self.name = Tkinter.Entry(body, width=40)
self.name.grid(row=0, column=1, sticky=sticky)
- Tkinter.Label(body, text=u"Account password").grid(row=1)
+ Tkinter.Label(body, text="Account password").grid(row=1)
self.ccn = Tkinter.Entry(body, width=40)
self.ccn.grid(row=1, column=1, sticky=sticky)
- Tkinter.Label(body, text=u"Output file").grid(row=2)
+ Tkinter.Label(body, text="Output file").grid(row=2)
self.keypath = Tkinter.Entry(body, width=40)
self.keypath.grid(row=2, column=1, sticky=sticky)
- self.keypath.insert(2, u"bnepubkey.b64")
- button = Tkinter.Button(body, text=u"...", command=self.get_keypath)
+ self.keypath.insert(2, "bnepubkey.b64")
+ button = Tkinter.Button(body, text="...", command=self.get_keypath)
button.grid(row=2, column=2)
buttons = Tkinter.Frame(self)
buttons.pack()
botton = Tkinter.Button(
- buttons, text=u"Fetch", width=10, command=self.generate)
+ buttons, text="Fetch", width=10, command=self.generate)
botton.pack(side=Tkconstants.LEFT)
Tkinter.Frame(buttons, width=10).pack(side=Tkconstants.LEFT)
button = Tkinter.Button(
- buttons, text=u"Quit", width=10, command=self.quit)
+ buttons, text="Quit", width=10, command=self.quit)
button.pack(side=Tkconstants.RIGHT)
def get_keypath(self):
keypath = tkFileDialog.asksaveasfilename(
- parent=None, title=u"Select B&N ePub key file to produce",
- defaultextension=u".b64",
+ parent=None, title="Select B&N ePub key file to produce",
+ defaultextension=".b64",
filetypes=[('base64-encoded files', '.b64'),
('All Files', '.*')])
if keypath:
password = self.ccn.get()
keypath = self.keypath.get()
if not email:
- self.status['text'] = u"Email address not given"
+ self.status['text'] = "Email address not given"
return
if not password:
- self.status['text'] = u"Account password not given"
+ self.status['text'] = "Account password not given"
return
if not keypath:
- self.status['text'] = u"Output keyfile path not set"
+ self.status['text'] = "Output keyfile path not set"
return
- self.status['text'] = u"Fetching..."
+ self.status['text'] = "Fetching..."
try:
userkey = fetch_key(email, password)
except Exception as e:
- self.status['text'] = u"Error: {0}".format(e.args[0])
+ self.status['text'] = "Error: {0}".format(e.args[0])
return
if len(userkey) == 28:
open(keypath,'wb').write(userkey)
- self.status['text'] = u"Keyfile fetched successfully"
+ self.status['text'] = "Keyfile fetched successfully"
else:
- self.status['text'] = u"Keyfile fetch failed."
+ self.status['text'] = "Keyfile fetch failed."
root = Tkinter.Tk()
- root.title(u"Barnes & Noble ePub Keyfile Fetch v.{0}".format(__version__))
+ root.title("Barnes & Noble ePub Keyfile Fetch v.{0}".format(__version__))
root.resizable(True, False)
root.minsize(300, 0)
DecryptionDialog(root).pack(fill=Tkconstants.X, expand=1)
-#!/usr/bin/env python
+#!/usr/bin/env python3
# -*- coding: utf-8 -*-
-from __future__ import with_statement
-from __future__ import print_function
-
-# ignoblekeygen.pyw
+# ignoblekeygen.py
# Copyright © 2009-2020 i♥cabbages, Apprentice Harper et al.
# Released under the terms of the GNU General Public Licence, version 3
range(start, argc.value)]
# if we don't have any arguments at all, just pass back script name
# this should never happen
- return [u"ignoblekeygen.py"]
+ return ["ignoblekeygen.py"]
else:
argvencoding = sys.stdin.encoding
if argvencoding == None:
(progname,))
return 1
if len(argv) != 4:
- print(u"usage: {0} <Name> <CC#> <keyfileout.b64>".format(progname))
+ print("usage: {0} <Name> <CC#> <keyfileout.b64>".format(progname))
return 1
name, ccn, keypath = argv[1:]
userkey = generate_key(name, ccn)
class DecryptionDialog(Tkinter.Frame):
def __init__(self, root):
Tkinter.Frame.__init__(self, root, border=5)
- self.status = Tkinter.Label(self, text=u"Enter parameters")
+ self.status = Tkinter.Label(self, text="Enter parameters")
self.status.pack(fill=Tkconstants.X, expand=1)
body = Tkinter.Frame(self)
body.pack(fill=Tkconstants.X, expand=1)
sticky = Tkconstants.E + Tkconstants.W
body.grid_columnconfigure(1, weight=2)
- Tkinter.Label(body, text=u"Account Name").grid(row=0)
+ Tkinter.Label(body, text="Account Name").grid(row=0)
self.name = Tkinter.Entry(body, width=40)
self.name.grid(row=0, column=1, sticky=sticky)
- Tkinter.Label(body, text=u"CC#").grid(row=1)
+ Tkinter.Label(body, text="CC#").grid(row=1)
self.ccn = Tkinter.Entry(body, width=40)
self.ccn.grid(row=1, column=1, sticky=sticky)
- Tkinter.Label(body, text=u"Output file").grid(row=2)
+ Tkinter.Label(body, text="Output file").grid(row=2)
self.keypath = Tkinter.Entry(body, width=40)
self.keypath.grid(row=2, column=1, sticky=sticky)
- self.keypath.insert(2, u"bnepubkey.b64")
- button = Tkinter.Button(body, text=u"...", command=self.get_keypath)
+ self.keypath.insert(2, "bnepubkey.b64")
+ button = Tkinter.Button(body, text="...", command=self.get_keypath)
button.grid(row=2, column=2)
buttons = Tkinter.Frame(self)
buttons.pack()
botton = Tkinter.Button(
- buttons, text=u"Generate", width=10, command=self.generate)
+ buttons, text="Generate", width=10, command=self.generate)
botton.pack(side=Tkconstants.LEFT)
Tkinter.Frame(buttons, width=10).pack(side=Tkconstants.LEFT)
button = Tkinter.Button(
- buttons, text=u"Quit", width=10, command=self.quit)
+ buttons, text="Quit", width=10, command=self.quit)
button.pack(side=Tkconstants.RIGHT)
def get_keypath(self):
keypath = tkFileDialog.asksaveasfilename(
- parent=None, title=u"Select B&N ePub key file to produce",
- defaultextension=u".b64",
+ parent=None, title="Select B&N ePub key file to produce",
+ defaultextension=".b64",
filetypes=[('base64-encoded files', '.b64'),
('All Files', '.*')])
if keypath:
ccn = self.ccn.get()
keypath = self.keypath.get()
if not name:
- self.status['text'] = u"Name not specified"
+ self.status['text'] = "Name not specified"
return
if not ccn:
- self.status['text'] = u"Credit card number not specified"
+ self.status['text'] = "Credit card number not specified"
return
if not keypath:
- self.status['text'] = u"Output keyfile path not specified"
+ self.status['text'] = "Output keyfile path not specified"
return
- self.status['text'] = u"Generating..."
+ self.status['text'] = "Generating..."
try:
userkey = generate_key(name, ccn)
except Exception as e:
- self.status['text'] = u"Error: (0}".format(e.args[0])
+ self.status['text'] = "Error: (0}".format(e.args[0])
return
open(keypath,'wb').write(userkey)
- self.status['text'] = u"Keyfile successfully generated"
+ self.status['text'] = "Keyfile successfully generated"
root = Tkinter.Tk()
if AES is None:
"This script requires OpenSSL or PyCrypto, which must be installed "
"separately. Read the top-of-script comment for details.")
return 1
- root.title(u"Barnes & Noble ePub Keyfile Generator v.{0}".format(__version__))
+ root.title("Barnes & Noble ePub Keyfile Generator v.{0}".format(__version__))
root.resizable(True, False)
root.minsize(300, 0)
DecryptionDialog(root).pack(fill=Tkconstants.X, expand=1)
-#! /usr/bin/python
+#!/usr/bin/env python3
# -*- coding: utf-8 -*-
-from __future__ import with_statement
# ignoblepdf.py
# Copyright © 2009-2020 by Apprentice Harper et al.
# Revision history:
# 0.1 - Initial alpha testing release 2020 by Pu D. Pud
+# 0.2 - Python 3 for calibre 5.0 (in testing)
"""
start = argc.value - len(sys.argv)
return [argv[i] for i in
xrange(start, argc.value)]
- return [u"ignoblepdf.py"]
+ return ["ignoblepdf.py"]
else:
argvencoding = sys.stdin.encoding
if argvencoding == None:
def decryptBook(userkey, inpath, outpath):
if AES is None:
- raise IGNOBLEError(u"PyCrypto or OpenSSL must be installed.")
+ raise IGNOBLEError("PyCrypto or OpenSSL must be installed.")
with open(inpath, 'rb') as inf:
#try:
serializer = PDFSerializer(inf, userkey)
#except:
- # print u"Error serializing pdf {0}. Probably wrong key.".format(os.path.basename(inpath))
+ # print "Error serializing pdf {0}. Probably wrong key.".format(os.path.basename(inpath))
# return 2
# hope this will fix the 'bad file descriptor' problem
with open(outpath, 'wb') as outf:
try:
serializer.dump(outf)
except Exception, e:
- print u"error writing pdf: {0}".format(e.args[0])
+ print "error writing pdf: {0}".format(e.args[0])
return 2
return 0
argv=unicode_argv()
progname = os.path.basename(argv[0])
if len(argv) != 4:
- print u"usage: {0} <keyfile.b64> <inbook.pdf> <outbook.pdf>".format(progname)
+ print "usage: {0} <keyfile.b64> <inbook.pdf> <outbook.pdf>".format(progname)
return 1
keypath, inpath, outpath = argv[1:]
userkey = open(keypath,'rb').read()
result = decryptBook(userkey, inpath, outpath)
if result == 0:
- print u"Successfully decrypted {0:s} as {1:s}".format(os.path.basename(inpath),os.path.basename(outpath))
+ print "Successfully decrypted {0:s} as {1:s}".format(os.path.basename(inpath),os.path.basename(outpath))
return result
class DecryptionDialog(Tkinter.Frame):
def __init__(self, root):
Tkinter.Frame.__init__(self, root, border=5)
- self.status = Tkinter.Label(self, text=u"Select files for decryption")
+ self.status = Tkinter.Label(self, text="Select files for decryption")
self.status.pack(fill=Tkconstants.X, expand=1)
body = Tkinter.Frame(self)
body.pack(fill=Tkconstants.X, expand=1)
sticky = Tkconstants.E + Tkconstants.W
body.grid_columnconfigure(1, weight=2)
- Tkinter.Label(body, text=u"Key file").grid(row=0)
+ Tkinter.Label(body, text="Key file").grid(row=0)
self.keypath = Tkinter.Entry(body, width=30)
self.keypath.grid(row=0, column=1, sticky=sticky)
- if os.path.exists(u"bnpdfkey.b64"):
- self.keypath.insert(0, u"bnpdfkey.b64")
- button = Tkinter.Button(body, text=u"...", command=self.get_keypath)
+ if os.path.exists("bnpdfkey.b64"):
+ self.keypath.insert(0, "bnpdfkey.b64")
+ button = Tkinter.Button(body, text="...", command=self.get_keypath)
button.grid(row=0, column=2)
- Tkinter.Label(body, text=u"Input file").grid(row=1)
+ Tkinter.Label(body, text="Input file").grid(row=1)
self.inpath = Tkinter.Entry(body, width=30)
self.inpath.grid(row=1, column=1, sticky=sticky)
- button = Tkinter.Button(body, text=u"...", command=self.get_inpath)
+ button = Tkinter.Button(body, text="...", command=self.get_inpath)
button.grid(row=1, column=2)
- Tkinter.Label(body, text=u"Output file").grid(row=2)
+ Tkinter.Label(body, text="Output file").grid(row=2)
self.outpath = Tkinter.Entry(body, width=30)
self.outpath.grid(row=2, column=1, sticky=sticky)
- button = Tkinter.Button(body, text=u"...", command=self.get_outpath)
+ button = Tkinter.Button(body, text="...", command=self.get_outpath)
button.grid(row=2, column=2)
buttons = Tkinter.Frame(self)
buttons.pack()
botton = Tkinter.Button(
- buttons, text=u"Decrypt", width=10, command=self.decrypt)
+ buttons, text="Decrypt", width=10, command=self.decrypt)
botton.pack(side=Tkconstants.LEFT)
Tkinter.Frame(buttons, width=10).pack(side=Tkconstants.LEFT)
button = Tkinter.Button(
- buttons, text=u"Quit", width=10, command=self.quit)
+ buttons, text="Quit", width=10, command=self.quit)
button.pack(side=Tkconstants.RIGHT)
def get_keypath(self):
keypath = tkFileDialog.askopenfilename(
- parent=None, title=u"Select Barnes & Noble \'.b64\' key file",
- defaultextension=u".b64",
+ parent=None, title="Select Barnes & Noble \'.b64\' key file",
+ defaultextension=".b64",
filetypes=[('base64-encoded files', '.b64'),
('All Files', '.*')])
if keypath:
def get_inpath(self):
inpath = tkFileDialog.askopenfilename(
- parent=None, title=u"Select B&N-encrypted PDF file to decrypt",
- defaultextension=u".pdf", filetypes=[('PDF files', '.pdf')])
+ parent=None, title="Select B&N-encrypted PDF file to decrypt",
+ defaultextension=".pdf", filetypes=[('PDF files', '.pdf')])
if inpath:
inpath = os.path.normpath(inpath)
self.inpath.delete(0, Tkconstants.END)
def get_outpath(self):
outpath = tkFileDialog.asksaveasfilename(
- parent=None, title=u"Select unencrypted PDF file to produce",
- defaultextension=u".pdf", filetypes=[('PDF files', '.pdf')])
+ parent=None, title="Select unencrypted PDF file to produce",
+ defaultextension=".pdf", filetypes=[('PDF files', '.pdf')])
if outpath:
outpath = os.path.normpath(outpath)
self.outpath.delete(0, Tkconstants.END)
inpath = self.inpath.get()
outpath = self.outpath.get()
if not keypath or not os.path.exists(keypath):
- self.status['text'] = u"Specified key file does not exist"
+ self.status['text'] = "Specified key file does not exist"
return
if not inpath or not os.path.exists(inpath):
- self.status['text'] = u"Specified input file does not exist"
+ self.status['text'] = "Specified input file does not exist"
return
if not outpath:
- self.status['text'] = u"Output file not specified"
+ self.status['text'] = "Output file not specified"
return
if inpath == outpath:
- self.status['text'] = u"Must have different input and output files"
+ self.status['text'] = "Must have different input and output files"
return
userkey = open(keypath,'rb').read()
- self.status['text'] = u"Decrypting..."
+ self.status['text'] = "Decrypting..."
try:
decrypt_status = decryptBook(userkey, inpath, outpath)
except Exception, e:
- self.status['text'] = u"Error; {0}".format(e.args[0])
+ self.status['text'] = "Error; {0}".format(e.args[0])
return
if decrypt_status == 0:
- self.status['text'] = u"File successfully decrypted"
+ self.status['text'] = "File successfully decrypted"
else:
- self.status['text'] = u"The was an error decrypting the file."
+ self.status['text'] = "The was an error decrypting the file."
root = Tkinter.Tk()
"This script requires OpenSSL or PyCrypto, which must be installed "
"separately. Read the top-of-script comment for details.")
return 1
- root.title(u"Barnes & Noble PDF Decrypter v.{0}".format(__version__))
+ root.title("Barnes & Noble PDF Decrypter v.{0}".format(__version__))
root.resizable(True, False)
root.minsize(370, 0)
DecryptionDialog(root).pack(fill=Tkconstants.X, expand=1)
-#!/usr/bin/env python
+#!/usr/bin/env python3
# -*- coding: utf-8 -*-
-from __future__ import with_statement
-
-# ineptepub.pyw
+# ineptepub.py
# Copyright © 2009-2020 by i♥cabbages, Apprentice Harper et al.
# Released under the terms of the GNU General Public Licence, version 3
start = argc.value - len(sys.argv)
return [argv[i] for i in
range(start, argc.value)]
- return [u"ineptepub.py"]
+ return ["ineptepub.py"]
else:
argvencoding = sys.stdin.encoding
if argvencoding == None:
def decryptBook(userkey, inpath, outpath):
if AES is None:
- raise ADEPTError(u"PyCrypto or OpenSSL must be installed.")
+ raise ADEPTError("PyCrypto or OpenSSL must be installed.")
rsa = RSA(userkey)
with closing(ZipFile(open(inpath, 'rb'))) as inf:
namelist = set(inf.namelist())
if 'META-INF/rights.xml' not in namelist or \
'META-INF/encryption.xml' not in namelist:
- print(u"{0:s} is DRM-free.".format(os.path.basename(inpath)))
+ print("{0:s} is DRM-free.".format(os.path.basename(inpath)))
return 1
for name in META_NAMES:
namelist.remove(name)
expr = './/%s' % (adept('encryptedKey'),)
bookkey = ''.join(rights.findtext(expr))
if len(bookkey) != 172:
- print(u"{0:s} is not a secure Adobe Adept ePub.".format(os.path.basename(inpath)))
+ print("{0:s} is not a secure Adobe Adept ePub.".format(os.path.basename(inpath)))
return 1
bookkey = rsa.decrypt(codecs.decode(bookkey.encode('ascii'), 'base64'))
# Padded as per RSAES-PKCS1-v1_5
if bookkey[-17] != '\x00' and bookkey[-17] != 0:
- print(u"Could not decrypt {0:s}. Wrong key".format(os.path.basename(inpath)))
+ print("Could not decrypt {0:s}. Wrong key".format(os.path.basename(inpath)))
return 2
encryption = inf.read('META-INF/encryption.xml')
decryptor = Decryptor(bookkey[-16:], encryption)
pass
outf.writestr(zi, decryptor.decrypt(path, data))
except:
- print(u"Could not decrypt {0:s} because of an exception:\n{1:s}".format(os.path.basename(inpath), traceback.format_exc()))
+ print("Could not decrypt {0:s} because of an exception:\n{1:s}".format(os.path.basename(inpath), traceback.format_exc()))
return 2
return 0
argv=unicode_argv()
progname = os.path.basename(argv[0])
if len(argv) != 4:
- print(u"usage: {0} <keyfile.der> <inbook.epub> <outbook.epub>".format(progname))
+ print("usage: {0} <keyfile.der> <inbook.epub> <outbook.epub>".format(progname))
return 1
keypath, inpath, outpath = argv[1:]
userkey = open(keypath,'rb').read()
result = decryptBook(userkey, inpath, outpath)
if result == 0:
- print(u"Successfully decrypted {0:s} as {1:s}".format(os.path.basename(inpath),os.path.basename(outpath)))
+ print("Successfully decrypted {0:s} as {1:s}".format(os.path.basename(inpath),os.path.basename(outpath)))
return result
def gui_main():
class DecryptionDialog(Tkinter.Frame):
def __init__(self, root):
Tkinter.Frame.__init__(self, root, border=5)
- self.status = Tkinter.Label(self, text=u"Select files for decryption")
+ self.status = Tkinter.Label(self, text="Select files for decryption")
self.status.pack(fill=Tkconstants.X, expand=1)
body = Tkinter.Frame(self)
body.pack(fill=Tkconstants.X, expand=1)
sticky = Tkconstants.E + Tkconstants.W
body.grid_columnconfigure(1, weight=2)
- Tkinter.Label(body, text=u"Key file").grid(row=0)
+ Tkinter.Label(body, text="Key file").grid(row=0)
self.keypath = Tkinter.Entry(body, width=30)
self.keypath.grid(row=0, column=1, sticky=sticky)
- if os.path.exists(u"adeptkey.der"):
- self.keypath.insert(0, u"adeptkey.der")
- button = Tkinter.Button(body, text=u"...", command=self.get_keypath)
+ if os.path.exists("adeptkey.der"):
+ self.keypath.insert(0, "adeptkey.der")
+ button = Tkinter.Button(body, text="...", command=self.get_keypath)
button.grid(row=0, column=2)
- Tkinter.Label(body, text=u"Input file").grid(row=1)
+ Tkinter.Label(body, text="Input file").grid(row=1)
self.inpath = Tkinter.Entry(body, width=30)
self.inpath.grid(row=1, column=1, sticky=sticky)
- button = Tkinter.Button(body, text=u"...", command=self.get_inpath)
+ button = Tkinter.Button(body, text="...", command=self.get_inpath)
button.grid(row=1, column=2)
- Tkinter.Label(body, text=u"Output file").grid(row=2)
+ Tkinter.Label(body, text="Output file").grid(row=2)
self.outpath = Tkinter.Entry(body, width=30)
self.outpath.grid(row=2, column=1, sticky=sticky)
- button = Tkinter.Button(body, text=u"...", command=self.get_outpath)
+ button = Tkinter.Button(body, text="...", command=self.get_outpath)
button.grid(row=2, column=2)
buttons = Tkinter.Frame(self)
buttons.pack()
botton = Tkinter.Button(
- buttons, text=u"Decrypt", width=10, command=self.decrypt)
+ buttons, text="Decrypt", width=10, command=self.decrypt)
botton.pack(side=Tkconstants.LEFT)
Tkinter.Frame(buttons, width=10).pack(side=Tkconstants.LEFT)
button = Tkinter.Button(
- buttons, text=u"Quit", width=10, command=self.quit)
+ buttons, text="Quit", width=10, command=self.quit)
button.pack(side=Tkconstants.RIGHT)
def get_keypath(self):
keypath = tkFileDialog.askopenfilename(
- parent=None, title=u"Select Adobe Adept \'.der\' key file",
- defaultextension=u".der",
+ parent=None, title="Select Adobe Adept \'.der\' key file",
+ defaultextension=".der",
filetypes=[('Adobe Adept DER-encoded files', '.der'),
('All Files', '.*')])
if keypath:
def get_inpath(self):
inpath = tkFileDialog.askopenfilename(
- parent=None, title=u"Select ADEPT-encrypted ePub file to decrypt",
- defaultextension=u".epub", filetypes=[('ePub files', '.epub')])
+ parent=None, title="Select ADEPT-encrypted ePub file to decrypt",
+ defaultextension=".epub", filetypes=[('ePub files', '.epub')])
if inpath:
inpath = os.path.normpath(inpath)
self.inpath.delete(0, Tkconstants.END)
def get_outpath(self):
outpath = tkFileDialog.asksaveasfilename(
- parent=None, title=u"Select unencrypted ePub file to produce",
- defaultextension=u".epub", filetypes=[('ePub files', '.epub')])
+ parent=None, title="Select unencrypted ePub file to produce",
+ defaultextension=".epub", filetypes=[('ePub files', '.epub')])
if outpath:
outpath = os.path.normpath(outpath)
self.outpath.delete(0, Tkconstants.END)
inpath = self.inpath.get()
outpath = self.outpath.get()
if not keypath or not os.path.exists(keypath):
- self.status['text'] = u"Specified key file does not exist"
+ self.status['text'] = "Specified key file does not exist"
return
if not inpath or not os.path.exists(inpath):
- self.status['text'] = u"Specified input file does not exist"
+ self.status['text'] = "Specified input file does not exist"
return
if not outpath:
- self.status['text'] = u"Output file not specified"
+ self.status['text'] = "Output file not specified"
return
if inpath == outpath:
- self.status['text'] = u"Must have different input and output files"
+ self.status['text'] = "Must have different input and output files"
return
userkey = open(keypath,'rb').read()
- self.status['text'] = u"Decrypting..."
+ self.status['text'] = "Decrypting..."
try:
decrypt_status = decryptBook(userkey, inpath, outpath)
except Exception as e:
- self.status['text'] = u"Error: {0}".format(e.args[0])
+ self.status['text'] = "Error: {0}".format(e.args[0])
return
if decrypt_status == 0:
- self.status['text'] = u"File successfully decrypted"
+ self.status['text'] = "File successfully decrypted"
else:
- self.status['text'] = u"The was an error decrypting the file."
+ self.status['text'] = "The was an error decrypting the file."
root = Tkinter.Tk()
- root.title(u"Adobe Adept ePub Decrypter v.{0}".format(__version__))
+ root.title("Adobe Adept ePub Decrypter v.{0}".format(__version__))
root.resizable(True, False)
root.minsize(300, 0)
DecryptionDialog(root).pack(fill=Tkconstants.X, expand=1)
-#! /usr/bin/python
+#!/usr/bin/env python3
# -*- coding: utf-8 -*-
-from __future__ import with_statement
-
# ineptpdf.py
# Copyright © 2009-2020 by i♥cabbages, Apprentice Harper et al.
start = argc.value - len(sys.argv)
return [argv[i] for i in
range(start, argc.value)]
- return [u"ineptpdf.py"]
+ return ["ineptpdf.py"]
else:
argvencoding = sys.stdin.encoding
if argvencoding is None:
def decryptBook(userkey, inpath, outpath):
if RSA is None:
- raise ADEPTError(u"PyCrypto or OpenSSL must be installed.")
+ raise ADEPTError("PyCrypto or OpenSSL must be installed.")
with open(inpath, 'rb') as inf:
#try:
serializer = PDFSerializer(inf, userkey)
#except:
- # print u"Error serializing pdf {0}. Probably wrong key.".format(os.path.basename(inpath))
+ # print "Error serializing pdf {0}. Probably wrong key.".format(os.path.basename(inpath))
# return 2
# hope this will fix the 'bad file descriptor' problem
with open(outpath, 'wb') as outf:
try:
serializer.dump(outf)
except Exception as e:
- print(u"error writing pdf: {0}".format(e.args[0]))
+ print("error writing pdf: {0}".format(e.args[0]))
return 2
return 0
argv=unicode_argv()
progname = os.path.basename(argv[0])
if len(argv) != 4:
- print(u"usage: {0} <keyfile.der> <inbook.pdf> <outbook.pdf>".format(progname))
+ print("usage: {0} <keyfile.der> <inbook.pdf> <outbook.pdf>".format(progname))
return 1
keypath, inpath, outpath = argv[1:]
userkey = open(keypath,'rb').read()
result = decryptBook(userkey, inpath, outpath)
if result == 0:
- print(u"Successfully decrypted {0:s} as {1:s}".format(os.path.basename(inpath),os.path.basename(outpath)))
+ print("Successfully decrypted {0:s} as {1:s}".format(os.path.basename(inpath),os.path.basename(outpath)))
return result
class DecryptionDialog(Tkinter.Frame):
def __init__(self, root):
Tkinter.Frame.__init__(self, root, border=5)
- self.status = Tkinter.Label(self, text=u"Select files for decryption")
+ self.status = Tkinter.Label(self, text="Select files for decryption")
self.status.pack(fill=Tkconstants.X, expand=1)
body = Tkinter.Frame(self)
body.pack(fill=Tkconstants.X, expand=1)
sticky = Tkconstants.E + Tkconstants.W
body.grid_columnconfigure(1, weight=2)
- Tkinter.Label(body, text=u"Key file").grid(row=0)
+ Tkinter.Label(body, text="Key file").grid(row=0)
self.keypath = Tkinter.Entry(body, width=30)
self.keypath.grid(row=0, column=1, sticky=sticky)
- if os.path.exists(u"adeptkey.der"):
- self.keypath.insert(0, u"adeptkey.der")
- button = Tkinter.Button(body, text=u"...", command=self.get_keypath)
+ if os.path.exists("adeptkey.der"):
+ self.keypath.insert(0, "adeptkey.der")
+ button = Tkinter.Button(body, text="...", command=self.get_keypath)
button.grid(row=0, column=2)
- Tkinter.Label(body, text=u"Input file").grid(row=1)
+ Tkinter.Label(body, text="Input file").grid(row=1)
self.inpath = Tkinter.Entry(body, width=30)
self.inpath.grid(row=1, column=1, sticky=sticky)
- button = Tkinter.Button(body, text=u"...", command=self.get_inpath)
+ button = Tkinter.Button(body, text="...", command=self.get_inpath)
button.grid(row=1, column=2)
- Tkinter.Label(body, text=u"Output file").grid(row=2)
+ Tkinter.Label(body, text="Output file").grid(row=2)
self.outpath = Tkinter.Entry(body, width=30)
self.outpath.grid(row=2, column=1, sticky=sticky)
- button = Tkinter.Button(body, text=u"...", command=self.get_outpath)
+ button = Tkinter.Button(body, text="...", command=self.get_outpath)
button.grid(row=2, column=2)
buttons = Tkinter.Frame(self)
buttons.pack()
botton = Tkinter.Button(
- buttons, text=u"Decrypt", width=10, command=self.decrypt)
+ buttons, text="Decrypt", width=10, command=self.decrypt)
botton.pack(side=Tkconstants.LEFT)
Tkinter.Frame(buttons, width=10).pack(side=Tkconstants.LEFT)
button = Tkinter.Button(
- buttons, text=u"Quit", width=10, command=self.quit)
+ buttons, text="Quit", width=10, command=self.quit)
button.pack(side=Tkconstants.RIGHT)
def get_keypath(self):
keypath = tkFileDialog.askopenfilename(
- parent=None, title=u"Select Adobe Adept \'.der\' key file",
- defaultextension=u".der",
+ parent=None, title="Select Adobe Adept \'.der\' key file",
+ defaultextension=".der",
filetypes=[('Adobe Adept DER-encoded files', '.der'),
('All Files', '.*')])
if keypath:
def get_inpath(self):
inpath = tkFileDialog.askopenfilename(
- parent=None, title=u"Select ADEPT-encrypted PDF file to decrypt",
- defaultextension=u".pdf", filetypes=[('PDF files', '.pdf')])
+ parent=None, title="Select ADEPT-encrypted PDF file to decrypt",
+ defaultextension=".pdf", filetypes=[('PDF files', '.pdf')])
if inpath:
inpath = os.path.normpath(inpath)
self.inpath.delete(0, Tkconstants.END)
def get_outpath(self):
outpath = tkFileDialog.asksaveasfilename(
- parent=None, title=u"Select unencrypted PDF file to produce",
- defaultextension=u".pdf", filetypes=[('PDF files', '.pdf')])
+ parent=None, title="Select unencrypted PDF file to produce",
+ defaultextension=".pdf", filetypes=[('PDF files', '.pdf')])
if outpath:
outpath = os.path.normpath(outpath)
self.outpath.delete(0, Tkconstants.END)
inpath = self.inpath.get()
outpath = self.outpath.get()
if not keypath or not os.path.exists(keypath):
- self.status['text'] = u"Specified key file does not exist"
+ self.status['text'] = "Specified key file does not exist"
return
if not inpath or not os.path.exists(inpath):
- self.status['text'] = u"Specified input file does not exist"
+ self.status['text'] = "Specified input file does not exist"
return
if not outpath:
- self.status['text'] = u"Output file not specified"
+ self.status['text'] = "Output file not specified"
return
if inpath == outpath:
- self.status['text'] = u"Must have different input and output files"
+ self.status['text'] = "Must have different input and output files"
return
userkey = open(keypath,'rb').read()
- self.status['text'] = u"Decrypting..."
+ self.status['text'] = "Decrypting..."
try:
decrypt_status = decryptBook(userkey, inpath, outpath)
except Exception as e:
- self.status['text'] = u"Error; {0}".format(e.args[0])
+ self.status['text'] = "Error; {0}".format(e.args[0])
return
if decrypt_status == 0:
- self.status['text'] = u"File successfully decrypted"
+ self.status['text'] = "File successfully decrypted"
else:
- self.status['text'] = u"The was an error decrypting the file."
+ self.status['text'] = "The was an error decrypting the file."
root = Tkinter.Tk()
"This script requires OpenSSL or PyCrypto, which must be installed "
"separately. Read the top-of-script comment for details.")
return 1
- root.title(u"Adobe Adept PDF Decrypter v.{0}".format(__version__))
+ root.title("Adobe Adept PDF Decrypter v.{0}".format(__version__))
root.resizable(True, False)
root.minsize(370, 0)
DecryptionDialog(root).pack(fill=Tkconstants.X, expand=1)
-#!/usr/bin/env python
+#!/usr/bin/env python3
# -*- coding: utf-8 -*-
-from __future__ import with_statement
-
# ion.py
# Copyright © 2013-2020 Apprentice Harper et al.
-#!/usr/bin/env python
+#!/usr/bin/env python3
# -*- coding: utf-8 -*-
-from __future__ import with_statement
-
# k4mobidedrm.py
# Copyright © 2008-2020 by Apprentice Harper et al.
range(start, argc.value)]
# if we don't have any arguments at all, just pass back script name
# this should never happen
- return [u"mobidedrm.py"]
+ return ["mobidedrm.py"]
else:
argvencoding = sys.stdin.encoding
if argvencoding == None:
# and some improvements suggested by jhaisley
def cleanup_name(name):
# substitute filename unfriendly characters
- name = name.replace(u"<",u"[").replace(u">",u"]").replace(u" : ",u" – ").replace(u": ",u" – ").replace(u":",u"—").replace(u"/",u"_").replace(u"\\",u"_").replace(u"|",u"_").replace(u"\"",u"\'").replace(u"*",u"_").replace(u"?",u"")
+ name = name.replace("<","[").replace(">","]").replace(" : "," – ").replace(": "," – ").replace(":","—").replace("/","_").replace("\\","_").replace("|","_").replace("\"","\'").replace("*","_").replace("?",u"")
# white space to single space, delete leading and trailing while space
- name = re.sub(r"\s", u" ", name).strip()
+ name = re.sub(r"\s", " ", name).strip()
# delete control characters
name = u"".join(char for char in name if ord(char)>=32)
# delete non-ascii characters
name = u"".join(char for char in name if ord(char)<=126)
# remove leading dots
- while len(name)>0 and name[0] == u".":
+ while len(name)>0 and name[0] == ".":
name = name[1:]
# remove trailing dots (Windows doesn't like them)
- while name.endswith(u'.'):
+ while name.endswith("."):
name = name[:-1]
if len(name)==0:
- name=u"DecryptedBook"
+ name="DecryptedBook"
return name
# must be passed unicode
def unescape(text):
def fixup(m):
text = m.group(0)
- if text[:2] == u"&#":
+ if text[:2] == "&#":
# character reference
try:
- if text[:3] == u"&#x":
+ if text[:3] == "&#x":
return chr(int(text[3:-1], 16))
else:
return chr(int(text[2:-1]))
except KeyError:
pass
return text # leave as is
- return re.sub(u"&#?\\w+;", fixup, text)
+ return re.sub("&#?\\w+;", fixup, text)
def GetDecryptedBook(infile, kDatabases, androidFiles, serials, pids, starttime = time.time()):
# handle the obvious cases at the beginning
if not os.path.isfile(infile):
- raise DrmException(u"Input file does not exist.")
+ raise DrmException("Input file does not exist.")
mobi = True
magic8 = open(infile,'rb').read(8)
if magic8 == '\xeaDRMION\xee':
- raise DrmException(u"The .kfx DRMION file cannot be decrypted by itself. A .kfx-zip archive containing a DRM voucher is required.")
+ raise DrmException("The .kfx DRMION file cannot be decrypted by itself. A .kfx-zip archive containing a DRM voucher is required.")
magic3 = magic8[:3]
if magic3 == 'TPZ':
mb = topazextract.TopazBook(infile)
bookname = unescape(mb.getBookTitle())
- print(u"Decrypting {1} ebook: {0}".format(bookname, mb.getBookType()))
+ print("Decrypting {1} ebook: {0}".format(bookname, mb.getBookType()))
# copy list of pids
totalpids = list(pids)
totalpids.extend(kgenpids.getPidList(md1, md2, serials, kDatabases))
# remove any duplicates
totalpids = list(set(totalpids))
- print(u"Found {1:d} keys to try after {0:.1f} seconds".format(time.time()-starttime, len(totalpids)))
+ print("Found {1:d} keys to try after {0:.1f} seconds".format(time.time()-starttime, len(totalpids)))
#print totalpids
try:
mb.cleanup
raise
- print(u"Decryption succeeded after {0:.1f} seconds".format(time.time()-starttime))
+ print("Decryption succeeded after {0:.1f} seconds".format(time.time()-starttime))
return mb
kindleDatabase = json.loads(keyfilein.read())
kDatabases.append([dbfile,kindleDatabase])
except Exception as e:
- print(u"Error getting database from file {0:s}: {1:s}".format(dbfile,e))
+ print("Error getting database from file {0:s}: {1:s}".format(dbfile,e))
traceback.print_exc()
try:
book = GetDecryptedBook(infile, kDatabases, androidFiles, serials, pids, starttime)
except Exception as e:
- print(u"Error decrypting book after {1:.1f} seconds: {0}".format(e.args[0],time.time()-starttime))
+ print("Error decrypting book after {1:.1f} seconds: {0}".format(e.args[0],time.time()-starttime))
traceback.print_exc()
return 1
re.match('^{0-9A-F-}{36}$', orig_fn_root)
): # Kindle for PC / Mac / Android / Fire / iOS
clean_title = cleanup_name(book.getBookTitle())
- outfilename = u'{}_{}'.format(orig_fn_root, clean_title)
+ outfilename = "{}_{}".format(orig_fn_root, clean_title)
else: # E Ink Kindle, which already uses a reasonable name
outfilename = orig_fn_root
if len(outfilename)>150:
outfilename = outfilename[:99]+"--"+outfilename[-49:]
- outfilename = outfilename+u"_nodrm"
+ outfilename = outfilename+"_nodrm"
outfile = os.path.join(outdir, outfilename + book.getBookExtension())
book.getFile(outfile)
- print(u"Saved decrypted book {1:s} after {0:.1f} seconds".format(time.time()-starttime, outfilename))
+ print("Saved decrypted book {1:s} after {0:.1f} seconds".format(time.time()-starttime, outfilename))
- if book.getBookType()==u"Topaz":
- zipname = os.path.join(outdir, outfilename + u"_SVG.zip")
+ if book.getBookType()=="Topaz":
+ zipname = os.path.join(outdir, outfilename + "_SVG.zip")
book.getSVGZip(zipname)
- print(u"Saved SVG ZIP Archive for {1:s} after {0:.1f} seconds".format(time.time()-starttime, outfilename))
+ print("Saved SVG ZIP Archive for {1:s} after {0:.1f} seconds".format(time.time()-starttime, outfilename))
# remove internal temporary directory of Topaz pieces
book.cleanup()
def usage(progname):
- print(u"Removes DRM protection from Mobipocket, Amazon KF8, Amazon Print Replica and Amazon Topaz ebooks")
- print(u"Usage:")
- print(u" {0} [-k <kindle.k4i>] [-p <comma separated PIDs>] [-s <comma separated Kindle serial numbers>] [ -a <AmazonSecureStorage.xml|backup.ab> ] <infile> <outdir>".format(progname))
+ print("Removes DRM protection from Mobipocket, Amazon KF8, Amazon Print Replica and Amazon Topaz ebooks")
+ print("Usage:")
+ print(" {0} [-k <kindle.k4i>] [-p <comma separated PIDs>] [-s <comma separated Kindle serial numbers>] [ -a <AmazonSecureStorage.xml|backup.ab> ] <infile> <outdir>".format(progname))
#
# Main
def cli_main():
argv=unicode_argv()
progname = os.path.basename(argv[0])
- print(u"K4MobiDeDrm v{0}.\nCopyright © 2008-2017 Apprentice Harper et al.".format(__version__))
+ print("K4MobiDeDrm v{0}.\nCopyright © 2008-2017 Apprentice Harper et al.".format(__version__))
try:
opts, args = getopt.getopt(argv[1:], "k:p:s:a:")
except getopt.GetoptError as err:
- print(u"Error in options or arguments: {0}".format(err.args[0]))
+ print("Error in options or arguments: {0}".format(err.args[0]))
usage(progname)
sys.exit(2)
if len(args)<2:
-#!/usr/bin/env python
+#!/usr/bin/env python3
# -*- coding: utf-8 -*-
-from __future__ import with_statement
-from __future__ import print_function
-
# Engine to remove drm from Kindle KFX ebooks
-# 2.0 - Added Python 3 compatibility for calibre 5.0
+# 2.0 - Python 3 for calibre 5.0
import os
data += fh.read()
if self.voucher is None:
self.decrypt_voucher(totalpids)
- print(u'Decrypting KFX DRMION: {0}'.format(filename))
+ print("Decrypting KFX DRMION: {0}".format(filename))
outfile = StringIO()
ion.DrmIon(StringIO(data[8:-8]), lambda name: self.voucher).parse(outfile)
self.decrypted[filename] = outfile.getvalue()
if not self.decrypted:
- print(u'The .kfx-zip archive does not contain an encrypted DRMION file')
+ print("The .kfx-zip archive does not contain an encrypted DRMION file")
def decrypt_voucher(self, totalpids):
with zipfile.ZipFile(self.infile, 'r') as zf:
if 'ProtectedData' in data:
break # found DRM voucher
else:
- raise Exception(u'The .kfx-zip archive contains an encrypted DRMION file without a DRM voucher')
+ raise Exception("The .kfx-zip archive contains an encrypted DRMION file without a DRM voucher")
- print(u'Decrypting KFX DRM voucher: {0}'.format(info.filename))
+ print("Decrypting KFX DRM voucher: {0}".format(info.filename))
for pid in [''] + totalpids:
for dsn_len,secret_len in [(0,0), (16,0), (16,40), (32,40), (40,0), (40,40)]:
except:
pass
else:
- raise Exception(u'Failed to decrypt KFX DRM voucher with any key')
+ raise Exception("Failed to decrypt KFX DRM voucher with any key")
- print(u'KFX DRM voucher successfully decrypted')
+ print("KFX DRM voucher successfully decrypted")
license_type = voucher.getlicensetype()
if license_type != "Purchase":
- raise Exception((u'This book is licensed as {0}. '
+ raise Exception(("This book is licensed as {0}. "
'These tools are intended for use on purchased books.').format(license_type))
self.voucher = voucher
-#!/usr/bin/env python
+#!/usr/bin/env python3
# -*- coding: utf-8 -*-
-from __future__ import with_statement
-from __future__ import print_function
-
# kgenpids.py
# Copyright © 2008-2020 Apprentice Harper et al.
# 2.0 - Fix for non-ascii Windows user names
# 2.1 - Actual fix for non-ascii WIndows user names.
# 2.2 - Return information needed for KFX decryption
-# 3.0 - Added Python 3 compatibility for calibre 5.0
+# 3.0 - Python 3 for calibre 5.0
import sys
try:
# Get the DSN token, if present
DSN = bytearray.fromhex((kindleDatabase[1])['DSN']).decode()
- print(u"Got DSN key from database {0}".format(kindleDatabase[0]))
+ print("Got DSN key from database {0}".format(kindleDatabase[0]))
except KeyError:
# See if we have the info to generate the DSN
try:
# Get the Mazama Random number
MazamaRandomNumber = bytearray.fromhex((kindleDatabase[1])['MazamaRandomNumber']).decode()
- #print u"Got MazamaRandomNumber from database {0}".format(kindleDatabase[0])
+ #print "Got MazamaRandomNumber from database {0}".format(kindleDatabase[0])
try:
# Get the SerialNumber token, if present
IDString = bytearray.fromhex((kindleDatabase[1])['SerialNumber']).decode()
- print(u"Got SerialNumber from database {0}".format(kindleDatabase[0]))
+ print("Got SerialNumber from database {0}".format(kindleDatabase[0]))
except KeyError:
# Get the IDString we added
IDString = bytearray.fromhex((kindleDatabase[1])['IDString']).decode()
try:
# Get the UsernameHash token, if present
encodedUsername = bytearray.fromhex((kindleDatabase[1])['UsernameHash']).decode()
- print(u"Got UsernameHash from database {0}".format(kindleDatabase[0]))
+ print("Got UsernameHash from database {0}".format(kindleDatabase[0]))
except KeyError:
# Get the UserName we added
UserName = bytearray.fromhex((kindleDatabase[1])['UserName']).decode()
# encode it
encodedUsername = encodeHash(UserName,charMap1)
- #print u"encodedUsername",encodedUsername.encode('hex')
+ #print "encodedUsername",encodedUsername.encode('hex')
except KeyError:
- print(u"Keys not found in the database {0}.".format(kindleDatabase[0]))
+ print("Keys not found in the database {0}.".format(kindleDatabase[0]))
return pids
# Get the ID string used
encodedIDString = encodeHash(IDString,charMap1)
- #print u"encodedIDString",encodedIDString.encode('hex')
+ #print "encodedIDString",encodedIDString.encode('hex')
# concat, hash and encode to calculate the DSN
DSN = encode(SHA1(MazamaRandomNumber+encodedIDString+encodedUsername),charMap1)
- #print u"DSN",DSN.encode('hex')
+ #print "DSN",DSN.encode('hex')
pass
if rec209 is None:
try:
pidlst.extend(getK4Pids(md1, md2, kDatabase))
except Exception as e:
- print(u"Error getting PIDs from database {0}: {1}".format(kDatabase[0],e.args[0]))
+ print("Error getting PIDs from database {0}: {1}".format(kDatabase[0],e.args[0]))
traceback.print_exc()
for serialnum in serials:
try:
pidlst.extend(getKindlePids(md1, md2, serialnum))
except Exception as e:
- print(u"Error getting PIDs from serial number {0}: {1}".format(serialnum ,e.args[0]))
+ print("Error getting PIDs from serial number {0}: {1}".format(serialnum ,e.args[0]))
traceback.print_exc()
return pidlst
-#!/usr/bin/env python
+#!/usr/bin/env python3
# -*- coding: utf-8 -*-
-from __future__ import with_statement
-
# kindlekey.py
# Copyright © 2008-2020 Apprentice Harper et al.
# 2.5 - Final Fix for Windows user names with non-ascii characters, thanks to oneofusoneofus
# 2.6 - Start adding support for Kindle 1.25+ .kinf2018 file
# 2.7 - Finish .kinf2018 support, PC & Mac by Apprentice Sakuya
-# 3.0 - Added Python 3 compatibility for calibre 5.0
+# 3.0 - Python 3 for calibre 5.0
"""
xrange(start, argc.value)]
# if we don't have any arguments at all, just pass back script name
# this should never happen
- return [u"kindlekey.py"]
+ return ["kindlekey.py"]
else:
argvencoding = sys.stdin.encoding
if argvencoding == None:
# replace any non-ASCII values with 0xfffd
for i in xrange(0,len(buffer)):
- if buffer[i]>u"\u007f":
- #print u"swapping char "+str(i)+" ("+buffer[i]+")"
- buffer[i] = u"\ufffd"
+ if buffer[i]>"\u007f":
+ #print "swapping char "+str(i)+" ("+buffer[i]+")"
+ buffer[i] = "\ufffd"
# return utf-8 encoding of modified username
- #print u"modified username:"+buffer.value
+ #print "modified username:"+buffer.value
return buffer.value.encode('utf-8')
return GetUserName
GetUserName = GetUserName()
n = ctypes.windll.kernel32.GetEnvironmentVariableW(name, None, 0)
if n == 0:
return None
- buf = ctypes.create_unicode_buffer(u'\0'*n)
+ buf = ctypes.create_unicode_buffer("\0"*n)
ctypes.windll.kernel32.GetEnvironmentVariableW(name, buf, n)
return buf.value
path = ""
if 'LOCALAPPDATA' in os.environ.keys():
# Python 2.x does not return unicode env. Use Python 3.x
- path = winreg.ExpandEnvironmentStrings(u"%LOCALAPPDATA%")
+ path = winreg.ExpandEnvironmentStrings("%LOCALAPPDATA%")
# this is just another alternative.
# path = getEnvironmentVariable('LOCALAPPDATA')
if not os.path.isdir(path):
print ('Could not find the folder in which to look for kinfoFiles.')
else:
# Probably not the best. To Fix (shouldn't ignore in encoding) or use utf-8
- print(u'searching for kinfoFiles in ' + path.encode('ascii', 'ignore'))
+ print("searching for kinfoFiles in " + path.encode('ascii', 'ignore'))
# look for (K4PC 1.25.1 and later) .kinf2018 file
kinfopath = path +'\\Amazon\\Kindle\\storage\\.kinf2018'
# store values used in decryption
DB['IDString'] = GetIDString()
DB['UserName'] = GetUserName()
- print(u"Decrypted key file using IDString '{0:s}' and UserName '{1:s}'".format(GetIDString(), GetUserName().encode('hex')))
+ print("Decrypted key file using IDString '{0:s}' and UserName '{1:s}'".format(GetIDString(), GetUserName().encode('hex')))
else:
- print(u"Couldn't decrypt file.")
+ print("Couldn't decrypt file.")
DB = {}
return DB
elif isosx:
libcrypto = find_library('crypto')
if libcrypto is None:
- raise DrmException(u"libcrypto not found")
+ raise DrmException("libcrypto not found")
libcrypto = CDLL(libcrypto)
# From OpenSSL's crypto aes header
def set_decrypt_key(self, userkey, iv):
self._blocksize = len(userkey)
if (self._blocksize != 16) and (self._blocksize != 24) and (self._blocksize != 32) :
- raise DrmException(u"AES improper key used")
+ raise DrmException("AES improper key used")
return
keyctx = self._keyctx = AES_KEY()
self._iv = iv
self._userkey = userkey
rv = AES_set_decrypt_key(userkey, len(userkey) * 8, keyctx)
if rv < 0:
- raise DrmException(u"Failed to initialize AES key")
+ raise DrmException("Failed to initialize AES key")
def decrypt(self, data):
out = create_string_buffer(len(data))
keyctx = self._keyctx
rv = AES_cbc_encrypt(data, out, len(data), keyctx, mutable_iv, 0)
if rv == 0:
- raise DrmException(u"AES decryption failed")
+ raise DrmException("AES decryption failed")
return out.raw
def keyivgen(self, passwd, salt, iter, keylen):
pass
if len(DB)>6:
# store values used in decryption
- print(u"Decrypted key file using IDString '{0:s}' and UserName '{1:s}'".format(IDString, GetUserName()))
+ print("Decrypted key file using IDString '{0:s}' and UserName '{1:s}'".format(IDString, GetUserName()))
DB['IDString'] = IDString
DB['UserName'] = GetUserName()
else:
- print(u"Couldn't decrypt file.")
+ print("Couldn't decrypt file.")
DB = {}
return DB
else:
def getDBfromFile(kInfoFile):
- raise DrmException(u"This script only runs under Windows or Mac OS X.")
+ raise DrmException("This script only runs under Windows or Mac OS X.")
return {}
def kindlekeys(files = []):
outfile = outpath
with file(outfile, 'w') as keyfileout:
keyfileout.write(json.dumps(keys[0]))
- print(u"Saved a key to {0}".format(outfile))
+ print("Saved a key to {0}".format(outfile))
else:
keycount = 0
for key in keys:
while True:
keycount += 1
- outfile = os.path.join(outpath,u"kindlekey{0:d}.k4i".format(keycount))
+ outfile = os.path.join(outpath,"kindlekey{0:d}.k4i".format(keycount))
if not os.path.exists(outfile):
break
with file(outfile, 'w') as keyfileout:
keyfileout.write(json.dumps(key))
- print(u"Saved a key to {0}".format(outfile))
+ print("Saved a key to {0}".format(outfile))
return True
return False
def usage(progname):
- print(u"Finds, decrypts and saves the default Kindle For Mac/PC encryption keys.")
- print(u"Keys are saved to the current directory, or a specified output directory.")
- print(u"If a file name is passed instead of a directory, only the first key is saved, in that file.")
- print(u"Usage:")
- print(u" {0:s} [-h] [-k <kindle.info>] [<outpath>]".format(progname))
+ print("Finds, decrypts and saves the default Kindle For Mac/PC encryption keys.")
+ print("Keys are saved to the current directory, or a specified output directory.")
+ print("If a file name is passed instead of a directory, only the first key is saved, in that file.")
+ print("Usage:")
+ print(" {0:s} [-h] [-k <kindle.info>] [<outpath>]".format(progname))
def cli_main():
sys.stderr=SafeUnbuffered(sys.stderr)
argv=unicode_argv()
progname = os.path.basename(argv[0])
- print(u"{0} v{1}\nCopyright © 2010-2016 by some_updates, Apprentice Alf and Apprentice Harper".format(progname,__version__))
+ print("{0} v{1}\nCopyright © 2010-2016 by some_updates, Apprentice Alf and Apprentice Harper".format(progname,__version__))
try:
opts, args = getopt.getopt(argv[1:], "hk:")
except getopt.GetoptError as err:
- print(u"Error in options or arguments: {0}".format(err.args[0]))
+ print("Error in options or arguments: {0}".format(err.args[0]))
usage(progname)
sys.exit(2)
outpath = os.path.realpath(os.path.normpath(outpath))
if not getkey(outpath, files):
- print(u"Could not retrieve Kindle for Mac/PC key.")
+ print("Could not retrieve Kindle for Mac/PC key.")
return 0
class ExceptionDialog(Tkinter.Frame):
def __init__(self, root, text):
Tkinter.Frame.__init__(self, root, border=5)
- label = Tkinter.Label(self, text=u"Unexpected error:",
+ label = Tkinter.Label(self, text="Unexpected error:",
anchor=Tkconstants.W, justify=Tkconstants.LEFT)
label.pack(fill=Tkconstants.X, expand=0)
self.text = Tkinter.Text(self)
for key in keys:
while True:
keycount += 1
- outfile = os.path.join(progpath,u"kindlekey{0:d}.k4i".format(keycount))
+ outfile = os.path.join(progpath,"kindlekey{0:d}.k4i".format(keycount))
if not os.path.exists(outfile):
break
with file(outfile, 'w') as keyfileout:
keyfileout.write(json.dumps(key))
success = True
- tkMessageBox.showinfo(progname, u"Key successfully retrieved to {0}".format(outfile))
+ tkMessageBox.showinfo(progname, "Key successfully retrieved to {0}".format(outfile))
except DrmException as e:
- tkMessageBox.showerror(progname, u"Error: {0}".format(str(e)))
+ tkMessageBox.showerror(progname, "Error: {0}".format(str(e)))
except Exception:
root.wm_state('normal')
root.title(progname)
-#!/usr/bin/python
+#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Mobipocket PID calculator v0.4 for Amazon Kindle.
# 0.3 updated for unicode
# 0.4 Added support for serial numbers starting with '9', fixed unicode bugs.
# 0.5 moved unicode_argv call inside main for Windows DeDRM compatibility
-# 1.0 Added Python 3 compatibility for calibre 5.0
+# 1.0 Python 3 for calibre 5.0
from __future__ import print_function
import sys
range(start, argc.value)]
# if we don't have any arguments at all, just pass back script name
# this should never happen
- return [u"kindlepid.py"]
+ return ["kindlepid.py"]
else:
argvencoding = sys.stdin.encoding
if argvencoding == None:
return pid
def cli_main():
- print(u"Mobipocket PID calculator for Amazon Kindle. Copyright © 2007, 2009 Igor Skochinsky")
+ print("Mobipocket PID calculator for Amazon Kindle. Copyright © 2007, 2009 Igor Skochinsky")
argv=unicode_argv()
if len(argv)==2:
serial = argv[1]
else:
- print(u"Usage: kindlepid.py <Kindle Serial Number>/<iPhone/iPod Touch UDID>")
+ print("Usage: kindlepid.py <Kindle Serial Number>/<iPhone/iPod Touch UDID>")
return 1
if len(serial)==16:
if serial.startswith("B") or serial.startswith("9"):
- print(u"Kindle serial number detected")
+ print("Kindle serial number detected")
else:
- print(u"Warning: unrecognized serial number. Please recheck input.")
+ print("Warning: unrecognized serial number. Please recheck input.")
return 1
pid = pidFromSerial(serial.encode("utf-8"),7)+'*'
- print(u"Mobipocket PID for Kindle serial#{0} is {1}".format(serial,checksumPid(pid)))
+ print("Mobipocket PID for Kindle serial#{0} is {1}".format(serial,checksumPid(pid)))
return 0
elif len(serial)==40:
- print(u"iPhone serial number (UDID) detected")
+ print("iPhone serial number (UDID) detected")
pid = pidFromSerial(serial.encode("utf-8"),8)
- print(u"Mobipocket PID for iPhone serial#{0} is {1}".format(serial,checksumPid(pid)))
+ print("Mobipocket PID for iPhone serial#{0} is {1}".format(serial,checksumPid(pid)))
return 0
- print(u"Warning: unrecognized serial number. Please recheck input.")
+ print("Warning: unrecognized serial number. Please recheck input.")
return 1
-#!/usr/bin/env python
+#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# mobidedrm.py
from __future__ import print_function
__license__ = 'GPL v3'
-__version__ = u"1.00"
+__version__ = "1.00"
# This is a python script. You need a Python interpreter to run it.
# For example, ActiveState Python, which exists for windows.
try:
from alfcrypto import Pukall_Cipher
except:
- print(u"AlfCrypto not found. Using python PC1 implementation.")
+ print("AlfCrypto not found. Using python PC1 implementation.")
# Wrap a stream so that output gets flushed immediately
# and also make sure that any unicode strings get
range(start, argc.value)]
# if we don't have any arguments at all, just pass back script name
# this should never happen
- return [u"mobidedrm.py"]
+ return ["mobidedrm.py"]
else:
argvencoding = sys.stdin.encoding
if argvencoding == None:
sum2 = 0;
keyXorVal = 0;
if len(key)!=16:
- DrmException (u"PC1: Bad key length")
+ DrmException ("PC1: Bad key length")
wkey = []
for i in range(8):
wkey.append(key[i*2]<<8 | key[i*2+1])
pass
def __init__(self, infile):
- print(u"MobiDeDrm v{0:s}.\nCopyright © 2008-2017 The Dark Reverser, Apprentice Harper et al.".format(__version__))
+ print("MobiDeDrm v{0:s}.\nCopyright © 2008-2017 The Dark Reverser, Apprentice Harper et al.".format(__version__))
try:
from alfcrypto import Pukall_Cipher
except:
- print(u"AlfCrypto not found. Using python PC1 implementation.")
+ print("AlfCrypto not found. Using python PC1 implementation.")
# initial sanity check on file
self.data_file = open(infile, 'rb').read()
self.mobi_data = ''
self.header = self.data_file[0:78]
if self.header[0x3C:0x3C+8] != b'BOOKMOBI' and self.header[0x3C:0x3C+8] != b'TEXtREAd':
- raise DrmException(u"Invalid file format")
+ raise DrmException("Invalid file format")
self.magic = self.header[0x3C:0x3C+8]
self.crypto_type = -1
self.mobi_version = -1
if self.magic == 'TEXtREAd':
- print(u"PalmDoc format book detected.")
+ print("PalmDoc format book detected.")
return
self.mobi_length, = struct.unpack('>L',self.sect[0x14:0x18])
self.mobi_codepage, = struct.unpack('>L',self.sect[0x1c:0x20])
self.mobi_version, = struct.unpack('>L',self.sect[0x68:0x6C])
- #print u"MOBI header version {0:d}, header length {1:d}".format(self.mobi_version, self.mobi_length)
+ #print "MOBI header version {0:d}, header length {1:d}".format(self.mobi_version, self.mobi_length)
if (self.mobi_length >= 0xE4) and (self.mobi_version >= 5):
self.extra_data_flags, = struct.unpack('>H', self.sect[0xF2:0xF4])
- #print u"Extra Data Flags: {0:d}".format(self.extra_data_flags)
+ #print "Extra Data Flags: {0:d}".format(self.extra_data_flags)
if (self.compression != 17480):
# multibyte utf8 data is included in the encryption for PalmDoc compression
# so clear that byte so that we leave it to be decrypted.
# print type, size, content, content.encode('hex')
pos += size
except Exception as e:
- print(u"Cannot set meta_array: Error: {:s}".format(e.args[0]))
+ print("Cannot set meta_array: Error: {:s}".format(e.args[0]))
def getBookTitle(self):
codec_map = {
def getBookType(self):
if self.print_replica:
- return u"Print Replica"
+ return "Print Replica"
if self.mobi_version >= 8:
- return u"Kindle Format 8"
+ return "Kindle Format 8"
if self.mobi_version >= 0:
- return u"Mobipocket {0:d}".format(self.mobi_version)
- return u"PalmDoc"
+ return "Mobipocket {0:d}".format(self.mobi_version)
+ return "PalmDoc"
def getBookExtension(self):
if self.print_replica:
- return u".azw4"
+ return ".azw4"
if self.mobi_version >= 8:
- return u".azw3"
- return u".mobi"
+ return ".azw3"
+ return ".mobi"
def processBook(self, pidlist):
crypto_type, = struct.unpack('>H', self.sect[0xC:0xC+2])
- print(u"Crypto Type is: {0:d}".format(crypto_type))
+ print("Crypto Type is: {0:d}".format(crypto_type))
self.crypto_type = crypto_type
if crypto_type == 0:
- print(u"This book is not encrypted.")
+ print("This book is not encrypted.")
# we must still check for Print Replica
self.print_replica = (self.loadSection(1)[0:4] == '%MOP')
self.mobi_data = self.data_file
return
if crypto_type != 2 and crypto_type != 1:
- raise DrmException(u"Cannot decode unknown Mobipocket encryption type {0:d}".format(crypto_type))
+ raise DrmException("Cannot decode unknown Mobipocket encryption type {0:d}".format(crypto_type))
if 406 in self.meta_array:
data406 = self.meta_array[406]
val406, = struct.unpack('>Q',data406)
if val406 != 0:
- raise DrmException(u"Cannot decode library or rented ebooks.")
+ raise DrmException("Cannot decode library or rented ebooks.")
goodpids = []
# print("DEBUG ==== pidlist = ", pidlist)
for pid in pidlist:
if len(pid)==10:
if checksumPid(pid[0:-2]) != pid:
- print(u"Warning: PID {0} has incorrect checksum, should have been {1}".format(pid,checksumPid(pid[0:-2])))
+ print("Warning: PID {0} has incorrect checksum, should have been {1}".format(pid,checksumPid(pid[0:-2])))
goodpids.append(pid[0:-2])
elif len(pid)==8:
goodpids.append(pid)
else:
- print(u"Warning: PID {0} has wrong number of digits".format(pid))
+ print("Warning: PID {0} has wrong number of digits".format(pid))
- # print(u"======= DEBUG good pids = ", goodpids)
+ # print("======= DEBUG good pids = ", goodpids)
if self.crypto_type == 1:
t1_keyvec = 'QDCVEPMU675RUBSZ'
# calculate the keys
drm_ptr, drm_count, drm_size, drm_flags = struct.unpack('>LLLL', self.sect[0xA8:0xA8+16])
if drm_count == 0:
- raise DrmException(u"Encryption not initialised. Must be opened with Mobipocket Reader first.")
+ raise DrmException("Encryption not initialised. Must be opened with Mobipocket Reader first.")
found_key, pid = self.parseDRM(self.sect[drm_ptr:drm_ptr+drm_size], drm_count, goodpids)
if not found_key:
- raise DrmException(u"No key found in {0:d} keys tried.".format(len(goodpids)))
+ raise DrmException("No key found in {0:d} keys tried.".format(len(goodpids)))
# kill the drm keys
self.patchSection(0, b'\0' * drm_size, drm_ptr)
# kill the drm pointers
self.patchSection(0, b'\xff' * 4 + b'\0' * 12, 0xA8)
if pid=='00000000':
- print(u"File has default encryption, no specific key needed.")
+ print("File has default encryption, no specific key needed.")
else:
- print(u"File is encoded with PID {0}.".format(checksumPid(pid)))
+ print("File is encoded with PID {0}.".format(checksumPid(pid)))
# clear the crypto type
self.patchSection(0, b'\0' * 2, 0xC)
# decrypt sections
- print(u"Decrypting. Please wait . . .", end=' ')
+ print("Decrypting. Please wait . . .", end=' ')
mobidataList = []
mobidataList.append(self.data_file[:self.sections[1][0]])
for i in range(1, self.records+1):
data = self.loadSection(i)
extra_size = getSizeOfTrailingDataEntries(data, len(data), self.extra_data_flags)
if i%100 == 0:
- print(u".", end=' ')
+ print(".", end=' ')
# print "record %d, extra_size %d" %(i,extra_size)
decoded_data = PC1(found_key, data[0:len(data) - extra_size])
if i==1:
if self.num_sections > self.records+1:
mobidataList.append(self.data_file[self.sections[self.records+1][0]:])
self.mobi_data = b''.join(mobidataList)
- print(u"done")
+ print("done")
return
def getUnencryptedBook(infile,pidlist):
if not os.path.isfile(infile):
- raise DrmException(u"Input File Not Found.")
+ raise DrmException("Input File Not Found.")
book = MobiBook(infile)
book.processBook(pidlist)
return book.mobi_data
argv=unicode_argv()
progname = os.path.basename(argv[0])
if len(argv)<3 or len(argv)>4:
- print(u"MobiDeDrm v{0:s}.\nCopyright © 2008-2017 The Dark Reverser, Apprentice Harper et al.".format(__version__))
- print(u"Removes protection from Kindle/Mobipocket, Kindle/KF8 and Kindle/Print Replica ebooks")
- print(u"Usage:")
- print(u" {0} <infile> <outfile> [<Comma separated list of PIDs to try>]".format(progname))
+ print("MobiDeDrm v{0:s}.\nCopyright © 2008-2017 The Dark Reverser, Apprentice Harper et al.".format(__version__))
+ print("Removes protection from Kindle/Mobipocket, Kindle/KF8 and Kindle/Print Replica ebooks")
+ print("Usage:")
+ print(" {0} <infile> <outfile> [<Comma separated list of PIDs to try>]".format(progname))
return 1
else:
infile = argv[1]
stripped_file = getUnencryptedBook(infile, pidlist)
open(outfile, 'wb').write(stripped_file)
except DrmException as e:
- print(u"MobiDeDRM v{0} Error: {1:s}".format(__version__,e.args[0]))
+ print("MobiDeDRM v{0} Error: {1:s}".format(__version__,e.args[0]))
return 1
return 0
-#!/usr/bin/env python
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
-from __future__ import with_statement
__license__ = 'GPL v3'
# Standard Python modules.
class DeDRM_Prefs():
def __init__(self):
- JSON_PATH = os.path.join(u"plugins", PLUGIN_NAME.strip().lower().replace(' ', '_') + '.json')
+ JSON_PATH = os.path.join("plugins", PLUGIN_NAME.strip().lower().replace(' ', '_') + '.json')
self.dedrmprefs = JSONConfig(JSON_PATH)
self.dedrmprefs.defaults['configured'] = False
try:
name, ccn = keystring.split(',')
# Generate Barnes & Noble EPUB user key from name and credit card number.
- keyname = u"{0}_{1}".format(name.strip(),ccn.strip()[-4:])
+ keyname = "{0}_{1}".format(name.strip(),ccn.strip()[-4:])
keyvalue = generate_key(name, ccn)
userkeys.append([keyname,keyvalue])
except Exception as e:
try:
name, cc = keystring.split(',')
# Generate eReader user key from name and credit card number.
- keyname = u"{0}_{1}".format(name.strip(),cc.strip()[-4:])
+ keyname = "{0}_{1}".format(name.strip(),cc.strip()[-4:])
keyvalue = getuser_key(name,cc).encode('hex')
userkeys.append([keyname,keyvalue])
except Exception as e:
return
- print(u"{0} v{1}: Importing configuration data from old DeDRM plugins".format(PLUGIN_NAME, PLUGIN_VERSION))
+ print("{0} v{1}: Importing configuration data from old DeDRM plugins".format(PLUGIN_NAME, PLUGIN_VERSION))
IGNOBLEPLUGINNAME = "Ignoble Epub DeDRM"
EREADERPLUGINNAME = "eReader PDB 2 PML"
OLDKINDLEPLUGINNAME = "K4PC, K4Mac, Kindle Mobi and Topaz DeDRM"
# get prefs from older tools
- kindleprefs = JSONConfig(os.path.join(u"plugins", u"K4MobiDeDRM"))
- ignobleprefs = JSONConfig(os.path.join(u"plugins", u"ignoble_epub_dedrm"))
+ kindleprefs = JSONConfig(os.path.join("plugins", "K4MobiDeDRM"))
+ ignobleprefs = JSONConfig(os.path.join("plugins", "ignoble_epub_dedrm"))
# Handle the old ignoble plugin's customization string by converting the
# old string to stored keys... get that personal data out of plain sight.
sc = config['plugin_customization']
val = sc.pop(IGNOBLEPLUGINNAME, None)
if val is not None:
- print(u"{0} v{1}: Converting old Ignoble plugin configuration string.".format(PLUGIN_NAME, PLUGIN_VERSION))
+ print("{0} v{1}: Converting old Ignoble plugin configuration string.".format(PLUGIN_NAME, PLUGIN_VERSION))
priorkeycount = len(dedrmprefs['bandnkeys'])
userkeys = parseIgnobleString(str(val))
for keypair in userkeys:
value = keypair[1]
dedrmprefs.addnamedvaluetoprefs('bandnkeys', name, value)
addedkeycount = len(dedrmprefs['bandnkeys'])-priorkeycount
- print(u"{0} v{1}: {2:d} Barnes and Noble {3} imported from old Ignoble plugin configuration string".format(PLUGIN_NAME, PLUGIN_VERSION, addedkeycount, u"key" if addedkeycount==1 else u"keys"))
+ print("{0} v{1}: {2:d} Barnes and Noble {3} imported from old Ignoble plugin configuration string".format(PLUGIN_NAME, PLUGIN_VERSION, addedkeycount, "key" if addedkeycount==1 else "keys"))
# Make the json write all the prefs to disk
dedrmprefs.writeprefs(False)
# old string to stored keys... get that personal data out of plain sight.
val = sc.pop(EREADERPLUGINNAME, None)
if val is not None:
- print(u"{0} v{1}: Converting old eReader plugin configuration string.".format(PLUGIN_NAME, PLUGIN_VERSION))
+ print("{0} v{1}: Converting old eReader plugin configuration string.".format(PLUGIN_NAME, PLUGIN_VERSION))
priorkeycount = len(dedrmprefs['ereaderkeys'])
userkeys = parseeReaderString(str(val))
for keypair in userkeys:
value = keypair[1]
dedrmprefs.addnamedvaluetoprefs('ereaderkeys', name, value)
addedkeycount = len(dedrmprefs['ereaderkeys'])-priorkeycount
- print(u"{0} v{1}: {2:d} eReader {3} imported from old eReader plugin configuration string".format(PLUGIN_NAME, PLUGIN_VERSION, addedkeycount, u"key" if addedkeycount==1 else u"keys"))
+ print("{0} v{1}: {2:d} eReader {3} imported from old eReader plugin configuration string".format(PLUGIN_NAME, PLUGIN_VERSION, addedkeycount, "key" if addedkeycount==1 else "keys"))
# Make the json write all the prefs to disk
dedrmprefs.writeprefs(False)
# get old Kindle plugin configuration string
val = sc.pop(OLDKINDLEPLUGINNAME, None)
if val is not None:
- print(u"{0} v{1}: Converting old Kindle plugin configuration string.".format(PLUGIN_NAME, PLUGIN_VERSION))
+ print("{0} v{1}: Converting old Kindle plugin configuration string.".format(PLUGIN_NAME, PLUGIN_VERSION))
priorpidcount = len(dedrmprefs['pids'])
priorserialcount = len(dedrmprefs['serials'])
pids, serials = parseKindleString(val)
dedrmprefs.addvaluetoprefs('serials',serial)
addedpidcount = len(dedrmprefs['pids']) - priorpidcount
addedserialcount = len(dedrmprefs['serials']) - priorserialcount
- print(u"{0} v{1}: {2:d} {3} and {4:d} {5} imported from old Kindle plugin configuration string.".format(PLUGIN_NAME, PLUGIN_VERSION, addedpidcount, u"PID" if addedpidcount==1 else u"PIDs", addedserialcount, u"serial number" if addedserialcount==1 else u"serial numbers"))
+ print("{0} v{1}: {2:d} {3} and {4:d} {5} imported from old Kindle plugin configuration string.".format(PLUGIN_NAME, PLUGIN_VERSION, addedpidcount, "PID" if addedpidcount==1 else "PIDs", addedserialcount, "serial number" if addedserialcount==1 else "serial numbers"))
# Make the json write all the prefs to disk
dedrmprefs.writeprefs(False)
dedrmprefs.addnamedvaluetoprefs('bandnkeys', name, value)
addedkeycount = len(dedrmprefs['bandnkeys'])-priorkeycount
if addedkeycount > 0:
- print(u"{0} v{1}: {2:d} Barnes and Noble {3} imported from config folder.".format(PLUGIN_NAME, PLUGIN_VERSION, addedkeycount, u"key file" if addedkeycount==1 else u"key files"))
+ print("{0} v{1}: {2:d} Barnes and Noble {3} imported from config folder.".format(PLUGIN_NAME, PLUGIN_VERSION, addedkeycount, "key file" if addedkeycount==1 else "key files"))
# Make the json write all the prefs to disk
dedrmprefs.writeprefs(False)
dedrmprefs.addnamedvaluetoprefs('adeptkeys', name, value)
addedkeycount = len(dedrmprefs['adeptkeys'])-priorkeycount
if addedkeycount > 0:
- print(u"{0} v{1}: {2:d} Adobe Adept {3} imported from config folder.".format(PLUGIN_NAME, PLUGIN_VERSION, addedkeycount, u"keyfile" if addedkeycount==1 else u"keyfiles"))
+ print("{0} v{1}: {2:d} Adobe Adept {3} imported from config folder.".format(PLUGIN_NAME, PLUGIN_VERSION, addedkeycount, "keyfile" if addedkeycount==1 else "keyfiles"))
# Make the json write all the prefs to disk
dedrmprefs.writeprefs(False)
addedkeycount = len(dedrmprefs['bandnkeys']) - priorkeycount
# no need to delete old prefs, since they contain no recoverable private data
if addedkeycount > 0:
- print(u"{0} v{1}: {2:d} Barnes and Noble {3} imported from Ignoble plugin preferences.".format(PLUGIN_NAME, PLUGIN_VERSION, addedkeycount, u"key" if addedkeycount==1 else u"keys"))
+ print("{0} v{1}: {2:d} Barnes and Noble {3} imported from Ignoble plugin preferences.".format(PLUGIN_NAME, PLUGIN_VERSION, addedkeycount, "key" if addedkeycount==1 else "keys"))
# Make the json write all the prefs to disk
dedrmprefs.writeprefs(False)
dedrmprefs.addvaluetoprefs('serials',serial)
addedpidcount = len(dedrmprefs['pids']) - priorpidcount
if addedpidcount > 0:
- print(u"{0} v{1}: {2:d} {3} imported from Kindle plugin preferences".format(PLUGIN_NAME, PLUGIN_VERSION, addedpidcount, u"PID" if addedpidcount==1 else u"PIDs"))
+ print("{0} v{1}: {2:d} {3} imported from Kindle plugin preferences".format(PLUGIN_NAME, PLUGIN_VERSION, addedpidcount, "PID" if addedpidcount==1 else "PIDs"))
addedserialcount = len(dedrmprefs['serials']) - priorserialcount
if addedserialcount > 0:
- print(u"{0} v{1}: {2:d} {3} imported from Kindle plugin preferences".format(PLUGIN_NAME, PLUGIN_VERSION, addedserialcount, u"serial number" if addedserialcount==1 else u"serial numbers"))
+ print("{0} v{1}: {2:d} {3} imported from Kindle plugin preferences".format(PLUGIN_NAME, PLUGIN_VERSION, addedserialcount, "serial number" if addedserialcount==1 else "serial numbers"))
try:
if 'wineprefix' in kindleprefs and kindleprefs['wineprefix'] != "":
dedrmprefs.set('adobewineprefix',kindleprefs['wineprefix'])
dedrmprefs.set('kindlewineprefix',kindleprefs['wineprefix'])
- print(u"{0} v{1}: WINEPREFIX ‘(2)’ imported from Kindle plugin preferences".format(PLUGIN_NAME, PLUGIN_VERSION, kindleprefs['wineprefix']))
+ print("{0} v{1}: WINEPREFIX ‘(2)’ imported from Kindle plugin preferences".format(PLUGIN_NAME, PLUGIN_VERSION, kindleprefs['wineprefix']))
except:
traceback.print_exc()
# Make the json write all the prefs to disk
dedrmprefs.writeprefs()
- print(u"{0} v{1}: Finished setting up configuration data.".format(PLUGIN_NAME, PLUGIN_VERSION))
+ print("{0} v{1}: Finished setting up configuration data.".format(PLUGIN_NAME, PLUGIN_VERSION))
-#!/usr/bin/env python
+#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# vim:ts=4:sw=4:softtabstop=4:smarttab:expandtab
-#!/usr/bin/env python
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
# vim:ts=4:sw=4:softtabstop=4:smarttab:expandtab
import sys
-#!/usr/bin/env python
+#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# topazextract.py
range(start, argc.value)]
# if we don't have any arguments at all, just pass back script name
# this should never happen
- return [u"mobidedrm.py"]
+ return ["mobidedrm.py"]
else:
argvencoding = sys.stdin.encoding
if argvencoding == None:
record = decryptRecord(data,PID)
fields = unpack('3sB8sB8s3s',record)
if fields[0] != 'PID' or fields[5] != 'pid' :
- raise DrmException(u"Didn't find PID magic numbers in record")
+ raise DrmException("Didn't find PID magic numbers in record")
elif fields[1] != 8 or fields[3] != 8 :
- raise DrmException(u"Record didn't contain correct length fields")
+ raise DrmException("Record didn't contain correct length fields")
elif fields[2] != PID :
- raise DrmException(u"Record didn't contain PID")
+ raise DrmException("Record didn't contain PID")
return fields[4]
# Decrypt all dkey records (contain the book PID)
pass
data = data[1+length:]
if len(records) == 0:
- raise DrmException(u"BookKey Not Found")
+ raise DrmException("BookKey Not Found")
return records
self.bookKey = None
magic = unpack('4s',self.fo.read(4))[0]
if magic != 'TPZ0':
- raise DrmException(u"Parse Error : Invalid Header, not a Topaz file")
+ raise DrmException("Parse Error : Invalid Header, not a Topaz file")
self.parseTopazHeaders()
self.parseMetadata()
# Read and parse one header record at the current book file position and return the associated data
# [[offset,decompressedLength,compressedLength],...]
if ord(self.fo.read(1)) != 0x63:
- raise DrmException(u"Parse Error : Invalid Header")
+ raise DrmException("Parse Error : Invalid Header")
tag = bookReadString(self.fo)
record = bookReadHeaderRecordData()
return [tag,record]
if debug: print(result[0], ": ", result[1])
self.bookHeaderRecords[result[0]] = result[1]
if ord(self.fo.read(1)) != 0x64 :
- raise DrmException(u"Parse Error : Invalid Header")
+ raise DrmException("Parse Error : Invalid Header")
self.bookPayloadOffset = self.fo.tell()
def parseMetadata(self):
self.fo.seek(self.bookPayloadOffset + self.bookHeaderRecords['metadata'][0][0])
tag = bookReadString(self.fo)
if tag != 'metadata' :
- raise DrmException(u"Parse Error : Record Names Don't Match")
+ raise DrmException("Parse Error : Record Names Don't Match")
flags = ord(self.fo.read(1))
nbRecords = ord(self.fo.read(1))
if debug: print("Metadata Records: %d" % nbRecords)
try:
keydata = self.getBookPayloadRecord('dkey', 0)
except DrmException as e:
- print(u"no dkey record found, book may not be encrypted")
- print(u"attempting to extrct files without a book key")
+ print("no dkey record found, book may not be encrypted")
+ print("attempting to extrct files without a book key")
self.createBookDirectory()
self.extractFiles()
- print(u"Successfully Extracted Topaz contents")
+ print("Successfully Extracted Topaz contents")
if inCalibre:
from calibre_plugins.dedrm import genbook
else:
rv = genbook.generateBook(self.outdir, raw, fixedimage)
if rv == 0:
- print(u"Book Successfully generated.")
+ print("Book Successfully generated.")
return rv
# try each pid to decode the file
for pid in pidlst:
# use 8 digit pids here
pid = pid[0:8]
- print(u"Trying: {0}".format(pid))
+ print("Trying: {0}".format(pid))
bookKeys = []
data = keydata
try:
pass
else:
bookKey = bookKeys[0]
- print(u"Book Key Found! ({0})".format(bookKey.encode('hex')))
+ print("Book Key Found! ({0})".format(bookKey.encode('hex')))
break
if not bookKey:
- raise DrmException(u"No key found in {0:d} keys tried. Read the FAQs at Harper's repository: https://github.com/apprenticeharper/DeDRM_tools/blob/master/FAQs.md".format(len(pidlst)))
+ raise DrmException("No key found in {0:d} keys tried. Read the FAQs at Harper's repository: https://github.com/apprenticeharper/DeDRM_tools/blob/master/FAQs.md".format(len(pidlst)))
self.setBookKey(bookKey)
self.createBookDirectory()
self.extractFiles()
- print(u"Successfully Extracted Topaz contents")
+ print("Successfully Extracted Topaz contents")
if inCalibre:
from calibre_plugins.dedrm import genbook
else:
rv = genbook.generateBook(self.outdir, raw, fixedimage)
if rv == 0:
- print(u"Book Successfully generated")
+ print("Book Successfully generated")
return rv
def createBookDirectory(self):
# create output directory structure
if not os.path.exists(outdir):
os.makedirs(outdir)
- destdir = os.path.join(outdir,u"img")
+ destdir = os.path.join(outdir,"img")
if not os.path.exists(destdir):
os.makedirs(destdir)
- destdir = os.path.join(outdir,u"color_img")
+ destdir = os.path.join(outdir,"color_img")
if not os.path.exists(destdir):
os.makedirs(destdir)
- destdir = os.path.join(outdir,u"page")
+ destdir = os.path.join(outdir,"page")
if not os.path.exists(destdir):
os.makedirs(destdir)
- destdir = os.path.join(outdir,u"glyphs")
+ destdir = os.path.join(outdir,"glyphs")
if not os.path.exists(destdir):
os.makedirs(destdir)
for headerRecord in self.bookHeaderRecords:
name = headerRecord
if name != 'dkey':
- ext = u".dat"
- if name == 'img': ext = u".jpg"
- if name == 'color' : ext = u".jpg"
- print(u"Processing Section: {0}\n. . .".format(name), end=' ')
+ ext = ".dat"
+ if name == 'img': ext = ".jpg"
+ if name == 'color' : ext = ".jpg"
+ print("Processing Section: {0}\n. . .".format(name), end=' ')
for index in range (0,len(self.bookHeaderRecords[name])) :
- fname = u"{0}{1:04d}{2}".format(name,index,ext)
+ fname = "{0}{1:04d}{2}".format(name,index,ext)
destdir = outdir
if name == 'img':
- destdir = os.path.join(outdir,u"img")
+ destdir = os.path.join(outdir,"img")
if name == 'color':
- destdir = os.path.join(outdir,u"color_img")
+ destdir = os.path.join(outdir,"color_img")
if name == 'page':
- destdir = os.path.join(outdir,u"page")
+ destdir = os.path.join(outdir,"page")
if name == 'glyphs':
- destdir = os.path.join(outdir,u"glyphs")
+ destdir = os.path.join(outdir,"glyphs")
outputFile = os.path.join(destdir,fname)
- print(u".", end=' ')
+ print(".", end=' ')
record = self.getBookPayloadRecord(name,index)
if record != '':
open(outputFile, 'wb').write(record)
- print(u" ")
+ print(" ")
def getFile(self, zipname):
htmlzip = zipfile.ZipFile(zipname,'w',zipfile.ZIP_DEFLATED, False)
- htmlzip.write(os.path.join(self.outdir,u"book.html"),u"book.html")
- htmlzip.write(os.path.join(self.outdir,u"book.opf"),u"book.opf")
- if os.path.isfile(os.path.join(self.outdir,u"cover.jpg")):
- htmlzip.write(os.path.join(self.outdir,u"cover.jpg"),u"cover.jpg")
- htmlzip.write(os.path.join(self.outdir,u"style.css"),u"style.css")
- zipUpDir(htmlzip, self.outdir, u"img")
+ htmlzip.write(os.path.join(self.outdir,"book.html"),"book.html")
+ htmlzip.write(os.path.join(self.outdir,"book.opf"),"book.opf")
+ if os.path.isfile(os.path.join(self.outdir,"cover.jpg")):
+ htmlzip.write(os.path.join(self.outdir,"cover.jpg"),"cover.jpg")
+ htmlzip.write(os.path.join(self.outdir,"style.css"),"style.css")
+ zipUpDir(htmlzip, self.outdir, "img")
htmlzip.close()
def getBookType(self):
- return u"Topaz"
+ return "Topaz"
def getBookExtension(self):
- return u".htmlz"
+ return ".htmlz"
def getSVGZip(self, zipname):
svgzip = zipfile.ZipFile(zipname,'w',zipfile.ZIP_DEFLATED, False)
- svgzip.write(os.path.join(self.outdir,u"index_svg.xhtml"),u"index_svg.xhtml")
- zipUpDir(svgzip, self.outdir, u"svg")
- zipUpDir(svgzip, self.outdir, u"img")
+ svgzip.write(os.path.join(self.outdir,"index_svg.xhtml"),"index_svg.xhtml")
+ zipUpDir(svgzip, self.outdir, "svg")
+ zipUpDir(svgzip, self.outdir, "img")
svgzip.close()
def cleanup(self):
shutil.rmtree(self.outdir, True)
def usage(progname):
- print(u"Removes DRM protection from Topaz ebooks and extracts the contents")
- print(u"Usage:")
- print(u" {0} [-k <kindle.k4i>] [-p <comma separated PIDs>] [-s <comma separated Kindle serial numbers>] <infile> <outdir>".format(progname))
+ print("Removes DRM protection from Topaz ebooks and extracts the contents")
+ print("Usage:")
+ print(" {0} [-k <kindle.k4i>] [-p <comma separated PIDs>] [-s <comma separated Kindle serial numbers>] <infile> <outdir>".format(progname))
# Main
def cli_main():
argv=unicode_argv()
progname = os.path.basename(argv[0])
- print(u"TopazExtract v{0}.".format(__version__))
+ print("TopazExtract v{0}.".format(__version__))
try:
opts, args = getopt.getopt(argv[1:], "k:p:s:x")
except getopt.GetoptError as err:
- print(u"Error in options or arguments: {0}".format(err.args[0]))
+ print("Error in options or arguments: {0}".format(err.args[0]))
usage(progname)
return 1
if len(args)<2:
infile = args[0]
outdir = args[1]
if not os.path.isfile(infile):
- print(u"Input File {0} Does Not Exist.".format(infile))
+ print("Input File {0} Does Not Exist.".format(infile))
return 1
if not os.path.exists(outdir):
- print(u"Output Directory {0} Does Not Exist.".format(outdir))
+ print("Output Directory {0} Does Not Exist.".format(outdir))
return 1
kDatabaseFiles = []
tb = TopazBook(infile)
title = tb.getBookTitle()
- print(u"Processing Book: {0}".format(title))
+ print("Processing Book: {0}".format(title))
md1, md2 = tb.getPIDMetaInfo()
pids.extend(kgenpids.getPidList(md1, md2, serials, kDatabaseFiles))
try:
- print(u"Decrypting Book")
+ print("Decrypting Book")
tb.processBook(pids)
- print(u" Creating HTML ZIP Archive")
- zipname = os.path.join(outdir, bookname + u"_nodrm.htmlz")
+ print(" Creating HTML ZIP Archive")
+ zipname = os.path.join(outdir, bookname + "_nodrm.htmlz")
tb.getFile(zipname)
- print(u" Creating SVG ZIP Archive")
- zipname = os.path.join(outdir, bookname + u"_SVG.zip")
+ print(" Creating SVG ZIP Archive")
+ zipname = os.path.join(outdir, bookname + "_SVG.zip")
tb.getSVGZip(zipname)
# removing internal temporary directory of pieces
tb.cleanup()
except DrmException as e:
- print(u"Decryption failed\n{0}".format(traceback.format_exc()))
+ print("Decryption failed\n{0}".format(traceback.format_exc()))
try:
tb.cleanup()
return 1
except Exception as e:
- print(u"Decryption failed\n{0}".format(traceback.format_exc()))
+ print("Decryption failed\n{0}".format(traceback.format_exc()))
try:
tb.cleanup()
except:
-#!/usr/bin/env python
+#!/usr/bin/env python3
# -*- coding: utf-8 -*-
-from __future__ import with_statement
-
from calibre_plugins.dedrm.ignoblekeygen import generate_key
__license__ = 'GPL v3'
-#!/usr/bin/env python
+#!/usr/bin/env python3
# -*- coding: utf-8 -*-
-from __future__ import with_statement
-from __future__ import print_function
-
__license__ = 'GPL v3'
# Standard Python modules.
import subasyncio
from subasyncio import Process
- if extension == u".k4i":
+ if extension == ".k4i":
import json
basepath, script = os.path.split(scriptpath)
- print(u"{0} v{1}: Running {2} under Wine".format(PLUGIN_NAME, PLUGIN_VERSION, script))
+ print("{0} v{1}: Running {2} under Wine".format(PLUGIN_NAME, PLUGIN_VERSION, script))
- outdirpath = os.path.join(basepath, u"winekeysdir")
+ outdirpath = os.path.join(basepath, "winekeysdir")
if not os.path.exists(outdirpath):
os.makedirs(outdirpath)
wineprefix = os.path.abspath(os.path.expanduser(os.path.expandvars(wineprefix)))
if wineprefix != "" and os.path.exists(wineprefix):
- cmdline = u"WINEPREFIX=\"{2}\" wine python.exe \"{0}\" \"{1}\"".format(scriptpath,outdirpath,wineprefix)
+ cmdline = "WINEPREFIX=\"{2}\" wine python.exe \"{0}\" \"{1}\"".format(scriptpath,outdirpath,wineprefix)
else:
- cmdline = u"wine python.exe \"{0}\" \"{1}\"".format(scriptpath,outdirpath)
- print(u"{0} v{1}: Command line: '{2}'".format(PLUGIN_NAME, PLUGIN_VERSION, cmdline))
+ cmdline = "wine python.exe \"{0}\" \"{1}\"".format(scriptpath,outdirpath)
+ print("{0} v{1}: Command line: '{2}'".format(PLUGIN_NAME, PLUGIN_VERSION, cmdline))
try:
cmdline = cmdline.encode(sys.getfilesystemencoding())
p2 = Process(cmdline, shell=True, bufsize=1, stdin=None, stdout=sys.stdout, stderr=STDOUT, close_fds=False)
result = p2.wait("wait")
except Exception as e:
- print(u"{0} v{1}: Wine subprocess call error: {2}".format(PLUGIN_NAME, PLUGIN_VERSION, e.args[0]))
+ print("{0} v{1}: Wine subprocess call error: {2}".format(PLUGIN_NAME, PLUGIN_VERSION, e.args[0]))
if wineprefix != "" and os.path.exists(wineprefix):
- cmdline = u"WINEPREFIX=\"{2}\" wine C:\\Python27\\python.exe \"{0}\" \"{1}\"".format(scriptpath,outdirpath,wineprefix)
+ cmdline = "WINEPREFIX=\"{2}\" wine C:\\Python27\\python.exe \"{0}\" \"{1}\"".format(scriptpath,outdirpath,wineprefix)
else:
- cmdline = u"wine C:\\Python27\\python.exe \"{0}\" \"{1}\"".format(scriptpath,outdirpath)
- print(u"{0} v{1}: Command line: “{2}”".format(PLUGIN_NAME, PLUGIN_VERSION, cmdline))
+ cmdline = "wine C:\\Python27\\python.exe \"{0}\" \"{1}\"".format(scriptpath,outdirpath)
+ print("{0} v{1}: Command line: “{2}”".format(PLUGIN_NAME, PLUGIN_VERSION, cmdline))
try:
cmdline = cmdline.encode(sys.getfilesystemencoding())
p2 = Process(cmdline, shell=True, bufsize=1, stdin=None, stdout=sys.stdout, stderr=STDOUT, close_fds=False)
result = p2.wait("wait")
except Exception as e:
- print(u"{0} v{1}: Wine subprocess call error: {2}".format(PLUGIN_NAME, PLUGIN_VERSION, e.args[0]))
+ print("{0} v{1}: Wine subprocess call error: {2}".format(PLUGIN_NAME, PLUGIN_VERSION, e.args[0]))
# try finding winekeys anyway, even if above code errored
winekeys = []
try:
fpath = os.path.join(outdirpath, filename)
with open(fpath, 'rb') as keyfile:
- if extension == u".k4i":
+ if extension == ".k4i":
new_key_value = json.loads(keyfile.read())
else:
new_key_value = keyfile.read()
winekeys.append(new_key_value)
except:
- print(u"{0} v{1}: Error loading file {2}".format(PLUGIN_NAME, PLUGIN_VERSION, filename))
+ print("{0} v{1}: Error loading file {2}".format(PLUGIN_NAME, PLUGIN_VERSION, filename))
traceback.print_exc()
os.remove(fpath)
- print(u"{0} v{1}: Found and decrypted {2} {3}".format(PLUGIN_NAME, PLUGIN_VERSION, len(winekeys), u"key file" if len(winekeys) == 1 else u"key files"))
+ print("{0} v{1}: Found and decrypted {2} {3}".format(PLUGIN_NAME, PLUGIN_VERSION, len(winekeys), "key file" if len(winekeys) == 1 else "key files"))
return winekeys
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+
"""
Read and write ZIP files.
"""
def open(self, name, mode="r", pwd=None):
"""Return file-like object for 'name'."""
- if mode not in ("r", "U", "rU"):
- raise RuntimeError('open() requires mode "r", "U", or "rU"')
+ if mode not in ("r", "", "rU"):
+ raise RuntimeError('open() requires mode "r", "", or "rU"')
if not self.fp:
raise RuntimeError(
"Attempt to read ZIP archive that was already closed")
-#!/usr/bin/env python
+#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# zipfix.py
-# Copyright © 2010-2020 by some_updates, DiapDealer and Apprentice Alf
+# Copyright © 2010-2020 by Apprentice Harper et al.
# Released under the terms of the GNU General Public Licence, version 3
# <http://www.gnu.org/licenses/>
# Revision history:
# 1.0 - Initial release
# 1.1 - Updated to handle zip file metadata correctly
-# 2.0 - Added Python 3 compatibility for calibre 5.0
+# 2.0 - Python 3 for calibre 5.0
"""
Re-write zip (or ePub) fixing problems with file names (and mimetype entry).
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
-from __future__ import (unicode_literals, division, absolute_import,
- print_function)
-__license__ = 'GPL v3'
+_license__ = 'GPL v3'
__docformat__ = 'restructuredtext en'
-#!/usr/bin/env python
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
-from __future__ import (unicode_literals, division, absolute_import,
- print_function)
__license__ = 'GPL v3'
__copyright__ = '2012, David Forrester <davidfor@internode.on.net>'
import os, time, re, sys
from datetime import datetime
-try:
- from PyQt5.Qt import (Qt, QIcon, QPixmap, QLabel, QDialog, QHBoxLayout, QProgressBar,
- QTableWidgetItem, QFont, QLineEdit, QComboBox,
- QVBoxLayout, QDialogButtonBox, QStyledItemDelegate, QDateTime,
- QRegExpValidator, QRegExp, QDate, QDateEdit)
-except ImportError:
- from PyQt4.Qt import (Qt, QIcon, QPixmap, QLabel, QDialog, QHBoxLayout, QProgressBar,
+from PyQt5.Qt import (Qt, QIcon, QPixmap, QLabel, QDialog, QHBoxLayout, QProgressBar,
QTableWidgetItem, QFont, QLineEdit, QComboBox,
QVBoxLayout, QDialogButtonBox, QStyledItemDelegate, QDateTime,
QRegExpValidator, QRegExp, QDate, QDateEdit)
-# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
-from __future__ import (unicode_literals, division, absolute_import,
- print_function)
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
-try:
- from PyQt5.Qt import (Qt, QGroupBox, QListWidget, QLineEdit, QDialogButtonBox, QWidget, QLabel, QDialog, QVBoxLayout, QAbstractItemView, QIcon, QHBoxLayout, QComboBox, QListWidgetItem, QFileDialog)
-except ImportError:
- from PyQt4.Qt import (Qt, QGroupBox, QListWidget, QLineEdit, QDialogButtonBox, QWidget, QLabel, QDialog, QVBoxLayout, QAbstractItemView, QIcon, QHBoxLayout, QComboBox, QListWidgetItem, QFileDialog)
+# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
-try:
- from PyQt5 import Qt as QtGui
-except ImportError:
- from PyQt4 import QtGui
+from PyQt5.Qt import (Qt, QGroupBox, QListWidget, QLineEdit, QDialogButtonBox, QWidget, QLabel, QDialog, QVBoxLayout, QAbstractItemView, QIcon, QHBoxLayout, QComboBox, QListWidgetItem, QFileDialog)
+from PyQt5 import Qt as QtGui
from calibre.gui2 import (error_dialog, question_dialog, info_dialog, open_url)
from calibre.utils.config import JSONConfig, config_dir
self.find_homes.setCurrentIndex(index)
self.serials_button = QtGui.QPushButton(self)
- self.serials_button.setToolTip(_(u"Click to manage Kobo serial numbers for Kobo ebooks"))
- self.serials_button.setText(u"Kobo devices serials")
+ self.serials_button.setToolTip(_("Click to manage Kobo serial numbers for Kobo ebooks"))
+ self.serials_button.setText("Kobo devices serials")
self.serials_button.clicked.connect(self.edit_serials)
layout.addWidget(self.serials_button)
self.kobo_directory_button = QtGui.QPushButton(self)
- self.kobo_directory_button.setToolTip(_(u"Click to specify the Kobo directory"))
- self.kobo_directory_button.setText(u"Kobo directory")
+ self.kobo_directory_button.setToolTip(_("Click to specify the Kobo directory"))
+ self.kobo_directory_button.setText("Kobo directory")
self.kobo_directory_button.clicked.connect(self.edit_kobo_directory)
layout.addWidget(self.kobo_directory_button)
def edit_serials(self):
- d = ManageKeysDialog(self,u"Kobo device serial number",self.tmpserials, AddSerialDialog)
+ d = ManageKeysDialog(self,"Kobo device serial number",self.tmpserials, AddSerialDialog)
d.exec_()
def edit_kobo_directory(self):
- tmpkobodirectory = QFileDialog.getExistingDirectory(self, u"Select Kobo directory", self.kobodirectory or "/home", QFileDialog.ShowDirsOnly)
+ tmpkobodirectory = QFileDialog.getExistingDirectory(self, "Select Kobo directory", self.kobodirectory or "/home", QFileDialog.ShowDirsOnly)
if tmpkobodirectory != u"" and tmpkobodirectory is not None:
self.kobodirectory = tmpkobodirectory
self.plugin_keys = plugin_keys
self.create_key = create_key
self.keyfile_ext = keyfile_ext
- self.json_file = (keyfile_ext == u"k4i")
+ self.json_file = (keyfile_ext == "k4i")
self.setWindowTitle("{0} {1}: Manage {2}s".format(PLUGIN_NAME, PLUGIN_VERSION, self.key_type_name))
layout = QVBoxLayout(self)
self.setLayout(layout)
- keys_group_box = QGroupBox(_(u"{0}s".format(self.key_type_name)), self)
+ keys_group_box = QGroupBox(_("{0}s".format(self.key_type_name)), self)
layout.addWidget(keys_group_box)
keys_group_box_layout = QHBoxLayout()
keys_group_box.setLayout(keys_group_box_layout)
self.listy = QListWidget(self)
- self.listy.setToolTip(u"{0}s that will be used to decrypt ebooks".format(self.key_type_name))
+ self.listy.setToolTip("{0}s that will be used to decrypt ebooks".format(self.key_type_name))
self.listy.setSelectionMode(QAbstractItemView.SingleSelection)
self.populate_list()
keys_group_box_layout.addWidget(self.listy)
keys_group_box_layout.addLayout(button_layout)
self._add_key_button = QtGui.QToolButton(self)
self._add_key_button.setIcon(QIcon(I('plus.png')))
- self._add_key_button.setToolTip(u"Create new {0}".format(self.key_type_name))
+ self._add_key_button.setToolTip("Create new {0}".format(self.key_type_name))
self._add_key_button.clicked.connect(self.add_key)
button_layout.addWidget(self._add_key_button)
self._delete_key_button = QtGui.QToolButton(self)
- self._delete_key_button.setToolTip(_(u"Delete highlighted key"))
+ self._delete_key_button.setToolTip(_("Delete highlighted key"))
self._delete_key_button.setIcon(QIcon(I('list_remove.png')))
self._delete_key_button.clicked.connect(self.delete_key)
button_layout.addWidget(self._delete_key_button)
new_key_value = d.key_value
if new_key_value in self.plugin_keys:
info_dialog(None, "{0} {1}: Duplicate {2}".format(PLUGIN_NAME, PLUGIN_VERSION,self.key_type_name),
- u"This {0} is already in the list of {0}s has not been added.".format(self.key_type_name), show=True)
+ "This {0} is already in the list of {0}s has not been added.".format(self.key_type_name), show=True)
return
self.plugin_keys.append(d.key_value)
if not self.listy.currentItem():
return
keyname = self.listy.currentItem().text()
- if not question_dialog(self, "{0} {1}: Confirm Delete".format(PLUGIN_NAME, PLUGIN_VERSION), u"Do you really want to delete the {1} <strong>{0}</strong>?".format(keyname, self.key_type_name), show_copy_button=False, default_yes=False):
+ if not question_dialog(self, "{0} {1}: Confirm Delete".format(PLUGIN_NAME, PLUGIN_VERSION), "Do you really want to delete the {1} <strong>{0}</strong>?".format(keyname, self.key_type_name), show_copy_button=False, default_yes=False):
return
self.plugin_keys.remove(keyname)
def __init__(self, parent=None,):
QDialog.__init__(self, parent)
self.parent = parent
- self.setWindowTitle(u"{0} {1}: Add New eInk Kobo Serial Number".format(PLUGIN_NAME, PLUGIN_VERSION))
+ self.setWindowTitle("{0} {1}: Add New eInk Kobo Serial Number".format(PLUGIN_NAME, PLUGIN_VERSION))
layout = QVBoxLayout(self)
self.setLayout(layout)
key_group = QHBoxLayout()
data_group_box_layout.addLayout(key_group)
- key_group.addWidget(QLabel(u"EInk Kobo Serial Number:", self))
+ key_group.addWidget(QLabel("EInk Kobo Serial Number:", self))
self.key_ledit = QLineEdit("", self)
- self.key_ledit.setToolTip(u"Enter an eInk Kobo serial number. EInk Kobo serial numbers are 13 characters long and usually start with a 'N'. Kobo Serial Numbers are case-sensitive, so be sure to enter the upper and lower case letters unchanged.")
+ self.key_ledit.setToolTip("Enter an eInk Kobo serial number. EInk Kobo serial numbers are 13 characters long and usually start with a 'N'. Kobo Serial Numbers are case-sensitive, so be sure to enter the upper and lower case letters unchanged.")
key_group.addWidget(self.key_ledit)
self.button_box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
def accept(self):
if len(self.key_name) == 0 or self.key_name.isspace():
- errmsg = u"Please enter an eInk Kindle Serial Number or click Cancel in the dialog."
+ errmsg = "Please enter an eInk Kindle Serial Number or click Cancel in the dialog."
return error_dialog(None, "{0} {1}".format(PLUGIN_NAME, PLUGIN_VERSION), errmsg, show=True, show_copy_button=False)
if len(self.key_name) != 13:
- errmsg = u"EInk Kobo Serial Numbers must be 13 characters long. This is {0:d} characters long.".format(len(self.key_name))
+ errmsg = "EInk Kobo Serial Numbers must be 13 characters long. This is {0:d} characters long.".format(len(self.key_name))
return error_dialog(None, "{0} {1}".format(PLUGIN_NAME, PLUGIN_VERSION), errmsg, show=True, show_copy_button=False)
QDialog.accept(self)
-#!/usr/bin/env python
+#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Version 4.0.0 September 2020
from __future__ import print_function
__version__ = '4.0.0'
-__about__ = u"Obok v{0}\nCopyright © 2012-2020 Physisticated et al.".format(__version__)
+__about__ = "Obok v{0}\nCopyright © 2012-2020 Physisticated et al.".format(__version__)
import sys
import os
can_parse_xml = True
try:
from xml.etree import ElementTree as ET
- # print u"using xml.etree for xml parsing"
+ # print "using xml.etree for xml parsing"
except ImportError:
can_parse_xml = False
- # print u"Cannot find xml.etree, disabling extraction of serial numbers"
+ # print "Cannot find xml.etree, disabling extraction of serial numbers"
# List of all known hash keys
KOBO_HASH_KEYS = ['88b3a2e13', 'XzUhGYdFp', 'NoCanLook','QJhwzAtXL']
if self.encoding == None:
self.encoding = "utf-8"
def write(self, data):
- if isinstance(data,bytes):
+ if isinstance(data,str):
data = data.encode(self.encoding,"replace")
- self.stream.write(data)
- self.stream.flush()
+ self.stream.buffer.write(data)
+ self.stream.buffer.flush()
def __getattr__(self, attr):
return getattr(self.stream, attr)
# step 1. check whether this looks like a real device
if (device_path):
# we got a device path
- self.kobodir = os.path.join(device_path, u".kobo")
+ self.kobodir = os.path.join(device_path, ".kobo")
# devices use KoboReader.sqlite
- kobodb = os.path.join(self.kobodir, u"KoboReader.sqlite")
+ kobodb = os.path.join(self.kobodir, "KoboReader.sqlite")
if (not(os.path.isfile(kobodb))):
# device path seems to be wrong, unset it
device_path = u""
if (len(serials) == 0):
# we got a device path but no saved serial
# try to get the serial from the device
- # print u"get_device_settings - device_path = {0}".format(device_path)
+ # print "get_device_settings - device_path = {0}".format(device_path)
# get serial from device_path/.adobe-digital-editions/device.xml
if can_parse_xml:
devicexml = os.path.join(device_path, '.adobe-digital-editions', 'device.xml')
- # print u"trying to load {0}".format(devicexml)
+ # print "trying to load {0}".format(devicexml)
if (os.path.exists(devicexml)):
- # print u"trying to parse {0}".format(devicexml)
+ # print "trying to parse {0}".format(devicexml)
xmltree = ET.parse(devicexml)
for node in xmltree.iter():
if "deviceSerial" in node.tag:
serial = node.text
- # print u"found serial {0}".format(serial)
+ # print "found serial {0}".format(serial)
serials.append(serial)
break
else:
- # print u"cannot get serials from device."
+ # print "cannot get serials from device."
device_path = u""
self.kobodir = u""
kobodb = u""
if sys.getwindowsversion().major > 5:
if 'LOCALAPPDATA' in os.environ.keys():
# Python 2.x does not return unicode env. Use Python 3.x
- self.kobodir = winreg.ExpandEnvironmentStrings(u"%LOCALAPPDATA%")
+ self.kobodir = winreg.ExpandEnvironmentStrings("%LOCALAPPDATA%")
if (self.kobodir == u""):
if 'USERPROFILE' in os.environ.keys():
# Python 2.x does not return unicode env. Use Python 3.x
- self.kobodir = os.path.join(winreg.ExpandEnvironmentStrings(u"%USERPROFILE%"), u"Local Settings", u"Application Data")
- self.kobodir = os.path.join(self.kobodir, u"Kobo", u"Kobo Desktop Edition")
+ self.kobodir = os.path.join(winreg.ExpandEnvironmentStrings("%USERPROFILE%"), "Local Settings", "Application Data")
+ self.kobodir = os.path.join(self.kobodir, "Kobo", "Kobo Desktop Edition")
elif sys.platform.startswith('darwin'):
- self.kobodir = os.path.join(os.environ['HOME'], u"Library", u"Application Support", u"Kobo", u"Kobo Desktop Edition")
+ self.kobodir = os.path.join(os.environ['HOME'], "Library", "Application Support", "Kobo", "Kobo Desktop Edition")
#elif linux_path != None:
# Probably Linux, let's get the wine prefix and path to Kobo.
- # self.kobodir = os.path.join(linux_path, u"Local Settings", u"Application Data", u"Kobo", u"Kobo Desktop Edition")
+ # self.kobodir = os.path.join(linux_path, "Local Settings", "Application Data", "Kobo", "Kobo Desktop Edition")
# desktop versions use Kobo.sqlite
- kobodb = os.path.join(self.kobodir, u"Kobo.sqlite")
+ kobodb = os.path.join(self.kobodir, "Kobo.sqlite")
# check for existence of file
if (not(os.path.isfile(kobodb))):
# give up here, we haven't found anything useful
kobodb = u""
if (self.kobodir != u""):
- self.bookdir = os.path.join(self.kobodir, u"kepub")
+ self.bookdir = os.path.join(self.kobodir, "kepub")
# make a copy of the database in a temporary file
# so we can ensure it's not using WAL logging which sqlite3 can't do.
self.newdb = tempfile.NamedTemporaryFile(mode='wb', delete=False)
def __bookfile (self, volumeid):
"""The filename needed to open a given book."""
- return os.path.join(self.kobodir, u"kepub", volumeid)
+ return os.path.join(self.kobodir, "kepub", volumeid)
def __getmacaddrs (self):
"""The list of all MAC addresses on this machine."""
output = subprocess.check_output('/sbin/ifconfig -a', shell=True)
matches = c.findall(output)
for m in matches:
- # print u"m:{0}".format(m[0])
+ # print "m:{0}".format(m[0])
macaddrs.append(m[0].upper())
else:
# probably linux
# assume utf-8 with no BOM
textoffset = 0
stride = 1
- print(u"Checking text:{0}:".format(contents[:10]))
+ print("Checking text:{0}:".format(contents[:10]))
# check for byte order mark
if contents[:3]==b"\xef\xbb\xbf":
# seems to be utf-8 with BOM
- print(u"Could be utf-8 with BOM")
+ print("Could be utf-8 with BOM")
textoffset = 3
elif contents[:2]==b"\xfe\xff":
# seems to be utf-16BE
- print(u"Could be utf-16BE")
+ print("Could be utf-16BE")
textoffset = 3
stride = 2
elif contents[:2]==b"\xff\xfe":
# seems to be utf-16LE
- print(u"Could be utf-16LE")
+ print("Could be utf-16LE")
textoffset = 2
stride = 2
else:
- print(u"Perhaps utf-8 without BOM")
+ print("Perhaps utf-8 without BOM")
# now check that the first few characters are in the ASCII range
for i in range(textoffset,textoffset+5*stride,stride):
if contents[i]<32 or contents[i]>127:
# Non-ascii, so decryption probably failed
- print(u"Bad character at {0}, value {1}".format(i,contents[i]))
+ print("Bad character at {0}, value {1}".format(i,contents[i]))
raise ValueError
- print(u"Seems to be good text")
+ print("Seems to be good text")
return True
if contents[:5]==b"<?xml" or contents[:8]==b"\xef\xbb\xbf<?xml":
# utf-8
# utf-16LE of weird <!DOCTYPE start
return True
else:
- print(u"Bad XML: {0}".format(contents[:8]))
+ print("Bad XML: {0}".format(contents[:8]))
raise ValueError
elif self.mimetype == 'image/jpeg':
if contents[:3] == b'\xff\xd8\xff':
return True
else:
- print(u"Bad JPEG: {0}".format(contents[:3].hex()))
+ print("Bad JPEG: {0}".format(contents[:3].hex()))
raise ValueError()
return False
return contents
def decrypt_book(book, lib):
- print(u"Converting {0}".format(book.title))
+ print("Converting {0}".format(book.title))
zin = zipfile.ZipFile(book.filename, "r")
# make filename out of Unicode alphanumeric and whitespace equivalents from title
- outname = u"{0}.epub".format(re.sub('[^\s\w]', '_', book.title, 0, re.UNICODE))
+ outname = "{0}.epub".format(re.sub('[^\s\w]', '_', book.title, 0, re.UNICODE))
if (book.type == 'drm-free'):
- print(u"DRM-free book, conversion is not needed")
+ print("DRM-free book, conversion is not needed")
shutil.copyfile(book.filename, outname)
- print(u"Book saved as {0}".format(os.path.join(os.getcwd(), outname)))
+ print("Book saved as {0}".format(os.path.join(os.getcwd(), outname)))
return 0
result = 1
for userkey in lib.userkeys:
- print(u"Trying key: {0}".format(userkey.hex()))
+ print("Trying key: {0}".format(userkey.hex()))
try:
zout = zipfile.ZipFile(outname, "w", zipfile.ZIP_DEFLATED)
for filename in zin.namelist():
file.check(contents)
zout.writestr(filename, contents)
zout.close()
- print(u"Decryption succeeded.")
- print(u"Book saved as {0}".format(os.path.join(os.getcwd(), outname)))
+ print("Decryption succeeded.")
+ print("Book saved as {0}".format(os.path.join(os.getcwd(), outname)))
result = 0
break
except ValueError:
- print(u"Decryption failed.")
+ print("Decryption failed.")
zout.close()
os.remove(outname)
zin.close()
def cli_main():
description = __about__
- epilog = u"Parsing of arguments failed."
+ epilog = "Parsing of arguments failed."
parser = argparse.ArgumentParser(prog=sys.argv[0], description=description, epilog=epilog)
parser.add_argument('--devicedir', default='/media/KOBOeReader', help="directory of connected Kobo device")
parser.add_argument('--all', action='store_true', help="flag for converting all books on device")
books = lib.books
else:
for i, book in enumerate(lib.books):
- print(u"{0}: {1}".format(i + 1, book.title))
- print(u"Or 'all'")
+ print("{0}: {1}".format(i + 1, book.title))
+ print("Or 'all'")
- choice = input(u"Convert book number... ")
- if choice == u'all':
+ choice = input("Convert book number... ")
+ if choice == "all":
books = list(lib.books)
else:
try:
num = int(choice)
books = [lib.books[num - 1]]
except (ValueError, IndexError):
- print(u"Invalid choice. Exiting...")
+ print("Invalid choice. Exiting...")
exit()
results = [decrypt_book(book, lib) for book in books]
lib.close()
overall_result = all(result != 0 for result in results)
if overall_result != 0:
- print(u"Could not decrypt book with any of the keys found.")
+ print("Could not decrypt book with any of the keys found.")
return overall_result
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
-from __future__ import (unicode_literals, division, absolute_import,
- print_function)
__license__ = 'GPL v3'
__docformat__ = 'restructuredtext en'
from io import StringIO
from traceback import print_exc
-try:
- from PyQt5.Qt import (Qt, QDialog, QPixmap, QIcon, QLabel, QHBoxLayout, QFont, QTableWidgetItem)
-except ImportError:
- from PyQt4.Qt import (Qt, QDialog, QPixmap, QIcon, QLabel, QHBoxLayout, QFont, QTableWidgetItem)
+from PyQt5.Qt import (Qt, QDialog, QPixmap, QIcon, QLabel, QHBoxLayout, QFont, QTableWidgetItem)
from calibre.utils.config import config_dir
from calibre.constants import iswindows, DEBUG
from __future__ import print_function
__version__ = '3.2.4'
-__about__ = u"Obok v{0}\nCopyright © 2012-2016 Physisticated et al.".format(__version__)
+__about__ = "Obok v{0}\nCopyright © 2012-2016 Physisticated et al.".format(__version__)
import sys
import os
can_parse_xml = True
try:
from xml.etree import ElementTree as ET
- # print u"using xml.etree for xml parsing"
+ # print "using xml.etree for xml parsing"
except ImportError:
can_parse_xml = False
- # print u"Cannot find xml.etree, disabling extraction of serial numbers"
+ # print "Cannot find xml.etree, disabling extraction of serial numbers"
# List of all known hash keys
KOBO_HASH_KEYS = ['88b3a2e13', 'XzUhGYdFp', 'NoCanLook','QJhwzAtXL']
# step 1. check whether this looks like a real device
if (device_path):
# we got a device path
- self.kobodir = os.path.join(device_path, u".kobo")
+ self.kobodir = os.path.join(device_path, ".kobo")
# devices use KoboReader.sqlite
- kobodb = os.path.join(self.kobodir, u"KoboReader.sqlite")
+ kobodb = os.path.join(self.kobodir, "KoboReader.sqlite")
if (not(os.path.isfile(kobodb))):
# device path seems to be wrong, unset it
device_path = u""
if (len(serials) == 0):
# we got a device path but no saved serial
# try to get the serial from the device
- # print u"get_device_settings - device_path = {0}".format(device_path)
+ # print "get_device_settings - device_path = {0}".format(device_path)
# get serial from device_path/.adobe-digital-editions/device.xml
if can_parse_xml:
devicexml = os.path.join(device_path, '.adobe-digital-editions', 'device.xml')
- # print u"trying to load {0}".format(devicexml)
+ # print "trying to load {0}".format(devicexml)
if (os.path.exists(devicexml)):
- # print u"trying to parse {0}".format(devicexml)
+ # print "trying to parse {0}".format(devicexml)
xmltree = ET.parse(devicexml)
for node in xmltree.iter():
if "deviceSerial" in node.tag:
serial = node.text
- # print u"found serial {0}".format(serial)
+ # print "found serial {0}".format(serial)
serials.append(serial)
break
else:
- # print u"cannot get serials from device."
+ # print "cannot get serials from device."
device_path = u""
self.kobodir = u""
kobodb = u""
if sys.getwindowsversion().major > 5:
if 'LOCALAPPDATA' in os.environ.keys():
# Python 2.x does not return unicode env. Use Python 3.x
- self.kobodir = winreg.ExpandEnvironmentStrings(u"%LOCALAPPDATA%")
+ self.kobodir = winreg.ExpandEnvironmentStrings("%LOCALAPPDATA%")
if (self.kobodir == u""):
if 'USERPROFILE' in os.environ.keys():
# Python 2.x does not return unicode env. Use Python 3.x
- self.kobodir = os.path.join(winreg.ExpandEnvironmentStrings(u"%USERPROFILE%"), u"Local Settings", u"Application Data")
- self.kobodir = os.path.join(self.kobodir, u"Kobo", u"Kobo Desktop Edition")
+ self.kobodir = os.path.join(winreg.ExpandEnvironmentStrings("%USERPROFILE%"), "Local Settings", "Application Data")
+ self.kobodir = os.path.join(self.kobodir, "Kobo", "Kobo Desktop Edition")
elif sys.platform.startswith('darwin'):
- self.kobodir = os.path.join(os.environ['HOME'], u"Library", u"Application Support", u"Kobo", u"Kobo Desktop Edition")
+ self.kobodir = os.path.join(os.environ['HOME'], "Library", "Application Support", "Kobo", "Kobo Desktop Edition")
#elif linux_path != None:
# Probably Linux, let's get the wine prefix and path to Kobo.
- # self.kobodir = os.path.join(linux_path, u"Local Settings", u"Application Data", u"Kobo", u"Kobo Desktop Edition")
+ # self.kobodir = os.path.join(linux_path, "Local Settings", "Application Data", "Kobo", "Kobo Desktop Edition")
# desktop versions use Kobo.sqlite
- kobodb = os.path.join(self.kobodir, u"Kobo.sqlite")
+ kobodb = os.path.join(self.kobodir, "Kobo.sqlite")
# check for existence of file
if (not(os.path.isfile(kobodb))):
# give up here, we haven't found anything useful
if (self.kobodir != u""):
- self.bookdir = os.path.join(self.kobodir, u"kepub")
+ self.bookdir = os.path.join(self.kobodir, "kepub")
# make a copy of the database in a temporary file
# so we can ensure it's not using WAL logging which sqlite3 can't do.
self.newdb = tempfile.NamedTemporaryFile(mode='wb', delete=False)
def __bookfile (self, volumeid):
"""The filename needed to open a given book."""
- return os.path.join(self.kobodir, u"kepub", volumeid)
+ return os.path.join(self.kobodir, "kepub", volumeid)
def __getmacaddrs (self):
"""The list of all MAC addresses on this machine."""
output = subprocess.check_output('/sbin/ifconfig -a', shell=True)
matches = c.findall(output)
for m in matches:
- # print u"m:{0}".format(m[0])
+ # print "m:{0}".format(m[0])
macaddrs.append(m[0].upper())
elif sys.platform.startswith('linux'):
p_out = subprocess.check_output("ip -br link show | awk '{print $3}'", shell=True)
# assume utf-8 with no BOM
textoffset = 0
stride = 1
- print(u"Checking text:{0}:".format(contents[:10]))
+ print("Checking text:{0}:".format(contents[:10]))
# check for byte order mark
if contents[:3]=="\xef\xbb\xbf":
# seems to be utf-8 with BOM
- print(u"Could be utf-8 with BOM")
+ print("Could be utf-8 with BOM")
textoffset = 3
elif contents[:2]=="\xfe\xff":
# seems to be utf-16BE
- print(u"Could be utf-16BE")
+ print("Could be utf-16BE")
textoffset = 3
stride = 2
elif contents[:2]=="\xff\xfe":
# seems to be utf-16LE
- print(u"Could be utf-16LE")
+ print("Could be utf-16LE")
textoffset = 2
stride = 2
else:
- print(u"Perhaps utf-8 without BOM")
+ print("Perhaps utf-8 without BOM")
# now check that the first few characters are in the ASCII range
for i in xrange(textoffset,textoffset+5*stride,stride):
if ord(contents[i])<32 or ord(contents[i])>127:
# Non-ascii, so decryption probably failed
- print(u"Bad character at {0}, value {1}".format(i,ord(contents[i])))
+ print("Bad character at {0}, value {1}".format(i,ord(contents[i])))
raise ValueError
- print(u"Seems to be good text")
+ print("Seems to be good text")
return True
if contents[:5]=="<?xml" or contents[:8]=="\xef\xbb\xbf<?xml":
# utf-8
# utf-16LE of weird <!DOCTYPE start
return True
else:
- print(u"Bad XML: {0}".format(contents[:8]))
+ print("Bad XML: {0}".format(contents[:8]))
raise ValueError
elif self.mimetype == 'image/jpeg':
if contents[:3] == '\xff\xd8\xff':
return True
else:
- print(u"Bad JPEG: {0}".format(contents[:3].encode('hex')))
+ print("Bad JPEG: {0}".format(contents[:3].encode('hex')))
raise ValueError()
return False
return contents
def decrypt_book(book, lib):
- print(u"Converting {0}".format(book.title))
+ print("Converting {0}".format(book.title))
zin = zipfile.ZipFile(book.filename, "r")
# make filename out of Unicode alphanumeric and whitespace equivalents from title
- outname = u"{0}.epub".format(re.sub('[^\s\w]', '_', book.title, 0, re.UNICODE))
+ outname = "{0}.epub".format(re.sub('[^\s\w]', '_', book.title, 0, re.UNICODE))
if (book.type == 'drm-free'):
- print(u"DRM-free book, conversion is not needed")
+ print("DRM-free book, conversion is not needed")
shutil.copyfile(book.filename, outname)
- print(u"Book saved as {0}".format(os.path.join(os.getcwd(), outname)))
+ print("Book saved as {0}".format(os.path.join(os.getcwd(), outname)))
return 0
result = 1
for userkey in lib.userkeys:
- print(u"Trying key: {0}".format(userkey.encode('hex_codec')))
+ print("Trying key: {0}".format(userkey.encode('hex_codec')))
try:
zout = zipfile.ZipFile(outname, "w", zipfile.ZIP_DEFLATED)
for filename in zin.namelist():
file.check(contents)
zout.writestr(filename, contents)
zout.close()
- print(u"Decryption succeeded.")
- print(u"Book saved as {0}".format(os.path.join(os.getcwd(), outname)))
+ print("Decryption succeeded.")
+ print("Book saved as {0}".format(os.path.join(os.getcwd(), outname)))
result = 0
break
except ValueError:
- print(u"Decryption failed.")
+ print("Decryption failed.")
zout.close()
os.remove(outname)
zin.close()
def cli_main():
description = __about__
- epilog = u"Parsing of arguments failed."
+ epilog = "Parsing of arguments failed."
parser = argparse.ArgumentParser(prog=sys.argv[0], description=description, epilog=epilog)
parser.add_argument('--devicedir', default='/media/KOBOeReader', help="directory of connected Kobo device")
parser.add_argument('--all', action='store_true', help="flag for converting all books on device")
books = lib.books
else:
for i, book in enumerate(lib.books):
- print(u"{0}: {1}".format(i + 1, book.title))
- print(u"Or 'all'")
+ print("{0}: {1}".format(i + 1, book.title))
+ print("Or 'all'")
- choice = raw_input(u"Convert book number... ")
- if choice == u'all':
+ choice = raw_input("Convert book number... ")
+ if choice == "all":
books = list(lib.books)
else:
try:
num = int(choice)
books = [lib.books[num - 1]]
except (ValueError, IndexError):
- print(u"Invalid choice. Exiting...")
+ print("Invalid choice. Exiting...")
exit()
results = [decrypt_book(book, lib) for book in books]
lib.close()
overall_result = all(result != 0 for result in results)
if overall_result != 0:
- print(u"Could not decrypt book with any of the keys found.")
+ print("Could not decrypt book with any of the keys found.")
return overall_result
-#!/usr/bin/env python
-# code: utf-8
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
'''
A wrapper script to generate zip files for GitHub releases.