More generic 3.0 changes, to be tested.
This commit is contained in:
parent
6920f79a26
commit
de50a02af9
|
@ -1,8 +1,6 @@
|
|||
#!/usr/bin/env python
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from __future__ import with_statement
|
||||
|
||||
# __init__.py for DeDRM_plugin
|
||||
# Copyright © 2008-2020 Apprentice Harper et al.
|
||||
|
||||
|
@ -77,9 +75,9 @@ __docformat__ = 'restructuredtext en'
|
|||
Decrypt DRMed ebooks.
|
||||
"""
|
||||
|
||||
PLUGIN_NAME = u"DeDRM"
|
||||
PLUGIN_NAME = "DeDRM"
|
||||
PLUGIN_VERSION_TUPLE = (7, 0, 0)
|
||||
PLUGIN_VERSION = u".".join([unicode(str(x)) for x in PLUGIN_VERSION_TUPLE])
|
||||
PLUGIN_VERSION = ".".join([str(x)for x in PLUGIN_VERSION_TUPLE])
|
||||
# Include an html helpfile in the plugin's zipfile with the following name.
|
||||
RESOURCE_NAME = PLUGIN_NAME + '_Help.htm'
|
||||
|
||||
|
@ -109,11 +107,11 @@ class SafeUnbuffered:
|
|||
if self.encoding == None:
|
||||
self.encoding = "utf-8"
|
||||
def write(self, data):
|
||||
if isinstance(data,bytes):
|
||||
if isinstance(data,str):
|
||||
data = data.encode(self.encoding,"replace")
|
||||
try:
|
||||
self.stream.write(data)
|
||||
self.stream.flush()
|
||||
self.stream.buffer.write(data)
|
||||
self.stream.buffer.flush()
|
||||
except:
|
||||
# We can do nothing if a write fails
|
||||
pass
|
||||
|
@ -122,11 +120,11 @@ class SafeUnbuffered:
|
|||
|
||||
class DeDRM(FileTypePlugin):
|
||||
name = PLUGIN_NAME
|
||||
description = u"Removes DRM from Amazon Kindle, Adobe Adept (including Kobo), Barnes & Noble, Mobipocket and eReader ebooks. Credit given to i♥cabbages and The Dark Reverser for the original stand-alone scripts."
|
||||
description = "Removes DRM from Amazon Kindle, Adobe Adept (including Kobo), Barnes & Noble, Mobipocket and eReader ebooks. Credit given to i♥cabbages and The Dark Reverser for the original stand-alone scripts."
|
||||
supported_platforms = ['linux', 'osx', 'windows']
|
||||
author = u"Apprentice Alf, Aprentice Harper, The Dark Reverser and i♥cabbages"
|
||||
author = "Apprentice Alf, Aprentice Harper, The Dark Reverser and i♥cabbages"
|
||||
version = PLUGIN_VERSION_TUPLE
|
||||
minimum_calibre_version = (1, 0, 0) # Compiled python libraries cannot be imported in earlier versions.
|
||||
minimum_calibre_version = (5, 0, 0) # Python 3.
|
||||
file_types = set(['epub','pdf','pdb','prc','mobi','pobi','azw','azw1','azw3','azw4','azw8','tpz','kfx','kfx-zip'])
|
||||
on_import = True
|
||||
on_preprocess = True
|
||||
|
@ -146,27 +144,27 @@ class DeDRM(FileTypePlugin):
|
|||
Also perform upgrade of preferences once per version
|
||||
"""
|
||||
try:
|
||||
self.pluginsdir = os.path.join(config_dir,u"plugins")
|
||||
self.pluginsdir = os.path.join(config_dir,"plugins")
|
||||
if not os.path.exists(self.pluginsdir):
|
||||
os.mkdir(self.pluginsdir)
|
||||
self.maindir = os.path.join(self.pluginsdir,u"DeDRM")
|
||||
self.maindir = os.path.join(self.pluginsdir,"DeDRM")
|
||||
if not os.path.exists(self.maindir):
|
||||
os.mkdir(self.maindir)
|
||||
self.helpdir = os.path.join(self.maindir,u"help")
|
||||
self.helpdir = os.path.join(self.maindir,"help")
|
||||
if not os.path.exists(self.helpdir):
|
||||
os.mkdir(self.helpdir)
|
||||
self.alfdir = os.path.join(self.maindir,u"libraryfiles")
|
||||
self.alfdir = os.path.join(self.maindir,"libraryfiles")
|
||||
if not os.path.exists(self.alfdir):
|
||||
os.mkdir(self.alfdir)
|
||||
# only continue if we've never run this version of the plugin before
|
||||
self.verdir = os.path.join(self.maindir,PLUGIN_VERSION)
|
||||
if not os.path.exists(self.verdir):
|
||||
if iswindows:
|
||||
names = [u"alfcrypto.dll",u"alfcrypto64.dll"]
|
||||
names = ["alfcrypto.dll","alfcrypto64.dll"]
|
||||
elif isosx:
|
||||
names = [u"libalfcrypto.dylib"]
|
||||
names = ["libalfcrypto.dylib"]
|
||||
else:
|
||||
names = [u"libalfcrypto32.so",u"libalfcrypto64.so",u"kindlekey.py",u"adobekey.py",u"subasyncio.py"]
|
||||
names = ["libalfcrypto32.so","libalfcrypto64.so","kindlekey.py","adobekey.py","subasyncio.py"]
|
||||
lib_dict = self.load_resources(names)
|
||||
print("{0} v{1}: Copying needed library files from plugin's zip".format(PLUGIN_NAME, PLUGIN_VERSION))
|
||||
|
||||
|
@ -199,13 +197,13 @@ class DeDRM(FileTypePlugin):
|
|||
# Check original epub archive for zip errors.
|
||||
import calibre_plugins.dedrm.zipfix
|
||||
|
||||
inf = self.temporary_file(u".epub")
|
||||
inf = self.temporary_file(".epub")
|
||||
try:
|
||||
print("{0} v{1}: Verifying zip archive integrity".format(PLUGIN_NAME, PLUGIN_VERSION))
|
||||
fr = zipfix.fixZip(path_to_ebook, inf.name)
|
||||
fr.fix()
|
||||
except Exception as e:
|
||||
print(u"{0} v{1}: Error \'{2}\' when checking zip archive".format(PLUGIN_NAME, PLUGIN_VERSION, e.args[0]))
|
||||
print("{0} v{1}: Error \'{2}\' when checking zip archive".format(PLUGIN_NAME, PLUGIN_VERSION, e.args[0]))
|
||||
raise Exception(e)
|
||||
|
||||
# import the decryption keys
|
||||
|
@ -222,9 +220,9 @@ class DeDRM(FileTypePlugin):
|
|||
|
||||
# Attempt to decrypt epub with each encryption key (generated or provided).
|
||||
for keyname, userkey in dedrmprefs['bandnkeys'].items():
|
||||
keyname_masked = u"".join((u'X' if (x.isdigit()) else x) for x in keyname)
|
||||
keyname_masked = u"".join(("X" if (x.isdigit()) else x) for x in keyname)
|
||||
print("{0} v{1}: Trying Encryption key {2:s}".format(PLUGIN_NAME, PLUGIN_VERSION, keyname_masked))
|
||||
of = self.temporary_file(u".epub")
|
||||
of = self.temporary_file(".epub")
|
||||
|
||||
# Give the user key, ebook and TemporaryPersistent file to the decryption function.
|
||||
try:
|
||||
|
@ -255,10 +253,10 @@ class DeDRM(FileTypePlugin):
|
|||
|
||||
defaultkeys = nookkeys()
|
||||
else: # linux
|
||||
from wineutils import WineGetKeys
|
||||
from .wineutils import WineGetKeys
|
||||
|
||||
scriptpath = os.path.join(self.alfdir,u"ignoblekey.py")
|
||||
defaultkeys = WineGetKeys(scriptpath, u".b64",dedrmprefs['adobewineprefix'])
|
||||
scriptpath = os.path.join(self.alfdir,"ignoblekey.py")
|
||||
defaultkeys = WineGetKeys(scriptpath, ".b64",dedrmprefs['adobewineprefix'])
|
||||
|
||||
except:
|
||||
print("{0} v{1}: Exception when getting default NOOK Study Key after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION, time.time()-self.starttime))
|
||||
|
@ -274,7 +272,7 @@ class DeDRM(FileTypePlugin):
|
|||
for i,userkey in enumerate(newkeys):
|
||||
print("{0} v{1}: Trying a new default key".format(PLUGIN_NAME, PLUGIN_VERSION))
|
||||
|
||||
of = self.temporary_file(u".epub")
|
||||
of = self.temporary_file(".epub")
|
||||
|
||||
# Give the user key, ebook and TemporaryPersistent file to the decryption function.
|
||||
try:
|
||||
|
@ -300,12 +298,12 @@ class DeDRM(FileTypePlugin):
|
|||
# Return the modified PersistentTemporary file to calibre.
|
||||
return of.name
|
||||
|
||||
print(u"{0} v{1}: Failed to decrypt with new default key after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION,time.time()-self.starttime))
|
||||
print("{0} v{1}: Failed to decrypt with new default key after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION,time.time()-self.starttime))
|
||||
except Exception as e:
|
||||
pass
|
||||
|
||||
print("{0} v{1}: Ultimately failed to decrypt after {2:.1f} seconds. Read the FAQs at Harper's repository: https://github.com/apprenticeharper/DeDRM_tools/blob/master/FAQs.md".format(PLUGIN_NAME, PLUGIN_VERSION,time.time()-self.starttime))
|
||||
raise DeDRMError(u"{0} v{1}: Ultimately failed to decrypt after {2:.1f} seconds. Read the FAQs at Harper's repository: https://github.com/apprenticeharper/DeDRM_tools/blob/master/FAQs.md".format(PLUGIN_NAME, PLUGIN_VERSION, time.time()-self.starttime))
|
||||
raise DeDRMError("{0} v{1}: Ultimately failed to decrypt after {2:.1f} seconds. Read the FAQs at Harper's repository: https://github.com/apprenticeharper/DeDRM_tools/blob/master/FAQs.md".format(PLUGIN_NAME, PLUGIN_VERSION, time.time()-self.starttime))
|
||||
|
||||
# import the Adobe Adept ePub handler
|
||||
import calibre_plugins.dedrm.ineptepub as ineptepub
|
||||
|
@ -316,8 +314,8 @@ class DeDRM(FileTypePlugin):
|
|||
# Attempt to decrypt epub with each encryption key (generated or provided).
|
||||
for keyname, userkeyhex in dedrmprefs['adeptkeys'].items():
|
||||
userkey = codecs.decode(userkeyhex, 'hex')
|
||||
print(u"{0} v{1}: Trying Encryption key {2:s}".format(PLUGIN_NAME, PLUGIN_VERSION, keyname))
|
||||
of = self.temporary_file(u".epub")
|
||||
print("{0} v{1}: Trying Encryption key {2:s}".format(PLUGIN_NAME, PLUGIN_VERSION, keyname))
|
||||
of = self.temporary_file(".epub")
|
||||
|
||||
# Give the user key, ebook and TemporaryPersistent file to the decryption function.
|
||||
try:
|
||||
|
@ -352,10 +350,10 @@ class DeDRM(FileTypePlugin):
|
|||
|
||||
defaultkeys = adeptkeys()
|
||||
else: # linux
|
||||
from wineutils import WineGetKeys
|
||||
from .wineutils import WineGetKeys
|
||||
|
||||
scriptpath = os.path.join(self.alfdir,u"adobekey.py")
|
||||
defaultkeys = WineGetKeys(scriptpath, u".der",dedrmprefs['adobewineprefix'])
|
||||
scriptpath = os.path.join(self.alfdir,"adobekey.py")
|
||||
defaultkeys = WineGetKeys(scriptpath, ".der",dedrmprefs['adobewineprefix'])
|
||||
|
||||
self.default_key = defaultkeys[0]
|
||||
except:
|
||||
|
@ -365,14 +363,14 @@ class DeDRM(FileTypePlugin):
|
|||
|
||||
newkeys = []
|
||||
for keyvalue in defaultkeys:
|
||||
if keyvalue.encode('hex') not in dedrmprefs['adeptkeys'].values():
|
||||
if codecs.encode(keyvalue, 'hex').decode('ascii') not in dedrmprefs['adeptkeys'].values():
|
||||
newkeys.append(keyvalue)
|
||||
|
||||
if len(newkeys) > 0:
|
||||
try:
|
||||
for i,userkey in enumerate(newkeys):
|
||||
print("{0} v{1}: Trying a new default key".format(PLUGIN_NAME, PLUGIN_VERSION))
|
||||
of = self.temporary_file(u".epub")
|
||||
of = self.temporary_file(".epub")
|
||||
|
||||
# Give the user key, ebook and TemporaryPersistent file to the decryption function.
|
||||
try:
|
||||
|
@ -389,7 +387,7 @@ class DeDRM(FileTypePlugin):
|
|||
# Store the new successful key in the defaults
|
||||
print("{0} v{1}: Saving a new default key".format(PLUGIN_NAME, PLUGIN_VERSION))
|
||||
try:
|
||||
dedrmprefs.addnamedvaluetoprefs('adeptkeys','default_key',keyvalue.encode('hex'))
|
||||
dedrmprefs.addnamedvaluetoprefs('adeptkeys','default_key',codecs.encode(keyvalue, 'hex').decode('ascii'))
|
||||
dedrmprefs.writeprefs()
|
||||
print("{0} v{1}: Saved a new default key after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION,time.time()-self.starttime))
|
||||
except:
|
||||
|
@ -399,20 +397,20 @@ class DeDRM(FileTypePlugin):
|
|||
# Return the modified PersistentTemporary file to calibre.
|
||||
return of.name
|
||||
|
||||
print(u"{0} v{1}: Failed to decrypt with new default key after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION,time.time()-self.starttime))
|
||||
print("{0} v{1}: Failed to decrypt with new default key after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION,time.time()-self.starttime))
|
||||
except Exception as e:
|
||||
print(u"{0} v{1}: Unexpected Exception trying a new default key after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION, time.time()-self.starttime))
|
||||
print("{0} v{1}: Unexpected Exception trying a new default key after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION, time.time()-self.starttime))
|
||||
traceback.print_exc()
|
||||
pass
|
||||
|
||||
# Something went wrong with decryption.
|
||||
print("{0} v{1}: Ultimately failed to decrypt after {2:.1f} seconds. Read the FAQs at Harper's repository: https://github.com/apprenticeharper/DeDRM_tools/blob/master/FAQs.md".format(PLUGIN_NAME, PLUGIN_VERSION,time.time()-self.starttime))
|
||||
raise DeDRMError(u"{0} v{1}: Ultimately failed to decrypt after {2:.1f} seconds. Read the FAQs at Harper's repository: https://github.com/apprenticeharper/DeDRM_tools/blob/master/FAQs.md".format(PLUGIN_NAME, PLUGIN_VERSION,time.time()-self.starttime))
|
||||
raise DeDRMError("{0} v{1}: Ultimately failed to decrypt after {2:.1f} seconds. Read the FAQs at Harper's repository: https://github.com/apprenticeharper/DeDRM_tools/blob/master/FAQs.md".format(PLUGIN_NAME, PLUGIN_VERSION,time.time()-self.starttime))
|
||||
|
||||
# Not a Barnes & Noble nor an Adobe Adept
|
||||
# Import the fixed epub.
|
||||
print("{0} v{1}: “{2}” is neither an Adobe Adept nor a Barnes & Noble encrypted ePub".format(PLUGIN_NAME, PLUGIN_VERSION, os.path.basename(path_to_ebook)))
|
||||
raise DeDRMError(u"{0} v{1}: Couldn't decrypt after {2:.1f} seconds. DRM free perhaps?".format(PLUGIN_NAME, PLUGIN_VERSION,time.time()-self.starttime))
|
||||
raise DeDRMError("{0} v{1}: Couldn't decrypt after {2:.1f} seconds. DRM free perhaps?".format(PLUGIN_NAME, PLUGIN_VERSION,time.time()-self.starttime))
|
||||
|
||||
def PDFDecrypt(self,path_to_ebook):
|
||||
import calibre_plugins.dedrm.prefs as prefs
|
||||
|
@ -422,9 +420,9 @@ class DeDRM(FileTypePlugin):
|
|||
# Attempt to decrypt epub with each encryption key (generated or provided).
|
||||
print("{0} v{1}: {2} is a PDF ebook".format(PLUGIN_NAME, PLUGIN_VERSION, os.path.basename(path_to_ebook)))
|
||||
for keyname, userkeyhex in dedrmprefs['adeptkeys'].items():
|
||||
userkey = codecs.decode(userkeyhex, 'hex')
|
||||
userkey = userkeyhex.decode('hex')
|
||||
print("{0} v{1}: Trying Encryption key {2:s}".format(PLUGIN_NAME, PLUGIN_VERSION, keyname))
|
||||
of = self.temporary_file(u".pdf")
|
||||
of = self.temporary_file(".pdf")
|
||||
|
||||
# Give the user key, ebook and TemporaryPersistent file to the decryption function.
|
||||
try:
|
||||
|
@ -455,10 +453,10 @@ class DeDRM(FileTypePlugin):
|
|||
|
||||
defaultkeys = adeptkeys()
|
||||
else: # linux
|
||||
from wineutils import WineGetKeys
|
||||
from .wineutils import WineGetKeys
|
||||
|
||||
scriptpath = os.path.join(self.alfdir,u"adobekey.py")
|
||||
defaultkeys = WineGetKeys(scriptpath, u".der",dedrmprefs['adobewineprefix'])
|
||||
scriptpath = os.path.join(self.alfdir,"adobekey.py")
|
||||
defaultkeys = WineGetKeys(scriptpath, ".der",dedrmprefs['adobewineprefix'])
|
||||
|
||||
self.default_key = defaultkeys[0]
|
||||
except:
|
||||
|
@ -475,7 +473,7 @@ class DeDRM(FileTypePlugin):
|
|||
try:
|
||||
for i,userkey in enumerate(newkeys):
|
||||
print("{0} v{1}: Trying a new default key".format(PLUGIN_NAME, PLUGIN_VERSION))
|
||||
of = self.temporary_file(u".pdf")
|
||||
of = self.temporary_file(".pdf")
|
||||
|
||||
# Give the user key, ebook and TemporaryPersistent file to the decryption function.
|
||||
try:
|
||||
|
@ -501,13 +499,13 @@ class DeDRM(FileTypePlugin):
|
|||
# Return the modified PersistentTemporary file to calibre.
|
||||
return of.name
|
||||
|
||||
print(u"{0} v{1}: Failed to decrypt with new default key after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION,time.time()-self.starttime))
|
||||
print("{0} v{1}: Failed to decrypt with new default key after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION,time.time()-self.starttime))
|
||||
except Exception as e:
|
||||
pass
|
||||
|
||||
# Something went wrong with decryption.
|
||||
print("{0} v{1}: Ultimately failed to decrypt after {2:.1f} seconds. Read the FAQs at Harper's repository: https://github.com/apprenticeharper/DeDRM_tools/blob/master/FAQs.md".format(PLUGIN_NAME, PLUGIN_VERSION,time.time()-self.starttime))
|
||||
raise DeDRMError(u"{0} v{1}: Ultimately failed to decrypt after {2:.1f} seconds. Read the FAQs at Harper's repository: https://github.com/apprenticeharper/DeDRM_tools/blob/master/FAQs.md".format(PLUGIN_NAME, PLUGIN_VERSION, time.time()-self.starttime))
|
||||
raise DeDRMError("{0} v{1}: Ultimately failed to decrypt after {2:.1f} seconds. Read the FAQs at Harper's repository: https://github.com/apprenticeharper/DeDRM_tools/blob/master/FAQs.md".format(PLUGIN_NAME, PLUGIN_VERSION, time.time()-self.starttime))
|
||||
|
||||
|
||||
def KindleMobiDecrypt(self,path_to_ebook):
|
||||
|
@ -529,7 +527,7 @@ class DeDRM(FileTypePlugin):
|
|||
serials.extend(android_serials_list)
|
||||
#print serials
|
||||
androidFiles = []
|
||||
kindleDatabases = dedrmprefs['kindlekeys'].items()
|
||||
kindleDatabases = list(dedrmprefs['kindlekeys'].items())
|
||||
|
||||
try:
|
||||
book = k4mobidedrm.GetDecryptedBook(path_to_ebook,kindleDatabases,androidFiles,serials,pids,self.starttime)
|
||||
|
@ -546,10 +544,10 @@ class DeDRM(FileTypePlugin):
|
|||
|
||||
defaultkeys = kindlekeys()
|
||||
else: # linux
|
||||
from wineutils import WineGetKeys
|
||||
from .wineutils import WineGetKeys
|
||||
|
||||
scriptpath = os.path.join(self.alfdir,u"kindlekey.py")
|
||||
defaultkeys = WineGetKeys(scriptpath, u".k4i",dedrmprefs['kindlewineprefix'])
|
||||
scriptpath = os.path.join(self.alfdir,"kindlekey.py")
|
||||
defaultkeys = WineGetKeys(scriptpath, ".k4i",dedrmprefs['kindlewineprefix'])
|
||||
except:
|
||||
print("{0} v{1}: Exception when getting default Kindle Key after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION, time.time()-self.starttime))
|
||||
traceback.print_exc()
|
||||
|
@ -557,16 +555,16 @@ class DeDRM(FileTypePlugin):
|
|||
|
||||
newkeys = {}
|
||||
for i,keyvalue in enumerate(defaultkeys):
|
||||
keyname = u"default_key_{0:d}".format(i+1)
|
||||
keyname = "default_key_{0:d}".format(i+1)
|
||||
if keyvalue not in dedrmprefs['kindlekeys'].values():
|
||||
newkeys[keyname] = keyvalue
|
||||
if len(newkeys) > 0:
|
||||
print("{0} v{1}: Found {2} new {3}".format(PLUGIN_NAME, PLUGIN_VERSION, len(newkeys), u"key" if len(newkeys)==1 else u"keys"))
|
||||
print("{0} v{1}: Found {2} new {3}".format(PLUGIN_NAME, PLUGIN_VERSION, len(newkeys), "key" if len(newkeys)==1 else "keys"))
|
||||
try:
|
||||
book = k4mobidedrm.GetDecryptedBook(path_to_ebook,newkeys.items(),[],[],[],self.starttime)
|
||||
book = k4mobidedrm.GetDecryptedBook(path_to_ebook,list(newkeys.items()),[],[],[],self.starttime)
|
||||
decoded = True
|
||||
# store the new successful keys in the defaults
|
||||
print("{0} v{1}: Saving {2} new {3}".format(PLUGIN_NAME, PLUGIN_VERSION, len(newkeys), u"key" if len(newkeys)==1 else u"keys"))
|
||||
print("{0} v{1}: Saving {2} new {3}".format(PLUGIN_NAME, PLUGIN_VERSION, len(newkeys), "key" if len(newkeys)==1 else "keys"))
|
||||
for keyvalue in newkeys.values():
|
||||
dedrmprefs.addnamedvaluetoprefs('kindlekeys','default_key',keyvalue)
|
||||
dedrmprefs.writeprefs()
|
||||
|
@ -575,7 +573,7 @@ class DeDRM(FileTypePlugin):
|
|||
if not decoded:
|
||||
#if you reached here then no luck raise and exception
|
||||
print("{0} v{1}: Ultimately failed to decrypt after {2:.1f} seconds. Read the FAQs at Harper's repository: https://github.com/apprenticeharper/DeDRM_tools/blob/master/FAQs.md".format(PLUGIN_NAME, PLUGIN_VERSION,time.time()-self.starttime))
|
||||
raise DeDRMError(u"{0} v{1}: Ultimately failed to decrypt after {2:.1f} seconds. Read the FAQs at Harper's repository: https://github.com/apprenticeharper/DeDRM_tools/blob/master/FAQs.md".format(PLUGIN_NAME, PLUGIN_VERSION,time.time()-self.starttime))
|
||||
raise DeDRMError("{0} v{1}: Ultimately failed to decrypt after {2:.1f} seconds. Read the FAQs at Harper's repository: https://github.com/apprenticeharper/DeDRM_tools/blob/master/FAQs.md".format(PLUGIN_NAME, PLUGIN_VERSION,time.time()-self.starttime))
|
||||
|
||||
of = self.temporary_file(book.getBookExtension())
|
||||
book.getFile(of.name)
|
||||
|
@ -592,9 +590,9 @@ class DeDRM(FileTypePlugin):
|
|||
dedrmprefs = prefs.DeDRM_Prefs()
|
||||
# Attempt to decrypt epub with each encryption key (generated or provided).
|
||||
for keyname, userkey in dedrmprefs['ereaderkeys'].items():
|
||||
keyname_masked = u"".join((u'X' if (x.isdigit()) else x) for x in keyname)
|
||||
keyname_masked = u"".join(("X" if (x.isdigit()) else x) for x in keyname)
|
||||
print("{0} v{1}: Trying Encryption key {2:s}".format(PLUGIN_NAME, PLUGIN_VERSION, keyname_masked))
|
||||
of = self.temporary_file(u".pmlz")
|
||||
of = self.temporary_file(".pmlz")
|
||||
|
||||
# Give the userkey, ebook and TemporaryPersistent file to the decryption function.
|
||||
result = erdr2pml.decryptBook(path_to_ebook, of.name, True, userkey.decode('hex'))
|
||||
|
@ -610,7 +608,7 @@ class DeDRM(FileTypePlugin):
|
|||
print("{0} v{1}: Failed to decrypt with key {2:s} after {3:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION,keyname_masked,time.time()-self.starttime))
|
||||
|
||||
print("{0} v{1}: Ultimately failed to decrypt after {2:.1f} seconds. Read the FAQs at Harper's repository: https://github.com/apprenticeharper/DeDRM_tools/blob/master/FAQs.md".format(PLUGIN_NAME, PLUGIN_VERSION,time.time()-self.starttime))
|
||||
raise DeDRMError(u"{0} v{1}: Ultimately failed to decrypt after {2:.1f} seconds. Read the FAQs at Harper's repository: https://github.com/apprenticeharper/DeDRM_tools/blob/master/FAQs.md".format(PLUGIN_NAME, PLUGIN_VERSION, time.time()-self.starttime))
|
||||
raise DeDRMError("{0} v{1}: Ultimately failed to decrypt after {2:.1f} seconds. Read the FAQs at Harper's repository: https://github.com/apprenticeharper/DeDRM_tools/blob/master/FAQs.md".format(PLUGIN_NAME, PLUGIN_VERSION, time.time()-self.starttime))
|
||||
|
||||
|
||||
def run(self, path_to_ebook):
|
||||
|
|
|
@ -1,30 +1,12 @@
|
|||
#!/usr/bin/env python
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# adobekey.pyw, version 6.0
|
||||
# Copyright © 2009-2010 i♥cabbages
|
||||
# Copyright © 2009-2020 i♥cabbages, Apprentice Harper et al.
|
||||
|
||||
# Released under the terms of the GNU General Public Licence, version 3
|
||||
# <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
|
||||
|
@ -46,7 +28,7 @@
|
|||
# 5.8 - Added getkey interface for Windows DeDRM application
|
||||
# 5.9 - moved unicode_argv call inside main for Windows DeDRM compatibility
|
||||
# 6.0 - Work if TkInter is missing
|
||||
# 7.0 - Python 3 compatible for calibre 5
|
||||
# 7.0 - Python 3 for calibre 5
|
||||
|
||||
"""
|
||||
Retrieve Adobe ADEPT user key.
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#! /usr/bin/env python
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
Routines for doing AES CBC in one file
|
||||
|
@ -14,7 +15,7 @@
|
|||
See the wonderful pure python package cryptopy-1.2.5
|
||||
and read its LICENSE.txt for complete license details.
|
||||
|
||||
Adjusted for Python 3 compatibility, September 2020
|
||||
Adjusted for Python 3, September 2020
|
||||
"""
|
||||
|
||||
class CryptoError(Exception):
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#!/usr/bin/env python
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# crypto library mainly by some_updates
|
||||
|
@ -8,7 +8,6 @@
|
|||
# pbkdf2.py Copyright © 2009 Daniel Holth <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
|
||||
|
@ -159,7 +158,7 @@ def _load_libalfcrypto():
|
|||
topazCryptoDecrypt(ctx, data, out, len(data))
|
||||
return out.raw
|
||||
|
||||
print(u"Using Library AlfCrypto DLL/DYLIB/SO")
|
||||
print("Using Library AlfCrypto DLL/DYLIB/SO")
|
||||
return (AES_CBC, Pukall_Cipher, Topaz_Cipher)
|
||||
|
||||
|
||||
|
@ -245,7 +244,7 @@ def _load_python_alfcrypto():
|
|||
cleartext = self.aes.decrypt(iv + data)
|
||||
return cleartext
|
||||
|
||||
print(u"Using Library AlfCrypto Python")
|
||||
print("Using Library AlfCrypto Python")
|
||||
return (AES_CBC, Pukall_Cipher, Topaz_Cipher)
|
||||
|
||||
|
||||
|
|
|
@ -1,13 +1,8 @@
|
|||
#!/usr/bin/env python
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from __future__ import with_statement
|
||||
from __future__ import print_function
|
||||
|
||||
# androidkindlekey.py
|
||||
# Copyright © 2013-15 by Thom and Apprentice Harper
|
||||
# Some portions Copyright © 2010-15 by some_updates and Apprentice Alf
|
||||
#
|
||||
# Copyright © 2010-20 by Thom, Apprentice et al.
|
||||
|
||||
# Revision history:
|
||||
# 1.0 - AmazonSecureStorage.xml decryption to serial number
|
||||
|
@ -18,7 +13,7 @@ from __future__ import print_function
|
|||
# 1.3 - added in TkInter interface, output to a file
|
||||
# 1.4 - Fix some problems identified by Aldo Bleeker
|
||||
# 1.5 - Fix another problem identified by Aldo Bleeker
|
||||
# 2.0 - Add Python 3 compatibility
|
||||
# 2.0 - Python 3 compatibility
|
||||
|
||||
"""
|
||||
Retrieve Kindle for Android Serial Number.
|
||||
|
@ -53,10 +48,11 @@ class SafeUnbuffered:
|
|||
if self.encoding == None:
|
||||
self.encoding = "utf-8"
|
||||
def write(self, data):
|
||||
if isinstance(data,bytes):
|
||||
if isinstance(data,str):
|
||||
data = data.encode(self.encoding,"replace")
|
||||
self.stream.write(data)
|
||||
self.stream.flush()
|
||||
self.stream.buffer.write(data)
|
||||
self.stream.buffer.flush()
|
||||
|
||||
def __getattr__(self, attr):
|
||||
return getattr(self.stream, attr)
|
||||
|
||||
|
@ -97,7 +93,7 @@ def unicode_argv():
|
|||
range(start, argc.value)]
|
||||
# if we don't have any arguments at all, just pass back script name
|
||||
# this should never happen
|
||||
return [u"kindlekey.py"]
|
||||
return ["kindlekey.py"]
|
||||
else:
|
||||
argvencoding = sys.stdin.encoding
|
||||
if argvencoding == None:
|
||||
|
@ -107,9 +103,9 @@ def unicode_argv():
|
|||
class DrmException(Exception):
|
||||
pass
|
||||
|
||||
STORAGE = u"backup.ab"
|
||||
STORAGE1 = u"AmazonSecureStorage.xml"
|
||||
STORAGE2 = u"map_data_storage.db"
|
||||
STORAGE = "backup.ab"
|
||||
STORAGE1 = "AmazonSecureStorage.xml"
|
||||
STORAGE2 = "map_data_storage.db"
|
||||
|
||||
class AndroidObfuscation(object):
|
||||
'''AndroidObfuscation
|
||||
|
@ -326,13 +322,13 @@ def getkey(outfile, inpath):
|
|||
|
||||
|
||||
def usage(progname):
|
||||
print(u"Decrypts the serial number(s) of Kindle For Android from Android backup or file")
|
||||
print(u"Get backup.ab file using adb backup com.amazon.kindle for Android 4.0+.")
|
||||
print(u"Otherwise extract AmazonSecureStorage.xml from /data/data/com.amazon.kindle/shared_prefs/AmazonSecureStorage.xml")
|
||||
print(u"Or map_data_storage.db from /data/data/com.amazon.kindle/databases/map_data_storage.db")
|
||||
print("Decrypts the serial number(s) of Kindle For Android from Android backup or file")
|
||||
print("Get backup.ab file using adb backup com.amazon.kindle for Android 4.0+.")
|
||||
print("Otherwise extract AmazonSecureStorage.xml from /data/data/com.amazon.kindle/shared_prefs/AmazonSecureStorage.xml")
|
||||
print("Or map_data_storage.db from /data/data/com.amazon.kindle/databases/map_data_storage.db")
|
||||
print(u"")
|
||||
print(u"Usage:")
|
||||
print(u" {0:s} [-h] [-b <backup.ab>] [<outfile.k4a>]".format(progname))
|
||||
print("Usage:")
|
||||
print(" {0:s} [-h] [-b <backup.ab>] [<outfile.k4a>]".format(progname))
|
||||
|
||||
|
||||
def cli_main():
|
||||
|
@ -340,13 +336,13 @@ def cli_main():
|
|||
sys.stderr=SafeUnbuffered(sys.stderr)
|
||||
argv=unicode_argv()
|
||||
progname = os.path.basename(argv[0])
|
||||
print(u"{0} v{1}\nCopyright © 2010-2015 Thom, some_updates, Apprentice Alf and Apprentice Harper".format(progname,__version__))
|
||||
print("{0} v{1}\nCopyright © 2010-2015 Thom, some_updates, Apprentice Alf and Apprentice Harper".format(progname,__version__))
|
||||
|
||||
try:
|
||||
opts, args = getopt.getopt(argv[1:], "hb:")
|
||||
except getopt.GetoptError as err:
|
||||
usage(progname)
|
||||
print(u"\nError in options or arguments: {0}".format(err.args[0]))
|
||||
print("\nError in options or arguments: {0}".format(err.args[0]))
|
||||
return 2
|
||||
|
||||
inpath = ""
|
||||
|
@ -378,13 +374,13 @@ def cli_main():
|
|||
|
||||
if not os.path.isfile(inpath):
|
||||
usage(progname)
|
||||
print(u"\n{0:s} file not found".format(inpath))
|
||||
print("\n{0:s} file not found".format(inpath))
|
||||
return 2
|
||||
|
||||
if getkey(outfile, inpath):
|
||||
print(u"\nSaved Kindle for Android key to {0}".format(outfile))
|
||||
print("\nSaved Kindle for Android key to {0}".format(outfile))
|
||||
else:
|
||||
print(u"\nCould not retrieve Kindle for Android key.")
|
||||
print("\nCould not retrieve Kindle for Android key.")
|
||||
return 0
|
||||
|
||||
|
||||
|
@ -401,32 +397,32 @@ def gui_main():
|
|||
class DecryptionDialog(Tkinter.Frame):
|
||||
def __init__(self, root):
|
||||
Tkinter.Frame.__init__(self, root, border=5)
|
||||
self.status = Tkinter.Label(self, text=u"Select backup.ab file")
|
||||
self.status = Tkinter.Label(self, text="Select backup.ab file")
|
||||
self.status.pack(fill=Tkconstants.X, expand=1)
|
||||
body = Tkinter.Frame(self)
|
||||
body.pack(fill=Tkconstants.X, expand=1)
|
||||
sticky = Tkconstants.E + Tkconstants.W
|
||||
body.grid_columnconfigure(1, weight=2)
|
||||
Tkinter.Label(body, text=u"Backup file").grid(row=0, column=0)
|
||||
Tkinter.Label(body, text="Backup file").grid(row=0, column=0)
|
||||
self.keypath = Tkinter.Entry(body, width=40)
|
||||
self.keypath.grid(row=0, column=1, sticky=sticky)
|
||||
self.keypath.insert(2, u"backup.ab")
|
||||
button = Tkinter.Button(body, text=u"...", command=self.get_keypath)
|
||||
self.keypath.insert(2, "backup.ab")
|
||||
button = Tkinter.Button(body, text="...", command=self.get_keypath)
|
||||
button.grid(row=0, column=2)
|
||||
buttons = Tkinter.Frame(self)
|
||||
buttons.pack()
|
||||
button2 = Tkinter.Button(
|
||||
buttons, text=u"Extract", width=10, command=self.generate)
|
||||
buttons, text="Extract", width=10, command=self.generate)
|
||||
button2.pack(side=Tkconstants.LEFT)
|
||||
Tkinter.Frame(buttons, width=10).pack(side=Tkconstants.LEFT)
|
||||
button3 = Tkinter.Button(
|
||||
buttons, text=u"Quit", width=10, command=self.quit)
|
||||
buttons, text="Quit", width=10, command=self.quit)
|
||||
button3.pack(side=Tkconstants.RIGHT)
|
||||
|
||||
def get_keypath(self):
|
||||
keypath = tkFileDialog.askopenfilename(
|
||||
parent=None, title=u"Select backup.ab file",
|
||||
defaultextension=u".ab",
|
||||
parent=None, title="Select backup.ab file",
|
||||
defaultextension=".ab",
|
||||
filetypes=[('adb backup com.amazon.kindle', '.ab'),
|
||||
('All Files', '.*')])
|
||||
if keypath:
|
||||
|
@ -437,30 +433,30 @@ def gui_main():
|
|||
|
||||
def generate(self):
|
||||
inpath = self.keypath.get()
|
||||
self.status['text'] = u"Getting key..."
|
||||
self.status['text'] = "Getting key..."
|
||||
try:
|
||||
keys = get_serials(inpath)
|
||||
keycount = 0
|
||||
for key in keys:
|
||||
while True:
|
||||
keycount += 1
|
||||
outfile = os.path.join(progpath,u"kindlekey{0:d}.k4a".format(keycount))
|
||||
outfile = os.path.join(progpath,"kindlekey{0:d}.k4a".format(keycount))
|
||||
if not os.path.exists(outfile):
|
||||
break
|
||||
|
||||
with open(outfile, 'w') as keyfileout:
|
||||
keyfileout.write(key)
|
||||
success = True
|
||||
tkMessageBox.showinfo(progname, u"Key successfully retrieved to {0}".format(outfile))
|
||||
tkMessageBox.showinfo(progname, "Key successfully retrieved to {0}".format(outfile))
|
||||
except Exception as e:
|
||||
self.status['text'] = u"Error: {0}".format(e.args[0])
|
||||
self.status['text'] = "Error: {0}".format(e.args[0])
|
||||
return
|
||||
self.status['text'] = u"Select backup.ab file"
|
||||
self.status['text'] = "Select backup.ab file"
|
||||
|
||||
argv=unicode_argv()
|
||||
progpath, progname = os.path.split(argv[0])
|
||||
root = Tkinter.Tk()
|
||||
root.title(u"Kindle for Android Key Extraction v.{0}".format(__version__))
|
||||
root.title("Kindle for Android Key Extraction v.{0}".format(__version__))
|
||||
root.resizable(True, False)
|
||||
root.minsize(300, 0)
|
||||
DecryptionDialog(root).pack(fill=Tkconstants.X, expand=1)
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#!/usr/bin/env python
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import sys, os
|
||||
|
@ -37,7 +37,7 @@ def unicode_argv():
|
|||
xrange(start, argc.value)]
|
||||
# if we don't have any arguments at all, just pass back script name
|
||||
# this should never happen
|
||||
return [u"DeDRM.py"]
|
||||
return ["DeDRM.py"]
|
||||
else:
|
||||
argvencoding = sys.stdin.encoding
|
||||
if argvencoding == None:
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#!/usr/bin/env python
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
# vim:ts=4:sw=4:softtabstop=4:smarttab:expandtab
|
||||
|
||||
|
@ -202,7 +202,7 @@ def AskFolder(
|
|||
if not pidl:
|
||||
result = None
|
||||
else:
|
||||
path = LPCWSTR(u" " * (MAX_PATH+1))
|
||||
path = LPCWSTR(" " * (MAX_PATH+1))
|
||||
shell32.SHGetPathFromIDListW(pidl, path)
|
||||
ole32.CoTaskMemFree(pidl)
|
||||
result = path.value
|
||||
|
|
|
@ -1,30 +1,18 @@
|
|||
#!/usr/bin/env python
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from __future__ import with_statement
|
||||
from __future__ import print_function
|
||||
|
||||
__license__ = 'GPL v3'
|
||||
|
||||
# Added Python 3 compatibility, September 2020
|
||||
# Python 3, September 2020
|
||||
|
||||
# Standard Python modules.
|
||||
import os, traceback, json
|
||||
|
||||
# PyQT4 modules (part of calibre).
|
||||
try:
|
||||
from PyQt5.Qt import (Qt, QWidget, QHBoxLayout, QVBoxLayout, QLabel, QLineEdit,
|
||||
QGroupBox, QPushButton, QListWidget, QListWidgetItem,
|
||||
QAbstractItemView, QIcon, QDialog, QDialogButtonBox, QUrl)
|
||||
except ImportError:
|
||||
from PyQt4.Qt import (Qt, QWidget, QHBoxLayout, QVBoxLayout, QLabel, QLineEdit,
|
||||
QGroupBox, QPushButton, QListWidget, QListWidgetItem,
|
||||
QAbstractItemView, QIcon, QDialog, QDialogButtonBox, QUrl)
|
||||
try:
|
||||
from PyQt5 import Qt as QtGui
|
||||
except ImportError:
|
||||
from PyQt4 import QtGui
|
||||
|
||||
from PyQt5 import Qt as QtGui
|
||||
from zipfile import ZipFile
|
||||
|
||||
# calibre modules and constants.
|
||||
|
@ -85,32 +73,32 @@ class ConfigWidget(QWidget):
|
|||
button_layout = QVBoxLayout()
|
||||
keys_group_box_layout.addLayout(button_layout)
|
||||
self.bandn_button = QtGui.QPushButton(self)
|
||||
self.bandn_button.setToolTip(_(u"Click to manage keys for Barnes and Noble ebooks"))
|
||||
self.bandn_button.setText(u"Barnes and Noble ebooks")
|
||||
self.bandn_button.setToolTip(_("Click to manage keys for Barnes and Noble ebooks"))
|
||||
self.bandn_button.setText("Barnes and Noble ebooks")
|
||||
self.bandn_button.clicked.connect(self.bandn_keys)
|
||||
self.kindle_android_button = QtGui.QPushButton(self)
|
||||
self.kindle_android_button.setToolTip(_(u"Click to manage keys for Kindle for Android ebooks"))
|
||||
self.kindle_android_button.setText(u"Kindle for Android ebooks")
|
||||
self.kindle_android_button.setToolTip(_("Click to manage keys for Kindle for Android ebooks"))
|
||||
self.kindle_android_button.setText("Kindle for Android ebooks")
|
||||
self.kindle_android_button.clicked.connect(self.kindle_android)
|
||||
self.kindle_serial_button = QtGui.QPushButton(self)
|
||||
self.kindle_serial_button.setToolTip(_(u"Click to manage eInk Kindle serial numbers for Kindle ebooks"))
|
||||
self.kindle_serial_button.setText(u"eInk Kindle ebooks")
|
||||
self.kindle_serial_button.setToolTip(_("Click to manage eInk Kindle serial numbers for Kindle ebooks"))
|
||||
self.kindle_serial_button.setText("eInk Kindle ebooks")
|
||||
self.kindle_serial_button.clicked.connect(self.kindle_serials)
|
||||
self.kindle_key_button = QtGui.QPushButton(self)
|
||||
self.kindle_key_button.setToolTip(_(u"Click to manage keys for Kindle for Mac/PC ebooks"))
|
||||
self.kindle_key_button.setText(u"Kindle for Mac/PC ebooks")
|
||||
self.kindle_key_button.setToolTip(_("Click to manage keys for Kindle for Mac/PC ebooks"))
|
||||
self.kindle_key_button.setText("Kindle for Mac/PC ebooks")
|
||||
self.kindle_key_button.clicked.connect(self.kindle_keys)
|
||||
self.adept_button = QtGui.QPushButton(self)
|
||||
self.adept_button.setToolTip(_(u"Click to manage keys for Adobe Digital Editions ebooks"))
|
||||
self.adept_button.setText(u"Adobe Digital Editions ebooks")
|
||||
self.adept_button.setToolTip(_("Click to manage keys for Adobe Digital Editions ebooks"))
|
||||
self.adept_button.setText("Adobe Digital Editions ebooks")
|
||||
self.adept_button.clicked.connect(self.adept_keys)
|
||||
self.mobi_button = QtGui.QPushButton(self)
|
||||
self.mobi_button.setToolTip(_(u"Click to manage PIDs for Mobipocket ebooks"))
|
||||
self.mobi_button.setText(u"Mobipocket ebooks")
|
||||
self.mobi_button.setToolTip(_("Click to manage PIDs for Mobipocket ebooks"))
|
||||
self.mobi_button.setText("Mobipocket ebooks")
|
||||
self.mobi_button.clicked.connect(self.mobi_keys)
|
||||
self.ereader_button = QtGui.QPushButton(self)
|
||||
self.ereader_button.setToolTip(_(u"Click to manage keys for eReader ebooks"))
|
||||
self.ereader_button.setText(u"eReader ebooks")
|
||||
self.ereader_button.setToolTip(_("Click to manage keys for eReader ebooks"))
|
||||
self.ereader_button.setText("eReader ebooks")
|
||||
self.ereader_button.clicked.connect(self.ereader_keys)
|
||||
button_layout.addWidget(self.kindle_serial_button)
|
||||
button_layout.addWidget(self.kindle_android_button)
|
||||
|
@ -123,48 +111,48 @@ class ConfigWidget(QWidget):
|
|||
self.resize(self.sizeHint())
|
||||
|
||||
def kindle_serials(self):
|
||||
d = ManageKeysDialog(self,u"EInk Kindle Serial Number",self.tempdedrmprefs['serials'], AddSerialDialog)
|
||||
d = ManageKeysDialog(self,"EInk Kindle Serial Number",self.tempdedrmprefs['serials'], AddSerialDialog)
|
||||
d.exec_()
|
||||
|
||||
def kindle_android(self):
|
||||
d = ManageKeysDialog(self,u"Kindle for Android Key",self.tempdedrmprefs['androidkeys'], AddAndroidDialog, 'k4a')
|
||||
d = ManageKeysDialog(self,"Kindle for Android Key",self.tempdedrmprefs['androidkeys'], AddAndroidDialog, 'k4a')
|
||||
d.exec_()
|
||||
|
||||
def kindle_keys(self):
|
||||
if isosx or iswindows:
|
||||
d = ManageKeysDialog(self,u"Kindle for Mac and PC Key",self.tempdedrmprefs['kindlekeys'], AddKindleDialog, 'k4i')
|
||||
d = ManageKeysDialog(self,"Kindle for Mac and PC Key",self.tempdedrmprefs['kindlekeys'], AddKindleDialog, 'k4i')
|
||||
else:
|
||||
# linux
|
||||
d = ManageKeysDialog(self,u"Kindle for Mac and PC Key",self.tempdedrmprefs['kindlekeys'], AddKindleDialog, 'k4i', self.tempdedrmprefs['kindlewineprefix'])
|
||||
d = ManageKeysDialog(self,"Kindle for Mac and PC Key",self.tempdedrmprefs['kindlekeys'], AddKindleDialog, 'k4i', self.tempdedrmprefs['kindlewineprefix'])
|
||||
d.exec_()
|
||||
self.tempdedrmprefs['kindlewineprefix'] = d.getwineprefix()
|
||||
|
||||
def adept_keys(self):
|
||||
if isosx or iswindows:
|
||||
d = ManageKeysDialog(self,u"Adobe Digital Editions Key",self.tempdedrmprefs['adeptkeys'], AddAdeptDialog, 'der')
|
||||
d = ManageKeysDialog(self,"Adobe Digital Editions Key",self.tempdedrmprefs['adeptkeys'], AddAdeptDialog, 'der')
|
||||
else:
|
||||
# linux
|
||||
d = ManageKeysDialog(self,u"Adobe Digital Editions Key",self.tempdedrmprefs['adeptkeys'], AddAdeptDialog, 'der', self.tempdedrmprefs['adobewineprefix'])
|
||||
d = ManageKeysDialog(self,"Adobe Digital Editions Key",self.tempdedrmprefs['adeptkeys'], AddAdeptDialog, 'der', self.tempdedrmprefs['adobewineprefix'])
|
||||
d.exec_()
|
||||
self.tempdedrmprefs['adobewineprefix'] = d.getwineprefix()
|
||||
|
||||
def mobi_keys(self):
|
||||
d = ManageKeysDialog(self,u"Mobipocket PID",self.tempdedrmprefs['pids'], AddPIDDialog)
|
||||
d = ManageKeysDialog(self,"Mobipocket PID",self.tempdedrmprefs['pids'], AddPIDDialog)
|
||||
d.exec_()
|
||||
|
||||
def bandn_keys(self):
|
||||
d = ManageKeysDialog(self,u"Barnes and Noble Key",self.tempdedrmprefs['bandnkeys'], AddBandNKeyDialog, 'b64')
|
||||
d = ManageKeysDialog(self,"Barnes and Noble Key",self.tempdedrmprefs['bandnkeys'], AddBandNKeyDialog, 'b64')
|
||||
d.exec_()
|
||||
|
||||
def ereader_keys(self):
|
||||
d = ManageKeysDialog(self,u"eReader Key",self.tempdedrmprefs['ereaderkeys'], AddEReaderDialog, 'b63')
|
||||
d = ManageKeysDialog(self,"eReader Key",self.tempdedrmprefs['ereaderkeys'], AddEReaderDialog, 'b63')
|
||||
d.exec_()
|
||||
|
||||
def help_link_activated(self, url):
|
||||
def get_help_file_resource():
|
||||
# Copy the HTML helpfile to the plugin directory each time the
|
||||
# link is clicked in case the helpfile is updated in newer plugins.
|
||||
file_path = os.path.join(config_dir, u"plugins", u"DeDRM", u"help", help_file_name)
|
||||
file_path = os.path.join(config_dir, "plugins", "DeDRM", "help", help_file_name)
|
||||
with open(file_path,'w') as f:
|
||||
f.write(self.load_resource(help_file_name))
|
||||
return file_path
|
||||
|
@ -201,9 +189,9 @@ class ManageKeysDialog(QDialog):
|
|||
self.create_key = create_key
|
||||
self.keyfile_ext = keyfile_ext
|
||||
self.import_key = (keyfile_ext != u"")
|
||||
self.binary_file = (keyfile_ext == u"der")
|
||||
self.json_file = (keyfile_ext == u"k4i")
|
||||
self.android_file = (keyfile_ext == u"k4a")
|
||||
self.binary_file = (keyfile_ext == "der")
|
||||
self.json_file = (keyfile_ext == "k4i")
|
||||
self.android_file = (keyfile_ext == "k4a")
|
||||
self.wineprefix = wineprefix
|
||||
|
||||
self.setWindowTitle("{0} {1}: Manage {2}s".format(PLUGIN_NAME, PLUGIN_VERSION, self.key_type_name))
|
||||
|
@ -221,13 +209,13 @@ class ManageKeysDialog(QDialog):
|
|||
help_label.linkActivated.connect(self.help_link_activated)
|
||||
help_layout.addWidget(help_label)
|
||||
|
||||
keys_group_box = QGroupBox(_(u"{0}s".format(self.key_type_name)), self)
|
||||
keys_group_box = QGroupBox(_("{0}s".format(self.key_type_name)), self)
|
||||
layout.addWidget(keys_group_box)
|
||||
keys_group_box_layout = QHBoxLayout()
|
||||
keys_group_box.setLayout(keys_group_box_layout)
|
||||
|
||||
self.listy = QListWidget(self)
|
||||
self.listy.setToolTip(u"{0}s that will be used to decrypt ebooks".format(self.key_type_name))
|
||||
self.listy.setToolTip("{0}s that will be used to decrypt ebooks".format(self.key_type_name))
|
||||
self.listy.setSelectionMode(QAbstractItemView.SingleSelection)
|
||||
self.populate_list()
|
||||
keys_group_box_layout.addWidget(self.listy)
|
||||
|
@ -236,25 +224,25 @@ class ManageKeysDialog(QDialog):
|
|||
keys_group_box_layout.addLayout(button_layout)
|
||||
self._add_key_button = QtGui.QToolButton(self)
|
||||
self._add_key_button.setIcon(QIcon(I('plus.png')))
|
||||
self._add_key_button.setToolTip(u"Create new {0}".format(self.key_type_name))
|
||||
self._add_key_button.setToolTip("Create new {0}".format(self.key_type_name))
|
||||
self._add_key_button.clicked.connect(self.add_key)
|
||||
button_layout.addWidget(self._add_key_button)
|
||||
|
||||
self._delete_key_button = QtGui.QToolButton(self)
|
||||
self._delete_key_button.setToolTip(_(u"Delete highlighted key"))
|
||||
self._delete_key_button.setToolTip(_("Delete highlighted key"))
|
||||
self._delete_key_button.setIcon(QIcon(I('list_remove.png')))
|
||||
self._delete_key_button.clicked.connect(self.delete_key)
|
||||
button_layout.addWidget(self._delete_key_button)
|
||||
|
||||
if type(self.plugin_keys) == dict and self.import_key:
|
||||
self._rename_key_button = QtGui.QToolButton(self)
|
||||
self._rename_key_button.setToolTip(_(u"Rename highlighted key"))
|
||||
self._rename_key_button.setToolTip(_("Rename highlighted key"))
|
||||
self._rename_key_button.setIcon(QIcon(I('edit-select-all.png')))
|
||||
self._rename_key_button.clicked.connect(self.rename_key)
|
||||
button_layout.addWidget(self._rename_key_button)
|
||||
|
||||
self.export_key_button = QtGui.QToolButton(self)
|
||||
self.export_key_button.setToolTip(u"Save highlighted key to a .{0} file".format(self.keyfile_ext))
|
||||
self.export_key_button.setToolTip("Save highlighted key to a .{0} file".format(self.keyfile_ext))
|
||||
self.export_key_button.setIcon(QIcon(I('save.png')))
|
||||
self.export_key_button.clicked.connect(self.export_key)
|
||||
button_layout.addWidget(self.export_key_button)
|
||||
|
@ -266,7 +254,7 @@ class ManageKeysDialog(QDialog):
|
|||
wineprefix_layout = QHBoxLayout()
|
||||
layout.addLayout(wineprefix_layout)
|
||||
wineprefix_layout.setAlignment(Qt.AlignCenter)
|
||||
self.wp_label = QLabel(u"WINEPREFIX:")
|
||||
self.wp_label = QLabel("WINEPREFIX:")
|
||||
wineprefix_layout.addWidget(self.wp_label)
|
||||
self.wp_lineedit = QLineEdit(self)
|
||||
wineprefix_layout.addWidget(self.wp_lineedit)
|
||||
|
@ -278,8 +266,8 @@ class ManageKeysDialog(QDialog):
|
|||
layout.addLayout(migrate_layout)
|
||||
if self.import_key:
|
||||
migrate_layout.setAlignment(Qt.AlignJustify)
|
||||
self.migrate_btn = QPushButton(u"Import Existing Keyfiles", self)
|
||||
self.migrate_btn.setToolTip(u"Import *.{0} files (created using other tools).".format(self.keyfile_ext))
|
||||
self.migrate_btn = QPushButton("Import Existing Keyfiles", self)
|
||||
self.migrate_btn.setToolTip("Import *.{0} files (created using other tools).".format(self.keyfile_ext))
|
||||
self.migrate_btn.clicked.connect(self.migrate_wrapper)
|
||||
migrate_layout.addWidget(self.migrate_btn)
|
||||
migrate_layout.addStretch()
|
||||
|
@ -314,13 +302,13 @@ class ManageKeysDialog(QDialog):
|
|||
if new_key_value in self.plugin_keys.values():
|
||||
old_key_name = [name for name, value in self.plugin_keys.iteritems() if value == new_key_value][0]
|
||||
info_dialog(None, "{0} {1}: Duplicate {2}".format(PLUGIN_NAME, PLUGIN_VERSION,self.key_type_name),
|
||||
u"The new {1} is the same as the existing {1} named <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)
|
||||
|
@ -329,7 +317,7 @@ class ManageKeysDialog(QDialog):
|
|||
|
||||
def rename_key(self):
|
||||
if not self.listy.currentItem():
|
||||
errmsg = u"No {0} selected to rename. Highlight a keyfile first.".format(self.key_type_name)
|
||||
errmsg = "No {0} selected to rename. Highlight a keyfile first.".format(self.key_type_name)
|
||||
r = error_dialog(None, "{0} {1}".format(PLUGIN_NAME, PLUGIN_VERSION),
|
||||
_(errmsg), show=True, show_copy_button=False)
|
||||
return
|
||||
|
@ -341,7 +329,7 @@ class ManageKeysDialog(QDialog):
|
|||
# rename cancelled or moot.
|
||||
return
|
||||
keyname = self.listy.currentItem().text()
|
||||
if not question_dialog(self, "{0} {1}: Confirm Rename".format(PLUGIN_NAME, PLUGIN_VERSION), u"Do you really want to rename the {2} named <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]
|
||||
|
@ -353,7 +341,7 @@ class ManageKeysDialog(QDialog):
|
|||
if not self.listy.currentItem():
|
||||
return
|
||||
keyname = self.listy.currentItem().text()
|
||||
if not question_dialog(self, "{0} {1}: Confirm Delete".format(PLUGIN_NAME, PLUGIN_VERSION), u"Do you really want to delete the {1} <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]
|
||||
|
@ -367,8 +355,8 @@ class ManageKeysDialog(QDialog):
|
|||
def get_help_file_resource():
|
||||
# Copy the HTML helpfile to the plugin directory each time the
|
||||
# link is clicked in case the helpfile is updated in newer plugins.
|
||||
help_file_name = u"{0}_{1}_Help.htm".format(PLUGIN_NAME, self.key_type_name)
|
||||
file_path = os.path.join(config_dir, u"plugins", u"DeDRM", u"help", help_file_name)
|
||||
help_file_name = "{0}_{1}_Help.htm".format(PLUGIN_NAME, self.key_type_name)
|
||||
file_path = os.path.join(config_dir, "plugins", "DeDRM", "help", help_file_name)
|
||||
with open(file_path,'w') as f:
|
||||
f.write(self.parent.load_resource(help_file_name))
|
||||
return file_path
|
||||
|
@ -376,9 +364,9 @@ class ManageKeysDialog(QDialog):
|
|||
open_url(QUrl(url))
|
||||
|
||||
def migrate_files(self):
|
||||
unique_dlg_name = PLUGIN_NAME + u"import {0} keys".format(self.key_type_name).replace(' ', '_') #takes care of automatically remembering last directory
|
||||
caption = u"Select {0} files to import".format(self.key_type_name)
|
||||
filters = [(u"{0} files".format(self.key_type_name), [self.keyfile_ext])]
|
||||
unique_dlg_name = PLUGIN_NAME + "import {0} keys".format(self.key_type_name).replace(' ', '_') #takes care of automatically remembering last directory
|
||||
caption = "Select {0} files to import".format(self.key_type_name)
|
||||
filters = [("{0} files".format(self.key_type_name), [self.keyfile_ext])]
|
||||
files = choose_files(self, unique_dlg_name, caption, filters, all_files=False)
|
||||
counter = 0
|
||||
skipped = 0
|
||||
|
@ -400,7 +388,7 @@ class ManageKeysDialog(QDialog):
|
|||
for key in self.plugin_keys.keys():
|
||||
if uStrCmp(new_key_name, key, True):
|
||||
skipped += 1
|
||||
msg = u"A key with the name <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
|
||||
|
@ -410,7 +398,7 @@ class ManageKeysDialog(QDialog):
|
|||
old_key_name = [name for name, value in self.plugin_keys.iteritems() if value == new_key_value][0]
|
||||
skipped += 1
|
||||
info_dialog(None, "{0} {1}".format(PLUGIN_NAME, PLUGIN_VERSION),
|
||||
u"The key in file {0} is the same as the existing key <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
|
||||
|
@ -418,9 +406,9 @@ class ManageKeysDialog(QDialog):
|
|||
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
|
||||
|
@ -432,15 +420,15 @@ class ManageKeysDialog(QDialog):
|
|||
|
||||
def export_key(self):
|
||||
if not self.listy.currentItem():
|
||||
errmsg = u"No keyfile selected to export. Highlight a keyfile first."
|
||||
errmsg = "No keyfile selected to export. Highlight a keyfile first."
|
||||
r = error_dialog(None, "{0} {1}".format(PLUGIN_NAME, PLUGIN_VERSION),
|
||||
_(errmsg), show=True, show_copy_button=False)
|
||||
return
|
||||
keyname = self.listy.currentItem().text()
|
||||
unique_dlg_name = PLUGIN_NAME + u"export {0} keys".format(self.key_type_name).replace(' ', '_') #takes care of automatically remembering last directory
|
||||
caption = u"Save {0} File as...".format(self.key_type_name)
|
||||
filters = [(u"{0} Files".format(self.key_type_name), [u"{0}".format(self.keyfile_ext)])]
|
||||
defaultname = u"{0}.{1}".format(keyname, self.keyfile_ext)
|
||||
unique_dlg_name = PLUGIN_NAME + "export {0} keys".format(self.key_type_name).replace(' ', '_') #takes care of automatically remembering last directory
|
||||
caption = "Save {0} File as...".format(self.key_type_name)
|
||||
filters = [("{0} Files".format(self.key_type_name), ["{0}".format(self.keyfile_ext)])]
|
||||
defaultname = "{0}.{1}".format(keyname, self.keyfile_ext)
|
||||
filename = choose_save_file(self, unique_dlg_name, caption, filters, all_files=False, initial_filename=defaultname)
|
||||
if filename:
|
||||
with file(filename, 'wb') as fname:
|
||||
|
@ -474,7 +462,7 @@ class RenameKeyDialog(QDialog):
|
|||
|
||||
data_group_box_layout.addWidget(QLabel('New Key Name:', self))
|
||||
self.key_ledit = QLineEdit(self.parent.listy.currentItem().text(), self)
|
||||
self.key_ledit.setToolTip(u"Enter a new name for this existing {0}.".format(parent.key_type_name))
|
||||
self.key_ledit.setToolTip("Enter a new name for this existing {0}.".format(parent.key_type_name))
|
||||
data_group_box_layout.addWidget(self.key_ledit)
|
||||
|
||||
layout.addSpacing(20)
|
||||
|
@ -488,11 +476,11 @@ class RenameKeyDialog(QDialog):
|
|||
|
||||
def accept(self):
|
||||
if not self.key_ledit.text() or self.key_ledit.text().isspace():
|
||||
errmsg = u"Key name field cannot be empty!"
|
||||
errmsg = "Key name field cannot be empty!"
|
||||
return error_dialog(None, "{0} {1}".format(PLUGIN_NAME, PLUGIN_VERSION),
|
||||
_(errmsg), show=True, show_copy_button=False)
|
||||
if len(self.key_ledit.text()) < 4:
|
||||
errmsg = u"Key name must be at <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()):
|
||||
|
@ -501,7 +489,7 @@ class RenameKeyDialog(QDialog):
|
|||
for k in self.parent.plugin_keys.keys():
|
||||
if (uStrCmp(self.key_ledit.text(), k, True) and
|
||||
not uStrCmp(k, self.parent.listy.currentItem().text(), True)):
|
||||
errmsg = u"The key name <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)
|
||||
|
@ -521,7 +509,7 @@ class AddBandNKeyDialog(QDialog):
|
|||
def __init__(self, parent=None,):
|
||||
QDialog.__init__(self, parent)
|
||||
self.parent = parent
|
||||
self.setWindowTitle(u"{0} {1}: Create New Barnes & Noble Key".format(PLUGIN_NAME, PLUGIN_VERSION))
|
||||
self.setWindowTitle("{0} {1}: Create New Barnes & Noble Key".format(PLUGIN_NAME, PLUGIN_VERSION))
|
||||
layout = QVBoxLayout(self)
|
||||
self.setLayout(layout)
|
||||
|
||||
|
@ -532,37 +520,37 @@ class AddBandNKeyDialog(QDialog):
|
|||
|
||||
key_group = QHBoxLayout()
|
||||
data_group_box_layout.addLayout(key_group)
|
||||
key_group.addWidget(QLabel(u"Unique Key Name:", self))
|
||||
key_group.addWidget(QLabel("Unique Key Name:", self))
|
||||
self.key_ledit = QLineEdit("", self)
|
||||
self.key_ledit.setToolTip(_(u"<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)
|
||||
|
@ -571,13 +559,13 @@ class AddBandNKeyDialog(QDialog):
|
|||
|
||||
key_group = QHBoxLayout()
|
||||
data_group_box_layout.addLayout(key_group)
|
||||
key_group.addWidget(QLabel(u"Retrieved key:", self))
|
||||
key_group.addWidget(QLabel("Retrieved key:", self))
|
||||
self.key_display = QLabel(u"", self)
|
||||
self.key_display.setToolTip(_(u"Click the Retrieve Key button to fetch your B&N encryption key from the B&N servers"))
|
||||
self.key_display.setToolTip(_("Click the Retrieve Key button to fetch your B&N encryption key from the B&N servers"))
|
||||
key_group.addWidget(self.key_display)
|
||||
self.retrieve_button = QtGui.QPushButton(self)
|
||||
self.retrieve_button.setToolTip(_(u"Click to retrieve your B&N encryption key from the B&N servers"))
|
||||
self.retrieve_button.setText(u"Retrieve Key")
|
||||
self.retrieve_button.setToolTip(_("Click to retrieve your B&N encryption key from the B&N servers"))
|
||||
self.retrieve_button.setText("Retrieve Key")
|
||||
self.retrieve_button.clicked.connect(self.retrieve_key)
|
||||
key_group.addWidget(self.retrieve_button)
|
||||
layout.addSpacing(10)
|
||||
|
@ -609,17 +597,17 @@ class AddBandNKeyDialog(QDialog):
|
|||
from calibre_plugins.dedrm.ignoblekeyfetch import fetch_key as fetch_bandn_key
|
||||
fetched_key = fetch_bandn_key(self.user_name,self.cc_number)
|
||||
if fetched_key == "":
|
||||
errmsg = u"Could not retrieve key. Check username, password and intenet connectivity and try again."
|
||||
errmsg = "Could not retrieve key. Check username, password and intenet connectivity and try again."
|
||||
error_dialog(None, "{0} {1}".format(PLUGIN_NAME, PLUGIN_VERSION), errmsg, show=True, show_copy_button=False)
|
||||
else:
|
||||
self.key_display.setText(fetched_key)
|
||||
|
||||
def accept(self):
|
||||
if len(self.key_name) == 0 or len(self.user_name) == 0 or len(self.cc_number) == 0 or self.key_name.isspace() or self.user_name.isspace() or self.cc_number.isspace():
|
||||
errmsg = u"All fields are required!"
|
||||
errmsg = "All fields are required!"
|
||||
return error_dialog(None, "{0} {1}".format(PLUGIN_NAME, PLUGIN_VERSION), errmsg, show=True, show_copy_button=False)
|
||||
if len(self.key_name) < 4:
|
||||
errmsg = u"Key name must be at <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()
|
||||
|
@ -631,7 +619,7 @@ class AddEReaderDialog(QDialog):
|
|||
def __init__(self, parent=None,):
|
||||
QDialog.__init__(self, parent)
|
||||
self.parent = parent
|
||||
self.setWindowTitle(u"{0} {1}: Create New eReader Key".format(PLUGIN_NAME, PLUGIN_VERSION))
|
||||
self.setWindowTitle("{0} {1}: Create New eReader Key".format(PLUGIN_NAME, PLUGIN_VERSION))
|
||||
layout = QVBoxLayout(self)
|
||||
self.setLayout(layout)
|
||||
|
||||
|
@ -642,26 +630,26 @@ class AddEReaderDialog(QDialog):
|
|||
|
||||
key_group = QHBoxLayout()
|
||||
data_group_box_layout.addLayout(key_group)
|
||||
key_group.addWidget(QLabel(u"Unique Key Name:", self))
|
||||
key_group.addWidget(QLabel("Unique Key Name:", self))
|
||||
self.key_ledit = QLineEdit("", self)
|
||||
self.key_ledit.setToolTip(u"<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)
|
||||
|
@ -695,13 +683,13 @@ class AddEReaderDialog(QDialog):
|
|||
|
||||
def accept(self):
|
||||
if len(self.key_name) == 0 or len(self.user_name) == 0 or len(self.cc_number) == 0 or self.key_name.isspace() or self.user_name.isspace() or self.cc_number.isspace():
|
||||
errmsg = u"All fields are required!"
|
||||
errmsg = "All fields are required!"
|
||||
return error_dialog(None, "{0} {1}".format(PLUGIN_NAME, PLUGIN_VERSION), errmsg, show=True, show_copy_button=False)
|
||||
if not self.cc_number.isdigit():
|
||||
errmsg = u"Numbers only in the credit card number field!"
|
||||
errmsg = "Numbers only in the credit card number field!"
|
||||
return error_dialog(None, "{0} {1}".format(PLUGIN_NAME, PLUGIN_VERSION), errmsg, show=True, show_copy_button=False)
|
||||
if len(self.key_name) < 4:
|
||||
errmsg = u"Key name must be at <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)
|
||||
|
||||
|
@ -710,7 +698,7 @@ class AddAdeptDialog(QDialog):
|
|||
def __init__(self, parent=None,):
|
||||
QDialog.__init__(self, parent)
|
||||
self.parent = parent
|
||||
self.setWindowTitle(u"{0} {1}: Getting Default Adobe Digital Editions Key".format(PLUGIN_NAME, PLUGIN_VERSION))
|
||||
self.setWindowTitle("{0} {1}: Getting Default Adobe Digital Editions Key".format(PLUGIN_NAME, PLUGIN_VERSION))
|
||||
layout = QVBoxLayout(self)
|
||||
self.setLayout(layout)
|
||||
|
||||
|
@ -722,8 +710,8 @@ class AddAdeptDialog(QDialog):
|
|||
else: # linux
|
||||
from wineutils import WineGetKeys
|
||||
|
||||
scriptpath = os.path.join(parent.parent.alfdir,u"adobekey.py")
|
||||
defaultkeys = WineGetKeys(scriptpath, u".der",parent.getwineprefix())
|
||||
scriptpath = os.path.join(parent.parent.alfdir,"adobekey.py")
|
||||
defaultkeys = WineGetKeys(scriptpath, ".der",parent.getwineprefix())
|
||||
|
||||
self.default_key = defaultkeys[0]
|
||||
except:
|
||||
|
@ -740,14 +728,14 @@ class AddAdeptDialog(QDialog):
|
|||
|
||||
key_group = QHBoxLayout()
|
||||
data_group_box_layout.addLayout(key_group)
|
||||
key_group.addWidget(QLabel(u"Unique Key Name:", self))
|
||||
self.key_ledit = QLineEdit(u"default_key", self)
|
||||
self.key_ledit.setToolTip(u"<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
|
||||
|
@ -769,10 +757,10 @@ class AddAdeptDialog(QDialog):
|
|||
|
||||
def accept(self):
|
||||
if len(self.key_name) == 0 or self.key_name.isspace():
|
||||
errmsg = u"All fields are required!"
|
||||
errmsg = "All fields are required!"
|
||||
return error_dialog(None, "{0} {1}".format(PLUGIN_NAME, PLUGIN_VERSION), errmsg, show=True, show_copy_button=False)
|
||||
if len(self.key_name) < 4:
|
||||
errmsg = u"Key name must be at <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)
|
||||
|
||||
|
@ -781,7 +769,7 @@ class AddKindleDialog(QDialog):
|
|||
def __init__(self, parent=None,):
|
||||
QDialog.__init__(self, parent)
|
||||
self.parent = parent
|
||||
self.setWindowTitle(u"{0} {1}: Getting Default Kindle for Mac/PC Key".format(PLUGIN_NAME, PLUGIN_VERSION))
|
||||
self.setWindowTitle("{0} {1}: Getting Default Kindle for Mac/PC Key".format(PLUGIN_NAME, PLUGIN_VERSION))
|
||||
layout = QVBoxLayout(self)
|
||||
self.setLayout(layout)
|
||||
|
||||
|
@ -793,8 +781,8 @@ class AddKindleDialog(QDialog):
|
|||
else: # linux
|
||||
from wineutils import WineGetKeys
|
||||
|
||||
scriptpath = os.path.join(parent.parent.alfdir,u"kindlekey.py")
|
||||
defaultkeys = WineGetKeys(scriptpath, u".k4i",parent.getwineprefix())
|
||||
scriptpath = os.path.join(parent.parent.alfdir,"kindlekey.py")
|
||||
defaultkeys = WineGetKeys(scriptpath, ".k4i",parent.getwineprefix())
|
||||
|
||||
self.default_key = defaultkeys[0]
|
||||
except:
|
||||
|
@ -811,14 +799,14 @@ class AddKindleDialog(QDialog):
|
|||
|
||||
key_group = QHBoxLayout()
|
||||
data_group_box_layout.addLayout(key_group)
|
||||
key_group.addWidget(QLabel(u"Unique Key Name:", self))
|
||||
self.key_ledit = QLineEdit(u"default_key", self)
|
||||
self.key_ledit.setToolTip(u"<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)
|
||||
|
||||
|
@ -841,10 +829,10 @@ class AddKindleDialog(QDialog):
|
|||
|
||||
def accept(self):
|
||||
if len(self.key_name) == 0 or self.key_name.isspace():
|
||||
errmsg = u"All fields are required!"
|
||||
errmsg = "All fields are required!"
|
||||
return error_dialog(None, "{0} {1}".format(PLUGIN_NAME, PLUGIN_VERSION), errmsg, show=True, show_copy_button=False)
|
||||
if len(self.key_name) < 4:
|
||||
errmsg = u"Key name must be at <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)
|
||||
|
||||
|
@ -853,7 +841,7 @@ class AddSerialDialog(QDialog):
|
|||
def __init__(self, parent=None,):
|
||||
QDialog.__init__(self, parent)
|
||||
self.parent = parent
|
||||
self.setWindowTitle(u"{0} {1}: Add New EInk Kindle Serial Number".format(PLUGIN_NAME, PLUGIN_VERSION))
|
||||
self.setWindowTitle("{0} {1}: Add New EInk Kindle Serial Number".format(PLUGIN_NAME, PLUGIN_VERSION))
|
||||
layout = QVBoxLayout(self)
|
||||
self.setLayout(layout)
|
||||
|
||||
|
@ -864,9 +852,9 @@ class AddSerialDialog(QDialog):
|
|||
|
||||
key_group = QHBoxLayout()
|
||||
data_group_box_layout.addLayout(key_group)
|
||||
key_group.addWidget(QLabel(u"EInk Kindle Serial Number:", self))
|
||||
key_group.addWidget(QLabel("EInk Kindle Serial Number:", self))
|
||||
self.key_ledit = QLineEdit("", self)
|
||||
self.key_ledit.setToolTip(u"Enter an eInk Kindle serial number. EInk Kindle serial numbers are 16 characters long and usually start with a 'B' or a '9'. Kindle Serial Numbers are case-sensitive, so be sure to enter the upper and lower case letters unchanged.")
|
||||
self.key_ledit.setToolTip("Enter an eInk Kindle serial number. EInk Kindle serial numbers are 16 characters long and usually start with a 'B' or a '9'. Kindle Serial Numbers are case-sensitive, so be sure to enter the upper and lower case letters unchanged.")
|
||||
key_group.addWidget(self.key_ledit)
|
||||
|
||||
self.button_box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
|
||||
|
@ -886,10 +874,10 @@ class AddSerialDialog(QDialog):
|
|||
|
||||
def accept(self):
|
||||
if len(self.key_name) == 0 or self.key_name.isspace():
|
||||
errmsg = u"Please enter an eInk Kindle Serial Number or click Cancel in the dialog."
|
||||
errmsg = "Please enter an eInk Kindle Serial Number or click Cancel in the dialog."
|
||||
return error_dialog(None, "{0} {1}".format(PLUGIN_NAME, PLUGIN_VERSION), errmsg, show=True, show_copy_button=False)
|
||||
if len(self.key_name) != 16:
|
||||
errmsg = u"EInk Kindle Serial Numbers must be 16 characters long. This is {0:d} characters long.".format(len(self.key_name))
|
||||
errmsg = "EInk Kindle Serial Numbers must be 16 characters long. This is {0:d} characters long.".format(len(self.key_name))
|
||||
return error_dialog(None, "{0} {1}".format(PLUGIN_NAME, PLUGIN_VERSION), errmsg, show=True, show_copy_button=False)
|
||||
QDialog.accept(self)
|
||||
|
||||
|
@ -899,7 +887,7 @@ class AddAndroidDialog(QDialog):
|
|||
|
||||
QDialog.__init__(self, parent)
|
||||
self.parent = parent
|
||||
self.setWindowTitle(u"{0} {1}: Add new Kindle for Android Key".format(PLUGIN_NAME, PLUGIN_VERSION))
|
||||
self.setWindowTitle("{0} {1}: Add new Kindle for Android Key".format(PLUGIN_NAME, PLUGIN_VERSION))
|
||||
layout = QVBoxLayout(self)
|
||||
self.setLayout(layout)
|
||||
self.button_box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
|
||||
|
@ -911,8 +899,8 @@ class AddAndroidDialog(QDialog):
|
|||
|
||||
file_group = QHBoxLayout()
|
||||
data_group_box_layout.addLayout(file_group)
|
||||
add_btn = QPushButton(u"Choose Backup File", self)
|
||||
add_btn.setToolTip(u"Import Kindle for Android backup file.")
|
||||
add_btn = QPushButton("Choose Backup File", self)
|
||||
add_btn.setToolTip("Import Kindle for Android backup file.")
|
||||
add_btn.clicked.connect(self.get_android_file)
|
||||
file_group.addWidget(add_btn)
|
||||
self.selected_file_name = QLabel(u"",self)
|
||||
|
@ -921,9 +909,9 @@ class AddAndroidDialog(QDialog):
|
|||
|
||||
key_group = QHBoxLayout()
|
||||
data_group_box_layout.addLayout(key_group)
|
||||
key_group.addWidget(QLabel(u"Unique Key Name:", self))
|
||||
key_group.addWidget(QLabel("Unique Key Name:", self))
|
||||
self.key_ledit = QLineEdit(u"", self)
|
||||
self.key_ledit.setToolTip(u"<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)
|
||||
|
@ -947,9 +935,9 @@ class AddAndroidDialog(QDialog):
|
|||
return self.serials_from_file
|
||||
|
||||
def get_android_file(self):
|
||||
unique_dlg_name = PLUGIN_NAME + u"Import Kindle for Android backup file" #takes care of automatically remembering last directory
|
||||
caption = u"Select Kindle for Android backup file to add"
|
||||
filters = [(u"Kindle for Android backup files", ['db','ab','xml'])]
|
||||
unique_dlg_name = PLUGIN_NAME + "Import Kindle for Android backup file" #takes care of automatically remembering last directory
|
||||
caption = "Select Kindle for Android backup file to add"
|
||||
filters = [("Kindle for Android backup files", ['db','ab','xml'])]
|
||||
files = choose_files(self, unique_dlg_name, caption, filters, all_files=False)
|
||||
self.serials_from_file = []
|
||||
file_name = u""
|
||||
|
@ -967,13 +955,13 @@ class AddAndroidDialog(QDialog):
|
|||
|
||||
def accept(self):
|
||||
if len(self.file_name) == 0 or len(self.key_value) == 0:
|
||||
errmsg = u"Please choose a Kindle for Android backup file."
|
||||
errmsg = "Please choose a Kindle for Android backup file."
|
||||
return error_dialog(None, "{0} {1}".format(PLUGIN_NAME, PLUGIN_VERSION), errmsg, show=True, show_copy_button=False)
|
||||
if len(self.key_name) == 0 or self.key_name.isspace():
|
||||
errmsg = u"Please enter a key name."
|
||||
errmsg = "Please enter a key name."
|
||||
return error_dialog(None, "{0} {1}".format(PLUGIN_NAME, PLUGIN_VERSION), errmsg, show=True, show_copy_button=False)
|
||||
if len(self.key_name) < 4:
|
||||
errmsg = u"Key name must be at <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)
|
||||
|
||||
|
@ -981,7 +969,7 @@ class AddPIDDialog(QDialog):
|
|||
def __init__(self, parent=None,):
|
||||
QDialog.__init__(self, parent)
|
||||
self.parent = parent
|
||||
self.setWindowTitle(u"{0} {1}: Add New Mobipocket PID".format(PLUGIN_NAME, PLUGIN_VERSION))
|
||||
self.setWindowTitle("{0} {1}: Add New Mobipocket PID".format(PLUGIN_NAME, PLUGIN_VERSION))
|
||||
layout = QVBoxLayout(self)
|
||||
self.setLayout(layout)
|
||||
|
||||
|
@ -992,9 +980,9 @@ class AddPIDDialog(QDialog):
|
|||
|
||||
key_group = QHBoxLayout()
|
||||
data_group_box_layout.addLayout(key_group)
|
||||
key_group.addWidget(QLabel(u"PID:", self))
|
||||
key_group.addWidget(QLabel("PID:", self))
|
||||
self.key_ledit = QLineEdit("", self)
|
||||
self.key_ledit.setToolTip(u"Enter a Mobipocket PID. Mobipocket PIDs are 8 or 10 characters long. Mobipocket PIDs are case-sensitive, so be sure to enter the upper and lower case letters unchanged.")
|
||||
self.key_ledit.setToolTip("Enter a Mobipocket PID. Mobipocket PIDs are 8 or 10 characters long. Mobipocket PIDs are case-sensitive, so be sure to enter the upper and lower case letters unchanged.")
|
||||
key_group.addWidget(self.key_ledit)
|
||||
|
||||
self.button_box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
|
||||
|
@ -1014,10 +1002,10 @@ class AddPIDDialog(QDialog):
|
|||
|
||||
def accept(self):
|
||||
if len(self.key_name) == 0 or self.key_name.isspace():
|
||||
errmsg = u"Please enter a Mobipocket PID or click Cancel in the dialog."
|
||||
errmsg = "Please enter a Mobipocket PID or click Cancel in the dialog."
|
||||
return error_dialog(None, "{0} {1}".format(PLUGIN_NAME, PLUGIN_VERSION), errmsg, show=True, show_copy_button=False)
|
||||
if len(self.key_name) != 8 and len(self.key_name) != 10:
|
||||
errmsg = u"Mobipocket PIDs must be 8 or 10 characters long. This is {0:d} characters long.".format(len(self.key_name))
|
||||
errmsg = "Mobipocket PIDs must be 8 or 10 characters long. This is {0:d} characters long.".format(len(self.key_name))
|
||||
return error_dialog(None, "{0} {1}".format(PLUGIN_NAME, PLUGIN_VERSION), errmsg, show=True, show_copy_button=False)
|
||||
QDialog.accept(self)
|
||||
|
||||
|
|
|
@ -1,15 +1,16 @@
|
|||
#! /usr/bin/python
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
# vim:ts=4:sw=4:softtabstop=4:smarttab:expandtab
|
||||
# For use with Topaz Scripts Version 2.6
|
||||
# Added Python 3 compatibility, September 2020
|
||||
|
||||
from __future__ import print_function
|
||||
# For use with Topaz Scripts Version 2.6
|
||||
# Python 3, September 2020
|
||||
|
||||
class Unbuffered:
|
||||
def __init__(self, stream):
|
||||
self.stream = stream
|
||||
def write(self, data):
|
||||
self.stream.write(data)
|
||||
self.stream.flush()
|
||||
self.stream.buffer.write(data)
|
||||
self.stream.buffer.flush()
|
||||
def __getattr__(self, attr):
|
||||
return getattr(self.stream, attr)
|
||||
|
||||
|
|
|
@ -1,46 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# base64.py, version 1.0
|
||||
# Copyright © 2010 Apprentice Alf
|
||||
|
||||
# Released under the terms of the GNU General Public Licence, version 3 or
|
||||
# later. <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())
|
|
@ -1,4 +1,5 @@
|
|||
#!/usr/bin/python
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# This is a python script. You need a Python interpreter to run it.
|
||||
# For example, ActiveState Python, which exists for windows.
|
||||
|
@ -10,7 +11,7 @@
|
|||
# Changelog epubtest
|
||||
# 1.00 - Cut to epubtest.py, testing ePub files only by Apprentice Alf
|
||||
# 1.01 - Added routine for use by Windows DeDRM
|
||||
# 2.00 - Added Python 3 compatibility, September 2020
|
||||
# 2.00 - Python 3, September 2020
|
||||
#
|
||||
# Written in 2011 by Paul Durrant
|
||||
# Released with unlicense. See http://unlicense.org/
|
||||
|
@ -45,9 +46,6 @@
|
|||
# It's still polite to give attribution if you do reuse this code.
|
||||
#
|
||||
|
||||
from __future__ import with_statement
|
||||
from __future__ import print_function
|
||||
|
||||
__version__ = '2.0'
|
||||
|
||||
import sys, struct, os, traceback
|
||||
|
@ -112,7 +110,7 @@ def unicode_argv():
|
|||
xrange(start, argc.value)]
|
||||
# if we don't have any arguments at all, just pass back script name
|
||||
# this should never happen
|
||||
return [u"epubtest.py"]
|
||||
return ["epubtest.py"]
|
||||
else:
|
||||
argvencoding = sys.stdin.encoding
|
||||
if argvencoding == None:
|
||||
|
|
|
@ -1,13 +1,9 @@
|
|||
#!/usr/bin/env python
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# erdr2pml.py
|
||||
# Copyright © 2008 The Dark Reverser
|
||||
# Copyright © 2008-2020 The Dark Reverser, Apprentice Harper et al.
|
||||
#
|
||||
# Modified 2008–2012 by some_updates, DiapDealer and Apprentice Alf
|
||||
|
||||
# This is a python script. You need a Python interpreter to run it.
|
||||
# For example, ActiveState Python, which exists for windows.
|
||||
# Changelog
|
||||
#
|
||||
# Based on ereader2html version 0.08 plus some later small fixes
|
||||
|
@ -89,10 +85,10 @@ class SafeUnbuffered:
|
|||
if self.encoding == None:
|
||||
self.encoding = "utf-8"
|
||||
def write(self, data):
|
||||
if isinstance(data,bytes):
|
||||
if isinstance(data,str):
|
||||
data = data.encode(self.encoding,"replace")
|
||||
self.stream.write(data)
|
||||
self.stream.flush()
|
||||
self.stream.buffer.write(data)
|
||||
self.stream.buffer.flush()
|
||||
def __getattr__(self, attr):
|
||||
return getattr(self.stream, attr)
|
||||
|
||||
|
@ -130,7 +126,7 @@ def unicode_argv():
|
|||
range(start, argc.value)]
|
||||
# if we don't have any arguments at all, just pass back script name
|
||||
# this should never happen
|
||||
return [u"mobidedrm.py"]
|
||||
return ["mobidedrm.py"]
|
||||
else:
|
||||
argvencoding = sys.stdin.encoding
|
||||
if argvencoding == None:
|
||||
|
@ -230,16 +226,16 @@ class Sectionizer(object):
|
|||
# and with some (heavily edited) code from Paul Durrant's kindlenamer.py
|
||||
def sanitizeFileName(name):
|
||||
# substitute filename unfriendly characters
|
||||
name = name.replace(u"<",u"[").replace(u">",u"]").replace(u" : ",u" – ").replace(u": ",u" – ").replace(u":",u"—").replace(u"/",u"_").replace(u"\\",u"_").replace(u"|",u"_").replace(u"\"",u"\'")
|
||||
name = name.replace("<","[").replace(">","]").replace(" : "," – ").replace(": "," – ").replace(":","—").replace("/","_").replace("\\","_").replace("|","_").replace("\"","\'")
|
||||
# delete control characters
|
||||
name = u"".join(char for char in name if ord(char)>=32)
|
||||
name = "".join(char for char in name if ord(char)>=32)
|
||||
# white space to single space, delete leading and trailing while space
|
||||
name = re.sub(r"\s", u" ", name).strip()
|
||||
name = re.sub(r"\s", " ", name).strip()
|
||||
# remove leading dots
|
||||
while len(name)>0 and name[0] == u".":
|
||||
while len(name)>0 and name[0] == ".":
|
||||
name = name[1:]
|
||||
# remove trailing dots (Windows doesn't like them)
|
||||
if name.endswith(u'.'):
|
||||
if name.endswith("."):
|
||||
name = name[:-1]
|
||||
return name
|
||||
|
||||
|
@ -472,35 +468,35 @@ def decryptBook(infile, outpath, make_pmlz, user_key):
|
|||
# outpath is actually pmlz name
|
||||
pmlzname = outpath
|
||||
outdir = tempfile.mkdtemp()
|
||||
imagedirpath = os.path.join(outdir,u"images")
|
||||
imagedirpath = os.path.join(outdir,"images")
|
||||
else:
|
||||
pmlzname = None
|
||||
outdir = outpath
|
||||
imagedirpath = os.path.join(outdir,bookname + u"_img")
|
||||
imagedirpath = os.path.join(outdir,bookname + "_img")
|
||||
|
||||
try:
|
||||
if not os.path.exists(outdir):
|
||||
os.makedirs(outdir)
|
||||
print(u"Decoding File")
|
||||
print("Decoding File")
|
||||
sect =Sectionizer(infile, 'PNRdPPrs')
|
||||
er = EreaderProcessor(sect, user_key)
|
||||
|
||||
if er.getNumImages() > 0:
|
||||
print(u"Extracting images")
|
||||
print("Extracting images")
|
||||
if not os.path.exists(imagedirpath):
|
||||
os.makedirs(imagedirpath)
|
||||
for i in range(er.getNumImages()):
|
||||
name, contents = er.getImage(i)
|
||||
open(os.path.join(imagedirpath, name), 'wb').write(contents)
|
||||
|
||||
print(u"Extracting pml")
|
||||
print("Extracting pml")
|
||||
pml_string = er.getText()
|
||||
pmlfilename = bookname + ".pml"
|
||||
open(os.path.join(outdir, pmlfilename),'wb').write(cleanPML(pml_string))
|
||||
if pmlzname is not None:
|
||||
import zipfile
|
||||
import shutil
|
||||
print(u"Creating PMLZ file {0}".format(os.path.basename(pmlzname)))
|
||||
print("Creating PMLZ file {0}".format(os.path.basename(pmlzname)))
|
||||
myZipFile = zipfile.ZipFile(pmlzname,'w',zipfile.ZIP_STORED, False)
|
||||
list = os.listdir(outdir)
|
||||
for filename in list:
|
||||
|
@ -519,33 +515,33 @@ def decryptBook(infile, outpath, make_pmlz, user_key):
|
|||
myZipFile.close()
|
||||
# remove temporary directory
|
||||
shutil.rmtree(outdir, True)
|
||||
print(u"Output is {0}".format(pmlzname))
|
||||
else :
|
||||
print(u"Output is in {0}".format(outdir))
|
||||
print("Output is {0}".format(pmlzname))
|
||||
else
|
||||
print("Output is in {0}".format(outdir))
|
||||
print("done")
|
||||
except ValueError as e:
|
||||
print(u"Error: {0}".format(e))
|
||||
print("Error: {0}".format(e))
|
||||
traceback.print_exc()
|
||||
return 1
|
||||
return 0
|
||||
|
||||
|
||||
def usage():
|
||||
print(u"Converts DRMed eReader books to PML Source")
|
||||
print(u"Usage:")
|
||||
print(u" erdr2pml [options] infile.pdb [outpath] \"your name\" credit_card_number")
|
||||
print(u" ")
|
||||
print(u"Options: ")
|
||||
print(u" -h prints this message")
|
||||
print(u" -p create PMLZ instead of source folder")
|
||||
print(u" --make-pmlz create PMLZ instead of source folder")
|
||||
print(u" ")
|
||||
print(u"Note:")
|
||||
print(u" if outpath is ommitted, creates source in 'infile_Source' folder")
|
||||
print(u" if outpath is ommitted and pmlz option, creates PMLZ 'infile.pmlz'")
|
||||
print(u" if source folder created, images are in infile_img folder")
|
||||
print(u" if pmlz file created, images are in images folder")
|
||||
print(u" It's enough to enter the last 8 digits of the credit card number")
|
||||
print("Converts DRMed eReader books to PML Source")
|
||||
print("Usage:")
|
||||
print(" erdr2pml [options] infile.pdb [outpath] \"your name\" credit_card_number")
|
||||
print(" ")
|
||||
print("Options: ")
|
||||
print(" -h prints this message")
|
||||
print(" -p create PMLZ instead of source folder")
|
||||
print(" --make-pmlz create PMLZ instead of source folder")
|
||||
print(" ")
|
||||
print("Note:")
|
||||
print(" if outpath is ommitted, creates source in 'infile_Source' folder")
|
||||
print(" if outpath is ommitted and pmlz option, creates PMLZ 'infile.pmlz'")
|
||||
print(" if source folder created, images are in infile_img folder")
|
||||
print(" if pmlz file created, images are in images folder")
|
||||
print(" It's enough to enter the last 8 digits of the credit card number")
|
||||
return
|
||||
|
||||
def getuser_key(name,cc):
|
||||
|
@ -554,7 +550,7 @@ def getuser_key(name,cc):
|
|||
return struct.pack('>LL', binascii.crc32(newname) & 0xffffffff,binascii.crc32(cc[-8:])& 0xffffffff)
|
||||
|
||||
def cli_main():
|
||||
print(u"eRdr2Pml v{0}. Copyright © 2009–2012 The Dark Reverser et al.".format(__version__))
|
||||
print("eRdr2Pml v{0}. Copyright © 2009–2020 The Dark Reverser et al.".format(__version__))
|
||||
|
||||
argv=unicode_argv()
|
||||
try:
|
||||
|
@ -580,9 +576,9 @@ def cli_main():
|
|||
if len(args)==3:
|
||||
infile, name, cc = args
|
||||
if make_pmlz:
|
||||
outpath = os.path.splitext(infile)[0] + u".pmlz"
|
||||
outpath = os.path.splitext(infile)[0] + ".pmlz"
|
||||
else:
|
||||
outpath = os.path.splitext(infile)[0] + u"_Source"
|
||||
outpath = os.path.splitext(infile)[0] + "_Source"
|
||||
elif len(args)==4:
|
||||
infile, outpath, name, cc = args
|
||||
|
||||
|
|
|
@ -1,16 +1,14 @@
|
|||
#! /usr/bin/python
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
# vim:ts=4:sw=4:softtabstop=4:smarttab:expandtab
|
||||
# Added Python 3 compatibility for calibre 5.0
|
||||
|
||||
from __future__ import print_function
|
||||
from .convert2xml import encodeNumber
|
||||
# Python 3 for calibre 5.0
|
||||
|
||||
class Unbuffered:
|
||||
def __init__(self, stream):
|
||||
self.stream = stream
|
||||
def write(self, data):
|
||||
self.stream.write(data)
|
||||
self.stream.flush()
|
||||
self.stream.buffer.write(data)
|
||||
self.stream.buffer.flush()
|
||||
def __getattr__(self, attr):
|
||||
return getattr(self.stream, attr)
|
||||
|
||||
|
|
|
@ -1,28 +1,13 @@
|
|||
#!/usr/bin/env python
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from __future__ import with_statement
|
||||
from __future__ import print_function
|
||||
|
||||
# ignobleepub.pyw, version 4.1
|
||||
# Copyright © 2009-2010 by i♥cabbages
|
||||
# ignobleepub.py
|
||||
# Copyright © 2009-2020 by i♥cabbages, Apprentice Harper et al.
|
||||
|
||||
# Released under the terms of the GNU General Public Licence, version 3
|
||||
# <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
|
||||
|
@ -38,7 +23,7 @@ from __future__ import print_function
|
|||
# 3.9 - moved unicode_argv call inside main for Windows DeDRM compatibility
|
||||
# 4.0 - Work if TkInter is missing
|
||||
# 4.1 - Import tkFileDialog, don't assume something else will import it.
|
||||
# 5.0 - Added Python 3 compatibility for calibre 5.0
|
||||
# 5.0 - Python 3 for calibre 5.0
|
||||
|
||||
"""
|
||||
Decrypt Barnes & Noble encrypted ePub books.
|
||||
|
@ -66,10 +51,10 @@ class SafeUnbuffered:
|
|||
if self.encoding == None:
|
||||
self.encoding = "utf-8"
|
||||
def write(self, data):
|
||||
if isinstance(data,bytes):
|
||||
if isinstance(data,str):
|
||||
data = data.encode(self.encoding,"replace")
|
||||
self.stream.write(data)
|
||||
self.stream.flush()
|
||||
self.stream.buffer.write(data)
|
||||
self.stream.buffer.flush()
|
||||
def __getattr__(self, attr):
|
||||
return getattr(self.stream, attr)
|
||||
|
||||
|
@ -108,7 +93,7 @@ def unicode_argv():
|
|||
start = argc.value - len(sys.argv)
|
||||
return [argv[i] for i in
|
||||
range(start, argc.value)]
|
||||
return [u"ineptepub.py"]
|
||||
return ["ineptepub.py"]
|
||||
else:
|
||||
argvencoding = sys.stdin.encoding
|
||||
if argvencoding == None:
|
||||
|
@ -257,14 +242,14 @@ def ignobleBook(inpath):
|
|||
|
||||
def decryptBook(keyb64, inpath, outpath):
|
||||
if AES is None:
|
||||
raise IGNOBLEError(u"PyCrypto or OpenSSL must be installed.")
|
||||
raise IGNOBLEError("PyCrypto or OpenSSL must be installed.")
|
||||
key = keyb64.decode('base64')[:16]
|
||||
aes = AES(key)
|
||||
with closing(ZipFile(open(inpath, 'rb'))) as inf:
|
||||
namelist = set(inf.namelist())
|
||||
if 'META-INF/rights.xml' not in namelist or \
|
||||
'META-INF/encryption.xml' not in namelist:
|
||||
print(u"{0:s} is DRM-free.".format(os.path.basename(inpath)))
|
||||
print("{0:s} is DRM-free.".format(os.path.basename(inpath)))
|
||||
return 1
|
||||
for name in META_NAMES:
|
||||
namelist.remove(name)
|
||||
|
@ -274,7 +259,7 @@ def decryptBook(keyb64, inpath, outpath):
|
|||
expr = './/%s' % (adept('encryptedKey'),)
|
||||
bookkey = ''.join(rights.findtext(expr))
|
||||
if len(bookkey) != 64:
|
||||
print(u"{0:s} is not a secure Barnes & Noble ePub.".format(os.path.basename(inpath)))
|
||||
print("{0:s} is not a secure Barnes & Noble ePub.".format(os.path.basename(inpath)))
|
||||
return 1
|
||||
bookkey = aes.decrypt(bookkey.decode('base64'))
|
||||
bookkey = bookkey[:-ord(bookkey[-1])]
|
||||
|
@ -317,7 +302,7 @@ def decryptBook(keyb64, inpath, outpath):
|
|||
pass
|
||||
outf.writestr(zi, decryptor.decrypt(path, data))
|
||||
except:
|
||||
print(u"Could not decrypt {0:s} because of an exception:\n{1:s}".format(os.path.basename(inpath), traceback.format_exc()))
|
||||
print("Could not decrypt {0:s} because of an exception:\n{1:s}".format(os.path.basename(inpath), traceback.format_exc()))
|
||||
return 2
|
||||
return 0
|
||||
|
||||
|
@ -328,13 +313,13 @@ def cli_main():
|
|||
argv=unicode_argv()
|
||||
progname = os.path.basename(argv[0])
|
||||
if len(argv) != 4:
|
||||
print(u"usage: {0} <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():
|
||||
|
@ -350,43 +335,43 @@ def gui_main():
|
|||
class DecryptionDialog(Tkinter.Frame):
|
||||
def __init__(self, root):
|
||||
Tkinter.Frame.__init__(self, root, border=5)
|
||||
self.status = Tkinter.Label(self, text=u"Select files for decryption")
|
||||
self.status = Tkinter.Label(self, text="Select files for decryption")
|
||||
self.status.pack(fill=Tkconstants.X, expand=1)
|
||||
body = Tkinter.Frame(self)
|
||||
body.pack(fill=Tkconstants.X, expand=1)
|
||||
sticky = Tkconstants.E + Tkconstants.W
|
||||
body.grid_columnconfigure(1, weight=2)
|
||||
Tkinter.Label(body, text=u"Key file").grid(row=0)
|
||||
Tkinter.Label(body, text="Key file").grid(row=0)
|
||||
self.keypath = Tkinter.Entry(body, width=30)
|
||||
self.keypath.grid(row=0, column=1, sticky=sticky)
|
||||
if os.path.exists(u"bnepubkey.b64"):
|
||||
self.keypath.insert(0, u"bnepubkey.b64")
|
||||
button = Tkinter.Button(body, text=u"...", command=self.get_keypath)
|
||||
if os.path.exists("bnepubkey.b64"):
|
||||
self.keypath.insert(0, "bnepubkey.b64")
|
||||
button = Tkinter.Button(body, text="...", command=self.get_keypath)
|
||||
button.grid(row=0, column=2)
|
||||
Tkinter.Label(body, text=u"Input file").grid(row=1)
|
||||
Tkinter.Label(body, text="Input file").grid(row=1)
|
||||
self.inpath = Tkinter.Entry(body, width=30)
|
||||
self.inpath.grid(row=1, column=1, sticky=sticky)
|
||||
button = Tkinter.Button(body, text=u"...", command=self.get_inpath)
|
||||
button = Tkinter.Button(body, text="...", command=self.get_inpath)
|
||||
button.grid(row=1, column=2)
|
||||
Tkinter.Label(body, text=u"Output file").grid(row=2)
|
||||
Tkinter.Label(body, text="Output file").grid(row=2)
|
||||
self.outpath = Tkinter.Entry(body, width=30)
|
||||
self.outpath.grid(row=2, column=1, sticky=sticky)
|
||||
button = Tkinter.Button(body, text=u"...", command=self.get_outpath)
|
||||
button = Tkinter.Button(body, text="...", command=self.get_outpath)
|
||||
button.grid(row=2, column=2)
|
||||
buttons = Tkinter.Frame(self)
|
||||
buttons.pack()
|
||||
botton = Tkinter.Button(
|
||||
buttons, text=u"Decrypt", width=10, command=self.decrypt)
|
||||
buttons, text="Decrypt", width=10, command=self.decrypt)
|
||||
botton.pack(side=Tkconstants.LEFT)
|
||||
Tkinter.Frame(buttons, width=10).pack(side=Tkconstants.LEFT)
|
||||
button = Tkinter.Button(
|
||||
buttons, text=u"Quit", width=10, command=self.quit)
|
||||
buttons, text="Quit", width=10, command=self.quit)
|
||||
button.pack(side=Tkconstants.RIGHT)
|
||||
|
||||
def get_keypath(self):
|
||||
keypath = tkFileDialog.askopenfilename(
|
||||
parent=None, title=u"Select Barnes & Noble \'.b64\' key file",
|
||||
defaultextension=u".b64",
|
||||
parent=None, title="Select Barnes & Noble \'.b64\' key file",
|
||||
defaultextension=".b64",
|
||||
filetypes=[('base64-encoded files', '.b64'),
|
||||
('All Files', '.*')])
|
||||
if keypath:
|
||||
|
@ -397,8 +382,8 @@ def gui_main():
|
|||
|
||||
def get_inpath(self):
|
||||
inpath = tkFileDialog.askopenfilename(
|
||||
parent=None, title=u"Select B&N-encrypted ePub file to decrypt",
|
||||
defaultextension=u".epub", filetypes=[('ePub files', '.epub')])
|
||||
parent=None, title="Select B&N-encrypted ePub file to decrypt",
|
||||
defaultextension=".epub", filetypes=[('ePub files', '.epub')])
|
||||
if inpath:
|
||||
inpath = os.path.normpath(inpath)
|
||||
self.inpath.delete(0, Tkconstants.END)
|
||||
|
@ -407,8 +392,8 @@ def gui_main():
|
|||
|
||||
def get_outpath(self):
|
||||
outpath = tkFileDialog.asksaveasfilename(
|
||||
parent=None, title=u"Select unencrypted ePub file to produce",
|
||||
defaultextension=u".epub", filetypes=[('ePub files', '.epub')])
|
||||
parent=None, title="Select unencrypted ePub file to produce",
|
||||
defaultextension=".epub", filetypes=[('ePub files', '.epub')])
|
||||
if outpath:
|
||||
outpath = os.path.normpath(outpath)
|
||||
self.outpath.delete(0, Tkconstants.END)
|
||||
|
@ -420,31 +405,31 @@ def gui_main():
|
|||
inpath = self.inpath.get()
|
||||
outpath = self.outpath.get()
|
||||
if not keypath or not os.path.exists(keypath):
|
||||
self.status['text'] = u"Specified key file does not exist"
|
||||
self.status['text'] = "Specified key file does not exist"
|
||||
return
|
||||
if not inpath or not os.path.exists(inpath):
|
||||
self.status['text'] = u"Specified input file does not exist"
|
||||
self.status['text'] = "Specified input file does not exist"
|
||||
return
|
||||
if not outpath:
|
||||
self.status['text'] = u"Output file not specified"
|
||||
self.status['text'] = "Output file not specified"
|
||||
return
|
||||
if inpath == outpath:
|
||||
self.status['text'] = u"Must have different input and output files"
|
||||
self.status['text'] = "Must have different input and output files"
|
||||
return
|
||||
userkey = open(keypath,'rb').read()
|
||||
self.status['text'] = u"Decrypting..."
|
||||
self.status['text'] = "Decrypting..."
|
||||
try:
|
||||
decrypt_status = decryptBook(userkey, inpath, outpath)
|
||||
except Exception as e:
|
||||
self.status['text'] = u"Error: {0}".format(e.args[0])
|
||||
self.status['text'] = "Error: {0}".format(e.args[0])
|
||||
return
|
||||
if decrypt_status == 0:
|
||||
self.status['text'] = u"File successfully decrypted"
|
||||
self.status['text'] = "File successfully decrypted"
|
||||
else:
|
||||
self.status['text'] = u"The was an error decrypting the file."
|
||||
self.status['text'] = "The was an error decrypting the file."
|
||||
|
||||
root = Tkinter.Tk()
|
||||
root.title(u"Barnes & Noble ePub Decrypter v.{0}".format(__version__))
|
||||
root.title("Barnes & Noble ePub Decrypter v.{0}".format(__version__))
|
||||
root.resizable(True, False)
|
||||
root.minsize(300, 0)
|
||||
DecryptionDialog(root).pack(fill=Tkconstants.X, expand=1)
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
#!/usr/bin/env python
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from __future__ import with_statement
|
||||
from __future__ import print_function
|
||||
|
||||
# ignoblekey.py
|
||||
# Copyright © 2015-2020 Apprentice Alf, Apprentice Harper et al.
|
||||
|
||||
|
@ -15,7 +12,7 @@ from __future__ import print_function
|
|||
# Revision history:
|
||||
# 1.0 - Initial release
|
||||
# 1.1 - remove duplicates and return last key as single key
|
||||
# 2.0 - Added Python 3 compatibility for calibre 5.0
|
||||
# 2.0 - Python 3 for calibre 5.0
|
||||
|
||||
"""
|
||||
Get Barnes & Noble EPUB user key from nook Studio log file
|
||||
|
@ -40,10 +37,10 @@ class SafeUnbuffered:
|
|||
if self.encoding == None:
|
||||
self.encoding = "utf-8"
|
||||
def write(self, data):
|
||||
if isinstance(data,bytes):
|
||||
if isinstance(data,str):
|
||||
data = data.encode(self.encoding,"replace")
|
||||
self.stream.write(data)
|
||||
self.stream.flush()
|
||||
self.stream.buffer.write(data)
|
||||
self.stream.buffer.flush()
|
||||
def __getattr__(self, attr):
|
||||
return getattr(self.stream, attr)
|
||||
|
||||
|
@ -84,7 +81,7 @@ def unicode_argv():
|
|||
range(start, argc.value)]
|
||||
# if we don't have any arguments at all, just pass back script name
|
||||
# this should never happen
|
||||
return [u"ignoblekey.py"]
|
||||
return ["ignoblekey.py"]
|
||||
else:
|
||||
argvencoding = sys.stdin.encoding
|
||||
if argvencoding == None:
|
||||
|
@ -106,15 +103,15 @@ def getNookLogFiles():
|
|||
paths = set()
|
||||
if 'LOCALAPPDATA' in os.environ.keys():
|
||||
# Python 2.x does not return unicode env. Use Python 3.x
|
||||
path = winreg.ExpandEnvironmentStrings(u"%LOCALAPPDATA%")
|
||||
path = winreg.ExpandEnvironmentStrings("%LOCALAPPDATA%")
|
||||
if os.path.isdir(path):
|
||||
paths.add(path)
|
||||
if 'USERPROFILE' in os.environ.keys():
|
||||
# Python 2.x does not return unicode env. Use Python 3.x
|
||||
path = winreg.ExpandEnvironmentStrings(u"%USERPROFILE%")+u"\\AppData\\Local"
|
||||
path = winreg.ExpandEnvironmentStrings("%USERPROFILE%")+"\\AppData\\Local"
|
||||
if os.path.isdir(path):
|
||||
paths.add(path)
|
||||
path = winreg.ExpandEnvironmentStrings(u"%USERPROFILE%")+u"\\AppData\\Roaming"
|
||||
path = winreg.ExpandEnvironmentStrings("%USERPROFILE%")+"\\AppData\\Roaming"
|
||||
if os.path.isdir(path):
|
||||
paths.add(path)
|
||||
# User Shell Folders show take precedent over Shell Folders if present
|
||||
|
@ -200,7 +197,7 @@ def nookkeys(files = []):
|
|||
for file in files:
|
||||
fileKeys = getKeysFromLog(file)
|
||||
if fileKeys:
|
||||
print(u"Found {0} keys in the Nook Study log files".format(len(fileKeys)))
|
||||
print("Found {0} keys in the Nook Study log files".format(len(fileKeys)))
|
||||
keys.extend(fileKeys)
|
||||
return list(set(keys))
|
||||
|
||||
|
@ -213,27 +210,27 @@ def getkey(outpath, files=[]):
|
|||
outfile = outpath
|
||||
with open(outfile, 'w') as keyfileout:
|
||||
keyfileout.write(keys[-1])
|
||||
print(u"Saved a key to {0}".format(outfile))
|
||||
print("Saved a key to {0}".format(outfile))
|
||||
else:
|
||||
keycount = 0
|
||||
for key in keys:
|
||||
while True:
|
||||
keycount += 1
|
||||
outfile = os.path.join(outpath,u"nookkey{0:d}.b64".format(keycount))
|
||||
outfile = os.path.join(outpath,"nookkey{0:d}.b64".format(keycount))
|
||||
if not os.path.exists(outfile):
|
||||
break
|
||||
with open(outfile, 'w') as keyfileout:
|
||||
keyfileout.write(key)
|
||||
print(u"Saved a key to {0}".format(outfile))
|
||||
print("Saved a key to {0}".format(outfile))
|
||||
return True
|
||||
return False
|
||||
|
||||
def usage(progname):
|
||||
print(u"Finds the nook Study encryption keys.")
|
||||
print(u"Keys are saved to the current directory, or a specified output directory.")
|
||||
print(u"If a file name is passed instead of a directory, only the first key is saved, in that file.")
|
||||
print(u"Usage:")
|
||||
print(u" {0:s} [-h] [-k <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():
|
||||
|
@ -241,12 +238,12 @@ def cli_main():
|
|||
sys.stderr=SafeUnbuffered(sys.stderr)
|
||||
argv=unicode_argv()
|
||||
progname = os.path.basename(argv[0])
|
||||
print(u"{0} v{1}\nCopyright © 2015 Apprentice Alf".format(progname,__version__))
|
||||
print("{0} v{1}\nCopyright © 2015 Apprentice Alf".format(progname,__version__))
|
||||
|
||||
try:
|
||||
opts, args = getopt.getopt(argv[1:], "hk:")
|
||||
except getopt.GetoptError as err:
|
||||
print(u"Error in options or arguments: {0}".format(err.args[0]))
|
||||
print("Error in options or arguments: {0}".format(err.args[0]))
|
||||
usage(progname)
|
||||
sys.exit(2)
|
||||
|
||||
|
@ -275,7 +272,7 @@ def cli_main():
|
|||
outpath = os.path.realpath(os.path.normpath(outpath))
|
||||
|
||||
if not getkey(outpath, files):
|
||||
print(u"Could not retrieve nook Study key.")
|
||||
print("Could not retrieve nook Study key.")
|
||||
return 0
|
||||
|
||||
|
||||
|
@ -291,7 +288,7 @@ def gui_main():
|
|||
class ExceptionDialog(Tkinter.Frame):
|
||||
def __init__(self, root, text):
|
||||
Tkinter.Frame.__init__(self, root, border=5)
|
||||
label = Tkinter.Label(self, text=u"Unexpected error:",
|
||||
label = Tkinter.Label(self, text="Unexpected error:",
|
||||
anchor=Tkconstants.W, justify=Tkconstants.LEFT)
|
||||
label.pack(fill=Tkconstants.X, expand=0)
|
||||
self.text = Tkinter.Text(self)
|
||||
|
@ -312,16 +309,16 @@ def gui_main():
|
|||
print(key)
|
||||
while True:
|
||||
keycount += 1
|
||||
outfile = os.path.join(progpath,u"nookkey{0:d}.b64".format(keycount))
|
||||
outfile = os.path.join(progpath,"nookkey{0:d}.b64".format(keycount))
|
||||
if not os.path.exists(outfile):
|
||||
break
|
||||
|
||||
with open(outfile, 'w') as keyfileout:
|
||||
keyfileout.write(key)
|
||||
success = True
|
||||
tkMessageBox.showinfo(progname, u"Key successfully retrieved to {0}".format(outfile))
|
||||
tkMessageBox.showinfo(progname, "Key successfully retrieved to {0}".format(outfile))
|
||||
except DrmException as e:
|
||||
tkMessageBox.showerror(progname, u"Error: {0}".format(str(e)))
|
||||
tkMessageBox.showerror(progname, "Error: {0}".format(str(e)))
|
||||
except Exception:
|
||||
root.wm_state('normal')
|
||||
root.title(progname)
|
||||
|
|
|
@ -1,10 +1,7 @@
|
|||
#!/usr/bin/env python
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from __future__ import with_statement
|
||||
from __future__ import print_function
|
||||
|
||||
# ignoblekeyfetch.pyw, version 2.0
|
||||
# ignoblekeyfetch.py
|
||||
# Copyright © 2015-2020 Apprentice Harper et al.
|
||||
|
||||
# Released under the terms of the GNU General Public Licence, version 3
|
||||
|
@ -25,14 +22,14 @@ from __future__ import print_function
|
|||
# Revision history:
|
||||
# 1.0 - Initial version
|
||||
# 1.1 - Try second URL if first one fails
|
||||
# 2.0 - Added Python 3 compatibility for calibre 5.0
|
||||
# 2.0 - Python 3 for calibre 5.0
|
||||
|
||||
"""
|
||||
Fetch Barnes & Noble EPUB user key from B&N servers using email and password
|
||||
"""
|
||||
|
||||
__license__ = 'GPL v3'
|
||||
__version__ = "1.1"
|
||||
__version__ = "2.0"
|
||||
|
||||
import sys
|
||||
import os
|
||||
|
@ -91,7 +88,7 @@ def unicode_argv():
|
|||
range(start, argc.value)]
|
||||
# if we don't have any arguments at all, just pass back script name
|
||||
# this should never happen
|
||||
return [u"ignoblekeyfetch.py"]
|
||||
return ["ignoblekeyfetch.py"]
|
||||
else:
|
||||
argvencoding = sys.stdin.encoding
|
||||
if argvencoding == None:
|
||||
|
@ -157,14 +154,14 @@ def cli_main():
|
|||
argv=unicode_argv()
|
||||
progname = os.path.basename(argv[0])
|
||||
if len(argv) != 4:
|
||||
print(u"usage: {0} <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
|
||||
|
||||
|
||||
|
@ -181,38 +178,38 @@ def gui_main():
|
|||
class DecryptionDialog(Tkinter.Frame):
|
||||
def __init__(self, root):
|
||||
Tkinter.Frame.__init__(self, root, border=5)
|
||||
self.status = Tkinter.Label(self, text=u"Enter parameters")
|
||||
self.status = Tkinter.Label(self, text="Enter parameters")
|
||||
self.status.pack(fill=Tkconstants.X, expand=1)
|
||||
body = Tkinter.Frame(self)
|
||||
body.pack(fill=Tkconstants.X, expand=1)
|
||||
sticky = Tkconstants.E + Tkconstants.W
|
||||
body.grid_columnconfigure(1, weight=2)
|
||||
Tkinter.Label(body, text=u"Account email address").grid(row=0)
|
||||
Tkinter.Label(body, text="Account email address").grid(row=0)
|
||||
self.name = Tkinter.Entry(body, width=40)
|
||||
self.name.grid(row=0, column=1, sticky=sticky)
|
||||
Tkinter.Label(body, text=u"Account password").grid(row=1)
|
||||
Tkinter.Label(body, text="Account password").grid(row=1)
|
||||
self.ccn = Tkinter.Entry(body, width=40)
|
||||
self.ccn.grid(row=1, column=1, sticky=sticky)
|
||||
Tkinter.Label(body, text=u"Output file").grid(row=2)
|
||||
Tkinter.Label(body, text="Output file").grid(row=2)
|
||||
self.keypath = Tkinter.Entry(body, width=40)
|
||||
self.keypath.grid(row=2, column=1, sticky=sticky)
|
||||
self.keypath.insert(2, u"bnepubkey.b64")
|
||||
button = Tkinter.Button(body, text=u"...", command=self.get_keypath)
|
||||
self.keypath.insert(2, "bnepubkey.b64")
|
||||
button = Tkinter.Button(body, text="...", command=self.get_keypath)
|
||||
button.grid(row=2, column=2)
|
||||
buttons = Tkinter.Frame(self)
|
||||
buttons.pack()
|
||||
botton = Tkinter.Button(
|
||||
buttons, text=u"Fetch", width=10, command=self.generate)
|
||||
buttons, text="Fetch", width=10, command=self.generate)
|
||||
botton.pack(side=Tkconstants.LEFT)
|
||||
Tkinter.Frame(buttons, width=10).pack(side=Tkconstants.LEFT)
|
||||
button = Tkinter.Button(
|
||||
buttons, text=u"Quit", width=10, command=self.quit)
|
||||
buttons, text="Quit", width=10, command=self.quit)
|
||||
button.pack(side=Tkconstants.RIGHT)
|
||||
|
||||
def get_keypath(self):
|
||||
keypath = tkFileDialog.asksaveasfilename(
|
||||
parent=None, title=u"Select B&N ePub key file to produce",
|
||||
defaultextension=u".b64",
|
||||
parent=None, title="Select B&N ePub key file to produce",
|
||||
defaultextension=".b64",
|
||||
filetypes=[('base64-encoded files', '.b64'),
|
||||
('All Files', '.*')])
|
||||
if keypath:
|
||||
|
@ -226,28 +223,28 @@ def gui_main():
|
|||
password = self.ccn.get()
|
||||
keypath = self.keypath.get()
|
||||
if not email:
|
||||
self.status['text'] = u"Email address not given"
|
||||
self.status['text'] = "Email address not given"
|
||||
return
|
||||
if not password:
|
||||
self.status['text'] = u"Account password not given"
|
||||
self.status['text'] = "Account password not given"
|
||||
return
|
||||
if not keypath:
|
||||
self.status['text'] = u"Output keyfile path not set"
|
||||
self.status['text'] = "Output keyfile path not set"
|
||||
return
|
||||
self.status['text'] = u"Fetching..."
|
||||
self.status['text'] = "Fetching..."
|
||||
try:
|
||||
userkey = fetch_key(email, password)
|
||||
except Exception as e:
|
||||
self.status['text'] = u"Error: {0}".format(e.args[0])
|
||||
self.status['text'] = "Error: {0}".format(e.args[0])
|
||||
return
|
||||
if len(userkey) == 28:
|
||||
open(keypath,'wb').write(userkey)
|
||||
self.status['text'] = u"Keyfile fetched successfully"
|
||||
self.status['text'] = "Keyfile fetched successfully"
|
||||
else:
|
||||
self.status['text'] = u"Keyfile fetch failed."
|
||||
self.status['text'] = "Keyfile fetch failed."
|
||||
|
||||
root = Tkinter.Tk()
|
||||
root.title(u"Barnes & Noble ePub Keyfile Fetch v.{0}".format(__version__))
|
||||
root.title("Barnes & Noble ePub Keyfile Fetch v.{0}".format(__version__))
|
||||
root.resizable(True, False)
|
||||
root.minsize(300, 0)
|
||||
DecryptionDialog(root).pack(fill=Tkconstants.X, expand=1)
|
||||
|
|
|
@ -1,10 +1,7 @@
|
|||
#!/usr/bin/env python
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from __future__ import with_statement
|
||||
from __future__ import print_function
|
||||
|
||||
# ignoblekeygen.pyw
|
||||
# ignoblekeygen.py
|
||||
# Copyright © 2009-2020 i♥cabbages, Apprentice Harper et al.
|
||||
|
||||
# Released under the terms of the GNU General Public Licence, version 3
|
||||
|
@ -100,7 +97,7 @@ def unicode_argv():
|
|||
range(start, argc.value)]
|
||||
# if we don't have any arguments at all, just pass back script name
|
||||
# this should never happen
|
||||
return [u"ignoblekeygen.py"]
|
||||
return ["ignoblekeygen.py"]
|
||||
else:
|
||||
argvencoding = sys.stdin.encoding
|
||||
if argvencoding == None:
|
||||
|
@ -228,7 +225,7 @@ def cli_main():
|
|||
(progname,))
|
||||
return 1
|
||||
if len(argv) != 4:
|
||||
print(u"usage: {0} <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)
|
||||
|
@ -249,38 +246,38 @@ def gui_main():
|
|||
class DecryptionDialog(Tkinter.Frame):
|
||||
def __init__(self, root):
|
||||
Tkinter.Frame.__init__(self, root, border=5)
|
||||
self.status = Tkinter.Label(self, text=u"Enter parameters")
|
||||
self.status = Tkinter.Label(self, text="Enter parameters")
|
||||
self.status.pack(fill=Tkconstants.X, expand=1)
|
||||
body = Tkinter.Frame(self)
|
||||
body.pack(fill=Tkconstants.X, expand=1)
|
||||
sticky = Tkconstants.E + Tkconstants.W
|
||||
body.grid_columnconfigure(1, weight=2)
|
||||
Tkinter.Label(body, text=u"Account Name").grid(row=0)
|
||||
Tkinter.Label(body, text="Account Name").grid(row=0)
|
||||
self.name = Tkinter.Entry(body, width=40)
|
||||
self.name.grid(row=0, column=1, sticky=sticky)
|
||||
Tkinter.Label(body, text=u"CC#").grid(row=1)
|
||||
Tkinter.Label(body, text="CC#").grid(row=1)
|
||||
self.ccn = Tkinter.Entry(body, width=40)
|
||||
self.ccn.grid(row=1, column=1, sticky=sticky)
|
||||
Tkinter.Label(body, text=u"Output file").grid(row=2)
|
||||
Tkinter.Label(body, text="Output file").grid(row=2)
|
||||
self.keypath = Tkinter.Entry(body, width=40)
|
||||
self.keypath.grid(row=2, column=1, sticky=sticky)
|
||||
self.keypath.insert(2, u"bnepubkey.b64")
|
||||
button = Tkinter.Button(body, text=u"...", command=self.get_keypath)
|
||||
self.keypath.insert(2, "bnepubkey.b64")
|
||||
button = Tkinter.Button(body, text="...", command=self.get_keypath)
|
||||
button.grid(row=2, column=2)
|
||||
buttons = Tkinter.Frame(self)
|
||||
buttons.pack()
|
||||
botton = Tkinter.Button(
|
||||
buttons, text=u"Generate", width=10, command=self.generate)
|
||||
buttons, text="Generate", width=10, command=self.generate)
|
||||
botton.pack(side=Tkconstants.LEFT)
|
||||
Tkinter.Frame(buttons, width=10).pack(side=Tkconstants.LEFT)
|
||||
button = Tkinter.Button(
|
||||
buttons, text=u"Quit", width=10, command=self.quit)
|
||||
buttons, text="Quit", width=10, command=self.quit)
|
||||
button.pack(side=Tkconstants.RIGHT)
|
||||
|
||||
def get_keypath(self):
|
||||
keypath = tkFileDialog.asksaveasfilename(
|
||||
parent=None, title=u"Select B&N ePub key file to produce",
|
||||
defaultextension=u".b64",
|
||||
parent=None, title="Select B&N ePub key file to produce",
|
||||
defaultextension=".b64",
|
||||
filetypes=[('base64-encoded files', '.b64'),
|
||||
('All Files', '.*')])
|
||||
if keypath:
|
||||
|
@ -294,22 +291,22 @@ def gui_main():
|
|||
ccn = self.ccn.get()
|
||||
keypath = self.keypath.get()
|
||||
if not name:
|
||||
self.status['text'] = u"Name not specified"
|
||||
self.status['text'] = "Name not specified"
|
||||
return
|
||||
if not ccn:
|
||||
self.status['text'] = u"Credit card number not specified"
|
||||
self.status['text'] = "Credit card number not specified"
|
||||
return
|
||||
if not keypath:
|
||||
self.status['text'] = u"Output keyfile path not specified"
|
||||
self.status['text'] = "Output keyfile path not specified"
|
||||
return
|
||||
self.status['text'] = u"Generating..."
|
||||
self.status['text'] = "Generating..."
|
||||
try:
|
||||
userkey = generate_key(name, ccn)
|
||||
except Exception as e:
|
||||
self.status['text'] = u"Error: (0}".format(e.args[0])
|
||||
self.status['text'] = "Error: (0}".format(e.args[0])
|
||||
return
|
||||
open(keypath,'wb').write(userkey)
|
||||
self.status['text'] = u"Keyfile successfully generated"
|
||||
self.status['text'] = "Keyfile successfully generated"
|
||||
|
||||
root = Tkinter.Tk()
|
||||
if AES is None:
|
||||
|
@ -319,7 +316,7 @@ def gui_main():
|
|||
"This script requires OpenSSL or PyCrypto, which must be installed "
|
||||
"separately. Read the top-of-script comment for details.")
|
||||
return 1
|
||||
root.title(u"Barnes & Noble ePub Keyfile Generator v.{0}".format(__version__))
|
||||
root.title("Barnes & Noble ePub Keyfile Generator v.{0}".format(__version__))
|
||||
root.resizable(True, False)
|
||||
root.minsize(300, 0)
|
||||
DecryptionDialog(root).pack(fill=Tkconstants.X, expand=1)
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
#! /usr/bin/python
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from __future__ import with_statement
|
||||
|
||||
# ignoblepdf.py
|
||||
# Copyright © 2009-2020 by Apprentice Harper et al.
|
||||
|
@ -14,6 +13,7 @@ from __future__ import with_statement
|
|||
|
||||
# Revision history:
|
||||
# 0.1 - Initial alpha testing release 2020 by Pu D. Pud
|
||||
# 0.2 - Python 3 for calibre 5.0 (in testing)
|
||||
|
||||
|
||||
"""
|
||||
|
@ -82,7 +82,7 @@ def unicode_argv():
|
|||
start = argc.value - len(sys.argv)
|
||||
return [argv[i] for i in
|
||||
xrange(start, argc.value)]
|
||||
return [u"ignoblepdf.py"]
|
||||
return ["ignoblepdf.py"]
|
||||
else:
|
||||
argvencoding = sys.stdin.encoding
|
||||
if argvencoding == None:
|
||||
|
@ -2005,12 +2005,12 @@ class PDFSerializer(object):
|
|||
|
||||
def decryptBook(userkey, inpath, outpath):
|
||||
if AES is None:
|
||||
raise IGNOBLEError(u"PyCrypto or OpenSSL must be installed.")
|
||||
raise IGNOBLEError("PyCrypto or OpenSSL must be installed.")
|
||||
with open(inpath, 'rb') as inf:
|
||||
#try:
|
||||
serializer = PDFSerializer(inf, userkey)
|
||||
#except:
|
||||
# print u"Error serializing pdf {0}. Probably wrong key.".format(os.path.basename(inpath))
|
||||
# print "Error serializing pdf {0}. Probably wrong key.".format(os.path.basename(inpath))
|
||||
# return 2
|
||||
# hope this will fix the 'bad file descriptor' problem
|
||||
with open(outpath, 'wb') as outf:
|
||||
|
@ -2018,7 +2018,7 @@ def decryptBook(userkey, inpath, outpath):
|
|||
try:
|
||||
serializer.dump(outf)
|
||||
except Exception, e:
|
||||
print u"error writing pdf: {0}".format(e.args[0])
|
||||
print "error writing pdf: {0}".format(e.args[0])
|
||||
return 2
|
||||
return 0
|
||||
|
||||
|
@ -2029,13 +2029,13 @@ def cli_main():
|
|||
argv=unicode_argv()
|
||||
progname = os.path.basename(argv[0])
|
||||
if len(argv) != 4:
|
||||
print u"usage: {0} <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
|
||||
|
||||
|
||||
|
@ -2052,43 +2052,43 @@ def gui_main():
|
|||
class DecryptionDialog(Tkinter.Frame):
|
||||
def __init__(self, root):
|
||||
Tkinter.Frame.__init__(self, root, border=5)
|
||||
self.status = Tkinter.Label(self, text=u"Select files for decryption")
|
||||
self.status = Tkinter.Label(self, text="Select files for decryption")
|
||||
self.status.pack(fill=Tkconstants.X, expand=1)
|
||||
body = Tkinter.Frame(self)
|
||||
body.pack(fill=Tkconstants.X, expand=1)
|
||||
sticky = Tkconstants.E + Tkconstants.W
|
||||
body.grid_columnconfigure(1, weight=2)
|
||||
Tkinter.Label(body, text=u"Key file").grid(row=0)
|
||||
Tkinter.Label(body, text="Key file").grid(row=0)
|
||||
self.keypath = Tkinter.Entry(body, width=30)
|
||||
self.keypath.grid(row=0, column=1, sticky=sticky)
|
||||
if os.path.exists(u"bnpdfkey.b64"):
|
||||
self.keypath.insert(0, u"bnpdfkey.b64")
|
||||
button = Tkinter.Button(body, text=u"...", command=self.get_keypath)
|
||||
if os.path.exists("bnpdfkey.b64"):
|
||||
self.keypath.insert(0, "bnpdfkey.b64")
|
||||
button = Tkinter.Button(body, text="...", command=self.get_keypath)
|
||||
button.grid(row=0, column=2)
|
||||
Tkinter.Label(body, text=u"Input file").grid(row=1)
|
||||
Tkinter.Label(body, text="Input file").grid(row=1)
|
||||
self.inpath = Tkinter.Entry(body, width=30)
|
||||
self.inpath.grid(row=1, column=1, sticky=sticky)
|
||||
button = Tkinter.Button(body, text=u"...", command=self.get_inpath)
|
||||
button = Tkinter.Button(body, text="...", command=self.get_inpath)
|
||||
button.grid(row=1, column=2)
|
||||
Tkinter.Label(body, text=u"Output file").grid(row=2)
|
||||
Tkinter.Label(body, text="Output file").grid(row=2)
|
||||
self.outpath = Tkinter.Entry(body, width=30)
|
||||
self.outpath.grid(row=2, column=1, sticky=sticky)
|
||||
button = Tkinter.Button(body, text=u"...", command=self.get_outpath)
|
||||
button = Tkinter.Button(body, text="...", command=self.get_outpath)
|
||||
button.grid(row=2, column=2)
|
||||
buttons = Tkinter.Frame(self)
|
||||
buttons.pack()
|
||||
botton = Tkinter.Button(
|
||||
buttons, text=u"Decrypt", width=10, command=self.decrypt)
|
||||
buttons, text="Decrypt", width=10, command=self.decrypt)
|
||||
botton.pack(side=Tkconstants.LEFT)
|
||||
Tkinter.Frame(buttons, width=10).pack(side=Tkconstants.LEFT)
|
||||
button = Tkinter.Button(
|
||||
buttons, text=u"Quit", width=10, command=self.quit)
|
||||
buttons, text="Quit", width=10, command=self.quit)
|
||||
button.pack(side=Tkconstants.RIGHT)
|
||||
|
||||
def get_keypath(self):
|
||||
keypath = tkFileDialog.askopenfilename(
|
||||
parent=None, title=u"Select Barnes & Noble \'.b64\' key file",
|
||||
defaultextension=u".b64",
|
||||
parent=None, title="Select Barnes & Noble \'.b64\' key file",
|
||||
defaultextension=".b64",
|
||||
filetypes=[('base64-encoded files', '.b64'),
|
||||
('All Files', '.*')])
|
||||
if keypath:
|
||||
|
@ -2099,8 +2099,8 @@ def gui_main():
|
|||
|
||||
def get_inpath(self):
|
||||
inpath = tkFileDialog.askopenfilename(
|
||||
parent=None, title=u"Select B&N-encrypted PDF file to decrypt",
|
||||
defaultextension=u".pdf", filetypes=[('PDF files', '.pdf')])
|
||||
parent=None, title="Select B&N-encrypted PDF file to decrypt",
|
||||
defaultextension=".pdf", filetypes=[('PDF files', '.pdf')])
|
||||
if inpath:
|
||||
inpath = os.path.normpath(inpath)
|
||||
self.inpath.delete(0, Tkconstants.END)
|
||||
|
@ -2109,8 +2109,8 @@ def gui_main():
|
|||
|
||||
def get_outpath(self):
|
||||
outpath = tkFileDialog.asksaveasfilename(
|
||||
parent=None, title=u"Select unencrypted PDF file to produce",
|
||||
defaultextension=u".pdf", filetypes=[('PDF files', '.pdf')])
|
||||
parent=None, title="Select unencrypted PDF file to produce",
|
||||
defaultextension=".pdf", filetypes=[('PDF files', '.pdf')])
|
||||
if outpath:
|
||||
outpath = os.path.normpath(outpath)
|
||||
self.outpath.delete(0, Tkconstants.END)
|
||||
|
@ -2122,28 +2122,28 @@ def gui_main():
|
|||
inpath = self.inpath.get()
|
||||
outpath = self.outpath.get()
|
||||
if not keypath or not os.path.exists(keypath):
|
||||
self.status['text'] = u"Specified key file does not exist"
|
||||
self.status['text'] = "Specified key file does not exist"
|
||||
return
|
||||
if not inpath or not os.path.exists(inpath):
|
||||
self.status['text'] = u"Specified input file does not exist"
|
||||
self.status['text'] = "Specified input file does not exist"
|
||||
return
|
||||
if not outpath:
|
||||
self.status['text'] = u"Output file not specified"
|
||||
self.status['text'] = "Output file not specified"
|
||||
return
|
||||
if inpath == outpath:
|
||||
self.status['text'] = u"Must have different input and output files"
|
||||
self.status['text'] = "Must have different input and output files"
|
||||
return
|
||||
userkey = open(keypath,'rb').read()
|
||||
self.status['text'] = u"Decrypting..."
|
||||
self.status['text'] = "Decrypting..."
|
||||
try:
|
||||
decrypt_status = decryptBook(userkey, inpath, outpath)
|
||||
except Exception, e:
|
||||
self.status['text'] = u"Error; {0}".format(e.args[0])
|
||||
self.status['text'] = "Error; {0}".format(e.args[0])
|
||||
return
|
||||
if decrypt_status == 0:
|
||||
self.status['text'] = u"File successfully decrypted"
|
||||
self.status['text'] = "File successfully decrypted"
|
||||
else:
|
||||
self.status['text'] = u"The was an error decrypting the file."
|
||||
self.status['text'] = "The was an error decrypting the file."
|
||||
|
||||
|
||||
root = Tkinter.Tk()
|
||||
|
@ -2154,7 +2154,7 @@ def gui_main():
|
|||
"This script requires OpenSSL or PyCrypto, which must be installed "
|
||||
"separately. Read the top-of-script comment for details.")
|
||||
return 1
|
||||
root.title(u"Barnes & Noble PDF Decrypter v.{0}".format(__version__))
|
||||
root.title("Barnes & Noble PDF Decrypter v.{0}".format(__version__))
|
||||
root.resizable(True, False)
|
||||
root.minsize(370, 0)
|
||||
DecryptionDialog(root).pack(fill=Tkconstants.X, expand=1)
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
#!/usr/bin/env python
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from __future__ import with_statement
|
||||
|
||||
# ineptepub.pyw
|
||||
# ineptepub.py
|
||||
# Copyright © 2009-2020 by i♥cabbages, Apprentice Harper et al.
|
||||
|
||||
# Released under the terms of the GNU General Public Licence, version 3
|
||||
|
@ -102,7 +100,7 @@ def unicode_argv():
|
|||
start = argc.value - len(sys.argv)
|
||||
return [argv[i] for i in
|
||||
range(start, argc.value)]
|
||||
return [u"ineptepub.py"]
|
||||
return ["ineptepub.py"]
|
||||
else:
|
||||
argvencoding = sys.stdin.encoding
|
||||
if argvencoding == None:
|
||||
|
@ -393,13 +391,13 @@ def adeptBook(inpath):
|
|||
|
||||
def decryptBook(userkey, inpath, outpath):
|
||||
if AES is None:
|
||||
raise ADEPTError(u"PyCrypto or OpenSSL must be installed.")
|
||||
raise ADEPTError("PyCrypto or OpenSSL must be installed.")
|
||||
rsa = RSA(userkey)
|
||||
with closing(ZipFile(open(inpath, 'rb'))) as inf:
|
||||
namelist = set(inf.namelist())
|
||||
if 'META-INF/rights.xml' not in namelist or \
|
||||
'META-INF/encryption.xml' not in namelist:
|
||||
print(u"{0:s} is DRM-free.".format(os.path.basename(inpath)))
|
||||
print("{0:s} is DRM-free.".format(os.path.basename(inpath)))
|
||||
return 1
|
||||
for name in META_NAMES:
|
||||
namelist.remove(name)
|
||||
|
@ -409,12 +407,12 @@ def decryptBook(userkey, inpath, outpath):
|
|||
expr = './/%s' % (adept('encryptedKey'),)
|
||||
bookkey = ''.join(rights.findtext(expr))
|
||||
if len(bookkey) != 172:
|
||||
print(u"{0:s} is not a secure Adobe Adept ePub.".format(os.path.basename(inpath)))
|
||||
print("{0:s} is not a secure Adobe Adept ePub.".format(os.path.basename(inpath)))
|
||||
return 1
|
||||
bookkey = rsa.decrypt(codecs.decode(bookkey.encode('ascii'), 'base64'))
|
||||
# Padded as per RSAES-PKCS1-v1_5
|
||||
if bookkey[-17] != '\x00' and bookkey[-17] != 0:
|
||||
print(u"Could not decrypt {0:s}. Wrong key".format(os.path.basename(inpath)))
|
||||
print("Could not decrypt {0:s}. Wrong key".format(os.path.basename(inpath)))
|
||||
return 2
|
||||
encryption = inf.read('META-INF/encryption.xml')
|
||||
decryptor = Decryptor(bookkey[-16:], encryption)
|
||||
|
@ -455,7 +453,7 @@ def decryptBook(userkey, inpath, outpath):
|
|||
pass
|
||||
outf.writestr(zi, decryptor.decrypt(path, data))
|
||||
except:
|
||||
print(u"Could not decrypt {0:s} because of an exception:\n{1:s}".format(os.path.basename(inpath), traceback.format_exc()))
|
||||
print("Could not decrypt {0:s} because of an exception:\n{1:s}".format(os.path.basename(inpath), traceback.format_exc()))
|
||||
return 2
|
||||
return 0
|
||||
|
||||
|
@ -466,13 +464,13 @@ def cli_main():
|
|||
argv=unicode_argv()
|
||||
progname = os.path.basename(argv[0])
|
||||
if len(argv) != 4:
|
||||
print(u"usage: {0} <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():
|
||||
|
@ -488,43 +486,43 @@ def gui_main():
|
|||
class DecryptionDialog(Tkinter.Frame):
|
||||
def __init__(self, root):
|
||||
Tkinter.Frame.__init__(self, root, border=5)
|
||||
self.status = Tkinter.Label(self, text=u"Select files for decryption")
|
||||
self.status = Tkinter.Label(self, text="Select files for decryption")
|
||||
self.status.pack(fill=Tkconstants.X, expand=1)
|
||||
body = Tkinter.Frame(self)
|
||||
body.pack(fill=Tkconstants.X, expand=1)
|
||||
sticky = Tkconstants.E + Tkconstants.W
|
||||
body.grid_columnconfigure(1, weight=2)
|
||||
Tkinter.Label(body, text=u"Key file").grid(row=0)
|
||||
Tkinter.Label(body, text="Key file").grid(row=0)
|
||||
self.keypath = Tkinter.Entry(body, width=30)
|
||||
self.keypath.grid(row=0, column=1, sticky=sticky)
|
||||
if os.path.exists(u"adeptkey.der"):
|
||||
self.keypath.insert(0, u"adeptkey.der")
|
||||
button = Tkinter.Button(body, text=u"...", command=self.get_keypath)
|
||||
if os.path.exists("adeptkey.der"):
|
||||
self.keypath.insert(0, "adeptkey.der")
|
||||
button = Tkinter.Button(body, text="...", command=self.get_keypath)
|
||||
button.grid(row=0, column=2)
|
||||
Tkinter.Label(body, text=u"Input file").grid(row=1)
|
||||
Tkinter.Label(body, text="Input file").grid(row=1)
|
||||
self.inpath = Tkinter.Entry(body, width=30)
|
||||
self.inpath.grid(row=1, column=1, sticky=sticky)
|
||||
button = Tkinter.Button(body, text=u"...", command=self.get_inpath)
|
||||
button = Tkinter.Button(body, text="...", command=self.get_inpath)
|
||||
button.grid(row=1, column=2)
|
||||
Tkinter.Label(body, text=u"Output file").grid(row=2)
|
||||
Tkinter.Label(body, text="Output file").grid(row=2)
|
||||
self.outpath = Tkinter.Entry(body, width=30)
|
||||
self.outpath.grid(row=2, column=1, sticky=sticky)
|
||||
button = Tkinter.Button(body, text=u"...", command=self.get_outpath)
|
||||
button = Tkinter.Button(body, text="...", command=self.get_outpath)
|
||||
button.grid(row=2, column=2)
|
||||
buttons = Tkinter.Frame(self)
|
||||
buttons.pack()
|
||||
botton = Tkinter.Button(
|
||||
buttons, text=u"Decrypt", width=10, command=self.decrypt)
|
||||
buttons, text="Decrypt", width=10, command=self.decrypt)
|
||||
botton.pack(side=Tkconstants.LEFT)
|
||||
Tkinter.Frame(buttons, width=10).pack(side=Tkconstants.LEFT)
|
||||
button = Tkinter.Button(
|
||||
buttons, text=u"Quit", width=10, command=self.quit)
|
||||
buttons, text="Quit", width=10, command=self.quit)
|
||||
button.pack(side=Tkconstants.RIGHT)
|
||||
|
||||
def get_keypath(self):
|
||||
keypath = tkFileDialog.askopenfilename(
|
||||
parent=None, title=u"Select Adobe Adept \'.der\' key file",
|
||||
defaultextension=u".der",
|
||||
parent=None, title="Select Adobe Adept \'.der\' key file",
|
||||
defaultextension=".der",
|
||||
filetypes=[('Adobe Adept DER-encoded files', '.der'),
|
||||
('All Files', '.*')])
|
||||
if keypath:
|
||||
|
@ -535,8 +533,8 @@ def gui_main():
|
|||
|
||||
def get_inpath(self):
|
||||
inpath = tkFileDialog.askopenfilename(
|
||||
parent=None, title=u"Select ADEPT-encrypted ePub file to decrypt",
|
||||
defaultextension=u".epub", filetypes=[('ePub files', '.epub')])
|
||||
parent=None, title="Select ADEPT-encrypted ePub file to decrypt",
|
||||
defaultextension=".epub", filetypes=[('ePub files', '.epub')])
|
||||
if inpath:
|
||||
inpath = os.path.normpath(inpath)
|
||||
self.inpath.delete(0, Tkconstants.END)
|
||||
|
@ -545,8 +543,8 @@ def gui_main():
|
|||
|
||||
def get_outpath(self):
|
||||
outpath = tkFileDialog.asksaveasfilename(
|
||||
parent=None, title=u"Select unencrypted ePub file to produce",
|
||||
defaultextension=u".epub", filetypes=[('ePub files', '.epub')])
|
||||
parent=None, title="Select unencrypted ePub file to produce",
|
||||
defaultextension=".epub", filetypes=[('ePub files', '.epub')])
|
||||
if outpath:
|
||||
outpath = os.path.normpath(outpath)
|
||||
self.outpath.delete(0, Tkconstants.END)
|
||||
|
@ -558,31 +556,31 @@ def gui_main():
|
|||
inpath = self.inpath.get()
|
||||
outpath = self.outpath.get()
|
||||
if not keypath or not os.path.exists(keypath):
|
||||
self.status['text'] = u"Specified key file does not exist"
|
||||
self.status['text'] = "Specified key file does not exist"
|
||||
return
|
||||
if not inpath or not os.path.exists(inpath):
|
||||
self.status['text'] = u"Specified input file does not exist"
|
||||
self.status['text'] = "Specified input file does not exist"
|
||||
return
|
||||
if not outpath:
|
||||
self.status['text'] = u"Output file not specified"
|
||||
self.status['text'] = "Output file not specified"
|
||||
return
|
||||
if inpath == outpath:
|
||||
self.status['text'] = u"Must have different input and output files"
|
||||
self.status['text'] = "Must have different input and output files"
|
||||
return
|
||||
userkey = open(keypath,'rb').read()
|
||||
self.status['text'] = u"Decrypting..."
|
||||
self.status['text'] = "Decrypting..."
|
||||
try:
|
||||
decrypt_status = decryptBook(userkey, inpath, outpath)
|
||||
except Exception as e:
|
||||
self.status['text'] = u"Error: {0}".format(e.args[0])
|
||||
self.status['text'] = "Error: {0}".format(e.args[0])
|
||||
return
|
||||
if decrypt_status == 0:
|
||||
self.status['text'] = u"File successfully decrypted"
|
||||
self.status['text'] = "File successfully decrypted"
|
||||
else:
|
||||
self.status['text'] = u"The was an error decrypting the file."
|
||||
self.status['text'] = "The was an error decrypting the file."
|
||||
|
||||
root = Tkinter.Tk()
|
||||
root.title(u"Adobe Adept ePub Decrypter v.{0}".format(__version__))
|
||||
root.title("Adobe Adept ePub Decrypter v.{0}".format(__version__))
|
||||
root.resizable(True, False)
|
||||
root.minsize(300, 0)
|
||||
DecryptionDialog(root).pack(fill=Tkconstants.X, expand=1)
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
#! /usr/bin/python
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from __future__ import with_statement
|
||||
|
||||
# ineptpdf.py
|
||||
# Copyright © 2009-2020 by i♥cabbages, Apprentice Harper et al.
|
||||
|
||||
|
@ -115,7 +113,7 @@ def unicode_argv():
|
|||
start = argc.value - len(sys.argv)
|
||||
return [argv[i] for i in
|
||||
range(start, argc.value)]
|
||||
return [u"ineptpdf.py"]
|
||||
return ["ineptpdf.py"]
|
||||
else:
|
||||
argvencoding = sys.stdin.encoding
|
||||
if argvencoding is None:
|
||||
|
@ -2178,12 +2176,12 @@ class PDFSerializer(object):
|
|||
|
||||
def decryptBook(userkey, inpath, outpath):
|
||||
if RSA is None:
|
||||
raise ADEPTError(u"PyCrypto or OpenSSL must be installed.")
|
||||
raise ADEPTError("PyCrypto or OpenSSL must be installed.")
|
||||
with open(inpath, 'rb') as inf:
|
||||
#try:
|
||||
serializer = PDFSerializer(inf, userkey)
|
||||
#except:
|
||||
# print u"Error serializing pdf {0}. Probably wrong key.".format(os.path.basename(inpath))
|
||||
# print "Error serializing pdf {0}. Probably wrong key.".format(os.path.basename(inpath))
|
||||
# return 2
|
||||
# hope this will fix the 'bad file descriptor' problem
|
||||
with open(outpath, 'wb') as outf:
|
||||
|
@ -2191,7 +2189,7 @@ def decryptBook(userkey, inpath, outpath):
|
|||
try:
|
||||
serializer.dump(outf)
|
||||
except Exception as e:
|
||||
print(u"error writing pdf: {0}".format(e.args[0]))
|
||||
print("error writing pdf: {0}".format(e.args[0]))
|
||||
return 2
|
||||
return 0
|
||||
|
||||
|
@ -2202,13 +2200,13 @@ def cli_main():
|
|||
argv=unicode_argv()
|
||||
progname = os.path.basename(argv[0])
|
||||
if len(argv) != 4:
|
||||
print(u"usage: {0} <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
|
||||
|
||||
|
||||
|
@ -2225,43 +2223,43 @@ def gui_main():
|
|||
class DecryptionDialog(Tkinter.Frame):
|
||||
def __init__(self, root):
|
||||
Tkinter.Frame.__init__(self, root, border=5)
|
||||
self.status = Tkinter.Label(self, text=u"Select files for decryption")
|
||||
self.status = Tkinter.Label(self, text="Select files for decryption")
|
||||
self.status.pack(fill=Tkconstants.X, expand=1)
|
||||
body = Tkinter.Frame(self)
|
||||
body.pack(fill=Tkconstants.X, expand=1)
|
||||
sticky = Tkconstants.E + Tkconstants.W
|
||||
body.grid_columnconfigure(1, weight=2)
|
||||
Tkinter.Label(body, text=u"Key file").grid(row=0)
|
||||
Tkinter.Label(body, text="Key file").grid(row=0)
|
||||
self.keypath = Tkinter.Entry(body, width=30)
|
||||
self.keypath.grid(row=0, column=1, sticky=sticky)
|
||||
if os.path.exists(u"adeptkey.der"):
|
||||
self.keypath.insert(0, u"adeptkey.der")
|
||||
button = Tkinter.Button(body, text=u"...", command=self.get_keypath)
|
||||
if os.path.exists("adeptkey.der"):
|
||||
self.keypath.insert(0, "adeptkey.der")
|
||||
button = Tkinter.Button(body, text="...", command=self.get_keypath)
|
||||
button.grid(row=0, column=2)
|
||||
Tkinter.Label(body, text=u"Input file").grid(row=1)
|
||||
Tkinter.Label(body, text="Input file").grid(row=1)
|
||||
self.inpath = Tkinter.Entry(body, width=30)
|
||||
self.inpath.grid(row=1, column=1, sticky=sticky)
|
||||
button = Tkinter.Button(body, text=u"...", command=self.get_inpath)
|
||||
button = Tkinter.Button(body, text="...", command=self.get_inpath)
|
||||
button.grid(row=1, column=2)
|
||||
Tkinter.Label(body, text=u"Output file").grid(row=2)
|
||||
Tkinter.Label(body, text="Output file").grid(row=2)
|
||||
self.outpath = Tkinter.Entry(body, width=30)
|
||||
self.outpath.grid(row=2, column=1, sticky=sticky)
|
||||
button = Tkinter.Button(body, text=u"...", command=self.get_outpath)
|
||||
button = Tkinter.Button(body, text="...", command=self.get_outpath)
|
||||
button.grid(row=2, column=2)
|
||||
buttons = Tkinter.Frame(self)
|
||||
buttons.pack()
|
||||
botton = Tkinter.Button(
|
||||
buttons, text=u"Decrypt", width=10, command=self.decrypt)
|
||||
buttons, text="Decrypt", width=10, command=self.decrypt)
|
||||
botton.pack(side=Tkconstants.LEFT)
|
||||
Tkinter.Frame(buttons, width=10).pack(side=Tkconstants.LEFT)
|
||||
button = Tkinter.Button(
|
||||
buttons, text=u"Quit", width=10, command=self.quit)
|
||||
buttons, text="Quit", width=10, command=self.quit)
|
||||
button.pack(side=Tkconstants.RIGHT)
|
||||
|
||||
def get_keypath(self):
|
||||
keypath = tkFileDialog.askopenfilename(
|
||||
parent=None, title=u"Select Adobe Adept \'.der\' key file",
|
||||
defaultextension=u".der",
|
||||
parent=None, title="Select Adobe Adept \'.der\' key file",
|
||||
defaultextension=".der",
|
||||
filetypes=[('Adobe Adept DER-encoded files', '.der'),
|
||||
('All Files', '.*')])
|
||||
if keypath:
|
||||
|
@ -2272,8 +2270,8 @@ def gui_main():
|
|||
|
||||
def get_inpath(self):
|
||||
inpath = tkFileDialog.askopenfilename(
|
||||
parent=None, title=u"Select ADEPT-encrypted PDF file to decrypt",
|
||||
defaultextension=u".pdf", filetypes=[('PDF files', '.pdf')])
|
||||
parent=None, title="Select ADEPT-encrypted PDF file to decrypt",
|
||||
defaultextension=".pdf", filetypes=[('PDF files', '.pdf')])
|
||||
if inpath:
|
||||
inpath = os.path.normpath(inpath)
|
||||
self.inpath.delete(0, Tkconstants.END)
|
||||
|
@ -2282,8 +2280,8 @@ def gui_main():
|
|||
|
||||
def get_outpath(self):
|
||||
outpath = tkFileDialog.asksaveasfilename(
|
||||
parent=None, title=u"Select unencrypted PDF file to produce",
|
||||
defaultextension=u".pdf", filetypes=[('PDF files', '.pdf')])
|
||||
parent=None, title="Select unencrypted PDF file to produce",
|
||||
defaultextension=".pdf", filetypes=[('PDF files', '.pdf')])
|
||||
if outpath:
|
||||
outpath = os.path.normpath(outpath)
|
||||
self.outpath.delete(0, Tkconstants.END)
|
||||
|
@ -2295,28 +2293,28 @@ def gui_main():
|
|||
inpath = self.inpath.get()
|
||||
outpath = self.outpath.get()
|
||||
if not keypath or not os.path.exists(keypath):
|
||||
self.status['text'] = u"Specified key file does not exist"
|
||||
self.status['text'] = "Specified key file does not exist"
|
||||
return
|
||||
if not inpath or not os.path.exists(inpath):
|
||||
self.status['text'] = u"Specified input file does not exist"
|
||||
self.status['text'] = "Specified input file does not exist"
|
||||
return
|
||||
if not outpath:
|
||||
self.status['text'] = u"Output file not specified"
|
||||
self.status['text'] = "Output file not specified"
|
||||
return
|
||||
if inpath == outpath:
|
||||
self.status['text'] = u"Must have different input and output files"
|
||||
self.status['text'] = "Must have different input and output files"
|
||||
return
|
||||
userkey = open(keypath,'rb').read()
|
||||
self.status['text'] = u"Decrypting..."
|
||||
self.status['text'] = "Decrypting..."
|
||||
try:
|
||||
decrypt_status = decryptBook(userkey, inpath, outpath)
|
||||
except Exception as e:
|
||||
self.status['text'] = u"Error; {0}".format(e.args[0])
|
||||
self.status['text'] = "Error; {0}".format(e.args[0])
|
||||
return
|
||||
if decrypt_status == 0:
|
||||
self.status['text'] = u"File successfully decrypted"
|
||||
self.status['text'] = "File successfully decrypted"
|
||||
else:
|
||||
self.status['text'] = u"The was an error decrypting the file."
|
||||
self.status['text'] = "The was an error decrypting the file."
|
||||
|
||||
|
||||
root = Tkinter.Tk()
|
||||
|
@ -2327,7 +2325,7 @@ def gui_main():
|
|||
"This script requires OpenSSL or PyCrypto, which must be installed "
|
||||
"separately. Read the top-of-script comment for details.")
|
||||
return 1
|
||||
root.title(u"Adobe Adept PDF Decrypter v.{0}".format(__version__))
|
||||
root.title("Adobe Adept PDF Decrypter v.{0}".format(__version__))
|
||||
root.resizable(True, False)
|
||||
root.minsize(370, 0)
|
||||
DecryptionDialog(root).pack(fill=Tkconstants.X, expand=1)
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
#!/usr/bin/env python
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from __future__ import with_statement
|
||||
|
||||
# ion.py
|
||||
# Copyright © 2013-2020 Apprentice Harper et al.
|
||||
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
#!/usr/bin/env python
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from __future__ import with_statement
|
||||
|
||||
# k4mobidedrm.py
|
||||
# Copyright © 2008-2020 by Apprentice Harper et al.
|
||||
|
||||
|
@ -146,7 +144,7 @@ def unicode_argv():
|
|||
range(start, argc.value)]
|
||||
# if we don't have any arguments at all, just pass back script name
|
||||
# this should never happen
|
||||
return [u"mobidedrm.py"]
|
||||
return ["mobidedrm.py"]
|
||||
else:
|
||||
argvencoding = sys.stdin.encoding
|
||||
if argvencoding == None:
|
||||
|
@ -161,31 +159,31 @@ def unicode_argv():
|
|||
# and some improvements suggested by jhaisley
|
||||
def cleanup_name(name):
|
||||
# substitute filename unfriendly characters
|
||||
name = name.replace(u"<",u"[").replace(u">",u"]").replace(u" : ",u" – ").replace(u": ",u" – ").replace(u":",u"—").replace(u"/",u"_").replace(u"\\",u"_").replace(u"|",u"_").replace(u"\"",u"\'").replace(u"*",u"_").replace(u"?",u"")
|
||||
name = name.replace("<","[").replace(">","]").replace(" : "," – ").replace(": "," – ").replace(":","—").replace("/","_").replace("\\","_").replace("|","_").replace("\"","\'").replace("*","_").replace("?",u"")
|
||||
# white space to single space, delete leading and trailing while space
|
||||
name = re.sub(r"\s", u" ", name).strip()
|
||||
name = re.sub(r"\s", " ", name).strip()
|
||||
# delete control characters
|
||||
name = u"".join(char for char in name if ord(char)>=32)
|
||||
# delete non-ascii characters
|
||||
name = u"".join(char for char in name if ord(char)<=126)
|
||||
# remove leading dots
|
||||
while len(name)>0 and name[0] == u".":
|
||||
while len(name)>0 and name[0] == ".":
|
||||
name = name[1:]
|
||||
# remove trailing dots (Windows doesn't like them)
|
||||
while name.endswith(u'.'):
|
||||
while name.endswith("."):
|
||||
name = name[:-1]
|
||||
if len(name)==0:
|
||||
name=u"DecryptedBook"
|
||||
name="DecryptedBook"
|
||||
return name
|
||||
|
||||
# must be passed unicode
|
||||
def unescape(text):
|
||||
def fixup(m):
|
||||
text = m.group(0)
|
||||
if text[:2] == u"&#":
|
||||
if text[:2] == "&#":
|
||||
# character reference
|
||||
try:
|
||||
if text[:3] == u"&#x":
|
||||
if text[:3] == "&#x":
|
||||
return chr(int(text[3:-1], 16))
|
||||
else:
|
||||
return chr(int(text[2:-1]))
|
||||
|
@ -198,17 +196,17 @@ def unescape(text):
|
|||
except KeyError:
|
||||
pass
|
||||
return text # leave as is
|
||||
return re.sub(u"&#?\\w+;", fixup, text)
|
||||
return re.sub("&#?\\w+;", fixup, text)
|
||||
|
||||
def GetDecryptedBook(infile, kDatabases, androidFiles, serials, pids, starttime = time.time()):
|
||||
# handle the obvious cases at the beginning
|
||||
if not os.path.isfile(infile):
|
||||
raise DrmException(u"Input file does not exist.")
|
||||
raise DrmException("Input file does not exist.")
|
||||
|
||||
mobi = True
|
||||
magic8 = open(infile,'rb').read(8)
|
||||
if magic8 == '\xeaDRMION\xee':
|
||||
raise DrmException(u"The .kfx DRMION file cannot be decrypted by itself. A .kfx-zip archive containing a DRM voucher is required.")
|
||||
raise DrmException("The .kfx DRMION file cannot be decrypted by itself. A .kfx-zip archive containing a DRM voucher is required.")
|
||||
|
||||
magic3 = magic8[:3]
|
||||
if magic3 == 'TPZ':
|
||||
|
@ -222,7 +220,7 @@ def GetDecryptedBook(infile, kDatabases, androidFiles, serials, pids, starttime
|
|||
mb = topazextract.TopazBook(infile)
|
||||
|
||||
bookname = unescape(mb.getBookTitle())
|
||||
print(u"Decrypting {1} ebook: {0}".format(bookname, mb.getBookType()))
|
||||
print("Decrypting {1} ebook: {0}".format(bookname, mb.getBookType()))
|
||||
|
||||
# copy list of pids
|
||||
totalpids = list(pids)
|
||||
|
@ -234,7 +232,7 @@ def GetDecryptedBook(infile, kDatabases, androidFiles, serials, pids, starttime
|
|||
totalpids.extend(kgenpids.getPidList(md1, md2, serials, kDatabases))
|
||||
# remove any duplicates
|
||||
totalpids = list(set(totalpids))
|
||||
print(u"Found {1:d} keys to try after {0:.1f} seconds".format(time.time()-starttime, len(totalpids)))
|
||||
print("Found {1:d} keys to try after {0:.1f} seconds".format(time.time()-starttime, len(totalpids)))
|
||||
#print totalpids
|
||||
|
||||
try:
|
||||
|
@ -243,7 +241,7 @@ def GetDecryptedBook(infile, kDatabases, androidFiles, serials, pids, starttime
|
|||
mb.cleanup
|
||||
raise
|
||||
|
||||
print(u"Decryption succeeded after {0:.1f} seconds".format(time.time()-starttime))
|
||||
print("Decryption succeeded after {0:.1f} seconds".format(time.time()-starttime))
|
||||
return mb
|
||||
|
||||
|
||||
|
@ -258,7 +256,7 @@ def decryptBook(infile, outdir, kDatabaseFiles, androidFiles, serials, pids):
|
|||
kindleDatabase = json.loads(keyfilein.read())
|
||||
kDatabases.append([dbfile,kindleDatabase])
|
||||
except Exception as e:
|
||||
print(u"Error getting database from file {0:s}: {1:s}".format(dbfile,e))
|
||||
print("Error getting database from file {0:s}: {1:s}".format(dbfile,e))
|
||||
traceback.print_exc()
|
||||
|
||||
|
||||
|
@ -266,7 +264,7 @@ def decryptBook(infile, outdir, kDatabaseFiles, androidFiles, serials, pids):
|
|||
try:
|
||||
book = GetDecryptedBook(infile, kDatabases, androidFiles, serials, pids, starttime)
|
||||
except Exception as e:
|
||||
print(u"Error decrypting book after {1:.1f} seconds: {0}".format(e.args[0],time.time()-starttime))
|
||||
print("Error decrypting book after {1:.1f} seconds: {0}".format(e.args[0],time.time()-starttime))
|
||||
traceback.print_exc()
|
||||
return 1
|
||||
|
||||
|
@ -277,7 +275,7 @@ def decryptBook(infile, outdir, kDatabaseFiles, androidFiles, serials, pids):
|
|||
re.match('^{0-9A-F-}{36}$', orig_fn_root)
|
||||
): # Kindle for PC / Mac / Android / Fire / iOS
|
||||
clean_title = cleanup_name(book.getBookTitle())
|
||||
outfilename = u'{}_{}'.format(orig_fn_root, clean_title)
|
||||
outfilename = "{}_{}".format(orig_fn_root, clean_title)
|
||||
else: # E Ink Kindle, which already uses a reasonable name
|
||||
outfilename = orig_fn_root
|
||||
|
||||
|
@ -285,16 +283,16 @@ def decryptBook(infile, outdir, kDatabaseFiles, androidFiles, serials, pids):
|
|||
if len(outfilename)>150:
|
||||
outfilename = outfilename[:99]+"--"+outfilename[-49:]
|
||||
|
||||
outfilename = outfilename+u"_nodrm"
|
||||
outfilename = outfilename+"_nodrm"
|
||||
outfile = os.path.join(outdir, outfilename + book.getBookExtension())
|
||||
|
||||
book.getFile(outfile)
|
||||
print(u"Saved decrypted book {1:s} after {0:.1f} seconds".format(time.time()-starttime, outfilename))
|
||||
print("Saved decrypted book {1:s} after {0:.1f} seconds".format(time.time()-starttime, outfilename))
|
||||
|
||||
if book.getBookType()==u"Topaz":
|
||||
zipname = os.path.join(outdir, outfilename + u"_SVG.zip")
|
||||
if book.getBookType()=="Topaz":
|
||||
zipname = os.path.join(outdir, outfilename + "_SVG.zip")
|
||||
book.getSVGZip(zipname)
|
||||
print(u"Saved SVG ZIP Archive for {1:s} after {0:.1f} seconds".format(time.time()-starttime, outfilename))
|
||||
print("Saved SVG ZIP Archive for {1:s} after {0:.1f} seconds".format(time.time()-starttime, outfilename))
|
||||
|
||||
# remove internal temporary directory of Topaz pieces
|
||||
book.cleanup()
|
||||
|
@ -302,9 +300,9 @@ def decryptBook(infile, outdir, kDatabaseFiles, androidFiles, serials, pids):
|
|||
|
||||
|
||||
def usage(progname):
|
||||
print(u"Removes DRM protection from Mobipocket, Amazon KF8, Amazon Print Replica and Amazon Topaz ebooks")
|
||||
print(u"Usage:")
|
||||
print(u" {0} [-k <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
|
||||
|
@ -312,12 +310,12 @@ def usage(progname):
|
|||
def cli_main():
|
||||
argv=unicode_argv()
|
||||
progname = os.path.basename(argv[0])
|
||||
print(u"K4MobiDeDrm v{0}.\nCopyright © 2008-2017 Apprentice Harper et al.".format(__version__))
|
||||
print("K4MobiDeDrm v{0}.\nCopyright © 2008-2017 Apprentice Harper et al.".format(__version__))
|
||||
|
||||
try:
|
||||
opts, args = getopt.getopt(argv[1:], "k:p:s:a:")
|
||||
except getopt.GetoptError as err:
|
||||
print(u"Error in options or arguments: {0}".format(err.args[0]))
|
||||
print("Error in options or arguments: {0}".format(err.args[0]))
|
||||
usage(progname)
|
||||
sys.exit(2)
|
||||
if len(args)<2:
|
||||
|
|
|
@ -1,12 +1,9 @@
|
|||
#!/usr/bin/env python
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from __future__ import with_statement
|
||||
from __future__ import print_function
|
||||
|
||||
# Engine to remove drm from Kindle KFX ebooks
|
||||
|
||||
# 2.0 - Added Python 3 compatibility for calibre 5.0
|
||||
# 2.0 - Python 3 for calibre 5.0
|
||||
|
||||
|
||||
import os
|
||||
|
@ -50,13 +47,13 @@ class KFXZipBook:
|
|||
data += fh.read()
|
||||
if self.voucher is None:
|
||||
self.decrypt_voucher(totalpids)
|
||||
print(u'Decrypting KFX DRMION: {0}'.format(filename))
|
||||
print("Decrypting KFX DRMION: {0}".format(filename))
|
||||
outfile = StringIO()
|
||||
ion.DrmIon(StringIO(data[8:-8]), lambda name: self.voucher).parse(outfile)
|
||||
self.decrypted[filename] = outfile.getvalue()
|
||||
|
||||
if not self.decrypted:
|
||||
print(u'The .kfx-zip archive does not contain an encrypted DRMION file')
|
||||
print("The .kfx-zip archive does not contain an encrypted DRMION file")
|
||||
|
||||
def decrypt_voucher(self, totalpids):
|
||||
with zipfile.ZipFile(self.infile, 'r') as zf:
|
||||
|
@ -70,9 +67,9 @@ class KFXZipBook:
|
|||
if 'ProtectedData' in data:
|
||||
break # found DRM voucher
|
||||
else:
|
||||
raise Exception(u'The .kfx-zip archive contains an encrypted DRMION file without a DRM voucher')
|
||||
raise Exception("The .kfx-zip archive contains an encrypted DRMION file without a DRM voucher")
|
||||
|
||||
print(u'Decrypting KFX DRM voucher: {0}'.format(info.filename))
|
||||
print("Decrypting KFX DRM voucher: {0}".format(info.filename))
|
||||
|
||||
for pid in [''] + totalpids:
|
||||
for dsn_len,secret_len in [(0,0), (16,0), (16,40), (32,40), (40,0), (40,40)]:
|
||||
|
@ -89,13 +86,13 @@ class KFXZipBook:
|
|||
except:
|
||||
pass
|
||||
else:
|
||||
raise Exception(u'Failed to decrypt KFX DRM voucher with any key')
|
||||
raise Exception("Failed to decrypt KFX DRM voucher with any key")
|
||||
|
||||
print(u'KFX DRM voucher successfully decrypted')
|
||||
print("KFX DRM voucher successfully decrypted")
|
||||
|
||||
license_type = voucher.getlicensetype()
|
||||
if license_type != "Purchase":
|
||||
raise Exception((u'This book is licensed as {0}. '
|
||||
raise Exception(("This book is licensed as {0}. "
|
||||
'These tools are intended for use on purchased books.').format(license_type))
|
||||
|
||||
self.voucher = voucher
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
#!/usr/bin/env python
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from __future__ import with_statement
|
||||
from __future__ import print_function
|
||||
|
||||
# kgenpids.py
|
||||
# Copyright © 2008-2020 Apprentice Harper et al.
|
||||
|
||||
|
@ -14,7 +11,7 @@ __version__ = '3.0'
|
|||
# 2.0 - Fix for non-ascii Windows user names
|
||||
# 2.1 - Actual fix for non-ascii WIndows user names.
|
||||
# 2.2 - Return information needed for KFX decryption
|
||||
# 3.0 - Added Python 3 compatibility for calibre 5.0
|
||||
# 3.0 - Python 3 for calibre 5.0
|
||||
|
||||
|
||||
import sys
|
||||
|
@ -217,18 +214,18 @@ def getK4Pids(rec209, token, kindleDatabase):
|
|||
try:
|
||||
# Get the DSN token, if present
|
||||
DSN = bytearray.fromhex((kindleDatabase[1])['DSN']).decode()
|
||||
print(u"Got DSN key from database {0}".format(kindleDatabase[0]))
|
||||
print("Got DSN key from database {0}".format(kindleDatabase[0]))
|
||||
except KeyError:
|
||||
# See if we have the info to generate the DSN
|
||||
try:
|
||||
# Get the Mazama Random number
|
||||
MazamaRandomNumber = bytearray.fromhex((kindleDatabase[1])['MazamaRandomNumber']).decode()
|
||||
#print u"Got MazamaRandomNumber from database {0}".format(kindleDatabase[0])
|
||||
#print "Got MazamaRandomNumber from database {0}".format(kindleDatabase[0])
|
||||
|
||||
try:
|
||||
# Get the SerialNumber token, if present
|
||||
IDString = bytearray.fromhex((kindleDatabase[1])['SerialNumber']).decode()
|
||||
print(u"Got SerialNumber from database {0}".format(kindleDatabase[0]))
|
||||
print("Got SerialNumber from database {0}".format(kindleDatabase[0]))
|
||||
except KeyError:
|
||||
# Get the IDString we added
|
||||
IDString = bytearray.fromhex((kindleDatabase[1])['IDString']).decode()
|
||||
|
@ -236,24 +233,24 @@ def getK4Pids(rec209, token, kindleDatabase):
|
|||
try:
|
||||
# Get the UsernameHash token, if present
|
||||
encodedUsername = bytearray.fromhex((kindleDatabase[1])['UsernameHash']).decode()
|
||||
print(u"Got UsernameHash from database {0}".format(kindleDatabase[0]))
|
||||
print("Got UsernameHash from database {0}".format(kindleDatabase[0]))
|
||||
except KeyError:
|
||||
# Get the UserName we added
|
||||
UserName = bytearray.fromhex((kindleDatabase[1])['UserName']).decode()
|
||||
# encode it
|
||||
encodedUsername = encodeHash(UserName,charMap1)
|
||||
#print u"encodedUsername",encodedUsername.encode('hex')
|
||||
#print "encodedUsername",encodedUsername.encode('hex')
|
||||
except KeyError:
|
||||
print(u"Keys not found in the database {0}.".format(kindleDatabase[0]))
|
||||
print("Keys not found in the database {0}.".format(kindleDatabase[0]))
|
||||
return pids
|
||||
|
||||
# Get the ID string used
|
||||
encodedIDString = encodeHash(IDString,charMap1)
|
||||
#print u"encodedIDString",encodedIDString.encode('hex')
|
||||
#print "encodedIDString",encodedIDString.encode('hex')
|
||||
|
||||
# concat, hash and encode to calculate the DSN
|
||||
DSN = encode(SHA1(MazamaRandomNumber+encodedIDString+encodedUsername),charMap1)
|
||||
#print u"DSN",DSN.encode('hex')
|
||||
#print "DSN",DSN.encode('hex')
|
||||
pass
|
||||
|
||||
if rec209 is None:
|
||||
|
@ -300,14 +297,14 @@ def getPidList(md1, md2, serials=[], kDatabases=[]):
|
|||
try:
|
||||
pidlst.extend(getK4Pids(md1, md2, kDatabase))
|
||||
except Exception as e:
|
||||
print(u"Error getting PIDs from database {0}: {1}".format(kDatabase[0],e.args[0]))
|
||||
print("Error getting PIDs from database {0}: {1}".format(kDatabase[0],e.args[0]))
|
||||
traceback.print_exc()
|
||||
|
||||
for serialnum in serials:
|
||||
try:
|
||||
pidlst.extend(getKindlePids(md1, md2, serialnum))
|
||||
except Exception as e:
|
||||
print(u"Error getting PIDs from serial number {0}: {1}".format(serialnum ,e.args[0]))
|
||||
print("Error getting PIDs from serial number {0}: {1}".format(serialnum ,e.args[0]))
|
||||
traceback.print_exc()
|
||||
|
||||
return pidlst
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
#!/usr/bin/env python
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from __future__ import with_statement
|
||||
|
||||
# kindlekey.py
|
||||
# Copyright © 2008-2020 Apprentice Harper et al.
|
||||
|
||||
|
@ -30,7 +28,7 @@ __version__ = '3.0'
|
|||
# 2.5 - Final Fix for Windows user names with non-ascii characters, thanks to oneofusoneofus
|
||||
# 2.6 - Start adding support for Kindle 1.25+ .kinf2018 file
|
||||
# 2.7 - Finish .kinf2018 support, PC & Mac by Apprentice Sakuya
|
||||
# 3.0 - Added Python 3 compatibility for calibre 5.0
|
||||
# 3.0 - Python 3 for calibre 5.0
|
||||
|
||||
|
||||
"""
|
||||
|
@ -104,7 +102,7 @@ def unicode_argv():
|
|||
xrange(start, argc.value)]
|
||||
# if we don't have any arguments at all, just pass back script name
|
||||
# this should never happen
|
||||
return [u"kindlekey.py"]
|
||||
return ["kindlekey.py"]
|
||||
else:
|
||||
argvencoding = sys.stdin.encoding
|
||||
if argvencoding == None:
|
||||
|
@ -905,11 +903,11 @@ if iswindows:
|
|||
|
||||
# replace any non-ASCII values with 0xfffd
|
||||
for i in xrange(0,len(buffer)):
|
||||
if buffer[i]>u"\u007f":
|
||||
#print u"swapping char "+str(i)+" ("+buffer[i]+")"
|
||||
buffer[i] = u"\ufffd"
|
||||
if buffer[i]>"\u007f":
|
||||
#print "swapping char "+str(i)+" ("+buffer[i]+")"
|
||||
buffer[i] = "\ufffd"
|
||||
# return utf-8 encoding of modified username
|
||||
#print u"modified username:"+buffer.value
|
||||
#print "modified username:"+buffer.value
|
||||
return buffer.value.encode('utf-8')
|
||||
return GetUserName
|
||||
GetUserName = GetUserName()
|
||||
|
@ -939,7 +937,7 @@ if iswindows:
|
|||
n = ctypes.windll.kernel32.GetEnvironmentVariableW(name, None, 0)
|
||||
if n == 0:
|
||||
return None
|
||||
buf = ctypes.create_unicode_buffer(u'\0'*n)
|
||||
buf = ctypes.create_unicode_buffer("\0"*n)
|
||||
ctypes.windll.kernel32.GetEnvironmentVariableW(name, buf, n)
|
||||
return buf.value
|
||||
|
||||
|
@ -951,7 +949,7 @@ if iswindows:
|
|||
path = ""
|
||||
if 'LOCALAPPDATA' in os.environ.keys():
|
||||
# Python 2.x does not return unicode env. Use Python 3.x
|
||||
path = winreg.ExpandEnvironmentStrings(u"%LOCALAPPDATA%")
|
||||
path = winreg.ExpandEnvironmentStrings("%LOCALAPPDATA%")
|
||||
# this is just another alternative.
|
||||
# path = getEnvironmentVariable('LOCALAPPDATA')
|
||||
if not os.path.isdir(path):
|
||||
|
@ -979,7 +977,7 @@ if iswindows:
|
|||
print ('Could not find the folder in which to look for kinfoFiles.')
|
||||
else:
|
||||
# Probably not the best. To Fix (shouldn't ignore in encoding) or use utf-8
|
||||
print(u'searching for kinfoFiles in ' + path.encode('ascii', 'ignore'))
|
||||
print("searching for kinfoFiles in " + path.encode('ascii', 'ignore'))
|
||||
|
||||
# look for (K4PC 1.25.1 and later) .kinf2018 file
|
||||
kinfopath = path +'\\Amazon\\Kindle\\storage\\.kinf2018'
|
||||
|
@ -1166,9 +1164,9 @@ if iswindows:
|
|||
# store values used in decryption
|
||||
DB['IDString'] = GetIDString()
|
||||
DB['UserName'] = GetUserName()
|
||||
print(u"Decrypted key file using IDString '{0:s}' and UserName '{1:s}'".format(GetIDString(), GetUserName().encode('hex')))
|
||||
print("Decrypted key file using IDString '{0:s}' and UserName '{1:s}'".format(GetIDString(), GetUserName().encode('hex')))
|
||||
else:
|
||||
print(u"Couldn't decrypt file.")
|
||||
print("Couldn't decrypt file.")
|
||||
DB = {}
|
||||
return DB
|
||||
elif isosx:
|
||||
|
@ -1183,7 +1181,7 @@ elif isosx:
|
|||
|
||||
libcrypto = find_library('crypto')
|
||||
if libcrypto is None:
|
||||
raise DrmException(u"libcrypto not found")
|
||||
raise DrmException("libcrypto not found")
|
||||
libcrypto = CDLL(libcrypto)
|
||||
|
||||
# From OpenSSL's crypto aes header
|
||||
|
@ -1241,14 +1239,14 @@ elif isosx:
|
|||
def set_decrypt_key(self, userkey, iv):
|
||||
self._blocksize = len(userkey)
|
||||
if (self._blocksize != 16) and (self._blocksize != 24) and (self._blocksize != 32) :
|
||||
raise DrmException(u"AES improper key used")
|
||||
raise DrmException("AES improper key used")
|
||||
return
|
||||
keyctx = self._keyctx = AES_KEY()
|
||||
self._iv = iv
|
||||
self._userkey = userkey
|
||||
rv = AES_set_decrypt_key(userkey, len(userkey) * 8, keyctx)
|
||||
if rv < 0:
|
||||
raise DrmException(u"Failed to initialize AES key")
|
||||
raise DrmException("Failed to initialize AES key")
|
||||
|
||||
def decrypt(self, data):
|
||||
out = create_string_buffer(len(data))
|
||||
|
@ -1256,7 +1254,7 @@ elif isosx:
|
|||
keyctx = self._keyctx
|
||||
rv = AES_cbc_encrypt(data, out, len(data), keyctx, mutable_iv, 0)
|
||||
if rv == 0:
|
||||
raise DrmException(u"AES decryption failed")
|
||||
raise DrmException("AES decryption failed")
|
||||
return out.raw
|
||||
|
||||
def keyivgen(self, passwd, salt, iter, keylen):
|
||||
|
@ -1649,16 +1647,16 @@ elif isosx:
|
|||
pass
|
||||
if len(DB)>6:
|
||||
# store values used in decryption
|
||||
print(u"Decrypted key file using IDString '{0:s}' and UserName '{1:s}'".format(IDString, GetUserName()))
|
||||
print("Decrypted key file using IDString '{0:s}' and UserName '{1:s}'".format(IDString, GetUserName()))
|
||||
DB['IDString'] = IDString
|
||||
DB['UserName'] = GetUserName()
|
||||
else:
|
||||
print(u"Couldn't decrypt file.")
|
||||
print("Couldn't decrypt file.")
|
||||
DB = {}
|
||||
return DB
|
||||
else:
|
||||
def getDBfromFile(kInfoFile):
|
||||
raise DrmException(u"This script only runs under Windows or Mac OS X.")
|
||||
raise DrmException("This script only runs under Windows or Mac OS X.")
|
||||
return {}
|
||||
|
||||
def kindlekeys(files = []):
|
||||
|
@ -1683,27 +1681,27 @@ def getkey(outpath, files=[]):
|
|||
outfile = outpath
|
||||
with file(outfile, 'w') as keyfileout:
|
||||
keyfileout.write(json.dumps(keys[0]))
|
||||
print(u"Saved a key to {0}".format(outfile))
|
||||
print("Saved a key to {0}".format(outfile))
|
||||
else:
|
||||
keycount = 0
|
||||
for key in keys:
|
||||
while True:
|
||||
keycount += 1
|
||||
outfile = os.path.join(outpath,u"kindlekey{0:d}.k4i".format(keycount))
|
||||
outfile = os.path.join(outpath,"kindlekey{0:d}.k4i".format(keycount))
|
||||
if not os.path.exists(outfile):
|
||||
break
|
||||
with file(outfile, 'w') as keyfileout:
|
||||
keyfileout.write(json.dumps(key))
|
||||
print(u"Saved a key to {0}".format(outfile))
|
||||
print("Saved a key to {0}".format(outfile))
|
||||
return True
|
||||
return False
|
||||
|
||||
def usage(progname):
|
||||
print(u"Finds, decrypts and saves the default Kindle For Mac/PC encryption keys.")
|
||||
print(u"Keys are saved to the current directory, or a specified output directory.")
|
||||
print(u"If a file name is passed instead of a directory, only the first key is saved, in that file.")
|
||||
print(u"Usage:")
|
||||
print(u" {0:s} [-h] [-k <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():
|
||||
|
@ -1711,12 +1709,12 @@ def cli_main():
|
|||
sys.stderr=SafeUnbuffered(sys.stderr)
|
||||
argv=unicode_argv()
|
||||
progname = os.path.basename(argv[0])
|
||||
print(u"{0} v{1}\nCopyright © 2010-2016 by some_updates, Apprentice Alf and Apprentice Harper".format(progname,__version__))
|
||||
print("{0} v{1}\nCopyright © 2010-2016 by some_updates, Apprentice Alf and Apprentice Harper".format(progname,__version__))
|
||||
|
||||
try:
|
||||
opts, args = getopt.getopt(argv[1:], "hk:")
|
||||
except getopt.GetoptError as err:
|
||||
print(u"Error in options or arguments: {0}".format(err.args[0]))
|
||||
print("Error in options or arguments: {0}".format(err.args[0]))
|
||||
usage(progname)
|
||||
sys.exit(2)
|
||||
|
||||
|
@ -1745,7 +1743,7 @@ def cli_main():
|
|||
outpath = os.path.realpath(os.path.normpath(outpath))
|
||||
|
||||
if not getkey(outpath, files):
|
||||
print(u"Could not retrieve Kindle for Mac/PC key.")
|
||||
print("Could not retrieve Kindle for Mac/PC key.")
|
||||
return 0
|
||||
|
||||
|
||||
|
@ -1761,7 +1759,7 @@ def gui_main():
|
|||
class ExceptionDialog(Tkinter.Frame):
|
||||
def __init__(self, root, text):
|
||||
Tkinter.Frame.__init__(self, root, border=5)
|
||||
label = Tkinter.Label(self, text=u"Unexpected error:",
|
||||
label = Tkinter.Label(self, text="Unexpected error:",
|
||||
anchor=Tkconstants.W, justify=Tkconstants.LEFT)
|
||||
label.pack(fill=Tkconstants.X, expand=0)
|
||||
self.text = Tkinter.Text(self)
|
||||
|
@ -1781,16 +1779,16 @@ def gui_main():
|
|||
for key in keys:
|
||||
while True:
|
||||
keycount += 1
|
||||
outfile = os.path.join(progpath,u"kindlekey{0:d}.k4i".format(keycount))
|
||||
outfile = os.path.join(progpath,"kindlekey{0:d}.k4i".format(keycount))
|
||||
if not os.path.exists(outfile):
|
||||
break
|
||||
|
||||
with file(outfile, 'w') as keyfileout:
|
||||
keyfileout.write(json.dumps(key))
|
||||
success = True
|
||||
tkMessageBox.showinfo(progname, u"Key successfully retrieved to {0}".format(outfile))
|
||||
tkMessageBox.showinfo(progname, "Key successfully retrieved to {0}".format(outfile))
|
||||
except DrmException as e:
|
||||
tkMessageBox.showerror(progname, u"Error: {0}".format(str(e)))
|
||||
tkMessageBox.showerror(progname, "Error: {0}".format(str(e)))
|
||||
except Exception:
|
||||
root.wm_state('normal')
|
||||
root.title(progname)
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#!/usr/bin/python
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Mobipocket PID calculator v0.4 for Amazon Kindle.
|
||||
|
@ -10,7 +10,7 @@
|
|||
# 0.3 updated for unicode
|
||||
# 0.4 Added support for serial numbers starting with '9', fixed unicode bugs.
|
||||
# 0.5 moved unicode_argv call inside main for Windows DeDRM compatibility
|
||||
# 1.0 Added Python 3 compatibility for calibre 5.0
|
||||
# 1.0 Python 3 for calibre 5.0
|
||||
|
||||
from __future__ import print_function
|
||||
import sys
|
||||
|
@ -67,7 +67,7 @@ def unicode_argv():
|
|||
range(start, argc.value)]
|
||||
# if we don't have any arguments at all, just pass back script name
|
||||
# this should never happen
|
||||
return [u"kindlepid.py"]
|
||||
return ["kindlepid.py"]
|
||||
else:
|
||||
argvencoding = sys.stdin.encoding
|
||||
if argvencoding == None:
|
||||
|
@ -111,28 +111,28 @@ def pidFromSerial(s, l):
|
|||
return pid
|
||||
|
||||
def cli_main():
|
||||
print(u"Mobipocket PID calculator for Amazon Kindle. Copyright © 2007, 2009 Igor Skochinsky")
|
||||
print("Mobipocket PID calculator for Amazon Kindle. Copyright © 2007, 2009 Igor Skochinsky")
|
||||
argv=unicode_argv()
|
||||
if len(argv)==2:
|
||||
serial = argv[1]
|
||||
else:
|
||||
print(u"Usage: kindlepid.py <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
|
||||
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#!/usr/bin/env python
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# mobidedrm.py
|
||||
|
@ -7,7 +7,7 @@
|
|||
|
||||
from __future__ import print_function
|
||||
__license__ = 'GPL v3'
|
||||
__version__ = u"1.00"
|
||||
__version__ = "1.00"
|
||||
|
||||
# This is a python script. You need a Python interpreter to run it.
|
||||
# For example, ActiveState Python, which exists for windows.
|
||||
|
@ -82,7 +82,7 @@ import binascii
|
|||
try:
|
||||
from alfcrypto import Pukall_Cipher
|
||||
except:
|
||||
print(u"AlfCrypto not found. Using python PC1 implementation.")
|
||||
print("AlfCrypto not found. Using python PC1 implementation.")
|
||||
|
||||
# Wrap a stream so that output gets flushed immediately
|
||||
# and also make sure that any unicode strings get
|
||||
|
@ -135,7 +135,7 @@ def unicode_argv():
|
|||
range(start, argc.value)]
|
||||
# if we don't have any arguments at all, just pass back script name
|
||||
# this should never happen
|
||||
return [u"mobidedrm.py"]
|
||||
return ["mobidedrm.py"]
|
||||
else:
|
||||
argvencoding = sys.stdin.encoding
|
||||
if argvencoding == None:
|
||||
|
@ -166,7 +166,7 @@ def PC1(key, src, decryption=True):
|
|||
sum2 = 0;
|
||||
keyXorVal = 0;
|
||||
if len(key)!=16:
|
||||
DrmException (u"PC1: Bad key length")
|
||||
DrmException ("PC1: Bad key length")
|
||||
wkey = []
|
||||
for i in range(8):
|
||||
wkey.append(key[i*2]<<8 | key[i*2+1])
|
||||
|
@ -246,19 +246,19 @@ class MobiBook:
|
|||
pass
|
||||
|
||||
def __init__(self, infile):
|
||||
print(u"MobiDeDrm v{0:s}.\nCopyright © 2008-2017 The Dark Reverser, Apprentice Harper et al.".format(__version__))
|
||||
print("MobiDeDrm v{0:s}.\nCopyright © 2008-2017 The Dark Reverser, Apprentice Harper et al.".format(__version__))
|
||||
|
||||
try:
|
||||
from alfcrypto import Pukall_Cipher
|
||||
except:
|
||||
print(u"AlfCrypto not found. Using python PC1 implementation.")
|
||||
print("AlfCrypto not found. Using python PC1 implementation.")
|
||||
|
||||
# initial sanity check on file
|
||||
self.data_file = open(infile, 'rb').read()
|
||||
self.mobi_data = ''
|
||||
self.header = self.data_file[0:78]
|
||||
if self.header[0x3C:0x3C+8] != b'BOOKMOBI' and self.header[0x3C:0x3C+8] != b'TEXtREAd':
|
||||
raise DrmException(u"Invalid file format")
|
||||
raise DrmException("Invalid file format")
|
||||
self.magic = self.header[0x3C:0x3C+8]
|
||||
self.crypto_type = -1
|
||||
|
||||
|
@ -284,16 +284,16 @@ class MobiBook:
|
|||
self.mobi_version = -1
|
||||
|
||||
if self.magic == 'TEXtREAd':
|
||||
print(u"PalmDoc format book detected.")
|
||||
print("PalmDoc format book detected.")
|
||||
return
|
||||
|
||||
self.mobi_length, = struct.unpack('>L',self.sect[0x14:0x18])
|
||||
self.mobi_codepage, = struct.unpack('>L',self.sect[0x1c:0x20])
|
||||
self.mobi_version, = struct.unpack('>L',self.sect[0x68:0x6C])
|
||||
#print u"MOBI header version {0:d}, header length {1:d}".format(self.mobi_version, self.mobi_length)
|
||||
#print "MOBI header version {0:d}, header length {1:d}".format(self.mobi_version, self.mobi_length)
|
||||
if (self.mobi_length >= 0xE4) and (self.mobi_version >= 5):
|
||||
self.extra_data_flags, = struct.unpack('>H', self.sect[0xF2:0xF4])
|
||||
#print u"Extra Data Flags: {0:d}".format(self.extra_data_flags)
|
||||
#print "Extra Data Flags: {0:d}".format(self.extra_data_flags)
|
||||
if (self.compression != 17480):
|
||||
# multibyte utf8 data is included in the encryption for PalmDoc compression
|
||||
# so clear that byte so that we leave it to be decrypted.
|
||||
|
@ -322,7 +322,7 @@ class MobiBook:
|
|||
# print type, size, content, content.encode('hex')
|
||||
pos += size
|
||||
except Exception as e:
|
||||
print(u"Cannot set meta_array: Error: {:s}".format(e.args[0]))
|
||||
print("Cannot set meta_array: Error: {:s}".format(e.args[0]))
|
||||
|
||||
def getBookTitle(self):
|
||||
codec_map = {
|
||||
|
@ -411,51 +411,51 @@ class MobiBook:
|
|||
|
||||
def getBookType(self):
|
||||
if self.print_replica:
|
||||
return u"Print Replica"
|
||||
return "Print Replica"
|
||||
if self.mobi_version >= 8:
|
||||
return u"Kindle Format 8"
|
||||
return "Kindle Format 8"
|
||||
if self.mobi_version >= 0:
|
||||
return u"Mobipocket {0:d}".format(self.mobi_version)
|
||||
return u"PalmDoc"
|
||||
return "Mobipocket {0:d}".format(self.mobi_version)
|
||||
return "PalmDoc"
|
||||
|
||||
def getBookExtension(self):
|
||||
if self.print_replica:
|
||||
return u".azw4"
|
||||
return ".azw4"
|
||||
if self.mobi_version >= 8:
|
||||
return u".azw3"
|
||||
return u".mobi"
|
||||
return ".azw3"
|
||||
return ".mobi"
|
||||
|
||||
def processBook(self, pidlist):
|
||||
crypto_type, = struct.unpack('>H', self.sect[0xC:0xC+2])
|
||||
print(u"Crypto Type is: {0:d}".format(crypto_type))
|
||||
print("Crypto Type is: {0:d}".format(crypto_type))
|
||||
self.crypto_type = crypto_type
|
||||
if crypto_type == 0:
|
||||
print(u"This book is not encrypted.")
|
||||
print("This book is not encrypted.")
|
||||
# we must still check for Print Replica
|
||||
self.print_replica = (self.loadSection(1)[0:4] == '%MOP')
|
||||
self.mobi_data = self.data_file
|
||||
return
|
||||
if crypto_type != 2 and crypto_type != 1:
|
||||
raise DrmException(u"Cannot decode unknown Mobipocket encryption type {0:d}".format(crypto_type))
|
||||
raise DrmException("Cannot decode unknown Mobipocket encryption type {0:d}".format(crypto_type))
|
||||
if 406 in self.meta_array:
|
||||
data406 = self.meta_array[406]
|
||||
val406, = struct.unpack('>Q',data406)
|
||||
if val406 != 0:
|
||||
raise DrmException(u"Cannot decode library or rented ebooks.")
|
||||
raise DrmException("Cannot decode library or rented ebooks.")
|
||||
|
||||
goodpids = []
|
||||
# print("DEBUG ==== pidlist = ", pidlist)
|
||||
for pid in pidlist:
|
||||
if len(pid)==10:
|
||||
if checksumPid(pid[0:-2]) != pid:
|
||||
print(u"Warning: PID {0} has incorrect checksum, should have been {1}".format(pid,checksumPid(pid[0:-2])))
|
||||
print("Warning: PID {0} has incorrect checksum, should have been {1}".format(pid,checksumPid(pid[0:-2])))
|
||||
goodpids.append(pid[0:-2])
|
||||
elif len(pid)==8:
|
||||
goodpids.append(pid)
|
||||
else:
|
||||
print(u"Warning: PID {0} has wrong number of digits".format(pid))
|
||||
print("Warning: PID {0} has wrong number of digits".format(pid))
|
||||
|
||||
# print(u"======= DEBUG good pids = ", goodpids)
|
||||
# print("======= DEBUG good pids = ", goodpids)
|
||||
|
||||
if self.crypto_type == 1:
|
||||
t1_keyvec = 'QDCVEPMU675RUBSZ'
|
||||
|
@ -471,32 +471,32 @@ class MobiBook:
|
|||
# calculate the keys
|
||||
drm_ptr, drm_count, drm_size, drm_flags = struct.unpack('>LLLL', self.sect[0xA8:0xA8+16])
|
||||
if drm_count == 0:
|
||||
raise DrmException(u"Encryption not initialised. Must be opened with Mobipocket Reader first.")
|
||||
raise DrmException("Encryption not initialised. Must be opened with Mobipocket Reader first.")
|
||||
found_key, pid = self.parseDRM(self.sect[drm_ptr:drm_ptr+drm_size], drm_count, goodpids)
|
||||
if not found_key:
|
||||
raise DrmException(u"No key found in {0:d} keys tried.".format(len(goodpids)))
|
||||
raise DrmException("No key found in {0:d} keys tried.".format(len(goodpids)))
|
||||
# kill the drm keys
|
||||
self.patchSection(0, b'\0' * drm_size, drm_ptr)
|
||||
# kill the drm pointers
|
||||
self.patchSection(0, b'\xff' * 4 + b'\0' * 12, 0xA8)
|
||||
|
||||
if pid=='00000000':
|
||||
print(u"File has default encryption, no specific key needed.")
|
||||
print("File has default encryption, no specific key needed.")
|
||||
else:
|
||||
print(u"File is encoded with PID {0}.".format(checksumPid(pid)))
|
||||
print("File is encoded with PID {0}.".format(checksumPid(pid)))
|
||||
|
||||
# clear the crypto type
|
||||
self.patchSection(0, b'\0' * 2, 0xC)
|
||||
|
||||
# decrypt sections
|
||||
print(u"Decrypting. Please wait . . .", end=' ')
|
||||
print("Decrypting. Please wait . . .", end=' ')
|
||||
mobidataList = []
|
||||
mobidataList.append(self.data_file[:self.sections[1][0]])
|
||||
for i in range(1, self.records+1):
|
||||
data = self.loadSection(i)
|
||||
extra_size = getSizeOfTrailingDataEntries(data, len(data), self.extra_data_flags)
|
||||
if i%100 == 0:
|
||||
print(u".", end=' ')
|
||||
print(".", end=' ')
|
||||
# print "record %d, extra_size %d" %(i,extra_size)
|
||||
decoded_data = PC1(found_key, data[0:len(data) - extra_size])
|
||||
if i==1:
|
||||
|
@ -507,12 +507,12 @@ class MobiBook:
|
|||
if self.num_sections > self.records+1:
|
||||
mobidataList.append(self.data_file[self.sections[self.records+1][0]:])
|
||||
self.mobi_data = b''.join(mobidataList)
|
||||
print(u"done")
|
||||
print("done")
|
||||
return
|
||||
|
||||
def getUnencryptedBook(infile,pidlist):
|
||||
if not os.path.isfile(infile):
|
||||
raise DrmException(u"Input File Not Found.")
|
||||
raise DrmException("Input File Not Found.")
|
||||
book = MobiBook(infile)
|
||||
book.processBook(pidlist)
|
||||
return book.mobi_data
|
||||
|
@ -522,10 +522,10 @@ def cli_main():
|
|||
argv=unicode_argv()
|
||||
progname = os.path.basename(argv[0])
|
||||
if len(argv)<3 or len(argv)>4:
|
||||
print(u"MobiDeDrm v{0:s}.\nCopyright © 2008-2017 The Dark Reverser, Apprentice Harper et al.".format(__version__))
|
||||
print(u"Removes protection from Kindle/Mobipocket, Kindle/KF8 and Kindle/Print Replica ebooks")
|
||||
print(u"Usage:")
|
||||
print(u" {0} <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]
|
||||
|
@ -538,7 +538,7 @@ def cli_main():
|
|||
stripped_file = getUnencryptedBook(infile, pidlist)
|
||||
open(outfile, 'wb').write(stripped_file)
|
||||
except DrmException as e:
|
||||
print(u"MobiDeDRM v{0} Error: {1:s}".format(__version__,e.args[0]))
|
||||
print("MobiDeDRM v{0} Error: {1:s}".format(__version__,e.args[0]))
|
||||
return 1
|
||||
return 0
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#!/usr/bin/env python
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
|
||||
|
||||
from __future__ import with_statement
|
||||
__license__ = 'GPL v3'
|
||||
|
||||
# Standard Python modules.
|
||||
|
@ -15,7 +15,7 @@ from calibre.constants import iswindows, isosx
|
|||
|
||||
class DeDRM_Prefs():
|
||||
def __init__(self):
|
||||
JSON_PATH = os.path.join(u"plugins", PLUGIN_NAME.strip().lower().replace(' ', '_') + '.json')
|
||||
JSON_PATH = os.path.join("plugins", PLUGIN_NAME.strip().lower().replace(' ', '_') + '.json')
|
||||
self.dedrmprefs = JSONConfig(JSON_PATH)
|
||||
|
||||
self.dedrmprefs.defaults['configured'] = False
|
||||
|
@ -98,7 +98,7 @@ def convertprefs(always = False):
|
|||
try:
|
||||
name, ccn = keystring.split(',')
|
||||
# Generate Barnes & Noble EPUB user key from name and credit card number.
|
||||
keyname = u"{0}_{1}".format(name.strip(),ccn.strip()[-4:])
|
||||
keyname = "{0}_{1}".format(name.strip(),ccn.strip()[-4:])
|
||||
keyvalue = generate_key(name, ccn)
|
||||
userkeys.append([keyname,keyvalue])
|
||||
except Exception as e:
|
||||
|
@ -115,7 +115,7 @@ def convertprefs(always = False):
|
|||
try:
|
||||
name, cc = keystring.split(',')
|
||||
# Generate eReader user key from name and credit card number.
|
||||
keyname = u"{0}_{1}".format(name.strip(),cc.strip()[-4:])
|
||||
keyname = "{0}_{1}".format(name.strip(),cc.strip()[-4:])
|
||||
keyvalue = getuser_key(name,cc).encode('hex')
|
||||
userkeys.append([keyname,keyvalue])
|
||||
except Exception as e:
|
||||
|
@ -161,15 +161,15 @@ def convertprefs(always = False):
|
|||
return
|
||||
|
||||
|
||||
print(u"{0} v{1}: Importing configuration data from old DeDRM plugins".format(PLUGIN_NAME, PLUGIN_VERSION))
|
||||
print("{0} v{1}: Importing configuration data from old DeDRM plugins".format(PLUGIN_NAME, PLUGIN_VERSION))
|
||||
|
||||
IGNOBLEPLUGINNAME = "Ignoble Epub DeDRM"
|
||||
EREADERPLUGINNAME = "eReader PDB 2 PML"
|
||||
OLDKINDLEPLUGINNAME = "K4PC, K4Mac, Kindle Mobi and Topaz DeDRM"
|
||||
|
||||
# get prefs from older tools
|
||||
kindleprefs = JSONConfig(os.path.join(u"plugins", u"K4MobiDeDRM"))
|
||||
ignobleprefs = JSONConfig(os.path.join(u"plugins", u"ignoble_epub_dedrm"))
|
||||
kindleprefs = JSONConfig(os.path.join("plugins", "K4MobiDeDRM"))
|
||||
ignobleprefs = JSONConfig(os.path.join("plugins", "ignoble_epub_dedrm"))
|
||||
|
||||
# Handle the old ignoble plugin's customization string by converting the
|
||||
# old string to stored keys... get that personal data out of plain sight.
|
||||
|
@ -177,7 +177,7 @@ def convertprefs(always = False):
|
|||
sc = config['plugin_customization']
|
||||
val = sc.pop(IGNOBLEPLUGINNAME, None)
|
||||
if val is not None:
|
||||
print(u"{0} v{1}: Converting old Ignoble plugin configuration string.".format(PLUGIN_NAME, PLUGIN_VERSION))
|
||||
print("{0} v{1}: Converting old Ignoble plugin configuration string.".format(PLUGIN_NAME, PLUGIN_VERSION))
|
||||
priorkeycount = len(dedrmprefs['bandnkeys'])
|
||||
userkeys = parseIgnobleString(str(val))
|
||||
for keypair in userkeys:
|
||||
|
@ -185,7 +185,7 @@ def convertprefs(always = False):
|
|||
value = keypair[1]
|
||||
dedrmprefs.addnamedvaluetoprefs('bandnkeys', name, value)
|
||||
addedkeycount = len(dedrmprefs['bandnkeys'])-priorkeycount
|
||||
print(u"{0} v{1}: {2:d} Barnes and Noble {3} imported from old Ignoble plugin configuration string".format(PLUGIN_NAME, PLUGIN_VERSION, addedkeycount, u"key" if addedkeycount==1 else u"keys"))
|
||||
print("{0} v{1}: {2:d} Barnes and Noble {3} imported from old Ignoble plugin configuration string".format(PLUGIN_NAME, PLUGIN_VERSION, addedkeycount, "key" if addedkeycount==1 else "keys"))
|
||||
# Make the json write all the prefs to disk
|
||||
dedrmprefs.writeprefs(False)
|
||||
|
||||
|
@ -193,7 +193,7 @@ def convertprefs(always = False):
|
|||
# old string to stored keys... get that personal data out of plain sight.
|
||||
val = sc.pop(EREADERPLUGINNAME, None)
|
||||
if val is not None:
|
||||
print(u"{0} v{1}: Converting old eReader plugin configuration string.".format(PLUGIN_NAME, PLUGIN_VERSION))
|
||||
print("{0} v{1}: Converting old eReader plugin configuration string.".format(PLUGIN_NAME, PLUGIN_VERSION))
|
||||
priorkeycount = len(dedrmprefs['ereaderkeys'])
|
||||
userkeys = parseeReaderString(str(val))
|
||||
for keypair in userkeys:
|
||||
|
@ -201,14 +201,14 @@ def convertprefs(always = False):
|
|||
value = keypair[1]
|
||||
dedrmprefs.addnamedvaluetoprefs('ereaderkeys', name, value)
|
||||
addedkeycount = len(dedrmprefs['ereaderkeys'])-priorkeycount
|
||||
print(u"{0} v{1}: {2:d} eReader {3} imported from old eReader plugin configuration string".format(PLUGIN_NAME, PLUGIN_VERSION, addedkeycount, u"key" if addedkeycount==1 else u"keys"))
|
||||
print("{0} v{1}: {2:d} eReader {3} imported from old eReader plugin configuration string".format(PLUGIN_NAME, PLUGIN_VERSION, addedkeycount, "key" if addedkeycount==1 else "keys"))
|
||||
# Make the json write all the prefs to disk
|
||||
dedrmprefs.writeprefs(False)
|
||||
|
||||
# get old Kindle plugin configuration string
|
||||
val = sc.pop(OLDKINDLEPLUGINNAME, None)
|
||||
if val is not None:
|
||||
print(u"{0} v{1}: Converting old Kindle plugin configuration string.".format(PLUGIN_NAME, PLUGIN_VERSION))
|
||||
print("{0} v{1}: Converting old Kindle plugin configuration string.".format(PLUGIN_NAME, PLUGIN_VERSION))
|
||||
priorpidcount = len(dedrmprefs['pids'])
|
||||
priorserialcount = len(dedrmprefs['serials'])
|
||||
pids, serials = parseKindleString(val)
|
||||
|
@ -218,7 +218,7 @@ def convertprefs(always = False):
|
|||
dedrmprefs.addvaluetoprefs('serials',serial)
|
||||
addedpidcount = len(dedrmprefs['pids']) - priorpidcount
|
||||
addedserialcount = len(dedrmprefs['serials']) - priorserialcount
|
||||
print(u"{0} v{1}: {2:d} {3} and {4:d} {5} imported from old Kindle plugin configuration string.".format(PLUGIN_NAME, PLUGIN_VERSION, addedpidcount, u"PID" if addedpidcount==1 else u"PIDs", addedserialcount, u"serial number" if addedserialcount==1 else u"serial numbers"))
|
||||
print("{0} v{1}: {2:d} {3} and {4:d} {5} imported from old Kindle plugin configuration string.".format(PLUGIN_NAME, PLUGIN_VERSION, addedpidcount, "PID" if addedpidcount==1 else "PIDs", addedserialcount, "serial number" if addedserialcount==1 else "serial numbers"))
|
||||
# Make the json write all the prefs to disk
|
||||
dedrmprefs.writeprefs(False)
|
||||
|
||||
|
@ -234,7 +234,7 @@ def convertprefs(always = False):
|
|||
dedrmprefs.addnamedvaluetoprefs('bandnkeys', name, value)
|
||||
addedkeycount = len(dedrmprefs['bandnkeys'])-priorkeycount
|
||||
if addedkeycount > 0:
|
||||
print(u"{0} v{1}: {2:d} Barnes and Noble {3} imported from config folder.".format(PLUGIN_NAME, PLUGIN_VERSION, addedkeycount, u"key file" if addedkeycount==1 else u"key files"))
|
||||
print("{0} v{1}: {2:d} Barnes and Noble {3} imported from config folder.".format(PLUGIN_NAME, PLUGIN_VERSION, addedkeycount, "key file" if addedkeycount==1 else "key files"))
|
||||
# Make the json write all the prefs to disk
|
||||
dedrmprefs.writeprefs(False)
|
||||
|
||||
|
@ -247,7 +247,7 @@ def convertprefs(always = False):
|
|||
dedrmprefs.addnamedvaluetoprefs('adeptkeys', name, value)
|
||||
addedkeycount = len(dedrmprefs['adeptkeys'])-priorkeycount
|
||||
if addedkeycount > 0:
|
||||
print(u"{0} v{1}: {2:d} Adobe Adept {3} imported from config folder.".format(PLUGIN_NAME, PLUGIN_VERSION, addedkeycount, u"keyfile" if addedkeycount==1 else u"keyfiles"))
|
||||
print("{0} v{1}: {2:d} Adobe Adept {3} imported from config folder.".format(PLUGIN_NAME, PLUGIN_VERSION, addedkeycount, "keyfile" if addedkeycount==1 else "keyfiles"))
|
||||
# Make the json write all the prefs to disk
|
||||
dedrmprefs.writeprefs(False)
|
||||
|
||||
|
@ -260,7 +260,7 @@ def convertprefs(always = False):
|
|||
addedkeycount = len(dedrmprefs['bandnkeys']) - priorkeycount
|
||||
# no need to delete old prefs, since they contain no recoverable private data
|
||||
if addedkeycount > 0:
|
||||
print(u"{0} v{1}: {2:d} Barnes and Noble {3} imported from Ignoble plugin preferences.".format(PLUGIN_NAME, PLUGIN_VERSION, addedkeycount, u"key" if addedkeycount==1 else u"keys"))
|
||||
print("{0} v{1}: {2:d} Barnes and Noble {3} imported from Ignoble plugin preferences.".format(PLUGIN_NAME, PLUGIN_VERSION, addedkeycount, "key" if addedkeycount==1 else "keys"))
|
||||
# Make the json write all the prefs to disk
|
||||
dedrmprefs.writeprefs(False)
|
||||
|
||||
|
@ -277,19 +277,19 @@ def convertprefs(always = False):
|
|||
dedrmprefs.addvaluetoprefs('serials',serial)
|
||||
addedpidcount = len(dedrmprefs['pids']) - priorpidcount
|
||||
if addedpidcount > 0:
|
||||
print(u"{0} v{1}: {2:d} {3} imported from Kindle plugin preferences".format(PLUGIN_NAME, PLUGIN_VERSION, addedpidcount, u"PID" if addedpidcount==1 else u"PIDs"))
|
||||
print("{0} v{1}: {2:d} {3} imported from Kindle plugin preferences".format(PLUGIN_NAME, PLUGIN_VERSION, addedpidcount, "PID" if addedpidcount==1 else "PIDs"))
|
||||
addedserialcount = len(dedrmprefs['serials']) - priorserialcount
|
||||
if addedserialcount > 0:
|
||||
print(u"{0} v{1}: {2:d} {3} imported from Kindle plugin preferences".format(PLUGIN_NAME, PLUGIN_VERSION, addedserialcount, u"serial number" if addedserialcount==1 else u"serial numbers"))
|
||||
print("{0} v{1}: {2:d} {3} imported from Kindle plugin preferences".format(PLUGIN_NAME, PLUGIN_VERSION, addedserialcount, "serial number" if addedserialcount==1 else "serial numbers"))
|
||||
try:
|
||||
if 'wineprefix' in kindleprefs and kindleprefs['wineprefix'] != "":
|
||||
dedrmprefs.set('adobewineprefix',kindleprefs['wineprefix'])
|
||||
dedrmprefs.set('kindlewineprefix',kindleprefs['wineprefix'])
|
||||
print(u"{0} v{1}: WINEPREFIX ‘(2)’ imported from Kindle plugin preferences".format(PLUGIN_NAME, PLUGIN_VERSION, kindleprefs['wineprefix']))
|
||||
print("{0} v{1}: WINEPREFIX ‘(2)’ imported from Kindle plugin preferences".format(PLUGIN_NAME, PLUGIN_VERSION, kindleprefs['wineprefix']))
|
||||
except:
|
||||
traceback.print_exc()
|
||||
|
||||
|
||||
# Make the json write all the prefs to disk
|
||||
dedrmprefs.writeprefs()
|
||||
print(u"{0} v{1}: Finished setting up configuration data.".format(PLUGIN_NAME, PLUGIN_VERSION))
|
||||
print("{0} v{1}: Finished setting up configuration data.".format(PLUGIN_NAME, PLUGIN_VERSION))
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#!/usr/bin/env python
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
# vim:ts=4:sw=4:softtabstop=4:smarttab:expandtab
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#!/usr/bin/env python
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
# vim:ts=4:sw=4:softtabstop=4:smarttab:expandtab
|
||||
|
||||
import sys
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#!/usr/bin/env python
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# topazextract.py
|
||||
|
@ -68,7 +68,7 @@ def unicode_argv():
|
|||
range(start, argc.value)]
|
||||
# if we don't have any arguments at all, just pass back script name
|
||||
# this should never happen
|
||||
return [u"mobidedrm.py"]
|
||||
return ["mobidedrm.py"]
|
||||
else:
|
||||
argvencoding = sys.stdin.encoding
|
||||
if argvencoding == None:
|
||||
|
@ -170,11 +170,11 @@ def decryptDkeyRecord(data,PID):
|
|||
record = decryptRecord(data,PID)
|
||||
fields = unpack('3sB8sB8s3s',record)
|
||||
if fields[0] != 'PID' or fields[5] != 'pid' :
|
||||
raise DrmException(u"Didn't find PID magic numbers in record")
|
||||
raise DrmException("Didn't find PID magic numbers in record")
|
||||
elif fields[1] != 8 or fields[3] != 8 :
|
||||
raise DrmException(u"Record didn't contain correct length fields")
|
||||
raise DrmException("Record didn't contain correct length fields")
|
||||
elif fields[2] != PID :
|
||||
raise DrmException(u"Record didn't contain PID")
|
||||
raise DrmException("Record didn't contain PID")
|
||||
return fields[4]
|
||||
|
||||
# Decrypt all dkey records (contain the book PID)
|
||||
|
@ -191,7 +191,7 @@ def decryptDkeyRecords(data,PID):
|
|||
pass
|
||||
data = data[1+length:]
|
||||
if len(records) == 0:
|
||||
raise DrmException(u"BookKey Not Found")
|
||||
raise DrmException("BookKey Not Found")
|
||||
return records
|
||||
|
||||
|
||||
|
@ -206,7 +206,7 @@ class TopazBook:
|
|||
self.bookKey = None
|
||||
magic = unpack('4s',self.fo.read(4))[0]
|
||||
if magic != 'TPZ0':
|
||||
raise DrmException(u"Parse Error : Invalid Header, not a Topaz file")
|
||||
raise DrmException("Parse Error : Invalid Header, not a Topaz file")
|
||||
self.parseTopazHeaders()
|
||||
self.parseMetadata()
|
||||
|
||||
|
@ -224,7 +224,7 @@ class TopazBook:
|
|||
# Read and parse one header record at the current book file position and return the associated data
|
||||
# [[offset,decompressedLength,compressedLength],...]
|
||||
if ord(self.fo.read(1)) != 0x63:
|
||||
raise DrmException(u"Parse Error : Invalid Header")
|
||||
raise DrmException("Parse Error : Invalid Header")
|
||||
tag = bookReadString(self.fo)
|
||||
record = bookReadHeaderRecordData()
|
||||
return [tag,record]
|
||||
|
@ -235,7 +235,7 @@ class TopazBook:
|
|||
if debug: print(result[0], ": ", result[1])
|
||||
self.bookHeaderRecords[result[0]] = result[1]
|
||||
if ord(self.fo.read(1)) != 0x64 :
|
||||
raise DrmException(u"Parse Error : Invalid Header")
|
||||
raise DrmException("Parse Error : Invalid Header")
|
||||
self.bookPayloadOffset = self.fo.tell()
|
||||
|
||||
def parseMetadata(self):
|
||||
|
@ -243,7 +243,7 @@ class TopazBook:
|
|||
self.fo.seek(self.bookPayloadOffset + self.bookHeaderRecords['metadata'][0][0])
|
||||
tag = bookReadString(self.fo)
|
||||
if tag != 'metadata' :
|
||||
raise DrmException(u"Parse Error : Record Names Don't Match")
|
||||
raise DrmException("Parse Error : Record Names Don't Match")
|
||||
flags = ord(self.fo.read(1))
|
||||
nbRecords = ord(self.fo.read(1))
|
||||
if debug: print("Metadata Records: %d" % nbRecords)
|
||||
|
@ -321,11 +321,11 @@ class TopazBook:
|
|||
try:
|
||||
keydata = self.getBookPayloadRecord('dkey', 0)
|
||||
except DrmException as e:
|
||||
print(u"no dkey record found, book may not be encrypted")
|
||||
print(u"attempting to extrct files without a book key")
|
||||
print("no dkey record found, book may not be encrypted")
|
||||
print("attempting to extrct files without a book key")
|
||||
self.createBookDirectory()
|
||||
self.extractFiles()
|
||||
print(u"Successfully Extracted Topaz contents")
|
||||
print("Successfully Extracted Topaz contents")
|
||||
if inCalibre:
|
||||
from calibre_plugins.dedrm import genbook
|
||||
else:
|
||||
|
@ -333,7 +333,7 @@ class TopazBook:
|
|||
|
||||
rv = genbook.generateBook(self.outdir, raw, fixedimage)
|
||||
if rv == 0:
|
||||
print(u"Book Successfully generated.")
|
||||
print("Book Successfully generated.")
|
||||
return rv
|
||||
|
||||
# try each pid to decode the file
|
||||
|
@ -341,7 +341,7 @@ class TopazBook:
|
|||
for pid in pidlst:
|
||||
# use 8 digit pids here
|
||||
pid = pid[0:8]
|
||||
print(u"Trying: {0}".format(pid))
|
||||
print("Trying: {0}".format(pid))
|
||||
bookKeys = []
|
||||
data = keydata
|
||||
try:
|
||||
|
@ -350,16 +350,16 @@ class TopazBook:
|
|||
pass
|
||||
else:
|
||||
bookKey = bookKeys[0]
|
||||
print(u"Book Key Found! ({0})".format(bookKey.encode('hex')))
|
||||
print("Book Key Found! ({0})".format(bookKey.encode('hex')))
|
||||
break
|
||||
|
||||
if not bookKey:
|
||||
raise DrmException(u"No key found in {0:d} keys tried. Read the FAQs at Harper's repository: https://github.com/apprenticeharper/DeDRM_tools/blob/master/FAQs.md".format(len(pidlst)))
|
||||
raise DrmException("No key found in {0:d} keys tried. Read the FAQs at Harper's repository: https://github.com/apprenticeharper/DeDRM_tools/blob/master/FAQs.md".format(len(pidlst)))
|
||||
|
||||
self.setBookKey(bookKey)
|
||||
self.createBookDirectory()
|
||||
self.extractFiles()
|
||||
print(u"Successfully Extracted Topaz contents")
|
||||
print("Successfully Extracted Topaz contents")
|
||||
if inCalibre:
|
||||
from calibre_plugins.dedrm import genbook
|
||||
else:
|
||||
|
@ -367,7 +367,7 @@ class TopazBook:
|
|||
|
||||
rv = genbook.generateBook(self.outdir, raw, fixedimage)
|
||||
if rv == 0:
|
||||
print(u"Book Successfully generated")
|
||||
print("Book Successfully generated")
|
||||
return rv
|
||||
|
||||
def createBookDirectory(self):
|
||||
|
@ -375,16 +375,16 @@ class TopazBook:
|
|||
# create output directory structure
|
||||
if not os.path.exists(outdir):
|
||||
os.makedirs(outdir)
|
||||
destdir = os.path.join(outdir,u"img")
|
||||
destdir = os.path.join(outdir,"img")
|
||||
if not os.path.exists(destdir):
|
||||
os.makedirs(destdir)
|
||||
destdir = os.path.join(outdir,u"color_img")
|
||||
destdir = os.path.join(outdir,"color_img")
|
||||
if not os.path.exists(destdir):
|
||||
os.makedirs(destdir)
|
||||
destdir = os.path.join(outdir,u"page")
|
||||
destdir = os.path.join(outdir,"page")
|
||||
if not os.path.exists(destdir):
|
||||
os.makedirs(destdir)
|
||||
destdir = os.path.join(outdir,u"glyphs")
|
||||
destdir = os.path.join(outdir,"glyphs")
|
||||
if not os.path.exists(destdir):
|
||||
os.makedirs(destdir)
|
||||
|
||||
|
@ -393,49 +393,49 @@ class TopazBook:
|
|||
for headerRecord in self.bookHeaderRecords:
|
||||
name = headerRecord
|
||||
if name != 'dkey':
|
||||
ext = u".dat"
|
||||
if name == 'img': ext = u".jpg"
|
||||
if name == 'color' : ext = u".jpg"
|
||||
print(u"Processing Section: {0}\n. . .".format(name), end=' ')
|
||||
ext = ".dat"
|
||||
if name == 'img': ext = ".jpg"
|
||||
if name == 'color' : ext = ".jpg"
|
||||
print("Processing Section: {0}\n. . .".format(name), end=' ')
|
||||
for index in range (0,len(self.bookHeaderRecords[name])) :
|
||||
fname = u"{0}{1:04d}{2}".format(name,index,ext)
|
||||
fname = "{0}{1:04d}{2}".format(name,index,ext)
|
||||
destdir = outdir
|
||||
if name == 'img':
|
||||
destdir = os.path.join(outdir,u"img")
|
||||
destdir = os.path.join(outdir,"img")
|
||||
if name == 'color':
|
||||
destdir = os.path.join(outdir,u"color_img")
|
||||
destdir = os.path.join(outdir,"color_img")
|
||||
if name == 'page':
|
||||
destdir = os.path.join(outdir,u"page")
|
||||
destdir = os.path.join(outdir,"page")
|
||||
if name == 'glyphs':
|
||||
destdir = os.path.join(outdir,u"glyphs")
|
||||
destdir = os.path.join(outdir,"glyphs")
|
||||
outputFile = os.path.join(destdir,fname)
|
||||
print(u".", end=' ')
|
||||
print(".", end=' ')
|
||||
record = self.getBookPayloadRecord(name,index)
|
||||
if record != '':
|
||||
open(outputFile, 'wb').write(record)
|
||||
print(u" ")
|
||||
print(" ")
|
||||
|
||||
def getFile(self, zipname):
|
||||
htmlzip = zipfile.ZipFile(zipname,'w',zipfile.ZIP_DEFLATED, False)
|
||||
htmlzip.write(os.path.join(self.outdir,u"book.html"),u"book.html")
|
||||
htmlzip.write(os.path.join(self.outdir,u"book.opf"),u"book.opf")
|
||||
if os.path.isfile(os.path.join(self.outdir,u"cover.jpg")):
|
||||
htmlzip.write(os.path.join(self.outdir,u"cover.jpg"),u"cover.jpg")
|
||||
htmlzip.write(os.path.join(self.outdir,u"style.css"),u"style.css")
|
||||
zipUpDir(htmlzip, self.outdir, u"img")
|
||||
htmlzip.write(os.path.join(self.outdir,"book.html"),"book.html")
|
||||
htmlzip.write(os.path.join(self.outdir,"book.opf"),"book.opf")
|
||||
if os.path.isfile(os.path.join(self.outdir,"cover.jpg")):
|
||||
htmlzip.write(os.path.join(self.outdir,"cover.jpg"),"cover.jpg")
|
||||
htmlzip.write(os.path.join(self.outdir,"style.css"),"style.css")
|
||||
zipUpDir(htmlzip, self.outdir, "img")
|
||||
htmlzip.close()
|
||||
|
||||
def getBookType(self):
|
||||
return u"Topaz"
|
||||
return "Topaz"
|
||||
|
||||
def getBookExtension(self):
|
||||
return u".htmlz"
|
||||
return ".htmlz"
|
||||
|
||||
def getSVGZip(self, zipname):
|
||||
svgzip = zipfile.ZipFile(zipname,'w',zipfile.ZIP_DEFLATED, False)
|
||||
svgzip.write(os.path.join(self.outdir,u"index_svg.xhtml"),u"index_svg.xhtml")
|
||||
zipUpDir(svgzip, self.outdir, u"svg")
|
||||
zipUpDir(svgzip, self.outdir, u"img")
|
||||
svgzip.write(os.path.join(self.outdir,"index_svg.xhtml"),"index_svg.xhtml")
|
||||
zipUpDir(svgzip, self.outdir, "svg")
|
||||
zipUpDir(svgzip, self.outdir, "img")
|
||||
svgzip.close()
|
||||
|
||||
def cleanup(self):
|
||||
|
@ -443,20 +443,20 @@ class TopazBook:
|
|||
shutil.rmtree(self.outdir, True)
|
||||
|
||||
def usage(progname):
|
||||
print(u"Removes DRM protection from Topaz ebooks and extracts the contents")
|
||||
print(u"Usage:")
|
||||
print(u" {0} [-k <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:
|
||||
|
@ -466,11 +466,11 @@ def cli_main():
|
|||
infile = args[0]
|
||||
outdir = args[1]
|
||||
if not os.path.isfile(infile):
|
||||
print(u"Input File {0} Does Not Exist.".format(infile))
|
||||
print("Input File {0} Does Not Exist.".format(infile))
|
||||
return 1
|
||||
|
||||
if not os.path.exists(outdir):
|
||||
print(u"Output Directory {0} Does Not Exist.".format(outdir))
|
||||
print("Output Directory {0} Does Not Exist.".format(outdir))
|
||||
return 1
|
||||
|
||||
kDatabaseFiles = []
|
||||
|
@ -495,27 +495,27 @@ def cli_main():
|
|||
|
||||
tb = TopazBook(infile)
|
||||
title = tb.getBookTitle()
|
||||
print(u"Processing Book: {0}".format(title))
|
||||
print("Processing Book: {0}".format(title))
|
||||
md1, md2 = tb.getPIDMetaInfo()
|
||||
pids.extend(kgenpids.getPidList(md1, md2, serials, kDatabaseFiles))
|
||||
|
||||
try:
|
||||
print(u"Decrypting Book")
|
||||
print("Decrypting Book")
|
||||
tb.processBook(pids)
|
||||
|
||||
print(u" Creating HTML ZIP Archive")
|
||||
zipname = os.path.join(outdir, bookname + u"_nodrm.htmlz")
|
||||
print(" Creating HTML ZIP Archive")
|
||||
zipname = os.path.join(outdir, bookname + "_nodrm.htmlz")
|
||||
tb.getFile(zipname)
|
||||
|
||||
print(u" Creating SVG ZIP Archive")
|
||||
zipname = os.path.join(outdir, bookname + u"_SVG.zip")
|
||||
print(" Creating SVG ZIP Archive")
|
||||
zipname = os.path.join(outdir, bookname + "_SVG.zip")
|
||||
tb.getSVGZip(zipname)
|
||||
|
||||
# removing internal temporary directory of pieces
|
||||
tb.cleanup()
|
||||
|
||||
except DrmException as e:
|
||||
print(u"Decryption failed\n{0}".format(traceback.format_exc()))
|
||||
print("Decryption failed\n{0}".format(traceback.format_exc()))
|
||||
|
||||
try:
|
||||
tb.cleanup()
|
||||
|
@ -524,7 +524,7 @@ def cli_main():
|
|||
return 1
|
||||
|
||||
except Exception as e:
|
||||
print(u"Decryption failed\n{0}".format(traceback.format_exc()))
|
||||
print("Decryption failed\n{0}".format(traceback.format_exc()))
|
||||
try:
|
||||
tb.cleanup()
|
||||
except:
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
#!/usr/bin/env python
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from __future__ import with_statement
|
||||
|
||||
from calibre_plugins.dedrm.ignoblekeygen import generate_key
|
||||
|
||||
__license__ = 'GPL v3'
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
#!/usr/bin/env python
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from __future__ import with_statement
|
||||
from __future__ import print_function
|
||||
|
||||
__license__ = 'GPL v3'
|
||||
|
||||
# Standard Python modules.
|
||||
|
@ -17,13 +14,13 @@ def WineGetKeys(scriptpath, extension, wineprefix=""):
|
|||
import subasyncio
|
||||
from subasyncio import Process
|
||||
|
||||
if extension == u".k4i":
|
||||
if extension == ".k4i":
|
||||
import json
|
||||
|
||||
basepath, script = os.path.split(scriptpath)
|
||||
print(u"{0} v{1}: Running {2} under Wine".format(PLUGIN_NAME, PLUGIN_VERSION, script))
|
||||
print("{0} v{1}: Running {2} under Wine".format(PLUGIN_NAME, PLUGIN_VERSION, script))
|
||||
|
||||
outdirpath = os.path.join(basepath, u"winekeysdir")
|
||||
outdirpath = os.path.join(basepath, "winekeysdir")
|
||||
if not os.path.exists(outdirpath):
|
||||
os.makedirs(outdirpath)
|
||||
|
||||
|
@ -31,29 +28,29 @@ def WineGetKeys(scriptpath, extension, wineprefix=""):
|
|||
wineprefix = os.path.abspath(os.path.expanduser(os.path.expandvars(wineprefix)))
|
||||
|
||||
if wineprefix != "" and os.path.exists(wineprefix):
|
||||
cmdline = u"WINEPREFIX=\"{2}\" wine python.exe \"{0}\" \"{1}\"".format(scriptpath,outdirpath,wineprefix)
|
||||
cmdline = "WINEPREFIX=\"{2}\" wine python.exe \"{0}\" \"{1}\"".format(scriptpath,outdirpath,wineprefix)
|
||||
else:
|
||||
cmdline = u"wine python.exe \"{0}\" \"{1}\"".format(scriptpath,outdirpath)
|
||||
print(u"{0} v{1}: Command line: '{2}'".format(PLUGIN_NAME, PLUGIN_VERSION, cmdline))
|
||||
cmdline = "wine python.exe \"{0}\" \"{1}\"".format(scriptpath,outdirpath)
|
||||
print("{0} v{1}: Command line: '{2}'".format(PLUGIN_NAME, PLUGIN_VERSION, cmdline))
|
||||
|
||||
try:
|
||||
cmdline = cmdline.encode(sys.getfilesystemencoding())
|
||||
p2 = Process(cmdline, shell=True, bufsize=1, stdin=None, stdout=sys.stdout, stderr=STDOUT, close_fds=False)
|
||||
result = p2.wait("wait")
|
||||
except Exception as e:
|
||||
print(u"{0} v{1}: Wine subprocess call error: {2}".format(PLUGIN_NAME, PLUGIN_VERSION, e.args[0]))
|
||||
print("{0} v{1}: Wine subprocess call error: {2}".format(PLUGIN_NAME, PLUGIN_VERSION, e.args[0]))
|
||||
if wineprefix != "" and os.path.exists(wineprefix):
|
||||
cmdline = u"WINEPREFIX=\"{2}\" wine C:\\Python27\\python.exe \"{0}\" \"{1}\"".format(scriptpath,outdirpath,wineprefix)
|
||||
cmdline = "WINEPREFIX=\"{2}\" wine C:\\Python27\\python.exe \"{0}\" \"{1}\"".format(scriptpath,outdirpath,wineprefix)
|
||||
else:
|
||||
cmdline = u"wine C:\\Python27\\python.exe \"{0}\" \"{1}\"".format(scriptpath,outdirpath)
|
||||
print(u"{0} v{1}: Command line: “{2}”".format(PLUGIN_NAME, PLUGIN_VERSION, cmdline))
|
||||
cmdline = "wine C:\\Python27\\python.exe \"{0}\" \"{1}\"".format(scriptpath,outdirpath)
|
||||
print("{0} v{1}: Command line: “{2}”".format(PLUGIN_NAME, PLUGIN_VERSION, cmdline))
|
||||
|
||||
try:
|
||||
cmdline = cmdline.encode(sys.getfilesystemencoding())
|
||||
p2 = Process(cmdline, shell=True, bufsize=1, stdin=None, stdout=sys.stdout, stderr=STDOUT, close_fds=False)
|
||||
result = p2.wait("wait")
|
||||
except Exception as e:
|
||||
print(u"{0} v{1}: Wine subprocess call error: {2}".format(PLUGIN_NAME, PLUGIN_VERSION, e.args[0]))
|
||||
print("{0} v{1}: Wine subprocess call error: {2}".format(PLUGIN_NAME, PLUGIN_VERSION, e.args[0]))
|
||||
|
||||
# try finding winekeys anyway, even if above code errored
|
||||
winekeys = []
|
||||
|
@ -63,14 +60,14 @@ def WineGetKeys(scriptpath, extension, wineprefix=""):
|
|||
try:
|
||||
fpath = os.path.join(outdirpath, filename)
|
||||
with open(fpath, 'rb') as keyfile:
|
||||
if extension == u".k4i":
|
||||
if extension == ".k4i":
|
||||
new_key_value = json.loads(keyfile.read())
|
||||
else:
|
||||
new_key_value = keyfile.read()
|
||||
winekeys.append(new_key_value)
|
||||
except:
|
||||
print(u"{0} v{1}: Error loading file {2}".format(PLUGIN_NAME, PLUGIN_VERSION, filename))
|
||||
print("{0} v{1}: Error loading file {2}".format(PLUGIN_NAME, PLUGIN_VERSION, filename))
|
||||
traceback.print_exc()
|
||||
os.remove(fpath)
|
||||
print(u"{0} v{1}: Found and decrypted {2} {3}".format(PLUGIN_NAME, PLUGIN_VERSION, len(winekeys), u"key file" if len(winekeys) == 1 else u"key files"))
|
||||
print("{0} v{1}: Found and decrypted {2} {3}".format(PLUGIN_NAME, PLUGIN_VERSION, len(winekeys), "key file" if len(winekeys) == 1 else "key files"))
|
||||
return winekeys
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
Read and write ZIP files.
|
||||
"""
|
||||
|
@ -824,8 +827,8 @@ class ZipFile:
|
|||
|
||||
def open(self, name, mode="r", pwd=None):
|
||||
"""Return file-like object for 'name'."""
|
||||
if mode not in ("r", "U", "rU"):
|
||||
raise RuntimeError('open() requires mode "r", "U", or "rU"')
|
||||
if mode not in ("r", "", "rU"):
|
||||
raise RuntimeError('open() requires mode "r", "", or "rU"')
|
||||
if not self.fp:
|
||||
raise RuntimeError(
|
||||
"Attempt to read ZIP archive that was already closed")
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
#!/usr/bin/env python
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# zipfix.py
|
||||
# Copyright © 2010-2020 by some_updates, DiapDealer and Apprentice Alf
|
||||
# Copyright © 2010-2020 by Apprentice Harper et al.
|
||||
|
||||
# Released under the terms of the GNU General Public Licence, version 3
|
||||
# <http://www.gnu.org/licenses/>
|
||||
|
@ -10,7 +10,7 @@
|
|||
# Revision history:
|
||||
# 1.0 - Initial release
|
||||
# 1.1 - Updated to handle zip file metadata correctly
|
||||
# 2.0 - Added Python 3 compatibility for calibre 5.0
|
||||
# 2.0 - Python 3 for calibre 5.0
|
||||
|
||||
"""
|
||||
Re-write zip (or ePub) fixing problems with file names (and mimetype entry).
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
|
||||
from __future__ import (unicode_literals, division, absolute_import,
|
||||
print_function)
|
||||
|
||||
__license__ = 'GPL v3'
|
||||
_license__ = 'GPL v3'
|
||||
__docformat__ = 'restructuredtext en'
|
||||
|
||||
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
#!/usr/bin/env python
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
|
||||
from __future__ import (unicode_literals, division, absolute_import,
|
||||
print_function)
|
||||
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2012, David Forrester <davidfor@internode.on.net>'
|
||||
|
@ -9,16 +8,10 @@ __docformat__ = 'restructuredtext en'
|
|||
|
||||
import os, time, re, sys
|
||||
from datetime import datetime
|
||||
try:
|
||||
from PyQt5.Qt import (Qt, QIcon, QPixmap, QLabel, QDialog, QHBoxLayout, QProgressBar,
|
||||
QTableWidgetItem, QFont, QLineEdit, QComboBox,
|
||||
QVBoxLayout, QDialogButtonBox, QStyledItemDelegate, QDateTime,
|
||||
QRegExpValidator, QRegExp, QDate, QDateEdit)
|
||||
except ImportError:
|
||||
from PyQt4.Qt import (Qt, QIcon, QPixmap, QLabel, QDialog, QHBoxLayout, QProgressBar,
|
||||
QTableWidgetItem, QFont, QLineEdit, QComboBox,
|
||||
QVBoxLayout, QDialogButtonBox, QStyledItemDelegate, QDateTime,
|
||||
QRegExpValidator, QRegExp, QDate, QDateEdit)
|
||||
|
||||
from calibre.constants import iswindows, filesystem_encoding, DEBUG
|
||||
from calibre.gui2 import gprefs, error_dialog, UNDEFINED_QDATETIME, Application
|
||||
|
|
|
@ -1,16 +1,10 @@
|
|||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
|
||||
from __future__ import (unicode_literals, division, absolute_import,
|
||||
print_function)
|
||||
|
||||
try:
|
||||
from PyQt5.Qt import (Qt, QGroupBox, QListWidget, QLineEdit, QDialogButtonBox, QWidget, QLabel, QDialog, QVBoxLayout, QAbstractItemView, QIcon, QHBoxLayout, QComboBox, QListWidgetItem, QFileDialog)
|
||||
except ImportError:
|
||||
from PyQt4.Qt import (Qt, QGroupBox, QListWidget, QLineEdit, QDialogButtonBox, QWidget, QLabel, QDialog, QVBoxLayout, QAbstractItemView, QIcon, QHBoxLayout, QComboBox, QListWidgetItem, QFileDialog)
|
||||
|
||||
try:
|
||||
from PyQt5 import Qt as QtGui
|
||||
except ImportError:
|
||||
from PyQt4 import QtGui
|
||||
|
||||
from calibre.gui2 import (error_dialog, question_dialog, info_dialog, open_url)
|
||||
from calibre.utils.config import JSONConfig, config_dir
|
||||
|
@ -50,25 +44,25 @@ class ConfigWidget(QWidget):
|
|||
self.find_homes.setCurrentIndex(index)
|
||||
|
||||
self.serials_button = QtGui.QPushButton(self)
|
||||
self.serials_button.setToolTip(_(u"Click to manage Kobo serial numbers for Kobo ebooks"))
|
||||
self.serials_button.setText(u"Kobo devices serials")
|
||||
self.serials_button.setToolTip(_("Click to manage Kobo serial numbers for Kobo ebooks"))
|
||||
self.serials_button.setText("Kobo devices serials")
|
||||
self.serials_button.clicked.connect(self.edit_serials)
|
||||
layout.addWidget(self.serials_button)
|
||||
|
||||
self.kobo_directory_button = QtGui.QPushButton(self)
|
||||
self.kobo_directory_button.setToolTip(_(u"Click to specify the Kobo directory"))
|
||||
self.kobo_directory_button.setText(u"Kobo directory")
|
||||
self.kobo_directory_button.setToolTip(_("Click to specify the Kobo directory"))
|
||||
self.kobo_directory_button.setText("Kobo directory")
|
||||
self.kobo_directory_button.clicked.connect(self.edit_kobo_directory)
|
||||
layout.addWidget(self.kobo_directory_button)
|
||||
|
||||
|
||||
def edit_serials(self):
|
||||
d = ManageKeysDialog(self,u"Kobo device serial number",self.tmpserials, AddSerialDialog)
|
||||
d = ManageKeysDialog(self,"Kobo device serial number",self.tmpserials, AddSerialDialog)
|
||||
d.exec_()
|
||||
|
||||
|
||||
def edit_kobo_directory(self):
|
||||
tmpkobodirectory = QFileDialog.getExistingDirectory(self, u"Select Kobo directory", self.kobodirectory or "/home", QFileDialog.ShowDirsOnly)
|
||||
tmpkobodirectory = QFileDialog.getExistingDirectory(self, "Select Kobo directory", self.kobodirectory or "/home", QFileDialog.ShowDirsOnly)
|
||||
|
||||
if tmpkobodirectory != u"" and tmpkobodirectory is not None:
|
||||
self.kobodirectory = tmpkobodirectory
|
||||
|
@ -91,7 +85,7 @@ class ManageKeysDialog(QDialog):
|
|||
self.plugin_keys = plugin_keys
|
||||
self.create_key = create_key
|
||||
self.keyfile_ext = keyfile_ext
|
||||
self.json_file = (keyfile_ext == u"k4i")
|
||||
self.json_file = (keyfile_ext == "k4i")
|
||||
|
||||
self.setWindowTitle("{0} {1}: Manage {2}s".format(PLUGIN_NAME, PLUGIN_VERSION, self.key_type_name))
|
||||
|
||||
|
@ -99,13 +93,13 @@ class ManageKeysDialog(QDialog):
|
|||
layout = QVBoxLayout(self)
|
||||
self.setLayout(layout)
|
||||
|
||||
keys_group_box = QGroupBox(_(u"{0}s".format(self.key_type_name)), self)
|
||||
keys_group_box = QGroupBox(_("{0}s".format(self.key_type_name)), self)
|
||||
layout.addWidget(keys_group_box)
|
||||
keys_group_box_layout = QHBoxLayout()
|
||||
keys_group_box.setLayout(keys_group_box_layout)
|
||||
|
||||
self.listy = QListWidget(self)
|
||||
self.listy.setToolTip(u"{0}s that will be used to decrypt ebooks".format(self.key_type_name))
|
||||
self.listy.setToolTip("{0}s that will be used to decrypt ebooks".format(self.key_type_name))
|
||||
self.listy.setSelectionMode(QAbstractItemView.SingleSelection)
|
||||
self.populate_list()
|
||||
keys_group_box_layout.addWidget(self.listy)
|
||||
|
@ -114,12 +108,12 @@ class ManageKeysDialog(QDialog):
|
|||
keys_group_box_layout.addLayout(button_layout)
|
||||
self._add_key_button = QtGui.QToolButton(self)
|
||||
self._add_key_button.setIcon(QIcon(I('plus.png')))
|
||||
self._add_key_button.setToolTip(u"Create new {0}".format(self.key_type_name))
|
||||
self._add_key_button.setToolTip("Create new {0}".format(self.key_type_name))
|
||||
self._add_key_button.clicked.connect(self.add_key)
|
||||
button_layout.addWidget(self._add_key_button)
|
||||
|
||||
self._delete_key_button = QtGui.QToolButton(self)
|
||||
self._delete_key_button.setToolTip(_(u"Delete highlighted key"))
|
||||
self._delete_key_button.setToolTip(_("Delete highlighted key"))
|
||||
self._delete_key_button.setIcon(QIcon(I('list_remove.png')))
|
||||
self._delete_key_button.clicked.connect(self.delete_key)
|
||||
button_layout.addWidget(self._delete_key_button)
|
||||
|
@ -155,7 +149,7 @@ class ManageKeysDialog(QDialog):
|
|||
new_key_value = d.key_value
|
||||
if new_key_value in self.plugin_keys:
|
||||
info_dialog(None, "{0} {1}: Duplicate {2}".format(PLUGIN_NAME, PLUGIN_VERSION,self.key_type_name),
|
||||
u"This {0} is already in the list of {0}s has not been added.".format(self.key_type_name), show=True)
|
||||
"This {0} is already in the list of {0}s has not been added.".format(self.key_type_name), show=True)
|
||||
return
|
||||
|
||||
self.plugin_keys.append(d.key_value)
|
||||
|
@ -166,7 +160,7 @@ class ManageKeysDialog(QDialog):
|
|||
if not self.listy.currentItem():
|
||||
return
|
||||
keyname = self.listy.currentItem().text()
|
||||
if not question_dialog(self, "{0} {1}: Confirm Delete".format(PLUGIN_NAME, PLUGIN_VERSION), u"Do you really want to delete the {1} <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)
|
||||
|
||||
|
@ -177,7 +171,7 @@ class AddSerialDialog(QDialog):
|
|||
def __init__(self, parent=None,):
|
||||
QDialog.__init__(self, parent)
|
||||
self.parent = parent
|
||||
self.setWindowTitle(u"{0} {1}: Add New eInk Kobo Serial Number".format(PLUGIN_NAME, PLUGIN_VERSION))
|
||||
self.setWindowTitle("{0} {1}: Add New eInk Kobo Serial Number".format(PLUGIN_NAME, PLUGIN_VERSION))
|
||||
layout = QVBoxLayout(self)
|
||||
self.setLayout(layout)
|
||||
|
||||
|
@ -188,9 +182,9 @@ class AddSerialDialog(QDialog):
|
|||
|
||||
key_group = QHBoxLayout()
|
||||
data_group_box_layout.addLayout(key_group)
|
||||
key_group.addWidget(QLabel(u"EInk Kobo Serial Number:", self))
|
||||
key_group.addWidget(QLabel("EInk Kobo Serial Number:", self))
|
||||
self.key_ledit = QLineEdit("", self)
|
||||
self.key_ledit.setToolTip(u"Enter an eInk Kobo serial number. EInk Kobo serial numbers are 13 characters long and usually start with a 'N'. Kobo Serial Numbers are case-sensitive, so be sure to enter the upper and lower case letters unchanged.")
|
||||
self.key_ledit.setToolTip("Enter an eInk Kobo serial number. EInk Kobo serial numbers are 13 characters long and usually start with a 'N'. Kobo Serial Numbers are case-sensitive, so be sure to enter the upper and lower case letters unchanged.")
|
||||
key_group.addWidget(self.key_ledit)
|
||||
|
||||
self.button_box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
|
||||
|
@ -210,9 +204,9 @@ class AddSerialDialog(QDialog):
|
|||
|
||||
def accept(self):
|
||||
if len(self.key_name) == 0 or self.key_name.isspace():
|
||||
errmsg = u"Please enter an eInk Kindle Serial Number or click Cancel in the dialog."
|
||||
errmsg = "Please enter an eInk Kindle Serial Number or click Cancel in the dialog."
|
||||
return error_dialog(None, "{0} {1}".format(PLUGIN_NAME, PLUGIN_VERSION), errmsg, show=True, show_copy_button=False)
|
||||
if len(self.key_name) != 13:
|
||||
errmsg = u"EInk Kobo Serial Numbers must be 13 characters long. This is {0:d} characters long.".format(len(self.key_name))
|
||||
errmsg = "EInk Kobo Serial Numbers must be 13 characters long. This is {0:d} characters long.".format(len(self.key_name))
|
||||
return error_dialog(None, "{0} {1}".format(PLUGIN_NAME, PLUGIN_VERSION), errmsg, show=True, show_copy_button=False)
|
||||
QDialog.accept(self)
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#!/usr/bin/env python
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Version 4.0.0 September 2020
|
||||
|
@ -156,7 +156,7 @@
|
|||
from __future__ import print_function
|
||||
|
||||
__version__ = '4.0.0'
|
||||
__about__ = u"Obok v{0}\nCopyright © 2012-2020 Physisticated et al.".format(__version__)
|
||||
__about__ = "Obok v{0}\nCopyright © 2012-2020 Physisticated et al.".format(__version__)
|
||||
|
||||
import sys
|
||||
import os
|
||||
|
@ -176,10 +176,10 @@ import tempfile
|
|||
can_parse_xml = True
|
||||
try:
|
||||
from xml.etree import ElementTree as ET
|
||||
# print u"using xml.etree for xml parsing"
|
||||
# print "using xml.etree for xml parsing"
|
||||
except ImportError:
|
||||
can_parse_xml = False
|
||||
# print u"Cannot find xml.etree, disabling extraction of serial numbers"
|
||||
# print "Cannot find xml.etree, disabling extraction of serial numbers"
|
||||
|
||||
# List of all known hash keys
|
||||
KOBO_HASH_KEYS = ['88b3a2e13', 'XzUhGYdFp', 'NoCanLook','QJhwzAtXL']
|
||||
|
@ -279,10 +279,10 @@ class SafeUnbuffered:
|
|||
if self.encoding == None:
|
||||
self.encoding = "utf-8"
|
||||
def write(self, data):
|
||||
if isinstance(data,bytes):
|
||||
if isinstance(data,str):
|
||||
data = data.encode(self.encoding,"replace")
|
||||
self.stream.write(data)
|
||||
self.stream.flush()
|
||||
self.stream.buffer.write(data)
|
||||
self.stream.buffer.flush()
|
||||
def __getattr__(self, attr):
|
||||
return getattr(self.stream, attr)
|
||||
|
||||
|
@ -312,9 +312,9 @@ class KoboLibrary(object):
|
|||
# step 1. check whether this looks like a real device
|
||||
if (device_path):
|
||||
# we got a device path
|
||||
self.kobodir = os.path.join(device_path, u".kobo")
|
||||
self.kobodir = os.path.join(device_path, ".kobo")
|
||||
# devices use KoboReader.sqlite
|
||||
kobodb = os.path.join(self.kobodir, u"KoboReader.sqlite")
|
||||
kobodb = os.path.join(self.kobodir, "KoboReader.sqlite")
|
||||
if (not(os.path.isfile(kobodb))):
|
||||
# device path seems to be wrong, unset it
|
||||
device_path = u""
|
||||
|
@ -326,22 +326,22 @@ class KoboLibrary(object):
|
|||
if (len(serials) == 0):
|
||||
# we got a device path but no saved serial
|
||||
# try to get the serial from the device
|
||||
# print u"get_device_settings - device_path = {0}".format(device_path)
|
||||
# print "get_device_settings - device_path = {0}".format(device_path)
|
||||
# get serial from device_path/.adobe-digital-editions/device.xml
|
||||
if can_parse_xml:
|
||||
devicexml = os.path.join(device_path, '.adobe-digital-editions', 'device.xml')
|
||||
# print u"trying to load {0}".format(devicexml)
|
||||
# print "trying to load {0}".format(devicexml)
|
||||
if (os.path.exists(devicexml)):
|
||||
# print u"trying to parse {0}".format(devicexml)
|
||||
# print "trying to parse {0}".format(devicexml)
|
||||
xmltree = ET.parse(devicexml)
|
||||
for node in xmltree.iter():
|
||||
if "deviceSerial" in node.tag:
|
||||
serial = node.text
|
||||
# print u"found serial {0}".format(serial)
|
||||
# print "found serial {0}".format(serial)
|
||||
serials.append(serial)
|
||||
break
|
||||
else:
|
||||
# print u"cannot get serials from device."
|
||||
# print "cannot get serials from device."
|
||||
device_path = u""
|
||||
self.kobodir = u""
|
||||
kobodb = u""
|
||||
|
@ -357,19 +357,19 @@ class KoboLibrary(object):
|
|||
if sys.getwindowsversion().major > 5:
|
||||
if 'LOCALAPPDATA' in os.environ.keys():
|
||||
# Python 2.x does not return unicode env. Use Python 3.x
|
||||
self.kobodir = winreg.ExpandEnvironmentStrings(u"%LOCALAPPDATA%")
|
||||
self.kobodir = winreg.ExpandEnvironmentStrings("%LOCALAPPDATA%")
|
||||
if (self.kobodir == u""):
|
||||
if 'USERPROFILE' in os.environ.keys():
|
||||
# Python 2.x does not return unicode env. Use Python 3.x
|
||||
self.kobodir = os.path.join(winreg.ExpandEnvironmentStrings(u"%USERPROFILE%"), u"Local Settings", u"Application Data")
|
||||
self.kobodir = os.path.join(self.kobodir, u"Kobo", u"Kobo Desktop Edition")
|
||||
self.kobodir = os.path.join(winreg.ExpandEnvironmentStrings("%USERPROFILE%"), "Local Settings", "Application Data")
|
||||
self.kobodir = os.path.join(self.kobodir, "Kobo", "Kobo Desktop Edition")
|
||||
elif sys.platform.startswith('darwin'):
|
||||
self.kobodir = os.path.join(os.environ['HOME'], u"Library", u"Application Support", u"Kobo", u"Kobo Desktop Edition")
|
||||
self.kobodir = os.path.join(os.environ['HOME'], "Library", "Application Support", "Kobo", "Kobo Desktop Edition")
|
||||
#elif linux_path != None:
|
||||
# Probably Linux, let's get the wine prefix and path to Kobo.
|
||||
# self.kobodir = os.path.join(linux_path, u"Local Settings", u"Application Data", u"Kobo", u"Kobo Desktop Edition")
|
||||
# self.kobodir = os.path.join(linux_path, "Local Settings", "Application Data", "Kobo", "Kobo Desktop Edition")
|
||||
# desktop versions use Kobo.sqlite
|
||||
kobodb = os.path.join(self.kobodir, u"Kobo.sqlite")
|
||||
kobodb = os.path.join(self.kobodir, "Kobo.sqlite")
|
||||
# check for existence of file
|
||||
if (not(os.path.isfile(kobodb))):
|
||||
# give up here, we haven't found anything useful
|
||||
|
@ -377,7 +377,7 @@ class KoboLibrary(object):
|
|||
kobodb = u""
|
||||
|
||||
if (self.kobodir != u""):
|
||||
self.bookdir = os.path.join(self.kobodir, u"kepub")
|
||||
self.bookdir = os.path.join(self.kobodir, "kepub")
|
||||
# make a copy of the database in a temporary file
|
||||
# so we can ensure it's not using WAL logging which sqlite3 can't do.
|
||||
self.newdb = tempfile.NamedTemporaryFile(mode='wb', delete=False)
|
||||
|
@ -437,7 +437,7 @@ class KoboLibrary(object):
|
|||
|
||||
def __bookfile (self, volumeid):
|
||||
"""The filename needed to open a given book."""
|
||||
return os.path.join(self.kobodir, u"kepub", volumeid)
|
||||
return os.path.join(self.kobodir, "kepub", volumeid)
|
||||
|
||||
def __getmacaddrs (self):
|
||||
"""The list of all MAC addresses on this machine."""
|
||||
|
@ -454,7 +454,7 @@ class KoboLibrary(object):
|
|||
output = subprocess.check_output('/sbin/ifconfig -a', shell=True)
|
||||
matches = c.findall(output)
|
||||
for m in matches:
|
||||
# print u"m:{0}".format(m[0])
|
||||
# print "m:{0}".format(m[0])
|
||||
macaddrs.append(m[0].upper())
|
||||
else:
|
||||
# probably linux
|
||||
|
@ -607,32 +607,32 @@ class KoboFile(object):
|
|||
# assume utf-8 with no BOM
|
||||
textoffset = 0
|
||||
stride = 1
|
||||
print(u"Checking text:{0}:".format(contents[:10]))
|
||||
print("Checking text:{0}:".format(contents[:10]))
|
||||
# check for byte order mark
|
||||
if contents[:3]==b"\xef\xbb\xbf":
|
||||
# seems to be utf-8 with BOM
|
||||
print(u"Could be utf-8 with BOM")
|
||||
print("Could be utf-8 with BOM")
|
||||
textoffset = 3
|
||||
elif contents[:2]==b"\xfe\xff":
|
||||
# seems to be utf-16BE
|
||||
print(u"Could be utf-16BE")
|
||||
print("Could be utf-16BE")
|
||||
textoffset = 3
|
||||
stride = 2
|
||||
elif contents[:2]==b"\xff\xfe":
|
||||
# seems to be utf-16LE
|
||||
print(u"Could be utf-16LE")
|
||||
print("Could be utf-16LE")
|
||||
textoffset = 2
|
||||
stride = 2
|
||||
else:
|
||||
print(u"Perhaps utf-8 without BOM")
|
||||
print("Perhaps utf-8 without BOM")
|
||||
|
||||
# now check that the first few characters are in the ASCII range
|
||||
for i in range(textoffset,textoffset+5*stride,stride):
|
||||
if contents[i]<32 or contents[i]>127:
|
||||
# Non-ascii, so decryption probably failed
|
||||
print(u"Bad character at {0}, value {1}".format(i,contents[i]))
|
||||
print("Bad character at {0}, value {1}".format(i,contents[i]))
|
||||
raise ValueError
|
||||
print(u"Seems to be good text")
|
||||
print("Seems to be good text")
|
||||
return True
|
||||
if contents[:5]==b"<?xml" or contents[:8]==b"\xef\xbb\xbf<?xml":
|
||||
# utf-8
|
||||
|
@ -653,13 +653,13 @@ class KoboFile(object):
|
|||
# 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
|
||||
|
||||
|
@ -682,18 +682,18 @@ class KoboFile(object):
|
|||
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():
|
||||
|
@ -705,12 +705,12 @@ def decrypt_book(book, lib):
|
|||
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()
|
||||
|
@ -719,7 +719,7 @@ def decrypt_book(book, lib):
|
|||
|
||||
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")
|
||||
|
@ -735,25 +735,25 @@ def cli_main():
|
|||
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
|
||||
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#!/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'
|
||||
|
@ -13,10 +13,7 @@ except ImportError:
|
|||
from io import StringIO
|
||||
from traceback import print_exc
|
||||
|
||||
try:
|
||||
from PyQt5.Qt import (Qt, QDialog, QPixmap, QIcon, QLabel, QHBoxLayout, QFont, QTableWidgetItem)
|
||||
except ImportError:
|
||||
from PyQt4.Qt import (Qt, QDialog, QPixmap, QIcon, QLabel, QHBoxLayout, QFont, QTableWidgetItem)
|
||||
|
||||
from calibre.utils.config import config_dir
|
||||
from calibre.constants import iswindows, DEBUG
|
||||
|
|
|
@ -153,7 +153,7 @@
|
|||
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
|
||||
|
@ -173,10 +173,10 @@ import tempfile
|
|||
can_parse_xml = True
|
||||
try:
|
||||
from xml.etree import ElementTree as ET
|
||||
# print u"using xml.etree for xml parsing"
|
||||
# print "using xml.etree for xml parsing"
|
||||
except ImportError:
|
||||
can_parse_xml = False
|
||||
# print u"Cannot find xml.etree, disabling extraction of serial numbers"
|
||||
# print "Cannot find xml.etree, disabling extraction of serial numbers"
|
||||
|
||||
# List of all known hash keys
|
||||
KOBO_HASH_KEYS = ['88b3a2e13', 'XzUhGYdFp', 'NoCanLook','QJhwzAtXL']
|
||||
|
@ -309,9 +309,9 @@ class KoboLibrary(object):
|
|||
# step 1. check whether this looks like a real device
|
||||
if (device_path):
|
||||
# we got a device path
|
||||
self.kobodir = os.path.join(device_path, u".kobo")
|
||||
self.kobodir = os.path.join(device_path, ".kobo")
|
||||
# devices use KoboReader.sqlite
|
||||
kobodb = os.path.join(self.kobodir, u"KoboReader.sqlite")
|
||||
kobodb = os.path.join(self.kobodir, "KoboReader.sqlite")
|
||||
if (not(os.path.isfile(kobodb))):
|
||||
# device path seems to be wrong, unset it
|
||||
device_path = u""
|
||||
|
@ -323,22 +323,22 @@ class KoboLibrary(object):
|
|||
if (len(serials) == 0):
|
||||
# we got a device path but no saved serial
|
||||
# try to get the serial from the device
|
||||
# print u"get_device_settings - device_path = {0}".format(device_path)
|
||||
# print "get_device_settings - device_path = {0}".format(device_path)
|
||||
# get serial from device_path/.adobe-digital-editions/device.xml
|
||||
if can_parse_xml:
|
||||
devicexml = os.path.join(device_path, '.adobe-digital-editions', 'device.xml')
|
||||
# print u"trying to load {0}".format(devicexml)
|
||||
# print "trying to load {0}".format(devicexml)
|
||||
if (os.path.exists(devicexml)):
|
||||
# print u"trying to parse {0}".format(devicexml)
|
||||
# print "trying to parse {0}".format(devicexml)
|
||||
xmltree = ET.parse(devicexml)
|
||||
for node in xmltree.iter():
|
||||
if "deviceSerial" in node.tag:
|
||||
serial = node.text
|
||||
# print u"found serial {0}".format(serial)
|
||||
# print "found serial {0}".format(serial)
|
||||
serials.append(serial)
|
||||
break
|
||||
else:
|
||||
# print u"cannot get serials from device."
|
||||
# print "cannot get serials from device."
|
||||
device_path = u""
|
||||
self.kobodir = u""
|
||||
kobodb = u""
|
||||
|
@ -350,19 +350,19 @@ class KoboLibrary(object):
|
|||
if sys.getwindowsversion().major > 5:
|
||||
if 'LOCALAPPDATA' in os.environ.keys():
|
||||
# Python 2.x does not return unicode env. Use Python 3.x
|
||||
self.kobodir = winreg.ExpandEnvironmentStrings(u"%LOCALAPPDATA%")
|
||||
self.kobodir = winreg.ExpandEnvironmentStrings("%LOCALAPPDATA%")
|
||||
if (self.kobodir == u""):
|
||||
if 'USERPROFILE' in os.environ.keys():
|
||||
# Python 2.x does not return unicode env. Use Python 3.x
|
||||
self.kobodir = os.path.join(winreg.ExpandEnvironmentStrings(u"%USERPROFILE%"), u"Local Settings", u"Application Data")
|
||||
self.kobodir = os.path.join(self.kobodir, u"Kobo", u"Kobo Desktop Edition")
|
||||
self.kobodir = os.path.join(winreg.ExpandEnvironmentStrings("%USERPROFILE%"), "Local Settings", "Application Data")
|
||||
self.kobodir = os.path.join(self.kobodir, "Kobo", "Kobo Desktop Edition")
|
||||
elif sys.platform.startswith('darwin'):
|
||||
self.kobodir = os.path.join(os.environ['HOME'], u"Library", u"Application Support", u"Kobo", u"Kobo Desktop Edition")
|
||||
self.kobodir = os.path.join(os.environ['HOME'], "Library", "Application Support", "Kobo", "Kobo Desktop Edition")
|
||||
#elif linux_path != None:
|
||||
# Probably Linux, let's get the wine prefix and path to Kobo.
|
||||
# self.kobodir = os.path.join(linux_path, u"Local Settings", u"Application Data", u"Kobo", u"Kobo Desktop Edition")
|
||||
# self.kobodir = os.path.join(linux_path, "Local Settings", "Application Data", "Kobo", "Kobo Desktop Edition")
|
||||
# desktop versions use Kobo.sqlite
|
||||
kobodb = os.path.join(self.kobodir, u"Kobo.sqlite")
|
||||
kobodb = os.path.join(self.kobodir, "Kobo.sqlite")
|
||||
# check for existence of file
|
||||
if (not(os.path.isfile(kobodb))):
|
||||
# give up here, we haven't found anything useful
|
||||
|
@ -371,7 +371,7 @@ class KoboLibrary(object):
|
|||
|
||||
|
||||
if (self.kobodir != u""):
|
||||
self.bookdir = os.path.join(self.kobodir, u"kepub")
|
||||
self.bookdir = os.path.join(self.kobodir, "kepub")
|
||||
# make a copy of the database in a temporary file
|
||||
# so we can ensure it's not using WAL logging which sqlite3 can't do.
|
||||
self.newdb = tempfile.NamedTemporaryFile(mode='wb', delete=False)
|
||||
|
@ -431,7 +431,7 @@ class KoboLibrary(object):
|
|||
|
||||
def __bookfile (self, volumeid):
|
||||
"""The filename needed to open a given book."""
|
||||
return os.path.join(self.kobodir, u"kepub", volumeid)
|
||||
return os.path.join(self.kobodir, "kepub", volumeid)
|
||||
|
||||
def __getmacaddrs (self):
|
||||
"""The list of all MAC addresses on this machine."""
|
||||
|
@ -448,7 +448,7 @@ class KoboLibrary(object):
|
|||
output = subprocess.check_output('/sbin/ifconfig -a', shell=True)
|
||||
matches = c.findall(output)
|
||||
for m in matches:
|
||||
# print u"m:{0}".format(m[0])
|
||||
# print "m:{0}".format(m[0])
|
||||
macaddrs.append(m[0].upper())
|
||||
elif sys.platform.startswith('linux'):
|
||||
p_out = subprocess.check_output("ip -br link show | awk '{print $3}'", shell=True)
|
||||
|
@ -596,32 +596,32 @@ class KoboFile(object):
|
|||
# assume utf-8 with no BOM
|
||||
textoffset = 0
|
||||
stride = 1
|
||||
print(u"Checking text:{0}:".format(contents[:10]))
|
||||
print("Checking text:{0}:".format(contents[:10]))
|
||||
# check for byte order mark
|
||||
if contents[:3]=="\xef\xbb\xbf":
|
||||
# seems to be utf-8 with BOM
|
||||
print(u"Could be utf-8 with BOM")
|
||||
print("Could be utf-8 with BOM")
|
||||
textoffset = 3
|
||||
elif contents[:2]=="\xfe\xff":
|
||||
# seems to be utf-16BE
|
||||
print(u"Could be utf-16BE")
|
||||
print("Could be utf-16BE")
|
||||
textoffset = 3
|
||||
stride = 2
|
||||
elif contents[:2]=="\xff\xfe":
|
||||
# seems to be utf-16LE
|
||||
print(u"Could be utf-16LE")
|
||||
print("Could be utf-16LE")
|
||||
textoffset = 2
|
||||
stride = 2
|
||||
else:
|
||||
print(u"Perhaps utf-8 without BOM")
|
||||
print("Perhaps utf-8 without BOM")
|
||||
|
||||
# now check that the first few characters are in the ASCII range
|
||||
for i in xrange(textoffset,textoffset+5*stride,stride):
|
||||
if ord(contents[i])<32 or ord(contents[i])>127:
|
||||
# Non-ascii, so decryption probably failed
|
||||
print(u"Bad character at {0}, value {1}".format(i,ord(contents[i])))
|
||||
print("Bad character at {0}, value {1}".format(i,ord(contents[i])))
|
||||
raise ValueError
|
||||
print(u"Seems to be good text")
|
||||
print("Seems to be good text")
|
||||
return True
|
||||
if contents[:5]=="<?xml" or contents[:8]=="\xef\xbb\xbf<?xml":
|
||||
# utf-8
|
||||
|
@ -642,13 +642,13 @@ class KoboFile(object):
|
|||
# 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
|
||||
|
||||
|
@ -671,18 +671,18 @@ class KoboFile(object):
|
|||
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():
|
||||
|
@ -694,12 +694,12 @@ def decrypt_book(book, lib):
|
|||
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()
|
||||
|
@ -708,7 +708,7 @@ def decrypt_book(book, lib):
|
|||
|
||||
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")
|
||||
|
@ -724,25 +724,25 @@ def cli_main():
|
|||
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
|
||||
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#!/usr/bin/env python
|
||||
# code: utf-8
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
'''
|
||||
A wrapper script to generate zip files for GitHub releases.
|
||||
|
|
Loading…
Reference in New Issue