summaryrefslogtreecommitdiffstats
path: root/Obok_plugin
diff options
context:
space:
mode:
authorApprentice Harper <[email protected]>2020-09-26 21:22:47 +0100
committerApprentice Harper <[email protected]>2020-09-26 21:22:47 +0100
commitafa4ac571664368dc27512768a67656ea7dd645a (patch)
tree57ca18d568ce426ce3bfa27b45dff60efa803604 /Obok_plugin
parent4868a7460e39910ad22d0949d26b3250656dea46 (diff)
Starting on Version 7.0 using the work done by others. Completely untested. I will be testing things, but I thought I'd get this base version up for others to give pull requests.
THIS IS ON THE MASTER BRANCH. The Master branch will be Python 3.0 from now on. While Python 2.7 support will not be deliberately broken, all efforts should now focus on Python 3.0 compatibility. I can see a lot of work has been done. There's more to do. I've bumped the version number of everything I came across to the next major number for Python 3.0 compatibility indication. Thanks everyone. I hope to update here at least once a week until we have a stable 7.0 release for calibre 5.0
Diffstat (limited to 'Obok_plugin')
-rw-r--r--Obok_plugin/action.py37
-rw-r--r--Obok_plugin/common_utils.py4
-rw-r--r--Obok_plugin/config.py8
-rw-r--r--Obok_plugin/obok/obok.py55
-rw-r--r--Obok_plugin/utilities.py15
5 files changed, 63 insertions, 56 deletions
diff --git a/Obok_plugin/action.py b/Obok_plugin/action.py
index a0b63a6..802a833 100644
--- a/Obok_plugin/action.py
+++ b/Obok_plugin/action.py
@@ -6,13 +6,14 @@ __license__ = 'GPL v3'
__docformat__ = 'restructuredtext en'
+import codecs
import os, traceback, zipfile
try:
from PyQt5.Qt import QToolButton, QUrl
except ImportError:
from PyQt4.Qt import QToolButton, QUrl
-
+
from calibre.gui2 import open_url, question_dialog
from calibre.gui2.actions import InterfaceAction
from calibre.utils.config import config_dir
@@ -24,7 +25,7 @@ from calibre.ebooks.metadata.meta import get_metadata
from calibre_plugins.obok_dedrm.dialogs import (SelectionDialog, DecryptAddProgressDialog,
AddEpubFormatsProgressDialog, ResultsSummaryDialog)
from calibre_plugins.obok_dedrm.config import plugin_prefs as cfg
-from calibre_plugins.obok_dedrm.__init__ import (PLUGIN_NAME, PLUGIN_SAFE_NAME,
+from calibre_plugins.obok_dedrm.__init__ import (PLUGIN_NAME, PLUGIN_SAFE_NAME,
PLUGIN_VERSION, PLUGIN_DESCRIPTION, HELPFILE_NAME)
from calibre_plugins.obok_dedrm.utilities import (
get_icon, set_plugin_icon_resources, showErrorDlg, format_plural,
@@ -53,7 +54,7 @@ class InterfacePluginAction(InterfaceAction):
def genesis(self):
icon_resources = self.load_resources(PLUGIN_ICONS)
set_plugin_icon_resources(PLUGIN_NAME, icon_resources)
-
+
self.qaction.setIcon(get_icon(PLUGIN_ICONS[0]))
self.qaction.triggered.connect(self.launchObok)
self.gui.keyboard.finalize()
@@ -106,10 +107,10 @@ class InterfacePluginAction(InterfaceAction):
# Get a list of Kobo titles
books = self.build_book_list()
if len(books) < 1:
- msg = _('<p>No books found in Kobo Library\nAre you sure it\'s installed\configured\synchronized?')
+ msg = _('<p>No books found in Kobo Library\nAre you sure it\'s installed/configured/synchronized?')
showErrorDlg(msg, None)
return
-
+
# Check to see if a key can be retrieved using the legacy obok method.
legacy_key = legacy_obok().get_legacy_cookie_id
if legacy_key is not None:
@@ -154,7 +155,7 @@ class InterfacePluginAction(InterfaceAction):
# Close Kobo Library object
self.library.close()
- # If we have decrypted books to work with, feed the list of decrypted books details
+ # If we have decrypted books to work with, feed the list of decrypted books details
# and the callback function (self.add_new_books) to the ProgressDialog dispatcher.
if len(self.books_to_add):
d = DecryptAddProgressDialog(self.gui, self.books_to_add, self.add_new_books, self.db, 'calibre',
@@ -212,7 +213,7 @@ class InterfacePluginAction(InterfaceAction):
def get_decrypted_kobo_books(self, book):
'''
This method is a call-back function used by DecryptAddProgressDialog in dialogs.py to decrypt Kobo books
-
+
:param book: A KoboBook object that is to be decrypted.
'''
print (_('{0} - Decrypting {1}').format(PLUGIN_NAME + ' v' + PLUGIN_VERSION, book.title))
@@ -233,7 +234,7 @@ class InterfacePluginAction(InterfaceAction):
'''
This method is a call-back function used by DecryptAddProgressDialog in dialogs.py to add books to calibre
(It's set up to handle multiple books, but will only be fed books one at a time by DecryptAddProgressDialog)
-
+
:param books_to_add: List of calibre bookmaps (created in get_decrypted_kobo_books)
'''
added = self.db.add_books(books_to_add, add_duplicates=False, run_hooks=False)
@@ -253,7 +254,7 @@ class InterfacePluginAction(InterfaceAction):
def add_epub_format(self, book_id, mi, path):
'''
This method is a call-back function used by AddEpubFormatsProgressDialog in dialogs.py
-
+
:param book_id: calibre ID of the book to add the encrypted epub to.
:param mi: calibre metadata object
:param path: path to the decrypted epub (temp file)
@@ -281,7 +282,7 @@ class InterfacePluginAction(InterfaceAction):
self.formats_to_add.append((home_id, mi, tmp_file))
else:
self.no_home_for_book.append(mi)
- # If we found homes for decrypted epubs in existing calibre entries, feed the list of decrypted book
+ # If we found homes for decrypted epubs in existing calibre entries, feed the list of decrypted book
# details and the callback function (self.add_epub_format) to the ProgressDialog dispatcher.
if self.formats_to_add:
d = AddEpubFormatsProgressDialog(self.gui, self.formats_to_add, self.add_epub_format)
@@ -306,10 +307,10 @@ class InterfacePluginAction(InterfaceAction):
sd = ResultsSummaryDialog(self.gui, caption, msg, log)
sd.exec_()
return
-
+
def ask_about_inserting_epubs(self):
'''
- Build question dialog with details about kobo books
+ Build question dialog with details about kobo books
that couldn't be added to calibre as new books.
'''
''' Terisa: Improve the message
@@ -327,13 +328,13 @@ class InterfacePluginAction(InterfaceAction):
msg = _('<p><b>{0}</b> -- not added because of {1} in your library.<br /><br />').format(self.duplicate_book_list[0][0].title, self.duplicate_book_list[0][2])
msg += _('Would you like to try and add the EPUB format to an available calibre duplicate?<br /><br />')
msg += _('NOTE: no pre-existing EPUB will be overwritten.')
-
+
return question_dialog(self.gui, caption, msg, det_msg)
def find_a_home(self, ids):
'''
Find the ID of the first EPUB-Free duplicate available
-
+
:param ids: List of calibre IDs that might serve as a home.
'''
for id in ids:
@@ -373,7 +374,7 @@ class InterfacePluginAction(InterfaceAction):
zin = zipfile.ZipFile(book.filename, 'r')
#print ('Kobo library filename: {0}'.format(book.filename))
for userkey in self.userkeys:
- print (_('Trying key: '), userkey.encode('hex_codec'))
+ print (_('Trying key: '), codecs.encode(userkey, 'hex'))
check = True
try:
fileout = PersistentTemporaryFile('.epub', dir=self.tdir)
@@ -455,7 +456,7 @@ class InterfacePluginAction(InterfaceAction):
if cancelled_count > 0:
log += _('<p><b>Book imports cancelled by user:</b> {}</p>\n').format(cancelled_count)
return (msg, log)
- log += _('<p><b>New EPUB formats inserted in existing calibre books:</b> {0}</p>\n').format(len(self.successful_format_adds))
+ log += _('<p><b>New EPUB formats inserted in existing calibre books:</b> {0}</p>\n').format(len(self.successful_format_adds))
if self.successful_format_adds:
log += '<ul>\n'
for id, mi in self.successful_format_adds:
@@ -474,7 +475,7 @@ class InterfacePluginAction(InterfaceAction):
log += _('<p><b>Format imports cancelled by user:</b> {}</p>\n').format(cancelled_count)
return (msg, log)
else:
-
+
# Single book ... don't get fancy.
if self.ids_of_new_books:
title = self.ids_of_new_books[0][1].title
@@ -494,4 +495,4 @@ class InterfacePluginAction(InterfaceAction):
reason = _('of unknown reasons. Gosh I\'m embarrassed!')
msg = _('<p>{0} not added because {1}').format(title, reason)
return (msg, log)
-
+
diff --git a/Obok_plugin/common_utils.py b/Obok_plugin/common_utils.py
index 0f2164a..babad1c 100644
--- a/Obok_plugin/common_utils.py
+++ b/Obok_plugin/common_utils.py
@@ -427,7 +427,7 @@ class KeyValueComboBox(QComboBox):
def selected_key(self):
for key, value in self.values.iteritems():
- if value == unicode(self.currentText()).strip():
+ if value == self.currentText().strip():
return key
@@ -450,7 +450,7 @@ class KeyComboBox(QComboBox):
def selected_key(self):
for key, value in self.values.iteritems():
- if key == unicode(self.currentText()).strip():
+ if key == self.currentText().strip():
return key
diff --git a/Obok_plugin/config.py b/Obok_plugin/config.py
index 8244b91..522b86a 100644
--- a/Obok_plugin/config.py
+++ b/Obok_plugin/config.py
@@ -75,7 +75,7 @@ class ConfigWidget(QWidget):
def save_settings(self):
- plugin_prefs['finding_homes_for_formats'] = unicode(self.find_homes.currentText())
+ plugin_prefs['finding_homes_for_formats'] = self.find_homes.currentText()
plugin_prefs['kobo_serials'] = self.tmpserials
plugin_prefs['kobo_directory'] = self.kobodirectory
@@ -165,7 +165,7 @@ class ManageKeysDialog(QDialog):
def delete_key(self):
if not self.listy.currentItem():
return
- keyname = unicode(self.listy.currentItem().text())
+ 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):
return
self.plugin_keys.remove(keyname)
@@ -202,11 +202,11 @@ class AddSerialDialog(QDialog):
@property
def key_name(self):
- return unicode(self.key_ledit.text()).strip()
+ return self.key_ledit.text().strip()
@property
def key_value(self):
- return unicode(self.key_ledit.text()).strip()
+ return self.key_ledit.text().strip()
def accept(self):
if len(self.key_name) == 0 or self.key_name.isspace():
diff --git a/Obok_plugin/obok/obok.py b/Obok_plugin/obok/obok.py
index 21ef14a..80bc058 100644
--- a/Obok_plugin/obok/obok.py
+++ b/Obok_plugin/obok/obok.py
@@ -1,6 +1,9 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
+# Version 4.0.0 September 2020
+# Python 3.0
+#
# Version 3.2.5 December 2016
# Improve detection of good text decryption.
#
@@ -152,8 +155,8 @@
"""Manage all Kobo books, either encrypted or DRM-free."""
from __future__ import print_function
-__version__ = '3.2.4'
-__about__ = u"Obok v{0}\nCopyright © 2012-2016 Physisticated et al.".format(__version__)
+__version__ = '4.0.0'
+__about__ = u"Obok v{0}\nCopyright © 2012-2020 Physisticated et al.".format(__version__)
import sys
import os
@@ -231,7 +234,7 @@ def _load_crypto_libcrypto():
raise ENCRYPTIONError(_('Failed to initialize AES key'))
def decrypt(self, data):
- clear = ''
+ clear = b''
for i in range(0, len(data), 16):
out = create_string_buffer(16)
rv = AES_ecb_encrypt(data[i:i+16], out, self._key, 0)
@@ -276,7 +279,7 @@ class SafeUnbuffered:
if self.encoding == None:
self.encoding = "utf-8"
def write(self, data):
- if isinstance(data,unicode):
+ if isinstance(data,bytes):
data = data.encode(self.encoding,"replace")
self.stream.write(data)
self.stream.flush()
@@ -381,7 +384,7 @@ class KoboLibrary(object):
print(self.newdb.name)
olddb = open(kobodb, 'rb')
self.newdb.write(olddb.read(18))
- self.newdb.write('\x01\x01')
+ self.newdb.write(b'\x01\x01')
olddb.read(2)
self.newdb.write(olddb.read())
olddb.close()
@@ -488,14 +491,14 @@ class KoboLibrary(object):
pass
row = cursor.fetchone()
return userids
-
+
def __getuserkeys (self, macaddr):
userids = self.__getuserids()
userkeys = []
for hash in KOBO_HASH_KEYS:
- deviceid = hashlib.sha256(hash + macaddr).hexdigest()
+ deviceid = hashlib.sha256((hash + macaddr).encode('ascii')).hexdigest()
for userid in userids:
- userkey = hashlib.sha256(deviceid + userid).hexdigest()
+ userkey = hashlib.sha256((deviceid + userid).encode('ascii')).hexdigest()
userkeys.append(binascii.a2b_hex(userkey[32:]))
return userkeys
@@ -556,7 +559,7 @@ class KoboBook(object):
# Convert relative URIs
href = item.attrib['href']
if not c.match(href):
- href = string.join((basedir, href), '')
+ href = ''.join((basedir, href))
# Update books we've found from the DB.
if href in self._encryptedfiles:
@@ -606,57 +609,57 @@ class KoboFile(object):
stride = 1
print(u"Checking text:{0}:".format(contents[:10]))
# check for byte order mark
- if contents[:3]=="\xef\xbb\xbf":
+ if contents[:3]==b"\xef\xbb\xbf":
# seems to be utf-8 with BOM
print(u"Could be utf-8 with BOM")
textoffset = 3
- elif contents[:2]=="\xfe\xff":
+ elif contents[:2]==b"\xfe\xff":
# seems to be utf-16BE
print(u"Could be utf-16BE")
textoffset = 3
stride = 2
- elif contents[:2]=="\xff\xfe":
+ elif contents[:2]==b"\xff\xfe":
# seems to be utf-16LE
print(u"Could be utf-16LE")
textoffset = 2
stride = 2
else:
print(u"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:
+ 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,ord(contents[i])))
+ print(u"Bad character at {0}, value {1}".format(i,contents[i]))
raise ValueError
print(u"Seems to be good text")
return True
- if contents[:5]=="<?xml" or contents[:8]=="\xef\xbb\xbf<?xml":
+ if contents[:5]==b"<?xml" or contents[:8]==b"\xef\xbb\xbf<?xml":
# utf-8
return True
- elif contents[:14]=="\xfe\xff\x00<\x00?\x00x\x00m\x00l":
+ elif contents[:14]==b"\xfe\xff\x00<\x00?\x00x\x00m\x00l":
# utf-16BE
return True
- elif contents[:14]=="\xff\xfe<\x00?\x00x\x00m\x00l\x00":
+ elif contents[:14]==b"\xff\xfe<\x00?\x00x\x00m\x00l\x00":
# utf-16LE
return True
- elif contents[:9]=="<!DOCTYPE" or contents[:12]=="\xef\xbb\xbf<!DOCTYPE":
+ elif contents[:9]==b"<!DOCTYPE" or contents[:12]==b"\xef\xbb\xbf<!DOCTYPE":
# utf-8 of weird <!DOCTYPE start
return True
- elif contents[:22]=="\xfe\xff\x00<\x00!\x00D\x00O\x00C\x00T\x00Y\x00P\x00E":
+ elif contents[:22]==b"\xfe\xff\x00<\x00!\x00D\x00O\x00C\x00T\x00Y\x00P\x00E":
# utf-16BE of weird <!DOCTYPE start
return True
- elif contents[:22]=="\xff\xfe<\x00!\x00D\x00O\x00C\x00T\x00Y\x00P\x00E\x00":
+ elif contents[:22]==b"\xff\xfe<\x00!\x00D\x00O\x00C\x00T\x00Y\x00P\x00E\x00":
# utf-16LE of weird <!DOCTYPE start
return True
else:
print(u"Bad XML: {0}".format(contents[:8]))
raise ValueError
elif self.mimetype == 'image/jpeg':
- if contents[:3] == '\xff\xd8\xff':
+ if contents[:3] == b'\xff\xd8\xff':
return True
else:
- print(u"Bad JPEG: {0}".format(contents[:3].encode('hex')))
+ print(u"Bad JPEG: {0}".format(contents[:3].hex()))
raise ValueError()
return False
@@ -690,7 +693,7 @@ def decrypt_book(book, lib):
return 0
result = 1
for userkey in lib.userkeys:
- print(u"Trying key: {0}".format(userkey.encode('hex_codec')))
+ print(u"Trying key: {0}".format(userkey.hex()))
try:
zout = zipfile.ZipFile(outname, "w", zipfile.ZIP_DEFLATED)
for filename in zin.namelist():
@@ -735,7 +738,7 @@ def cli_main():
print(u"{0}: {1}".format(i + 1, book.title))
print(u"Or 'all'")
- choice = raw_input(u"Convert book number... ")
+ choice = input(u"Convert book number... ")
if choice == u'all':
books = list(lib.books)
else:
diff --git a/Obok_plugin/utilities.py b/Obok_plugin/utilities.py
index 62e305c..f81bd1c 100644
--- a/Obok_plugin/utilities.py
+++ b/Obok_plugin/utilities.py
@@ -7,21 +7,24 @@ __docformat__ = 'restructuredtext en'
import os, struct, time
-from StringIO import StringIO
+try:
+ from StringIO import StringIO
+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
from calibre import prints
from calibre.gui2 import (error_dialog, gprefs)
from calibre.gui2.actions import menu_action_unique_name
-from calibre_plugins.obok_dedrm.__init__ import (PLUGIN_NAME,
+from calibre_plugins.obok_dedrm.__init__ import (PLUGIN_NAME,
PLUGIN_SAFE_NAME, PLUGIN_VERSION, PLUGIN_DESCRIPTION)
plugin_ID = None
@@ -39,7 +42,7 @@ else:
def convert_qvariant(x):
vt = x.type()
if vt == x.String:
- return unicode(x.toString())
+ return x.toString()
if vt == x.List:
return [convert_qvariant(i) for i in x.toList()]
return x.toPyObject()
@@ -62,7 +65,7 @@ except NameError:
def format_plural(number, possessive=False):
'''
Cosmetic ditty to provide the proper string formatting variable to handle singular/plural situations
-
+
:param: number: variable that represents the count/len of something
'''
if not possessive:
@@ -141,7 +144,7 @@ def showErrorDlg(errmsg, parent, trcbk=False):
'''
if trcbk:
error= ''
- f=StringIO()
+ f=StringIO()
print_exc(file=f)
error_mess = f.getvalue().splitlines()
for line in error_mess: