#!/usr/bin/env python
# -*- coding: utf-8 -*-

# DeDRM.pyw
# Copyright 2010-2016 some_updates, Apprentice Alf and Apprentice Harper

# Revision history:
#   6.0.0 - Release along with unified plugin
#   6.0.1 - Bug Fixes for Windows App
#   6.0.2 - Fixed problem with spaces in paths and the bat file
#   6.0.3 - Fix for Windows non-ascii user names
#   6.0.4 - Fix for other potential unicode problems
#   6.0.5 - Fix typo
#   6.2.0 - Update to match plugin and AppleScript
#   6.2.1 - Fix for non-ascii user names
#   6.2.2 - Added URL method for B&N/nook books
#   6.3.0 - Add in Android support
#   6.3.1 - Version bump for clarity
#   6.3.2 - Version bump to match plugin
#   6.3.3 - Version bump to match plugin
#   6.3.4 - Version bump to match plugin
#   6.3.5 - Version bump to match plugin
#   6.3.6 - Version bump to match plugin
#   6.4.0 - Fix for Kindle for PC encryption change
#   6.4.1 - Fix for new tags in Topaz ebooks
#   6.4.2 - Fix for new tags in Topaz ebooks, and very small Topaz ebooks
#   6.4.3 - Version bump to match plugin & Mac app
#   6.5.0 - Fix for some new tags in Topaz ebooks
#   6.5.1 - Version bump to match plugin & Mac app
#   6.5.2 - Fix for a new tag in Topaz ebooks
#   6.5.3 - Explicitly warn about KFX files
#   6.5.4 - PDF float fix.

__version__ = '6.5.4'

import sys
import os, os.path
sys.path.append(os.path.join(sys.path[0],"lib"))
import sys, os
import codecs

from argv_utils import add_cp65001_codec, set_utf8_default_encoding, unicode_argv
add_cp65001_codec()
set_utf8_default_encoding()


import shutil
import Tkinter
from Tkinter import *
import Tkconstants
import tkFileDialog
from scrolltextwidget import ScrolledText
from activitybar import ActivityBar
if sys.platform.startswith("win"):
    from askfolder_ed import AskFolder
import re
import simpleprefs
import traceback

from Queue import Full
from Queue import Empty
from multiprocessing import Process, Queue

from scriptinterface import decryptepub, decryptpdb, decryptpdf, decryptk4mobi


# Wrap a stream so that output gets flushed immediately
# and appended to shared queue
class QueuedUTF8Stream:
    def __init__(self, stream, q):
        self.stream = stream
        self.encoding = 'utf-8'
        self.q = q
    def write(self, data):
        if isinstance(data,unicode):
            data = data.encode('utf-8',"replace")
        self.q.put(data)
    def __getattr__(self, attr):
        return getattr(self.stream, attr)

class DrmException(Exception):
    pass

class MainApp(Tk):
    def __init__(self, apphome, dnd=False, filenames=[]):
        Tk.__init__(self)
        self.withdraw()
        self.dnd = dnd
        self.apphome = apphome

        # preference settings
        # [dictionary key, file in preferences directory where info is stored]
        description = [ ['pids'   , 'pidlist.txt'   ],
                        ['serials', 'seriallist.txt'],
                        ['sdrms'  , 'sdrmlist.txt'  ],
                        ['outdir' , 'outdir.txt'    ]]
        self.po = simpleprefs.SimplePrefs("DeDRM",description)
        if self.dnd:
            self.cd = ConvDialog(self)
            prefs = self.getPreferences()
            self.cd.doit(prefs, filenames)
        else:
            prefs = self.getPreferences()
            self.pd = PrefsDialog(self, prefs)
            self.cd = ConvDialog(self)
            self.pd.show()

    def getPreferences(self):
        prefs = self.po.getPreferences()
        prefdir = prefs['dir']
        adeptkeyfile = os.path.join(prefdir,'adeptkey.der')
        if not os.path.exists(adeptkeyfile):
            import adobekey
            try:
                adobekey.getkey(adeptkeyfile)
            except:
                pass
        kindlekeyfile = os.path.join(prefdir,'kindlekey.k4i')
        if not os.path.exists(kindlekeyfile):
            import kindlekey
            try:
                kindlekey.getkey(kindlekeyfile)
            except:
                traceback.print_exc()
                pass
        bnepubkeyfile = os.path.join(prefdir,'bnepubkey.b64')
        if not os.path.exists(bnepubkeyfile):
            import ignoblekey
            try:
                ignoblekey.getkey(bnepubkeyfile)
            except:
                traceback.print_exc()
                pass
        return prefs

    def setPreferences(self, newprefs):
        prefdir = self.po.prefdir
        if 'adkfile' in newprefs:
            dfile = newprefs['adkfile']
            fname = os.path.basename(dfile)
            nfile = os.path.join(prefdir,fname)
            if os.path.isfile(dfile):
                shutil.copyfile(dfile,nfile)
        if 'bnkfile' in newprefs:
            dfile = newprefs['bnkfile']
            fname = os.path.basename(dfile)
            nfile = os.path.join(prefdir,fname)
            if os.path.isfile(dfile):
                shutil.copyfile(dfile,nfile)
        if 'kindlefile' in newprefs:
            dfile = newprefs['kindlefile']
            fname = os.path.basename(dfile)
            nfile = os.path.join(prefdir,fname)
            if os.path.isfile(dfile):
                shutil.copyfile(dfile,nfile)
        if 'androidfile' in newprefs:
            dfile = newprefs['androidfile']
            fname = os.path.basename(dfile)
            nfile = os.path.join(prefdir,fname)
            if os.path.isfile(dfile):
                shutil.copyfile(dfile,nfile)
        self.po.setPreferences(newprefs)
        return

    def alldone(self):
        if not self.dnd:
            self.pd.enablebuttons()
        else:
            self.destroy()

class PrefsDialog(Toplevel):
    def __init__(self, mainapp, prefs_array):
        Toplevel.__init__(self, mainapp)
        self.withdraw()
        self.protocol("WM_DELETE_WINDOW", self.withdraw)
        self.title("DeDRM " + __version__)
        self.prefs_array = prefs_array
        self.status = Tkinter.Label(self, text='Setting Preferences')
        self.status.pack(fill=Tkconstants.X, expand=1)
        body = Tkinter.Frame(self)
        self.body = body
        body.pack(fill=Tkconstants.X, expand=1)
        sticky = Tkconstants.E + Tkconstants.W
        body.grid_columnconfigure(1, weight=2)

        cur_row = 0
        Tkinter.Label(body, text='Adobe Key file (adeptkey.der)').grid(row=cur_row, sticky=Tkconstants.E)
        self.adkpath = Tkinter.Entry(body, width=50)
        self.adkpath.grid(row=cur_row, column=1, sticky=sticky)
        prefdir = self.prefs_array['dir']
        keyfile = os.path.join(prefdir,'adeptkey.der')
        if os.path.isfile(keyfile):
            path = keyfile
            self.adkpath.insert(cur_row, path)
        button = Tkinter.Button(body, text="...", command=self.get_adkpath)
        button.grid(row=cur_row, column=2)

        cur_row = cur_row + 1
        Tkinter.Label(body, text='Kindle Key file (kindlekey.k4i)').grid(row=cur_row, sticky=Tkconstants.E)
        self.kkpath = Tkinter.Entry(body, width=50)
        self.kkpath.grid(row=cur_row, column=1, sticky=sticky)
        prefdir = self.prefs_array['dir']
        keyfile = os.path.join(prefdir,'kindlekey.k4i')
        if os.path.isfile(keyfile):
            path = keyfile
            self.kkpath.insert(0, path)
        button = Tkinter.Button(body, text="...", command=self.get_kkpath)
        button.grid(row=cur_row, column=2)

        cur_row = cur_row + 1
        Tkinter.Label(body, text='Android Kindle backup file (backup.ab)').grid(row=cur_row, sticky=Tkconstants.E)
        self.akkpath = Tkinter.Entry(body, width=50)
        self.akkpath.grid(row=cur_row, column=1, sticky=sticky)
        prefdir = self.prefs_array['dir']
        keyfile = os.path.join(prefdir,'backup.ab')
        if os.path.isfile(keyfile):
            path = keyfile
            self.akkpath.insert(0, path)
        button = Tkinter.Button(body, text="...", command=self.get_akkpath)
        button.grid(row=cur_row, column=2)

        cur_row = cur_row + 1
        Tkinter.Label(body, text='Barnes and Noble Key file (bnepubkey.b64)').grid(row=cur_row, sticky=Tkconstants.E)
        self.bnkpath = Tkinter.Entry(body, width=50)
        self.bnkpath.grid(row=cur_row, column=1, sticky=sticky)
        prefdir = self.prefs_array['dir']
        keyfile = os.path.join(prefdir,'bnepubkey.b64')
        if os.path.isfile(keyfile):
            path = keyfile
            self.bnkpath.insert(0, path)
        button = Tkinter.Button(body, text="...", command=self.get_bnkpath)
        button.grid(row=cur_row, column=2)

        cur_row = cur_row + 1
        Tkinter.Label(body, text='Mobipocket PID list\n(8 or 10 characters, comma separated)').grid(row=cur_row, sticky=Tkconstants.E)
        self.pidnums = Tkinter.StringVar()
        self.pidinfo = Tkinter.Entry(body, width=50, textvariable=self.pidnums)
        if 'pids' in self.prefs_array:
            self.pidnums.set(self.prefs_array['pids'])
        self.pidinfo.grid(row=cur_row, column=1, sticky=sticky)

        cur_row = cur_row + 1
        Tkinter.Label(body, text='eInk Kindle Serial Number list\n(16 characters, comma separated)').grid(row=cur_row, sticky=Tkconstants.E)
        self.sernums = Tkinter.StringVar()
        self.serinfo = Tkinter.Entry(body, width=50, textvariable=self.sernums)
        if 'serials' in self.prefs_array:
            self.sernums.set(self.prefs_array['serials'])
        self.serinfo.grid(row=cur_row, column=1, sticky=sticky)

        cur_row = cur_row + 1
        Tkinter.Label(body, text='eReader data list\n(name:last 8 digits on credit card, comma separated)').grid(row=cur_row, sticky=Tkconstants.E)
        self.sdrmnums = Tkinter.StringVar()
        self.sdrminfo = Tkinter.Entry(body, width=50, textvariable=self.sdrmnums)
        if 'sdrms' in self.prefs_array:
            self.sdrmnums.set(self.prefs_array['sdrms'])
        self.sdrminfo.grid(row=cur_row, column=1, sticky=sticky)

        cur_row = cur_row + 1
        Tkinter.Label(body, text="Output Folder (if blank, use input ebook's folder)").grid(row=cur_row, sticky=Tkconstants.E)
        self.outpath = Tkinter.Entry(body, width=50)
        self.outpath.grid(row=cur_row, column=1, sticky=sticky)
        if 'outdir' in self.prefs_array:
            dpath = self.prefs_array['outdir']
            self.outpath.insert(0, dpath)
        button = Tkinter.Button(body, text="...", command=self.get_outpath)
        button.grid(row=cur_row, column=2)

        cur_row = cur_row + 1
        Tkinter.Label(body, text='').grid(row=cur_row, column=0, columnspan=2, sticky=Tkconstants.N)

        cur_row = cur_row + 1
        Tkinter.Label(body, text='Alternatively Process an eBook').grid(row=cur_row, column=0, columnspan=2, sticky=Tkconstants.N)

        cur_row = cur_row + 1
        Tkinter.Label(body, text='Select an eBook to Process*').grid(row=cur_row, sticky=Tkconstants.E)
        self.bookpath = Tkinter.Entry(body, width=50)
        self.bookpath.grid(row=cur_row, column=1, sticky=sticky)
        button = Tkinter.Button(body, text="...", command=self.get_bookpath)
        button.grid(row=cur_row, column=2)

        cur_row = cur_row + 1
        Tkinter.Label(body, font=("Helvetica", "10", "italic"), text='*To DeDRM multiple ebooks simultaneously, set your preferences and quit.\nThen drag and drop ebooks or folders onto the DeDRM_Drop_Target').grid(row=cur_row, column=1, sticky=Tkconstants.E)

        cur_row = cur_row + 1
        Tkinter.Label(body, text='').grid(row=cur_row, column=0, columnspan=2, sticky=Tkconstants.E)

        buttons = Tkinter.Frame(self)
        buttons.pack()
        self.sbotton = Tkinter.Button(buttons, text="Set Prefs", width=14, command=self.setprefs)
        self.sbotton.pack(side=Tkconstants.LEFT)

        buttons.pack()
        self.pbotton = Tkinter.Button(buttons, text="Process eBook", width=14, command=self.doit)
        self.pbotton.pack(side=Tkconstants.LEFT)
        buttons.pack()
        self.qbotton = Tkinter.Button(buttons, text="Quit", width=14, command=self.quitting)
        self.qbotton.pack(side=Tkconstants.RIGHT)
        buttons.pack()

    def disablebuttons(self):
        self.sbotton.configure(state='disabled')
        self.pbotton.configure(state='disabled')
        self.qbotton.configure(state='disabled')

    def enablebuttons(self):
        self.sbotton.configure(state='normal')
        self.pbotton.configure(state='normal')
        self.qbotton.configure(state='normal')

    def show(self):
        self.deiconify()
        self.tkraise()

    def hide(self):
        self.withdraw()

    def get_outpath(self):
        cpath = self.outpath.get()
        if sys.platform.startswith("win"):
            # tk_chooseDirectory is horribly broken for unicode paths
            # on windows - bug has been reported but not fixed for years
            # workaround by using our own unicode aware version
            outpath = AskFolder(message="Choose the folder for DRM-free ebooks",
                defaultLocation=cpath)
        else:
            outpath = tkFileDialog.askdirectory(
                parent=None, title='Choose the folder for DRM-free ebooks',
                initialdir=cpath, initialfile=None)
        if outpath:
            outpath = os.path.normpath(outpath)
            self.outpath.delete(0, Tkconstants.END)
            self.outpath.insert(0, outpath)
        return

    def get_adkpath(self):
        cpath = self.adkpath.get()
        adkpath = tkFileDialog.askopenfilename(initialdir = cpath, parent=None, title='Select Adept Key file',
            defaultextension='.der', filetypes=[('Adept Key file', '.der'), ('All Files', '.*')])
        if adkpath:
            adkpath = os.path.normpath(adkpath)
            self.adkpath.delete(0, Tkconstants.END)
            self.adkpath.insert(0, adkpath)
        return

    def get_kkpath(self):
        cpath = self.kkpath.get()
        kkpath = tkFileDialog.askopenfilename(initialdir = cpath, parent=None, title='Select Kindle Key file',
            defaultextension='.k4i', filetypes=[('Kindle Key file', '.k4i'), ('All Files', '.*')])
        if kkpath:
            kkpath = os.path.normpath(kkpath)
            self.kkpath.delete(0, Tkconstants.END)
            self.kkpath.insert(0, kkpath)
        return

    def get_akkpath(self):
        cpath = self.akkpath.get()
        akkpath = tkFileDialog.askopenfilename(initialdir = cpath, parent=None, title='Select Android for Kindle backup file',
            defaultextension='.ab', filetypes=[('Kindle for Android backup file', '.ab'), ('All Files', '.*')])
        if akkpath:
            akkpath = os.path.normpath(akkpath)
            self.akkpath.delete(0, Tkconstants.END)
            self.akkpath.insert(0, akkpath)
        return

    def get_bnkpath(self):
        cpath = self.bnkpath.get()
        bnkpath = tkFileDialog.askopenfilename(initialdir = cpath, parent=None, title='Select Barnes and Noble Key file',
            defaultextension='.b64', filetypes=[('Barnes and Noble Key file', '.b64'), ('All Files', '.*')])
        if bnkpath:
            bnkpath = os.path.normpath(bnkpath)
            self.bnkpath.delete(0, Tkconstants.END)
            self.bnkpath.insert(0, bnkpath)
        return

    def get_bookpath(self):
        cpath = self.bookpath.get()
        bookpath = tkFileDialog.askopenfilename(parent=None, title='Select eBook for DRM Removal',
            filetypes=[('All Files', '.*'),
                    ('ePub Files','.epub'),
                    ('Kindle','.azw'),
                    ('Kindle','.azw1'),
                    ('Kindle','.azw3'),
                    ('Kindle','.azw4'),
                    ('Kindle','.tpz'),
                    ('Kindle','.mobi'),
                    ('Kindle','.prc'),
                    ('eReader','.pdb'),
                    ('PDF','.pdf')],
            initialdir=cpath)
        if bookpath:
            bookpath = os.path.normpath(bookpath)
            self.bookpath.delete(0, Tkconstants.END)
            self.bookpath.insert(0, bookpath)
        return

    def quitting(self):
        self.master.destroy()

    def setprefs(self):
        # setting new prefereces
        new_prefs = {}
        prefdir = self.prefs_array['dir']
        new_prefs['dir'] = prefdir
        new_prefs['pids'] = self.pidinfo.get().replace(" ","")
        new_prefs['serials'] = self.serinfo.get().replace(" ","")
        new_prefs['sdrms'] = self.sdrminfo.get().strip().replace(", ",",")
        new_prefs['outdir'] = self.outpath.get().strip()
        adkpath = self.adkpath.get()
        if os.path.dirname(adkpath) != prefdir:
            new_prefs['adkfile'] = adkpath
        bnkpath = self.bnkpath.get()
        if os.path.dirname(bnkpath) != prefdir:
            new_prefs['bnkfile'] = bnkpath
        kkpath = self.kkpath.get()
        if os.path.dirname(kkpath) != prefdir:
            new_prefs['kindlefile'] = kkpath
        akkpath = self.akkpath.get()
        if os.path.dirname(akkpath) != prefdir:
            new_prefs['androidfile'] = akkpath
        self.master.setPreferences(new_prefs)
        # and update internal copies
        self.prefs_array['pids'] = new_prefs['pids']
        self.prefs_array['serials'] = new_prefs['serials']
        self.prefs_array['sdrms'] = new_prefs['sdrms']
        self.prefs_array['outdir'] = new_prefs['outdir']

    def doit(self):
        self.disablebuttons()
        filenames=[]
        bookpath = self.bookpath.get()
        bookpath = os.path.abspath(bookpath)
        filenames.append(bookpath)
        self.master.cd.doit(self.prefs_array,filenames)



class ConvDialog(Toplevel):
    def __init__(self, master, prefs_array={}, filenames=[]):
        Toplevel.__init__(self, master)
        self.withdraw()
        self.protocol("WM_DELETE_WINDOW", self.withdraw)
        self.title("DeDRM Processing")
        self.master = master
        self.apphome = self.master.apphome
        self.prefs_array = prefs_array
        self.filenames = filenames
        self.interval = 50
        self.p2 = None
        self.q = Queue()
        self.running = 'inactive'
        self.numgood = 0
        self.numbad = 0
        self.status = Tkinter.Label(self, text='DeDRM processing...')
        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='Activity Bar').grid(row=0, sticky=Tkconstants.E)
        self.bar = ActivityBar(body, length=80, height=15, barwidth=5)
        self.bar.grid(row=0, column=1, sticky=sticky)

        msg1 = ''
        self.stext = ScrolledText(body, bd=5, relief=Tkconstants.RIDGE, height=4, width=80, wrap=Tkconstants.WORD)
        self.stext.grid(row=2, column=0, columnspan=2,sticky=sticky)
        self.stext.insert(Tkconstants.END,msg1)

        buttons = Tkinter.Frame(self)
        buttons.pack()
        self.qbutton = Tkinter.Button(buttons, text="Quit", width=14, command=self.quitting)
        self.qbutton.pack(side=Tkconstants.BOTTOM)
        self.status['text'] = ''

        self.logfile = open(os.path.join(os.path.expanduser('~'),'DeDRM.log'),'w')

    def show(self):
        self.deiconify()
        self.tkraise()

    def hide(self):
        self.withdraw()

    def doit(self, prefs, filenames):
        self.running = 'inactive'
        self.prefs_array = prefs
        self.filenames = filenames
        self.show()
        self.processBooks()

    def conversion_done(self):
        self.hide()
        self.master.alldone()

    def processBooks(self):
        while self.running == 'inactive':
            rscpath = self.prefs_array['dir']
            filename = None
            if len(self.filenames) > 0:
                filename = self.filenames.pop(0)
            if filename == None:
                msg = 'Complete:  '
                msg += 'Successes: %d, ' % self.numgood
                msg += 'Failures: %d\n' % self.numbad
                self.showCmdOutput(msg)
                if self.numbad == 0:
                    self.after(2000,self.conversion_done())
                self.logfile.write("DeDRM v{0}: {1}".format(__version__,msg))
                self.logfile.close()
                return
            infile = filename
            bname = os.path.basename(infile)
            msg = 'Processing: ' + bname + '...'
            self.logfile.write("DeDRM v{0}: {1}\n".format(__version__,msg))
            self.showCmdOutput(msg)
            outdir = os.path.dirname(filename)
            if 'outdir' in self.prefs_array:
                dpath = self.prefs_array['outdir']
                if dpath.strip() != '':
                    outdir = dpath
            rv = self.decrypt_ebook(infile, outdir, rscpath)
            if rv == 0:
                self.bar.start()
                self.running = 'active'
                self.processQueue()
            else:
                msg = 'Unknown File: ' + bname + '\n'
                self.logfile.write("DeDRM v{0}: {1}".format(__version__,msg))
                self.showCmdOutput(msg)
                self.numbad += 1

    def quitting(self):
        # kill any still running subprocess
        self.running = 'stopped'
        if self.p2 != None:
            if (self.p2.exitcode == None):
                self.p2.terminate()
        self.conversion_done()

    # post output from subprocess in scrolled text widget
    def showCmdOutput(self, msg):
        if msg and msg !='':
            if sys.platform.startswith('win'):
                msg = msg.replace('\r\n','\n')
            self.stext.insert(Tkconstants.END,msg)
            self.stext.yview_pickplace(Tkconstants.END)
        return

    # 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 processQueue(self):
        if self.p2 == None:
            # nothing to wait for so just return
            return
        poll = self.p2.exitcode
        #print "processing", poll
        done = False
        text = ''
        while not done:
            try:
                data = self.q.get_nowait()
                text += data
            except Empty:
                done = True
        if text != '':
            self.logfile.write(text)
        if poll != None:
            self.bar.stop()
            if poll == 0:
                msg = 'Success\n'
                self.numgood += 1
            else:
                msg = 'Failed\n'
                self.numbad += 1
            self.p2.join()
            self.logfile.write("DeDRM v{0}: {1}\n".format(__version__,msg))
            self.showCmdOutput(msg)
            self.p2 = None
            self.running = 'inactive'
            self.after(50,self.processBooks)
            return
        # make sure we get invoked again by event loop after interval
        self.stext.after(self.interval,self.processQueue)
        return

    def decrypt_ebook(self, infile, outdir, rscpath):
        q = self.q
        rv = 1
        name, ext = os.path.splitext(os.path.basename(infile))
        ext = ext.lower()
        if ext == '.epub':
            self.p2 = Process(target=processEPUB, args=(q, infile, outdir, rscpath))
            self.p2.start()
            return 0
        if ext == '.pdb':
            self.p2 = Process(target=processPDB, args=(q, infile, outdir, rscpath))
            self.p2.start()
            return 0
        if ext in ['.azw', '.azw1', '.azw3', '.azw4', '.prc', '.mobi', '.pobi', '.tpz']:
            self.p2 = Process(target=processK4MOBI,args=(q, infile, outdir, rscpath))
            self.p2.start()
            return 0
        if ext == '.pdf':
            self.p2 = Process(target=processPDF, args=(q, infile, outdir, rscpath))
            self.p2.start()
            return 0
        return rv


# child process starts here
def processK4MOBI(q, infile, outdir, rscpath):
    add_cp65001_codec()
    set_utf8_default_encoding()
    sys.stdout = QueuedUTF8Stream(sys.stdout, q)
    sys.stderr = QueuedUTF8Stream(sys.stderr, q)
    rv = decryptk4mobi(infile, outdir, rscpath)
    sys.exit(rv)

# child process starts here
def processPDF(q, infile, outdir, rscpath):
    add_cp65001_codec()
    set_utf8_default_encoding()
    sys.stdout = QueuedUTF8Stream(sys.stdout, q)
    sys.stderr = QueuedUTF8Stream(sys.stderr, q)
    rv = decryptpdf(infile, outdir, rscpath)
    sys.exit(rv)

# child process starts here
def processEPUB(q, infile, outdir, rscpath):
    add_cp65001_codec()
    set_utf8_default_encoding()
    sys.stdout = QueuedUTF8Stream(sys.stdout, q)
    sys.stderr = QueuedUTF8Stream(sys.stderr, q)
    rv = decryptepub(infile, outdir, rscpath)
    sys.exit(rv)

# child process starts here
def processPDB(q, infile, outdir, rscpath):
    add_cp65001_codec()
    set_utf8_default_encoding()
    sys.stdout = QueuedUTF8Stream(sys.stdout, q)
    sys.stderr = QueuedUTF8Stream(sys.stderr, q)
    rv = decryptpdb(infile, outdir, rscpath)
    sys.exit(rv)


def main():
    argv=unicode_argv()
    apphome = os.path.dirname(argv[0])
    apphome = os.path.abspath(apphome)

    # windows may pass a spurious quoted null string as argv[1] from bat file
    # simply work around this until we can figure out a better way to handle things
    if sys.platform.startswith('win') and len(argv) == 2:
        temp = argv[1]
        temp = temp.strip('"')
        temp = temp.strip()
        if temp == '':
            argv.pop()

    if len(argv) == 1:
        filenames = []
        dnd = False

    else : # processing books via drag and drop
        dnd = True
        # build a list of the files to be processed
        # note all filenames and paths have been utf-8 encoded
        infilelst = argv[1:]
        filenames = []
        for infile in infilelst:
            infile = infile.replace('"','')
            infile = os.path.abspath(infile)
            if os.path.isdir(infile):
                bpath = infile
                filelst = os.listdir(infile)
                for afile in filelst:
                    if not afile.startswith('.'):
                        filepath = os.path.join(bpath,afile)
                        if os.path.isfile(filepath):
                            filenames.append(filepath)
            else :
                afile = os.path.basename(infile)
                if not afile.startswith('.'):
                    if os.path.isfile(infile):
                        filenames.append(infile)

    # start up gui app
    app = MainApp(apphome, dnd, filenames)
    app.mainloop()
    return 0


if __name__ == "__main__":
    sys.exit(main())
