Compare commits

..

13 Commits

Author SHA1 Message Date
NoDRM
ca6d30b2d9 More stuff I missed 2022-08-06 20:25:07 +02:00
NoDRM
dfa247bf88 Cleanup 2022-08-06 20:19:36 +02:00
NoDRM
a0bb84fbfc Move unicode_argv to its own file 2022-08-06 20:19:18 +02:00
NoDRM
410e086d08 Remove AlfCrypto libraries and perform everything in Python
The old AlfCrypto DLL, SO and DYLIB files are ancient,
I don't have the systems to recompile them all, they
cause issues on ARM Macs, and I doubt with all the Python
improvements over the last years that they have a significant
performance advantage. And even if that's the case, nobody is
importing hundreds of DRM books at the same time so it shouldn't
hurt if some decryptions might take a bit longer.
2022-08-06 20:13:19 +02:00
NoDRM
9276d77f63 Couple Python 2 fixes in (unsupported) standalone scripts 2022-08-06 20:10:51 +02:00
NoDRM
de23b5c221 Move SafeUnbuffered to own Python file 2022-08-06 20:09:30 +02:00
NoDRM
b404605878 Another Python2 Bugfix for Obok 2022-08-06 19:57:20 +02:00
NoDRM
1cc5d383cc Delete unused files 2022-08-06 19:56:18 +02:00
NoDRM
41df9ecda0 Fix PDF corruption in Calibre 4 (#104) 2022-08-06 15:29:45 +02:00
NoDRM
80cbaa4841 Fix ZIP attribute "external_attr" getting reset 2022-08-06 13:53:03 +02:00
NoDRM
9a11f480b5 Fix plugin crash with invalid ADE key 2022-08-03 19:49:20 +02:00
NoDRM
59839ae5c7 Fix Calibre 6 issue in Obok plugin 2022-08-03 17:16:42 +02:00
NoDRM
c15135b12f Fix RSA.import_key (fixes #101)
Apparently "import_key" only exists in newer versions (as an alias to
"importKey"). "importKey" works in all versions ...
2022-07-16 09:54:00 +02:00
48 changed files with 437 additions and 2121 deletions

View File

@@ -69,4 +69,11 @@ List of changes since the fork of Apprentice Harper's repository:
## Fixes on master (not yet released): ## Fixes on master (not yet released):
- (None) - Fix a bug introduced with #48 that breaks DeDRM'ing on Calibre 4 (fixes #101).
- Fix some more Calibre-6 bugs in the Obok plugin (should fix #114).
- Fix a bug where invalid Adobe keys could cause the plugin to stop trying subsequent keys (partially fixes #109).
- Fix DRM removal sometimes resetting the ZIP's internal "external_attr" value on Calibre 5 and newer.
- Fix PDF decryption issues on Calibre 4 (hopefully fixes #104).
- Small Python 2 / Calibre 4 bugfix for Obok.
- Removing ancient AlfCrypto machine code libraries, moving all encryption / decryption to Python code.
- General cleanup and removal of dead code.

View File

@@ -134,29 +134,8 @@ except:
config_dir = "" config_dir = ""
# Wrap a stream so that output gets flushed immediately from utilities import SafeUnbuffered
# and also make sure that any unicode strings get safely
# 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,str) or isinstance(data,unicode):
# str for Python3, unicode for Python2
data = data.encode(self.encoding,"replace")
try:
buffer = getattr(self.stream, 'buffer', self.stream)
# self.stream.buffer for Python3, self.stream for Python2
buffer.write(data)
buffer.flush()
except:
# We can do nothing if a write fails
raise
def __getattr__(self, attr):
return getattr(self.stream, attr)
PLUGIN_NAME = __version.PLUGIN_NAME PLUGIN_NAME = __version.PLUGIN_NAME
PLUGIN_VERSION = __version.PLUGIN_VERSION PLUGIN_VERSION = __version.PLUGIN_VERSION
@@ -182,12 +161,8 @@ class DeDRM(FileTypePlugin):
def initialize(self): def initialize(self):
""" """
Dynamic modules can't be imported/loaded from a zipfile. Extracting a couple Python scripts if running on Linux,
So this routine will extract the appropriate just in case we need to run them in Wine.
library for the target OS and copy it to the 'alfcrypto' subdirectory of
calibre's configuration directory. That 'alfcrypto' directory is then
inserted into the syspath (as the very first entry) in the run function
so the CDLL stuff will work in the alfcrypto.py script.
The extraction only happens once per version of the plugin The extraction only happens once per version of the plugin
Also perform upgrade of preferences once per version Also perform upgrade of preferences once per version
@@ -208,15 +183,12 @@ class DeDRM(FileTypePlugin):
os.mkdir(self.alfdir) os.mkdir(self.alfdir)
# only continue if we've never run this version of the plugin before # only continue if we've never run this version of the plugin before
self.verdir = os.path.join(self.maindir,PLUGIN_VERSION) self.verdir = os.path.join(self.maindir,PLUGIN_VERSION)
if not os.path.exists(self.verdir): if not os.path.exists(self.verdir) and not iswindows and not isosx:
if iswindows:
names = ["alfcrypto.dll","alfcrypto64.dll"] names = ["kindlekey.py","adobekey.py","ignoblekeyNookStudy.py","utilities.py","argv_utils.py"]
elif isosx:
names = ["libalfcrypto.dylib"]
else:
names = ["libalfcrypto32.so","libalfcrypto64.so","kindlekey.py","adobekey.py","subasyncio.py"]
lib_dict = self.load_resources(names) lib_dict = self.load_resources(names)
print("{0} v{1}: Copying needed library files from plugin's zip".format(PLUGIN_NAME, PLUGIN_VERSION)) print("{0} v{1}: Copying needed Python scripts from plugin's zip".format(PLUGIN_NAME, PLUGIN_VERSION))
for entry, data in lib_dict.items(): for entry, data in lib_dict.items():
file_path = os.path.join(self.alfdir, entry) file_path = os.path.join(self.alfdir, entry)
@@ -228,7 +200,7 @@ class DeDRM(FileTypePlugin):
try: try:
open(file_path,'wb').write(data) open(file_path,'wb').write(data)
except: except:
print("{0} v{1}: Exception when copying needed library files".format(PLUGIN_NAME, PLUGIN_VERSION)) print("{0} v{1}: Exception when copying needed python scripts".format(PLUGIN_NAME, PLUGIN_VERSION))
traceback.print_exc() traceback.print_exc()
pass pass
@@ -511,10 +483,10 @@ class DeDRM(FileTypePlugin):
continue continue
# Found matching key # Found matching key
userkey = codecs.decode(userkeyhex, 'hex')
print("{0} v{1}: Trying UUID-matched encryption key {2:s}".format(PLUGIN_NAME, PLUGIN_VERSION, keyname)) print("{0} v{1}: Trying UUID-matched encryption key {2:s}".format(PLUGIN_NAME, PLUGIN_VERSION, keyname))
of = self.temporary_file(".epub") of = self.temporary_file(".epub")
try: try:
userkey = codecs.decode(userkeyhex, 'hex')
result = ineptepub.decryptBook(userkey, inf.name, of.name) result = ineptepub.decryptBook(userkey, inf.name, of.name)
of.close() of.close()
if result == 0: if result == 0:
@@ -531,12 +503,13 @@ class DeDRM(FileTypePlugin):
# Attempt to decrypt epub with each encryption key (generated or provided). # Attempt to decrypt epub with each encryption key (generated or provided).
for keyname, userkeyhex in dedrmprefs['adeptkeys'].items(): for keyname, userkeyhex in dedrmprefs['adeptkeys'].items():
userkey = codecs.decode(userkeyhex, 'hex')
print("{0} v{1}: Trying Encryption key {2:s}".format(PLUGIN_NAME, PLUGIN_VERSION, keyname)) print("{0} v{1}: Trying Encryption key {2:s}".format(PLUGIN_NAME, PLUGIN_VERSION, keyname))
of = self.temporary_file(".epub") of = self.temporary_file(".epub")
# Give the user key, ebook and TemporaryPersistent file to the decryption function. # Give the user key, ebook and TemporaryPersistent file to the decryption function.
try: try:
userkey = codecs.decode(userkeyhex, 'hex')
result = ineptepub.decryptBook(userkey, inf.name, of.name) result = ineptepub.decryptBook(userkey, inf.name, of.name)
except ineptepub.ADEPTNewVersionError: except ineptepub.ADEPTNewVersionError:
print("{0} v{1}: Book uses unsupported (too new) Adobe DRM.".format(PLUGIN_NAME, PLUGIN_VERSION, time.time()-self.starttime)) print("{0} v{1}: Book uses unsupported (too new) Adobe DRM.".format(PLUGIN_NAME, PLUGIN_VERSION, time.time()-self.starttime))
@@ -673,11 +646,11 @@ class DeDRM(FileTypePlugin):
continue continue
# Found matching key # Found matching key
userkey = codecs.decode(userkeyhex, 'hex')
print("{0} v{1}: Trying UUID-matched encryption key {2:s}".format(PLUGIN_NAME, PLUGIN_VERSION, keyname)) print("{0} v{1}: Trying UUID-matched encryption key {2:s}".format(PLUGIN_NAME, PLUGIN_VERSION, keyname))
of = self.temporary_file(".pdf") of = self.temporary_file(".pdf")
try: try:
userkey = codecs.decode(userkeyhex, 'hex')
result = ineptpdf.decryptBook(userkey, path_to_ebook, of.name) result = ineptpdf.decryptBook(userkey, path_to_ebook, of.name)
of.close() of.close()
if result == 0: if result == 0:

View File

@@ -1,77 +0,0 @@
# I think this file is unused?
import sys
import tkinter
import tkinter.constants
class ActivityBar(tkinter.Frame):
def __init__(self, master, length=300, height=20, barwidth=15, interval=50, bg='white', fillcolor='orchid1',\
bd=2, relief=tkinter.constants.GROOVE, *args, **kw):
tkinter.Frame.__init__(self, master, bg=bg, width=length, height=height, *args, **kw)
self._master = master
self._interval = interval
self._maximum = length
self._startx = 0
self._barwidth = barwidth
self._bardiv = length / barwidth
if self._bardiv < 10:
self._bardiv = 10
stopx = self._startx + self._barwidth
if stopx > self._maximum:
stopx = self._maximum
# self._canv = Tkinter.Canvas(self, bg=self['bg'], width=self['width'], height=self['height'],\
# highlightthickness=0, relief='flat', bd=0)
self._canv = tkinter.Canvas(self, bg=self['bg'], width=self['width'], height=self['height'],\
highlightthickness=0, relief=relief, bd=bd)
self._canv.pack(fill='both', expand=1)
self._rect = self._canv.create_rectangle(0, 0, self._canv.winfo_reqwidth(), self._canv.winfo_reqheight(), fill=fillcolor, width=0)
self._set()
self.bind('<Configure>', self._update_coords)
self._running = False
def _update_coords(self, event):
'''Updates the position of the rectangle inside the canvas when the size of
the widget gets changed.'''
# looks like we have to call update_idletasks() twice to make sure
# to get the results we expect
self._canv.update_idletasks()
self._maximum = self._canv.winfo_width()
self._startx = 0
self._barwidth = self._maximum / self._bardiv
if self._barwidth < 2:
self._barwidth = 2
stopx = self._startx + self._barwidth
if stopx > self._maximum:
stopx = self._maximum
self._canv.coords(self._rect, 0, 0, stopx, self._canv.winfo_height())
self._canv.update_idletasks()
def _set(self):
if self._startx < 0:
self._startx = 0
if self._startx > self._maximum:
self._startx = self._startx % self._maximum
stopx = self._startx + self._barwidth
if stopx > self._maximum:
stopx = self._maximum
self._canv.coords(self._rect, self._startx, 0, stopx, self._canv.winfo_height())
self._canv.update_idletasks()
def start(self):
self._running = True
self.after(self._interval, self._step)
def stop(self):
self._running = False
self._set()
def _step(self):
if self._running:
stepsize = self._barwidth / 4
if stepsize < 2:
stepsize = 2
self._startx += stepsize
self._set()
self.after(self._interval, self._step)

View File

@@ -1,30 +0,0 @@
#!/usr/bin/env python
# vim:ts=4:sw=4:softtabstop=4:smarttab:expandtab
# I think this file is unused?
import tkinter
import tkinter.constants
# basic scrolled text widget
class ScrolledText(tkinter.Text):
def __init__(self, master=None, **kw):
self.frame = tkinter.Frame(master)
self.vbar = tkinter.Scrollbar(self.frame)
self.vbar.pack(side=tkinter.constants.RIGHT, fill=tkinter.constants.Y)
kw.update({'yscrollcommand': self.vbar.set})
tkinter.Text.__init__(self, self.frame, **kw)
self.pack(side=tkinter.constants.LEFT, fill=tkinter.constants.BOTH, expand=True)
self.vbar['command'] = self.yview
# Copy geometry methods of self.frame without overriding Text
# methods = hack!
text_meths = list(vars(tkinter.Text).keys())
methods = list(vars(tkinter.Pack).keys()) + list(vars(tkinter.Grid).keys()) + list(vars(tkinter.Place).keys())
methods = set(methods).difference(text_meths)
for m in methods:
if m[0] != '_' and m != 'config' and m != 'configure':
setattr(self, m, getattr(self.frame, m))
def __str__(self):
return str(self.frame)

View File

@@ -45,29 +45,10 @@ import sys, os, struct, getopt
from base64 import b64decode from base64 import b64decode
# Wrap a stream so that output gets flushed immediately
# and also make sure that any unicode strings get from utilities import SafeUnbuffered
# encoded using "replace" before writing them. from argv_utils import unicode_argv
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,str) or isinstance(data,unicode):
# str for Python3, unicode for Python2
data = data.encode(self.encoding,"replace")
try:
buffer = getattr(self.stream, 'buffer', self.stream)
# self.stream.buffer for Python3, self.stream for Python2
buffer.write(data)
buffer.flush()
except:
# We can do nothing if a write fails
raise
def __getattr__(self, attr):
return getattr(self.stream, attr)
try: try:
from calibre.constants import iswindows, isosx from calibre.constants import iswindows, isosx
@@ -75,41 +56,6 @@ except:
iswindows = sys.platform.startswith('win') iswindows = sys.platform.startswith('win')
isosx = sys.platform.startswith('darwin') isosx = sys.platform.startswith('darwin')
def unicode_argv():
if iswindows:
# Uses shell32.GetCommandLineArgvW to get sys.argv as a list of Unicode
# strings.
# Versions 2.x of Python don't support Unicode in sys.argv on
# Windows, with the underlying Windows API instead replacing multi-byte
# characters with '?'. So use shell32.GetCommandLineArgvW to get sys.argv
# as a list of Unicode strings and encode them as utf-8
from ctypes import POINTER, byref, cdll, c_int, windll
from ctypes.wintypes import LPCWSTR, LPWSTR
GetCommandLineW = cdll.kernel32.GetCommandLineW
GetCommandLineW.argtypes = []
GetCommandLineW.restype = LPCWSTR
CommandLineToArgvW = windll.shell32.CommandLineToArgvW
CommandLineToArgvW.argtypes = [LPCWSTR, POINTER(c_int)]
CommandLineToArgvW.restype = POINTER(LPWSTR)
cmd = GetCommandLineW()
argc = c_int(0)
argv = CommandLineToArgvW(cmd, byref(argc))
if argc.value > 0:
# Remove Python executable and commands if present
start = argc.value - len(sys.argv)
return [argv[i] for i in
range(start, argc.value)]
# if we don't have any arguments at all, just pass back script name
# this should never happen
return ["adobekey.py"]
else:
argvencoding = sys.stdin.encoding or "utf-8"
return [arg if (isinstance(arg, str) or isinstance(arg,unicode)) else str(arg, argvencoding) for arg in sys.argv]
class ADEPTError(Exception): class ADEPTError(Exception):
pass pass
@@ -507,7 +453,7 @@ def usage(progname):
def cli_main(): def cli_main():
sys.stdout=SafeUnbuffered(sys.stdout) sys.stdout=SafeUnbuffered(sys.stdout)
sys.stderr=SafeUnbuffered(sys.stderr) sys.stderr=SafeUnbuffered(sys.stderr)
argv=unicode_argv() argv=unicode_argv("adobekey.py")
progname = os.path.basename(argv[0]) progname = os.path.basename(argv[0])
print("{0} v{1}\nCopyright © 2009-2020 i♥cabbages, Apprentice Harper et al.".format(progname,__version__)) print("{0} v{1}\nCopyright © 2009-2020 i♥cabbages, Apprentice Harper et al.".format(progname,__version__))
@@ -585,7 +531,7 @@ def gui_main():
self.text.insert(tkinter.constants.END, text) self.text.insert(tkinter.constants.END, text)
argv=unicode_argv() argv=unicode_argv("adobekey.py")
root = tkinter.Tk() root = tkinter.Tk()
root.withdraw() root.withdraw()
progpath, progname = os.path.split(argv[0]) progpath, progname = os.path.split(argv[0])

Binary file not shown.

View File

@@ -8,260 +8,90 @@
# pbkdf2.py Copyright © 2009 Daniel Holth <dholth@fastmail.fm> # pbkdf2.py Copyright © 2009 Daniel Holth <dholth@fastmail.fm>
# pbkdf2.py This code may be freely used and modified for any purpose. # pbkdf2.py This code may be freely used and modified for any purpose.
import sys, os
import hmac import hmac
from struct import pack from struct import pack
import hashlib import hashlib
import aescbc
# interface to needed routines libalfcrypto class Pukall_Cipher(object):
def _load_libalfcrypto(): def __init__(self):
import ctypes self.key = None
from ctypes import CDLL, byref, POINTER, c_void_p, c_char_p, c_int, c_long, \
Structure, c_ulong, create_string_buffer, addressof, string_at, cast, sizeof
pointer_size = ctypes.sizeof(ctypes.c_voidp) def PC1(self, key, src, decryption=True):
name_of_lib = None sum1 = 0;
if sys.platform.startswith('darwin'): sum2 = 0;
name_of_lib = 'libalfcrypto.dylib' keyXorVal = 0;
elif sys.platform.startswith('win'): if len(key)!=16:
if pointer_size == 4: raise Exception("PC1: Bad key length")
name_of_lib = 'alfcrypto.dll' wkey = []
else: for i in range(8):
name_of_lib = 'alfcrypto64.dll' wkey.append(key[i*2]<<8 | key[i*2+1])
else: dst = bytearray(len(src))
if pointer_size == 4: for i in range(len(src)):
name_of_lib = 'libalfcrypto32.so' temp1 = 0;
else: byteXorVal = 0;
name_of_lib = 'libalfcrypto64.so' for j in range(8):
temp1 ^= wkey[j]
# hard code to local location for libalfcrypto sum2 = (sum2+j)*20021 + sum1
libalfcrypto = os.path.join(sys.path[0],name_of_lib) sum1 = (temp1*346)&0xFFFF
if not os.path.isfile(libalfcrypto): sum2 = (sum2+sum1)&0xFFFF
libalfcrypto = os.path.join(sys.path[0], 'lib', name_of_lib) temp1 = (temp1*20021+1)&0xFFFF
if not os.path.isfile(libalfcrypto): byteXorVal ^= temp1 ^ sum2
libalfcrypto = os.path.join('.',name_of_lib) curByte = src[i]
if not os.path.isfile(libalfcrypto): if not decryption:
raise Exception('libalfcrypto not found at %s' % libalfcrypto) keyXorVal = curByte * 257;
curByte = ((curByte ^ (byteXorVal >> 8)) ^ byteXorVal) & 0xFF
libalfcrypto = CDLL(libalfcrypto)
c_char_pp = POINTER(c_char_p)
c_int_p = POINTER(c_int)
def F(restype, name, argtypes):
func = getattr(libalfcrypto, name)
func.restype = restype
func.argtypes = argtypes
return func
# aes cbc decryption
#
# struct aes_key_st {
# unsigned long rd_key[4 *(AES_MAXNR + 1)];
# int rounds;
# };
#
# typedef struct aes_key_st AES_KEY;
#
# int AES_set_decrypt_key(const unsigned char *userKey, const int bits, AES_KEY *key);
#
#
# void AES_cbc_encrypt(const unsigned char *in, unsigned char *out,
# const unsigned long length, const AES_KEY *key,
# unsigned char *ivec, const int enc);
AES_MAXNR = 14
class AES_KEY(Structure):
_fields_ = [('rd_key', c_long * (4 * (AES_MAXNR + 1))), ('rounds', c_int)]
AES_KEY_p = POINTER(AES_KEY)
AES_cbc_encrypt = F(None, 'AES_cbc_encrypt',[c_char_p, c_char_p, c_ulong, AES_KEY_p, c_char_p, c_int])
AES_set_decrypt_key = F(c_int, 'AES_set_decrypt_key',[c_char_p, c_int, AES_KEY_p])
# Pukall 1 Cipher
# unsigned char *PC1(const unsigned char *key, unsigned int klen, const unsigned char *src,
# unsigned char *dest, unsigned int len, int decryption);
PC1 = F(c_char_p, 'PC1', [c_char_p, c_ulong, c_char_p, c_char_p, c_ulong, c_ulong])
# Topaz Encryption
# typedef struct _TpzCtx {
# unsigned int v[2];
# } TpzCtx;
#
# void topazCryptoInit(TpzCtx *ctx, const unsigned char *key, int klen);
# void topazCryptoDecrypt(const TpzCtx *ctx, const unsigned char *in, unsigned char *out, int len);
class TPZ_CTX(Structure):
_fields_ = [('v', c_long * 2)]
TPZ_CTX_p = POINTER(TPZ_CTX)
topazCryptoInit = F(None, 'topazCryptoInit', [TPZ_CTX_p, c_char_p, c_ulong])
topazCryptoDecrypt = F(None, 'topazCryptoDecrypt', [TPZ_CTX_p, c_char_p, c_char_p, c_ulong])
class AES_CBC(object):
def __init__(self):
self._blocksize = 0
self._keyctx = None
self._iv = 0
def set_decrypt_key(self, userkey, iv):
self._blocksize = len(userkey)
if (self._blocksize != 16) and (self._blocksize != 24) and (self._blocksize != 32) :
raise Exception('AES CBC improper key used')
return
keyctx = self._keyctx = AES_KEY()
self._iv = iv
rv = AES_set_decrypt_key(userkey, len(userkey) * 8, keyctx)
if rv < 0:
raise Exception('Failed to initialize AES CBC key')
def decrypt(self, data):
out = create_string_buffer(len(data))
mutable_iv = create_string_buffer(self._iv, len(self._iv))
rv = AES_cbc_encrypt(data, out, len(data), self._keyctx, mutable_iv, 0)
if rv == 0:
raise Exception('AES CBC decryption failed')
return out.raw
class Pukall_Cipher(object):
def __init__(self):
self.key = None
def PC1(self, key, src, decryption=True):
self.key = key
out = create_string_buffer(len(src))
de = 0
if decryption: if decryption:
de = 1 keyXorVal = curByte * 257;
rv = PC1(key, len(key), src, out, len(src), de) for j in range(8):
return out.raw wkey[j] ^= keyXorVal;
dst[i] = curByte
return bytes(dst)
class Topaz_Cipher(object): class Topaz_Cipher(object):
def __init__(self): def __init__(self):
self._ctx = None self._ctx = None
def ctx_init(self, key): def ctx_init(self, key):
tpz_ctx = self._ctx = TPZ_CTX() ctx1 = 0x0CAFFE19E
topazCryptoInit(tpz_ctx, key, len(key)) if isinstance(key, str):
return tpz_ctx key = key.encode('latin-1')
for keyByte in key:
ctx2 = ctx1
ctx1 = ((((ctx1 >>2) * (ctx1 >>7))&0xFFFFFFFF) ^ (keyByte * keyByte * 0x0F902007)& 0xFFFFFFFF )
self._ctx = [ctx1, ctx2]
return [ctx1,ctx2]
def decrypt(self, data, ctx=None): def decrypt(self, data, ctx=None):
if ctx == None: if ctx == None:
ctx = self._ctx ctx = self._ctx
out = create_string_buffer(len(data)) ctx1 = ctx[0]
topazCryptoDecrypt(ctx, data, out, len(data)) ctx2 = ctx[1]
return out.raw plainText = ""
if isinstance(data, str):
data = data.encode('latin-1')
for dataByte in data:
m = (dataByte ^ ((ctx1 >> 3) &0xFF) ^ ((ctx2<<3) & 0xFF)) &0xFF
ctx2 = ctx1
ctx1 = (((ctx1 >> 2) * (ctx1 >> 7)) &0xFFFFFFFF) ^((m * m * 0x0F902007) &0xFFFFFFFF)
plainText += chr(m)
return plainText
print("Using Library AlfCrypto DLL/DYLIB/SO") class AES_CBC(object):
return (AES_CBC, Pukall_Cipher, Topaz_Cipher) def __init__(self):
self._key = None
self._iv = None
self.aes = None
def set_decrypt_key(self, userkey, iv):
self._key = userkey
self._iv = iv
self.aes = aescbc.AES_CBC(userkey, aescbc.noPadding(), len(userkey))
def _load_python_alfcrypto(): def decrypt(self, data):
iv = self._iv
import aescbc cleartext = self.aes.decrypt(iv + data)
return cleartext
class Pukall_Cipher(object):
def __init__(self):
self.key = None
def PC1(self, key, src, decryption=True):
sum1 = 0;
sum2 = 0;
keyXorVal = 0;
if len(key)!=16:
raise Exception('Pukall_Cipher: Bad key length.')
wkey = []
for i in range(8):
wkey.append(ord(key[i*2])<<8 | ord(key[i*2+1]))
dst = ""
for i in range(len(src)):
temp1 = 0;
byteXorVal = 0;
for j in range(8):
temp1 ^= wkey[j]
sum2 = (sum2+j)*20021 + sum1
sum1 = (temp1*346)&0xFFFF
sum2 = (sum2+sum1)&0xFFFF
temp1 = (temp1*20021+1)&0xFFFF
byteXorVal ^= temp1 ^ sum2
curByte = ord(src[i])
if not decryption:
keyXorVal = curByte * 257;
curByte = ((curByte ^ (byteXorVal >> 8)) ^ byteXorVal) & 0xFF
if decryption:
keyXorVal = curByte * 257;
for j in range(8):
wkey[j] ^= keyXorVal;
dst+=chr(curByte)
return dst
class Topaz_Cipher(object):
def __init__(self):
self._ctx = None
def ctx_init(self, key):
ctx1 = 0x0CAFFE19E
if isinstance(key, str):
key = key.encode('latin-1')
for keyByte in key:
ctx2 = ctx1
ctx1 = ((((ctx1 >>2) * (ctx1 >>7))&0xFFFFFFFF) ^ (keyByte * keyByte * 0x0F902007)& 0xFFFFFFFF )
self._ctx = [ctx1, ctx2]
return [ctx1,ctx2]
def decrypt(self, data, ctx=None):
if ctx == None:
ctx = self._ctx
ctx1 = ctx[0]
ctx2 = ctx[1]
plainText = ""
if isinstance(data, str):
data = data.encode('latin-1')
for dataByte in data:
m = (dataByte ^ ((ctx1 >> 3) &0xFF) ^ ((ctx2<<3) & 0xFF)) &0xFF
ctx2 = ctx1
ctx1 = (((ctx1 >> 2) * (ctx1 >> 7)) &0xFFFFFFFF) ^((m * m * 0x0F902007) &0xFFFFFFFF)
plainText += chr(m)
return plainText
class AES_CBC(object):
def __init__(self):
self._key = None
self._iv = None
self.aes = None
def set_decrypt_key(self, userkey, iv):
self._key = userkey
self._iv = iv
self.aes = aescbc.AES_CBC(userkey, aescbc.noPadding(), len(userkey))
def decrypt(self, data):
iv = self._iv
cleartext = self.aes.decrypt(iv + data)
return cleartext
print("Using Library AlfCrypto Python")
return (AES_CBC, Pukall_Cipher, Topaz_Cipher)
def _load_crypto():
AES_CBC = Pukall_Cipher = Topaz_Cipher = None
cryptolist = (_load_libalfcrypto, _load_python_alfcrypto)
for loader in cryptolist:
try:
AES_CBC, Pukall_Cipher, Topaz_Cipher = loader()
break
except (ImportError, Exception):
pass
return AES_CBC, Pukall_Cipher, Topaz_Cipher
AES_CBC, Pukall_Cipher, Topaz_Cipher = _load_crypto()
class KeyIVGen(object): class KeyIVGen(object):

Binary file not shown.

Binary file not shown.

View File

@@ -41,71 +41,6 @@ except ImportError:
# Routines common to Mac and PC # Routines common to Mac and PC
# 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,str) or isinstance(data,unicode):
# str for Python3, unicode for Python2
data = data.encode(self.encoding,"replace")
try:
buffer = getattr(self.stream, 'buffer', self.stream)
# self.stream.buffer for Python3, self.stream for Python2
buffer.write(data)
buffer.flush()
except:
# We can do nothing if a write fails
raise
def __getattr__(self, attr):
return getattr(self.stream, attr)
try:
from calibre.constants import iswindows, isosx
except:
iswindows = sys.platform.startswith('win')
isosx = sys.platform.startswith('darwin')
def unicode_argv():
if iswindows:
# Uses shell32.GetCommandLineArgvW to get sys.argv as a list of Unicode
# strings.
# Versions 2.x of Python don't support Unicode in sys.argv on
# Windows, with the underlying Windows API instead replacing multi-byte
# characters with '?'. So use shell32.GetCommandLineArgvW to get sys.argv
# as a list of Unicode strings and encode them as utf-8
from ctypes import POINTER, byref, cdll, c_int, windll
from ctypes.wintypes import LPCWSTR, LPWSTR
GetCommandLineW = cdll.kernel32.GetCommandLineW
GetCommandLineW.argtypes = []
GetCommandLineW.restype = LPCWSTR
CommandLineToArgvW = windll.shell32.CommandLineToArgvW
CommandLineToArgvW.argtypes = [LPCWSTR, POINTER(c_int)]
CommandLineToArgvW.restype = POINTER(LPWSTR)
cmd = GetCommandLineW()
argc = c_int(0)
argv = CommandLineToArgvW(cmd, byref(argc))
if argc.value > 0:
# Remove Python executable and commands if present
start = argc.value - len(sys.argv)
return [argv[i] for i in
range(start, argc.value)]
# if we don't have any arguments at all, just pass back script name
# this should never happen
return ["kindlekey.py"]
else:
argvencoding = sys.stdin.encoding or "utf-8"
return [arg if (isinstance(arg, str) or isinstance(arg,unicode)) else str(arg, argvencoding) for arg in sys.argv]
class DrmException(Exception): class DrmException(Exception):
pass pass
@@ -342,9 +277,7 @@ def usage(progname):
def cli_main(): def cli_main():
sys.stdout=SafeUnbuffered(sys.stdout) argv=sys.argv
sys.stderr=SafeUnbuffered(sys.stderr)
argv=unicode_argv()
progname = os.path.basename(argv[0]) progname = os.path.basename(argv[0])
print("{0} v{1}\nCopyright © 2010-2020 Thom, Apprentice Harper et al.".format(progname,__version__)) print("{0} v{1}\nCopyright © 2010-2020 Thom, Apprentice Harper et al.".format(progname,__version__))

View File

@@ -1,14 +1,17 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import sys, os import sys
import locale
import codecs
import importlib
# get sys.argv arguments and encode them into utf-8 # get sys.argv arguments and encode them into utf-8
def unicode_argv(): def unicode_argv(default_name):
if sys.platform.startswith('win'):
try:
from calibre.constants import iswindows
except:
iswindows = sys.platform.startswith('win')
if iswindows:
# Uses shell32.GetCommandLineArgvW to get sys.argv as a list of Unicode # Uses shell32.GetCommandLineArgvW to get sys.argv as a list of Unicode
# strings. # strings.
@@ -38,50 +41,8 @@ def unicode_argv():
range(start, argc.value)] range(start, argc.value)]
# if we don't have any arguments at all, just pass back script name # if we don't have any arguments at all, just pass back script name
# this should never happen # this should never happen
return ["DeDRM.py"] return [ default_name ]
else: else:
argvencoding = sys.stdin.encoding or "utf-8" argvencoding = sys.stdin.encoding or "utf-8"
return [arg if (isinstance(arg, str) or isinstance(arg,unicode)) else str(arg, argvencoding) for arg in sys.argv] return [arg if (isinstance(arg, str) or isinstance(arg,unicode)) else str(arg, argvencoding) for arg in sys.argv]
def add_cp65001_codec():
try:
codecs.lookup('cp65001')
except LookupError:
codecs.register(
lambda name: name == 'cp65001' and codecs.lookup('utf-8') or None)
return
def set_utf8_default_encoding():
if sys.getdefaultencoding() == 'utf-8':
return
# Regenerate setdefaultencoding.
importlib.reload(sys)
sys.setdefaultencoding('utf-8')
for attr in dir(locale):
if attr[0:3] != 'LC_':
continue
aref = getattr(locale, attr)
try:
locale.setlocale(aref, '')
except locale.Error:
continue
try:
lang = locale.getlocale(aref)[0]
except (TypeError, ValueError):
continue
if lang:
try:
locale.setlocale(aref, (lang, 'UTF-8'))
except locale.Error:
os.environ[attr] = lang + '.UTF-8'
try:
locale.setlocale(locale.LC_ALL, '')
except locale.Error:
pass
return

View File

@@ -1,213 +0,0 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# vim:ts=4:sw=4:softtabstop=4:smarttab:expandtab
# to work around tk_chooseDirectory not properly returning unicode paths on Windows
# need to use a dialog that can be hacked up to actually return full unicode paths
# originally based on AskFolder from EasyDialogs for Windows but modified to fix it
# to actually use unicode for path
# The original license for EasyDialogs is as follows
#
# Copyright (c) 2003-2005 Jimmy Retzlaff
#
# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the "Software"),
# to deal in the Software without restriction, including without limitation
# the rights to use, copy, modify, merge, publish, distribute, sublicense,
# and/or sell copies of the Software, and to permit persons to whom the
# Software is furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
# DEALINGS IN THE SOFTWARE.
# Adjusted for Python 3, September 2020
"""
AskFolder(...) -- Ask the user to select a folder Windows specific
"""
import os
import ctypes
from ctypes import POINTER, byref, cdll, c_int, windll
from ctypes.wintypes import LPCWSTR, LPWSTR
import ctypes.wintypes as wintypes
__all__ = ['AskFolder']
# Load required Windows DLLs
ole32 = ctypes.windll.ole32
shell32 = ctypes.windll.shell32
user32 = ctypes.windll.user32
# Windows Constants
BFFM_INITIALIZED = 1
BFFM_SETOKTEXT = 1129
BFFM_SETSELECTIONA = 1126
BFFM_SETSELECTIONW = 1127
BIF_EDITBOX = 16
BS_DEFPUSHBUTTON = 1
CB_ADDSTRING = 323
CB_GETCURSEL = 327
CB_SETCURSEL = 334
CDM_SETCONTROLTEXT = 1128
EM_GETLINECOUNT = 186
EM_GETMARGINS = 212
EM_POSFROMCHAR = 214
EM_SETSEL = 177
GWL_STYLE = -16
IDC_STATIC = -1
IDCANCEL = 2
IDNO = 7
IDOK = 1
IDYES = 6
MAX_PATH = 260
OFN_ALLOWMULTISELECT = 512
OFN_ENABLEHOOK = 32
OFN_ENABLESIZING = 8388608
OFN_ENABLETEMPLATEHANDLE = 128
OFN_EXPLORER = 524288
OFN_OVERWRITEPROMPT = 2
OPENFILENAME_SIZE_VERSION_400 = 76
PBM_GETPOS = 1032
PBM_SETMARQUEE = 1034
PBM_SETPOS = 1026
PBM_SETRANGE = 1025
PBM_SETRANGE32 = 1030
PBS_MARQUEE = 8
PM_REMOVE = 1
SW_HIDE = 0
SW_SHOW = 5
SW_SHOWNORMAL = 1
SWP_NOACTIVATE = 16
SWP_NOMOVE = 2
SWP_NOSIZE = 1
SWP_NOZORDER = 4
VER_PLATFORM_WIN32_NT = 2
WM_COMMAND = 273
WM_GETTEXT = 13
WM_GETTEXTLENGTH = 14
WM_INITDIALOG = 272
WM_NOTIFY = 78
# Windows function prototypes
BrowseCallbackProc = ctypes.WINFUNCTYPE(ctypes.c_int, wintypes.HWND, ctypes.c_uint, wintypes.LPARAM, wintypes.LPARAM)
# Windows types
LPCTSTR = ctypes.c_char_p
LPTSTR = ctypes.c_char_p
LPVOID = ctypes.c_voidp
TCHAR = ctypes.c_char
class BROWSEINFO(ctypes.Structure):
_fields_ = [
("hwndOwner", wintypes.HWND),
("pidlRoot", LPVOID),
("pszDisplayName", LPTSTR),
("lpszTitle", LPCTSTR),
("ulFlags", ctypes.c_uint),
("lpfn", BrowseCallbackProc),
("lParam", wintypes.LPARAM),
("iImage", ctypes.c_int)
]
# Utilities
def CenterWindow(hwnd):
desktopRect = GetWindowRect(user32.GetDesktopWindow())
myRect = GetWindowRect(hwnd)
x = width(desktopRect) // 2 - width(myRect) // 2
y = height(desktopRect) // 2 - height(myRect) // 2
user32.SetWindowPos(hwnd, 0,
desktopRect.left + x,
desktopRect.top + y,
0, 0,
SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOZORDER
)
def GetWindowRect(hwnd):
rect = wintypes.RECT()
user32.GetWindowRect(hwnd, ctypes.byref(rect))
return rect
def width(rect):
return rect.right-rect.left
def height(rect):
return rect.bottom-rect.top
def AskFolder(
message=None,
version=None,
defaultLocation=None,
location=None,
windowTitle=None,
actionButtonLabel=None,
cancelButtonLabel=None,
multiple=None):
"""Display a dialog asking the user for select a folder.
modified to use unicode strings as much as possible
returns unicode path
"""
def BrowseCallback(hwnd, uMsg, lParam, lpData):
if uMsg == BFFM_INITIALIZED:
if actionButtonLabel:
label = str(actionButtonLabel, errors='replace')
user32.SendMessageW(hwnd, BFFM_SETOKTEXT, 0, label)
if cancelButtonLabel:
label = str(cancelButtonLabel, errors='replace')
cancelButton = user32.GetDlgItem(hwnd, IDCANCEL)
if cancelButton:
user32.SetWindowTextW(cancelButton, label)
if windowTitle:
title = str(windowTitle, errors='replace')
user32.SetWindowTextW(hwnd, title)
if defaultLocation:
user32.SendMessageW(hwnd, BFFM_SETSELECTIONW, 1, defaultLocation.replace('/', '\\'))
if location:
x, y = location
desktopRect = wintypes.RECT()
user32.GetWindowRect(0, ctypes.byref(desktopRect))
user32.SetWindowPos(hwnd, 0,
desktopRect.left + x,
desktopRect.top + y, 0, 0,
SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOZORDER)
else:
CenterWindow(hwnd)
return 0
# This next line is needed to prevent gc of the callback
callback = BrowseCallbackProc(BrowseCallback)
browseInfo = BROWSEINFO()
browseInfo.pszDisplayName = ctypes.c_char_p('\0' * (MAX_PATH+1))
browseInfo.lpszTitle = message
browseInfo.lpfn = callback
pidl = shell32.SHBrowseForFolder(ctypes.byref(browseInfo))
if not pidl:
result = None
else:
path = LPCWSTR(" " * (MAX_PATH+1))
shell32.SHGetPathFromIDListW(pidl, path)
ole32.CoTaskMemFree(pidl)
result = path.value
return result

View File

@@ -5,36 +5,13 @@
# For use with Topaz Scripts Version 2.6 # For use with Topaz Scripts Version 2.6
# Python 3, September 2020 # Python 3, September 2020
# Wrap a stream so that output gets flushed immediately from utilities import SafeUnbuffered
# 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,str) or isinstance(data,unicode):
# str for Python3, unicode for Python2
data = data.encode(self.encoding,"replace")
try:
buffer = getattr(self.stream, 'buffer', self.stream)
# self.stream.buffer for Python3, self.stream for Python2
buffer.write(data)
buffer.flush()
except:
# We can do nothing if a write fails
raise
def __getattr__(self, attr):
return getattr(self.stream, attr)
import sys import sys
import csv import csv
import os import os
import getopt import getopt
from struct import pack from struct import pack, unpack
from struct import unpack
class TpzDRMError(Exception): class TpzDRMError(Exception):
pass pass

View File

@@ -25,6 +25,7 @@ import traceback
import zlib import zlib
import zipfile import zipfile
from zipfile import ZipInfo, ZipFile, ZIP_STORED, ZIP_DEFLATED from zipfile import ZipInfo, ZipFile, ZIP_STORED, ZIP_DEFLATED
from zeroedzipinfo import ZeroedZipInfo
from contextlib import closing from contextlib import closing
from lxml import etree from lxml import etree
import itertools import itertools
@@ -298,13 +299,21 @@ def decryptFontsBook(inpath, outpath):
zi.internal_attr = oldzi.internal_attr zi.internal_attr = oldzi.internal_attr
# external attributes are dependent on the create system, so copy both. # external attributes are dependent on the create system, so copy both.
zi.external_attr = oldzi.external_attr zi.external_attr = oldzi.external_attr
zi.volume = oldzi.volume
zi.create_system = oldzi.create_system zi.create_system = oldzi.create_system
zi.create_version = oldzi.create_version
if any(ord(c) >= 128 for c in path) or any(ord(c) >= 128 for c in zi.comment): if any(ord(c) >= 128 for c in path) or any(ord(c) >= 128 for c in zi.comment):
# If the file name or the comment contains any non-ASCII char, set the UTF8-flag # If the file name or the comment contains any non-ASCII char, set the UTF8-flag
zi.flag_bits |= 0x800 zi.flag_bits |= 0x800
except: except:
pass pass
# Python 3 has a bug where the external_attr is reset to `0o600 << 16`
# if it's NULL, so we need a workaround:
if zi.external_attr == 0:
zi = ZeroedZipInfo(zi)
if path == "mimetype": if path == "mimetype":
outf.writestr(zi, inf.read('mimetype')) outf.writestr(zi, inf.read('mimetype'))
elif path == "META-INF/encryption.xml": elif path == "META-INF/encryption.xml":

View File

@@ -53,75 +53,13 @@ import sys, struct, os, traceback
import zlib import zlib
import zipfile import zipfile
import xml.etree.ElementTree as etree import xml.etree.ElementTree as etree
from argv_utils import unicode_argv
NSMAP = {'adept': 'http://ns.adobe.com/adept', NSMAP = {'adept': 'http://ns.adobe.com/adept',
'enc': 'http://www.w3.org/2001/04/xmlenc#'} 'enc': 'http://www.w3.org/2001/04/xmlenc#'}
# Wrap a stream so that output gets flushed immediately from utilities import SafeUnbuffered
# 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,str) or isinstance(data,unicode):
# str for Python3, unicode for Python2
data = data.encode(self.encoding,"replace")
try:
buffer = getattr(self.stream, 'buffer', self.stream)
# self.stream.buffer for Python3, self.stream for Python2
buffer.write(data)
buffer.flush()
except:
# We can do nothing if a write fails
raise
def __getattr__(self, attr):
return getattr(self.stream, attr)
try:
from calibre.constants import iswindows, isosx
except:
iswindows = sys.platform.startswith('win')
isosx = sys.platform.startswith('darwin')
def unicode_argv():
if iswindows:
# Uses shell32.GetCommandLineArgvW to get sys.argv as a list of Unicode
# strings.
# Versions 2.x of Python don't support Unicode in sys.argv on
# Windows, with the underlying Windows API instead replacing multi-byte
# characters with '?'. So use shell32.GetCommandLineArgvW to get sys.argv
# as a list of Unicode strings and encode them as utf-8
from ctypes import POINTER, byref, cdll, c_int, windll
from ctypes.wintypes import LPCWSTR, LPWSTR
GetCommandLineW = cdll.kernel32.GetCommandLineW
GetCommandLineW.argtypes = []
GetCommandLineW.restype = LPCWSTR
CommandLineToArgvW = windll.shell32.CommandLineToArgvW
CommandLineToArgvW.argtypes = [LPCWSTR, POINTER(c_int)]
CommandLineToArgvW.restype = POINTER(LPWSTR)
cmd = GetCommandLineW()
argc = c_int(0)
argv = CommandLineToArgvW(cmd, byref(argc))
if argc.value > 0:
# Remove Python executable and commands if present
start = argc.value - len(sys.argv)
return [argv[i] for i in
range(start, argc.value)]
# if we don't have any arguments at all, just pass back script name
# this should never happen
return ["epubtest.py"]
else:
argvencoding = sys.stdin.encoding or "utf-8"
return [arg if (isinstance(arg, str) or isinstance(arg,unicode)) else str(arg, argvencoding) for arg in sys.argv]
_FILENAME_LEN_OFFSET = 26 _FILENAME_LEN_OFFSET = 26
_EXTRA_LEN_OFFSET = 28 _EXTRA_LEN_OFFSET = 28
@@ -219,7 +157,7 @@ def encryption(infile):
return encryption return encryption
def main(): def main():
argv=unicode_argv() argv=unicode_argv("epubtest.py")
if len(argv) < 2: if len(argv) < 2:
print("Give an ePub file as a parameter.") print("Give an ePub file as a parameter.")
else: else:

View File

@@ -16,6 +16,7 @@ Removes various watermarks from EPUB files
import traceback import traceback
from zipfile import ZipInfo, ZipFile, ZIP_STORED, ZIP_DEFLATED from zipfile import ZipInfo, ZipFile, ZIP_STORED, ZIP_DEFLATED
from zeroedzipinfo import ZeroedZipInfo
from contextlib import closing from contextlib import closing
from lxml import etree from lxml import etree
import re import re
@@ -133,13 +134,22 @@ def removeHTMLwatermarks(object, path_to_ebook):
zi.extra = oldzi.extra zi.extra = oldzi.extra
zi.internal_attr = oldzi.internal_attr zi.internal_attr = oldzi.internal_attr
zi.external_attr = oldzi.external_attr zi.external_attr = oldzi.external_attr
zi.volume = oldzi.volume
zi.create_system = oldzi.create_system zi.create_system = oldzi.create_system
zi.create_version = oldzi.create_version
if any(ord(c) >= 128 for c in path) or any(ord(c) >= 128 for c in zi.comment): if any(ord(c) >= 128 for c in path) or any(ord(c) >= 128 for c in zi.comment):
# If the file name or the comment contains any non-ASCII char, set the UTF8-flag # If the file name or the comment contains any non-ASCII char, set the UTF8-flag
zi.flag_bits |= 0x800 zi.flag_bits |= 0x800
except: except:
pass pass
# Python 3 has a bug where the external_attr is reset to `0o600 << 16`
# if it's NULL, so we need a workaround:
if zi.external_attr == 0:
zi = ZeroedZipInfo(zi)
outf.writestr(zi, data) outf.writestr(zi, data)
except: except:
traceback.print_exc() traceback.print_exc()
@@ -249,13 +259,21 @@ def removeOPFwatermarks(object, path_to_ebook):
zi.extra = oldzi.extra zi.extra = oldzi.extra
zi.internal_attr = oldzi.internal_attr zi.internal_attr = oldzi.internal_attr
zi.external_attr = oldzi.external_attr zi.external_attr = oldzi.external_attr
zi.volume = oldzi.volume
zi.create_system = oldzi.create_system zi.create_system = oldzi.create_system
zi.create_version = oldzi.create_version
if any(ord(c) >= 128 for c in path) or any(ord(c) >= 128 for c in zi.comment): if any(ord(c) >= 128 for c in path) or any(ord(c) >= 128 for c in zi.comment):
# If the file name or the comment contains any non-ASCII char, set the UTF8-flag # If the file name or the comment contains any non-ASCII char, set the UTF8-flag
zi.flag_bits |= 0x800 zi.flag_bits |= 0x800
except: except:
pass pass
# Python 3 has a bug where the external_attr is reset to `0o600 << 16`
# if it's NULL, so we need a workaround:
if zi.external_attr == 0:
zi = ZeroedZipInfo(zi)
outf.writestr(zi, data) outf.writestr(zi, data)
except: except:
traceback.print_exc() traceback.print_exc()
@@ -301,13 +319,21 @@ def removeCDPwatermark(object, path_to_ebook):
zi.extra = oldzi.extra zi.extra = oldzi.extra
zi.internal_attr = oldzi.internal_attr zi.internal_attr = oldzi.internal_attr
zi.external_attr = oldzi.external_attr zi.external_attr = oldzi.external_attr
zi.volume = oldzi.volume
zi.create_system = oldzi.create_system zi.create_system = oldzi.create_system
zi.create_version = oldzi.create_version
if any(ord(c) >= 128 for c in path) or any(ord(c) >= 128 for c in zi.comment): if any(ord(c) >= 128 for c in path) or any(ord(c) >= 128 for c in zi.comment):
# If the file name or the comment contains any non-ASCII char, set the UTF8-flag # If the file name or the comment contains any non-ASCII char, set the UTF8-flag
zi.flag_bits |= 0x800 zi.flag_bits |= 0x800
except: except:
pass pass
# Python 3 has a bug where the external_attr is reset to `0o600 << 16`
# if it's NULL, so we need a workaround:
if zi.external_attr == 0:
zi = ZeroedZipInfo(zi)
outf.writestr(zi, data) outf.writestr(zi, data)
print("Watermark: Successfully removed cdp.info watermark") print("Watermark: Successfully removed cdp.info watermark")

View File

@@ -79,68 +79,12 @@ except ImportError:
#@@CALIBRE_COMPAT_CODE@@ #@@CALIBRE_COMPAT_CODE@@
# Wrap a stream so that output gets flushed immediately from utilities import SafeUnbuffered
# 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,str) or isinstance(data,unicode):
# str for Python3, unicode for Python2
data = data.encode(self.encoding,"replace")
try:
buffer = getattr(self.stream, 'buffer', self.stream)
# self.stream.buffer for Python3, self.stream for Python2
buffer.write(data)
buffer.flush()
except:
# We can do nothing if a write fails
raise
def __getattr__(self, attr):
return getattr(self.stream, attr)
iswindows = sys.platform.startswith('win') iswindows = sys.platform.startswith('win')
isosx = sys.platform.startswith('darwin') isosx = sys.platform.startswith('darwin')
def unicode_argv(): from argv_utils import unicode_argv
if iswindows:
# Uses shell32.GetCommandLineArgvW to get sys.argv as a list of Unicode
# strings.
# Versions 2.x of Python don't support Unicode in sys.argv on
# Windows, with the underlying Windows API instead replacing multi-byte
# characters with '?'.
from ctypes import POINTER, byref, cdll, c_int, windll
from ctypes.wintypes import LPCWSTR, LPWSTR
GetCommandLineW = cdll.kernel32.GetCommandLineW
GetCommandLineW.argtypes = []
GetCommandLineW.restype = LPCWSTR
CommandLineToArgvW = windll.shell32.CommandLineToArgvW
CommandLineToArgvW.argtypes = [LPCWSTR, POINTER(c_int)]
CommandLineToArgvW.restype = POINTER(LPWSTR)
cmd = GetCommandLineW()
argc = c_int(0)
argv = CommandLineToArgvW(cmd, byref(argc))
if argc.value > 0:
# Remove Python executable and commands if present
start = argc.value - len(sys.argv)
return [argv[i] for i in
range(start, argc.value)]
# if we don't have any arguments at all, just pass back script name
# this should never happen
return ["mobidedrm.py"]
else:
argvencoding = sys.stdin.encoding or "utf-8"
return [arg if (isinstance(arg, str) or isinstance(arg,unicode)) else str(arg, argvencoding) for arg in sys.argv]
import cgi import cgi
import logging import logging
@@ -507,7 +451,7 @@ def getuser_key(name,cc):
def cli_main(): def cli_main():
print("eRdr2Pml v{0}. Copyright © 20092020 The Dark Reverser et al.".format(__version__)) print("eRdr2Pml v{0}. Copyright © 20092020 The Dark Reverser et al.".format(__version__))
argv=unicode_argv() argv=unicode_argv("erdr2pml.py")
try: try:
opts, args = getopt.getopt(argv[1:], "hp", ["make-pmlz"]) opts, args = getopt.getopt(argv[1:], "hp", ["make-pmlz"])
except getopt.GetoptError as err: except getopt.GetoptError as err:

View File

@@ -4,29 +4,7 @@
# Python 3 for calibre 5.0 # Python 3 for calibre 5.0
from __future__ import print_function from __future__ import print_function
# Wrap a stream so that output gets flushed immediately from utilities import SafeUnbuffered
# 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,str) or isinstance(data,unicode):
# str for Python3, unicode for Python2
data = data.encode(self.encoding,"replace")
try:
buffer = getattr(self.stream, 'buffer', self.stream)
# self.stream.buffer for Python3, self.stream for Python2
buffer.write(data)
buffer.flush()
except:
# We can do nothing if a write fails
raise
def __getattr__(self, attr):
return getattr(self.stream, attr)
import sys import sys
import csv import csv

View File

@@ -50,72 +50,9 @@ try:
except ImportError: except ImportError:
from Crypto.Cipher import AES from Crypto.Cipher import AES
# Wrap a stream so that output gets flushed immediately from utilities import SafeUnbuffered
# 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,str) or isinstance(data,unicode):
# str for Python3, unicode for Python2
data = data.encode(self.encoding,"replace")
try:
buffer = getattr(self.stream, 'buffer', self.stream)
# self.stream.buffer for Python3, self.stream for Python2
buffer.write(data)
buffer.flush()
except:
# We can do nothing if a write fails
raise
def __getattr__(self, attr):
return getattr(self.stream, attr)
try:
from calibre.constants import iswindows, isosx
except:
iswindows = sys.platform.startswith('win')
isosx = sys.platform.startswith('darwin')
def unicode_argv():
if iswindows:
# Uses shell32.GetCommandLineArgvW to get sys.argv as a list of Unicode
# strings.
# Versions 2.x of Python don't support Unicode in sys.argv on
# Windows, with the underlying Windows API instead replacing multi-byte
# characters with '?'. So use shell32.GetCommandLineArgvW to get sys.argv
# as a list of Unicode strings and encode them as utf-8
from ctypes import POINTER, byref, cdll, c_int, windll
from ctypes.wintypes import LPCWSTR, LPWSTR
GetCommandLineW = cdll.kernel32.GetCommandLineW
GetCommandLineW.argtypes = []
GetCommandLineW.restype = LPCWSTR
CommandLineToArgvW = windll.shell32.CommandLineToArgvW
CommandLineToArgvW.argtypes = [LPCWSTR, POINTER(c_int)]
CommandLineToArgvW.restype = POINTER(LPWSTR)
cmd = GetCommandLineW()
argc = c_int(0)
argv = CommandLineToArgvW(cmd, byref(argc))
if argc.value > 0:
# Remove Python executable and commands if present
start = argc.value - len(sys.argv)
return [argv[i] for i in
range(start, argc.value)]
# if we don't have any arguments at all, just pass back script name
# this should never happen
return ["ignoblekeygen.py"]
else:
argvencoding = sys.stdin.encoding or "utf-8"
return [arg if (isinstance(arg, str) or isinstance(arg,unicode)) else str(arg, argvencoding) for arg in sys.argv]
from argv_utils import unicode_argv
class IGNOBLEError(Exception): class IGNOBLEError(Exception):
pass pass
@@ -148,7 +85,7 @@ def generate_key(name, ccn):
def cli_main(): def cli_main():
sys.stdout=SafeUnbuffered(sys.stdout) sys.stdout=SafeUnbuffered(sys.stdout)
sys.stderr=SafeUnbuffered(sys.stderr) sys.stderr=SafeUnbuffered(sys.stderr)
argv=unicode_argv() argv=unicode_argv("ignoblekeyGenPassHash.py")
progname = os.path.basename(argv[0]) progname = os.path.basename(argv[0])
if len(argv) != 4: if len(argv) != 4:
print("usage: {0} <Name> <CC#> <keyfileout.b64>".format(progname)) print("usage: {0} <Name> <CC#> <keyfileout.b64>".format(progname))

View File

@@ -27,71 +27,14 @@ import hashlib
import getopt import getopt
import re import re
# Wrap a stream so that output gets flushed immediately from utilities import SafeUnbuffered
# 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,str) or isinstance(data,unicode):
# str for Python3, unicode for Python2
data = data.encode(self.encoding,"replace")
try:
buffer = getattr(self.stream, 'buffer', self.stream)
# self.stream.buffer for Python3, self.stream for Python2
buffer.write(data)
buffer.flush()
except:
# We can do nothing if a write fails
raise
def __getattr__(self, attr):
return getattr(self.stream, attr)
try: try:
from calibre.constants import iswindows, isosx from calibre.constants import iswindows
except: except:
iswindows = sys.platform.startswith('win') iswindows = sys.platform.startswith('win')
isosx = sys.platform.startswith('darwin')
def unicode_argv(): from argv_utils import unicode_argv
if iswindows:
# Uses shell32.GetCommandLineArgvW to get sys.argv as a list of Unicode
# strings.
# Versions 2.x of Python don't support Unicode in sys.argv on
# Windows, with the underlying Windows API instead replacing multi-byte
# characters with '?'. So use shell32.GetCommandLineArgvW to get sys.argv
# as a list of Unicode strings and encode them as utf-8
from ctypes import POINTER, byref, cdll, c_int, windll
from ctypes.wintypes import LPCWSTR, LPWSTR
GetCommandLineW = cdll.kernel32.GetCommandLineW
GetCommandLineW.argtypes = []
GetCommandLineW.restype = LPCWSTR
CommandLineToArgvW = windll.shell32.CommandLineToArgvW
CommandLineToArgvW.argtypes = [LPCWSTR, POINTER(c_int)]
CommandLineToArgvW.restype = POINTER(LPWSTR)
cmd = GetCommandLineW()
argc = c_int(0)
argv = CommandLineToArgvW(cmd, byref(argc))
if argc.value > 0:
# Remove Python executable and commands if present
start = argc.value - len(sys.argv)
return [argv[i] for i in
range(start, argc.value)]
# if we don't have any arguments at all, just pass back script name
# this should never happen
return ["ignoblekey.py"]
else:
argvencoding = sys.stdin.encoding or "utf-8"
return [arg if (isinstance(arg, str) or isinstance(arg,unicode)) else str(arg, argvencoding) for arg in sys.argv]
class DrmException(Exception): class DrmException(Exception):
pass pass
@@ -244,7 +187,7 @@ def usage(progname):
def cli_main(): def cli_main():
sys.stdout=SafeUnbuffered(sys.stdout) sys.stdout=SafeUnbuffered(sys.stdout)
sys.stderr=SafeUnbuffered(sys.stderr) sys.stderr=SafeUnbuffered(sys.stderr)
argv=unicode_argv() argv=unicode_argv("ignoblekeyNookStudy.py")
progname = os.path.basename(argv[0]) progname = os.path.basename(argv[0])
print("{0} v{1}\nCopyright © 2015 Apprentice Alf".format(progname,__version__)) print("{0} v{1}\nCopyright © 2015 Apprentice Alf".format(progname,__version__))
@@ -305,7 +248,7 @@ def gui_main():
self.text.insert(tkinter.constants.END, text) self.text.insert(tkinter.constants.END, text)
argv=unicode_argv() argv=unicode_argv("ignoblekeyNookStudy.py")
root = tkinter.Tk() root = tkinter.Tk()
root.withdraw() root.withdraw()
progpath, progname = os.path.split(argv[0]) progpath, progname = os.path.split(argv[0])

View File

@@ -1,265 +0,0 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# ignoblekeyfetch.py
# Copyright © 2015-2020 Apprentice Harper et al.
# Released under the terms of the GNU General Public Licence, version 3
# <http://www.gnu.org/licenses/>
# Based on discoveries by "Nobody You Know"
# Code partly based on ignoblekeygen.py by several people.
# Windows users: Before running this program, you must first install Python.
# We recommend ActiveState Python 2.7.X for Windows from
# http://www.activestate.com/activepython/downloads.
# Then save this script file as ignoblekeyfetch.pyw and double-click on it to run it.
#
# Mac OS X users: Save this script file as ignoblekeyfetch.pyw. You can run this
# program from the command line (python ignoblekeyfetch.pyw) or by double-clicking
# it when it has been associated with PythonLauncher.
# Revision history:
# 1.0 - Initial version
# 1.1 - Try second URL if first one fails
# 2.0 - Python 3 for calibre 5.0
"""
Fetch Barnes & Noble EPUB user key from B&N servers using email and password.
NOTE: This script used to work in the past, but the server it uses is long gone.
It can no longer be used to download keys from B&N servers, it is no longer
supported by the Calibre plugin, and it will be removed in the future.
"""
__license__ = 'GPL v3'
__version__ = "2.0"
import sys
import os
# 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,str) or isinstance(data,unicode):
# str for Python3, unicode for Python2
data = data.encode(self.encoding,"replace")
try:
buffer = getattr(self.stream, 'buffer', self.stream)
# self.stream.buffer for Python3, self.stream for Python2
buffer.write(data)
buffer.flush()
except:
# We can do nothing if a write fails
raise
def __getattr__(self, attr):
return getattr(self.stream, attr)
try:
from calibre.constants import iswindows, isosx
except:
iswindows = sys.platform.startswith('win')
isosx = sys.platform.startswith('darwin')
def unicode_argv():
if iswindows:
# Uses shell32.GetCommandLineArgvW to get sys.argv as a list of Unicode
# strings.
# Versions 2.x of Python don't support Unicode in sys.argv on
# Windows, with the underlying Windows API instead replacing multi-byte
# characters with '?'. So use shell32.GetCommandLineArgvW to get sys.argv
# as a list of Unicode strings and encode them as utf-8
from ctypes import POINTER, byref, cdll, c_int, windll
from ctypes.wintypes import LPCWSTR, LPWSTR
GetCommandLineW = cdll.kernel32.GetCommandLineW
GetCommandLineW.argtypes = []
GetCommandLineW.restype = LPCWSTR
CommandLineToArgvW = windll.shell32.CommandLineToArgvW
CommandLineToArgvW.argtypes = [LPCWSTR, POINTER(c_int)]
CommandLineToArgvW.restype = POINTER(LPWSTR)
cmd = GetCommandLineW()
argc = c_int(0)
argv = CommandLineToArgvW(cmd, byref(argc))
if argc.value > 0:
# Remove Python executable and commands if present
start = argc.value - len(sys.argv)
return [argv[i] for i in
range(start, argc.value)]
# if we don't have any arguments at all, just pass back script name
# this should never happen
return ["ignoblekeyfetch.py"]
else:
argvencoding = sys.stdin.encoding or "utf-8"
return [arg if (isinstance(arg, str) or isinstance(arg,unicode)) else str(arg, argvencoding) for arg in sys.argv]
class IGNOBLEError(Exception):
pass
def fetch_key(email, password):
# change email and password to utf-8 if unicode
if type(email)==str:
email = email.encode('utf-8')
if type(password)==str:
password = password.encode('utf-8')
import random
random = "%030x" % random.randrange(16**30)
import urllib.parse, urllib.request, re
# try the URL from nook for PC
fetch_url = "https://cart4.barnesandnoble.com/services/service.aspx?Version=2&acctPassword="
fetch_url += urllib.parse.quote(password,'')+"&devID=PC_BN_2.5.6.9575_"+random+"&emailAddress="
fetch_url += urllib.parse.quote(email,"")+"&outFormat=5&schema=1&service=1&stage=deviceHashB"
#print fetch_url
found = ''
try:
response = urllib.request.urlopen(fetch_url)
the_page = response.read()
#print the_page
found = re.search('ccHash>(.+?)</ccHash', the_page).group(1)
except:
found = ''
if len(found)!=28:
# try the URL from android devices
fetch_url = "https://cart4.barnesandnoble.com/services/service.aspx?Version=2&acctPassword="
fetch_url += urllib.parse.quote(password,'')+"&devID=hobbes_9.3.50818_"+random+"&emailAddress="
fetch_url += urllib.parse.quote(email,"")+"&outFormat=5&schema=1&service=1&stage=deviceHashB"
#print fetch_url
found = ''
try:
response = urllib.request.urlopen(fetch_url)
the_page = response.read()
#print the_page
found = re.search('ccHash>(.+?)</ccHash', the_page).group(1)
except:
found = ''
return found
def cli_main():
sys.stdout=SafeUnbuffered(sys.stdout)
sys.stderr=SafeUnbuffered(sys.stderr)
argv=unicode_argv()
progname = os.path.basename(argv[0])
if len(argv) != 4:
print("usage: {0} <email> <password> <keyfileout.b64>".format(progname))
return 1
email, password, keypath = argv[1:]
userkey = fetch_key(email, password)
if len(userkey) == 28:
open(keypath,'wb').write(userkey)
return 0
print("Failed to fetch key.")
return 1
def gui_main():
try:
import tkinter
import tkinter.filedialog
import tkinter.constants
import tkinter.messagebox
import traceback
except:
return cli_main()
class DecryptionDialog(tkinter.Frame):
def __init__(self, root):
tkinter.Frame.__init__(self, root, border=5)
self.status = tkinter.Label(self, text="Enter parameters")
self.status.pack(fill=tkinter.constants.X, expand=1)
body = tkinter.Frame(self)
body.pack(fill=tkinter.constants.X, expand=1)
sticky = tkinter.constants.E + tkinter.constants.W
body.grid_columnconfigure(1, weight=2)
tkinter.Label(body, text="Account email address").grid(row=0)
self.name = tkinter.Entry(body, width=40)
self.name.grid(row=0, column=1, sticky=sticky)
tkinter.Label(body, text="Account password").grid(row=1)
self.ccn = tkinter.Entry(body, width=40)
self.ccn.grid(row=1, column=1, sticky=sticky)
tkinter.Label(body, text="Output file").grid(row=2)
self.keypath = tkinter.Entry(body, width=40)
self.keypath.grid(row=2, column=1, sticky=sticky)
self.keypath.insert(2, "bnepubkey.b64")
button = tkinter.Button(body, text="...", command=self.get_keypath)
button.grid(row=2, column=2)
buttons = tkinter.Frame(self)
buttons.pack()
botton = tkinter.Button(
buttons, text="Fetch", width=10, command=self.generate)
botton.pack(side=tkinter.constants.LEFT)
tkinter.Frame(buttons, width=10).pack(side=tkinter.constants.LEFT)
button = tkinter.Button(
buttons, text="Quit", width=10, command=self.quit)
button.pack(side=tkinter.constants.RIGHT)
def get_keypath(self):
keypath = tkinter.filedialog.asksaveasfilename(
parent=None, title="Select B&N ePub key file to produce",
defaultextension=".b64",
filetypes=[('base64-encoded files', '.b64'),
('All Files', '.*')])
if keypath:
keypath = os.path.normpath(keypath)
self.keypath.delete(0, tkinter.constants.END)
self.keypath.insert(0, keypath)
return
def generate(self):
email = self.name.get()
password = self.ccn.get()
keypath = self.keypath.get()
if not email:
self.status['text'] = "Email address not given"
return
if not password:
self.status['text'] = "Account password not given"
return
if not keypath:
self.status['text'] = "Output keyfile path not set"
return
self.status['text'] = "Fetching..."
try:
userkey = fetch_key(email, password)
except Exception as e:
self.status['text'] = "Error: {0}".format(e.args[0])
return
if len(userkey) == 28:
open(keypath,'wb').write(userkey)
self.status['text'] = "Keyfile fetched successfully"
else:
self.status['text'] = "Keyfile fetch failed."
root = tkinter.Tk()
root.title("Barnes & Noble ePub Keyfile Fetch v.{0}".format(__version__))
root.resizable(True, False)
root.minsize(300, 0)
DecryptionDialog(root).pack(fill=tkinter.constants.X, expand=1)
root.mainloop()
return 0
if __name__ == '__main__':
if len(sys.argv) > 1:
sys.exit(cli_main())
sys.exit(gui_main())

View File

@@ -48,6 +48,7 @@ import base64
import zlib import zlib
import zipfile import zipfile
from zipfile import ZipInfo, ZipFile, ZIP_STORED, ZIP_DEFLATED from zipfile import ZipInfo, ZipFile, ZIP_STORED, ZIP_DEFLATED
from zeroedzipinfo import ZeroedZipInfo
from contextlib import closing from contextlib import closing
from lxml import etree from lxml import etree
from uuid import UUID from uuid import UUID
@@ -69,69 +70,9 @@ def unpad(data, padding=16):
return data[:-pad_len] return data[:-pad_len]
# Wrap a stream so that output gets flushed immediately from utilities import SafeUnbuffered
# 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,str) or isinstance(data,unicode):
# str for Python3, unicode for Python2
data = data.encode(self.encoding,"replace")
try:
buffer = getattr(self.stream, 'buffer', self.stream)
# self.stream.buffer for Python3, self.stream for Python2
buffer.write(data)
buffer.flush()
except:
# We can do nothing if a write fails
raise
def __getattr__(self, attr):
return getattr(self.stream, attr)
try: from argv_utils import unicode_argv
from calibre.constants import iswindows, isosx
except:
iswindows = sys.platform.startswith('win')
isosx = sys.platform.startswith('darwin')
def unicode_argv():
if iswindows:
# Uses shell32.GetCommandLineArgvW to get sys.argv as a list of Unicode
# strings.
# Versions 2.x of Python don't support Unicode in sys.argv on
# Windows, with the underlying Windows API instead replacing multi-byte
# characters with '?'.
from ctypes import POINTER, byref, cdll, c_int, windll
from ctypes.wintypes import LPCWSTR, LPWSTR
GetCommandLineW = cdll.kernel32.GetCommandLineW
GetCommandLineW.argtypes = []
GetCommandLineW.restype = LPCWSTR
CommandLineToArgvW = windll.shell32.CommandLineToArgvW
CommandLineToArgvW.argtypes = [LPCWSTR, POINTER(c_int)]
CommandLineToArgvW.restype = POINTER(LPWSTR)
cmd = GetCommandLineW()
argc = c_int(0)
argv = CommandLineToArgvW(cmd, byref(argc))
if argc.value > 0:
# Remove Python executable and commands if present
start = argc.value - len(sys.argv)
return [argv[i] for i in
range(start, argc.value)]
return ["ineptepub.py"]
else:
argvencoding = sys.stdin.encoding or "utf-8"
return [arg if (isinstance(arg, str) or isinstance(arg,unicode)) else str(arg, argvencoding) for arg in sys.argv]
class ADEPTError(Exception): class ADEPTError(Exception):
@@ -302,7 +243,7 @@ def decryptBook(userkey, inpath, outpath):
if len(bookkey) != 64: if len(bookkey) != 64:
# Normal or "hardened" Adobe ADEPT # Normal or "hardened" Adobe ADEPT
rsakey = RSA.import_key(userkey) # parses the ASN1 structure rsakey = RSA.importKey(userkey) # parses the ASN1 structure
bookkey = base64.b64decode(bookkey) bookkey = base64.b64decode(bookkey)
if int(keytype, 10) > 2: if int(keytype, 10) > 2:
bookkey = removeHardening(rights, keytype, bookkey) bookkey = removeHardening(rights, keytype, bookkey)
@@ -356,12 +297,23 @@ def decryptBook(userkey, inpath, outpath):
zi.internal_attr = oldzi.internal_attr zi.internal_attr = oldzi.internal_attr
# external attributes are dependent on the create system, so copy both. # external attributes are dependent on the create system, so copy both.
zi.external_attr = oldzi.external_attr zi.external_attr = oldzi.external_attr
zi.volume = oldzi.volume
zi.create_system = oldzi.create_system zi.create_system = oldzi.create_system
zi.create_version = oldzi.create_version
if any(ord(c) >= 128 for c in path) or any(ord(c) >= 128 for c in zi.comment): if any(ord(c) >= 128 for c in path) or any(ord(c) >= 128 for c in zi.comment):
# If the file name or the comment contains any non-ASCII char, set the UTF8-flag # If the file name or the comment contains any non-ASCII char, set the UTF8-flag
zi.flag_bits |= 0x800 zi.flag_bits |= 0x800
except: except:
pass pass
# Python 3 has a bug where the external_attr is reset to `0o600 << 16`
# if it's NULL, so we need a workaround:
if zi.external_attr == 0:
zi = ZeroedZipInfo(zi)
if path == "META-INF/encryption.xml": if path == "META-INF/encryption.xml":
outf.writestr(zi, data) outf.writestr(zi, data)
else: else:
@@ -375,7 +327,7 @@ def decryptBook(userkey, inpath, outpath):
def cli_main(): def cli_main():
sys.stdout=SafeUnbuffered(sys.stdout) sys.stdout=SafeUnbuffered(sys.stdout)
sys.stderr=SafeUnbuffered(sys.stderr) sys.stderr=SafeUnbuffered(sys.stderr)
argv=unicode_argv() argv=unicode_argv("ineptepub.py")
progname = os.path.basename(argv[0]) progname = os.path.basename(argv[0])
if len(argv) != 4: if len(argv) != 4:
print("usage: {0} <keyfile.der> <inbook.epub> <outbook.epub>".format(progname)) print("usage: {0} <keyfile.der> <inbook.epub> <outbook.epub>".format(progname))

View File

@@ -51,13 +51,14 @@
# 9.1.1 - Only support PyCryptodome; clean up the code # 9.1.1 - Only support PyCryptodome; clean up the code
# 10.0.0 - Add support for "hardened" Adobe DRM (RMSDK >= 10) # 10.0.0 - Add support for "hardened" Adobe DRM (RMSDK >= 10)
# 10.0.2 - Fix some Python2 stuff # 10.0.2 - Fix some Python2 stuff
# 10.0.4 - Fix more Python2 stuff
""" """
Decrypts Adobe ADEPT-encrypted PDF files. Decrypts Adobe ADEPT-encrypted PDF files.
""" """
__license__ = 'GPL v3' __license__ = 'GPL v3'
__version__ = "10.0.2" __version__ = "10.0.4"
import codecs import codecs
import hashlib import hashlib
@@ -92,67 +93,12 @@ def unpad(data, padding=16):
return data[:-pad_len] return data[:-pad_len]
# Wrap a stream so that output gets flushed immediately from utilities import SafeUnbuffered
# 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,str) or isinstance(data,unicode):
# str for Python3, unicode for Python2
data = data.encode(self.encoding,"replace")
try:
buffer = getattr(self.stream, 'buffer', self.stream)
# self.stream.buffer for Python3, self.stream for Python2
buffer.write(data)
buffer.flush()
except:
# We can do nothing if a write fails
raise
def __getattr__(self, attr):
return getattr(self.stream, attr)
iswindows = sys.platform.startswith('win') iswindows = sys.platform.startswith('win')
isosx = sys.platform.startswith('darwin') isosx = sys.platform.startswith('darwin')
def unicode_argv(): from argv_utils import unicode_argv
if iswindows:
# Uses shell32.GetCommandLineArgvW to get sys.argv as a list of Unicode
# strings.
# Versions 2.x of Python don't support Unicode in sys.argv on
# Windows, with the underlying Windows API instead replacing multi-byte
# characters with '?'.
from ctypes import POINTER, byref, cdll, c_int, windll
from ctypes.wintypes import LPCWSTR, LPWSTR
GetCommandLineW = cdll.kernel32.GetCommandLineW
GetCommandLineW.argtypes = []
GetCommandLineW.restype = LPCWSTR
CommandLineToArgvW = windll.shell32.CommandLineToArgvW
CommandLineToArgvW.argtypes = [LPCWSTR, POINTER(c_int)]
CommandLineToArgvW.restype = POINTER(LPWSTR)
cmd = GetCommandLineW()
argc = c_int(0)
argv = CommandLineToArgvW(cmd, byref(argc))
if argc.value > 0:
# Remove Python executable and commands if present
start = argc.value - len(sys.argv)
return [argv[i] for i in
range(start, argc.value)]
return ["ineptpdf.py"]
else:
argvencoding = sys.stdin.encoding or "utf-8"
return [arg if (isinstance(arg, str) or isinstance(arg,unicode)) else str(arg, argvencoding) for arg in sys.argv]
class ADEPTError(Exception): class ADEPTError(Exception):
pass pass
@@ -200,7 +146,10 @@ def nunpack(s, default=0):
elif l == 2: elif l == 2:
return struct.unpack('>H', s)[0] return struct.unpack('>H', s)[0]
elif l == 3: elif l == 3:
return struct.unpack('>L', bytes([0]) + s)[0] if sys.version_info[0] == 2:
return struct.unpack('>L', '\x00'+s)[0]
else:
return struct.unpack('>L', bytes([0]) + s)[0]
elif l == 4: elif l == 4:
return struct.unpack('>L', s)[0] return struct.unpack('>L', s)[0]
else: else:
@@ -459,7 +408,10 @@ class PSBaseParser(object):
self.hex += c self.hex += c
return (self.parse_literal_hex, i+1) return (self.parse_literal_hex, i+1)
if self.hex: if self.hex:
self.token += bytes([int(self.hex, 16)]) if sys.version_info[0] == 2:
self.token += chr(int(self.hex, 16))
else:
self.token += bytes([int(self.hex, 16)])
return (self.parse_literal, i) return (self.parse_literal, i)
def parse_number(self, s, i): def parse_number(self, s, i):
@@ -543,10 +495,18 @@ class PSBaseParser(object):
self.oct += c self.oct += c
return (self.parse_string_1, i+1) return (self.parse_string_1, i+1)
if self.oct: if self.oct:
self.token += bytes([int(self.oct, 8)]) if sys.version_info[0] == 2:
self.token += chr(int(self.oct, 8))
else:
self.token += bytes([int(self.oct, 8)])
return (self.parse_string, i) return (self.parse_string, i)
if c in ESC_STRING: if c in ESC_STRING:
self.token += bytes([ESC_STRING[c]])
if sys.version_info[0] == 2:
self.token += chr(ESC_STRING[c])
else:
self.token += bytes([ESC_STRING[c]])
return (self.parse_string, i+1) return (self.parse_string, i+1)
def parse_wopen(self, s, i): def parse_wopen(self, s, i):
@@ -572,14 +532,19 @@ class PSBaseParser(object):
return (self.parse_main, i) return (self.parse_main, i)
def parse_hexstring(self, s, i): def parse_hexstring(self, s, i):
m1 = END_HEX_STRING.search(s, i) m = END_HEX_STRING.search(s, i)
if not m1: if not m:
self.token += s[i:] self.token += s[i:]
return (self.parse_hexstring, len(s)) return (self.parse_hexstring, len(s))
j = m1.start(0) j = m.start(0)
self.token += s[i:j] self.token += s[i:j]
token = HEX_PAIR.sub(lambda m2: bytes([int(m2.group(0), 16)]), if sys.version_info[0] == 2:
token = HEX_PAIR.sub(lambda m: chr(int(m.group(0), 16)),
SPC.sub('', self.token))
else:
token = HEX_PAIR.sub(lambda m: bytes([int(m.group(0), 16)]),
SPC.sub(b'', self.token)) SPC.sub(b'', self.token))
self.add_token(token) self.add_token(token)
return (self.parse_main, j) return (self.parse_main, j)
@@ -1669,7 +1634,7 @@ class PDFDocument(object):
def initialize_ebx_inept(self, password, docid, param): def initialize_ebx_inept(self, password, docid, param):
self.is_printable = self.is_modifiable = self.is_extractable = True self.is_printable = self.is_modifiable = self.is_extractable = True
rsakey = RSA.import_key(password) # parses the ASN1 structure rsakey = RSA.importKey(password) # parses the ASN1 structure
length = int_value(param.get('Length', 0)) // 8 length = int_value(param.get('Length', 0)) // 8
rights = codecs.decode(param.get('ADEPT_LICENSE'), 'base64') rights = codecs.decode(param.get('ADEPT_LICENSE'), 'base64')
rights = zlib.decompress(rights, -15) rights = zlib.decompress(rights, -15)
@@ -2307,7 +2272,7 @@ def getPDFencryptionType(inpath):
def cli_main(): def cli_main():
sys.stdout=SafeUnbuffered(sys.stdout) sys.stdout=SafeUnbuffered(sys.stdout)
sys.stderr=SafeUnbuffered(sys.stderr) sys.stderr=SafeUnbuffered(sys.stderr)
argv=unicode_argv() argv=unicode_argv("ineptpdf.py")
progname = os.path.basename(argv[0]) progname = os.path.basename(argv[0])
if len(argv) != 4: if len(argv) != 4:
print("usage: {0} <keyfile.der> <inbook.pdf> <outbook.pdf>".format(progname)) print("usage: {0} <keyfile.der> <inbook.pdf> <outbook.pdf>".format(progname))

View File

@@ -88,68 +88,10 @@ import kgenpids
import androidkindlekey import androidkindlekey
import kfxdedrm import kfxdedrm
# Wrap a stream so that output gets flushed immediately from utilities import SafeUnbuffered
# 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,str) or isinstance(data,unicode):
# str for Python3, unicode for Python2
data = data.encode(self.encoding,"replace")
try:
buffer = getattr(self.stream, 'buffer', self.stream)
# self.stream.buffer for Python3, self.stream for Python2
buffer.write(data)
buffer.flush()
except:
# We can do nothing if a write fails
raise
def __getattr__(self, attr):
return getattr(self.stream, attr)
iswindows = sys.platform.startswith('win') from argv_utils import unicode_argv
isosx = sys.platform.startswith('darwin')
def unicode_argv():
if iswindows:
# Uses shell32.GetCommandLineArgvW to get sys.argv as a list of Unicode
# strings.
# Versions 2.x of Python don't support Unicode in sys.argv on
# Windows, with the underlying Windows API instead replacing multi-byte
# characters with '?'.
from ctypes import POINTER, byref, cdll, c_int, windll
from ctypes.wintypes import LPCWSTR, LPWSTR
GetCommandLineW = cdll.kernel32.GetCommandLineW
GetCommandLineW.argtypes = []
GetCommandLineW.restype = LPCWSTR
CommandLineToArgvW = windll.shell32.CommandLineToArgvW
CommandLineToArgvW.argtypes = [LPCWSTR, POINTER(c_int)]
CommandLineToArgvW.restype = POINTER(LPWSTR)
cmd = GetCommandLineW()
argc = c_int(0)
argv = CommandLineToArgvW(cmd, byref(argc))
if argc.value > 0:
# Remove Python executable and commands if present
start = argc.value - len(sys.argv)
return [argv[i] for i in
range(start, argc.value)]
# if we don't have any arguments at all, just pass back script name
# this should never happen
return ["mobidedrm.py"]
else:
argvencoding = sys.stdin.encoding or "utf-8"
return [arg if (isinstance(arg, str) or isinstance(arg,unicode)) else str(arg, argvencoding) for arg in sys.argv]
# cleanup unicode filenames # cleanup unicode filenames
# borrowed from calibre from calibre/src/calibre/__init__.py # borrowed from calibre from calibre/src/calibre/__init__.py
@@ -311,7 +253,7 @@ def usage(progname):
# Main # Main
# #
def cli_main(): def cli_main():
argv=unicode_argv() argv=unicode_argv("k4mobidedrm.py")
progname = os.path.basename(argv[0]) progname = os.path.basename(argv[0])
print("K4MobiDeDrm v{0}.\nCopyright © 2008-2020 Apprentice Harper et al.".format(__version__)) print("K4MobiDeDrm v{0}.\nCopyright © 2008-2020 Apprentice Harper et al.".format(__version__))

View File

@@ -62,29 +62,7 @@ except NameError:
# Routines common to Mac and PC # Routines common to Mac and PC
# Wrap a stream so that output gets flushed immediately from utilities import SafeUnbuffered
# 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,str) or isinstance(data,unicode):
# str for Python3, unicode for Python2
data = data.encode(self.encoding,"replace")
try:
buffer = getattr(self.stream, 'buffer', self.stream)
# self.stream.buffer for Python3, self.stream for Python2
buffer.write(data)
buffer.flush()
except:
# We can do nothing if a write fails
raise
def __getattr__(self, attr):
return getattr(self.stream, attr)
try: try:
from calibre.constants import iswindows, isosx from calibre.constants import iswindows, isosx
@@ -92,41 +70,7 @@ except:
iswindows = sys.platform.startswith('win') iswindows = sys.platform.startswith('win')
isosx = sys.platform.startswith('darwin') isosx = sys.platform.startswith('darwin')
def unicode_argv(): from argv_utils import unicode_argv
if iswindows:
# Uses shell32.GetCommandLineArgvW to get sys.argv as a list of Unicode
# strings.
# Versions 2.x of Python don't support Unicode in sys.argv on
# Windows, with the underlying Windows API instead replacing multi-byte
# characters with '?'. So use shell32.GetCommandLineArgvW to get sys.argv
# as a list of Unicode strings and encode them as utf-8
from ctypes import POINTER, byref, cdll, c_int, windll
from ctypes.wintypes import LPCWSTR, LPWSTR
GetCommandLineW = cdll.kernel32.GetCommandLineW
GetCommandLineW.argtypes = []
GetCommandLineW.restype = LPCWSTR
CommandLineToArgvW = windll.shell32.CommandLineToArgvW
CommandLineToArgvW.argtypes = [LPCWSTR, POINTER(c_int)]
CommandLineToArgvW.restype = POINTER(LPWSTR)
cmd = GetCommandLineW()
argc = c_int(0)
argv = CommandLineToArgvW(cmd, byref(argc))
if argc.value > 0:
# Remove Python executable and commands if present
start = argc.value - len(sys.argv)
return [argv[i] for i in
range(start, argc.value)]
# if we don't have any arguments at all, just pass back script name
# this should never happen
return ["kindlekey.py"]
else:
argvencoding = sys.stdin.encoding or "utf-8"
return [arg if (isinstance(arg, str) or isinstance(arg,unicode)) else str(arg, argvencoding) for arg in sys.argv]
class DrmException(Exception): class DrmException(Exception):
pass pass
@@ -989,7 +933,7 @@ def usage(progname):
def cli_main(): def cli_main():
sys.stdout=SafeUnbuffered(sys.stdout) sys.stdout=SafeUnbuffered(sys.stdout)
sys.stderr=SafeUnbuffered(sys.stderr) sys.stderr=SafeUnbuffered(sys.stderr)
argv=unicode_argv() argv=unicode_argv("kindlekey.py")
progname = os.path.basename(argv[0]) progname = os.path.basename(argv[0])
print("{0} v{1}\nCopyright © 2010-2020 by some_updates, Apprentice Harper et al.".format(progname,__version__)) print("{0} v{1}\nCopyright © 2010-2020 by some_updates, Apprentice Harper et al.".format(progname,__version__))
@@ -1050,7 +994,7 @@ def gui_main():
self.text.insert(tkinter.constants.END, text) self.text.insert(tkinter.constants.END, text)
argv=unicode_argv() argv=unicode_argv("kindlekey.py")
root = tkinter.Tk() root = tkinter.Tk()
root.withdraw() root.withdraw()
progpath, progname = os.path.split(argv[0]) progpath, progname = os.path.split(argv[0])

View File

@@ -16,68 +16,9 @@
import sys import sys
import binascii import binascii
# Wrap a stream so that output gets flushed immediately from utilities import SafeUnbuffered
# 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,str) or isinstance(data,unicode):
# str for Python3, unicode for Python2
data = data.encode(self.encoding,"replace")
try:
buffer = getattr(self.stream, 'buffer', self.stream)
# self.stream.buffer for Python3, self.stream for Python2
buffer.write(data)
buffer.flush()
except:
# We can do nothing if a write fails
raise
def __getattr__(self, attr):
return getattr(self.stream, attr)
iswindows = sys.platform.startswith('win') from argv_utils import unicode_argv
isosx = sys.platform.startswith('darwin')
def unicode_argv():
if iswindows:
# Uses shell32.GetCommandLineArgvW to get sys.argv as a list of Unicode
# strings.
# Versions 2.x of Python don't support Unicode in sys.argv on
# Windows, with the underlying Windows API instead replacing multi-byte
# characters with '?'.
from ctypes import POINTER, byref, cdll, c_int, windll
from ctypes.wintypes import LPCWSTR, LPWSTR
GetCommandLineW = cdll.kernel32.GetCommandLineW
GetCommandLineW.argtypes = []
GetCommandLineW.restype = LPCWSTR
CommandLineToArgvW = windll.shell32.CommandLineToArgvW
CommandLineToArgvW.argtypes = [LPCWSTR, POINTER(c_int)]
CommandLineToArgvW.restype = POINTER(LPWSTR)
cmd = GetCommandLineW()
argc = c_int(0)
argv = CommandLineToArgvW(cmd, byref(argc))
if argc.value > 0:
# Remove Python executable and commands if present
start = argc.value - len(sys.argv)
return [argv[i] for i in
range(start, argc.value)]
# if we don't have any arguments at all, just pass back script name
# this should never happen
return ["kindlepid.py"]
else:
argvencoding = sys.stdin.encoding or "utf-8"
return [arg if (isinstance(arg, str) or isinstance(arg,unicode)) else str(arg, argvencoding) for arg in sys.argv]
letters = 'ABCDEFGHIJKLMNPQRSTUVWXYZ123456789' letters = 'ABCDEFGHIJKLMNPQRSTUVWXYZ123456789'
@@ -117,7 +58,7 @@ def pidFromSerial(s, l):
def cli_main(): def cli_main():
print("Mobipocket PID calculator for Amazon Kindle. Copyright © 2007, 2009 Igor Skochinsky") print("Mobipocket PID calculator for Amazon Kindle. Copyright © 2007, 2009 Igor Skochinsky")
argv=unicode_argv() argv=unicode_argv("kindlepid.py")
if len(argv)==2: if len(argv)==2:
serial = argv[1] serial = argv[1]
else: else:

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -80,73 +80,11 @@ import sys
import os import os
import struct import struct
import binascii import binascii
try: from alfcrypto import Pukall_Cipher
from alfcrypto import Pukall_Cipher
except:
print("AlfCrypto not found. Using python PC1 implementation.")
# Wrap a stream so that output gets flushed immediately from utilities import SafeUnbuffered
# 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,str) or isinstance(data,unicode):
# str for Python3, unicode for Python2
data = data.encode(self.encoding,"replace")
try:
buffer = getattr(self.stream, 'buffer', self.stream)
# self.stream.buffer for Python3, self.stream for Python2
buffer.write(data)
buffer.flush()
except:
# We can do nothing if a write fails
raise
def __getattr__(self, attr):
return getattr(self.stream, attr)
iswindows = sys.platform.startswith('win') from argv_utils import unicode_argv
isosx = sys.platform.startswith('darwin')
def unicode_argv():
if iswindows:
# Uses shell32.GetCommandLineArgvW to get sys.argv as a list of Unicode
# strings.
# Versions 2.x of Python don't support Unicode in sys.argv on
# Windows, with the underlying Windows API instead replacing multi-byte
# characters with '?'.
from ctypes import POINTER, byref, cdll, c_int, windll
from ctypes.wintypes import LPCWSTR, LPWSTR
GetCommandLineW = cdll.kernel32.GetCommandLineW
GetCommandLineW.argtypes = []
GetCommandLineW.restype = LPCWSTR
CommandLineToArgvW = windll.shell32.CommandLineToArgvW
CommandLineToArgvW.argtypes = [LPCWSTR, POINTER(c_int)]
CommandLineToArgvW.restype = POINTER(LPWSTR)
cmd = GetCommandLineW()
argc = c_int(0)
argv = CommandLineToArgvW(cmd, byref(argc))
if argc.value > 0:
# Remove Python executable and commands if present
start = argc.value - len(sys.argv)
return [argv[i] for i in
range(start, argc.value)]
# if we don't have any arguments at all, just pass back script name
# this should never happen
return ["mobidedrm.py"]
else:
argvencoding = sys.stdin.encoding or "utf-8"
return [arg if (isinstance(arg, str) or isinstance(arg,unicode)) else str(arg, argvencoding) for arg in sys.argv]
class DrmException(Exception): class DrmException(Exception):
@@ -162,41 +100,8 @@ def PC1(key, src, decryption=True):
# if we can get it from alfcrypto, use that # if we can get it from alfcrypto, use that
try: try:
return Pukall_Cipher().PC1(key,src,decryption) return Pukall_Cipher().PC1(key,src,decryption)
except NameError: except:
pass raise
except TypeError:
pass
# use slow python version, since Pukall_Cipher didn't load
sum1 = 0;
sum2 = 0;
keyXorVal = 0;
if len(key)!=16:
DrmException ("PC1: Bad key length")
wkey = []
for i in range(8):
wkey.append(key[i*2]<<8 | key[i*2+1])
dst = bytearray(len(src))
for i in range(len(src)):
temp1 = 0;
byteXorVal = 0;
for j in range(8):
temp1 ^= wkey[j]
sum2 = (sum2+j)*20021 + sum1
sum1 = (temp1*346)&0xFFFF
sum2 = (sum2+sum1)&0xFFFF
temp1 = (temp1*20021+1)&0xFFFF
byteXorVal ^= temp1 ^ sum2
curByte = src[i]
if not decryption:
keyXorVal = curByte * 257;
curByte = ((curByte ^ (byteXorVal >> 8)) ^ byteXorVal) & 0xFF
if decryption:
keyXorVal = curByte * 257;
for j in range(8):
wkey[j] ^= keyXorVal;
dst[i] = curByte
return bytes(dst)
# accepts unicode returns unicode # accepts unicode returns unicode
def checksumPid(s): def checksumPid(s):
@@ -254,12 +159,7 @@ class MobiBook:
pass pass
def __init__(self, infile): def __init__(self, infile):
print("MobiDeDrm v{0:s}.\nCopyright © 2008-2020 The Dark Reverser, Apprentice Harper et al.".format(__version__)) print("MobiDeDrm v{0:s}.\nCopyright © 2008-2022 The Dark Reverser, Apprentice Harper et al.".format(__version__))
try:
from alfcrypto import Pukall_Cipher
except:
print("AlfCrypto not found. Using python PC1 implementation.")
# initial sanity check on file # initial sanity check on file
self.data_file = open(infile, 'rb').read() self.data_file = open(infile, 'rb').read()
@@ -544,7 +444,7 @@ def getUnencryptedBook(infile,pidlist):
def cli_main(): def cli_main():
argv=unicode_argv() argv=unicode_argv("mobidedrm.py")
progname = os.path.basename(argv[0]) progname = os.path.basename(argv[0])
if len(argv)<3 or len(argv)>4: if len(argv)<3 or len(argv)>4:
print("MobiDeDrm v{0:s}.\nCopyright © 2008-2020 The Dark Reverser, Apprentice Harper et al.".format(__version__)) print("MobiDeDrm v{0:s}.\nCopyright © 2008-2020 The Dark Reverser, Apprentice Harper et al.".format(__version__))

View File

@@ -1,81 +0,0 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# vim:ts=4:sw=4:softtabstop=4:smarttab:expandtab
import sys
import os, os.path
import shutil
class SimplePrefsError(Exception):
pass
class SimplePrefs(object):
def __init__(self, target, description):
self.prefs = {}
self.key2file={}
self.file2key={}
for keyfilemap in description:
[key, filename] = keyfilemap
self.key2file[key] = filename
self.file2key[filename] = key
self.target = target + 'Prefs'
if sys.platform.startswith('win'):
try:
import winreg
except ImportError:
import _winreg as winreg
regkey = winreg.OpenKey(winreg.HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders\\")
path = winreg.QueryValueEx(regkey, 'Local AppData')[0]
prefdir = path + os.sep + self.target
elif sys.platform.startswith('darwin'):
home = os.getenv('HOME')
prefdir = os.path.join(home,'Library','Preferences','org.' + self.target)
else:
# linux and various flavors of unix
home = os.getenv('HOME')
prefdir = os.path.join(home,'.' + self.target)
if not os.path.exists(prefdir):
os.makedirs(prefdir)
self.prefdir = prefdir
self.prefs['dir'] = self.prefdir
self._loadPreferences()
def _loadPreferences(self):
filenames = os.listdir(self.prefdir)
for filename in filenames:
if filename in self.file2key:
key = self.file2key[filename]
filepath = os.path.join(self.prefdir,filename)
if os.path.isfile(filepath):
try :
data = file(filepath,'rb').read()
self.prefs[key] = data
except Exception as e:
pass
def getPreferences(self):
return self.prefs
def setPreferences(self, newprefs={}):
if 'dir' not in newprefs:
raise SimplePrefsError('Error: Attempt to Set Preferences in unspecified directory')
if newprefs['dir'] != self.prefs['dir']:
raise SimplePrefsError('Error: Attempt to Set Preferences in unspecified directory')
for key in newprefs:
if key != 'dir':
if key in self.key2file:
filename = self.key2file[key]
filepath = os.path.join(self.prefdir,filename)
data = newprefs[key]
if data != None:
data = str(data)
if data == None or data == '':
if os.path.exists(filepath):
os.remove(filepath)
else:
try:
file(filepath,'wb').write(data)
except Exception as e:
pass
self.prefs = newprefs
return

View File

@@ -1,148 +0,0 @@
#!/usr/bin/env python
# vim:ts=4:sw=4:softtabstop=4:smarttab:expandtab
import os, sys
import signal
import threading
import subprocess
from subprocess import Popen, PIPE, STDOUT
# **heavily** chopped up and modfied version of asyncproc.py
# to make it actually work on Windows as well as Mac/Linux
# For the original see:
# "http://www.lysator.liu.se/~bellman/download/"
# author is "Thomas Bellman <bellman@lysator.liu.se>"
# available under GPL version 3 or Later
# create an asynchronous subprocess whose output can be collected in
# a non-blocking manner
# What a mess! Have to use threads just to get non-blocking io
# in a cross-platform manner
# luckily all thread use is hidden within this class
class Process(object):
def __init__(self, *params, **kwparams):
if len(params) <= 3:
kwparams.setdefault('stdin', subprocess.PIPE)
if len(params) <= 4:
kwparams.setdefault('stdout', subprocess.PIPE)
if len(params) <= 5:
kwparams.setdefault('stderr', subprocess.PIPE)
self.__pending_input = []
self.__collected_outdata = []
self.__collected_errdata = []
self.__exitstatus = None
self.__lock = threading.Lock()
self.__inputsem = threading.Semaphore(0)
self.__quit = False
self.__process = subprocess.Popen(*params, **kwparams)
if self.__process.stdin:
self.__stdin_thread = threading.Thread(
name="stdin-thread",
target=self.__feeder, args=(self.__pending_input,
self.__process.stdin))
self.__stdin_thread.setDaemon(True)
self.__stdin_thread.start()
if self.__process.stdout:
self.__stdout_thread = threading.Thread(
name="stdout-thread",
target=self.__reader, args=(self.__collected_outdata,
self.__process.stdout))
self.__stdout_thread.setDaemon(True)
self.__stdout_thread.start()
if self.__process.stderr:
self.__stderr_thread = threading.Thread(
name="stderr-thread",
target=self.__reader, args=(self.__collected_errdata,
self.__process.stderr))
self.__stderr_thread.setDaemon(True)
self.__stderr_thread.start()
def pid(self):
return self.__process.pid
def kill(self, signal):
self.__process.send_signal(signal)
# check on subprocess (pass in 'nowait') to act like poll
def wait(self, flag):
if flag.lower() == 'nowait':
rc = self.__process.poll()
else:
rc = self.__process.wait()
if rc != None:
if self.__process.stdin:
self.closeinput()
if self.__process.stdout:
self.__stdout_thread.join()
if self.__process.stderr:
self.__stderr_thread.join()
return self.__process.returncode
def terminate(self):
if self.__process.stdin:
self.closeinput()
self.__process.terminate()
# thread gets data from subprocess stdout
def __reader(self, collector, source):
while True:
data = os.read(source.fileno(), 65536)
self.__lock.acquire()
collector.append(data)
self.__lock.release()
if data == "":
source.close()
break
return
# thread feeds data to subprocess stdin
def __feeder(self, pending, drain):
while True:
self.__inputsem.acquire()
self.__lock.acquire()
if not pending and self.__quit:
drain.close()
self.__lock.release()
break
data = pending.pop(0)
self.__lock.release()
drain.write(data)
# non-blocking read of data from subprocess stdout
def read(self):
self.__lock.acquire()
outdata = "".join(self.__collected_outdata)
del self.__collected_outdata[:]
self.__lock.release()
return outdata
# non-blocking read of data from subprocess stderr
def readerr(self):
self.__lock.acquire()
errdata = "".join(self.__collected_errdata)
del self.__collected_errdata[:]
self.__lock.release()
return errdata
# non-blocking write to stdin of subprocess
def write(self, data):
if self.__process.stdin is None:
raise ValueError("Writing to process with stdin not a pipe")
self.__lock.acquire()
self.__pending_input.append(data)
self.__inputsem.release()
self.__lock.release()
# close stdinput of subprocess
def closeinput(self):
self.__lock.acquire()
self.__quit = True
self.__inputsem.release()
self.__lock.release()

View File

@@ -23,69 +23,10 @@ from struct import pack
from struct import unpack from struct import unpack
from alfcrypto import Topaz_Cipher from alfcrypto import Topaz_Cipher
from utilities import SafeUnbuffered
# Wrap a stream so that output gets flushed immediately from argv_utils import unicode_argv
# 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,str) or isinstance(data,unicode):
# str for Python3, unicode for Python2
data = data.encode(self.encoding,"replace")
try:
buffer = getattr(self.stream, 'buffer', self.stream)
# self.stream.buffer for Python3, self.stream for Python2
buffer.write(data)
buffer.flush()
except:
# We can do nothing if a write fails
raise
def __getattr__(self, attr):
return getattr(self.stream, attr)
iswindows = sys.platform.startswith('win')
isosx = sys.platform.startswith('darwin')
def unicode_argv():
if iswindows:
# Uses shell32.GetCommandLineArgvW to get sys.argv as a list of Unicode
# strings.
# Versions 2.x of Python don't support Unicode in sys.argv on
# Windows, with the underlying Windows API instead replacing multi-byte
# characters with '?'.
from ctypes import POINTER, byref, cdll, c_int, windll
from ctypes.wintypes import LPCWSTR, LPWSTR
GetCommandLineW = cdll.kernel32.GetCommandLineW
GetCommandLineW.argtypes = []
GetCommandLineW.restype = LPCWSTR
CommandLineToArgvW = windll.shell32.CommandLineToArgvW
CommandLineToArgvW.argtypes = [LPCWSTR, POINTER(c_int)]
CommandLineToArgvW.restype = POINTER(LPWSTR)
cmd = GetCommandLineW()
argc = c_int(0)
argv = CommandLineToArgvW(cmd, byref(argc))
if argc.value > 0:
# Remove Python executable and commands if present
start = argc.value - len(sys.argv)
return [argv[i] for i in
range(start, argc.value)]
# if we don't have any arguments at all, just pass back script name
# this should never happen
return ["mobidedrm.py"]
else:
argvencoding = sys.stdin.encoding or "utf-8"
return [arg if (isinstance(arg, str) or isinstance(arg,unicode)) else str(arg, argvencoding) for arg in sys.argv]
#global switch #global switch
debug = False debug = False
@@ -458,7 +399,7 @@ def usage(progname):
# Main # Main
def cli_main(): def cli_main():
argv=unicode_argv() argv=unicode_argv("topazextract.py")
progname = os.path.basename(argv[0]) progname = os.path.basename(argv[0])
print("TopazExtract v{0}.".format(__version__)) print("TopazExtract v{0}.".format(__version__))

View File

@@ -8,18 +8,6 @@ import sys
__license__ = 'GPL v3' __license__ = 'GPL v3'
DETAILED_MESSAGE = \
'You have personal information stored in this plugin\'s customization '+ \
'string from a previous version of this plugin.\n\n'+ \
'This new version of the plugin can convert that info '+ \
'into key data that the new plugin can then use (which doesn\'t '+ \
'require personal information to be stored/displayed in an insecure '+ \
'manner like the old plugin did).\n\nIf you choose NOT to migrate this data at this time '+ \
'you will be prompted to save that personal data to a file elsewhere; and you\'ll have '+ \
'to manually re-configure this plugin with your information.\n\nEither way... ' + \
'this new version of the plugin will not be responsible for storing that personal '+ \
'info in plain sight any longer.'
def uStrCmp (s1, s2, caseless=False): def uStrCmp (s1, s2, caseless=False):
import unicodedata as ud import unicodedata as ud
if sys.version_info[0] == 2: if sys.version_info[0] == 2:
@@ -45,3 +33,29 @@ def parseCustString(keystuff):
except: except:
pass pass
return userkeys return userkeys
# Wrap a stream so that output gets flushed immediately
# and also make sure that any unicode strings get safely
# 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,str) or isinstance(data,unicode):
# str for Python3, unicode for Python2
data = data.encode(self.encoding,"replace")
try:
buffer = getattr(self.stream, 'buffer', self.stream)
# self.stream.buffer for Python3, self.stream for Python2
buffer.write(data)
buffer.flush()
except:
# We can do nothing if a write fails
raise
def __getattr__(self, attr):
return getattr(self.stream, attr)

View File

@@ -0,0 +1,30 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Python 3's "zipfile" has an annoying bug where the `external_attr` field
of a ZIP file cannot be set to 0. However, if the original DRMed ZIP has
that set to 0 then we want the DRM-free ZIP to have that as 0, too.
See https://github.com/python/cpython/issues/87713
We cannot just set the "external_attr" to 0 as the code to save the ZIP
resets that variable.
So, here's a class that inherits from ZipInfo and ensures that EVERY
read access to that variable will return a 0 ...
"""
import zipfile
class ZeroedZipInfo(zipfile.ZipInfo):
def __init__(self, zinfo):
for k in self.__slots__:
if hasattr(zinfo, k):
setattr(self, k, getattr(zinfo, k))
def __getattribute__(self, name):
if name == "external_attr":
return 0
return object.__getattribute__(self, name)

View File

@@ -394,6 +394,19 @@ class ZipInfo (object):
extra = extra[ln+4:] extra = extra[ln+4:]
class ZeroedZipInfo(ZipInfo):
def __init__(self, zinfo):
for k in self.__slots__:
if hasattr(zinfo, k):
setattr(self, k, getattr(zinfo, k))
def __getattribute__(self, name):
if name == "external_attr":
return 0
return object.__getattribute__(self, name)
class _ZipDecrypter: class _ZipDecrypter:
"""Class to handle decryption of files stored within a ZIP archive. """Class to handle decryption of files stored within a ZIP archive.

View File

@@ -26,6 +26,7 @@ import sys, os
import zlib import zlib
import zipfilerugged import zipfilerugged
from zipfilerugged import ZipInfo, ZeroedZipInfo
import getopt import getopt
from struct import unpack from struct import unpack
@@ -36,12 +37,6 @@ _FILENAME_OFFSET = 30
_MAX_SIZE = 64 * 1024 _MAX_SIZE = 64 * 1024
_MIMETYPE = 'application/epub+zip' _MIMETYPE = 'application/epub+zip'
class ZipInfo(zipfilerugged.ZipInfo):
def __init__(self, *args, **kwargs):
if 'compress_type' in kwargs:
compress_type = kwargs.pop('compress_type')
super(ZipInfo, self).__init__(*args, **kwargs)
self.compress_type = compress_type
class fixZip: class fixZip:
def __init__(self, zinput, zoutput): def __init__(self, zinput, zoutput):
@@ -117,7 +112,8 @@ class fixZip:
# if epub write mimetype file first, with no compression # if epub write mimetype file first, with no compression
if self.ztype == 'epub': if self.ztype == 'epub':
# first get a ZipInfo with current time and no compression # first get a ZipInfo with current time and no compression
mimeinfo = ZipInfo(b'mimetype',compress_type=zipfilerugged.ZIP_STORED) mimeinfo = ZipInfo(b'mimetype')
mimeinfo.compress_type = zipfilerugged.ZIP_STORED
mimeinfo.internal_attr = 1 # text file mimeinfo.internal_attr = 1 # text file
try: try:
# if the mimetype is present, get its info, including time-stamp # if the mimetype is present, get its info, including time-stamp
@@ -129,8 +125,16 @@ class fixZip:
mimeinfo.internal_attr = oldmimeinfo.internal_attr mimeinfo.internal_attr = oldmimeinfo.internal_attr
mimeinfo.external_attr = oldmimeinfo.external_attr mimeinfo.external_attr = oldmimeinfo.external_attr
mimeinfo.create_system = oldmimeinfo.create_system mimeinfo.create_system = oldmimeinfo.create_system
mimeinfo.create_version = oldmimeinfo.create_version
mimeinfo.volume = oldmimeinfo.volume
except: except:
pass pass
# Python 3 has a bug where the external_attr is reset to `0o600 << 16`
# if it's NULL, so we need a workaround:
if mimeinfo.external_attr == 0:
mimeinfo = ZeroedZipInfo(mimeinfo)
self.outzip.writestr(mimeinfo, _MIMETYPE.encode('ascii')) self.outzip.writestr(mimeinfo, _MIMETYPE.encode('ascii'))
# write the rest of the files # write the rest of the files
@@ -145,13 +149,23 @@ class fixZip:
zinfo.filename = local_name zinfo.filename = local_name
# create new ZipInfo with only the useful attributes from the old info # create new ZipInfo with only the useful attributes from the old info
nzinfo = ZipInfo(zinfo.filename, zinfo.date_time, compress_type=zinfo.compress_type) nzinfo = ZipInfo(zinfo.filename)
nzinfo.date_time = zinfo.date_time
nzinfo.compress_type = zinfo.compress_type
nzinfo.comment=zinfo.comment nzinfo.comment=zinfo.comment
nzinfo.extra=zinfo.extra nzinfo.extra=zinfo.extra
nzinfo.internal_attr=zinfo.internal_attr nzinfo.internal_attr=zinfo.internal_attr
nzinfo.external_attr=zinfo.external_attr nzinfo.external_attr=zinfo.external_attr
nzinfo.create_system=zinfo.create_system nzinfo.create_system=zinfo.create_system
nzinfo.create_version = zinfo.create_version
nzinfo.volume = zinfo.volume
nzinfo.flag_bits = zinfo.flag_bits & 0x800 # preserve UTF-8 flag nzinfo.flag_bits = zinfo.flag_bits & 0x800 # preserve UTF-8 flag
# Python 3 has a bug where the external_attr is reset to `0o600 << 16`
# if it's NULL, so we need a workaround:
if nzinfo.external_attr == 0:
nzinfo = ZeroedZipInfo(nzinfo)
self.outzip.writestr(nzinfo,data) self.outzip.writestr(nzinfo,data)
self.bzf.close() self.bzf.close()

View File

@@ -265,13 +265,13 @@ class ReadOnlyTableWidgetItem(QTableWidgetItem):
def __init__(self, text): def __init__(self, text):
if text is None: if text is None:
text = '' text = ''
QTableWidgetItem.__init__(self, text, QTableWidgetItem.UserType) QTableWidgetItem.__init__(self, text, QTableWidgetItem.ItemType.UserType)
self.setFlags(Qt.ItemIsSelectable|Qt.ItemIsEnabled) self.setFlags(Qt.ItemIsSelectable|Qt.ItemIsEnabled)
class RatingTableWidgetItem(QTableWidgetItem): class RatingTableWidgetItem(QTableWidgetItem):
def __init__(self, rating, is_read_only=False): def __init__(self, rating, is_read_only=False):
QTableWidgetItem.__init__(self, '', QTableWidgetItem.UserType) QTableWidgetItem.__init__(self, '', QTableWidgetItem.ItemType.UserType)
self.setData(Qt.DisplayRole, rating) self.setData(Qt.DisplayRole, rating)
if is_read_only: if is_read_only:
self.setFlags(Qt.ItemIsSelectable|Qt.ItemIsEnabled) self.setFlags(Qt.ItemIsSelectable|Qt.ItemIsEnabled)
@@ -284,11 +284,11 @@ class DateTableWidgetItem(QTableWidgetItem):
if date_read is None or date_read == UNDEFINED_DATE and default_to_today: if date_read is None or date_read == UNDEFINED_DATE and default_to_today:
date_read = now() date_read = now()
if is_read_only: if is_read_only:
QTableWidgetItem.__init__(self, format_date(date_read, fmt), QTableWidgetItem.UserType) QTableWidgetItem.__init__(self, format_date(date_read, fmt), QTableWidgetItem.ItemType.UserType)
self.setFlags(Qt.ItemIsSelectable|Qt.ItemIsEnabled) self.setFlags(Qt.ItemIsSelectable|Qt.ItemIsEnabled)
self.setData(Qt.DisplayRole, QDateTime(date_read)) self.setData(Qt.DisplayRole, QDateTime(date_read))
else: else:
QTableWidgetItem.__init__(self, '', QTableWidgetItem.UserType) QTableWidgetItem.__init__(self, '', QTableWidgetItem.ItemType.UserType)
self.setData(Qt.DisplayRole, QDateTime(date_read)) self.setData(Qt.DisplayRole, QDateTime(date_read))
from calibre.gui2.library.delegates import DateDelegate as _DateDelegate from calibre.gui2.library.delegates import DateDelegate as _DateDelegate

View File

@@ -224,10 +224,17 @@ class SafeUnbuffered:
if self.encoding == None: if self.encoding == None:
self.encoding = "utf-8" self.encoding = "utf-8"
def write(self, data): def write(self, data):
if isinstance(data,str): if isinstance(data,str) or isinstance(data,unicode):
# str for Python3, unicode for Python2
data = data.encode(self.encoding,"replace") data = data.encode(self.encoding,"replace")
self.stream.buffer.write(data) try:
self.stream.buffer.flush() buffer = getattr(self.stream, 'buffer', self.stream)
# self.stream.buffer for Python3, self.stream for Python2
buffer.write(data)
buffer.flush()
except:
# We can do nothing if a write fails
raise
def __getattr__(self, attr): def __getattr__(self, attr):
return getattr(self.stream, attr) return getattr(self.stream, attr)

View File

@@ -224,5 +224,5 @@ class ReadOnlyTableWidgetItem(QTableWidgetItem):
def __init__(self, text): def __init__(self, text):
if text is None: if text is None:
text = '' text = ''
QTableWidgetItem.__init__(self, text, QTableWidgetItem.UserType) QTableWidgetItem.__init__(self, text, QTableWidgetItem.ItemType.UserType)
self.setFlags(Qt.ItemIsSelectable|Qt.ItemIsEnabled) self.setFlags(Qt.ItemIsSelectable|Qt.ItemIsEnabled)

View File

@@ -68,12 +68,20 @@ class SafeUnbuffered:
if self.encoding == None: if self.encoding == None:
self.encoding = "utf-8" self.encoding = "utf-8"
def write(self, data): def write(self, data):
if isinstance(data,unicode): if isinstance(data,str) or isinstance(data,unicode):
# str for Python3, unicode for Python2
data = data.encode(self.encoding,"replace") data = data.encode(self.encoding,"replace")
self.stream.write(data) try:
self.stream.flush() buffer = getattr(self.stream, 'buffer', self.stream)
# self.stream.buffer for Python3, self.stream for Python2
buffer.write(data)
buffer.flush()
except:
# We can do nothing if a write fails
raise
def __getattr__(self, attr): def __getattr__(self, attr):
return getattr(self.stream, attr) return getattr(self.stream, attr)
try: try:
from calibre.constants import iswindows, isosx from calibre.constants import iswindows, isosx

View File

@@ -39,12 +39,20 @@ class SafeUnbuffered:
if self.encoding == None: if self.encoding == None:
self.encoding = "utf-8" self.encoding = "utf-8"
def write(self, data): def write(self, data):
if isinstance(data,unicode): if isinstance(data,str) or isinstance(data,unicode):
# str for Python3, unicode for Python2
data = data.encode(self.encoding,"replace") data = data.encode(self.encoding,"replace")
self.stream.write(data) try:
self.stream.flush() buffer = getattr(self.stream, 'buffer', self.stream)
# self.stream.buffer for Python3, self.stream for Python2
buffer.write(data)
buffer.flush()
except:
# We can do nothing if a write fails
raise
def __getattr__(self, attr): def __getattr__(self, attr):
return getattr(self.stream, attr) return getattr(self.stream, attr)
try: try:
from calibre.constants import iswindows, isosx from calibre.constants import iswindows, isosx

View File

@@ -45,12 +45,20 @@ class SafeUnbuffered:
if self.encoding == None: if self.encoding == None:
self.encoding = "utf-8" self.encoding = "utf-8"
def write(self, data): def write(self, data):
if isinstance(data,unicode): if isinstance(data,str) or isinstance(data,unicode):
# str for Python3, unicode for Python2
data = data.encode(self.encoding,"replace") data = data.encode(self.encoding,"replace")
self.stream.write(data) try:
self.stream.flush() buffer = getattr(self.stream, 'buffer', self.stream)
# self.stream.buffer for Python3, self.stream for Python2
buffer.write(data)
buffer.flush()
except:
# We can do nothing if a write fails
raise
def __getattr__(self, attr): def __getattr__(self, attr):
return getattr(self.stream, attr) return getattr(self.stream, attr)
try: try:
from calibre.constants import iswindows, isosx from calibre.constants import iswindows, isosx

View File

@@ -56,12 +56,20 @@ class SafeUnbuffered:
if self.encoding == None: if self.encoding == None:
self.encoding = "utf-8" self.encoding = "utf-8"
def write(self, data): def write(self, data):
if isinstance(data,unicode): if isinstance(data,str) or isinstance(data,unicode):
# str for Python3, unicode for Python2
data = data.encode(self.encoding,"replace") data = data.encode(self.encoding,"replace")
self.stream.write(data) try:
self.stream.flush() buffer = getattr(self.stream, 'buffer', self.stream)
# self.stream.buffer for Python3, self.stream for Python2
buffer.write(data)
buffer.flush()
except:
# We can do nothing if a write fails
raise
def __getattr__(self, attr): def __getattr__(self, attr):
return getattr(self.stream, attr) return getattr(self.stream, attr)
try: try:
from calibre.constants import iswindows, isosx from calibre.constants import iswindows, isosx

View File

@@ -48,12 +48,20 @@ class SafeUnbuffered:
if self.encoding == None: if self.encoding == None:
self.encoding = "utf-8" self.encoding = "utf-8"
def write(self, data): def write(self, data):
if isinstance(data,unicode): if isinstance(data,str) or isinstance(data,unicode):
# str for Python3, unicode for Python2
data = data.encode(self.encoding,"replace") data = data.encode(self.encoding,"replace")
self.stream.write(data) try:
self.stream.flush() buffer = getattr(self.stream, 'buffer', self.stream)
# self.stream.buffer for Python3, self.stream for Python2
buffer.write(data)
buffer.flush()
except:
# We can do nothing if a write fails
raise
def __getattr__(self, attr): def __getattr__(self, attr):
return getattr(self.stream, attr) return getattr(self.stream, attr)
try: try:
from calibre.constants import iswindows, isosx from calibre.constants import iswindows, isosx

View File

@@ -51,12 +51,20 @@ class SafeUnbuffered:
if self.encoding == None: if self.encoding == None:
self.encoding = "utf-8" self.encoding = "utf-8"
def write(self, data): def write(self, data):
if isinstance(data,unicode): if isinstance(data,str) or isinstance(data,unicode):
# str for Python3, unicode for Python2
data = data.encode(self.encoding,"replace") data = data.encode(self.encoding,"replace")
self.stream.write(data) try:
self.stream.flush() buffer = getattr(self.stream, 'buffer', self.stream)
# self.stream.buffer for Python3, self.stream for Python2
buffer.write(data)
buffer.flush()
except:
# We can do nothing if a write fails
raise
def __getattr__(self, attr): def __getattr__(self, attr):
return getattr(self.stream, attr) return getattr(self.stream, attr)
try: try:
from calibre.constants import iswindows, isosx from calibre.constants import iswindows, isosx
@@ -292,7 +300,7 @@ if iswindows:
numBlocks, numExtraBytes = divmod(len(self.bytesToDecrypt), self.blockSize) numBlocks, numExtraBytes = divmod(len(self.bytesToDecrypt), self.blockSize)
if more == None: # no more calls to decrypt, should have all the data if more == None: # no more calls to decrypt, should have all the data
if numExtraBytes != 0: if numExtraBytes != 0:
raise DecryptNotBlockAlignedError, 'Data not block aligned on decrypt' raise DecryptNotBlockAlignedError('Data not block aligned on decrypt')
# hold back some bytes in case last decrypt has zero len # hold back some bytes in case last decrypt has zero len
if (more != None) and (numExtraBytes == 0) and (numBlocks >0) : if (more != None) and (numExtraBytes == 0) and (numBlocks >0) :
@@ -334,7 +342,7 @@ if iswindows:
def removePad(self, paddedBinaryString, blockSize): def removePad(self, paddedBinaryString, blockSize):
""" Remove padding from a binary string """ """ Remove padding from a binary string """
if not(0<len(paddedBinaryString)): if not(0<len(paddedBinaryString)):
raise DecryptNotBlockAlignedError, 'Expected More Data' raise DecryptNotBlockAlignedError('Expected More Data')
return paddedBinaryString[:-ord(paddedBinaryString[-1])] return paddedBinaryString[:-ord(paddedBinaryString[-1])]
class noPadding(Pad): class noPadding(Pad):
@@ -364,8 +372,8 @@ if iswindows:
self.blockSize = blockSize # blockSize is in bytes self.blockSize = blockSize # blockSize is in bytes
self.padding = padding # change default to noPadding() to get normal ECB behavior self.padding = padding # change default to noPadding() to get normal ECB behavior
assert( keySize%4==0 and NrTable[4].has_key(keySize/4)),'key size must be 16,20,24,29 or 32 bytes' assert( keySize%4==0 and keySize/4 in NrTable[4]),'key size must be 16,20,24,29 or 32 bytes'
assert( blockSize%4==0 and NrTable.has_key(blockSize/4)), 'block size must be 16,20,24,29 or 32 bytes' assert( blockSize%4==0 and blockSize/4 in NrTable), 'block size must be 16,20,24,29 or 32 bytes'
self.Nb = self.blockSize/4 # Nb is number of columns of 32 bit words self.Nb = self.blockSize/4 # Nb is number of columns of 32 bit words
self.Nk = keySize/4 # Nk is the key length in 32-bit words self.Nk = keySize/4 # Nk is the key length in 32-bit words
@@ -642,7 +650,7 @@ if iswindows:
def __init__(self, key = None, padding = padWithPadLen(), keySize=16): def __init__(self, key = None, padding = padWithPadLen(), keySize=16):
""" Initialize AES, keySize is in bytes """ """ Initialize AES, keySize is in bytes """
if not (keySize == 16 or keySize == 24 or keySize == 32) : if not (keySize == 16 or keySize == 24 or keySize == 32) :
raise BadKeySizeError, 'Illegal AES key size, must be 16, 24, or 32 bytes' raise BadKeySizeError('Illegal AES key size, must be 16, 24, or 32 bytes')
Rijndael.__init__( self, key, padding=padding, keySize=keySize, blockSize=16 ) Rijndael.__init__( self, key, padding=padding, keySize=keySize, blockSize=16 )
@@ -782,10 +790,11 @@ if iswindows:
# [c_char_p, c_ulong, c_char_p, c_ulong, c_ulong, c_ulong, c_char_p]) # [c_char_p, c_ulong, c_char_p, c_ulong, c_ulong, c_ulong, c_char_p])
def pbkdf2(self, passwd, salt, iter, keylen): def pbkdf2(self, passwd, salt, iter, keylen):
def xorstr( a, b ): def xorbytes( a, b ):
if len(a) != len(b): if len(a) != len(b):
raise Exception("xorstr(): lengths differ") raise Exception("xorbytes(): lengths differ")
return ''.join((chr(ord(x)^ord(y)) for x, y in zip(a, b))) return bytes([x ^ y for x, y in zip(a, b)])
def prf( h, data ): def prf( h, data ):
hm = h.copy() hm = h.copy()
@@ -797,13 +806,13 @@ if iswindows:
T = U T = U
for i in range(2, itercount+1): for i in range(2, itercount+1):
U = prf( h, U ) U = prf( h, U )
T = xorstr( T, U ) T = xorbytes( T, U )
return T return T
sha = hashlib.sha1 sha = hashlib.sha1
digest_size = sha().digest_size digest_size = sha().digest_size
# l - number of output blocks to produce # l - number of output blocks to produce
l = keylen / digest_size l = keylen // digest_size
if keylen % digest_size != 0: if keylen % digest_size != 0:
l += 1 l += 1
h = hmac.new( passwd, None, sha ) h = hmac.new( passwd, None, sha )

View File

@@ -32,12 +32,20 @@ class SafeUnbuffered:
if self.encoding == None: if self.encoding == None:
self.encoding = "utf-8" self.encoding = "utf-8"
def write(self, data): def write(self, data):
if isinstance(data,unicode): if isinstance(data,str) or isinstance(data,unicode):
# str for Python3, unicode for Python2
data = data.encode(self.encoding,"replace") data = data.encode(self.encoding,"replace")
self.stream.write(data) try:
self.stream.flush() buffer = getattr(self.stream, 'buffer', self.stream)
# self.stream.buffer for Python3, self.stream for Python2
buffer.write(data)
buffer.flush()
except:
# We can do nothing if a write fails
raise
def __getattr__(self, attr): def __getattr__(self, attr):
return getattr(self.stream, attr) return getattr(self.stream, attr)
iswindows = sys.platform.startswith('win') iswindows = sys.platform.startswith('win')
isosx = sys.platform.startswith('darwin') isosx = sys.platform.startswith('darwin')

View File

@@ -276,12 +276,20 @@ class SafeUnbuffered:
if self.encoding == None: if self.encoding == None:
self.encoding = "utf-8" self.encoding = "utf-8"
def write(self, data): def write(self, data):
if isinstance(data,unicode): if isinstance(data,str) or isinstance(data,unicode):
# str for Python3, unicode for Python2
data = data.encode(self.encoding,"replace") data = data.encode(self.encoding,"replace")
self.stream.write(data) try:
self.stream.flush() buffer = getattr(self.stream, 'buffer', self.stream)
# self.stream.buffer for Python3, self.stream for Python2
buffer.write(data)
buffer.flush()
except:
# We can do nothing if a write fails
raise
def __getattr__(self, attr): def __getattr__(self, attr):
return getattr(self.stream, attr) return getattr(self.stream, attr)
class KoboLibrary(object): class KoboLibrary(object):