diff --git a/controlpanel.py b/controlpanel.py index 0f833ee..475b7a6 100644 --- a/controlpanel.py +++ b/controlpanel.py @@ -10,185 +10,186 @@ 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_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=500, 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=500, 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 + + 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/logwriter.py b/logwriter.py index feac7aa..e0877e2 100644 --- a/logwriter.py +++ b/logwriter.py @@ -45,7 +45,7 @@ class LogWriter: ''' def __init__(self, settings, cmdoptions, q): - self.sepKey = '|' # should move this off into the ini file. just temporarily here. + #self.sepKey = '|' # should move this off into the ini file. just temporarily here. self.q = q self.settings = settings self.cmdoptions = cmdoptions @@ -63,7 +63,8 @@ class LogWriter: self.filter = re.compile(r"[\\\/\:\*\?\"\<\>\|]+") #regexp filter for the non-allowed characters in windows filenames. - self.writeTarget = "" + ##don't need to initialize this with the delimited log format + #self.writeTarget = "" if self.settings['General']['System Log'] != 'None': try: self.systemlog = open(os.path.join(self.settings['General']['Log Directory'], self.settings['General']['System Log']), 'a') @@ -75,7 +76,7 @@ class LogWriter: except: self.PrintDebug("Unexpected error: " + str(sys.exc_info()[0]) + ", " + str(sys.exc_info()[1]) + "\n") - # initialize self.log to None, so that we dont attempt to flush it until it exists + # 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 # Set up the subset of keys that we are going to log @@ -99,10 +100,11 @@ class LogWriter: self.oldlogtimer = mytimer.MyTimer(float(self.settings['Log Maintenance']['Age Check Interval'])*60*60, 0, self.DeleteOldLogs) self.oldlogtimer.start() + ## don't need anymore with the delimited log format # initialize the automatic timestamp timer - if self.settings['Timestamp']['Timestamp Enable'] == True: - self.timestamptimer = mytimer.MyTimer(float(self.settings['Timestamp']['Timestamp Interval'])*60, 0, self.WriteTimestamp) - self.timestamptimer.start() + #~ if self.settings['Timestamp']['Timestamp Enable'] == True: + #~ self.timestamptimer = mytimer.MyTimer(float(self.settings['Timestamp']['Timestamp Interval'])*60, 0, self.WriteTimestamp) + #~ self.timestamptimer.start() # initialize the automatic log flushing timer self.flushtimer = mytimer.MyTimer(float(self.settings['General']['Flush Interval']), 0, self.FlushLogWriteBuffers, ["Flushing file write buffers due to timer\n"]) @@ -117,10 +119,13 @@ class LogWriter: 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['General']['Log Rotation Interval'])*60*60, 0, self.RotateLogs) + self.logrotatetimer.start() + def start(self): - self.stopflag=False - self.eventlist = range(7) #initialize our eventlist to something. ## line format: ## date; time (1 minute resolution); fullapppath; hwnd; username; window title; eventdata ## @@ -136,6 +141,9 @@ class LogWriter: ## put the line into a list, check if all contents (except for eventdata) are equal, if so, just append eventdata to existing eventdata. ## on flush or on exit, make sure to write the latest dataline + self.stopflag=False + self.eventlist = range(7) #initialize our eventlist to something. + while self.stopflag == False: try: event = self.q.get(timeout=2) @@ -170,7 +178,7 @@ class LogWriter: '''Pass the event ascii value through the requisite filters. Returns the result as a string. ''' - if chr(event.Ascii) == self.sepKey: + if chr(event.Ascii) == self.settings['General']['Log File Field Separator']: return('[sep_key]') if event.Ascii in self.asciiSubset: @@ -232,8 +240,8 @@ class LogWriter: if self.eventlist != range(7): line = "" for item in self.eventlist: - line = line + str(item) + self.sepKey - line = line.rstrip(self.sepKey) + '\n' + line = line + str(item) + self.settings['General']['Log File Field Separator'] + line = line.rstrip(self.settings['General']['Log File Field Separator']) + '\n' self.PrintStuff(line) @@ -435,99 +443,125 @@ class LogWriter: emaillog.write(zipFileList.pop()) emaillog.close() - def ZipAndEmailTimerAction(self): - '''This is a timer action function that zips the logs and sends them by email. - - deprecated - should delete this. - ''' - self.PrintDebug("Sending mail to " + self.settings['E-mail']['SMTP To'] + "\n") - self.ZipLogFiles() - self.SendZipByEmail() + #~ def ZipAndEmailTimerAction(self): + #~ '''This is a timer action function that zips the logs and sends them by email. + + #~ deprecated - should delete this. + #~ ''' + #~ self.PrintDebug("Sending mail to " + self.settings['E-mail']['SMTP To'] + "\n") + #~ self.ZipLogFiles() + #~ self.SendZipByEmail() def OpenLogFile(self, event): '''Open the appropriate log file, depending on event properties and settings in .ini file. + Now, we only need to open the one file logfile ''' # if the "onefile" option is set, we don't that much to do: - if self.settings['General']['One File'] != 'None': - if self.writeTarget == "": - self.writeTarget = os.path.join(os.path.normpath(self.settings['General']['Log Directory']), os.path.normpath(self.settings['General']['One File'])) - try: - self.log = open(self.writeTarget, 'a') - 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 - - #write the timestamp upon opening the logfile - if self.settings['Timestamp']['Timestamp Enable'] == True: self.WriteTimestamp() - - self.PrintDebug("writing to: " + self.writeTarget + "\n") - return True - - # if "onefile" is not set, we start playing with the logfilenames: - subDirName = self.filter.sub(r'__',self.processName) #our subdirname is the full path of the process owning the hwnd, filtered. - subDirName = subDirName.decode(sys.getfilesystemencoding()) - - WindowName = self.filter.sub(r'__',str(event.WindowName)) - - filename = time.strftime('%Y%m%d') + "_" + str(event.Window) + "_" + WindowName + ".txt" - filename = filename.decode(sys.getfilesystemencoding()) - - #make sure our filename plus path is not longer than 255 characters, as per filesystem limit. - #filename = filename[0:200] + ".txt" - if len(os.path.join(self.settings['General']['Log Directory'], subDirName, filename)) > 255: - if len(os.path.join(self.settings['General']['Log Directory'], subDirName)) > 250: - self.PrintDebug("root log dir + subdirname is longer than 250. cannot log.") - return False - else: - filename = filename[0:255-len(os.path.join(self.settings['General']['Log Directory'], subDirName))-4] + ".txt" - - - #we have this writetarget conditional to make sure we dont keep opening and closing the log file when all inputs are going - #into the same log file. so, when our new writetarget is the same as the previous one, we just write to the same - #already-opened file. - if self.writeTarget != os.path.join(self.settings['General']['Log Directory'], subDirName, filename): - if self.writeTarget != "": - self.FlushLogWriteBuffers("flushing and closing old log\n") - #~ self.PrintDebug("flushing and closing old log\n") - #~ self.log.flush() - self.log.close() - self.writeTarget = os.path.join(self.settings['General']['Log Directory'], subDirName, filename) - self.PrintDebug("writeTarget:" + self.writeTarget + "\n") - - try: - os.makedirs(os.path.join(self.settings['General']['Log Directory'], subDirName), 0777) - except OSError, detail: - if(detail.errno==17): #if directory already exists, swallow the error - pass - else: - self.PrintDebug(sys.exc_info()[0] + ", " + sys.exc_info()[1] + "\n") - return False - except: - self.PrintDebug("Unexpected error: " + sys.exc_info()[0] + ", " + sys.exc_info()[1] + "\n") - return False - + #subDirName = self.filter.sub(r'__',self.processName) + # 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']) + + # 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.join(os.path.normpath(self.settings['General']['Log Directory']), os.path.normpath(self.settings['General']['Log File'])) try: self.log = open(self.writeTarget, 'a') + self.PrintDebug("writing to: " + self.writeTarget + "\n") + return True except OSError, detail: if(detail.errno==17): #if file already exists, swallow the error pass else: - self.PrintDebug(sys.exc_info()[0] + ", " + sys.exc_info()[1] + "\n") + self.PrintDebug(str(sys.exc_info()[0]) + ", " + str(sys.exc_info()[1]) + "\n") return False except: - self.PrintDebug("Unexpected error: " + sys.exc_info()[0] + ", " + sys.exc_info()[1] + "\n") + self.PrintDebug("Unexpected error: " + str(sys.exc_info()[0]) + ", " + str(sys.exc_info()[1]) + "\n") return False + else: + return True + + #~ if self.settings['General']['One File'] != 'None': + #~ if self.writeTarget == "": + #~ self.writeTarget = os.path.join(os.path.normpath(self.settings['General']['Log Directory']), os.path.normpath(self.settings['General']['One File'])) + #~ try: + #~ self.log = open(self.writeTarget, 'a') + #~ 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 + + #~ #write the timestamp upon opening the logfile + #~ if self.settings['Timestamp']['Timestamp Enable'] == True: self.WriteTimestamp() + + #~ self.PrintDebug("writing to: " + self.writeTarget + "\n") + #~ return True + + #~ # if "onefile" is not set, we start playing with the logfilenames: + #~ subDirName = self.filter.sub(r'__',self.processName) #our subdirname is the full path of the process owning the hwnd, filtered. + #~ subDirName = subDirName.decode(sys.getfilesystemencoding()) + + #~ WindowName = self.filter.sub(r'__',str(event.WindowName)) + + #~ filename = time.strftime('%Y%m%d') + "_" + str(event.Window) + "_" + WindowName + ".txt" + #~ filename = filename.decode(sys.getfilesystemencoding()) + + #~ #make sure our filename plus path is not longer than 255 characters, as per filesystem limit. + #~ #filename = filename[0:200] + ".txt" + #~ if len(os.path.join(self.settings['General']['Log Directory'], subDirName, filename)) > 255: + #~ if len(os.path.join(self.settings['General']['Log Directory'], subDirName)) > 250: + #~ self.PrintDebug("root log dir + subdirname is longer than 250. cannot log.") + #~ return False + #~ else: + #~ filename = filename[0:255-len(os.path.join(self.settings['General']['Log Directory'], subDirName))-4] + ".txt" + + + #~ #we have this writetarget conditional to make sure we dont keep opening and closing the log file when all inputs are going + #~ #into the same log file. so, when our new writetarget is the same as the previous one, we just write to the same + #~ #already-opened file. + #~ if self.writeTarget != os.path.join(self.settings['General']['Log Directory'], subDirName, filename): + #~ if self.writeTarget != "": + #~ self.FlushLogWriteBuffers("flushing and closing old log\n") + ## self.PrintDebug("flushing and closing old log\n") + ## self.log.flush() + #~ self.log.close() + #~ self.writeTarget = os.path.join(self.settings['General']['Log Directory'], subDirName, filename) + #~ self.PrintDebug("writeTarget:" + self.writeTarget + "\n") - #write the timestamp upon opening a new logfile - if self.settings['Timestamp']['Timestamp Enable'] == True: self.WriteTimestamp() + #~ try: + #~ os.makedirs(os.path.join(self.settings['General']['Log Directory'], subDirName), 0777) + #~ except OSError, detail: + #~ if(detail.errno==17): #if directory already exists, swallow the error + #~ pass + #~ else: + #~ self.PrintDebug(sys.exc_info()[0] + ", " + sys.exc_info()[1] + "\n") + #~ return False + #~ except: + #~ self.PrintDebug("Unexpected error: " + sys.exc_info()[0] + ", " + sys.exc_info()[1] + "\n") + #~ return False + + #~ try: + #~ self.log = open(self.writeTarget, 'a') + #~ except OSError, detail: + #~ if(detail.errno==17): #if file already exists, swallow the error + #~ pass + #~ else: + #~ self.PrintDebug(sys.exc_info()[0] + ", " + sys.exc_info()[1] + "\n") + #~ return False + #~ except: + #~ self.PrintDebug("Unexpected error: " + sys.exc_info()[0] + ", " + sys.exc_info()[1] + "\n") + #~ return False + + #~ #write the timestamp upon opening a new logfile + #~ if self.settings['Timestamp']['Timestamp Enable'] == True: self.WriteTimestamp() - return True + #~ return True def PrintStuff(self, stuff): '''Write stuff to log, or to debug outputs. @@ -548,6 +582,20 @@ class LogWriter: def WriteTimestamp(self): 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.join(os.path.normpath(self.settings['General']['Log Directory']), os.path.normpath(time.strftime("%Y%m%d_%H%M%S") + '_' + self.settings['General']['Log File'])) + self.log.close() + self.log = None + try: + os.rename(self.writetarget, rotatetarget) + except: + self.PrintDebug("Error rotating logfile") + + def DeleteOldLogs(self, lastmodcutoff=None): '''Walk the log directory tree and remove old logfiles. @@ -599,17 +647,19 @@ class LogWriter: self.stopflag = True time.sleep(3) self.queuetimer.cancel() - self.WriteToLogFile() + self.WriteToLogFile() self.FlushLogWriteBuffers("Flushing buffers prior to exiting") 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['Timestamp']['Timestamp Enable'] == True: + #~ self.timestamptimer.cancel() if self.settings['Zip']['Zip Enable'] == True: self.ziptimer.cancel() diff --git a/pykeylogger.ini b/pykeylogger.ini index 0796212..69a5728 100644 Binary files a/pykeylogger.ini and b/pykeylogger.ini differ diff --git a/pykeylogger.val b/pykeylogger.val index 33de24a..d673f1b 100644 --- a/pykeylogger.val +++ b/pykeylogger.val @@ -42,6 +42,10 @@ Log File = string(default="logfile.txt") Log File Field Separator Tooltip = string() Log File Field Separator = string(default="|") +# default: 4.0 +Log Rotation Interval Tooltip = string() +Log Rotation Interval = string(min=0.016, default=4.0) + # default: 120 Flush Interval Tooltip = string() Flush Interval = float(min=10, default=120.0)