Compare commits

...

9 Commits

Author SHA1 Message Date
Aleksa Sarai
8228e38d5a Merge ad33aea18d into 10b6caf9f5 2023-08-05 07:57:31 -07:00
NoDRM
10b6caf9f5 Enable autorelease into 2nd repo 2023-08-03 21:53:16 +02:00
NoDRM
53996cf49c More Python2 fixes 2023-08-03 20:45:06 +02:00
NoDRM
d388ae72fd More Py2 fixes 2023-08-03 20:14:33 +02:00
NoDRM
bc089ee46d More Python2 bugfixes 2023-08-03 20:01:38 +02:00
NoDRM
e509b7d520 Fix python2 issues in kgenpids and kindlekey 2023-08-03 11:26:05 +02:00
NoDRM
e82d2b5c9c Fix PDF decryption for 256-bit AES with V=5 2023-08-02 18:13:42 +02:00
NoDRM
7f6dd84389 Fix PDF decryption of ancient 40-bit RC4 with R=2 2023-08-02 16:55:41 +02: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
21 changed files with 259 additions and 149 deletions

View File

@@ -20,46 +20,33 @@ jobs:
path: |
DeDRM_tools_*.zip
DeDRM_tools.zip
- name: Prepare release
run: cp DeDRM_tools.zip DeDRM_alpha_${{ github.sha }}.zip
# - name: Delete old release
# uses: cb80/delrel@latest
# with:
# tag: autorelease
# token: ${{ github.token }}
#
# - name: Delete old tag
# uses: dev-drprasad/delete-tag-and-release@v1.0
# with:
# tag_name: autorelease
# github_token: ${{ github.token }}
# delete_release: true
#
# - name: Prepare release
# run: cp DeDRM_tools.zip DeDRM_alpha_${{ github.sha }}.zip
#
# - name: Auto-release
# id: autorelease
# uses: softprops/action-gh-release@v1
# with:
# tag_name: autorelease
# token: ${{ github.token }}
# name: Automatic alpha release with latest changes
# body: |
# This release is automatically generated by Github for each commit.
#
# This means, every time a change is made to this repo, this release will be updated to contain an untested copy of the plugin at that stage. This will contain the most up-to-date code, but it's not tested at all and may be broken.
#
# Last update based on Git commit ${{ github.sha }}.
# prerelease: true
# draft: true
# files: DeDRM_alpha_${{ github.sha }}.zip
#
# - name: Make release public
# uses: irongut/EditRelease@v1.2.0
# with:
# token: ${{ github.token }}
# id: ${{ steps.autorelease.outputs.id }}
# draft: false
# prerelease: true
#
#
- uses: dev-drprasad/delete-older-releases@v0.2.1
with:
repo: noDRM/DeDRM_tools_autorelease
keep_latest: 0
delete_tags: true
env:
GITHUB_TOKEN: ${{ secrets.AUTORELEASE_KEY }}
- name: Auto-release
id: autorelease
uses: softprops/action-gh-release@v1
with:
tag_name: autorelease_${{ github.sha }}
repository: noDRM/DeDRM_tools_autorelease
token: ${{ secrets.AUTORELEASE_KEY }}
name: Automatic alpha release with latest changes
body: |
This release is automatically generated by Github for each commit.
This means, every time a change is made to the repo, a release with an untested copy of the plugin at that stage will be created. This will contain the most up-to-date code, but it's not tested at all and may be broken.
Last update based on Git commit [${{ github.sha }}](https://github.com/noDRM/DeDRM_tools/commit/${{ github.sha }}).
prerelease: true
draft: false
files: DeDRM_alpha_${{ github.sha }}.zip

View File

@@ -99,4 +99,7 @@ This is v10.0.9, a release candidate for v10.1.0. I don't expect there to be maj
## Fixes on master (not yet released):
- (none)
- Fix a bug where decrypting a 40-bit RC4 pdf with R=2 didn't work.
- Fix a bug where decrypting a 256-bit AES pdf with V=5 didn't work.
- Fix bugs in kgenpids.py, alfcrypto.py, mobidedrm.py and kindlekey.py that caused it to fail on Python 2 (#380).
- Fix some bugs (Python 2 and Python 3) in erdr2pml.py (untested).

View File

@@ -15,6 +15,6 @@ if "calibre" in sys.modules and sys.version_info[0] == 2:
sys.path.insert(0, os.path.join(config_dir, "plugins", "DeDRM.zip"))
# Explicitly set the package identifier so we are allowed to import stuff ...
#__package__ = "DeDRM_plugin"
__package__ = "calibre_plugins.dedrm"
#@@CALIBRE_COMPAT_CODE_END@@

View File

@@ -96,7 +96,10 @@ import traceback
#@@CALIBRE_COMPAT_CODE@@
try:
import __version
try:
from . import __version
except:
import __version
except:
print("#############################")
print("Failed to load the DeDRM plugin")
@@ -134,8 +137,10 @@ try:
except:
config_dir = ""
import utilities
try:
from . import utilities
except:
import utilities
PLUGIN_NAME = __version.PLUGIN_NAME
@@ -915,6 +920,9 @@ class DeDRM(FileTypePlugin):
# perhaps we need to get a new default Kindle for Mac/PC key
defaultkeys = []
print("{0} v{1}: Failed to decrypt with error: {2}".format(PLUGIN_NAME, PLUGIN_VERSION,e.args[0]))
traceback.print_exc()
print("{0} v{1}: Looking for new default Kindle Key after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION, time.time()-self.starttime))
try:

View File

@@ -44,10 +44,11 @@ __version__ = '7.4'
import sys, os, struct, getopt
from base64 import b64decode
#@@CALIBRE_COMPAT_CODE@@
from utilities import SafeUnbuffered
from argv_utils import unicode_argv
from .utilities import SafeUnbuffered
from .argv_utils import unicode_argv
try:

View File

@@ -8,6 +8,7 @@
# pbkdf2.py Copyright © 2009 Daniel Holth <dholth@fastmail.fm>
# pbkdf2.py This code may be freely used and modified for any purpose.
import sys
import hmac
from struct import pack
import hashlib
@@ -25,7 +26,10 @@ class Pukall_Cipher(object):
raise Exception("PC1: Bad key length")
wkey = []
for i in range(8):
wkey.append(key[i*2]<<8 | key[i*2+1])
if sys.version_info[0] == 2:
wkey.append(ord(key[i*2])<<8 | ord(key[i*2+1]))
else:
wkey.append(key[i*2]<<8 | key[i*2+1])
dst = bytearray(len(src))
for i in range(len(src)):
temp1 = 0;
@@ -37,7 +41,12 @@ class Pukall_Cipher(object):
sum2 = (sum2+sum1)&0xFFFF
temp1 = (temp1*20021+1)&0xFFFF
byteXorVal ^= temp1 ^ sum2
curByte = src[i]
if sys.version_info[0] == 2:
curByte = ord(src[i])
else:
curByte = src[i]
if not decryption:
keyXorVal = curByte * 257;
curByte = ((curByte ^ (byteXorVal >> 8)) ^ byteXorVal) & 0xFF
@@ -45,7 +54,12 @@ class Pukall_Cipher(object):
keyXorVal = curByte * 257;
for j in range(8):
wkey[j] ^= keyXorVal;
dst[i] = curByte
if sys.version_info[0] == 2:
dst[i] = chr(curByte)
else:
dst[i] = curByte
return bytes(dst)
class Topaz_Cipher(object):
@@ -103,7 +117,7 @@ class KeyIVGen(object):
def xorbytes( a, b ):
if len(a) != len(b):
raise Exception("xorbytes(): lengths differ")
return bytes([x ^ y for x, y in zip(a, b)])
return bytes(bytearray([x ^ y for x, y in zip(a, b)]))
def prf( h, data ):
hm = h.copy()

View File

@@ -29,7 +29,7 @@ from calibre.constants import iswindows, isosx
from __init__ import PLUGIN_NAME, PLUGIN_VERSION
from __version import RESOURCE_NAME as help_file_name
from utilities import uStrCmp
from .utilities import uStrCmp
import prefs
import androidkindlekey

View File

@@ -53,12 +53,12 @@ import sys, struct, os, traceback
import zlib
import zipfile
import xml.etree.ElementTree as etree
from argv_utils import unicode_argv
from .argv_utils import unicode_argv
NSMAP = {'adept': 'http://ns.adobe.com/adept',
'enc': 'http://www.w3.org/2001/04/xmlenc#'}
from utilities import SafeUnbuffered
from .utilities import SafeUnbuffered
_FILENAME_LEN_OFFSET = 26

View File

@@ -79,12 +79,13 @@ except ImportError:
#@@CALIBRE_COMPAT_CODE@@
from utilities import SafeUnbuffered
from .utilities import SafeUnbuffered
from .argv_utils import unicode_argv
iswindows = sys.platform.startswith('win')
isosx = sys.platform.startswith('darwin')
from argv_utils import unicode_argv
import cgi
import logging
@@ -141,14 +142,20 @@ def sanitizeFileName(name):
def fixKey(key):
def fixByte(b):
if sys.version_info[0] == 2:
b = ord(b)
return b ^ ((b ^ (b<<1) ^ (b<<2) ^ (b<<3) ^ (b<<4) ^ (b<<5) ^ (b<<6) ^ (b<<7) ^ 0x80) & 0x80)
return bytes([fixByte(a) for a in key])
return bytes(bytearray([fixByte(a) for a in key]))
def deXOR(text, sp, table):
r=''
r=b''
j = sp
for i in range(len(text)):
r += chr(ord(table[j]) ^ ord(text[i]))
if sys.version_info[0] == 2:
r += chr(ord(table[j]) ^ ord(text[i]))
else:
r += bytes(bytearray([table[j] ^ text[i]]))
j = j + 1
if j == len(table):
j = 0

View File

@@ -50,9 +50,9 @@ try:
except ImportError:
from Crypto.Cipher import AES
from utilities import SafeUnbuffered
from .utilities import SafeUnbuffered
from argv_utils import unicode_argv
from .argv_utils import unicode_argv
class IGNOBLEError(Exception):
pass

View File

@@ -27,14 +27,14 @@ import hashlib
import getopt
import re
from utilities import SafeUnbuffered
from .utilities import SafeUnbuffered
try:
from calibre.constants import iswindows
except:
iswindows = sys.platform.startswith('win')
from argv_utils import unicode_argv
from .argv_utils import unicode_argv
class DrmException(Exception):
pass

View File

@@ -70,9 +70,8 @@ def unpad(data, padding=16):
return data[:-pad_len]
from utilities import SafeUnbuffered
from argv_utils import unicode_argv
from .utilities import SafeUnbuffered
from .argv_utils import unicode_argv
class ADEPTError(Exception):

View File

@@ -93,12 +93,12 @@ def unpad(data, padding=16):
return data[:-pad_len]
from utilities import SafeUnbuffered
from .utilities import SafeUnbuffered
iswindows = sys.platform.startswith('win')
isosx = sys.platform.startswith('darwin')
from argv_utils import unicode_argv
from .argv_utils import unicode_argv
class ADEPTError(Exception):
pass
@@ -1366,14 +1366,14 @@ class PDFDocument(object):
def process_with_aes(self, key, encrypt, data, repetitions = 1, iv = None):
if iv is None:
keylen = len(key)
iv = bytes([0x00]*keylen)
iv = bytes(bytearray(16))
aes = AES.new(key, AES.MODE_CBC, iv)
if not encrypt:
plaintext = AES.new(key,AES.MODE_CBC,iv, True).decrypt(data)
plaintext = aes.decrypt(data)
return plaintext
else:
aes = AES.new(key, AES.MODE_CBC, iv, False)
new_data = bytes(data * repetitions)
crypt = aes.encrypt(new_data)
return crypt
@@ -1394,10 +1394,18 @@ class PDFDocument(object):
raise Exception("K1 < 32 ...")
#def process_with_aes(self, key: bytes, encrypt: bool, data: bytes, repetitions: int = 1, iv: bytes = None):
E = self.process_with_aes(K[:16], True, K1, 64, K[16:32])
K = (hashlib.sha256, hashlib.sha384, hashlib.sha512)[sum(E) % 3](E).digest()
E = bytearray(E)
E_mod_3 = 0
for i in range(16):
E_mod_3 += E[i]
E_mod_3 %= 3
K = (hashlib.sha256, hashlib.sha384, hashlib.sha512)[E_mod_3](E).digest()
if round_number >= 64:
ch = int.from_bytes(E[-1:], "big", signed=False)
ch = E[-1:][0] # get last byte
if ch <= round_number - 32:
done = True
@@ -1478,14 +1486,23 @@ class PDFDocument(object):
EncMetadata = b'True'
if (EncMetadata == ('False' or 'false') or V < 4) and R >= 4:
hash.update(codecs.decode(b'ffffffff','hex'))
# Finish hash:
hash = hash.digest()
if R >= 3:
# 8
for _ in range(50):
hash = hashlib.md5(hash.digest()[:length//8])
key = hash.digest()[:length//8]
hash = hashlib.md5(hash[:length//8]).digest()
if R == 2:
# R=2 only uses first five bytes.
key = hash[:5]
else:
key = hash[:length//8]
if R == 2:
# Algorithm 3.4
u1 = ARC4.new(key).decrypt(password)
u1 = ARC4.new(key).decrypt(self.PASSWORD_PADDING)
elif R >= 3:
# Algorithm 3.5
hash = hashlib.md5(self.PASSWORD_PADDING) # 2
@@ -1498,6 +1515,7 @@ class PDFDocument(object):
k = b''.join(bytes([c ^ i]) for c in key )
x = ARC4.new(k).decrypt(x)
u1 = x+x # 32bytes total
if R == 2:
is_authenticated = (u1 == U)
else:

View File

@@ -88,9 +88,9 @@ import kgenpids
import androidkindlekey
import kfxdedrm
from utilities import SafeUnbuffered
from .utilities import SafeUnbuffered
from argv_utils import unicode_argv
from .argv_utils import unicode_argv
# cleanup unicode filenames

View File

@@ -53,11 +53,17 @@ def SHA1(message):
def encode(data, map):
result = b''
for char in data:
value = char
if sys.version_info[0] == 2:
value = ord(char)
else:
value = char
Q = (value ^ 0x80) // len(map)
R = value % len(map)
result += bytes([map[Q]])
result += bytes([map[R]])
result += bytes(bytearray([map[Q]]))
result += bytes(bytearray([map[R]]))
return result
# Hash the bytes in data and then encode the digest with the characters in map
@@ -84,8 +90,11 @@ def decode(data,map):
def getTwoBitsFromBitField(bitField,offset):
byteNumber = offset // 4
bitPosition = 6 - 2*(offset % 4)
return bitField[byteNumber] >> bitPosition & 3
if sys.version_info[0] == 2:
return ord(bitField[byteNumber]) >> bitPosition & 3
else:
return bitField[byteNumber] >> bitPosition & 3
# Returns the six bits at offset from a bit field
def getSixBitsFromBitField(bitField,offset):
offset *= 3
@@ -97,7 +106,8 @@ def encodePID(hash):
global charMap3
PID = b''
for position in range (0,8):
PID += bytes([charMap3[getSixBitsFromBitField(hash,position)]])
PID += bytes(bytearray([charMap3[getSixBitsFromBitField(hash,position)]]))
return PID
# Encryption table used to generate the device PID
@@ -134,7 +144,7 @@ def generateDevicePID(table,dsn,nbRoll):
index = (index+1) %8
for counter in range (0,8):
index = ((((pid[counter] >>5) & 3) ^ pid[counter]) & 0x1f) + (pid[counter] >> 7)
pidAscii += bytes([charMap4[index]])
pidAscii += bytes(bytearray([charMap4[index]]))
return pidAscii
def crc32(s):
@@ -150,7 +160,7 @@ def checksumPid(s):
for i in (0,1):
b = crc & 0xff
pos = (b // l) ^ (b % l)
res += bytes([charMap4[pos%l]])
res += bytes(bytearray([charMap4[pos%l]]))
crc >>= 8
return res
@@ -161,14 +171,17 @@ def pidFromSerial(s, l):
crc = crc32(s)
arr1 = [0]*l
for i in range(len(s)):
arr1[i%l] ^= s[i]
if sys.version_info[0] == 2:
arr1[i%l] ^= ord(s[i])
else:
arr1[i%l] ^= s[i]
crc_bytes = [crc >> 24 & 0xff, crc >> 16 & 0xff, crc >> 8 & 0xff, crc & 0xff]
for i in range(l):
arr1[i] ^= crc_bytes[i&3]
pid = b""
for i in range(l):
b = arr1[i] & 0xff
pid += bytes([charMap4[(b >> 7) + ((b >> 5 & 3) ^ (b & 0x1f))]])
pid += bytes(bytearray([charMap4[(b >> 7) + ((b >> 5 & 3) ^ (b & 0x1f))]]))
return pid
@@ -177,6 +190,10 @@ def getKindlePids(rec209, token, serialnum):
if isinstance(serialnum,str):
serialnum = serialnum.encode('utf-8')
if sys.version_info[0] == 2:
if isinstance(serialnum,unicode):
serialnum = serialnum.encode('utf-8')
if rec209 is None:
return [serialnum]

View File

@@ -62,7 +62,9 @@ except NameError:
# Routines common to Mac and PC
from utilities import SafeUnbuffered
from .utilities import SafeUnbuffered
from .argv_utils import unicode_argv
try:
from calibre.constants import iswindows, isosx
@@ -70,7 +72,7 @@ except:
iswindows = sys.platform.startswith('win')
isosx = sys.platform.startswith('darwin')
from argv_utils import unicode_argv
class DrmException(Exception):
pass
@@ -115,11 +117,17 @@ def primes(n):
def encode(data, map):
result = b''
for char in data:
value = char
if sys.version_info[0] == 2:
value = ord(char)
else:
value = char
Q = (value ^ 0x80) // len(map)
R = value % len(map)
result += bytes([map[Q]])
result += bytes([map[R]])
result += bytes(bytearray([map[Q]]))
result += bytes(bytearray([map[R]]))
return result
# Hash the bytes in data and then encode the digest with the characters in map
@@ -232,9 +240,14 @@ if iswindows:
# replace any non-ASCII values with 0xfffd
for i in range(0,len(buffer)):
if buffer[i]>"\u007f":
#print "swapping char "+str(i)+" ("+buffer[i]+")"
buffer[i] = "\ufffd"
if sys.version_info[0] == 2:
if buffer[i]>u"\u007f":
#print "swapping char "+str(i)+" ("+buffer[i]+")"
buffer[i] = u"\ufffd"
else:
if buffer[i]>"\u007f":
#print "swapping char "+str(i)+" ("+buffer[i]+")"
buffer[i] = "\ufffd"
# return utf-8 encoding of modified username
#print "modified username:"+buffer.value
return buffer.value.encode('utf-8')

View File

@@ -16,24 +16,24 @@
import sys
import binascii
from utilities import SafeUnbuffered
from .utilities import SafeUnbuffered
from argv_utils import unicode_argv
from .argv_utils import unicode_argv
letters = 'ABCDEFGHIJKLMNPQRSTUVWXYZ123456789'
letters = b'ABCDEFGHIJKLMNPQRSTUVWXYZ123456789'
def crc32(s):
return (~binascii.crc32(s,-1))&0xFFFFFFFF
def checksumPid(s):
crc = crc32(s.encode('ascii'))
crc = crc32(s)
crc = crc ^ (crc >> 16)
res = s
l = len(letters)
for i in (0,1):
b = crc & 0xff
pos = (b // l) ^ (b % l)
res += letters[pos%l]
res += bytes(bytearray([letters[pos%l]]))
crc >>= 8
return res
@@ -43,16 +43,19 @@ def pidFromSerial(s, l):
arr1 = [0]*l
for i in range(len(s)):
arr1[i%l] ^= s[i]
if sys.version_info[0] == 2:
arr1[i%l] ^= ord(s[i])
else:
arr1[i%l] ^= s[i]
crc_bytes = [crc >> 24 & 0xff, crc >> 16 & 0xff, crc >> 8 & 0xff, crc & 0xff]
for i in range(l):
arr1[i] ^= crc_bytes[i&3]
pid = ''
pid = b""
for i in range(l):
b = arr1[i] & 0xff
pid+=letters[(b >> 7) + ((b >> 5 & 3) ^ (b & 0x1f))]
pid+=bytes(bytearray([letters[(b >> 7) + ((b >> 5 & 3) ^ (b & 0x1f))]]))
return pid

View File

@@ -80,11 +80,14 @@ import sys
import os
import struct
import binascii
from alfcrypto import Pukall_Cipher
from utilities import SafeUnbuffered
from argv_utils import unicode_argv
#@@CALIBRE_COMPAT_CODE@@
from .alfcrypto import Pukall_Cipher
from .utilities import SafeUnbuffered
from .argv_utils import unicode_argv
class DrmException(Exception):
@@ -103,19 +106,26 @@ def PC1(key, src, decryption=True):
except:
raise
# accepts unicode returns unicode
letters = b'ABCDEFGHIJKLMNPQRSTUVWXYZ123456789'
def crc32(s):
return (~binascii.crc32(s,-1))&0xFFFFFFFF
def checksumPid(s):
letters = 'ABCDEFGHIJKLMNPQRSTUVWXYZ123456789'
crc = (~binascii.crc32(s.encode('utf-8'),-1))&0xFFFFFFFF
s = s.encode()
crc = crc32(s)
crc = crc ^ (crc >> 16)
res = s
l = len(letters)
for i in (0,1):
b = crc & 0xff
pos = (b // l) ^ (b % l)
res += letters[pos%l]
res += bytes(bytearray([letters[pos%l]]))
crc >>= 8
return res
return res.decode()
# expects bytearray
def getSizeOfTrailingDataEntries(ptr, size, flags):
@@ -124,7 +134,11 @@ def getSizeOfTrailingDataEntries(ptr, size, flags):
if size <= 0:
return result
while True:
v = ptr[size-1]
if sys.version_info[0] == 2:
v = ord(ptr[size-1])
else:
v = ptr[size-1]
result |= (v & 0x7F) << bitpos
bitpos += 7
size -= 1
@@ -140,7 +154,10 @@ def getSizeOfTrailingDataEntries(ptr, size, flags):
# if multibyte data is included in the encryped data, we'll
# have already cleared this flag.
if flags & 1:
num += (ptr[size - num - 1] & 0x3) + 1
if sys.version_info[0] == 2:
num += (ord(ptr[size - num - 1]) & 0x3) + 1
else:
num += (ptr[size - num - 1] & 0x3) + 1
return num
@@ -299,7 +316,10 @@ class MobiBook:
for pid in pidlist:
bigpid = pid.encode('utf-8').ljust(16,b'\0')
temp_key = PC1(keyvec1, bigpid, False)
temp_key_sum = sum(temp_key) & 0xff
if sys.version_info[0] == 2:
temp_key_sum = sum(map(ord,temp_key)) & 0xff
else:
temp_key_sum = sum(temp_key) & 0xff
found_key = None
for i in range(count):
verification, size, type, cksum, cookie = struct.unpack('>LLLBxxx32s', data[i*0x30:i*0x30+0x30])
@@ -315,7 +335,11 @@ class MobiBook:
# Then try the default encoding that doesn't require a PID
pid = '00000000'
temp_key = keyvec1
temp_key_sum = sum(temp_key) & 0xff
if sys.version_info[0] == 2:
temp_key_sum = sum(map(ord,temp_key)) & 0xff
else:
temp_key_sum = sum(temp_key) & 0xff
for i in range(count):
verification, size, type, cksum, cookie = struct.unpack('>LLLBxxx32s', data[i*0x30:i*0x30+0x30])
if cksum == temp_key_sum:

View File

@@ -24,10 +24,10 @@ import traceback
from struct import pack
from struct import unpack
from alfcrypto import Topaz_Cipher
from utilities import SafeUnbuffered
from .alfcrypto import Topaz_Cipher
from .utilities import SafeUnbuffered
from argv_utils import unicode_argv
from .argv_utils import unicode_argv
#global switch

View File

@@ -327,36 +327,50 @@ class KoboLibrary(object):
elif sys.platform.startswith('darwin'):
self.kobodir = os.path.join(os.environ['HOME'], "Library", "Application Support", "Kobo", "Kobo Desktop Edition")
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
kobodir_cache_dir = os.path.join(os.environ['HOME'], ".config", "calibre")
# We cache the kobodir we find in ~/.config/calibre.
kobodir_cache_dir = os.path.join(os.environ['HOME'], ".config", "calibre", "plugins", "obok")
if not os.path.isdir(kobodir_cache_dir):
os.mkdir(kobodir_cache_dir)
#appends the name of the file we're storing the kobodir location info to the above path
kobodir_cache_file = str(kobodir_cache_dir) + "/" + "kobo location"
"""if the above file does not exist, recursively searches from the root
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
kobodir_cache_file = os.path.join(kobodir_cache_dir, "kobo-location")
f = open(kobodir_cache_file, 'r' )
self.kobodir = f.read()
try:
# 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
# desktop versions use Kobo.sqlite
# Desktop versions use Kobo.sqlite.
kobodb = os.path.join(self.kobodir, "Kobo.sqlite")
# 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
self.kobodir = u""
kobodb = u""
@@ -430,7 +444,7 @@ class KoboLibrary(object):
macaddrs = []
if sys.platform.startswith('win'):
c = re.compile('\s?(' + '[0-9a-f]{2}[:\-]' * 5 + '[0-9a-f]{2})(\s|$)', re.IGNORECASE)
try:
try:
output = subprocess.Popen('ipconfig /all', shell=True, stdout=subprocess.PIPE, text=True).stdout
for line in output:
m = c.search(line)

View File

@@ -3,11 +3,13 @@ DeDRM tools for ebooks
This is a fork of Apprentice Harper's version of the DeDRM tools. Apprentice Harper said that the original version of the plugin [is no longer maintained](https://github.com/apprenticeharper/DeDRM_tools#no-longer-maintained), so I've taken over, merged a bunch of open PRs, and added a ton more features and bugfixes.
The latest stable (released) version is v10.0.3 which [can be downloaded here](https://github.com/noDRM/DeDRM_tools/releases/tag/v10.0.3).
The latest stable (released) version is v10.0.3 which [can be downloaded here](https://github.com/noDRM/DeDRM_tools/releases/tag/v10.0.3). The latest beta is v10.0.9, as a release candidate for v10.1.0. It [can be downloaded here](https://github.com/noDRM/DeDRM_tools/releases/tag/v10.0.9).
Take a look at [the CHANGELOG](https://github.com/noDRM/DeDRM_tools/blob/master/CHANGELOG.md) to see a list of changes since the last version by Apprentice Harper (v7.2.1). This plugin will start with version v10.0.0.
The latest alpha version is available [at this link](https://github.com/noDRM/DeDRM_tools_autorelease/releases). This version is completely untested and will contain the latest code changes in this repository. With each commit in this repository, a new automatic alpha version will be uploaded there. If you want the most up-to-date code to test things and are okay with the plugin occasionally breaking, you can download this version.
The v10.0.0 versions of this plugin should both work with Calibre 5.x (Python 3) as well as Calibre 4.x and lower (Python 2). If you encounter issues with this plugin in Calibre 4.x or lower, please open a bug report.
Take a look at [the CHANGELOG](https://github.com/noDRM/DeDRM_tools/blob/master/CHANGELOG.md) to see a list of changes since the last version by Apprentice Harper (v7.2.1).
My version of the plugin should both work with Calibre 5.x/6.x (Python 3) as well as Calibre 4.x and lower (Python 2). If you encounter issues with this plugin in Calibre 4.x or lower, please open a bug report.
# Original README from Apprentice Harper