www.pudn.com > 10040637740.zip > ClamTray.py


#----------------------------------------------------------------------------- 
# Name:        Tray.py 
# Product:     ClamWin Free Antivirus 
# 
# Author:      alch [alch at users dot sourceforge dot net] 
# 
# Created:     2004/19/03 
# Copyright:   Copyright alch (c) 2005 
# Licence:      
#   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 2 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, write to the Free Software 
#   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
 
#----------------------------------------------------------------------------- 
# this code is based on win32gui_taskbar.py demo from Mark Hammond's 
# win32 extensions. 
 
import SetUnicode 
import RedirectStd 
import sys, os, time, tempfile, locale, re 
import win32api, win32gui, win32con, win32event 
import win32process, win32event 
import Scheduler 
import Config 
import Process 
import EmailAlert 
import threading 
import Utils, wxDialogScheduledScan 
import version 
 
 
class MainWindow:     
    MENU_OPEN_CLAM, MENU_UPDATE_DB, MENU_CHECK_UPDATE, MENU_CLAMWIN_WEB, MENU_CONFIGURE, MENU_SHOWSCANLOG, \ 
        MENU_SHOWUPDATELOG, MENU_EXIT, MENU_CONFIGURESCHEDULER,\ 
        MENU_TERMINATESCHEDULE, MENU_RUNSCHEDULE = range(1023, 1023 + 11) 
         
    ACTIVE_MUTEX='ClamWinTrayMutex01'      
    WM_TASKBAR_NOTIFY=win32con.WM_USER+20 
    WM_CONFIG_UPDATED=win32con.WM_USER+21       
    WM_SHOW_BALLOON=win32con.WM_USER+22 
                   
    def __init__(self, config, logon):         
        self._config = config         
        self._schedulers = [] 
        self._scheduledScans = [] 
        self._processes = []      
        self._balloon_info = None                 
        self._balloonThreadLock = threading.Lock() 
        msg_TaskbarRestart = win32gui.RegisterWindowMessage("TaskbarCreated"); 
        message_map = { 
                msg_TaskbarRestart: self.OnRestart, 
                win32con.WM_DESTROY: self.OnDestroy, 
                win32con.WM_COMMAND: self.OnCommand, 
                MainWindow.WM_TASKBAR_NOTIFY: self.OnTaskbarNotify, 
                MainWindow.WM_CONFIG_UPDATED : self.OnConfigUpdated,                 
                MainWindow.WM_SHOW_BALLOON : self.OnShowBalloon                 
        } 
        # Register the Window class. 
        wc = win32gui.WNDCLASS() 
        hinst = wc.hInstance = win32api.GetModuleHandle(None) 
        wc.lpszClassName = "ClamWinTrayWindow" 
        wc.style = win32con.CS_VREDRAW | win32con.CS_HREDRAW; 
        wc.hCursor = win32gui.LoadCursor( 0, win32con.IDC_ARROW ) 
        wc.hbrBackground = win32con.COLOR_WINDOW 
        wc.lpfnWndProc = message_map # could also specify a wndproc. 
        classAtom = win32gui.RegisterClass(wc) 
        # Create the Window. 
        style = win32con.WS_OVERLAPPED | win32con.WS_SYSMENU 
        self.hwnd = win32gui.CreateWindow( classAtom, "ClamWin", style, \ 
                0, 0, win32con.CW_USEDEFAULT, win32con.CW_USEDEFAULT, \ 
                0, 0, hinst, None) 
        win32gui.UpdateWindow(self.hwnd) 
         
        # create mutex to prevent further instances 
        self._hActiveMutex = win32event.CreateMutex(None, True, self.ACTIVE_MUTEX) 
        self._DoCreateIcons() 
        self._InitSchedulers(logon) 
         
        # start config monitor thread 
        self._configMonitor = MonitorConfig(self.NotifyConfig, (self.hwnd,)) 
        self._configMonitor.start() 
         
    def _IsProcessRunning(self, proc, wait=False): 
        if wait: 
            timeout = 5 
        else: 
            timeout = 0             
        try:                     
            proc.wait(timeout) 
        except Exception, e: 
            if isinstance(e, Process.ProcessError): 
                if e.errno == Process.ProcessProxy.WAIT_TIMEOUT:         
                    return True      
                else: 
                    return False 
        return False 
 
    def _StopProcesses(self):           
        # check if process is still running         
        for proc in self._processes:                         
            if self._IsProcessRunning(proc):                        
                # running - kill             
                proc.kill()                     
                #wait to finish                   
                if self._IsProcessRunning(proc, True):        
                    #still running - complain and terminate 
                    win32gui.MessageBox(self.hwnd, 'Unable to stop scheduled process, terminating', 'ClamWin Free Antivirus', win32con.MB_OK | win32con.MB_ICONSTOP) 
                    os._exit(0)   
                proc.close() 
                 
        self._processes = []      
     
    def _TerminateSchedules(self):         
        self._StopProcesses()                
        for scheduler in self._schedulers: 
            try:                 
                scheduler.stop()                 
                # wait for completion 
                scheduler.join(2)                 
            except Exception, e: 
                print 'An error occured whilst termintaing scheduler thread. Error: %s' % str(e) 
        self._schedulers = []              
         
    def _InitSchedulers(self, logon=False):               
        # close all running schedules 
        self._TerminateSchedules()    
         
        # load persistent scheduler 
        self._scheduledScans = wxDialogScheduledScan.LoadPersistentScheduledScans( 
                os.path.join(Utils.GetScheduleShelvePath(self._config), 'ScheduledScans')) 
         
        # create an update schedule to run now if 'Update on Logon' is selected 
        if logon and self._config.Get('Updates', 'UpdateOnLogon') == '1':             
            # set C locale, otherwise python and wxpython complain 
            locale.setlocale(locale.LC_ALL, 'C') 
             
            start_time = time.localtime(time.time() + 120) 
            weekday = int(time.strftime('%w', start_time)) 
            if weekday: weekday -= 1 
            else: weekday = 6             
            scheduler = Scheduler.Scheduler('Once', 
                            time.strftime('%H:%M:%S', start_time), 
                            weekday,  
                            win32gui.SendMessage, (self.hwnd, win32con.WM_COMMAND, self.MENU_UPDATE_DB, 1), 
                            ('ClamWin_Scheduler_Info', 'ClamWin_Upadte_Time'))             
            scheduler.start()         
            self._schedulers.append(scheduler) 
         
        # create a scheduler thread for DB updates         
        if self._config.Get('Updates', 'Enable') == '1':             
            scheduler = Scheduler.Scheduler(self._config.Get('Updates', 'Frequency'), 
                            self._config.Get('Updates', 'Time'), 
                            int(self._config.Get('Updates', 'WeekDay')),  
                            win32gui.SendMessage, (self.hwnd, win32con.WM_COMMAND, self.MENU_UPDATE_DB, 1), 
                            ('ClamWin_Scheduler_Info', 'ClamWin_Upadte_Time'))             
            scheduler.start()         
            self._schedulers.append(scheduler) 
         
        # create scheduler threads for all scheduled scans                     
        for scan in self._scheduledScans: 
            if scan.Active: 
                scheduler = Scheduler.Scheduler(scan.Frequency, 
                            scan.Time, 
                            int(scan.WeekDay),  
                            self.ScanPath, (self, scan.Path, scan.Description))             
                scheduler.start()         
                self._schedulers.append(scheduler)    
                 
        # create scheduler thread for program version check                     
        if self._config.Get('Updates', 'CheckVersion') == '1': 
            curDir = Utils.GetCurrentDir(True)       
            scheduler = Scheduler.Scheduler('Daily', # check once aday 
                            time.strftime('%H:%M:%S', time.localtime(time.time() + 300)), # 5 minutes after start 
                            1, # unused 
                            Utils.SpawnPyOrExe, (os.path.join(curDir, 'ClamWin'), ' --mode=checkversion'), 
                            ('ClamWin_CheckVer_Info', 'ClamWin_CheckVer_Time'))             
            scheduler.start()         
            self._schedulers.append(scheduler) 
 
    def _Terminate(self): 
        # terminate running threads 
        self._TerminateSchedules() 
        if self._configMonitor is not None: 
            self._configMonitor.stop() 
            self._configMonitor.join(2)         
            self._configMonitor = None 
             
    def _DoCreateIcons(self): 
        # Try and find a custom icon 
        hinst =  win32api.GetModuleHandle(None) 
        iconPathName = os.path.abspath(os.path.join(os.path.split(sys.executable)[0],"img/TrayIcon.ico")) 
        if not os.path.isfile(iconPathName): 
            # Look in the current folder tree. 
            iconPathName = "img/TrayIcon.ico" 
        if os.path.isfile(iconPathName): 
            icon_flags = win32con.LR_LOADFROMFILE | win32con.LR_DEFAULTSIZE 
            hicon = win32gui.LoadImage(hinst, iconPathName, win32con.IMAGE_ICON, 0, 0, icon_flags) 
        else:             
            hicon = win32gui.LoadIcon(0, win32con.IDI_APPLICATION) 
 
        flags = win32gui.NIF_ICON | win32gui.NIF_MESSAGE | win32gui.NIF_TIP 
        nid = (self.hwnd, 0, flags, MainWindow.WM_TASKBAR_NOTIFY, hicon, "ClamWin Free Antivirus") 
        win32gui.Shell_NotifyIcon(win32gui.NIM_ADD, nid)         
             
    def OnRestart(self, hwnd, msg, wparam, lparam): 
        self._DoCreateIcons() 
 
    def OnDestroy(self, hwnd, msg, wparam, lparam):                   
        nid = (self.hwnd, 0) 
        win32gui.Shell_NotifyIcon(win32gui.NIM_DELETE, nid)         
        self._Terminate() 
        win32event.ReleaseMutex(self._hActiveMutex) 
        win32api.CloseHandle(self._hActiveMutex)         
        # Terminate the app. 
        win32gui.PostQuitMessage(0)  
 
    def OnTaskbarNotify(self, hwnd, msg, wparam, lparam): 
        if lparam==win32con.WM_LBUTTONUP: 
            pass 
        elif lparam==win32con.WM_LBUTTONDBLCLK: 
           self.OnCommand(hwnd, win32con.WM_COMMAND, self.MENU_OPEN_CLAM, 0) 
        elif lparam==win32con.WM_RBUTTONUP:                                  
                                 
            # create scheduler menu 
            scheduler_popup = win32gui.CreatePopupMenu() 
            win32gui.AppendMenu(scheduler_popup, win32con.MF_STRING,  
                self.MENU_CONFIGURESCHEDULER, "&Configure Scheduler") 
             
            if not self._processes: 
                flags = win32con.MF_GRAYED 
            else: 
                flags = 0 
             
            # create scheduled tasks menu 
            tasks_popup = win32gui.CreatePopupMenu()             
            i = 0 
            for scan in self._scheduledScans: 
                win32gui.AppendMenu(tasks_popup, win32con.MF_STRING,  
                    self.MENU_RUNSCHEDULE + i, scan.Description) 
                i+=1 
            if not i: 
                flags2 = win32con.MF_GRAYED 
            else: 
                flags2 = 0 
            win32gui.InsertMenu(scheduler_popup, self.MENU_CONFIGURESCHEDULER, 
                            win32con.MF_BYCOMMAND | win32con.MF_POPUP | flags2, 
                            tasks_popup, "&Run Scheduled Scan")                                             
                 
            win32gui.InsertMenu(scheduler_popup, flags,  
                                win32con.MF_BYCOMMAND | win32con.MF_STRING | flags,  
                                self.MENU_TERMINATESCHEDULE, "&Stop All Running Tasks Now")                         
                                 
            # create reports menu 
            reports_popup = win32gui.CreatePopupMenu()             
            if not len(self._config.Get('ClamAV', 'LogFile')): 
                flags = win32con.MF_GRAYED 
            else: 
                flags = 0 
            win32gui.InsertMenu( reports_popup, 0,  
                                win32con.MF_BYCOMMAND | win32con.MF_STRING | flags,  
                                self.MENU_SHOWSCANLOG, "&Virus Scan Report")                         
            if not len(self._config.Get('Updates', 'DBUpdateLogFile')): 
                flags = win32con.MF_GRAYED 
            else: 
                flags = 0 
            win32gui.InsertMenu( reports_popup, self.MENU_SHOWSCANLOG, 
                                win32con.MF_BYCOMMAND | win32con.MF_STRING | flags, 
                                self.MENU_SHOWUPDATELOG, "&Virus Database Update Report")                         
 
            # create main menu 
            menu = win32gui.CreatePopupMenu()                                                         
            win32gui.AppendMenu( menu, win32con.MF_STRING, self.MENU_OPEN_CLAM, "&Open ClamWin") 
            win32gui.AppendMenu( menu, win32con.MF_STRING, self.MENU_UPDATE_DB, "&Download Virus Database Update") 
            win32gui.AppendMenu( menu, win32con.MF_STRING, self.MENU_CONFIGURE, "&Configure ClamWin")                    
            win32gui.AppendMenu( menu, win32con.MF_POPUP, scheduler_popup, "&Scheduler")                      
            win32gui.AppendMenu( menu, win32con.MF_POPUP, reports_popup, "Display &Reports")                 
            win32gui.AppendMenu( menu, win32con.MF_SEPARATOR, 0, "" )                                 
            win32gui.AppendMenu( menu, win32con.MF_STRING, self.MENU_CHECK_UPDATE, "Check &Latest Version") 
            win32gui.AppendMenu( menu, win32con.MF_STRING, self.MENU_CLAMWIN_WEB, "&Visit ClamWin Website") 
            win32gui.AppendMenu( menu, win32con.MF_SEPARATOR, 0, "" )                                 
            win32gui.AppendMenu( menu, win32con.MF_STRING, self.MENU_EXIT, "&Exit" )                         
             
            pos = win32gui.GetCursorPos() 
            # See http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/menus_0hdi.asp 
            win32gui.SetForegroundWindow(self.hwnd) 
            try: 
                win32gui.SetMenuDefaultItem(menu, 0, 1) 
            except NameError: 
                pass 
 
            win32gui.TrackPopupMenu(menu, win32con.TPM_LEFTALIGN, pos[0], pos[1], 0, self.hwnd, None) 
            win32gui.PostMessage(self.hwnd, win32con.WM_NULL, 0, 0) 
             
        return 1 
 
    def OnCommand(self, hwnd, msg, wparam, lparam): 
        id = win32api.LOWORD(wparam) 
        if id == self.MENU_OPEN_CLAM: 
            self._ShowClamWin() 
        elif id == self.MENU_UPDATE_DB: 
            self._UpdateDB(lparam) 
        elif id == self.MENU_CHECK_UPDATE: 
            self._OpenWebPage('http://www.clamwin.com/index.php?option=content&task=view&id=40&Itemid=60&version='+version.clamwin_version) 
        elif id == self.MENU_CLAMWIN_WEB: 
            self._OpenWebPage('http://www.clamwin.com') 
        elif id == self.MENU_CONFIGURE: 
            self._ShowConfigure() 
        elif id == self.MENU_SHOWSCANLOG:             
            self._ShowLog(self._config.Get('ClamAV', 'LogFile')) 
        elif id == self.MENU_SHOWUPDATELOG:             
            self._ShowLog(self._config.Get('Updates', 'DBUpdateLogFile')) 
        elif id == self.MENU_EXIT:           
            self.OnExit() 
        elif id == self.MENU_CONFIGURESCHEDULER: 
            self._ShowConfigure(True) 
        elif id == self.MENU_TERMINATESCHEDULE: 
            self._TerminateSchedules() 
            self._InitSchedulers() 
        elif (id >= self.MENU_RUNSCHEDULE) and \ 
            (id < self.MENU_RUNSCHEDULE + len(self._scheduledScans)): 
                try: 
                    path = self._scheduledScans[id - self.MENU_RUNSCHEDULE].Path 
                    if path[len(path)-1] == '\\': 
                        path = path[:len(path)-1] 
                    self._ShowClamWin(path)  
                except Exception, e: 
                    win32gui.MessageBox(self.hwnd,  
                            'Could not launch ClamWin Scanner. Error: %s' % str(e),  
                            'ClamWin Free Antivirus', win32con.MB_OK | win32con.MB_ICONERROR) 
                 
             
    def OnConfigUpdated(self, hwnd, msg, wparam, lparam): 
    	self._config.Read() 
        self._InitSchedulers()    
         
    def OnShowBalloon(self, hwnd, msg, wparam, lparam): 
        if self._balloon_info is not None: 
            try:                 
                Utils.ShowBalloon(wparam, self._balloon_info, self.hwnd)                 
            except Exception, e: 
                print 'Could not display balloon tooltip. Error: %s' % str(e) 
                                              
    def OnExit(self): 
        win32gui.DestroyWindow(self.hwnd)                         
     
    def _ShowLog(self, logfile): 
        try:   
            curDir = Utils.GetCurrentDir(True) 
            params = (' --mode=viewlog',  '--path="%s"' % logfile) 
            Utils.SpawnPyOrExe(os.path.join(curDir, 'ClamWin'), *params)                 
        except Exception, e:             
            win32gui.MessageBox(self.hwnd, 'An error occured while displaying log file %s.\nError: %s' % (logfile, str(e)), 
                                 'ClamWin Free Antivirus', win32con.MB_OK | win32con.MB_ICONERROR)                                                                
                                  
    def _ShowClamWin(self, path=''):         
        try:               
            if path: 
                params = (' --mode=scanner',  ' --path=\"%s\"' % path) 
            else: 
                params = (' --mode=main',) 
            Utils.SpawnPyOrExe(os.path.join(Utils.GetCurrentDir(True), 'ClamWin'), *params) 
        except Exception, e:             
            win32gui.MessageBox(self.hwnd, 'An error occured while starting ClamWin Free Antivirus scanner.\n' + str(e), 'ClamWin Free Antivirus', win32con.MB_OK | win32con.MB_ICONERROR)                               
     
    def _UpdateDB(self, hide):                 
        if not hide:                         
            try: 
                params = (' --mode=update', ' --config_file="%s"' % self._config.GetFilename()) 
                Utils.SpawnPyOrExe(os.path.join(Utils.GetCurrentDir(True), 'ClamWin'), *params)                                 
            except Exception, e: 
                win32gui.MessageBox(self.hwnd, 'An error occured while starting ClamWin Free Antivirus Update.\n' + str(e), 'ClamWin Free Antivirus', win32con.MB_OK | win32con.MB_ICONERROR)                                   
        else:                         
            # update virus db silently 
            freshclam_conf = Utils.SaveFreshClamConf(self._config) 
            try: 
                if not len(freshclam_conf): 
                    win32gui.MessageBox(self.hwnd, 'Unable to create freshclam configuration file. Please check there is enough space on the disk', 'Error', win32con.MB_OK | win32con.MB_ICONSTOP) 
                    return 
                # create database folder before downloading 
                dbdir = self._config.Get('ClamAV', 'Database')                 
                if dbdir and not os.path.exists(dbdir): 
                    try: 
                        os.makedirs(dbdir) 
                    except: 
                        pass                                                                    
                updatelog = tempfile.mktemp() 
                cmd = '--stdout --datadir="' + dbdir + '"' + \ 
                        ' --config-file="%s" --log="%s"' % (freshclam_conf, updatelog)                                 
                cmd = cmd.replace('\\', '/') 
                cmd = '"%s" %s' % (self._config.Get('ClamAV', 'FreshClam'), cmd) 
                try: 
                    if self._config.Get('UI', 'TrayNotify') == '1': 
                        balloon = (('Virus database has been updated.', 0,  
                                   win32gui.NIIF_INFO, 10000), 
                                   ('An error occured during Scheduled Virus Database Update. Please review the update report.', 1,  
                                   win32gui.NIIF_WARNING, 30000)) 
                    else: 
                        balloon = None                     
                    proc = self._SpawnProcess(cmd, 
                        'n', 
                        self.DBUpdateProcessFinished, 
                        (self._config.Get('ClamAV', 'Database'), 
                        self._config.Get('Updates', 'DBUpdateLogFile'),                         
                        updatelog, False,                          
                        balloon))                              
                    self._processes.append(proc) 
                except Process.ProcessError, e: 
                    print 'Unable to spawn scheduled process.\nCommand line: %s\nError: %s' % (cmd , str(e)) 
                    try: 
                        os.remove(freshclam_conf) 
                        os.remove(updatelog) 
                    except: 
                        pass 
                    return 
                # wait 2 seconds for the process to start, then delete 
                # temp file 
                try: 
                    proc.wait(2) 
                except: 
                    pass 
                os.remove(freshclam_conf) 
            except Exception, e:                             
                print 'Error performing Scheduled Update.', str(e) 
                os.remove(freshclam_conf)                   
                                 
    def _OpenWebPage(self, url): 
        try: 
            import webbrowser 
            webbrowser.open(url) 
        except ImportError: 
            win32gui.MessageBox(self.hwnd, 'Please point your browser at: %s' % url, 'ClamWin Free Antivirus', win32con.MB_OK | win32con.MB_ICONINFORMATION) 
             
         
    def _ShowConfigure(self, switchToSchedule = False): 
        try:                            
            curDir = Utils.GetCurrentDir(True)         
            if switchToSchedule: 
                mode = 'configure_schedule'    
            else: 
                mode = 'configure'   
            params = (' --mode=%s' % mode, 
                        ' --config_file="%s"' % self._config.GetFilename())             
            Utils.SpawnPyOrExe(os.path.join(curDir, 'ClamWin'), *params) 
        except Exception, e:             
            win32gui.MessageBox(self.hwnd, 'An error occured while starting ClamWin Free Antivirus Preferences.\n' + str(e), 'ClamWin Free Antivirus', win32con.MB_OK | win32con.MB_ICONERROR) 
 
    # returns process and stdout buffer 
    def _SpawnProcess(self, cmd, proc_priority, finished_func, finished_params): 
        # initialise environment var TMPDIR 
        # for clamav 
        try: 
            if os.getenv('TMPDIR') is None: 
                os.putenv('TMPDIR', tempfile.gettempdir().replace('\\', '/')) 
            #Utils.SetCygwinTemp() 
        except Exception, e: 
            print str(e)                         
             
        # check that we got the command line         
        if cmd is None:    
            raise Process.ProcessError('Could not start process. No Command Line specified')                                                      
         
        # start our process     
        try:                 
            # check if the file exists first             
            executable = cmd.split('" ' ,1)[0].lstrip('"') 
            if not os.path.exists(executable): 
                raise Process.ProcessError('Could not start process.\n%s\nFile does not exist.' % executable)                             
            out = OutBuffer(self, finished_func, finished_params) 
            proc = Process.ProcessProxy(cmd, stdout=out, priority=proc_priority)             
            out.AttachProcess(proc) 
            proc.wait(0)             
        except Exception, e:              
            if isinstance(e, Process.ProcessError): 
                if e.errno != Process.ProcessProxy.WAIT_TIMEOUT:                                        
                    raise Process.ProcessError('Could not start process:\n%s\nError: %s' % (cmd, str(e)))                      
            else: 
                raise Process.ProcessError('Could not start process:\n%s\nError: %s' % (cmd, str(e)))                              
        return proc 
     
    def ScanPath(self, path, description): 
        scanlog = tempfile.mktemp() 
        path = '"%s"' % path.rstrip('\\').strip('"') 
        cmd = Utils.GetScanCmd(self._config, path, scanlog)         
        try:             
            if self._config.Get('UI', 'TrayNotify') == '1': 
                balloon = (('Virus has been detected during scheduled scan! Please review the scan report.', 1,  
                          win32gui.NIIF_ERROR, 30000), 
                          ('An error occured during scheduled scan. Please review the scan report.', 0,  
                          win32gui.NIIF_WARNING, 30000)) 
            else: 
                balloon = None 
                 
            try: 
                priority = self._config.Get('ClamAV', 'Priority')[:1].lower() 
            except: 
                priority = 'n' 
            # clamav stopped writing start time of the scan to the log file 
            #try: 
            #    file(scanlog, 'wt').write('Scan Started %s\n' % time.ctime(time.time()))       
            #except: 
            #    pass 
            proc = self._SpawnProcess(cmd,  
                        priority, 
                        self.ProcessFinished, 
                        (self._config.Get('ClamAV', 'LogFile'),                          
                        scanlog,                          
                        self._config.Get('EmailAlerts', 'Enable') == '1',                         
                        balloon 
                        ))              
            self._processes.append(proc)  
            result = 0                   
        except Process.ProcessError, e: 
            result = -1 
            try: 
               os.remove(scanlog) 
            except: 
               pass 
            print str(e) 
        if self._config.Get('UI', 'TrayNotify') == '1':   
            balloon_info = (('Running Scheduled Task:\n'+description, 0,  
                            win32gui.NIIF_INFO, 10000), 
                            ('An error occured whilst running Running Scheduled Task '+description, 1,  
                            win32gui.NIIF_WARNING, 30000))                                       
            self.ShowBalloon(result, balloon_info)         
    ScanPath = staticmethod(ScanPath) 
            
    def NotifyConfig(hwnd):         
        win32api.PostMessage(hwnd, MainWindow.WM_CONFIG_UPDATED, 0, 0)                 
    NotifyConfig = staticmethod(NotifyConfig) 
 
    def DBUpdateProcessFinished(self, process, dbpath, log, appendlog, email_alert, balloon_info): 
        #Utils.SetDbFilesPermissions(dbpath) 
        self.ProcessFinished(self, process, log, appendlog, email_alert, balloon_info)         
    DBUpdateProcessFinished = staticmethod(DBUpdateProcessFinished) 
 
     
    def ProcessFinished(self, process, log, appendlog, email_alert, balloon_info): 
        # send the notification alert if we need to         
        if email_alert:             
            try: 
                if process.wait() == 1: 
                    msg = EmailAlert.ConfigVirusAlertMsg(self._config, (appendlog,)) 
                    msg.Send() 
            except Exception, e: 
                print 'Could not send email alert. Error: %s' % str(e) 
                                    
        maxsize = int(self._config.Get('ClamAV', 'MaxLogSize'))*1048576                 
        Utils.AppendLogFile(log, appendlog, maxsize)                    
         
        try: 
            os.remove(appendlog) 
        except Exception, e: 
            print 'could not remove file: %s. Error: %s' % (appendlog, str(e)) 
                             
        if not process.isKilled() and balloon_info is not None:                         
            # show balloon 
            self.ShowBalloon(process.wait(), balloon_info)    
             
        # find and remove our process 
        try: 
            self._processes.remove(process)                                                 
        except ValueError: 
            # ignore "not in list" errors 
            pass 
    ProcessFinished = staticmethod(ProcessFinished) 
     
    # send message to the main window thread to display balloon notification 
    # we need to enclose the call to SendMessage within Lock().acquire()/Lock.release() 
    # to ensure that correct self._balloon_info is used when 2 threads want to  
    # display balloons simultaneously 
    def ShowBalloon(self, result, balloon_info):          
        self._balloonThreadLock.acquire()                 
        try: 
            self._balloon_info = balloon_info 
            win32api.SendMessage(self.hwnd, MainWindow.WM_SHOW_BALLOON, result, 0)                             
        finally: 
            self._balloon_info = None 
            self._balloonThreadLock.release()                 
      
 
# stdout buffer used by ProcessProxy to notify main thread 
# when execution is complete 
class OutBuffer(Process.IOBuffer):             
    def __init__(self, caller, notify, params): 
        Process.IOBuffer.__init__(self)                 
        self.notify = notify     
        self._caller = caller 
        self._params = params         
        self._proc = None 
                                                         
    def _doClose(self):                 
        self.notify(self._caller, self._proc, *self._params)  
        if self._proc: 
            del self._proc                
        Process.IOBuffer._doClose(self) 
         
    def AttachProcess(self, proc): 
        self._proc = proc 
     
     
# this thread monitors changes to config files 
#  and notifies tray to reload if a change occurs                           
class MonitorConfig(threading.Thread):    
    def __init__(self, notify, args): 
        self.notify = notify 
        self.args = args 
        self._terminate = False 
        threading.Thread.__init__(self) 
         
    def __del__(self):         
        self.stop()         
 
    def run(self): 
        self._terminate = False 
        try: 
           hEvent = win32event.CreateEvent(None, True, False, Utils.CONFIG_EVENT) 
        except win32api.error: 
            return 
                 
        while not self._terminate: 
            wait = win32event.WaitForSingleObject(hEvent, 1000); 
            if wait != win32event.WAIT_TIMEOUT: 
                self.notify(*self.args)                             
 
    def stop(self): 
        if not self.isAlive(): 
            return 
        self._terminate = True 
                 
    def is_cancelled(self): 
        return self._cancelled 
 
    def get_returnCode(self): 
        return self._ret 
                      
     
                 
def main(): 
    # set C locale, otherwise python and wxpython complain 
    locale.setlocale(locale.LC_ALL, 'C') 
         
    # get the directory of our exetutable file 
    # when running as pyexe built module 
    currentDir = Utils.GetCurrentDir(True) 
    os.chdir(currentDir)     
     
    Utils.CreateProfile() 
     
    # see if we are already running and exit if so 
    try: 
        # try to acquire our active mutex        
        hMutex = win32event.OpenMutex(win32con.SYNCHRONIZE, False, MainWindow.ACTIVE_MUTEX) 
        # could open it - most likely another window is active 
        # just to be sure wait for it to see if it is claimed 
        if win32event.WaitForSingleObject(hMutex, 0) == win32event.WAIT_TIMEOUT:            
            # mutex is claimed, another window is already running - terminate 
            return 
        win32api.CloseHandle(hMutex) 
    except win32api.error: 
        pass 
     
    conf_file = None 
    for arg in sys.argv[1:]: 
        if arg.find('--config_file=') == 0: 
            conf_file = Utils.SafeExpandEnvironmentStrings(arg[len('--config_file='):])               
    if conf_file is None: 
        conf_file = os.path.join(Utils.GetProfileDir(True),'ClamWin.conf') 
         
    if not os.path.isfile(conf_file): 
        conf_file = 'ClamWin.conf'     
    config = Config.Settings(conf_file)     
    config.Read() 
     
    logon = False 
    for arg in sys.argv[1:]: 
        if arg == '--logon': 
            logon = True     
    w=MainWindow(config, logon) 
    win32gui.PumpMessages() 
 
if __name__=='__main__': 
    main()