ClamTray.py
上传用户:lswyart
上传日期:2008-06-12
资源大小:3441k
文件大小:33k
源码类别:

杀毒

开发平台:

Visual C++

  1. #-----------------------------------------------------------------------------
  2. # Name:        Tray.py
  3. # Product:     ClamWin Free Antivirus
  4. #
  5. # Author:      alch [alch at users dot sourceforge dot net]
  6. #
  7. # Created:     2004/19/03
  8. # Copyright:   Copyright alch (c) 2005
  9. # Licence:     
  10. #   This program is free software; you can redistribute it and/or modify
  11. #   it under the terms of the GNU General Public License as published by
  12. #   the Free Software Foundation; either version 2 of the License, or
  13. #   (at your option) any later version.
  14. #   This program is distributed in the hope that it will be useful,
  15. #   but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. #   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  17. #   GNU General Public License for more details.
  18. #   You should have received a copy of the GNU General Public License
  19. #   along with this program; if not, write to the Free Software
  20. #   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  21. #-----------------------------------------------------------------------------
  22. # this code is based on win32gui_taskbar.py demo from Mark Hammond's
  23. # win32 extensions.
  24. import SetUnicode
  25. import RedirectStd
  26. import sys, os, time, tempfile, locale, re
  27. import win32api, win32gui, win32con, win32event
  28. import win32process, win32event
  29. import Scheduler
  30. import Config
  31. import Process
  32. import EmailAlert
  33. import threading
  34. import Utils, wxDialogScheduledScan
  35. import version
  36. class MainWindow:    
  37.     MENU_OPEN_CLAM, MENU_UPDATE_DB, MENU_CHECK_UPDATE, MENU_CLAMWIN_WEB, MENU_CONFIGURE, MENU_SHOWSCANLOG, 
  38.         MENU_SHOWUPDATELOG, MENU_EXIT, MENU_CONFIGURESCHEDULER,
  39.         MENU_TERMINATESCHEDULE, MENU_RUNSCHEDULE = range(1023, 1023 + 11)
  40.         
  41.     ACTIVE_MUTEX='ClamWinTrayMutex01'     
  42.     WM_TASKBAR_NOTIFY=win32con.WM_USER+20
  43.     WM_CONFIG_UPDATED=win32con.WM_USER+21      
  44.     WM_SHOW_BALLOON=win32con.WM_USER+22
  45.                   
  46.     def __init__(self, config, logon):        
  47.         self._config = config        
  48.         self._schedulers = []
  49.         self._scheduledScans = []
  50.         self._processes = []     
  51.         self._balloon_info = None                
  52.         self._balloonThreadLock = threading.Lock()
  53.         msg_TaskbarRestart = win32gui.RegisterWindowMessage("TaskbarCreated");
  54.         message_map = {
  55.                 msg_TaskbarRestart: self.OnRestart,
  56.                 win32con.WM_DESTROY: self.OnDestroy,
  57.                 win32con.WM_COMMAND: self.OnCommand,
  58.                 MainWindow.WM_TASKBAR_NOTIFY: self.OnTaskbarNotify,
  59.                 MainWindow.WM_CONFIG_UPDATED : self.OnConfigUpdated,                
  60.                 MainWindow.WM_SHOW_BALLOON : self.OnShowBalloon                
  61.         }
  62.         # Register the Window class.
  63.         wc = win32gui.WNDCLASS()
  64.         hinst = wc.hInstance = win32api.GetModuleHandle(None)
  65.         wc.lpszClassName = "ClamWinTrayWindow"
  66.         wc.style = win32con.CS_VREDRAW | win32con.CS_HREDRAW;
  67.         wc.hCursor = win32gui.LoadCursor( 0, win32con.IDC_ARROW )
  68.         wc.hbrBackground = win32con.COLOR_WINDOW
  69.         wc.lpfnWndProc = message_map # could also specify a wndproc.
  70.         classAtom = win32gui.RegisterClass(wc)
  71.         # Create the Window.
  72.         style = win32con.WS_OVERLAPPED | win32con.WS_SYSMENU
  73.         self.hwnd = win32gui.CreateWindow( classAtom, "ClamWin", style, 
  74.                 0, 0, win32con.CW_USEDEFAULT, win32con.CW_USEDEFAULT, 
  75.                 0, 0, hinst, None)
  76.         win32gui.UpdateWindow(self.hwnd)
  77.         
  78.         # create mutex to prevent further instances
  79.         self._hActiveMutex = win32event.CreateMutex(None, True, self.ACTIVE_MUTEX)
  80.         self._DoCreateIcons()
  81.         self._InitSchedulers(logon)
  82.         
  83.         # start config monitor thread
  84.         self._configMonitor = MonitorConfig(self.NotifyConfig, (self.hwnd,))
  85.         self._configMonitor.start()
  86.         
  87.     def _IsProcessRunning(self, proc, wait=False):
  88.         if wait:
  89.             timeout = 5
  90.         else:
  91.             timeout = 0            
  92.         try:                    
  93.             proc.wait(timeout)
  94.         except Exception, e:
  95.             if isinstance(e, Process.ProcessError):
  96.                 if e.errno == Process.ProcessProxy.WAIT_TIMEOUT:        
  97.                     return True     
  98.                 else:
  99.                     return False
  100.         return False
  101.     def _StopProcesses(self):          
  102.         # check if process is still running        
  103.         for proc in self._processes:                        
  104.             if self._IsProcessRunning(proc):                       
  105.                 # running - kill            
  106.                 proc.kill()                    
  107.                 #wait to finish                  
  108.                 if self._IsProcessRunning(proc, True):       
  109.                     #still running - complain and terminate
  110.                     win32gui.MessageBox(self.hwnd, 'Unable to stop scheduled process, terminating', 'ClamWin Free Antivirus', win32con.MB_OK | win32con.MB_ICONSTOP)
  111.                     os._exit(0)  
  112.                 proc.close()
  113.                 
  114.         self._processes = []     
  115.     
  116.     def _TerminateSchedules(self):        
  117.         self._StopProcesses()               
  118.         for scheduler in self._schedulers:
  119.             try:                
  120.                 scheduler.stop()                
  121.                 # wait for completion
  122.                 scheduler.join(2)                
  123.             except Exception, e:
  124.                 print 'An error occured whilst termintaing scheduler thread. Error: %s' % str(e)
  125.         self._schedulers = []             
  126.         
  127.     def _InitSchedulers(self, logon=False):              
  128.         # close all running schedules
  129.         self._TerminateSchedules()   
  130.         
  131.         # load persistent scheduler
  132.         self._scheduledScans = wxDialogScheduledScan.LoadPersistentScheduledScans(
  133.                 os.path.join(Utils.GetScheduleShelvePath(self._config), 'ScheduledScans'))
  134.         
  135.         # create an update schedule to run now if 'Update on Logon' is selected
  136.         if logon and self._config.Get('Updates', 'UpdateOnLogon') == '1':            
  137.             # set C locale, otherwise python and wxpython complain
  138.             locale.setlocale(locale.LC_ALL, 'C')
  139.             
  140.             start_time = time.localtime(time.time() + 120)
  141.             weekday = int(time.strftime('%w', start_time))
  142.             if weekday: weekday -= 1
  143.             else: weekday = 6            
  144.             scheduler = Scheduler.Scheduler('Once',
  145.                             time.strftime('%H:%M:%S', start_time),
  146.                             weekday, 
  147.                             win32gui.SendMessage, (self.hwnd, win32con.WM_COMMAND, self.MENU_UPDATE_DB, 1),
  148.                             ('ClamWin_Scheduler_Info', 'ClamWin_Upadte_Time'))            
  149.             scheduler.start()        
  150.             self._schedulers.append(scheduler)
  151.         
  152.         # create a scheduler thread for DB updates        
  153.         if self._config.Get('Updates', 'Enable') == '1':            
  154.             scheduler = Scheduler.Scheduler(self._config.Get('Updates', 'Frequency'),
  155.                             self._config.Get('Updates', 'Time'),
  156.                             int(self._config.Get('Updates', 'WeekDay')), 
  157.                             win32gui.SendMessage, (self.hwnd, win32con.WM_COMMAND, self.MENU_UPDATE_DB, 1),
  158.                             ('ClamWin_Scheduler_Info', 'ClamWin_Upadte_Time'))            
  159.             scheduler.start()        
  160.             self._schedulers.append(scheduler)
  161.         
  162.         # create scheduler threads for all scheduled scans                    
  163.         for scan in self._scheduledScans:
  164.             if scan.Active:
  165.                 scheduler = Scheduler.Scheduler(scan.Frequency,
  166.                             scan.Time,
  167.                             int(scan.WeekDay), 
  168.                             self.ScanPath, (self, scan.Path, scan.Description))            
  169.                 scheduler.start()        
  170.                 self._schedulers.append(scheduler)   
  171.                 
  172.         # create scheduler thread for program version check                    
  173.         if self._config.Get('Updates', 'CheckVersion') == '1':
  174.             curDir = Utils.GetCurrentDir(True)      
  175.             scheduler = Scheduler.Scheduler('Daily', # check once aday
  176.                             time.strftime('%H:%M:%S', time.localtime(time.time() + 300)), # 5 minutes after start
  177.                             1, # unused
  178.                             Utils.SpawnPyOrExe, (os.path.join(curDir, 'ClamWin'), ' --mode=checkversion'),
  179.                             ('ClamWin_CheckVer_Info', 'ClamWin_CheckVer_Time'))            
  180.             scheduler.start()        
  181.             self._schedulers.append(scheduler)
  182.     def _Terminate(self):
  183.         # terminate running threads
  184.         self._TerminateSchedules()
  185.         if self._configMonitor is not None:
  186.             self._configMonitor.stop()
  187.             self._configMonitor.join(2)        
  188.             self._configMonitor = None
  189.             
  190.     def _DoCreateIcons(self):
  191.         # Try and find a custom icon
  192.         hinst =  win32api.GetModuleHandle(None)
  193.         iconPathName = os.path.abspath(os.path.join(os.path.split(sys.executable)[0],"img/TrayIcon.ico"))
  194.         if not os.path.isfile(iconPathName):
  195.             # Look in the current folder tree.
  196.             iconPathName = "img/TrayIcon.ico"
  197.         if os.path.isfile(iconPathName):
  198.             icon_flags = win32con.LR_LOADFROMFILE | win32con.LR_DEFAULTSIZE
  199.             hicon = win32gui.LoadImage(hinst, iconPathName, win32con.IMAGE_ICON, 0, 0, icon_flags)
  200.         else:            
  201.             hicon = win32gui.LoadIcon(0, win32con.IDI_APPLICATION)
  202.         flags = win32gui.NIF_ICON | win32gui.NIF_MESSAGE | win32gui.NIF_TIP
  203.         nid = (self.hwnd, 0, flags, MainWindow.WM_TASKBAR_NOTIFY, hicon, "ClamWin Free Antivirus")
  204.         win32gui.Shell_NotifyIcon(win32gui.NIM_ADD, nid)        
  205.             
  206.     def OnRestart(self, hwnd, msg, wparam, lparam):
  207.         self._DoCreateIcons()
  208.     def OnDestroy(self, hwnd, msg, wparam, lparam):                  
  209.         nid = (self.hwnd, 0)
  210.         win32gui.Shell_NotifyIcon(win32gui.NIM_DELETE, nid)        
  211.         self._Terminate()
  212.         win32event.ReleaseMutex(self._hActiveMutex)
  213.         win32api.CloseHandle(self._hActiveMutex)        
  214.         # Terminate the app.
  215.         win32gui.PostQuitMessage(0) 
  216.     def OnTaskbarNotify(self, hwnd, msg, wparam, lparam):
  217.         if lparam==win32con.WM_LBUTTONUP:
  218.             pass
  219.         elif lparam==win32con.WM_LBUTTONDBLCLK:
  220.            self.OnCommand(hwnd, win32con.WM_COMMAND, self.MENU_OPEN_CLAM, 0)
  221.         elif lparam==win32con.WM_RBUTTONUP:                                 
  222.                                 
  223.             # create scheduler menu
  224.             scheduler_popup = win32gui.CreatePopupMenu()
  225.             win32gui.AppendMenu(scheduler_popup, win32con.MF_STRING, 
  226.                 self.MENU_CONFIGURESCHEDULER, "&Configure Scheduler")
  227.             
  228.             if not self._processes:
  229.                 flags = win32con.MF_GRAYED
  230.             else:
  231.                 flags = 0
  232.             
  233.             # create scheduled tasks menu
  234.             tasks_popup = win32gui.CreatePopupMenu()            
  235.             i = 0
  236.             for scan in self._scheduledScans:
  237.                 win32gui.AppendMenu(tasks_popup, win32con.MF_STRING, 
  238.                     self.MENU_RUNSCHEDULE + i, scan.Description)
  239.                 i+=1
  240.             if not i:
  241.                 flags2 = win32con.MF_GRAYED
  242.             else:
  243.                 flags2 = 0
  244.             win32gui.InsertMenu(scheduler_popup, self.MENU_CONFIGURESCHEDULER,
  245.                             win32con.MF_BYCOMMAND | win32con.MF_POPUP | flags2,
  246.                             tasks_popup, "&Run Scheduled Scan")                                            
  247.                 
  248.             win32gui.InsertMenu(scheduler_popup, flags, 
  249.                                 win32con.MF_BYCOMMAND | win32con.MF_STRING | flags, 
  250.                                 self.MENU_TERMINATESCHEDULE, "&Stop All Running Tasks Now")                        
  251.                                 
  252.             # create reports menu
  253.             reports_popup = win32gui.CreatePopupMenu()            
  254.             if not len(self._config.Get('ClamAV', 'LogFile')):
  255.                 flags = win32con.MF_GRAYED
  256.             else:
  257.                 flags = 0
  258.             win32gui.InsertMenu( reports_popup, 0, 
  259.                                 win32con.MF_BYCOMMAND | win32con.MF_STRING | flags, 
  260.                                 self.MENU_SHOWSCANLOG, "&Virus Scan Report")                        
  261.             if not len(self._config.Get('Updates', 'DBUpdateLogFile')):
  262.                 flags = win32con.MF_GRAYED
  263.             else:
  264.                 flags = 0
  265.             win32gui.InsertMenu( reports_popup, self.MENU_SHOWSCANLOG,
  266.                                 win32con.MF_BYCOMMAND | win32con.MF_STRING | flags,
  267.                                 self.MENU_SHOWUPDATELOG, "&Virus Database Update Report")                        
  268.             # create main menu
  269.             menu = win32gui.CreatePopupMenu()                                                        
  270.             win32gui.AppendMenu( menu, win32con.MF_STRING, self.MENU_OPEN_CLAM, "&Open ClamWin")
  271.             win32gui.AppendMenu( menu, win32con.MF_STRING, self.MENU_UPDATE_DB, "&Download Virus Database Update")
  272.             win32gui.AppendMenu( menu, win32con.MF_STRING, self.MENU_CONFIGURE, "&Configure ClamWin")                   
  273.             win32gui.AppendMenu( menu, win32con.MF_POPUP, scheduler_popup, "&Scheduler")                     
  274.             win32gui.AppendMenu( menu, win32con.MF_POPUP, reports_popup, "Display &Reports")                
  275.             win32gui.AppendMenu( menu, win32con.MF_SEPARATOR, 0, "" )                                
  276.             win32gui.AppendMenu( menu, win32con.MF_STRING, self.MENU_CHECK_UPDATE, "Check &Latest Version")
  277.             win32gui.AppendMenu( menu, win32con.MF_STRING, self.MENU_CLAMWIN_WEB, "&Visit ClamWin Website")
  278.             win32gui.AppendMenu( menu, win32con.MF_SEPARATOR, 0, "" )                                
  279.             win32gui.AppendMenu( menu, win32con.MF_STRING, self.MENU_EXIT, "&Exit" )                        
  280.             
  281.             pos = win32gui.GetCursorPos()
  282.             # See http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/menus_0hdi.asp
  283.             win32gui.SetForegroundWindow(self.hwnd)
  284.             try:
  285.                 win32gui.SetMenuDefaultItem(menu, 0, 1)
  286.             except NameError:
  287.                 pass
  288.             win32gui.TrackPopupMenu(menu, win32con.TPM_LEFTALIGN, pos[0], pos[1], 0, self.hwnd, None)
  289.             win32gui.PostMessage(self.hwnd, win32con.WM_NULL, 0, 0)
  290.             
  291.         return 1
  292.     def OnCommand(self, hwnd, msg, wparam, lparam):
  293.         id = win32api.LOWORD(wparam)
  294.         if id == self.MENU_OPEN_CLAM:
  295.             self._ShowClamWin()
  296.         elif id == self.MENU_UPDATE_DB:
  297.             self._UpdateDB(lparam)
  298.         elif id == self.MENU_CHECK_UPDATE:
  299.             self._OpenWebPage('http://www.clamwin.com/index.php?option=content&task=view&id=40&Itemid=60&version='+version.clamwin_version)
  300.         elif id == self.MENU_CLAMWIN_WEB:
  301.             self._OpenWebPage('http://www.clamwin.com')
  302.         elif id == self.MENU_CONFIGURE:
  303.             self._ShowConfigure()
  304.         elif id == self.MENU_SHOWSCANLOG:            
  305.             self._ShowLog(self._config.Get('ClamAV', 'LogFile'))
  306.         elif id == self.MENU_SHOWUPDATELOG:            
  307.             self._ShowLog(self._config.Get('Updates', 'DBUpdateLogFile'))
  308.         elif id == self.MENU_EXIT:          
  309.             self.OnExit()
  310.         elif id == self.MENU_CONFIGURESCHEDULER:
  311.             self._ShowConfigure(True)
  312.         elif id == self.MENU_TERMINATESCHEDULE:
  313.             self._TerminateSchedules()
  314.             self._InitSchedulers()
  315.         elif (id >= self.MENU_RUNSCHEDULE) and 
  316.             (id < self.MENU_RUNSCHEDULE + len(self._scheduledScans)):
  317.                 try:
  318.                     path = self._scheduledScans[id - self.MENU_RUNSCHEDULE].Path
  319.                     if path[len(path)-1] == '\':
  320.                         path = path[:len(path)-1]
  321.                     self._ShowClamWin(path) 
  322.                 except Exception, e:
  323.                     win32gui.MessageBox(self.hwnd, 
  324.                             'Could not launch ClamWin Scanner. Error: %s' % str(e), 
  325.                             'ClamWin Free Antivirus', win32con.MB_OK | win32con.MB_ICONERROR)
  326.                 
  327.             
  328.     def OnConfigUpdated(self, hwnd, msg, wparam, lparam):
  329.      self._config.Read()
  330.         self._InitSchedulers()   
  331.         
  332.     def OnShowBalloon(self, hwnd, msg, wparam, lparam):
  333.         if self._balloon_info is not None:
  334.             try:                
  335.                 Utils.ShowBalloon(wparam, self._balloon_info, self.hwnd)                
  336.             except Exception, e:
  337.                 print 'Could not display balloon tooltip. Error: %s' % str(e)
  338.                                              
  339.     def OnExit(self):
  340.         win32gui.DestroyWindow(self.hwnd)                        
  341.     
  342.     def _ShowLog(self, logfile):
  343.         try:  
  344.             curDir = Utils.GetCurrentDir(True)
  345.             params = (' --mode=viewlog',  '--path="%s"' % logfile)
  346.             Utils.SpawnPyOrExe(os.path.join(curDir, 'ClamWin'), *params)                
  347.         except Exception, e:            
  348.             win32gui.MessageBox(self.hwnd, 'An error occured while displaying log file %s.nError: %s' % (logfile, str(e)),
  349.                                  'ClamWin Free Antivirus', win32con.MB_OK | win32con.MB_ICONERROR)                                                               
  350.                                  
  351.     def _ShowClamWin(self, path=''):        
  352.         try:              
  353.             if path:
  354.                 params = (' --mode=scanner',  ' --path="%s"' % path)
  355.             else:
  356.                 params = (' --mode=main',)
  357.             Utils.SpawnPyOrExe(os.path.join(Utils.GetCurrentDir(True), 'ClamWin'), *params)
  358.         except Exception, e:            
  359.             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)                              
  360.     
  361.     def _UpdateDB(self, hide):                
  362.         if not hide:                        
  363.             try:
  364.                 params = (' --mode=update', ' --config_file="%s"' % self._config.GetFilename())
  365.                 Utils.SpawnPyOrExe(os.path.join(Utils.GetCurrentDir(True), 'ClamWin'), *params)                                
  366.             except Exception, e:
  367.                 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)                                  
  368.         else:                        
  369.             # update virus db silently
  370.             freshclam_conf = Utils.SaveFreshClamConf(self._config)
  371.             try:
  372.                 if not len(freshclam_conf):
  373.                     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)
  374.                     return
  375.                 # create database folder before downloading
  376.                 dbdir = self._config.Get('ClamAV', 'Database')                
  377.                 if dbdir and not os.path.exists(dbdir):
  378.                     try:
  379.                         os.makedirs(dbdir)
  380.                     except:
  381.                         pass                                                                   
  382.                 updatelog = tempfile.mktemp()
  383.                 cmd = '--stdout --datadir="' + dbdir + '"' + 
  384.                         ' --config-file="%s" --log="%s"' % (freshclam_conf, updatelog)                                
  385.                 cmd = cmd.replace('\', '/')
  386.                 cmd = '"%s" %s' % (self._config.Get('ClamAV', 'FreshClam'), cmd)
  387.                 try:
  388.                     if self._config.Get('UI', 'TrayNotify') == '1':
  389.                         balloon = (('Virus database has been updated.', 0, 
  390.                                    win32gui.NIIF_INFO, 10000),
  391.                                    ('An error occured during Scheduled Virus Database Update. Please review the update report.', 1, 
  392.                                    win32gui.NIIF_WARNING, 30000))
  393.                     else:
  394.                         balloon = None                    
  395.                     proc = self._SpawnProcess(cmd,
  396.                         'n',
  397.                         self.DBUpdateProcessFinished,
  398.                         (self._config.Get('ClamAV', 'Database'),
  399.                         self._config.Get('Updates', 'DBUpdateLogFile'),                        
  400.                         updatelog, False,                         
  401.                         balloon))                             
  402.                     self._processes.append(proc)
  403.                 except Process.ProcessError, e:
  404.                     print 'Unable to spawn scheduled process.nCommand line: %snError: %s' % (cmd , str(e))
  405.                     try:
  406.                         os.remove(freshclam_conf)
  407.                         os.remove(updatelog)
  408.                     except:
  409.                         pass
  410.                     return
  411.                 # wait 2 seconds for the process to start, then delete
  412.                 # temp file
  413.                 try:
  414.                     proc.wait(2)
  415.                 except:
  416.                     pass
  417.                 os.remove(freshclam_conf)
  418.             except Exception, e:                            
  419.                 print 'Error performing Scheduled Update.', str(e)
  420.                 os.remove(freshclam_conf)                  
  421.                                 
  422.     def _OpenWebPage(self, url):
  423.         try:
  424.             import webbrowser
  425.             webbrowser.open(url)
  426.         except ImportError:
  427.             win32gui.MessageBox(self.hwnd, 'Please point your browser at: %s' % url, 'ClamWin Free Antivirus', win32con.MB_OK | win32con.MB_ICONINFORMATION)
  428.             
  429.         
  430.     def _ShowConfigure(self, switchToSchedule = False):
  431.         try:                           
  432.             curDir = Utils.GetCurrentDir(True)        
  433.             if switchToSchedule:
  434.                 mode = 'configure_schedule'   
  435.             else:
  436.                 mode = 'configure'  
  437.             params = (' --mode=%s' % mode,
  438.                         ' --config_file="%s"' % self._config.GetFilename())            
  439.             Utils.SpawnPyOrExe(os.path.join(curDir, 'ClamWin'), *params)
  440.         except Exception, e:            
  441.             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)
  442.     # returns process and stdout buffer
  443.     def _SpawnProcess(self, cmd, proc_priority, finished_func, finished_params):
  444.         # initialise environment var TMPDIR
  445.         # for clamav
  446.         try:
  447.             if os.getenv('TMPDIR') is None:
  448.                 os.putenv('TMPDIR', tempfile.gettempdir().replace('\', '/'))
  449.             #Utils.SetCygwinTemp()
  450.         except Exception, e:
  451.             print str(e)                        
  452.             
  453.         # check that we got the command line        
  454.         if cmd is None:   
  455.             raise Process.ProcessError('Could not start process. No Command Line specified')                                                     
  456.         
  457.         # start our process    
  458.         try:                
  459.             # check if the file exists first            
  460.             executable = cmd.split('" ' ,1)[0].lstrip('"')
  461.             if not os.path.exists(executable):
  462.                 raise Process.ProcessError('Could not start process.n%snFile does not exist.' % executable)                            
  463.             out = OutBuffer(self, finished_func, finished_params)
  464.             proc = Process.ProcessProxy(cmd, stdout=out, priority=proc_priority)            
  465.             out.AttachProcess(proc)
  466.             proc.wait(0)            
  467.         except Exception, e:             
  468.             if isinstance(e, Process.ProcessError):
  469.                 if e.errno != Process.ProcessProxy.WAIT_TIMEOUT:                                       
  470.                     raise Process.ProcessError('Could not start process:n%snError: %s' % (cmd, str(e)))                     
  471.             else:
  472.                 raise Process.ProcessError('Could not start process:n%snError: %s' % (cmd, str(e)))                             
  473.         return proc
  474.     
  475.     def ScanPath(self, path, description):
  476.         scanlog = tempfile.mktemp()
  477.         path = '"%s"' % path.rstrip('\').strip('"')
  478.         cmd = Utils.GetScanCmd(self._config, path, scanlog)        
  479.         try:            
  480.             if self._config.Get('UI', 'TrayNotify') == '1':
  481.                 balloon = (('Virus has been detected during scheduled scan! Please review the scan report.', 1, 
  482.                           win32gui.NIIF_ERROR, 30000),
  483.                           ('An error occured during scheduled scan. Please review the scan report.', 0, 
  484.                           win32gui.NIIF_WARNING, 30000))
  485.             else:
  486.                 balloon = None
  487.                 
  488.             try:
  489.                 priority = self._config.Get('ClamAV', 'Priority')[:1].lower()
  490.             except:
  491.                 priority = 'n'
  492.             # clamav stopped writing start time of the scan to the log file
  493.             #try:
  494.             #    file(scanlog, 'wt').write('Scan Started %sn' % time.ctime(time.time()))      
  495.             #except:
  496.             #    pass
  497.             proc = self._SpawnProcess(cmd, 
  498.                         priority,
  499.                         self.ProcessFinished,
  500.                         (self._config.Get('ClamAV', 'LogFile'),                         
  501.                         scanlog,                         
  502.                         self._config.Get('EmailAlerts', 'Enable') == '1',                        
  503.                         balloon
  504.                         ))             
  505.             self._processes.append(proc) 
  506.             result = 0                  
  507.         except Process.ProcessError, e:
  508.             result = -1
  509.             try:
  510.                os.remove(scanlog)
  511.             except:
  512.                pass
  513.             print str(e)
  514.         if self._config.Get('UI', 'TrayNotify') == '1':  
  515.             balloon_info = (('Running Scheduled Task:n'+description, 0, 
  516.                             win32gui.NIIF_INFO, 10000),
  517.                             ('An error occured whilst running Running Scheduled Task '+description, 1, 
  518.                             win32gui.NIIF_WARNING, 30000))                                      
  519.             self.ShowBalloon(result, balloon_info)        
  520.     ScanPath = staticmethod(ScanPath)
  521.            
  522.     def NotifyConfig(hwnd):        
  523.         win32api.PostMessage(hwnd, MainWindow.WM_CONFIG_UPDATED, 0, 0)                
  524.     NotifyConfig = staticmethod(NotifyConfig)
  525.     def DBUpdateProcessFinished(self, process, dbpath, log, appendlog, email_alert, balloon_info):
  526.         #Utils.SetDbFilesPermissions(dbpath)
  527.         self.ProcessFinished(self, process, log, appendlog, email_alert, balloon_info)        
  528.     DBUpdateProcessFinished = staticmethod(DBUpdateProcessFinished)
  529.     
  530.     def ProcessFinished(self, process, log, appendlog, email_alert, balloon_info):
  531.         # send the notification alert if we need to        
  532.         if email_alert:            
  533.             try:
  534.                 if process.wait() == 1:
  535.                     msg = EmailAlert.ConfigVirusAlertMsg(self._config, (appendlog,))
  536.                     msg.Send()
  537.             except Exception, e:
  538.                 print 'Could not send email alert. Error: %s' % str(e)
  539.                                    
  540.         maxsize = int(self._config.Get('ClamAV', 'MaxLogSize'))*1048576                
  541.         Utils.AppendLogFile(log, appendlog, maxsize)                   
  542.         
  543.         try:
  544.             os.remove(appendlog)
  545.         except Exception, e:
  546.             print 'could not remove file: %s. Error: %s' % (appendlog, str(e))
  547.                             
  548.         if not process.isKilled() and balloon_info is not None:                        
  549.             # show balloon
  550.             self.ShowBalloon(process.wait(), balloon_info)   
  551.             
  552.         # find and remove our process
  553.         try:
  554.             self._processes.remove(process)                                                
  555.         except ValueError:
  556.             # ignore "not in list" errors
  557.             pass
  558.     ProcessFinished = staticmethod(ProcessFinished)
  559.     
  560.     # send message to the main window thread to display balloon notification
  561.     # we need to enclose the call to SendMessage within Lock().acquire()/Lock.release()
  562.     # to ensure that correct self._balloon_info is used when 2 threads want to 
  563.     # display balloons simultaneously
  564.     def ShowBalloon(self, result, balloon_info):         
  565.         self._balloonThreadLock.acquire()                
  566.         try:
  567.             self._balloon_info = balloon_info
  568.             win32api.SendMessage(self.hwnd, MainWindow.WM_SHOW_BALLOON, result, 0)                            
  569.         finally:
  570.             self._balloon_info = None
  571.             self._balloonThreadLock.release()                
  572.      
  573. # stdout buffer used by ProcessProxy to notify main thread
  574. # when execution is complete
  575. class OutBuffer(Process.IOBuffer):            
  576.     def __init__(self, caller, notify, params):
  577.         Process.IOBuffer.__init__(self)                
  578.         self.notify = notify    
  579.         self._caller = caller
  580.         self._params = params        
  581.         self._proc = None
  582.                                                         
  583.     def _doClose(self):                
  584.         self.notify(self._caller, self._proc, *self._params) 
  585.         if self._proc:
  586.             del self._proc               
  587.         Process.IOBuffer._doClose(self)
  588.         
  589.     def AttachProcess(self, proc):
  590.         self._proc = proc
  591.     
  592.     
  593. # this thread monitors changes to config files
  594. #  and notifies tray to reload if a change occurs                          
  595. class MonitorConfig(threading.Thread):   
  596.     def __init__(self, notify, args):
  597.         self.notify = notify
  598.         self.args = args
  599.         self._terminate = False
  600.         threading.Thread.__init__(self)
  601.         
  602.     def __del__(self):        
  603.         self.stop()        
  604.     def run(self):
  605.         self._terminate = False
  606.         try:
  607.            hEvent = win32event.CreateEvent(None, True, False, Utils.CONFIG_EVENT)
  608.         except win32api.error:
  609.             return
  610.                 
  611.         while not self._terminate:
  612.             wait = win32event.WaitForSingleObject(hEvent, 1000);
  613.             if wait != win32event.WAIT_TIMEOUT:
  614.                 self.notify(*self.args)                            
  615.     def stop(self):
  616.         if not self.isAlive():
  617.             return
  618.         self._terminate = True
  619.                 
  620.     def is_cancelled(self):
  621.         return self._cancelled
  622.     def get_returnCode(self):
  623.         return self._ret
  624.                      
  625.     
  626.                 
  627. def main():
  628.     # set C locale, otherwise python and wxpython complain
  629.     locale.setlocale(locale.LC_ALL, 'C')
  630.         
  631.     # get the directory of our exetutable file
  632.     # when running as pyexe built module
  633.     currentDir = Utils.GetCurrentDir(True)
  634.     os.chdir(currentDir)    
  635.     
  636.     Utils.CreateProfile()
  637.     
  638.     # see if we are already running and exit if so
  639.     try:
  640.         # try to acquire our active mutex       
  641.         hMutex = win32event.OpenMutex(win32con.SYNCHRONIZE, False, MainWindow.ACTIVE_MUTEX)
  642.         # could open it - most likely another window is active
  643.         # just to be sure wait for it to see if it is claimed
  644.         if win32event.WaitForSingleObject(hMutex, 0) == win32event.WAIT_TIMEOUT:           
  645.             # mutex is claimed, another window is already running - terminate
  646.             return
  647.         win32api.CloseHandle(hMutex)
  648.     except win32api.error:
  649.         pass
  650.     
  651.     conf_file = None
  652.     for arg in sys.argv[1:]:
  653.         if arg.find('--config_file=') == 0:
  654.             conf_file = Utils.SafeExpandEnvironmentStrings(arg[len('--config_file='):])              
  655.     if conf_file is None:
  656.         conf_file = os.path.join(Utils.GetProfileDir(True),'ClamWin.conf')
  657.         
  658.     if not os.path.isfile(conf_file):
  659.         conf_file = 'ClamWin.conf'    
  660.     config = Config.Settings(conf_file)    
  661.     config.Read()
  662.     
  663.     logon = False
  664.     for arg in sys.argv[1:]:
  665.         if arg == '--logon':
  666.             logon = True    
  667.     w=MainWindow(config, logon)
  668.     win32gui.PumpMessages()
  669. if __name__=='__main__':
  670.     main()