Source

iredapd / iredapd.py

Zhang Huangbin e85694f 

Zhang Huangbin 3a6d722 
Zhang Huangbin e85694f 






Zhang Huangbin 4ff6184 
Zhang Huangbin c39c38a 
Zhang Huangbin 4ff6184 
Zhang Huangbin e85694f 
Zhang Huangbin c39c38a 
Zhang Huangbin 6962a9a 
Zhang Huangbin c39c38a 
Zhang Huangbin 6962a9a 
Zhang Huangbin e85694f 


Zhang Huangbin 3a6d722 
Zhang Huangbin e85694f 
Zhang Huangbin c39c38a 
Zhang Huangbin e32d93b 
Zhang Huangbin 3cd5f78 



Zhang Huangbin 4ff6184 
Zhang Huangbin e85694f 

Zhang Huangbin 6a6022a 
Zhang Huangbin e85694f 
Zhang Huangbin 3cd5f78 



Zhang Huangbin e85694f 








Zhang Huangbin e32d93b 
Zhang Huangbin e85694f 

Zhang Huangbin e32d93b 

Zhang Huangbin 6a6022a 

Zhang Huangbin e85694f 
Zhang Huangbin c39c38a 
Zhang Huangbin e32d93b 





Zhang Huangbin e85694f 





Zhang Huangbin 3cd5f78 
Zhang Huangbin e85694f 

Zhang Huangbin 6a6022a 


Zhang Huangbin 4ff6184 
Zhang Huangbin e85694f 




Zhang Huangbin 8a60712 
Zhang Huangbin e85694f 
Zhang Huangbin 3cd5f78 
Zhang Huangbin e85694f 



Zhang Huangbin c39c38a 
Zhang Huangbin e32d93b 
Zhang Huangbin bd05693 
Zhang Huangbin e85694f 





Zhang Huangbin 56e4d63 
Zhang Huangbin 3cd5f78 





Zhang Huangbin e32d93b 
Zhang Huangbin 3cd5f78 


Zhang Huangbin 8a60712 







Zhang Huangbin e85694f 


Zhang Huangbin 3cd5f78 
Zhang Huangbin 8a60712 
Zhang Huangbin e32d93b 
Zhang Huangbin 2cfe01e 




Zhang Huangbin e85694f 



Zhang Huangbin 3a6d722 
Zhang Huangbin e85694f 

Zhang Huangbin c39c38a 
Zhang Huangbin e85694f 

Zhang Huangbin ebaf5b2 



Zhang Huangbin e85694f 

Zhang Huangbin ebaf5b2 
Zhang Huangbin e85694f 

Zhang Huangbin ebaf5b2 
Zhang Huangbin e85694f 

Zhang Huangbin c39c38a 
Zhang Huangbin e85694f 


Zhang Huangbin c39c38a 
Zhang Huangbin 3a6d722 
Zhang Huangbin e85694f 


Zhang Huangbin 3a6d722 
Zhang Huangbin e85694f 






# Author: Zhang Huangbin <zhb _at_ iredmail.org>

import os
import sys
import pwd
import socket
import asyncore
import asynchat
import logging

# iRedAPD setting file and modules
import settings
from libs import __version__, SMTP_ACTIONS, daemon

if settings.backend == 'ldap':
    from libs.ldaplib.modeler import Modeler
elif settings.backend in ['mysql', 'pgsql']:
    from libs.sql.modeler import Modeler
else:
    sys.exit('Invalid backend, it must be ldap, mysql or pgsql.')

sys.path.append(os.path.abspath(os.path.dirname(__file__)) + '/plugins')

class PolicyChannel(asynchat.async_chat):
    """Process each smtp policy request"""
    def __init__(self,
                 conn,
                 plugins=[],
                 sender_search_attrlist=None,
                 recipient_search_attrlist=None):
        asynchat.async_chat.__init__(self, conn)
        self.buffer = []
        self.smtp_session_data = {}
        self.set_terminator('\n')

        self.plugins = plugins
        self.sender_search_attrlist = sender_search_attrlist
        self.recipient_search_attrlist = recipient_search_attrlist

    def push(self, msg):
        asynchat.async_chat.push(self, msg + '\n')

    def collect_incoming_data(self, data):
        self.buffer.append(data)

    def found_terminator(self):
        if self.buffer:
            # Format received data
            line = self.buffer.pop()
            logging.debug("smtp session: " + line)
            if '=' in line:
                (key, value) = line.split('=', 1)
                self.smtp_session_data[key] = value
        elif len(self.smtp_session_data) != 0:
            try:
                modeler = Modeler()
                result = modeler.handle_data(
                    smtp_session_data=self.smtp_session_data,
                    plugins=self.plugins,
                    sender_search_attrlist=self.sender_search_attrlist,
                    recipient_search_attrlist=self.recipient_search_attrlist,
                )
                if result:
                    action = result
                else:
                    action = SMTP_ACTIONS['default']
            except Exception, e:
                action = SMTP_ACTIONS['default']
                logging.debug('Unexpected error: %s. Fallback to default action: %s' % (str(e), str(action)))

            # Log final action.
            logging.info('[%s] %s -> %s, %s' % (self.smtp_session_data['client_address'],
                                                self.smtp_session_data['sender'],
                                                self.smtp_session_data['recipient'],
                                                action))

            self.push('action=' + action + '\n')
            asynchat.async_chat.handle_close(self)
            logging.debug("Connection closed")
        else:
            action = SMTP_ACTIONS['default']
            logging.debug("replying: " + action)
            self.push('action=' + action + '\n')
            asynchat.async_chat.handle_close(self)
            logging.debug("Connection closed")


class DaemonSocket(asyncore.dispatcher):
    """Create socket daemon"""
    def __init__(self, localaddr):
        asyncore.dispatcher.__init__(self)
        self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
        self.set_reuse_addr()
        self.bind(localaddr)
        self.listen(5)
        ip, port = localaddr
        logging.info("Starting iRedAPD (version: %s, backend: %s), listening on %s:%d." % (__version__, settings.backend, ip, port))

        # Load plugins.
        self.loaded_plugins = []
        for plugin in settings.plugins:
            try:
                self.loaded_plugins.append(__import__(plugin))
                logging.info('Loading plugin: %s' % plugin)
            except Exception, e:
                logging.error('Error while loading plugin (%s): %s' % (plugin, str(e)))

        self.sender_search_attrlist = []
        self.recipient_search_attrlist = []
        if settings.backend == 'ldap':
            self.sender_search_attrlist = ['objectClass']
            self.recipient_search_attrlist = ['objectClass']
            for plugin in self.loaded_plugins:
                self.sender_search_attrlist += plugin.SENDER_SEARCH_ATTRLIST
                self.recipient_search_attrlist += plugin.RECIPIENT_SEARCH_ATTRLIST

    def handle_accept(self):
        conn, remote_addr = self.accept()
        logging.debug("Connect from %s, port %s." % remote_addr)

        PolicyChannel(
            conn,
            plugins=self.loaded_plugins,
            sender_search_attrlist=self.sender_search_attrlist,
            recipient_search_attrlist=self.recipient_search_attrlist,
        )


def main():
    # Set umask.
    os.umask(0077)

    # Get log level.
    log_level = getattr(logging, str(settings.log_level).upper())

    # Initialize file based logger.
    logging.basicConfig(level=log_level,
                        format='%(asctime)s %(levelname)s %(message)s',
                        datefmt='%Y-%m-%d %H:%M:%S',
                        filename=settings.log_file)

    # Initialize policy daemon.
    DaemonSocket((settings.listen_address, int(settings.listen_port)))

    # Run this program as daemon.
    daemon.daemonize()

    # Run as a low privileged user.
    uid = pwd.getpwnam(settings.run_as_user)[2]

    try:
        # Write pid number into pid file.
        f = open(settings.pid_file, 'w')
        f.write(str(os.getpid()))
        f.close()

        # Set uid.
        os.setuid(uid)

        # Starting loop.
        asyncore.loop()
    except KeyboardInterrupt:
        pass

if __name__ == '__main__':
    main()