'''
policy.py - SMTP policy service for SAGATOR.

(c) 2005-2007 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 stats

__all__=['smtpd_policy','recipient_policy']

class smtpd_policy(ServiceTCPServer):
  '''
  SMTP policy service.
  
  This service can be used as smtpd policy service for postfix.
  
  Usage: smtpd_polixy(scanners, dbc, host, port)
  
  Where: scanners is an array of policy scanners
           (see README.scanners for more info)
         dbc is an database connection
         host is a an ip address to bind
         port is a port to bind
  
  Example: smtpd_policy(SCANNERS, db.sqlite(), '127.0.0.1', 29)
  
  Postfix configuration example:
    /etc/postfix/main.cf:
      smtpd_recipient_restrictions=
              ...
              check_policy_service inet:127.0.0.1:29
              ...

  New in version 0.8.0.
  '''
  name='smtpd_policy()'
  def __init__(self,scanners,dbc,host,port):
      globals.DBC=dbc
      ServiceTCPServer.__init__(self,scanners,host,port,smtpd_policy_handler)

class smtpd_policy_handler(StreamRequestHandler):
  time1=None
  def handle(self):
      while True:
        self.stats=stats.statistics()
        # generate ID
        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)
        mail.policy_request={}
        mail.data=''
        try:
          while True:
            line=self.rfile.readline()
            if not line:
              break
            debug.echo(8,line)
            mail.data+=line
            if line=='\n' or line=='\r\n':
              break
            try:
              key,value=line.rstrip('\r\n').split('=',1)
              mail.policy_request[key]=value
            except ValueError:
              debug.echo(3,"%s: wrong line: '%s'" \
                           % (self.server.name,line.rstrip()))
          if not mail.data:
            break
          debug.echo(4,"%s: %s %s" \
                     % (self.server.name,
                        time.strftime("%c",time.localtime()),
                        mail.policy_request))
          self.wfile.write('action=%s\n\n'
                           % checkpolicy(self.server.SCANNERS,False))
          self.stats.policy_update()
        except socket.error, (ec,es):
          # ignore exception if connection is already down
          if ec==107: # Transport endpoint is not connected
            debug.echo(3, "%s: Transport endpoint is not connected"
                          % self.server.name)
            return
          elif ec==104: # Connection reset by peer
            debug.echo(3, "%s: Connection reset by peer"
                          % self.server.name)
            return
          else:
            debug.traceback(3, "%s: " % self.server.name)
            self.wfile.write('action=warn %s\n\n' % es)
        except Exception, err:
          debug.traceback(3, "%s: Unknown exception" % self.server.name)
          self.wfile.write('action=warn %s\n\n' % err)

class recipient_policy(service):
  '''
  Virtual recipient policy.
  
  This policy check is invoked after an "RCPT TO:" smtp command is received.
  You can use an policy scanner combination as scanner. It is useable for
  postfix's before-queue policy filter or an policy filter for milter.
  This service must be defined before service, which want to use it.
  
  Usage: recipient_policy(scanners,dbc)
  
  Where: scanners is an array of policy scanners
           (see README.scanners for more info)
         dbc is an database connection

  Example: recipient_policy(POLICY_SCANNERS, db.sqlite())

  New in version 0.8.0.
  '''
  def __init__(self,scanners,dbc):
      globals.DBC=dbc
      globals.recipient_policy=scanners
  def start(self):
      return []
