parent
dce51ae232
commit
6fb13373cf
@ -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())
|
@ -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())
|
Loading…
Reference in New Issue