Fix Python 3 correctness bugs found in code review

Fix str/bytes and leftover-py2 defects across the plugin, several on live
decryption paths:

  - erdr2pml.py: getText() footnote/sidebar handling mixed a bytes
    accumulator with str literals and called ord() on a bytes element,
    crashing on eReader/.pdb books that contain footnotes or sidebars.
  - ion.py: readdecimal() did `[ord(x) for x in self.read(...)]` over
    bytes (ord(int)), crashing KFX decryption on Ion DECIMAL values;
    printlob() had the same ord()-over-bytes in its debug path.
  - zipfilerugged.py: `isinstance(file, unicode)` raised NameError when a
    file-like object (not a path string) was passed to the ZipFile.
  - utilities.py: SafeUnbuffered.write referenced the undefined `unicode`
    (same fix already applied to the obok copy).
  - ineptpdf.py: ord(bookkey[0]) over an int in an error-diagnostic print.
  - epubfontdecrypt.py: removed a dead py2 itertools.izip fallback.
  - convert2xml.py: escapestr did bytes.replace(str, ...).
  - kgenpids.py: decode() built a str result then += bytes.

Cleanups from the earlier shim removal: drop now-unused `import sys`
(alfcrypto, utilities, kgenpids); unicode_argv returns list(sys.argv) so
callers can't mutate the process-global sys.argv.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-22 20:35:11 -05:00
co-authored by Claude Opus 4.8
parent 15592b84c8
commit ae2231727f
10 changed files with 20 additions and 31 deletions
-1
View File
@@ -8,7 +8,6 @@
# pbkdf2.py Copyright © 2009 Daniel Holth <dholth@fastmail.fm>
# pbkdf2.py This code may be freely used and modified for any purpose.
import sys
import hmac
from struct import pack
import hashlib
+1 -1
View File
@@ -8,7 +8,7 @@ def unicode_argv(default_name):
# On Python 3 sys.argv is already a list of Unicode strings on every
# platform, so no special handling is needed.
if len(sys.argv) > 0:
return sys.argv
return list(sys.argv)
# if we don't have any arguments at all, just pass back the script name
# this should never happen
return [default_name]
+4 -4
View File
@@ -124,10 +124,10 @@ class Dictionary(object):
self.pos = 0
def escapestr(self, str):
str = str.replace('&','&amp;')
str = str.replace('<','&lt;')
str = str.replace('>','&gt;')
str = str.replace('=','&#61;')
str = str.replace(b'&',b'&amp;')
str = str.replace(b'<',b'&lt;')
str = str.replace(b'>',b'&gt;')
str = str.replace(b'=',b'&#61;')
return str
def lookup(self,val):
+1 -6
View File
@@ -134,12 +134,7 @@ class Decryptor(object):
return data
def deobfuscate_single_data(self, key, data):
try:
msg = bytes([c^k for c,k in zip(data, itertools.cycle(key))])
except TypeError:
# Python 2
msg = ''.join(chr(ord(c)^ord(k)) for c,k in itertools.izip(data, itertools.cycle(key)))
return msg
return bytes([c^k for c,k in zip(data, itertools.cycle(key))])
+8 -8
View File
@@ -314,7 +314,7 @@ class EreaderProcessor(object):
# now handle footnotes pages
if self.num_footnote_pages > 0:
r += '\n'
r += b'\n'
# the record 0 of the footnote section must pass through the Xor Table to make it useful
sect = self.section_reader(self.first_footnote_page)
fnote_ids = deXOR(sect, 0, self.xortable)
@@ -322,11 +322,11 @@ class EreaderProcessor(object):
des = DES.new(fixKey(self.content_key), DES.MODE_ECB)
for i in range(1,self.num_footnote_pages):
logging.debug('get footnotepage %d', i)
id_len = ord(fnote_ids[2])
id_len = fnote_ids[2]
id = fnote_ids[3:3+id_len]
fmarker = '<footnote id="%s">\n' % id
fmarker = b'<footnote id="%s">\n' % id
fmarker += zlib.decompress(des.decrypt(self.section_reader(self.first_footnote_page + i)))
fmarker += '\n</footnote>\n'
fmarker += b'\n</footnote>\n'
r += fmarker
fnote_ids = fnote_ids[id_len+4:]
@@ -338,18 +338,18 @@ class EreaderProcessor(object):
# now handle sidebar pages
if self.num_sidebar_pages > 0:
r += '\n'
r += b'\n'
# the record 0 of the sidebar section must pass through the Xor Table to make it useful
sect = self.section_reader(self.first_sidebar_page)
sbar_ids = deXOR(sect, 0, self.xortable)
# the remaining records of the sidebar sections need to be decoded with the content_key and zlib inflated
des = DES.new(fixKey(self.content_key), DES.MODE_ECB)
for i in range(1,self.num_sidebar_pages):
id_len = ord(sbar_ids[2])
id_len = sbar_ids[2]
id = sbar_ids[3:3+id_len]
smarker = '<sidebar id="%s">\n' % id
smarker = b'<sidebar id="%s">\n' % id
smarker += zlib.decompress(des.decrypt(self.section_reader(self.first_sidebar_page + i)))
smarker += '\n</sidebar>\n'
smarker += b'\n</sidebar>\n'
r += smarker
sbar_ids = sbar_ids[id_len+4:]
+1 -1
View File
@@ -1624,7 +1624,7 @@ class PDFDocument(object):
print("ebx_V is %d and ebx_type is %d" % (ebx_V, ebx_type))
print("length is %d and len(bookkey) is %d" % (length, len(bookkey)))
if len(bookkey) > 0:
print("bookkey[0] is %d" % ord(bookkey[0]))
print("bookkey[0] is %d" % bookkey[0])
if ebx_V == 3:
V = 3
else:
+2 -2
View File
@@ -420,7 +420,7 @@ class BinaryIonParser(object):
_assert(self.localremaining <= 8, "Decimal overflow")
signed = False
b = [ord(x) for x in self.read(self.localremaining)]
b = list(self.read(self.localremaining))
if (b[0] & 0x80) != 0:
b[0] = b[0] & 0x7F
signed = True
@@ -657,7 +657,7 @@ class BinaryIonParser(object):
result = ""
for i in b:
result += ("%02x " % ord(i))
result += ("%02x " % i)
if len(result) > 0:
result = result[:-1]
+1 -2
View File
@@ -14,7 +14,6 @@ __version__ = '3.0'
# 3.0 - Python 3 for calibre 5.0
import sys
import os, csv
import binascii
import zlib
@@ -69,7 +68,7 @@ def encodeHash(data,map):
# Decode the string in data with the characters in map. Returns the decoded bytes
def decode(data,map):
result = ''
result = b''
for i in range (0,len(data)-1,2):
high = map.find(data[i])
low = map.find(data[i+1])
+1 -4
View File
@@ -3,8 +3,6 @@
#@@CALIBRE_COMPAT_CODE@@
import sys
__license__ = 'GPL v3'
def uStrCmp (s1, s2, caseless=False):
@@ -29,8 +27,7 @@ class SafeUnbuffered:
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
if isinstance(data,str):
data = data.encode(self.encoding,"replace")
try:
buffer = getattr(self.stream, 'buffer', self.stream)
+1 -2
View File
@@ -676,8 +676,7 @@ class ZipFile:
self.comment = b''
# Check if we were passed a file-like object
# "str" is python3, "unicode" is python2
if isinstance(file, str) or isinstance(file, unicode):
if isinstance(file, str):
self._filePassed = 0
self.filename = file
modeDict = {'r' : 'rb', 'w': 'wb', 'a' : 'r+b'}