'''
Condition scanners for sagator, version 0.1
 
(c) 2006 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 *
                                                                                
__all__=['rcpt_in_sql','rcpt_in_txt','rcpt_no_match']

class rcpt_in_sql(ascanner):
  '''
  Retun virus if recipient matches against a database row.
  
  This scanner is useful as an condition for other scanners. You can use
  SQL database to assingn required return value. Table description:
    CREATE TABLE signatures (
      prio int,
      email text,
      key varchar(16)
    );
  Priority is used when more than one rows will match. By default this
  SELECT is used to find proper line:

    SELECT email FROM signatures
      WHERE key='__name__' AND '__rcpt__' LIKE email
      ORDER BY prio

  The "__name__" is replaced by key from scanner definition and the
  "__rcpt__" is replaced by recipient's email address.
  
  Usage: rcpt_in_sql(dbc, key)
  
  Where: dbc is an database connection
         key is a scanner name defined in SQL table
  
  Example: rcpt_in_sql(db.sqlite(), 'AntiSpam') & spamassassind()
  
  New in version 0.8.0.
  '''
  name='rcpt_in_sql()'
  def __init__(self,dbc,key):
      self.DBC=dbc
      self.QUERY="SELECT email FROM signatures WHERE key='%(key)s' AND '%(rcpt)s' LIKE email ORDER BY prio"
      self.KEY=key
      self.reinit()
  def reinit(self):
      self.MATCHED={}
  def rcpt_signature(self,rcpt):
      try:
        match=self.DBC.query(
          self.QUERY % {'key':self.KEY, 'rcpt':rcpt}
        )[0][0]
        self.MATCHED[rcpt]=match
        globals.RCPT_MATCH[self.KEY]=match
        debug.echo(4,'%s: %s matched for %s [%s]' \
                     % (self.name,self.KEY,rcpt,match))
        return self.KEY
      except IndexError:
        self.MATCHED[rcpt]=''
      return ''
  def scanbuffer(self,buffer,args={}):
      try:
        rcpt=mail.recip[0]
        if self.MATCHED[rcpt]:
          # hide virus name
          return 1.0,'',[self.MATCHED[rcpt]]
      except IndexError:
        pass
      return 0.0,'',[]

class rcpt_in_txt(rcpt_in_sql):
  '''
  Retun virus if recipient matches against an text file row.
  
  This scanner is useful as an condition for other scanners. You can use
  an plain text file to assingn required return value. Each row in this
  file defines an regular expression searched against recipient's email
  address. Priority is not required, because it is defined by row.
  
  Usage: rcpt_in_txt(filename, flags=re.IGNORECASE)
  
  Where: filename is configuration full filename
         flags are flags for regular expression. By default ignore case.
  
  Example: rcpt_in_txt('/var/lib/sagator/antispam.conf')
             & spamassassind()
  
  New in version 0.8.0.
  '''
  name='rcpt_in_txt()'
  def __init__(self,filename,flags=re.I):
      self.FILENAME=filename
      self.STAT=0
      self.FLAGS=flags
  def reinit(self):
      self.MATCHED={}
      fn=safe.fn(self.FILENAME)
      # reload file if it has been changed
      if os.stat(fn).st_mtime!=self.STAT:
        self.STAT=os.stat(fn).st_mtime
        self.DATA=[
          (line.rstrip(),re.compile(line.rstrip(),self.FLAGS))
            for line in open(fn,'rt').readlines()
        ]
        debug.echo(3,'%s: reloaded %d lines from %s' \
                     % (self.name,len(self.DATA),self.FILENAME))
  def rcpt_signature(self,rcpt):
      for line,reg in self.DATA:
        if reg.search(rcpt):
          self.MATCHED[rcpt]=line
          globals.RCPT_MATCH[self.FILENAME]=line
          debug.echo(4,'%s: %s matched for %s [%s]' \
                       % (self.name,self.FILENAME,rcpt,line))
          return self.FILENAME
      self.MATCHED[rcpt]=''
      return ''

class rcpt_no_match(ascanner):
  '''
  Return virus is no rcpt_in_... match before.
  
  When no one rcpt_in_sql() or rcpt_in_txt() scanner matches, this scanner
  return's a match. It is useful when you need to use default scanner,
  when no one is defined in configuration.
  
  Usage: rcpt_no_match(key)
  
  Where: key is a scanner name defined in SQL table
  
  Example: rcpt_no_match('AntiSpam') & spamassassind()
  
  New in version 0.8.0.
  '''
  name='rcpt_no_match()'
  def __init__(self,key):
      self.KEY=key
      self.MATCHED=False
  def rcpt_signature(self,rcpt):
      if globals.RCPT_MATCH.has_key(self.KEY):
        self.MATCHED=True
        return self.KEY
      else:
        debug.echo(4,'%s: %s not mathed before' % (self.name,rcpt))
        self.MATCHED=False
        return ''
  def scanbuffer(self,buffer,args={}):
      if self.MATCHED:
        return 0.0,'',[]
      else:
        # hide virus name
        return 1.0,'',['%s not matched' % self.KEY]
