Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
00a5c4e1d1 | ||
|
|
4ea0d81144 | ||
|
|
b1cccf4b25 | ||
|
|
fe6074949b | ||
|
|
2db7ee8894 | ||
|
|
93d8758462 | ||
|
|
f97bc078db | ||
|
|
2e96db6cdc | ||
|
|
0d530c0c46 | ||
|
|
488924d443 |
Binary file not shown.
@@ -19,7 +19,7 @@ except NameError:
|
|||||||
PLUGIN_NAME = 'Obok DeDRM'
|
PLUGIN_NAME = 'Obok DeDRM'
|
||||||
PLUGIN_SAFE_NAME = PLUGIN_NAME.strip().lower().replace(' ', '_')
|
PLUGIN_SAFE_NAME = PLUGIN_NAME.strip().lower().replace(' ', '_')
|
||||||
PLUGIN_DESCRIPTION = _('Removes DRM from Kobo kepubs and adds them to the library.')
|
PLUGIN_DESCRIPTION = _('Removes DRM from Kobo kepubs and adds them to the library.')
|
||||||
PLUGIN_VERSION_TUPLE = (3, 1, 4)
|
PLUGIN_VERSION_TUPLE = (3, 1, 6)
|
||||||
PLUGIN_VERSION = '.'.join([str(x) for x in PLUGIN_VERSION_TUPLE])
|
PLUGIN_VERSION = '.'.join([str(x) for x in PLUGIN_VERSION_TUPLE])
|
||||||
HELPFILE_NAME = PLUGIN_SAFE_NAME + '_Help.htm'
|
HELPFILE_NAME = PLUGIN_SAFE_NAME + '_Help.htm'
|
||||||
PLUGIN_AUTHORS = 'Anon'
|
PLUGIN_AUTHORS = 'Anon'
|
||||||
@@ -29,7 +29,7 @@ class ObokDeDRMAction(InterfaceActionBase):
|
|||||||
|
|
||||||
name = PLUGIN_NAME
|
name = PLUGIN_NAME
|
||||||
description = PLUGIN_DESCRIPTION
|
description = PLUGIN_DESCRIPTION
|
||||||
supported_platforms = ['windows', 'osx']
|
supported_platforms = ['windows', 'osx', 'linux' ]
|
||||||
author = PLUGIN_AUTHORS
|
author = PLUGIN_AUTHORS
|
||||||
version = PLUGIN_VERSION_TUPLE
|
version = PLUGIN_VERSION_TUPLE
|
||||||
minimum_calibre_version = (1, 0, 0)
|
minimum_calibre_version = (1, 0, 0)
|
||||||
|
|||||||
@@ -34,6 +34,15 @@ from calibre_plugins.obok_dedrm.utilities import (
|
|||||||
from calibre_plugins.obok_dedrm.obok.obok import KoboLibrary
|
from calibre_plugins.obok_dedrm.obok.obok import KoboLibrary
|
||||||
from calibre_plugins.obok_dedrm.obok.legacy_obok import legacy_obok
|
from calibre_plugins.obok_dedrm.obok.legacy_obok import legacy_obok
|
||||||
|
|
||||||
|
can_parse_xml = True
|
||||||
|
try:
|
||||||
|
from xml.etree import ElementTree as ET
|
||||||
|
debug_print("using xml.etree for xml parsing")
|
||||||
|
except ImportError:
|
||||||
|
can_parse_xml = False
|
||||||
|
debug_print("Cannot find xml.etree, disabling extraction of serial numbers")
|
||||||
|
|
||||||
|
|
||||||
PLUGIN_ICONS = ['images/obok.png']
|
PLUGIN_ICONS = ['images/obok.png']
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@@ -78,8 +87,42 @@ class InterfacePluginAction(InterfaceAction):
|
|||||||
self.current_idx = self.gui.library_view.currentIndex()
|
self.current_idx = self.gui.library_view.currentIndex()
|
||||||
|
|
||||||
print ('Running {}'.format(PLUGIN_NAME + ' v' + PLUGIN_VERSION))
|
print ('Running {}'.format(PLUGIN_NAME + ' v' + PLUGIN_VERSION))
|
||||||
|
#
|
||||||
|
# search for connected device in case serials are saved
|
||||||
|
tmpserials = cfg['kobo_serials']
|
||||||
|
device = self.parent().device_manager.connected_device
|
||||||
|
device_path = None
|
||||||
|
if (device):
|
||||||
|
device_path = device._main_prefix
|
||||||
|
debug_print("get_device_settings - device_path=", 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')
|
||||||
|
debug_print("trying to load %s" % devicexml)
|
||||||
|
if (os.path.exists(devicexml)):
|
||||||
|
debug_print("trying to parse %s" % devicexml)
|
||||||
|
xmltree = ET.parse(devicexml)
|
||||||
|
for node in xmltree.iter():
|
||||||
|
if "deviceSerial" in node.tag:
|
||||||
|
serial = node.text
|
||||||
|
debug_print ("found serial %s" % serial)
|
||||||
|
tmpserials.append(serial)
|
||||||
|
break
|
||||||
|
|
||||||
|
|
||||||
|
else:
|
||||||
|
debug_print("didn't find device")
|
||||||
|
|
||||||
# Get the Kobo Library object (obok v3.01)
|
# Get the Kobo Library object (obok v3.01)
|
||||||
self.library = KoboLibrary()
|
self.library = KoboLibrary(tmpserials, device_path)
|
||||||
|
debug_print ("got kobodir %s" % self.library.kobodir)
|
||||||
|
if (self.library.kobodir == ''):
|
||||||
|
# linux and no device connected, but could be extended
|
||||||
|
# to the case where on Windows/Mac the prog is not installed
|
||||||
|
msg = _('<p>Could not find Kobo Library\n<p>Windows/Mac: do you have Kobo Desktop installed?\n<p>Windows/Mac/Linux: In case you have an Kobo eInk device, connect the device.')
|
||||||
|
showErrorDlg(msg, None)
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
# Get a list of Kobo titles
|
# Get a list of Kobo titles
|
||||||
books = self.build_book_list()
|
books = self.build_book_list()
|
||||||
|
|||||||
@@ -3,15 +3,23 @@ from __future__ import (unicode_literals, division, absolute_import,
|
|||||||
print_function)
|
print_function)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from PyQt5.Qt import (QWidget, QLabel, QVBoxLayout, QHBoxLayout, QComboBox)
|
from PyQt5.Qt import (Qt, QGroupBox, QListWidget, QLineEdit, QDialogButtonBox, QWidget, QLabel, QDialog, QVBoxLayout, QAbstractItemView, QIcon, QHBoxLayout, QComboBox, QListWidgetItem)
|
||||||
except ImportError:
|
except ImportError:
|
||||||
from PyQt4.Qt import (QWidget, QLabel, QVBoxLayout, QHBoxLayout, QComboBox)
|
from PyQt4.Qt import (Qt, QGroupBox, QListWidget, QLineEdit, QDialogButtonBox, QWidget, QLabel, QDialog, QVBoxLayout, QAbstractItemView, QIcon, QHBoxLayout, QComboBox, QListWidgetItem)
|
||||||
|
|
||||||
|
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
|
from calibre.utils.config import JSONConfig, config_dir
|
||||||
|
|
||||||
plugin_prefs = JSONConfig('plugins/obok_dedrm_prefs')
|
plugin_prefs = JSONConfig('plugins/obok_dedrm_prefs')
|
||||||
plugin_prefs.defaults['finding_homes_for_formats'] = 'Ask'
|
plugin_prefs.defaults['finding_homes_for_formats'] = 'Ask'
|
||||||
|
plugin_prefs.defaults['kobo_serials'] = []
|
||||||
|
|
||||||
|
from calibre_plugins.obok_dedrm.__init__ import PLUGIN_NAME, PLUGIN_VERSION
|
||||||
from calibre_plugins.obok_dedrm.utilities import (debug_print)
|
from calibre_plugins.obok_dedrm.utilities import (debug_print)
|
||||||
try:
|
try:
|
||||||
debug_print("obok::config.py - loading translations")
|
debug_print("obok::config.py - loading translations")
|
||||||
@@ -26,7 +34,10 @@ class ConfigWidget(QWidget):
|
|||||||
self.plugin_action = plugin_action
|
self.plugin_action = plugin_action
|
||||||
layout = QVBoxLayout(self)
|
layout = QVBoxLayout(self)
|
||||||
self.setLayout(layout)
|
self.setLayout(layout)
|
||||||
|
|
||||||
|
# copy of preferences
|
||||||
|
self.tmpserials = plugin_prefs['kobo_serials']
|
||||||
|
|
||||||
combo_label = QLabel(_('When should Obok try to insert EPUBs into existing calibre entries?'), self)
|
combo_label = QLabel(_('When should Obok try to insert EPUBs into existing calibre entries?'), self)
|
||||||
layout.addWidget(combo_label)
|
layout.addWidget(combo_label)
|
||||||
self.find_homes = QComboBox()
|
self.find_homes = QComboBox()
|
||||||
@@ -35,6 +46,178 @@ class ConfigWidget(QWidget):
|
|||||||
self.find_homes.addItems([_('Ask'), _('Always'), _('Never')])
|
self.find_homes.addItems([_('Ask'), _('Always'), _('Never')])
|
||||||
index = self.find_homes.findText(plugin_prefs['finding_homes_for_formats'])
|
index = self.find_homes.findText(plugin_prefs['finding_homes_for_formats'])
|
||||||
self.find_homes.setCurrentIndex(index)
|
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.clicked.connect(self.edit_serials)
|
||||||
|
layout.addWidget(self.serials_button)
|
||||||
|
|
||||||
|
|
||||||
|
def edit_serials(self):
|
||||||
|
d = ManageKeysDialog(self,u"Kobo device serial numbers",self.tmpserials, AddSerialDialog)
|
||||||
|
d.exec_()
|
||||||
|
|
||||||
|
|
||||||
def save_settings(self):
|
def save_settings(self):
|
||||||
plugin_prefs['finding_homes_for_formats'] = unicode(self.find_homes.currentText())
|
plugin_prefs['finding_homes_for_formats'] = unicode(self.find_homes.currentText())
|
||||||
|
plugin_prefs['kobo_serials'] = self.tmpserials
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class ManageKeysDialog(QDialog):
|
||||||
|
def __init__(self, parent, key_type_name, plugin_keys, create_key, keyfile_ext = u""):
|
||||||
|
QDialog.__init__(self,parent)
|
||||||
|
self.parent = parent
|
||||||
|
self.key_type_name = key_type_name
|
||||||
|
self.plugin_keys = plugin_keys
|
||||||
|
self.create_key = create_key
|
||||||
|
self.keyfile_ext = keyfile_ext
|
||||||
|
self.json_file = (keyfile_ext == u"k4i")
|
||||||
|
|
||||||
|
self.setWindowTitle("{0} {1}: Manage {2}s".format(PLUGIN_NAME, PLUGIN_VERSION, self.key_type_name))
|
||||||
|
|
||||||
|
# Start Qt Gui dialog layout
|
||||||
|
layout = QVBoxLayout(self)
|
||||||
|
self.setLayout(layout)
|
||||||
|
|
||||||
|
keys_group_box = QGroupBox(_(u"{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.setSelectionMode(QAbstractItemView.SingleSelection)
|
||||||
|
self.populate_list()
|
||||||
|
keys_group_box_layout.addWidget(self.listy)
|
||||||
|
|
||||||
|
button_layout = QVBoxLayout()
|
||||||
|
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.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.setIcon(QIcon(I('list_remove.png')))
|
||||||
|
self._delete_key_button.clicked.connect(self.delete_key)
|
||||||
|
button_layout.addWidget(self._delete_key_button)
|
||||||
|
|
||||||
|
spacerItem = QtGui.QSpacerItem(20, 40, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding)
|
||||||
|
button_layout.addItem(spacerItem)
|
||||||
|
|
||||||
|
layout.addSpacing(5)
|
||||||
|
migrate_layout = QHBoxLayout()
|
||||||
|
layout.addLayout(migrate_layout)
|
||||||
|
migrate_layout.addStretch()
|
||||||
|
self.button_box = QDialogButtonBox(QDialogButtonBox.Close)
|
||||||
|
self.button_box.rejected.connect(self.close)
|
||||||
|
migrate_layout.addWidget(self.button_box)
|
||||||
|
|
||||||
|
self.resize(self.sizeHint())
|
||||||
|
|
||||||
|
def populate_list(self):
|
||||||
|
if type(self.plugin_keys) == dict:
|
||||||
|
for key in self.plugin_keys.keys():
|
||||||
|
self.listy.addItem(QListWidgetItem(key))
|
||||||
|
else:
|
||||||
|
for key in self.plugin_keys:
|
||||||
|
self.listy.addItem(QListWidgetItem(key))
|
||||||
|
|
||||||
|
def add_key(self):
|
||||||
|
d = self.create_key(self)
|
||||||
|
d.exec_()
|
||||||
|
|
||||||
|
if d.result() != d.Accepted:
|
||||||
|
# New key generation cancelled.
|
||||||
|
return
|
||||||
|
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)
|
||||||
|
return
|
||||||
|
|
||||||
|
self.plugin_keys.append(d.key_value)
|
||||||
|
self.listy.clear()
|
||||||
|
self.populate_list()
|
||||||
|
|
||||||
|
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)
|
||||||
|
r = error_dialog(None, "{0} {1}".format(PLUGIN_NAME, PLUGIN_VERSION),
|
||||||
|
_(errmsg), show=True, show_copy_button=False)
|
||||||
|
return
|
||||||
|
|
||||||
|
d = RenameKeyDialog(self)
|
||||||
|
d.exec_()
|
||||||
|
|
||||||
|
if d.result() != d.Accepted:
|
||||||
|
# rename cancelled or moot.
|
||||||
|
return
|
||||||
|
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]
|
||||||
|
del self.plugin_keys[keyname]
|
||||||
|
|
||||||
|
self.listy.clear()
|
||||||
|
self.populate_list()
|
||||||
|
|
||||||
|
def delete_key(self):
|
||||||
|
if not self.listy.currentItem():
|
||||||
|
return
|
||||||
|
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
|
||||||
|
self.plugin_keys.remove(keyname)
|
||||||
|
|
||||||
|
self.listy.clear()
|
||||||
|
self.populate_list()
|
||||||
|
|
||||||
|
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))
|
||||||
|
layout = QVBoxLayout(self)
|
||||||
|
self.setLayout(layout)
|
||||||
|
|
||||||
|
data_group_box = QGroupBox(u"", self)
|
||||||
|
layout.addWidget(data_group_box)
|
||||||
|
data_group_box_layout = QVBoxLayout()
|
||||||
|
data_group_box.setLayout(data_group_box_layout)
|
||||||
|
|
||||||
|
key_group = QHBoxLayout()
|
||||||
|
data_group_box_layout.addLayout(key_group)
|
||||||
|
key_group.addWidget(QLabel(u"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.")
|
||||||
|
key_group.addWidget(self.key_ledit)
|
||||||
|
|
||||||
|
self.button_box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
|
||||||
|
self.button_box.accepted.connect(self.accept)
|
||||||
|
self.button_box.rejected.connect(self.reject)
|
||||||
|
layout.addWidget(self.button_box)
|
||||||
|
|
||||||
|
self.resize(self.sizeHint())
|
||||||
|
|
||||||
|
@property
|
||||||
|
def key_name(self):
|
||||||
|
return unicode(self.key_ledit.text()).strip()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def key_value(self):
|
||||||
|
return unicode(self.key_ledit.text()).strip()
|
||||||
|
|
||||||
|
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."
|
||||||
|
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))
|
||||||
|
return error_dialog(None, "{0} {1}".format(PLUGIN_NAME, PLUGIN_VERSION), errmsg, show=True, show_copy_button=False)
|
||||||
|
QDialog.accept(self)
|
||||||
|
|||||||
@@ -1,6 +1,14 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
# Version 3.1.6 September 2015
|
||||||
|
# Enable support for Kobo devices
|
||||||
|
# More character encoding fixes (unicode strings)
|
||||||
|
#
|
||||||
|
# Version 3.1.5 September 2015
|
||||||
|
# Removed requirement that a purchase has been made.
|
||||||
|
# Also add in character encoding fixes
|
||||||
|
#
|
||||||
# Version 3.1.4 September 2015
|
# Version 3.1.4 September 2015
|
||||||
# Updated for version 3.17 of the Windows Desktop app.
|
# Updated for version 3.17 of the Windows Desktop app.
|
||||||
#
|
#
|
||||||
@@ -115,7 +123,7 @@
|
|||||||
#
|
#
|
||||||
"""Manage all Kobo books, either encrypted or DRM-free."""
|
"""Manage all Kobo books, either encrypted or DRM-free."""
|
||||||
|
|
||||||
__version__ = '3.1.3'
|
__version__ = '3.1.6'
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
import os
|
import os
|
||||||
@@ -215,6 +223,24 @@ def _load_crypto():
|
|||||||
|
|
||||||
AES = _load_crypto()
|
AES = _load_crypto()
|
||||||
|
|
||||||
|
# Wrap a stream so that output gets flushed immediately
|
||||||
|
# and also make sure that any unicode strings get
|
||||||
|
# encoded using "replace" before writing them.
|
||||||
|
class SafeUnbuffered:
|
||||||
|
def __init__(self, stream):
|
||||||
|
self.stream = stream
|
||||||
|
self.encoding = stream.encoding
|
||||||
|
if self.encoding == None:
|
||||||
|
self.encoding = "utf-8"
|
||||||
|
def write(self, data):
|
||||||
|
if isinstance(data,unicode):
|
||||||
|
data = data.encode(self.encoding,"replace")
|
||||||
|
self.stream.write(data)
|
||||||
|
self.stream.flush()
|
||||||
|
def __getattr__(self, attr):
|
||||||
|
return getattr(self.stream, attr)
|
||||||
|
|
||||||
|
|
||||||
class KoboLibrary(object):
|
class KoboLibrary(object):
|
||||||
"""The Kobo library.
|
"""The Kobo library.
|
||||||
|
|
||||||
@@ -222,23 +248,49 @@ class KoboLibrary(object):
|
|||||||
written by the Kobo Desktop Edition application, including the list
|
written by the Kobo Desktop Edition application, including the list
|
||||||
of books, their titles, and the user's encryption key(s)."""
|
of books, their titles, and the user's encryption key(s)."""
|
||||||
|
|
||||||
def __init__ (self):
|
def __init__ (self, serials = [], device_path = None):
|
||||||
print u"Obok v{0}\nCopyright © 2012-2015 Physisticated et al.".format(__version__)
|
print u"Obok v{0}\nCopyright © 2012-2015 Physisticated et al.".format(__version__)
|
||||||
if sys.platform.startswith('win'):
|
self.kobodir = u""
|
||||||
if sys.getwindowsversion().major > 5:
|
kobodb = u""
|
||||||
self.kobodir = os.environ['LOCALAPPDATA']
|
|
||||||
else:
|
# - first check whether serials have been found or are provided
|
||||||
self.kobodir = os.path.join(os.environ['USERPROFILE'], 'Local Settings', 'Application Data')
|
# and a device is connected. In this case, use the device
|
||||||
self.kobodir = os.path.join(self.kobodir, 'Kobo', 'Kobo Desktop Edition')
|
# - otherwise fall back to Kobo Desktop Application for Windows and Mac
|
||||||
elif sys.platform.startswith('darwin'):
|
if (device_path and (len(serials) > 0)):
|
||||||
self.kobodir = os.path.join(os.environ['HOME'], 'Library', 'Application Support', 'Kobo', 'Kobo Desktop Edition')
|
self.kobodir = os.path.join(device_path, u".kobo")
|
||||||
self.bookdir = os.path.join(self.kobodir, 'kepub')
|
# devices use KoboReader.sqlite
|
||||||
kobodb = os.path.join(self.kobodir, 'Kobo.sqlite')
|
kobodb = os.path.join(self.kobodir, u"KoboReader.sqlite")
|
||||||
self.__sqlite = sqlite3.connect(kobodb)
|
if (not(os.path.exists(kobodb))):
|
||||||
self.__cursor = self.__sqlite.cursor()
|
# give up here, we haven't found anything useful
|
||||||
self._userkeys = []
|
self.kobodir = u""
|
||||||
self._books = []
|
kobodb = u""
|
||||||
self._volumeID = []
|
|
||||||
|
if (self.kobodir == u""):
|
||||||
|
# we haven't found a device with serials, so try desktop apps
|
||||||
|
if sys.platform.startswith('win'):
|
||||||
|
import _winreg as winreg
|
||||||
|
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%")
|
||||||
|
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")
|
||||||
|
elif sys.platform.startswith('darwin'):
|
||||||
|
self.kobodir = os.path.join(os.environ['HOME'], u"Library", u"Application Support", u"Kobo", u"Kobo Desktop Edition")
|
||||||
|
# desktop versions use Kobo.sqlite
|
||||||
|
kobodb = os.path.join(self.kobodir, u"Kobo.sqlite")
|
||||||
|
|
||||||
|
if (self.kobodir != u""):
|
||||||
|
self.bookdir = os.path.join(self.kobodir, u"kepub")
|
||||||
|
self.__sqlite = sqlite3.connect(kobodb)
|
||||||
|
self.__cursor = self.__sqlite.cursor()
|
||||||
|
self._userkeys = []
|
||||||
|
self._books = []
|
||||||
|
self._volumeID = []
|
||||||
|
self._serials = serials
|
||||||
|
|
||||||
def close (self):
|
def close (self):
|
||||||
"""Closes the database used by the library."""
|
"""Closes the database used by the library."""
|
||||||
@@ -279,7 +331,7 @@ class KoboLibrary(object):
|
|||||||
|
|
||||||
def __bookfile (self, volumeid):
|
def __bookfile (self, volumeid):
|
||||||
"""The filename needed to open a given book."""
|
"""The filename needed to open a given book."""
|
||||||
return os.path.join(self.kobodir, 'kepub', volumeid)
|
return os.path.join(self.kobodir, u"kepub", volumeid)
|
||||||
|
|
||||||
def __getmacaddrs (self):
|
def __getmacaddrs (self):
|
||||||
"""The list of all MAC addresses on this machine."""
|
"""The list of all MAC addresses on this machine."""
|
||||||
@@ -295,13 +347,18 @@ class KoboLibrary(object):
|
|||||||
output = subprocess.check_output('/sbin/ifconfig -a', shell=True)
|
output = subprocess.check_output('/sbin/ifconfig -a', shell=True)
|
||||||
matches = c.findall(output)
|
matches = c.findall(output)
|
||||||
for m in matches:
|
for m in matches:
|
||||||
# print "m:",m[0]
|
# print u"m:{0}".format(m[0])
|
||||||
macaddrs.append(m[0].upper())
|
macaddrs.append(m[0].upper())
|
||||||
|
|
||||||
|
# extend the list of macaddrs in any case with the serials
|
||||||
|
# cannot hurt ;-)
|
||||||
|
macaddrs.extend(self._serials)
|
||||||
|
|
||||||
return macaddrs
|
return macaddrs
|
||||||
|
|
||||||
def __getuserids (self):
|
def __getuserids (self):
|
||||||
userids = []
|
userids = []
|
||||||
cursor = self.__cursor.execute('SELECT UserID FROM user WHERE HasMadePurchase = "true"')
|
cursor = self.__cursor.execute('SELECT UserID FROM user')
|
||||||
row = cursor.fetchone()
|
row = cursor.fetchone()
|
||||||
while row is not None:
|
while row is not None:
|
||||||
try:
|
try:
|
||||||
@@ -432,13 +489,13 @@ class KoboFile(object):
|
|||||||
if contents[:5]=="<?xml":
|
if contents[:5]=="<?xml":
|
||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
print "Bad XML: ",contents[:5]
|
print u"Bad XML: {0}".format(contents[:5])
|
||||||
raise ValueError
|
raise ValueError
|
||||||
if self.mimetype == 'image/jpeg':
|
if self.mimetype == 'image/jpeg':
|
||||||
if contents[:3] == '\xff\xd8\xff':
|
if contents[:3] == '\xff\xd8\xff':
|
||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
print "Bad JPEG: ", contents[:3].encode('hex')
|
print u"Bad JPEG: {0}".format(contents[:3].encode('hex'))
|
||||||
raise ValueError()
|
raise ValueError()
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@@ -460,36 +517,34 @@ class KoboFile(object):
|
|||||||
contents = contents[:-padding]
|
contents = contents[:-padding]
|
||||||
return contents
|
return contents
|
||||||
|
|
||||||
if __name__ == '__main__':
|
def cli_main():
|
||||||
|
|
||||||
lib = KoboLibrary()
|
lib = KoboLibrary()
|
||||||
|
|
||||||
for i, book in enumerate(lib.books):
|
for i, book in enumerate(lib.books):
|
||||||
print ('%d: %s' % (i + 1, book.title)).encode('ascii', 'ignore')
|
print u"{0}: {1}".format(i + 1, book.title)
|
||||||
|
|
||||||
num_string = raw_input("Convert book number... ")
|
num_string = raw_input(u"Convert book number... ")
|
||||||
try:
|
try:
|
||||||
num = int(num_string)
|
num = int(num_string)
|
||||||
book = lib.books[num - 1]
|
book = lib.books[num - 1]
|
||||||
except (ValueError, IndexError):
|
except (ValueError, IndexError):
|
||||||
exit()
|
exit()
|
||||||
|
|
||||||
print "Converting", book.title
|
print u"Converting {0}".format(book.title)
|
||||||
|
|
||||||
zin = zipfile.ZipFile(book.filename, "r")
|
zin = zipfile.ZipFile(book.filename, "r")
|
||||||
# make filename out of Unicode alphanumeric and whitespace equivalents from title
|
# make filename out of Unicode alphanumeric and whitespace equivalents from title
|
||||||
outname = "%s.epub" % (re.sub('[^\s\w]', '', book.title, 0, re.UNICODE))
|
outname = u"{0}.epub".format(re.sub('[^\s\w]', '_', book.title, 0, re.UNICODE))
|
||||||
|
|
||||||
if (book.type == 'drm-free'):
|
if (book.type == 'drm-free'):
|
||||||
print "DRM-free book, conversion is not needed"
|
print u"DRM-free book, conversion is not needed"
|
||||||
shutil.copyfile(book.filename, outname)
|
shutil.copyfile(book.filename, outname)
|
||||||
print "Book saved as", os.path.join(os.getcwd(), outname)
|
print u"Book saved as {0}".format(os.path.join(os.getcwd(), outname))
|
||||||
exit(0)
|
exit(0)
|
||||||
|
|
||||||
result = 1
|
result = 1
|
||||||
for userkey in lib.userkeys:
|
for userkey in lib.userkeys:
|
||||||
# print "Trying key: ",userkey.encode('hex_codec')
|
print u"Trying key: {0}".format(userkey.encode('hex_codec'))
|
||||||
confirmedGood = False
|
|
||||||
try:
|
try:
|
||||||
zout = zipfile.ZipFile(outname, "w", zipfile.ZIP_DEFLATED)
|
zout = zipfile.ZipFile(outname, "w", zipfile.ZIP_DEFLATED)
|
||||||
for filename in zin.namelist():
|
for filename in zin.namelist():
|
||||||
@@ -498,18 +553,25 @@ if __name__ == '__main__':
|
|||||||
file = book.encryptedfiles[filename]
|
file = book.encryptedfiles[filename]
|
||||||
contents = file.decrypt(userkey, contents)
|
contents = file.decrypt(userkey, contents)
|
||||||
# Parse failures mean the key is probably wrong.
|
# Parse failures mean the key is probably wrong.
|
||||||
if not confirmedGood:
|
file.check(contents)
|
||||||
confirmedGood = file.check(contents)
|
|
||||||
zout.writestr(filename, contents)
|
zout.writestr(filename, contents)
|
||||||
zout.close()
|
zout.close()
|
||||||
print "Book saved as", os.path.join(os.getcwd(), outname)
|
print u"Decryption succeeded."
|
||||||
|
print u"Book saved as {0}".format(os.path.join(os.getcwd(), outname))
|
||||||
result = 0
|
result = 0
|
||||||
break
|
break
|
||||||
except ValueError:
|
except ValueError:
|
||||||
print "Decryption failed, trying next key"
|
print u"Decryption failed."
|
||||||
zout.close()
|
zout.close()
|
||||||
os.remove(outname)
|
os.remove(outname)
|
||||||
|
|
||||||
zin.close()
|
zin.close()
|
||||||
lib.close()
|
lib.close()
|
||||||
exit(result)
|
if result != 0:
|
||||||
|
print u"Could not decrypt book with any of the keys found."
|
||||||
|
return result
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
sys.stdout=SafeUnbuffered(sys.stdout)
|
||||||
|
sys.stderr=SafeUnbuffered(sys.stderr)
|
||||||
|
sys.exit(cli_main())
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
obok_plugin.zip
|
obok_plugin.zip
|
||||||
================
|
================
|
||||||
|
|
||||||
This plugin will remove the DRM from Kobo ebooks download on Mac or Windows using the Kobo desktop application.
|
This plugin will remove the DRM from Kobo ebooks download on Mac or Windows using the Kobo desktop application, or from Kobo ebooks on an attached Kobo reader. If both are available, ebooks will be read from the attached Kobo reader. To import from the desktop application, unplug the Kobo reader.
|
||||||
|
|
||||||
|
|
||||||
Installation
|
Installation
|
||||||
@@ -11,7 +11,7 @@ Do NOT select "Get plugins to enhance calibre" as this is reserved for 'official
|
|||||||
|
|
||||||
Customization
|
Customization
|
||||||
-------------
|
-------------
|
||||||
No customization is required, except choosing which menus will show the plugin.
|
No customization is required, except choosing which menus will show the plugin. Altough the ability to enter a device serial number is given, this should not need to be filled in, as the serial number should be picked up automatically from the attached Kobo reader.
|
||||||
|
|
||||||
|
|
||||||
Using the plugin
|
Using the plugin
|
||||||
@@ -47,6 +47,7 @@ Credits
|
|||||||
-------
|
-------
|
||||||
The original obok script was by Physisticated
|
The original obok script was by Physisticated
|
||||||
The plugin conversion was done anonymously.
|
The plugin conversion was done anonymously.
|
||||||
|
The Kobo reader support was added by norbusan
|
||||||
|
|
||||||
Improvements to the script and the plugin adaption have been by numerous people since.
|
Additional improvements to the script and the plugin adaption by numerous anonymous people.
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,14 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
# Version 3.1.6 September 2015
|
||||||
|
# Enable support for Kobo devices
|
||||||
|
# More character encoding fixes (unicode strings)
|
||||||
|
#
|
||||||
|
# Version 3.1.5 September 2015
|
||||||
|
# Removed requirement that a purchase has been made.
|
||||||
|
# Also add in character encoding fixes
|
||||||
|
#
|
||||||
# Version 3.1.4 September 2015
|
# Version 3.1.4 September 2015
|
||||||
# Updated for version 3.17 of the Windows Desktop app.
|
# Updated for version 3.17 of the Windows Desktop app.
|
||||||
#
|
#
|
||||||
@@ -115,7 +123,7 @@
|
|||||||
#
|
#
|
||||||
"""Manage all Kobo books, either encrypted or DRM-free."""
|
"""Manage all Kobo books, either encrypted or DRM-free."""
|
||||||
|
|
||||||
__version__ = '3.1.3'
|
__version__ = '3.1.6'
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
import os
|
import os
|
||||||
@@ -215,6 +223,24 @@ def _load_crypto():
|
|||||||
|
|
||||||
AES = _load_crypto()
|
AES = _load_crypto()
|
||||||
|
|
||||||
|
# Wrap a stream so that output gets flushed immediately
|
||||||
|
# and also make sure that any unicode strings get
|
||||||
|
# encoded using "replace" before writing them.
|
||||||
|
class SafeUnbuffered:
|
||||||
|
def __init__(self, stream):
|
||||||
|
self.stream = stream
|
||||||
|
self.encoding = stream.encoding
|
||||||
|
if self.encoding == None:
|
||||||
|
self.encoding = "utf-8"
|
||||||
|
def write(self, data):
|
||||||
|
if isinstance(data,unicode):
|
||||||
|
data = data.encode(self.encoding,"replace")
|
||||||
|
self.stream.write(data)
|
||||||
|
self.stream.flush()
|
||||||
|
def __getattr__(self, attr):
|
||||||
|
return getattr(self.stream, attr)
|
||||||
|
|
||||||
|
|
||||||
class KoboLibrary(object):
|
class KoboLibrary(object):
|
||||||
"""The Kobo library.
|
"""The Kobo library.
|
||||||
|
|
||||||
@@ -222,23 +248,49 @@ class KoboLibrary(object):
|
|||||||
written by the Kobo Desktop Edition application, including the list
|
written by the Kobo Desktop Edition application, including the list
|
||||||
of books, their titles, and the user's encryption key(s)."""
|
of books, their titles, and the user's encryption key(s)."""
|
||||||
|
|
||||||
def __init__ (self):
|
def __init__ (self, serials = [], device_path = None):
|
||||||
print u"Obok v{0}\nCopyright © 2012-2015 Physisticated et al.".format(__version__)
|
print u"Obok v{0}\nCopyright © 2012-2015 Physisticated et al.".format(__version__)
|
||||||
if sys.platform.startswith('win'):
|
self.kobodir = u""
|
||||||
if sys.getwindowsversion().major > 5:
|
kobodb = u""
|
||||||
self.kobodir = os.environ['LOCALAPPDATA']
|
|
||||||
else:
|
# - first check whether serials have been found or are provided
|
||||||
self.kobodir = os.path.join(os.environ['USERPROFILE'], 'Local Settings', 'Application Data')
|
# and a device is connected. In this case, use the device
|
||||||
self.kobodir = os.path.join(self.kobodir, 'Kobo', 'Kobo Desktop Edition')
|
# - otherwise fall back to Kobo Desktop Application for Windows and Mac
|
||||||
elif sys.platform.startswith('darwin'):
|
if (device_path and (len(serials) > 0)):
|
||||||
self.kobodir = os.path.join(os.environ['HOME'], 'Library', 'Application Support', 'Kobo', 'Kobo Desktop Edition')
|
self.kobodir = os.path.join(device_path, u".kobo")
|
||||||
self.bookdir = os.path.join(self.kobodir, 'kepub')
|
# devices use KoboReader.sqlite
|
||||||
kobodb = os.path.join(self.kobodir, 'Kobo.sqlite')
|
kobodb = os.path.join(self.kobodir, u"KoboReader.sqlite")
|
||||||
self.__sqlite = sqlite3.connect(kobodb)
|
if (not(os.path.exists(kobodb))):
|
||||||
self.__cursor = self.__sqlite.cursor()
|
# give up here, we haven't found anything useful
|
||||||
self._userkeys = []
|
self.kobodir = u""
|
||||||
self._books = []
|
kobodb = u""
|
||||||
self._volumeID = []
|
|
||||||
|
if (self.kobodir == u""):
|
||||||
|
# we haven't found a device with serials, so try desktop apps
|
||||||
|
if sys.platform.startswith('win'):
|
||||||
|
import _winreg as winreg
|
||||||
|
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%")
|
||||||
|
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")
|
||||||
|
elif sys.platform.startswith('darwin'):
|
||||||
|
self.kobodir = os.path.join(os.environ['HOME'], u"Library", u"Application Support", u"Kobo", u"Kobo Desktop Edition")
|
||||||
|
# desktop versions use Kobo.sqlite
|
||||||
|
kobodb = os.path.join(self.kobodir, u"Kobo.sqlite")
|
||||||
|
|
||||||
|
if (self.kobodir != u""):
|
||||||
|
self.bookdir = os.path.join(self.kobodir, u"kepub")
|
||||||
|
self.__sqlite = sqlite3.connect(kobodb)
|
||||||
|
self.__cursor = self.__sqlite.cursor()
|
||||||
|
self._userkeys = []
|
||||||
|
self._books = []
|
||||||
|
self._volumeID = []
|
||||||
|
self._serials = serials
|
||||||
|
|
||||||
def close (self):
|
def close (self):
|
||||||
"""Closes the database used by the library."""
|
"""Closes the database used by the library."""
|
||||||
@@ -279,7 +331,7 @@ class KoboLibrary(object):
|
|||||||
|
|
||||||
def __bookfile (self, volumeid):
|
def __bookfile (self, volumeid):
|
||||||
"""The filename needed to open a given book."""
|
"""The filename needed to open a given book."""
|
||||||
return os.path.join(self.kobodir, 'kepub', volumeid)
|
return os.path.join(self.kobodir, u"kepub", volumeid)
|
||||||
|
|
||||||
def __getmacaddrs (self):
|
def __getmacaddrs (self):
|
||||||
"""The list of all MAC addresses on this machine."""
|
"""The list of all MAC addresses on this machine."""
|
||||||
@@ -295,13 +347,18 @@ class KoboLibrary(object):
|
|||||||
output = subprocess.check_output('/sbin/ifconfig -a', shell=True)
|
output = subprocess.check_output('/sbin/ifconfig -a', shell=True)
|
||||||
matches = c.findall(output)
|
matches = c.findall(output)
|
||||||
for m in matches:
|
for m in matches:
|
||||||
# print "m:",m[0]
|
# print u"m:{0}".format(m[0])
|
||||||
macaddrs.append(m[0].upper())
|
macaddrs.append(m[0].upper())
|
||||||
|
|
||||||
|
# extend the list of macaddrs in any case with the serials
|
||||||
|
# cannot hurt ;-)
|
||||||
|
macaddrs.extend(self._serials)
|
||||||
|
|
||||||
return macaddrs
|
return macaddrs
|
||||||
|
|
||||||
def __getuserids (self):
|
def __getuserids (self):
|
||||||
userids = []
|
userids = []
|
||||||
cursor = self.__cursor.execute('SELECT UserID FROM user WHERE HasMadePurchase = "true"')
|
cursor = self.__cursor.execute('SELECT UserID FROM user')
|
||||||
row = cursor.fetchone()
|
row = cursor.fetchone()
|
||||||
while row is not None:
|
while row is not None:
|
||||||
try:
|
try:
|
||||||
@@ -432,13 +489,13 @@ class KoboFile(object):
|
|||||||
if contents[:5]=="<?xml":
|
if contents[:5]=="<?xml":
|
||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
print "Bad XML: ",contents[:5]
|
print u"Bad XML: {0}".format(contents[:5])
|
||||||
raise ValueError
|
raise ValueError
|
||||||
if self.mimetype == 'image/jpeg':
|
if self.mimetype == 'image/jpeg':
|
||||||
if contents[:3] == '\xff\xd8\xff':
|
if contents[:3] == '\xff\xd8\xff':
|
||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
print "Bad JPEG: ", contents[:3].encode('hex')
|
print u"Bad JPEG: {0}".format(contents[:3].encode('hex'))
|
||||||
raise ValueError()
|
raise ValueError()
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@@ -460,36 +517,34 @@ class KoboFile(object):
|
|||||||
contents = contents[:-padding]
|
contents = contents[:-padding]
|
||||||
return contents
|
return contents
|
||||||
|
|
||||||
if __name__ == '__main__':
|
def cli_main():
|
||||||
|
|
||||||
lib = KoboLibrary()
|
lib = KoboLibrary()
|
||||||
|
|
||||||
for i, book in enumerate(lib.books):
|
for i, book in enumerate(lib.books):
|
||||||
print ('%d: %s' % (i + 1, book.title)).encode('ascii', 'ignore')
|
print u"{0}: {1}".format(i + 1, book.title)
|
||||||
|
|
||||||
num_string = raw_input("Convert book number... ")
|
num_string = raw_input(u"Convert book number... ")
|
||||||
try:
|
try:
|
||||||
num = int(num_string)
|
num = int(num_string)
|
||||||
book = lib.books[num - 1]
|
book = lib.books[num - 1]
|
||||||
except (ValueError, IndexError):
|
except (ValueError, IndexError):
|
||||||
exit()
|
exit()
|
||||||
|
|
||||||
print "Converting", book.title
|
print u"Converting {0}".format(book.title)
|
||||||
|
|
||||||
zin = zipfile.ZipFile(book.filename, "r")
|
zin = zipfile.ZipFile(book.filename, "r")
|
||||||
# make filename out of Unicode alphanumeric and whitespace equivalents from title
|
# make filename out of Unicode alphanumeric and whitespace equivalents from title
|
||||||
outname = "%s.epub" % (re.sub('[^\s\w]', '', book.title, 0, re.UNICODE))
|
outname = u"{0}.epub".format(re.sub('[^\s\w]', '_', book.title, 0, re.UNICODE))
|
||||||
|
|
||||||
if (book.type == 'drm-free'):
|
if (book.type == 'drm-free'):
|
||||||
print "DRM-free book, conversion is not needed"
|
print u"DRM-free book, conversion is not needed"
|
||||||
shutil.copyfile(book.filename, outname)
|
shutil.copyfile(book.filename, outname)
|
||||||
print "Book saved as", os.path.join(os.getcwd(), outname)
|
print u"Book saved as {0}".format(os.path.join(os.getcwd(), outname))
|
||||||
exit(0)
|
exit(0)
|
||||||
|
|
||||||
result = 1
|
result = 1
|
||||||
for userkey in lib.userkeys:
|
for userkey in lib.userkeys:
|
||||||
# print "Trying key: ",userkey.encode('hex_codec')
|
print u"Trying key: {0}".format(userkey.encode('hex_codec'))
|
||||||
confirmedGood = False
|
|
||||||
try:
|
try:
|
||||||
zout = zipfile.ZipFile(outname, "w", zipfile.ZIP_DEFLATED)
|
zout = zipfile.ZipFile(outname, "w", zipfile.ZIP_DEFLATED)
|
||||||
for filename in zin.namelist():
|
for filename in zin.namelist():
|
||||||
@@ -498,18 +553,25 @@ if __name__ == '__main__':
|
|||||||
file = book.encryptedfiles[filename]
|
file = book.encryptedfiles[filename]
|
||||||
contents = file.decrypt(userkey, contents)
|
contents = file.decrypt(userkey, contents)
|
||||||
# Parse failures mean the key is probably wrong.
|
# Parse failures mean the key is probably wrong.
|
||||||
if not confirmedGood:
|
file.check(contents)
|
||||||
confirmedGood = file.check(contents)
|
|
||||||
zout.writestr(filename, contents)
|
zout.writestr(filename, contents)
|
||||||
zout.close()
|
zout.close()
|
||||||
print "Book saved as", os.path.join(os.getcwd(), outname)
|
print u"Decryption succeeded."
|
||||||
|
print u"Book saved as {0}".format(os.path.join(os.getcwd(), outname))
|
||||||
result = 0
|
result = 0
|
||||||
break
|
break
|
||||||
except ValueError:
|
except ValueError:
|
||||||
print "Decryption failed, trying next key"
|
print u"Decryption failed."
|
||||||
zout.close()
|
zout.close()
|
||||||
os.remove(outname)
|
os.remove(outname)
|
||||||
|
|
||||||
zin.close()
|
zin.close()
|
||||||
lib.close()
|
lib.close()
|
||||||
exit(result)
|
if result != 0:
|
||||||
|
print u"Could not decrypt book with any of the keys found."
|
||||||
|
return result
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
sys.stdout=SafeUnbuffered(sys.stdout)
|
||||||
|
sys.stderr=SafeUnbuffered(sys.stderr)
|
||||||
|
sys.exit(cli_main())
|
||||||
|
|||||||
Reference in New Issue
Block a user