'''
Action interscanners for sagator, version 0.6.0

(c) 2003-2004 Jan ONDREJ (SAL) <ondrejj(at)salstar.sk>

 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.
    
'''

from avlib import *
import re,os,time
from match import match_any

__all__=['quarantine','drop','deliver','deliver_to']

#####################################################################
### QUARANTINE class

class quarantine(match_any):
  '''
  quarantine() - quarantine into any directory
  
  usage: quarantine('qdir','dont quarantine regular expression',scanners)

  where: 'qdir' is a string, which defines a qatantine directory,
                directory, in which viruses/spams are stored.
                This string can contain %X substrings. These strings
                are represented as defined in strftime. For more information
                see "man strftime". If this directory not exitst,
                it's created.
         'dont quarantine regular expression' is a regular expression,
                which defines virus names, which can't be quarantined

  Example: quarantine('/tmp/quarantine/%Y%m', a_scanner())
             - every month will be stored in separate directory

  This scanner can be used to quarantine an virus/spam.
  qdir is a directory, in which viruses are stored.
  Following regular expression can be used to disable quarantining
  of some viruses or spams.
  '''
  name='quarantine()'
  def __init__(self,qdir,dontq,*scanners):
      self.QDIR=qdir
      self.DONTQ=dontq
      match_any.__init__(self,scanners)
  def scanstream(self,stream):
      level,detected,virlist=match_any.scanstream(self,stream)
      if globals.scan_only:
        return level,detected,virlist
      if is_infected(level,detected):
        if (self.QDIR!='') & (self.DONTQ!='') \
            & (re.search(self.DONTQ,detected,re.IGNORECASE)!=None):
          qfname='Not quarantined yet.'
        else:
          fext=time.strftime("-%Y%m%d-%H%M%S-",time.localtime(time.time()))
          fpath=time.strftime(self.QDIR)
          # try to make this directory
          try:
            os.makedirs(fpath)
          except OSError,(ec,es):
            if ec!=17: # file exists
              raise
          num=0
          while 1:
            qfname=fpath+'/qm'+fext+str(num)
            num+=1
            try:
              fd=safe.osopen(qfname,os.O_WRONLY|os.O_CREAT|os.O_EXCL,0660)
              f=os.fdopen(fd,'w',BUFSIZE)
              f.write(mail.addr)
              f.write(mail.comm)
              f.write(mail.xheader) # added headers
              f.write(mail.data) # original data, not modified
              f.write(".\r\nQUIT\r\n")
              f.close()
              debug.echo(3,"Quarantined as: "+qfname)
              break
            except OSError,(ec,es):
              if ec==17: # already exists
                continue
              else:
                debug.echo(2,"ERROR: quarantine(): ",es)
                #return level,detected,virlist
                raise ScannerError,"qarantine(): OSError: "+es
        globals.QFNAME=qfname
      return level,detected,virlist

#####################################################################
### drop and deliver classes

class drop(match_any):
  '''
  drop() - a class to DROP viruses/spams, by default they are REJECTED

  usage: drop(drop_pattern,scanners)
  
  This scanner can be used to drop some viruses, like Klez.
  Klez sends fake sender address and this virus can't be sent
  back to sender, because sender is faked.
  
  This scanner does nothing else, like sets the DROP flag.
  Email is passed to parent scanners and is dropped in sagator.
  '''
  name='drop()'
  def __init__(self,drop_pattern,*scanners):
      self.ONLY_IF=drop_pattern
      self.DROP_LEVEL=9999.9
      match_any.__init__(self,scanners)
  def scanstream(self,stream):
      level,detected,virlist=match_any.scanstream(self,stream)
      if re.search(self.ONLY_IF,detected,re.IGNORECASE):
        globals.ACTION=S_DROP
      if level>=self.DROP_LEVEL:
        globals.ACTION=S_DROP
      return level,detected,virlist

class deliver(match_any):
  '''
  deliver() - a class to force sending some viruses/spams

  usage: deliver(deliver_pattern,scanners)
  
  where: deliver_pattern is an regular expression, which defines
           virus name to really deliver, other emails will not be delivered.

  This scanner can be used to send some viruses/spams.
  You can define by regular expression, what virus name or spam
  can be sent.
  '''
  name='deliver()'
  def __init__(self,*scanners):
      self.ONLY_IF='.'
      match_any.__init__(self,scanners)
  def onlyif(self,pattern):
      self.ONLY_IF=pattern
      return self
  def scanstream(self,stream):
      level,detected,virlist=match_any.scanstream(self,stream)
      if re.search(self.ONLY_IF,detected,re.IGNORECASE):
        globals.ACTION=S_FORCE_SEND
      return level,detected,virlist

class deliver_to(match_any):
  '''
  deliver_to() - an interscanner to send emails to admins
  
  Usage: deliver_to(recipients,scanners)
     or: deliver_to(...).onlyif('string')
  
  Where: recipients is an array of recipients
         'string' is an string, which defines regular expression.
           If this expression is found in virus name, mail is
           delivered, otherwise don't.

  This scanner can be used to send viruses/spams to an administrator.
  '''
  name='deliver_to()'
  def __init__(self,recipients,*scanners):
      self.RECIPIENTS=recipients
      self.ONLY_IF='.'
      match_any.__init__(self,scanners)
  def onlyif(self,pattern='.'):
      self.ONLY_IF=pattern
      return self
  def scanstream(self,stream):
      level,detected,virlist=match_any.scanstream(self,stream)
      if globals.scan_only:
        return level,detected,virlist
      if re.search(self.ONLY_IF,detected,re.IGNORECASE):
        if self.RECIPIENTS:
          try:
            debug.echo(4,"deliver_to(): Trying to deliver to: ",
                         str(self.RECIPIENTS))
            smtpc().sendmail(mail.sender,self.RECIPIENTS,
              mail.header+mail.xheader+mail.data[mail.bodypos:])
          except:
            debug.echo(1,"deliver_to(): An error occured when sending reports")
            debug.traceback(4,"deliver_to():")
            raise ScannerError,"deliver_to()"
      return level,detected,virlist
