'''
filters.py - some filters which you can use in procmail...

(c) 2003-2006,2019 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 aglib import *
import sys

__all__ = ['sgfilterd']

class sgfilterd(service):
  '''
  A service to filter data sent by sgfilter command.

  This service can be used to filter an email through sagator.
  Some headers should be added to filtered email. A client for this
  service is the sgfilter script. See man sgfilter for more information.
  
  Usage: sgfilterd(scanners,host='127.0.0.1',port=27,prefork=2)
  
  Where: scanners is an array of scanners (see README.scanners for more info)
         host is a hostname to bind
         port is a tcp port to bind
         prefork is a number, which defines preforked process count.
           Set this parameter to actual processor count + 1
           or leave it's default (2).
  
  Example: sgfilterd(SCANNERS)
  
  Input protocol description:
    MAIL FROM: sender_email\r
    RCPT TO: recipient_email\r
    DATA length\r
    ...

  Where: sender_email is sender's email address
         recipient_email is recipient's email address. You can send more
           RCPT TO: lines.
         length is whole data length in bytes (including control characters,
           as newlines, ...)
  
  Output protocol description:
    XXX L.LL VIRNAME
    ...
    ^D
    
  Where: XXX is three digit status, one from these:
             250 - clean
             251 - not clean, but sending forced
             451 - an internal error occured during scanning
             550 - reject
             551 - drop
         L.LL is an floating number of virus/spam level status
         VIRNAME is an short description (like virus name, 'SPAM' string
             or other one line short description
         ... is modified email message (if some scanners are defined
             to modify scanned message)
         ^D is an EOF character, after message the communication is closed
  
  New in version 0.7.0.
  '''
  name='sgfilterd()'
  reg_data=re.compile('^DATA ([0-9]+)\r?$',re.I)
  def __init__(self,scanners,host='127.0.0.1',port=27,prefork=2):
      self.SCANNERS=scanners
      self.BINDTO=(host,port)
      self.LISTEN=prefork*3
      self.MIN_CHILDS=prefork
      self.EXITING=False
  def accept(self,connects=0):
      conn,addr=self.s.accept()
      socket_settimeout(conn,120)
      # reinit scanners
      for scnr in self.SCANNERS:
        scnr.reinit()
      globals.reset()
      mail.__init__()
      self.time2=time.strftime("%Y%m%d-%H%M%S",time.localtime(time.time()))
      if self.time2!=self.time1:
        self.time1,self.timec=self.time2,1
      globals.gen_id(self.time2,self.timec)
      debug.echo(1,"Connection from: "+addr[0]+" at "+\
        time.strftime("%c",time.localtime()))
      debug.echo(1,"Process id: %s" % globals.id)
      self.f=conn.makefile('rw')
      while True:
        line=self.f.readline()
        if not line:
          break
        debug.echo(5,line.rstrip())
        rcptto=smtp.reg_rcptto.search(line)
        if rcptto:
          try:
            rcpt_email=parseaddr(tostr(rcptto.group(1)))[1]
            mail.recip.append(rcpt_email)
          except:
            rcpt_email=rcptto.group(1)
          continue
        mailfrom=smtp.reg_mailfrom.search(line)
        if mailfrom:
          try:
            mail.sender=parseaddr(tostr(mailfrom.group(1)))[1]
          except:
            mail.sender=mailfrom.group(1)
        data=self.reg_data.search(line)
        if data:
          count=int(data.group(1))
          mail.df.write(self.f.read(count))
          mail.close()
          debug.echo(3,"%s: BODY DONE, size: %d" % (self.name,len(mail.data)))
          v,level,virname=checkvir(self.SCANNERS)
          if v==S_OK:
            conn.sendall('250 %f CLEAN\r\n' % level)
          elif v==S_FORCE_SEND:
            conn.sendall('251 %f CLEAN\r\n' % level)
          elif v==S_REJECT:
            conn.sendall('550 %f %s\r\n' % (level,virname))
          elif v==S_DROP:
            conn.sendall('551 %f %s\r\n' % (level,virname))
          else: # S_TEMPFAIL
            conn.sendall('451 %f %s\r\n' % (level,virname))
            break
          conn.sendall(mail.xheader)
          conn.sendall(mail.data)
          conn.sendall('\r\n')
          break
      conn.shutdown(socket.SHUT_RDWR)
      debug.echo(1,"Closing connection.")
      conn.close()
