www.pudn.com > clamwin-0.85.1-src.zip > ClamTray.py


#-----------------------------------------------------------------------------
# Name:        Tray.py
# Product:     ClamWin Antivirus
#
# Author:      alch [alch at users dot sourceforge dot net]
#
# Created:     2004/19/03
# Copyright:   Copyright alch (c) 2004
# 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


class MainWindow:    
    MENU_OPEN_CLAM, MENU_UPDATE_DB, MENU_CONFIGURE, MENU_SHOWSCANLOG, \
        MENU_SHOWUPDATELOG, MENU_EXIT, MENU_CONFIGURESCHEDULER,\
        MENU_TERMINATESCHEDULE, MENU_RUNSCHEDULE = range(1023, 1023 + 9)
        
    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', 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)
        
        # cerate 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)        

    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 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_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_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', 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', 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 scanner.\n' + str(e), 'ClamWin', 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 Update.\n' + str(e), 'ClamWin', 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 cerate 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 = re.sub('([A-Za-z]):[/\\\\]', r'/cygdrive/\1/', 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 _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 Preferences.\n' + str(e), 'ClamWin', 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', 
                           re.sub('([A-Za-z]):[/\\\\]', r'/cygdrive/\1/', 
                           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'      
            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()