tools v6.0.9

obok added to other tools
pull/6/head v6.0.9
Apprentice Alf 9 years ago
parent b1feca321d
commit a30aace99c

@ -24,19 +24,19 @@
<key>CFBundleExecutable</key>
<string>droplet</string>
<key>CFBundleGetInfoString</key>
<string>DeDRM AppleScript 6.0.8. Written 20102013 by Apprentice Alf and others.</string>
<string>DeDRM AppleScript 6.0.9. Written 20102013 by Apprentice Alf and others.</string>
<key>CFBundleIconFile</key>
<string>DeDRM</string>
<key>CFBundleIdentifier</key>
<string>com.apple.ScriptEditor.id.707CCCD5-0C6C-4BEB-B67C-B6E866ADE85A</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0.8</string>
<string>6.0.9</string>
<key>CFBundleName</key>
<string>DeDRM</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>6.0.8</string>
<string>6.0.9</string>
<key>CFBundleSignature</key>
<string>dplt</string>
<key>LSRequiresCarbon</key>

@ -17,7 +17,7 @@ p {margin-top: 0}
<body>
<h1>DeDRM Plugin <span class="version">(v6.0.0)</span></h1>
<h1>DeDRM Plugin <span class="version">(v6.0.9)</span></h1>
<p>This plugin removes DRM from ebooks when they are imported into calibre. If you already have DRMed ebooks in your calibre library, you will need to remove them and import them again.</p>

@ -35,13 +35,14 @@ __docformat__ = 'restructuredtext en'
# 6.0.6 - Fix up an incorrect function call
# 6.0.7 - Error handling for incomplete PDF metadata
# 6.0.8 - Fixes a Wine key issue and topaz support
# 6.0.9 - Ported to work with newer versions of Calibre (moved to Qt5). Still supports older Qt4 versions.
"""
Decrypt DRMed ebooks.
"""
PLUGIN_NAME = u"DeDRM"
PLUGIN_VERSION_TUPLE = (6, 0, 8)
PLUGIN_VERSION_TUPLE = (6, 0, 9)
PLUGIN_VERSION = u".".join([unicode(str(x)) for x in PLUGIN_VERSION_TUPLE])
# Include an html helpfile in the plugin's zipfile with the following name.
RESOURCE_NAME = PLUGIN_NAME + '_Help.htm'

@ -9,16 +9,24 @@ __license__ = 'GPL v3'
import os, traceback, json
# PyQT4 modules (part of calibre).
from PyQt4.Qt import (Qt, QWidget, QHBoxLayout, QVBoxLayout, QLabel, QLineEdit,
try:
from PyQt5.Qt import (Qt, QWidget, QHBoxLayout, QVBoxLayout, QLabel, QLineEdit,
QGroupBox, QPushButton, QListWidget, QListWidgetItem,
QAbstractItemView, QIcon, QDialog, QDialogButtonBox, QUrl, QString)
from PyQt4 import QtGui
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 zipfile import ZipFile
# calibre modules and constants.
from calibre.gui2 import (error_dialog, question_dialog, info_dialog, open_url,
choose_dir, choose_files)
choose_dir, choose_files, choose_save_file)
from calibre.utils.config import dynamic, config_dir, JSONConfig
from calibre.constants import iswindows, isosx
@ -267,7 +275,7 @@ class ManageKeysDialog(QDialog):
def getwineprefix(self):
if self.wineprefix is not None:
return unicode(self.wp_lineedit.text().toUtf8(), 'utf8').strip()
return unicode(self.wp_lineedit.text()).strip()
return u""
def populate_list(self):
@ -316,7 +324,7 @@ class ManageKeysDialog(QDialog):
if d.result() != d.Accepted:
# rename cancelled or moot.
return
keyname = unicode(self.listy.currentItem().text().toUtf8(),'utf8')
keyname = unicode(self.listy.currentItem().text())
if not question_dialog(self, "{0} {1}: Confirm Rename".format(PLUGIN_NAME, PLUGIN_VERSION), u"Do you really want to rename the {2} named <strong>{0}</strong> to <strong>{1}</strong>?".format(keyname,d.key_name,self.key_type_name), show_copy_button=False, default_yes=False):
return
self.plugin_keys[d.key_name] = self.plugin_keys[keyname]
@ -328,7 +336,7 @@ class ManageKeysDialog(QDialog):
def delete_key(self):
if not self.listy.currentItem():
return
keyname = unicode(self.listy.currentItem().text().toUtf8(), 'utf8')
keyname = unicode(self.listy.currentItem().text())
if not question_dialog(self, "{0} {1}: Confirm Delete".format(PLUGIN_NAME, PLUGIN_VERSION), u"Do you really want to delete the {1} <strong>{0}</strong>?".format(keyname, self.key_type_name), show_copy_button=False, default_yes=False):
return
if type(self.plugin_keys) == dict:
@ -352,9 +360,10 @@ class ManageKeysDialog(QDialog):
open_url(QUrl(url))
def migrate_files(self):
dynamic[PLUGIN_NAME + u"config_dir"] = config_dir
files = choose_files(self, PLUGIN_NAME + u"config_dir",
u"Select {0} files to import".format(self.key_type_name), [(u"{0} files".format(self.key_type_name), [self.keyfile_ext])], False)
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])]
files = choose_files(self, unique_dlg_name, caption, filters, all_files=False)
counter = 0
skipped = 0
if files:
@ -408,17 +417,14 @@ class ManageKeysDialog(QDialog):
r = error_dialog(None, "{0} {1}".format(PLUGIN_NAME, PLUGIN_VERSION),
_(errmsg), show=True, show_copy_button=False)
return
filter = QString(u"{0} Files (*.{1})".format(self.key_type_name, self.keyfile_ext))
keyname = unicode(self.listy.currentItem().text().toUtf8(), 'utf8')
if dynamic.get(PLUGIN_NAME + 'save_dir'):
defaultname = os.path.join(dynamic.get(PLUGIN_NAME + 'save_dir'), u"{0}.{1}".format(keyname , self.keyfile_ext))
else:
defaultname = os.path.join(os.path.expanduser('~'), u"{0}.{1}".format(keyname , self.keyfile_ext))
filename = unicode(QtGui.QFileDialog.getSaveFileName(self, u"Save {0} File as...".format(self.key_type_name), defaultname,
u"{0} Files (*.{1})".format(self.key_type_name,self.keyfile_ext), filter))
keyname = unicode(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)
filename = choose_save_file(self, unique_dlg_name, caption, filters, all_files=False, initial_filename=defaultname)
if filename:
dynamic[PLUGIN_NAME + 'save_dir'] = os.path.split(filename)[0]
with file(filename, 'w') as fname:
with file(filename, 'wb') as fname:
if self.binary_file:
fname.write(self.plugin_keys[keyname].decode('hex'))
elif self.json_file:
@ -458,7 +464,7 @@ class RenameKeyDialog(QDialog):
self.resize(self.sizeHint())
def accept(self):
if self.key_ledit.text().isEmpty() or unicode(self.key_ledit.text()).isspace():
if not unicode(self.key_ledit.text()) or unicode(self.key_ledit.text()).isspace():
errmsg = u"Key name field cannot be empty!"
return error_dialog(None, "{0} {1}".format(PLUGIN_NAME, PLUGIN_VERSION),
_(errmsg), show=True, show_copy_button=False)
@ -479,7 +485,7 @@ class RenameKeyDialog(QDialog):
@property
def key_name(self):
return unicode(self.key_ledit.text().toUtf8(), 'utf8').strip()
return unicode(self.key_ledit.text()).strip()
@ -553,7 +559,7 @@ class AddBandNKeyDialog(QDialog):
@property
def key_name(self):
return unicode(self.key_ledit.text().toUtf8(), 'utf8').strip()
return unicode(self.key_ledit.text()).strip()
@property
def key_value(self):
@ -562,11 +568,11 @@ class AddBandNKeyDialog(QDialog):
@property
def user_name(self):
return unicode(self.name_ledit.text().toUtf8(), 'utf8').strip().lower().replace(' ','')
return unicode(self.name_ledit.text()).strip().lower().replace(' ','')
@property
def cc_number(self):
return unicode(self.cc_ledit.text().toUtf8(), 'utf8').strip().replace(' ', '').replace('-','')
return unicode(self.cc_ledit.text()).strip().replace(' ', '').replace('-','')
def accept(self):
@ -634,7 +640,7 @@ class AddEReaderDialog(QDialog):
@property
def key_name(self):
return unicode(self.key_ledit.text().toUtf8(), 'utf8').strip()
return unicode(self.key_ledit.text()).strip()
@property
def key_value(self):
@ -643,11 +649,11 @@ class AddEReaderDialog(QDialog):
@property
def user_name(self):
return unicode(self.name_ledit.text().toUtf8(), 'utf8').strip().lower().replace(' ','')
return unicode(self.name_ledit.text()).strip().lower().replace(' ','')
@property
def cc_number(self):
return unicode(self.cc_ledit.text().toUtf8(), 'utf8').strip().replace(' ', '').replace('-','')
return unicode(self.cc_ledit.text()).strip().replace(' ', '').replace('-','')
def accept(self):
@ -719,7 +725,7 @@ class AddAdeptDialog(QDialog):
@property
def key_name(self):
return unicode(self.key_ledit.text().toUtf8(), 'utf8').strip()
return unicode(self.key_ledit.text()).strip()
@property
def key_value(self):
@ -792,7 +798,7 @@ class AddKindleDialog(QDialog):
@property
def key_name(self):
return unicode(self.key_ledit.text().toUtf8(), 'utf8').strip()
return unicode(self.key_ledit.text()).strip()
@property
def key_value(self):
@ -841,11 +847,11 @@ class AddSerialDialog(QDialog):
@property
def key_name(self):
return unicode(self.key_ledit.text().toUtf8(), 'utf8').strip()
return unicode(self.key_ledit.text()).strip()
@property
def key_value(self):
return unicode(self.key_ledit.text().toUtf8(), 'utf8').strip()
return unicode(self.key_ledit.text()).strip()
def accept(self):
if len(self.key_name) == 0 or self.key_name.isspace():
@ -889,11 +895,11 @@ class AddPIDDialog(QDialog):
@property
def key_name(self):
return unicode(self.key_ledit.text().toUtf8(), 'utf8').strip()
return unicode(self.key_ledit.text()).strip()
@property
def key_value(self):
return unicode(self.key_ledit.text().toUtf8(), 'utf8').strip()
return unicode(self.key_ledit.text()).strip()
def accept(self):
if len(self.key_name) == 0 or self.key_name.isspace():

@ -17,7 +17,7 @@ p {margin-top: 0}
<body>
<h1>DeDRM Plugin <span class="version">(v6.0.0)</span></h1>
<h1>DeDRM Plugin <span class="version">(v6.0.9)</span></h1>
<p>This plugin removes DRM from ebooks when they are imported into calibre. If you already have DRMed ebooks in your calibre library, you will need to remove them and import them again.</p>

@ -35,13 +35,14 @@ __docformat__ = 'restructuredtext en'
# 6.0.6 - Fix up an incorrect function call
# 6.0.7 - Error handling for incomplete PDF metadata
# 6.0.8 - Fixes a Wine key issue and topaz support
# 6.0.9 - Ported to work with newer versions of Calibre (moved to Qt5). Still supports older Qt4 versions.
"""
Decrypt DRMed ebooks.
"""
PLUGIN_NAME = u"DeDRM"
PLUGIN_VERSION_TUPLE = (6, 0, 8)
PLUGIN_VERSION_TUPLE = (6, 0, 9)
PLUGIN_VERSION = u".".join([unicode(str(x)) for x in PLUGIN_VERSION_TUPLE])
# Include an html helpfile in the plugin's zipfile with the following name.
RESOURCE_NAME = PLUGIN_NAME + '_Help.htm'

@ -9,16 +9,24 @@ __license__ = 'GPL v3'
import os, traceback, json
# PyQT4 modules (part of calibre).
from PyQt4.Qt import (Qt, QWidget, QHBoxLayout, QVBoxLayout, QLabel, QLineEdit,
try:
from PyQt5.Qt import (Qt, QWidget, QHBoxLayout, QVBoxLayout, QLabel, QLineEdit,
QGroupBox, QPushButton, QListWidget, QListWidgetItem,
QAbstractItemView, QIcon, QDialog, QDialogButtonBox, QUrl, QString)
from PyQt4 import QtGui
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 zipfile import ZipFile
# calibre modules and constants.
from calibre.gui2 import (error_dialog, question_dialog, info_dialog, open_url,
choose_dir, choose_files)
choose_dir, choose_files, choose_save_file)
from calibre.utils.config import dynamic, config_dir, JSONConfig
from calibre.constants import iswindows, isosx
@ -267,7 +275,7 @@ class ManageKeysDialog(QDialog):
def getwineprefix(self):
if self.wineprefix is not None:
return unicode(self.wp_lineedit.text().toUtf8(), 'utf8').strip()
return unicode(self.wp_lineedit.text()).strip()
return u""
def populate_list(self):
@ -316,7 +324,7 @@ class ManageKeysDialog(QDialog):
if d.result() != d.Accepted:
# rename cancelled or moot.
return
keyname = unicode(self.listy.currentItem().text().toUtf8(),'utf8')
keyname = unicode(self.listy.currentItem().text())
if not question_dialog(self, "{0} {1}: Confirm Rename".format(PLUGIN_NAME, PLUGIN_VERSION), u"Do you really want to rename the {2} named <strong>{0}</strong> to <strong>{1}</strong>?".format(keyname,d.key_name,self.key_type_name), show_copy_button=False, default_yes=False):
return
self.plugin_keys[d.key_name] = self.plugin_keys[keyname]
@ -328,7 +336,7 @@ class ManageKeysDialog(QDialog):
def delete_key(self):
if not self.listy.currentItem():
return
keyname = unicode(self.listy.currentItem().text().toUtf8(), 'utf8')
keyname = unicode(self.listy.currentItem().text())
if not question_dialog(self, "{0} {1}: Confirm Delete".format(PLUGIN_NAME, PLUGIN_VERSION), u"Do you really want to delete the {1} <strong>{0}</strong>?".format(keyname, self.key_type_name), show_copy_button=False, default_yes=False):
return
if type(self.plugin_keys) == dict:
@ -352,9 +360,10 @@ class ManageKeysDialog(QDialog):
open_url(QUrl(url))
def migrate_files(self):
dynamic[PLUGIN_NAME + u"config_dir"] = config_dir
files = choose_files(self, PLUGIN_NAME + u"config_dir",
u"Select {0} files to import".format(self.key_type_name), [(u"{0} files".format(self.key_type_name), [self.keyfile_ext])], False)
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])]
files = choose_files(self, unique_dlg_name, caption, filters, all_files=False)
counter = 0
skipped = 0
if files:
@ -408,17 +417,14 @@ class ManageKeysDialog(QDialog):
r = error_dialog(None, "{0} {1}".format(PLUGIN_NAME, PLUGIN_VERSION),
_(errmsg), show=True, show_copy_button=False)
return
filter = QString(u"{0} Files (*.{1})".format(self.key_type_name, self.keyfile_ext))
keyname = unicode(self.listy.currentItem().text().toUtf8(), 'utf8')
if dynamic.get(PLUGIN_NAME + 'save_dir'):
defaultname = os.path.join(dynamic.get(PLUGIN_NAME + 'save_dir'), u"{0}.{1}".format(keyname , self.keyfile_ext))
else:
defaultname = os.path.join(os.path.expanduser('~'), u"{0}.{1}".format(keyname , self.keyfile_ext))
filename = unicode(QtGui.QFileDialog.getSaveFileName(self, u"Save {0} File as...".format(self.key_type_name), defaultname,
u"{0} Files (*.{1})".format(self.key_type_name,self.keyfile_ext), filter))
keyname = unicode(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)
filename = choose_save_file(self, unique_dlg_name, caption, filters, all_files=False, initial_filename=defaultname)
if filename:
dynamic[PLUGIN_NAME + 'save_dir'] = os.path.split(filename)[0]
with file(filename, 'w') as fname:
with file(filename, 'wb') as fname:
if self.binary_file:
fname.write(self.plugin_keys[keyname].decode('hex'))
elif self.json_file:
@ -458,7 +464,7 @@ class RenameKeyDialog(QDialog):
self.resize(self.sizeHint())
def accept(self):
if self.key_ledit.text().isEmpty() or unicode(self.key_ledit.text()).isspace():
if not unicode(self.key_ledit.text()) or unicode(self.key_ledit.text()).isspace():
errmsg = u"Key name field cannot be empty!"
return error_dialog(None, "{0} {1}".format(PLUGIN_NAME, PLUGIN_VERSION),
_(errmsg), show=True, show_copy_button=False)
@ -479,7 +485,7 @@ class RenameKeyDialog(QDialog):
@property
def key_name(self):
return unicode(self.key_ledit.text().toUtf8(), 'utf8').strip()
return unicode(self.key_ledit.text()).strip()
@ -553,7 +559,7 @@ class AddBandNKeyDialog(QDialog):
@property
def key_name(self):
return unicode(self.key_ledit.text().toUtf8(), 'utf8').strip()
return unicode(self.key_ledit.text()).strip()
@property
def key_value(self):
@ -562,11 +568,11 @@ class AddBandNKeyDialog(QDialog):
@property
def user_name(self):
return unicode(self.name_ledit.text().toUtf8(), 'utf8').strip().lower().replace(' ','')
return unicode(self.name_ledit.text()).strip().lower().replace(' ','')
@property
def cc_number(self):
return unicode(self.cc_ledit.text().toUtf8(), 'utf8').strip().replace(' ', '').replace('-','')
return unicode(self.cc_ledit.text()).strip().replace(' ', '').replace('-','')
def accept(self):
@ -634,7 +640,7 @@ class AddEReaderDialog(QDialog):
@property
def key_name(self):
return unicode(self.key_ledit.text().toUtf8(), 'utf8').strip()
return unicode(self.key_ledit.text()).strip()
@property
def key_value(self):
@ -643,11 +649,11 @@ class AddEReaderDialog(QDialog):
@property
def user_name(self):
return unicode(self.name_ledit.text().toUtf8(), 'utf8').strip().lower().replace(' ','')
return unicode(self.name_ledit.text()).strip().lower().replace(' ','')
@property
def cc_number(self):
return unicode(self.cc_ledit.text().toUtf8(), 'utf8').strip().replace(' ', '').replace('-','')
return unicode(self.cc_ledit.text()).strip().replace(' ', '').replace('-','')
def accept(self):
@ -719,7 +725,7 @@ class AddAdeptDialog(QDialog):
@property
def key_name(self):
return unicode(self.key_ledit.text().toUtf8(), 'utf8').strip()
return unicode(self.key_ledit.text()).strip()
@property
def key_value(self):
@ -792,7 +798,7 @@ class AddKindleDialog(QDialog):
@property
def key_name(self):
return unicode(self.key_ledit.text().toUtf8(), 'utf8').strip()
return unicode(self.key_ledit.text()).strip()
@property
def key_value(self):
@ -841,11 +847,11 @@ class AddSerialDialog(QDialog):
@property
def key_name(self):
return unicode(self.key_ledit.text().toUtf8(), 'utf8').strip()
return unicode(self.key_ledit.text()).strip()
@property
def key_value(self):
return unicode(self.key_ledit.text().toUtf8(), 'utf8').strip()
return unicode(self.key_ledit.text()).strip()
def accept(self):
if len(self.key_name) == 0 or self.key_name.isspace():
@ -889,11 +895,11 @@ class AddPIDDialog(QDialog):
@property
def key_name(self):
return unicode(self.key_ledit.text().toUtf8(), 'utf8').strip()
return unicode(self.key_ledit.text()).strip()
@property
def key_value(self):
return unicode(self.key_ledit.text().toUtf8(), 'utf8').strip()
return unicode(self.key_ledit.text()).strip()
def accept(self):
if len(self.key_name) == 0 or self.key_name.isspace():

@ -17,7 +17,7 @@ p {margin-top: 0}
<body>
<h1>DeDRM Plugin <span class="version">(v6.0.0)</span></h1>
<h1>DeDRM Plugin <span class="version">(v6.0.9)</span></h1>
<p>This plugin removes DRM from ebooks when they are imported into calibre. If you already have DRMed ebooks in your calibre library, you will need to remove them and import them again.</p>

@ -35,13 +35,14 @@ __docformat__ = 'restructuredtext en'
# 6.0.6 - Fix up an incorrect function call
# 6.0.7 - Error handling for incomplete PDF metadata
# 6.0.8 - Fixes a Wine key issue and topaz support
# 6.0.9 - Ported to work with newer versions of Calibre (moved to Qt5). Still supports older Qt4 versions.
"""
Decrypt DRMed ebooks.
"""
PLUGIN_NAME = u"DeDRM"
PLUGIN_VERSION_TUPLE = (6, 0, 8)
PLUGIN_VERSION_TUPLE = (6, 0, 9)
PLUGIN_VERSION = u".".join([unicode(str(x)) for x in PLUGIN_VERSION_TUPLE])
# Include an html helpfile in the plugin's zipfile with the following name.
RESOURCE_NAME = PLUGIN_NAME + '_Help.htm'

@ -9,16 +9,24 @@ __license__ = 'GPL v3'
import os, traceback, json
# PyQT4 modules (part of calibre).
from PyQt4.Qt import (Qt, QWidget, QHBoxLayout, QVBoxLayout, QLabel, QLineEdit,
try:
from PyQt5.Qt import (Qt, QWidget, QHBoxLayout, QVBoxLayout, QLabel, QLineEdit,
QGroupBox, QPushButton, QListWidget, QListWidgetItem,
QAbstractItemView, QIcon, QDialog, QDialogButtonBox, QUrl, QString)
from PyQt4 import QtGui
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 zipfile import ZipFile
# calibre modules and constants.
from calibre.gui2 import (error_dialog, question_dialog, info_dialog, open_url,
choose_dir, choose_files)
choose_dir, choose_files, choose_save_file)
from calibre.utils.config import dynamic, config_dir, JSONConfig
from calibre.constants import iswindows, isosx
@ -267,7 +275,7 @@ class ManageKeysDialog(QDialog):
def getwineprefix(self):
if self.wineprefix is not None:
return unicode(self.wp_lineedit.text().toUtf8(), 'utf8').strip()
return unicode(self.wp_lineedit.text()).strip()
return u""
def populate_list(self):
@ -316,7 +324,7 @@ class ManageKeysDialog(QDialog):
if d.result() != d.Accepted:
# rename cancelled or moot.
return
keyname = unicode(self.listy.currentItem().text().toUtf8(),'utf8')
keyname = unicode(self.listy.currentItem().text())
if not question_dialog(self, "{0} {1}: Confirm Rename".format(PLUGIN_NAME, PLUGIN_VERSION), u"Do you really want to rename the {2} named <strong>{0}</strong> to <strong>{1}</strong>?".format(keyname,d.key_name,self.key_type_name), show_copy_button=False, default_yes=False):
return
self.plugin_keys[d.key_name] = self.plugin_keys[keyname]
@ -328,7 +336,7 @@ class ManageKeysDialog(QDialog):
def delete_key(self):
if not self.listy.currentItem():
return
keyname = unicode(self.listy.currentItem().text().toUtf8(), 'utf8')
keyname = unicode(self.listy.currentItem().text())
if not question_dialog(self, "{0} {1}: Confirm Delete".format(PLUGIN_NAME, PLUGIN_VERSION), u"Do you really want to delete the {1} <strong>{0}</strong>?".format(keyname, self.key_type_name), show_copy_button=False, default_yes=False):
return
if type(self.plugin_keys) == dict:
@ -352,9 +360,10 @@ class ManageKeysDialog(QDialog):
open_url(QUrl(url))
def migrate_files(self):
dynamic[PLUGIN_NAME + u"config_dir"] = config_dir
files = choose_files(self, PLUGIN_NAME + u"config_dir",
u"Select {0} files to import".format(self.key_type_name), [(u"{0} files".format(self.key_type_name), [self.keyfile_ext])], False)
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])]
files = choose_files(self, unique_dlg_name, caption, filters, all_files=False)
counter = 0
skipped = 0
if files:
@ -408,17 +417,14 @@ class ManageKeysDialog(QDialog):
r = error_dialog(None, "{0} {1}".format(PLUGIN_NAME, PLUGIN_VERSION),
_(errmsg), show=True, show_copy_button=False)
return
filter = QString(u"{0} Files (*.{1})".format(self.key_type_name, self.keyfile_ext))
keyname = unicode(self.listy.currentItem().text().toUtf8(), 'utf8')
if dynamic.get(PLUGIN_NAME + 'save_dir'):
defaultname = os.path.join(dynamic.get(PLUGIN_NAME + 'save_dir'), u"{0}.{1}".format(keyname , self.keyfile_ext))
else:
defaultname = os.path.join(os.path.expanduser('~'), u"{0}.{1}".format(keyname , self.keyfile_ext))
filename = unicode(QtGui.QFileDialog.getSaveFileName(self, u"Save {0} File as...".format(self.key_type_name), defaultname,
u"{0} Files (*.{1})".format(self.key_type_name,self.keyfile_ext), filter))
keyname = unicode(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)
filename = choose_save_file(self, unique_dlg_name, caption, filters, all_files=False, initial_filename=defaultname)
if filename:
dynamic[PLUGIN_NAME + 'save_dir'] = os.path.split(filename)[0]
with file(filename, 'w') as fname:
with file(filename, 'wb') as fname:
if self.binary_file:
fname.write(self.plugin_keys[keyname].decode('hex'))
elif self.json_file:
@ -458,7 +464,7 @@ class RenameKeyDialog(QDialog):
self.resize(self.sizeHint())
def accept(self):
if self.key_ledit.text().isEmpty() or unicode(self.key_ledit.text()).isspace():
if not unicode(self.key_ledit.text()) or unicode(self.key_ledit.text()).isspace():
errmsg = u"Key name field cannot be empty!"
return error_dialog(None, "{0} {1}".format(PLUGIN_NAME, PLUGIN_VERSION),
_(errmsg), show=True, show_copy_button=False)
@ -479,7 +485,7 @@ class RenameKeyDialog(QDialog):
@property
def key_name(self):
return unicode(self.key_ledit.text().toUtf8(), 'utf8').strip()
return unicode(self.key_ledit.text()).strip()
@ -553,7 +559,7 @@ class AddBandNKeyDialog(QDialog):
@property
def key_name(self):
return unicode(self.key_ledit.text().toUtf8(), 'utf8').strip()
return unicode(self.key_ledit.text()).strip()
@property
def key_value(self):
@ -562,11 +568,11 @@ class AddBandNKeyDialog(QDialog):
@property
def user_name(self):
return unicode(self.name_ledit.text().toUtf8(), 'utf8').strip().lower().replace(' ','')
return unicode(self.name_ledit.text()).strip().lower().replace(' ','')
@property
def cc_number(self):
return unicode(self.cc_ledit.text().toUtf8(), 'utf8').strip().replace(' ', '').replace('-','')
return unicode(self.cc_ledit.text()).strip().replace(' ', '').replace('-','')
def accept(self):
@ -634,7 +640,7 @@ class AddEReaderDialog(QDialog):
@property
def key_name(self):
return unicode(self.key_ledit.text().toUtf8(), 'utf8').strip()
return unicode(self.key_ledit.text()).strip()
@property
def key_value(self):
@ -643,11 +649,11 @@ class AddEReaderDialog(QDialog):
@property
def user_name(self):
return unicode(self.name_ledit.text().toUtf8(), 'utf8').strip().lower().replace(' ','')
return unicode(self.name_ledit.text()).strip().lower().replace(' ','')
@property
def cc_number(self):
return unicode(self.cc_ledit.text().toUtf8(), 'utf8').strip().replace(' ', '').replace('-','')
return unicode(self.cc_ledit.text()).strip().replace(' ', '').replace('-','')
def accept(self):
@ -719,7 +725,7 @@ class AddAdeptDialog(QDialog):
@property
def key_name(self):
return unicode(self.key_ledit.text().toUtf8(), 'utf8').strip()
return unicode(self.key_ledit.text()).strip()
@property
def key_value(self):
@ -792,7 +798,7 @@ class AddKindleDialog(QDialog):
@property
def key_name(self):
return unicode(self.key_ledit.text().toUtf8(), 'utf8').strip()
return unicode(self.key_ledit.text()).strip()
@property
def key_value(self):
@ -841,11 +847,11 @@ class AddSerialDialog(QDialog):
@property
def key_name(self):
return unicode(self.key_ledit.text().toUtf8(), 'utf8').strip()
return unicode(self.key_ledit.text()).strip()
@property
def key_value(self):
return unicode(self.key_ledit.text().toUtf8(), 'utf8').strip()
return unicode(self.key_ledit.text()).strip()
def accept(self):
if len(self.key_name) == 0 or self.key_name.isspace():
@ -889,11 +895,11 @@ class AddPIDDialog(QDialog):
@property
def key_name(self):
return unicode(self.key_ledit.text().toUtf8(), 'utf8').strip()
return unicode(self.key_ledit.text()).strip()
@property
def key_value(self):
return unicode(self.key_ledit.text().toUtf8(), 'utf8').strip()
return unicode(self.key_ledit.text()).strip()
def accept(self):
if len(self.key_name) == 0 or self.key_name.isspace():

@ -0,0 +1,238 @@
Only in kindle4.0.2.1: build
diff -r -u10 kindle4.0.2.1_orig/smali/com/amazon/kcp/application/AndroidDeviceInformationProvider.smali kindle4.0.2.1/smali/com/amazon/kcp/application/AndroidDeviceInformationProvider.smali
--- kindle4.0.2.1_orig/smali/com/amazon/kcp/application/AndroidDeviceInformationProvider.smali 2013-05-22 18:39:03.000000000 -0500
+++ kindle4.0.2.1/smali/com/amazon/kcp/application/AndroidDeviceInformationProvider.smali 2013-05-23 16:54:53.000000000 -0500
@@ -36,20 +36,22 @@
.field private maxCpuSpeed:J
.field private maxMemory:J
.field private minCpuSpeed:J
.field private resources:Landroid/content/res/Resources;
.field private security:Lcom/mobipocket/android/library/reader/AndroidSecurity;
+.field private pidList:Ljava/lang/String;
+
.field private totalMemory:J
# direct methods
.method static constructor <clinit>()V
.locals 1
.prologue
.line 30
const-class v0, Lcom/amazon/kcp/application/AndroidDeviceInformationProvider;
@@ -72,20 +74,24 @@
.prologue
.line 130
invoke-direct {p0}, Ljava/lang/Object;-><init>()V
.line 131
iput-object p1, p0, Lcom/amazon/kcp/application/AndroidDeviceInformationProvider;->security:Lcom/mobipocket/android/library/reader/AndroidSecurity;
.line 132
iput-object p2, p0, Lcom/amazon/kcp/application/AndroidDeviceInformationProvider;->deviceType:Lcom/amazon/kcp/application/AmazonDeviceType;
+ const-string v0, "Open DRMed book to show PID list."
+
+ iput-object v0, p0, Lcom/amazon/kcp/application/AndroidDeviceInformationProvider;->pidList:Ljava/lang/String;
+
.line 133
sget-object v0, Lcom/amazon/kcp/application/AndroidDeviceInformationProvider;->TAG:Ljava/lang/String;
new-instance v0, Ljava/lang/StringBuilder;
invoke-direct {v0}, Ljava/lang/StringBuilder;-><init>()V
const-string v1, "Device Type is set to \""
invoke-virtual {v0, v1}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
@@ -1235,10 +1241,33 @@
move-result-wide v0
iput-wide v0, p0, Lcom/amazon/kcp/application/AndroidDeviceInformationProvider;->totalMemory:J
.line 308
:cond_0
iget-wide v0, p0, Lcom/amazon/kcp/application/AndroidDeviceInformationProvider;->totalMemory:J
return-wide v0
.end method
+
+.method public getPidList()Ljava/lang/String;
+ .locals 1
+
+ .prologue
+ .line 15
+ iget-object v0, p0, Lcom/amazon/kcp/application/AndroidDeviceInformationProvider;->pidList:Ljava/lang/String;
+
+ return-object v0
+.end method
+
+.method public setPidList(Ljava/lang/String;)V
+ .locals 0
+ .parameter "value"
+
+ .prologue
+ .line 11
+ iput-object p1, p0, Lcom/amazon/kcp/application/AndroidDeviceInformationProvider;->pidList:Ljava/lang/String;
+
+ .line 12
+ return-void
+.end method
+
diff -r -u10 kindle4.0.2.1_orig/smali/com/amazon/kcp/application/IDeviceInformationProvider.smali kindle4.0.2.1/smali/com/amazon/kcp/application/IDeviceInformationProvider.smali
--- kindle4.0.2.1_orig/smali/com/amazon/kcp/application/IDeviceInformationProvider.smali 2013-05-22 18:39:03.000000000 -0500
+++ kindle4.0.2.1/smali/com/amazon/kcp/application/IDeviceInformationProvider.smali 2013-05-23 16:55:58.000000000 -0500
@@ -23,10 +23,16 @@
.end method
.method public abstract getDeviceTypeId()Ljava/lang/String;
.end method
.method public abstract getOsVersion()Ljava/lang/String;
.end method
.method public abstract getPid()Ljava/lang/String;
.end method
+
+.method public abstract getPidList()Ljava/lang/String;
+.end method
+
+.method public abstract setPidList(Ljava/lang/String;)V
+.end method
diff -r -u10 kindle4.0.2.1_orig/smali/com/amazon/kcp/info/AboutActivity.smali kindle4.0.2.1/smali/com/amazon/kcp/info/AboutActivity.smali
--- kindle4.0.2.1_orig/smali/com/amazon/kcp/info/AboutActivity.smali 2013-05-22 18:39:03.000000000 -0500
+++ kindle4.0.2.1/smali/com/amazon/kcp/info/AboutActivity.smali 2013-05-23 17:18:14.000000000 -0500
@@ -486,20 +486,71 @@
.end local v2 #screenDpi:Ljava/lang/String;
:cond_0
iget-object v5, p0, Lcom/amazon/kcp/info/AboutActivity;->detailItemList:Ljava/util/List;
invoke-interface {v5, v0}, Ljava/util/List;->add(Ljava/lang/Object;)Z
.line 317
return-void
.end method
+.method private populatePIDList()V
+ .locals 7
+
+ .prologue
+ .line 313
+ invoke-static {}, Lcom/amazon/kcp/application/DeviceInformationProviderFactory;->getProvider()Lcom/amazon/kcp/application/IDeviceInformationProvider;
+
+ move-result-object v0
+
+ invoke-interface {v0}, Lcom/amazon/kcp/application/IDeviceInformationProvider;->getPidList()Ljava/lang/String;
+
+ move-result-object v1
+
+ .line 314
+ .local v1, PidList:Ljava/lang/String;
+ iget-object v3, p0, Lcom/amazon/kcp/info/AboutActivity;->groupItemList:Ljava/util/List;
+
+ new-instance v4, Lcom/amazon/kcp/info/AboutActivity$GroupItem;
+
+ const-string v5, "PID List"
+
+ const v6, 0x1
+
+ invoke-direct {v4, p0, v5, v6}, Lcom/amazon/kcp/info/AboutActivity$GroupItem;-><init>(Lcom/amazon/kcp/info/AboutActivity;Ljava/lang/String;Z)V
+
+ invoke-interface {v3, v4}, Ljava/util/List;->add(Ljava/lang/Object;)Z
+
+ .line 315
+ new-instance v2, Ljava/util/ArrayList;
+
+ invoke-direct {v2}, Ljava/util/ArrayList;-><init>()V
+
+ .line 316
+ .local v2, children:Ljava/util/List;,"Ljava/util/List<Lcom/amazon/kcp/info/AboutActivity$DetailItem;>;"
+ new-instance v3, Lcom/amazon/kcp/info/AboutActivity$DetailItem;
+
+ const-string v4, "PIDs"
+
+ invoke-direct {v3, p0, v4, v1}, Lcom/amazon/kcp/info/AboutActivity$DetailItem;-><init>(Lcom/amazon/kcp/info/AboutActivity;Ljava/lang/String;Ljava/lang/String;)V
+
+ invoke-interface {v2, v3}, Ljava/util/List;->add(Ljava/lang/Object;)Z
+
+ .line 317
+ iget-object v3, p0, Lcom/amazon/kcp/info/AboutActivity;->detailItemList:Ljava/util/List;
+
+ invoke-interface {v3, v2}, Ljava/util/List;->add(Ljava/lang/Object;)Z
+
+ .line 318
+ return-void
+.end method
+
.method private populateDisplayItems()V
.locals 1
.prologue
.line 171
iget-object v0, p0, Lcom/amazon/kcp/info/AboutActivity;->groupItemList:Ljava/util/List;
if-nez v0, :cond_0
.line 173
@@ -531,20 +582,22 @@
.line 192
invoke-direct {p0}, Lcom/amazon/kcp/info/AboutActivity;->populateRamInformation()V
.line 193
invoke-direct {p0}, Lcom/amazon/kcp/info/AboutActivity;->populateStorageInformation()V
.line 194
invoke-direct {p0}, Lcom/amazon/kcp/info/AboutActivity;->populateDisplayInformation()V
+ invoke-direct {p0}, Lcom/amazon/kcp/info/AboutActivity;->populatePIDList()V
+
.line 195
return-void
.line 177
:cond_0
iget-object v0, p0, Lcom/amazon/kcp/info/AboutActivity;->groupItemList:Ljava/util/List;
invoke-interface {v0}, Ljava/util/List;->clear()V
goto :goto_0
diff -r -u10 kindle4.0.2.1_orig/smali/com/amazon/system/security/Security.smali kindle4.0.2.1/smali/com/amazon/system/security/Security.smali
--- kindle4.0.2.1_orig/smali/com/amazon/system/security/Security.smali 2013-05-22 18:39:04.000000000 -0500
+++ kindle4.0.2.1/smali/com/amazon/system/security/Security.smali 2013-05-23 17:19:05.000000000 -0500
@@ -920,20 +920,30 @@
.line 350
:cond_2
add-int/lit8 v8, v8, 0x1
.line 351
sget-object v0, Lcom/amazon/system/security/Security;->CUSTOM_PID_FOR_BUNDLED_DICTIONARY_DRM:Ljava/lang/String;
aput-object v0, v6, v8
+ invoke-static {}, Lcom/amazon/kcp/application/DeviceInformationProviderFactory;->getProvider()Lcom/amazon/kcp/application/IDeviceInformationProvider;
+
+ move-result-object v5
+
+ invoke-static {v6}, Ljava/util/Arrays;->toString([Ljava/lang/Object;)Ljava/lang/String;
+
+ move-result-object v2
+
+ invoke-interface {v5, v2}, Lcom/amazon/kcp/application/IDeviceInformationProvider;->setPidList(Ljava/lang/String;)V
+
.line 353
return-object v6
.end method
# virtual methods
.method public customDrmOnly()I
.locals 1
.prologue

@ -0,0 +1,231 @@
#!/usr/bin/env python
#
# Updated September 2013 by Anon
# Version 2.01
# Incorporated minor fixes posted at Apprentice Alf's.
#
# Updates July 2012 by Michael Newton
# PWSD ID is no longer a MAC address, but should always
# be stored in the registry. Script now works with OS X
# and checks plist for values instead of registry. Must
# have biplist installed for OS X support.
#
##########################################################
# KOBO DRM CRACK BY #
# PHYSISTICATED #
##########################################################
# This app was made for Python 2.7 on Windows 32-bit
#
# This app needs pycrypto - get from here:
# http://www.voidspace.org.uk/python/modules.shtml
#
# Usage: obok.py
# Choose the book you want to decrypt
#
# Shouts to my krew - you know who you are - and one in
# particular who gave me a lot of help with this - thank
# you so much!
#
# Kopimi /K\
# Keep sharing, keep copying, but remember that nothing is
# for free - make sure you compensate your favorite
# authors - and cut out the middle man whenever possible
# ;) ;) ;)
#
# DRM AUTOPSY
# The Kobo DRM was incredibly easy to crack, but it took
# me months to get around to making this. Here's the
# basics of how it works:
# 1: Get MAC address of first NIC in ipconfig (sometimes
# stored in registry as pwsdid)
# 2: Get user ID (stored in tons of places, this gets it
# from HKEY_CURRENT_USER\Software\Kobo\Kobo Desktop
# Edition\Browser\cookies)
# 3: Concatenate and SHA256, take the second half - this
# is your master key
# 4: Open %LOCALAPPDATA%\Kobo Desktop Editions\Kobo.sqlite
# and dump content_keys
# 5: Unbase64 the keys, then decode these with the master
# key - these are your page keys
# 6: Unzip EPUB of your choice, decrypt each page with its
# page key, then zip back up again
#
# WHY USE THIS WHEN INEPT WORKS FINE? (adobe DRM stripper)
# Inept works very well, but authors on Kobo can choose
# what DRM they want to use - and some have chosen not to
# let people download them with Adobe Digital Editions -
# they would rather lock you into a single platform.
#
# With Obok, you can sync Kobo Desktop, decrypt all your
# ebooks, and then use them on whatever device you want
# - you bought them, you own them, you can do what you
# like with them.
#
# Obok is Kobo backwards, but it is also means "next to"
# in Polish.
# When you buy a real book, it is right next to you. You
# can read it at home, at work, on a train, you can lend
# it to a friend, you can scribble on it, and add your own
# explanations/translations.
#
# Obok gives you this power over your ebooks - no longer
# are you restricted to one device. This allows you to
# embed foreign fonts into your books, as older Kobo's
# can't display them properly. You can read your books
# on your phones, in different PC readers, and different
# ereader devices. You can share them with your friends
# too, if you like - you can do that with a real book
# after all.
#
"""
Decrypt Kobo encrypted EPUB books.
"""
import os
import sys
if sys.platform.startswith('win'):
import _winreg
elif sys.platform.startswith('darwin'):
from biplist import readPlist
import re
import string
import hashlib
import sqlite3
import base64
import binascii
import zipfile
from Crypto.Cipher import AES
def SHA256(raw):
return hashlib.sha256(raw).hexdigest()
def RemoveAESPadding(contents):
lastchar = binascii.b2a_hex(contents[-1:])
strlen = int(lastchar, 16)
padding = strlen
if(strlen == 1):
return contents[:-1]
if(strlen < 16):
for i in range(strlen):
testchar = binascii.b2a_hex(contents[-strlen:-(strlen-1)])
if(testchar != lastchar):
padding = 0
if(padding > 0):
contents = contents[:-padding]
return contents
def GetVolumeKeys(dbase, enc):
volumekeys = {}
for row in dbase.execute("SELECT * from content_keys"):
if(row[0] not in volumekeys):
volumekeys[row[0]] = {}
volumekeys[row[0]][row[1]] = {}
volumekeys[row[0]][row[1]]["encryptedkey"] = base64.b64decode(row[2])
volumekeys[row[0]][row[1]]["decryptedkey"] = enc.decrypt(volumekeys[row[0]][row[1]]["encryptedkey"])
# get book name
for key in volumekeys.keys():
volumekeys[key]["title"] = dbase.execute("SELECT Title from content where ContentID = '%s'" % (key)).fetchone()[0]
return volumekeys
def ByteArrayToString(bytearr):
wincheck = re.match("@ByteArray\\((.+)\\)", bytearr)
if wincheck:
return wincheck.group(1)
return bytearr
def GetUserHexKey(prefs = ""):
"find wsuid and pwsdid"
wsuid = ""
pwsdid = ""
if sys.platform.startswith('win'):
regkey_browser = _winreg.OpenKey(_winreg.HKEY_CURRENT_USER, "Software\\Kobo\\Kobo Desktop Edition\\Browser")
cookies = _winreg.QueryValueEx(regkey_browser, "cookies")
bytearrays = cookies[0]
elif sys.platform.startswith('darwin'):
cookies = readPlist(prefs)
bytearrays = cookies["Browser.cookies"]
for bytearr in bytearrays:
cookie = ByteArrayToString(bytearr)
print cookie
wsuidcheck = re.match("^wsuid=([0-9a-f-]+)", cookie)
if(wsuidcheck):
wsuid = wsuidcheck.group(1)
pwsdidcheck = re.match("^pwsdid=([0-9a-f-]+)", cookie)
if (pwsdidcheck):
pwsdid = pwsdidcheck.group(1)
if(wsuid == "" or pwsdid == ""):
print "wsuid or pwsdid key not found :/"
exit()
preuserkey = string.join((pwsdid, wsuid), "")
print SHA256(pwsdid)
userkey = SHA256(preuserkey)
return userkey[32:]
# get dirs
if sys.platform.startswith('win'):
delim = "\\"
if (sys.getwindowsversion().major > 5):
kobodir = string.join((os.environ['LOCALAPPDATA'], "Kobo\\Kobo Desktop Edition"), delim)
else:
kobodir = string.join((os.environ['USERPROFILE'], "Local Settings\\Application Data\\Kobo\\Kobo Desktop Edition"), delim)
prefs = ""
elif sys.platform.startswith('darwin'):
delim = "/"
kobodir = string.join((os.environ['HOME'], "Library/Application Support/Kobo/Kobo Desktop Edition"), delim)
prefs = string.join((os.environ['HOME'], "Library/Preferences/com.kobo.Kobo Desktop Edition.plist"), delim)
sqlitefile = string.join((kobodir, "Kobo.sqlite"), delim)
bookdir = string.join((kobodir, "kepub"), delim)
# get key
userkeyhex = GetUserHexKey(prefs)
# load into AES
userkey = binascii.a2b_hex(userkeyhex)
enc = AES.new(userkey, AES.MODE_ECB)
# open sqlite
conn = sqlite3.connect(sqlitefile)
dbcursor = conn.cursor()
# get volume keys
volumekeys = GetVolumeKeys(dbcursor, enc)
# choose a volumeID
volumeid = ""
print "Choose a book to decrypt:"
i = 1
for key in volumekeys.keys():
print "%d: %s" % (i, volumekeys[key]["title"])
i += 1
num = input("...")
i = 1
for key in volumekeys.keys():
if(i == num):
volumeid = key
i += 1
if(volumeid == ""):
exit()
zippath = string.join((bookdir, volumeid), delim)
z = zipfile.ZipFile(zippath, "r")
# make filename out of Unicode alphanumeric and whitespace equivalents from title
outname = "%s.epub" % (re.sub("[^\s\w]", "", volumekeys[volumeid]["title"], 0, re.UNICODE))
zout = zipfile.ZipFile(outname, "w", zipfile.ZIP_DEFLATED)
for filename in z.namelist():
#print filename
# read in and decrypt
if(filename in volumekeys[volumeid]):
# do decrypted version
pagekey = volumekeys[volumeid][filename]["decryptedkey"]
penc = AES.new(pagekey, AES.MODE_ECB)
contents = RemoveAESPadding(penc.decrypt(z.read(filename)))
# need to fix padding
zout.writestr(filename, contents)
else:
zout.writestr(filename, z.read(filename))
print "Book saved as %s%s%s" % (os.getcwd(), delim, outname)
Loading…
Cancel
Save