Compare commits

...

8 Commits

Author SHA1 Message Date
Aleksa Sarai
d2808d83d9 Merge ad33aea18d into 808dc7d29a 2024-11-10 13:16:56 +00:00
Josh Cotton
808dc7d29a Fix Obok import in Calibre flatpak by using /sys/class/net/IFACE/address instead of ip (#586)
Fix #585.
Use /sys/class/net/IFACE/address for the MAC address instead of the ip
command.
2024-11-10 13:14:59 +00:00
precondition
2cd2792306 Obok.py/action.py: invoke _() only once 2024-11-10 13:11:28 +00:00
precondition
2e53d70e88 Catch FileNotFoundError due to undownloaded ebooks 2024-11-10 13:11:28 +00:00
Ben Combee
05fff5217b Fix crash using bare sha1 symbol
Use sha1 from hashlib, as it isn't imported globally, fixed crash trying to decrypt a eReader PDB file
2024-11-10 13:10:11 +00:00
Martin Rys
34c4c067e8 DeDRM ion: Correctly throw last exception if decrypt fails 2024-11-10 13:09:45 +00:00
Martin Rys
195ea69537 DeDRM ion: Clean out errorneous whitespace and UTF8 definition from python 2 times 2024-11-10 13:09:45 +00:00
Aleksa Sarai
ad33aea18d obok: linux: improve Kobo Desktop directory searching
The Kobo Desktop program will (when running under Wine on Linux) put all
of its data in the current working directory. This means that there
may be more than one Kobo.sqlite floating around by the user, which
leads to Obok showing an outdated list of books and the user being
confused by Obok cannot find the full list of books.

Solving this completely appears to be a bit too complicated, so this
patch is a best-effort improvement for the worst cases which can be
caused by this:

 1. If the user deletes the files but Obok has already cached the path,
    previously Obok would just error out rather than trying to search
    for a new Kobo.sqlite path and updating the cache.

 2. We search $HOME before searching /, which speeds up initial usage of
    Obok in the common case (usually Kobo Desktop will be installed in
    ~/.wine/drive_c) and also ensures that we correctly preference the
    current user's Kobo Desktop installation.

Signed-off-by: Aleksa Sarai <cyphar@cyphar.com>
2022-02-20 03:16:26 +11:00
4 changed files with 82 additions and 60 deletions

View File

@@ -255,7 +255,7 @@ class EreaderProcessor(object):
encrypted_key = r[172:172+8] encrypted_key = r[172:172+8]
encrypted_key_sha = r[56:56+20] encrypted_key_sha = r[56:56+20]
self.content_key = des.decrypt(encrypted_key) self.content_key = des.decrypt(encrypted_key)
if sha1(self.content_key).digest() != encrypted_key_sha: if hashlib.sha1(self.content_key).digest() != encrypted_key_sha:
raise ValueError('Incorrect Name and/or Credit Card') raise ValueError('Incorrect Name and/or Credit Card')
def getNumImages(self): def getNumImages(self):

View File

@@ -1,25 +1,19 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
# -*- coding: utf-8 -*- """ion.py: Decrypt Kindle KFX files.
# ion.py Revision history:
# Copyright © 2013-2020 Apprentice Harper et al. Pascal implementation by lulzkabulz.
BinaryIon.pas + DrmIon.pas + IonSymbols.pas
__license__ = 'GPL v3' 1.0 - Python translation by apprenticenaomi.
__version__ = '3.0' 1.1 - DeDRM integration by anon.
1.2 - Added pylzma import fallback
# Revision history: 1.3 - Fixed lzma support for calibre 4.6+
# Pascal implementation by lulzkabulz. 2.0 - VoucherEnvelope v2/v3 support by apprenticesakuya.
# BinaryIon.pas + DrmIon.pas + IonSymbols.pas 3.0 - Added Python 3 compatibility for calibre 5.0
# 1.0 - Python translation by apprenticenaomi.
# 1.1 - DeDRM integration by anon.
# 1.2 - Added pylzma import fallback
# 1.3 - Fixed lzma support for calibre 4.6+
# 2.0 - VoucherEnvelope v2/v3 support by apprenticesakuya.
# 3.0 - Added Python 3 compatibility for calibre 5.0
Copyright © 2013-2020 Apprentice Harper et al.
""" """
Decrypt Kindle KFX files. from __future__ import annotations
"""
import collections import collections
import hashlib import hashlib
@@ -30,6 +24,9 @@ import struct
from io import BytesIO from io import BytesIO
__license__ = 'GPL v3'
__version__ = '3.0'
#@@CALIBRE_COMPAT_CODE@@ #@@CALIBRE_COMPAT_CODE@@
@@ -1349,7 +1346,7 @@ class DrmIonVoucher(object):
process_V4648(shared), process_V5683(shared)] process_V4648(shared), process_V5683(shared)]
decrypted=False decrypted=False
ex=None lastexception: Exception | None = None
for sharedsecret in sharedsecrets: for sharedsecret in sharedsecrets:
key = hmac.new(sharedsecret, b"PIDv3", digestmod=hashlib.sha256).digest() key = hmac.new(sharedsecret, b"PIDv3", digestmod=hashlib.sha256).digest()
aes = AES.new(key[:32], AES.MODE_CBC, self.cipheriv[:16]) aes = AES.new(key[:32], AES.MODE_CBC, self.cipheriv[:16])
@@ -1366,9 +1363,10 @@ class DrmIonVoucher(object):
print("Decryption succeeded") print("Decryption succeeded")
break break
except Exception as ex: except Exception as ex:
lastexception = ex
print("Decryption failed, trying next fallback ") print("Decryption failed, trying next fallback ")
if not decrypted: if not decrypted:
raise ex raise lastexception
self.drmkey.stepin() self.drmkey.stepin()
while self.drmkey.hasnext(): while self.drmkey.hasnext():

View File

@@ -374,7 +374,11 @@ class InterfacePluginAction(InterfaceAction):
result['success'] = False result['success'] = False
result['fileobj'] = None result['fileobj'] = None
try:
zin = zipfile.ZipFile(book.filename, 'r') zin = zipfile.ZipFile(book.filename, 'r')
except FileNotFoundError:
print (_('{0} - File "{1}" not found. Make sure the eBook has been properly downloaded in the Kobo app.').format(PLUGIN_NAME, book.filename))
return result
#print ('Kobo library filename: {0}'.format(book.filename)) #print ('Kobo library filename: {0}'.format(book.filename))
for userkey in self.userkeys: for userkey in self.userkeys:
print (_('Trying key: '), codecs.encode(userkey, 'hex')) print (_('Trying key: '), codecs.encode(userkey, 'hex'))

View File

@@ -327,36 +327,50 @@ class KoboLibrary(object):
elif sys.platform.startswith('darwin'): elif sys.platform.startswith('darwin'):
self.kobodir = os.path.join(os.environ['HOME'], "Library", "Application Support", "Kobo", "Kobo Desktop Edition") self.kobodir = os.path.join(os.environ['HOME'], "Library", "Application Support", "Kobo", "Kobo Desktop Edition")
elif sys.platform.startswith('linux'): elif sys.platform.startswith('linux'):
# Since on Linux, you have to run Kobo Desktop within Wine,
# there is no guarantee where Kobo.sqlite (and the rest of
# the kobo directory) might be.
#
# It should also be noted that Kobo Desktop will store all
# of it files in the current directory where you run it,
# meaning that a user might accidentally create several
# Kobo.sqlite files which are all separate and then be
# confused why Obok can't find any of the new books. Sadly
# there isn't a trivial way to deal with this.
#sets ~/.config/calibre as the location to store the kobodir location info file and creates this directory if necessary # We cache the kobodir we find in ~/.config/calibre.
kobodir_cache_dir = os.path.join(os.environ['HOME'], ".config", "calibre") kobodir_cache_dir = os.path.join(os.environ['HOME'], ".config", "calibre", "plugins", "obok")
if not os.path.isdir(kobodir_cache_dir): if not os.path.isdir(kobodir_cache_dir):
os.mkdir(kobodir_cache_dir) os.mkdir(kobodir_cache_dir)
kobodir_cache_file = os.path.join(kobodir_cache_dir, "kobo-location")
#appends the name of the file we're storing the kobodir location info to the above path try:
kobodir_cache_file = str(kobodir_cache_dir) + "/" + "kobo location" # If the cached version exists and the path does really
# contain Kobo.sqlite, use that.
with open(kobodir_cache_file, "r") as f:
cached_kobodir = f.read().strip()
assert os.path.isfile(os.path.join(cached_kobodir, "Kobo.sqlite"))
self.kobodir = cached_kobodir
except (AssertionError, FileNotFoundError):
# If there was no cached version, search the entire
# filesystem tree for a directory containing
# "Kobo.sqlite".
#
# We first search $HOME to avoid picking another user's
# Kobo.sqlite file, but then fallback to / if there was
# nothing in $HOME.
for candidate_root in (os.environ["HOME"], "/"):
for root, _, files in os.walk(candidate_root):
if "Kobo.sqlite" in files:
with open(kobodir_cache_file, "w") as f:
f.write("%s/\n" % (root,))
self.kobodir = root
break
"""if the above file does not exist, recursively searches from the root # Desktop versions use Kobo.sqlite.
of the filesystem until kobodir is found and stores the location of kobodir
in that file so this loop can be skipped in the future"""
original_stdout = sys.stdout
if not os.path.isfile(kobodir_cache_file):
for root, dirs, files in os.walk('/'):
for file in files:
if file == 'Kobo.sqlite':
kobo_linux_path = str(root)
with open(kobodir_cache_file, 'w') as f:
sys.stdout = f
print(kobo_linux_path, end='')
sys.stdout = original_stdout
f = open(kobodir_cache_file, 'r' )
self.kobodir = f.read()
# desktop versions use Kobo.sqlite
kobodb = os.path.join(self.kobodir, "Kobo.sqlite") kobodb = os.path.join(self.kobodir, "Kobo.sqlite")
# check for existence of file # check for existence of file
if (not(os.path.isfile(kobodb))): if not os.path.isfile(kobodb):
# give up here, we haven't found anything useful # give up here, we haven't found anything useful
self.kobodir = u"" self.kobodir = u""
kobodb = u"" kobodb = u""
@@ -449,9 +463,15 @@ class KoboLibrary(object):
for m in matches: for m in matches:
# print "m:{0}".format(m[0]) # print "m:{0}".format(m[0])
macaddrs.append(m[0].upper()) macaddrs.append(m[0].upper())
elif sys.platform.startswith('linux'):
for interface in os.listdir('/sys/class/net'):
with open('/sys/class/net/' + interface + '/address', 'r') as f:
mac = f.read().strip().upper()
# some interfaces, like Tailscale's VPN interface, do not have a MAC address
if mac != '':
macaddrs.append(mac)
else: else:
# probably linux # final fallback
# let's try ip # let's try ip
c = re.compile('\s(' + '[0-9a-f]{2}:' * 5 + '[0-9a-f]{2})(\s|$)', re.IGNORECASE) c = re.compile('\s(' + '[0-9a-f]{2}:' * 5 + '[0-9a-f]{2})(\s|$)', re.IGNORECASE)
for line in os.popen('ip -br link'): for line in os.popen('ip -br link'):