diff --git a/Adobe_EPUB_Tools/ineptkeymac.pyw b/Adobe_EPUB_Tools/ineptkeymac.pyw new file mode 100644 index 0000000..0dd1307 --- /dev/null +++ b/Adobe_EPUB_Tools/ineptkeymac.pyw @@ -0,0 +1,123 @@ +#! /usr/bin/env python + +# ineptkeymac.py, version 1 + +# This program runs on Mac OS X, version 10.6.2 and probably several other +# versions. It uses Python 2.6, but it probably also runs on all versions +# 2.x with x >= 5. + +# This program extracts the private RSA key for your ADE account in a +# standard binary form (DER format) in a file of your choosing. Its purpose +# is to make a backup of that key so that your legally bought ADE encoded +# ebooks can be salvaged in case they would no longer be supported by ADE +# software. No other usages are intended. + +# It has been tested with the key storage structure of ADE 1.7.1 and 1.7.2 +# and Sony Reader Library. + +# This software does not contain any encryption code. Its only use of +# external encryption software is the use of openssl for the conversion of +# the private key from pem to der format. It doesn't use encryption or +# decryption, however. + +# You can run this program from the command line (python ineptkeymac.py +# filename), or by doubleclicking when it has been associated with +# Pythonlauncher. When no filename is given it will show a dialog to obtain one. + +from __future__ import with_statement + +__license__ = 'GPL v3' + +import sys +import os +import xml.etree.ElementTree as etree +from contextlib import closing +import Tkinter +import Tkconstants +import tkFileDialog +from tkMessageBox import showerror +from subprocess import Popen, PIPE +import textwrap + +NS = 'http://ns.adobe.com/adept' +ACTFILE = '~/Library/Application Support/Adobe/Digital Editions/activation.dat' +HEADER = '-----BEGIN PRIVATE KEY-----\n' +FOOTER = '\n-----END PRIVATE KEY-----\n' + +Gui = False + +def get_key(): + '''Returns the private key as a binary string (DER format)''' + try: + filename = os.path.expanduser(ACTFILE) + tree = etree.parse(filename) + xpath = '//{%s}credentials/{%s}privateLicenseKey' % (NS, NS) + b64key = tree.findtext(xpath) + pemkey = HEADER + textwrap.fill(b64key, 64) + FOOTER + + cmd = ['openssl', 'rsa', '-outform', 'der'] + proc = Popen(cmd, stdin=PIPE, stdout=PIPE, stderr=PIPE) + stdout, stderr = proc.communicate(pemkey) + + if proc.returncode != 0: + error("openssl error: " + stderr) + return None + return stdout + + except IOError: + error("Can find keyfile. Maybe you should activate your Adobe ID.") + sys.exit(1) + +def store_key(key, keypath): + '''Store the key in the file given as keypath. If no keypath is given a + dialog will ask for one.''' + + try: + if keypath is None: + keypath = get_keypath() + if not keypath: # Cancelled + return + + with closing(open(keypath, 'wb')) as outf: + outf.write(key) + + except IOError, e: + error("Can write keyfile: " + str(e)) + +def get_keypath(): + keypath = tkFileDialog.asksaveasfilename( + parent = None, title = 'Select file to store ADEPT key', + initialdir = os.path.expanduser('~/Desktop'), + initialfile = 'adeptkey.der', + defaultextension = '.der', filetypes = [('DER-encoded files', '.der'), + ('All Files', '.*')]) + if keypath: + keypath = os.path.normpath(keypath) + return keypath + +def error(text): + print text + if Gui: showerror('Error!', text) + +def gui_main(): + root = Tkinter.Tk() + root.iconify() + global Gui + Gui = True + store_key(get_key(), None) + + return 0 + +def main(argv=sys.argv): + progname = os.path.basename(argv[0]) + + if len(argv) == 1: # assume GUI if no argument given + return gui_main() + if len(argv) != 2: + print "usage: %s KEYFILE" % (progname,) + return 1 + + store_key(get_key(), argv[1]) + +if __name__ == '__main__': + sys.exit(main()) diff --git a/Topaz_Tools/lib/convert2xml.py b/Topaz_Tools/lib/convert2xml.py index 958e3b9..18ae3f0 100644 --- a/Topaz_Tools/lib/convert2xml.py +++ b/Topaz_Tools/lib/convert2xml.py @@ -1,6 +1,6 @@ #! /usr/bin/python # vim:ts=4:sw=4:softtabstop=4:smarttab:expandtab -# For use with Topaz Scripts Version 2.3 +# For use with Topaz Scripts Version 2.4 class Unbuffered: def __init__(self, stream): @@ -243,6 +243,8 @@ class PageParser(object): 'region.h' : (1, 'scalar_number', 0, 0), 'region.w' : (1, 'scalar_number', 0, 0), + 'empty_text_region' : (1, 'snippets', 1, 0), + 'img' : (1, 'snippets', 1, 0), 'img.x' : (1, 'scalar_number', 0, 0), 'img.y' : (1, 'scalar_number', 0, 0), diff --git a/Topaz_Tools/lib/gensvg.py b/Topaz_Tools/lib/gensvg.py index 82f6bc7..040fe9b 100644 --- a/Topaz_Tools/lib/gensvg.py +++ b/Topaz_Tools/lib/gensvg.py @@ -28,15 +28,24 @@ class GParser(object): self.gh = self.getData('info.glyph.h') self.gw = self.getData('info.glyph.w') self.guse = self.getData('info.glyph.use') - self.count = len(self.guse) + if self.guse : + self.count = len(self.guse) + else : + self.count = 0 self.gvtx = self.getData('info.glyph.vtx') self.glen = self.getData('info.glyph.len') self.gdpi = self.getData('info.glyph.dpi') self.vx = self.getData('info.vtx.x') self.vy = self.getData('info.vtx.y') self.vlen = self.getData('info.len.n') - self.glen.append(len(self.vlen)) - self.gvtx.append(len(self.vx)) + if self.vlen : + self.glen.append(len(self.vlen)) + elif self.glen: + self.glen.append(0) + if self.vx : + self.gvtx.append(len(self.vx)) + elif self.gvtx : + self.gvtx.append(0) def getData(self, path): result = None diff --git a/eReader_Tools/eReaderPDB2PMLZ.pyw b/eReader_Tools/eReaderPDB2PMLZ.pyw new file mode 100644 index 0000000..635f3fe --- /dev/null +++ b/eReader_Tools/eReaderPDB2PMLZ.pyw @@ -0,0 +1,180 @@ +#!/usr/bin/env python +# vim:ts=4:sw=4:softtabstop=4:smarttab:expandtab + +import sys +sys.path.append('lib') + +import os, os.path, urllib +import subprocess +from subprocess import Popen, PIPE, STDOUT +import Tkinter +import Tkconstants +import tkFileDialog +import tkMessageBox +import subasyncio +from subasyncio import Process +from scrolltextwidget import ScrolledText + +class MainDialog(Tkinter.Frame): + def __init__(self, root): + Tkinter.Frame.__init__(self, root, border=5) + self.root = root + self.interval = 2000 + self.p2 = None + self.status = Tkinter.Label(self, text='eReader eBook Conversion to PMLZ') + self.status.pack(fill=Tkconstants.X, expand=1) + body = Tkinter.Frame(self) + body.pack(fill=Tkconstants.X, expand=1) + sticky = Tkconstants.E + Tkconstants.W + body.grid_columnconfigure(1, weight=2) + + Tkinter.Label(body, text='eBook PDB input file').grid(row=0, sticky=Tkconstants.E) + self.pdbpath = Tkinter.Entry(body, width=50) + self.pdbpath.grid(row=0, column=1, sticky=sticky) + cwd = os.getcwdu() + cwd = cwd.encode('utf-8') + self.pdbpath.insert(0, cwd) + button = Tkinter.Button(body, text="...", command=self.get_pdbpath) + button.grid(row=0, column=2) + + Tkinter.Label(body, text='Name on CC').grid(row=1, sticky=Tkconstants.E) + self.name = Tkinter.StringVar() + self.nameinfo = Tkinter.Entry(body, width=40, textvariable=self.name) + self.nameinfo.grid(row=1, column=1, sticky=sticky) + + Tkinter.Label(body, text='Last 8 digits of CC Number').grid(row=2, sticky=Tkconstants.E) + self.ccnum = Tkinter.StringVar() + self.ccinfo = Tkinter.Entry(body, width=10, textvariable=self.ccnum) + self.ccinfo.grid(row=2, column=1, sticky=sticky) + + msg1 = 'Conversion Log \n\n' + self.stext = ScrolledText(body, bd=5, relief=Tkconstants.RIDGE, height=15, width=60, wrap=Tkconstants.WORD) + self.stext.grid(row=3, column=0, columnspan=2,sticky=sticky) + self.stext.insert(Tkconstants.END,msg1) + + buttons = Tkinter.Frame(self) + buttons.pack() + self.sbotton = Tkinter.Button( + buttons, text="Start", width=10, command=self.convertit) + self.sbotton.pack(side=Tkconstants.LEFT) + + Tkinter.Frame(buttons, width=10).pack(side=Tkconstants.LEFT) + self.qbutton = Tkinter.Button( + buttons, text="Quit", width=10, command=self.quitting) + self.qbutton.pack(side=Tkconstants.RIGHT) + + # read from subprocess pipe without blocking + # invoked every interval via the widget "after" + # option being used, so need to reset it for the next time + def processPipe(self): + poll = self.p2.wait('nowait') + if poll != None: + text = self.p2.readerr() + text += self.p2.read() + msg = text + '\n\n' + 'File successfully converted\n' + if poll != 0: + msg = text + '\n\n' + 'Error: Conversion Failed\n' + self.showCmdOutput(msg) + self.p2 = None + self.sbotton.configure(state='normal') + return + text = self.p2.readerr() + text += self.p2.read() + self.showCmdOutput(text) + # make sure we get invoked again by event loop after interval + self.stext.after(self.interval,self.processPipe) + return + + # post output from subprocess in scrolled text widget + def showCmdOutput(self, msg): + if msg and msg !='': + msg = msg.encode('utf-8') + self.stext.insert(Tkconstants.END,msg) + self.stext.yview_pickplace(Tkconstants.END) + return + + # run erdr2pml.py as a subprocess via pipes and collect stdout + def erdr(self, infile, name, ccnum): + # os.putenv('PYTHONUNBUFFERED', '1') + cmdline = 'python ./lib/erdr2pml.py --make-pmlz "' + infile + '" "' + name + '" ' + ccnum + if sys.platform[0:3] == 'win': + search_path = os.environ['PATH'] + search_path = search_path.lower() + if search_path.find('python') >= 0: + cmdline = 'python lib\erdr2pml.py --make-pmlz "' + infile + '" "' + name + '" ' + ccnum + else : + cmdline = 'lib\erdr2pml.py --make-pmlz "' + infile + '" "' + name + '" ' + ccnum + + cmdline = cmdline.encode(sys.getfilesystemencoding()) + p2 = Process(cmdline, shell=True, bufsize=1, stdin=None, stdout=PIPE, stderr=PIPE, close_fds=False) + return p2 + + + def get_pdbpath(self): + pdbpath = tkFileDialog.askopenfilename( + parent=None, title='Select eReader PDB File', + defaultextension='.pdb', filetypes=[('eReader eBooks', '.pdb'), + ('All Files', '.*')]) + if pdbpath: + pdbpath = os.path.normpath(pdbpath) + self.pdbpath.delete(0, Tkconstants.END) + self.pdbpath.insert(0, pdbpath) + return + + def quitting(self): + # kill any still running subprocess + if self.p2 != None: + if (self.p2.wait('nowait') == None): + self.p2.terminate() + self.root.destroy() + + # actually ready to run the subprocess and get its output + def convertit(self): + # now disable the button to prevent multiple launches + self.sbotton.configure(state='disabled') + pdbpath = self.pdbpath.get() + if not pdbpath or not os.path.exists(pdbpath): + self.status['text'] = 'Specified eBook file does not exist' + self.sbotton.configure(state='normal') + return + name = self.name.get() + if not name or name == '': + self.status['text'] = 'Your forgot to enter the Name on the CC' + self.sbotton.configure(state='normal') + return + ccnum = self.ccnum.get() + if not ccnum or ccnum == '': + self.status['text'] = 'Your forgot to enter the last 8 digits on the CC' + self.sbotton.configure(state='normal') + return + + log = 'Command = "python erdr2pml.py --make-pmlz "\n' + log += 'PDB Path = "'+ pdbpath + '"\n' + log += 'Name = "' + name + '"\n' + log += 'Last 8 of CC = "' + ccnum + '"\n' + log += '\n\n' + log += 'Please Wait ...\n' + log = log.encode('utf-8') + self.stext.insert(Tkconstants.END,log) + self.p2 = self.erdr(pdbpath, name, ccnum) + + # python does not seem to allow you to create + # your own eventloop which every other gui does - strange + # so need to use the widget "after" command to force + # event loop to run non-gui events every interval + self.stext.after(self.interval,self.processPipe) + return + + +def main(argv=None): + root = Tkinter.Tk() + root.title('eReader PDB to PMLZ Conversion') + root.resizable(True, False) + root.minsize(300, 0) + MainDialog(root).pack(fill=Tkconstants.X, expand=1) + root.mainloop() + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/eReader_Tools/lib/erdr2pml.py b/eReader_Tools/lib/erdr2pml.py index 3e9133a..4067ff6 100644 --- a/eReader_Tools/lib/erdr2pml.py +++ b/eReader_Tools/lib/erdr2pml.py @@ -52,8 +52,9 @@ # 0.11 - fixups for using correct xml for footnotes and sidebars for use with Dropbook # 0.12 - Fix added to prevent lowercasing of image names when the pml code itself uses a different case in the link name. # 0.13 - change to unbuffered stdout for use with gui front ends +# 0.14 - contributed enhancement to support --make-pmlz switch -__version__='0.13' +__version__='0.14' # Import Psyco if available try: @@ -85,7 +86,7 @@ class Unbuffered: import sys sys.stdout=Unbuffered(sys.stdout) -import struct, binascii, zlib, os, os.path, urllib +import struct, binascii, getopt, zlib, os, os.path, urllib, tempfile try: from hashlib import sha1 @@ -592,27 +593,54 @@ def convertEreaderToPml(infile, name, cc, outdir): # file(os.path.join(outdir, 'bookinfo.txt'),'wb').write(bkinfo) +def usage(): + print "Converts DRMed eReader books to PML Source" + print "Usage:" + print " erdr2pml [options] infile.pdb [outdir] \"your name\" credit_card_number " + print " " + print "Options: " + print " -h prints this message" + print " --make-pmlz create PMLZ instead of using output directory" + print " " + print "Note:" + print " if ommitted, outdir defaults based on 'infile.pdb'" + print " It's enough to enter the last 8 digits of the credit card number" + return + def main(argv=None): global bookname - if argv is None: - argv = sys.argv + try: + opts, args = getopt.getopt(sys.argv[1:], "h", ["make-pmlz"]) + except getopt.GetoptError, err: + print str(err) + usage() + return 1 + make_pmlz = False + zipname = None + for o, a in opts: + if o == "-h": + usage() + return 0 + elif o == "--make-pmlz": + make_pmlz = True + zipname = '' print "eRdr2Pml v%s. Copyright (c) 2009 The Dark Reverser" % __version__ - if len(argv)!=4 and len(argv)!=5: - print "Converts DRMed eReader books to PML Source" - print "Usage:" - print " erdr2pml infile.pdb [outdir] \"your name\" credit_card_number " - print "Note:" - print " if ommitted, outdir defaults based on 'infile.pdb'" - print " It's enough to enter the last 8 digits of the credit card number" + if len(args)!=3 and len(args)!=4: + usage() return 1 else: - if len(argv)==4: - infile, name, cc = argv[1], argv[2], argv[3] + if len(args)==3: + infile, name, cc = args[0], args[1], args[2] outdir = infile[:-4] + '_Source' - elif len(argv)==5: - infile, outdir, name, cc = argv[1], argv[2], argv[3], argv[4] + elif len(args)==4: + infile, outdir, name, cc = args[0], args[1], args[2], args[3] + + if make_pmlz : + # ignore specified outdir, use tempdir instead + outdir = tempfile.mkdtemp() + bookname = os.path.splitext(os.path.basename(infile))[0] try: @@ -620,10 +648,38 @@ def main(argv=None): import time start_time = time.time() convertEreaderToPml(infile, name, cc, outdir) + + if make_pmlz : + import zipfile + import shutil + print " Creating PMLZ file" + zipname = infile[:-4] + '.pmlz' + myZipFile = zipfile.ZipFile(zipname,'w',zipfile.ZIP_STORED, False) + list = os.listdir(outdir) + for file in list: + localname = file + filePath = os.path.join(outdir,file) + if os.path.isfile(filePath): + myZipFile.write(filePath, localname) + elif os.path.isdir(filePath): + imageList = os.listdir(filePath) + localimgdir = os.path.basename(filePath) + for image in imageList: + localname = os.path.join(localimgdir,image) + imagePath = os.path.join(filePath,image) + if os.path.isfile(imagePath): + myZipFile.write(imagePath, localname) + myZipFile.close() + # remove temporary directory + shutil.rmtree(outdir) + end_time = time.time() search_time = end_time - start_time print 'elapsed time: %.2f seconds' % (search_time, ) - print 'output in %s' % outdir + if make_pmlz : + print 'output in %s' % zipname + else : + print 'output in %s' % outdir print "done" except ValueError, e: print "Error: %s" % e