Zhang Huangbin avatar Zhang Huangbin committed 3cd5f78

* Use python file as config file instead of .ini.
* Merge iredapd.py and iredapd-rr.py.
* Query required LDAP attributes instead of all.

Comments (0)

Files changed (27)

 iRedAPD-1.3.9:
+    * Plugin renamed:
+      block_amavisd_blacklisted_senders -> amavisd_block_blacklisted_senders
     * New plugin for SQL backends: sql_user_restrictions.
       Note, it requires 4 new columns on table `vmail.mailbox`, please refer
       to iRedMail upgrade tutorials.
+
+# About
+
+* iRedAPD is a simple Postfix policy server.
+* iRedAPD is a part of iRedMail project. http://www.iredmail.org/
+
+Available plugins:
+
+* `ldap_maillist_access_policy`: Check user's rights to post messages to mail list which builtin in iRedMail OpenLDAP solution.
+* `amavisd_block_blacklisted_senders`: Used for per-user sender whitelist and blacklist.
+* `ldap_recipient_restrictions`: Used for per-user recipient whitelist and blacklist.
+
+# Authors & Contributors
+
+* Zhang Huangbin <zhb @ iredmail.org>: Core developer and maintainer.
+
+# License
+
+iRedAPD is based on [mlapd](http://code.google.com/p/mlapd) which released
+under GPL v2, so iRedAPD is GPL v2 too.
+
+Note: file src/daemon.py is released under its own license, shipped
+for easy deploying.
+
+# Requirments
+
+* [Python](http://www.python.org/) >= 2.4
+* [python-ldap](http://python-ldap.org/) >= 2.2.0. Required for OpenLDAP backend
+* [python-mysql](http://mysql-python.sourceforge.net/) >= 1.2.0. Required for MySQL backend.
+* [python-psycopg2](http://initd.org/) >= 2.1.0. Required for PostgreSQL backend.
+
+# Document
+
+* [Install iRedAPD for OpenLDAP backend](http://www.iredmail.org/wiki/index.php?title=Install/iRedAPD/OpenLDAP)
+* [Install iRedAPD for MySQL backend](http://www.iredmail.org/wiki/index.php?title=Install/iRedAPD/MySQL)
+
+* [iRedMail documentations](http://www.iredmail.org/doc.html)
+* [Postfix SMTP Access Policy Delegation](http://www.postfix.org/SMTPD_POLICY_README.html)

README.md

-
-# About
-
-* iRedAPD is a simple Postfix policy server.
-* iRedAPD is a part of iRedMail project. http://www.iredmail.org/
-
-Available plugins:
-
-* `ldap_maillist_access_policy`: Check user's rights to post messages to mail list which builtin in iRedMail OpenLDAP solution.
-* `block_amavisd_blacklisted_senders`: Used for per-user sender whitelist and blacklist.
-* `ldap_recipient_restrictions`: Used for per-user recipient whitelist and blacklist.
-
-# Authors & Contributors
-
-* Zhang Huangbin <zhb @ iredmail.org>: Core developer and maintainer.
-
-# License
-
-iRedAPD is based on [mlapd](http://code.google.com/p/mlapd) which released
-under GPL v2, so iRedAPD is GPL v2 too.
-
-Note: file src/daemon.py is released under its own license, shipped
-for easy deploying.
-
-# Requirments
-
-* [Python](http://www.python.org/) >= 2.4
-* [python-ldap](http://python-ldap.org/) >= 2.2.0. Required for OpenLDAP backend
-* [python-mysql](http://mysql-python.sourceforge.net/) >= 1.2.0. Required for MySQL backend.
-* [python-psycopg2](http://initd.org/) >= 2.1.0. Required for PostgreSQL backend.
-
-# Document
-
-* [Install iRedAPD for OpenLDAP backend](http://www.iredmail.org/wiki/index.php?title=Install/iRedAPD/OpenLDAP)
-* [Install iRedAPD for MySQL backend](http://www.iredmail.org/wiki/index.php?title=Install/iRedAPD/MySQL)
-
-* [iRedMail documentations](http://www.iredmail.org/doc.html)
-* [Postfix SMTP Access Policy Delegation](http://www.postfix.org/SMTPD_POLICY_README.html)
-* Use python file as config file instead of .ini
-* Merge iredapd.py and iredapd-rr.py
-* Query required LDAP attributes instead of all
 * Query required SQL columns instead of all
 * Plugins:
     + Greylisting (server-wide and per-user, per-domain)
     + White/Blacklisting (server-wide and per-domain)
+* Detect protocol_state=RCPT in smtp session

iredapd-rr.py

-# Author: Zhang Huangbin <zhb _at_ iredmail.org>
-
-import os
-import os.path
-import sys
-import pwd
-import ConfigParser
-import socket
-import asyncore
-import asynchat
-import logging
-
-# Append plugin directory.
-sys.path.append(os.path.abspath(os.path.dirname(__file__)) + '/plugins-rr')
-
-# Get config file.
-if len(sys.argv) != 2:
-    sys.exit('Usage: %s /path/to/iredapd-rr.ini')
-else:
-    config_file = sys.argv[1]
-
-    # Check file exists.
-    if not os.path.exists(config_file):
-        sys.exit('File not exist: %s.' % config_file)
-
-# Read configurations.
-cfg = ConfigParser.SafeConfigParser()
-cfg.read(config_file)
-
-from libs import __version__, SMTP_ACTIONS, daemon
-
-class apd_channel(asynchat.async_chat):
-    def __init__(self, conn, remote_addr):
-        asynchat.async_chat.__init__(self, conn)
-        self.remote_addr = remote_addr
-        self.buffer = []
-        self.smtp_attrs = {}
-        self.set_terminator('\n')
-        logging.debug("Connect from %s, port %s." % self.remote_addr)
-
-    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 len(self.buffer) is not 0:
-            line = self.buffer.pop()
-            logging.debug("smtp session: " + line)
-            if line.find('=') != -1:
-                key = line.split('=')[0]
-                value = line.split('=', 1)[1]
-                self.smtp_attrs[key] = value
-        elif len(self.smtp_attrs) != 0:
-            try:
-                if cfg.get('general', 'backend', 'ldap') == 'ldap':
-                    modeler = LDAPModeler()
-                else:
-                    modeler = SQLModeler()
-
-                result = modeler.handle_data(self.smtp_attrs)
-                if result:
-                    action = result
-                else:
-                    action = SMTP_ACTIONS['default']
-                logging.debug("Final action: %s." % str(result))
-            except Exception, e:
-                action = SMTP_ACTIONS['default']
-                logging.debug('Error: %s. Use default action instead: %s' %
-                        (str(e), str(action)))
-
-            # Log final action.
-            logging.info('[%s] %s -> %s, %s' % (self.smtp_attrs['client_address'],
-                                                self.smtp_attrs['sender'],
-                                                self.smtp_attrs['recipient'],
-                                                action,
-                                               ))
-
-            self.push('action=' + action + '\n')
-            asynchat.async_chat.handle_close(self)
-            logging.debug("Connection closed")
-        else:
-            action = SMTP_ACTIONS['defer']
-            logging.debug("replying: " + action)
-            self.push(action + '\n')
-            asynchat.async_chat.handle_close(self)
-            logging.debug("Connection closed")
-
-
-class apd_socket(asyncore.dispatcher):
-    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 (v%s, pid: %d), listening on %s:%s." %
-                (__version__, os.getpid(), ip, str(port)))
-
-    def handle_accept(self):
-        conn, remote_addr = self.accept()
-        channel = apd_channel(conn, remote_addr)
-
-
-class SQLModeler:
-    def __init__(self):
-        import web
-
-        # Turn off debug mode.
-        web.config.debug = False
-
-        self.db = web.database(
-            dbn='mysql',
-            host=cfg.get('sql', 'server', 'localhost'),
-            db=cfg.get('sql', 'db', 'vmail'),
-            user=cfg.get('sql', 'user', 'vmail'),
-            pw=cfg.get('sql', 'password'),
-        )
-
-    def handle_data(self, map):
-        if 'sender' in map.keys() and 'recipient' in map.keys():
-            # Get plugin module name and convert plugin list to python list type.
-            self.plugins = cfg.get('sql', 'plugins', '')
-            self.plugins = [v.strip() for v in self.plugins.split(',')]
-
-            # Get sender, recipient.
-            # Sender/recipient are used almost in all plugins, so store them
-            # a dict and pass to plugins.
-            senderReceiver = {
-                'sender': map['sender'],
-                'recipient': map['recipient'],
-                'sender_domain': map['sender'].split('@')[-1],
-                'recipient_domain': map['recipient'].split('@')[-1],
-            }
-
-            if len(self.plugins) > 0:
-                #
-                # Import plugin modules.
-                #
-                self.modules = []
-
-                # Load plugin module.
-                for plugin in self.plugins:
-                    try:
-                        self.modules.append(__import__(plugin))
-                    except ImportError:
-                        # Print error message if plugin module doesn't exist.
-                        # Use logging.info to let admin know this critical error.
-                        logging.info('Error: plugin %s.py not exist.' % plugin)
-                    except Exception, e:
-                        logging.debug('Error while importing plugin module (%s): %s' % (plugin, str(e)))
-
-                #
-                # Apply plugins.
-                #
-                for module in self.modules:
-                    try:
-                        logging.debug('Apply plugin (%s).' % (module.__name__, ))
-                        pluginAction = module.restriction(
-                            dbConn=self.db,
-                            senderReceiver=senderReceiver,
-                            smtpSessionData=map,
-                        )
-
-                        logging.debug('Response from plugin (%s): %s' % (module.__name__, pluginAction))
-                        if not pluginAction.startswith('DUNNO'):
-                            logging.info('Response from plugin (%s): %s' % (module.__name__, pluginAction))
-                            return pluginAction
-
-                        return 'DUNNO'
-                    except Exception, e:
-                        logging.debug('Error while apply plugin (%s): %s' % (module, str(e)))
-
-            else:
-                # No plugins available.
-                return 'DUNNO'
-        else:
-            return SMTP_ACTIONS['defer']
-
-
-
-class LDAPModeler:
-    def __init__(self):
-        import ldap
-
-        self.ldap = ldap
-
-        # Read LDAP server settings.
-        self.uri = cfg.get('ldap', 'uri', 'ldap://127.0.0.1:389')
-        self.binddn = cfg.get('ldap', 'binddn')
-        self.bindpw = cfg.get('ldap', 'bindpw')
-        self.baseDN = cfg.get('ldap', 'basedn')
-
-        # Initialize ldap connection.
-        try:
-            self.conn = self.ldap.initialize(self.uri)
-            logging.debug('LDAP connection initialied success.')
-        except Exception, e:
-            logging.error('LDAP initialized failed: %s.' % str(e))
-            sys.exit()
-
-        # Bind to ldap server.
-        if self.binddn != '' and self.bindpw != '':
-            try:
-                self.conn.bind_s(self.binddn, self.bindpw)
-                logging.debug('LDAP bind success.')
-            except self.ldap.INVALID_CREDENTIALS:
-                logging.error('LDAP bind failed: incorrect bind dn or password.')
-                sys.exit()
-            except Exception, e:
-                logging.error('LDAP bind failed: %s.' % str(e))
-                sys.exit()
-
-    def __get_sender_dn_ldif(self, sender):
-        logging.debug('__get_sender_dn_ldif (sender): %s' % sender)
-
-        if len(sender) < 6 or sender is None:
-            logging.debug('__get_sender_dn_ldif: Sender is not a valid email address.')
-            return (None, None)
-
-        try:
-            logging.debug('__get_sender_dn_ldif: Quering LDAP')
-            result = self.conn.search_s(
-                    self.baseDN,
-                    self.ldap.SCOPE_SUBTREE,
-                    '(&(|(mail=%s)(shadowAddress=%s))(|(objectClass=mailUser)(objectClass=mailList)(objectClass=mailAlias)))' % (sender, sender),
-                    )
-            logging.debug('__get_sender_dn_ldif (result): %s' % str(result))
-            if len(result) == 0:
-                return (None, None)
-            else:
-                return (result[0][0], result[0][1])
-        except Exception, e:
-            logging.debug('!!! ERROR !!! __get_sender_dn_ldif (result): %s' % str(e))
-            return (None, None)
-
-    def handle_data(self, map):
-        if 'sender' in map.keys() and 'recipient' in map.keys():
-            # Get plugin module name and convert plugin list to python list type.
-            self.plugins = cfg.get('ldap', 'plugins', '')
-            self.plugins = [v.strip() for v in self.plugins.split(',')]
-
-            if len(self.plugins) > 0:
-
-                # Get account dn and LDIF data.
-                bypass_mynetworks = cfg.get('general', 'bypass_mynetworks', 'no')
-                if bypass_mynetworks == 'yes':
-                    sender = map['sasl_username']
-                else:
-                    sender = map['sender']
-
-                senderDN, senderLdif = self.__get_sender_dn_ldif(sender)
-
-                # Return if recipient account doesn't exist.
-                if senderDN is None or senderLdif is None:
-                    logging.debug('Sender DN or LDIF is none.')
-                    return SMTP_ACTIONS['default']
-
-                #
-                # Import plugin modules.
-                #
-                self.modules = []
-
-                # Load plugin module.
-                for plugin in self.plugins:
-                    try:
-                        self.modules.append(__import__(plugin))
-                    except ImportError:
-                        # Print error message if plugin module doesn't exist.
-                        # Use logging.info to let admin know this critical error.
-                        logging.info('Error: plugin %s.py not exist.' % plugin)
-                    except Exception, e:
-                        logging.debug('Error while importing plugin module (%s): %s' % (plugin, str(e)))
-
-                #
-                # Apply plugins.
-                #
-                for module in self.modules:
-                    try:
-                        logging.debug('Apply plugin (%s).' % (module.__name__, ))
-                        pluginAction = module.restriction(
-                            ldapConn=self.conn,
-                            ldapBaseDn=self.baseDN,
-                            ldapSenderDn=senderDN,
-                            ldapSenderLdif=senderLdif,
-                            smtpSessionData=map,
-                        )
-
-                        logging.debug('Response from plugin (%s): %s' % (module.__name__, pluginAction))
-                        if not pluginAction.startswith('DUNNO'):
-                            logging.info('Response from plugin (%s): %s' % (module.__name__, pluginAction))
-                            return pluginAction
-
-                        return 'DUNNO'
-                    except Exception, e:
-                        logging.debug('Error while apply plugin (%s): %s' % (module, str(e)))
-
-            else:
-                # No plugins available.
-                return 'DUNNO'
-        else:
-            return SMTP_ACTIONS['defer']
-
-
-def main():
-    # Set umask.
-    os.umask(0077)
-
-    # Get listen address/port.
-    listen_addr = cfg.get('general', 'listen_addr', '127.0.0.1')
-    listen_port = int(cfg.get('general', 'listen_port', '7777'))
-
-    run_as_daemon = cfg.get('general', 'run_as_daemon', 'yes')
-
-    # Get log level.
-    log_level = getattr(logging, cfg.get('general', 'log_level', 'info').upper())
-
-    # Initialize file based logger.
-    if cfg.get('general', 'log_type', 'file') == 'file':
-        if run_as_daemon == 'yes':
-            logging.basicConfig(
-                    level=log_level,
-                    format='%(asctime)s %(levelname)s %(message)s',
-                    datefmt='%Y-%m-%d %H:%M:%S',
-                    filename=cfg.get('general', 'log_file', '/var/log/iredapd.log'),
-                    )
-        else:
-            logging.basicConfig(
-                    level=log_level,
-                    format='%(asctime)s %(levelname)s %(message)s',
-                    datefmt='%Y-%m-%d %H:%M:%S',
-                    )
-
-    # Initialize policy daemon.
-    socket_daemon = apd_socket((listen_addr, listen_port))
-
-    # Run this program as daemon.
-    if run_as_daemon == 'yes':
-        daemon.daemonize()
-
-    # Run as a low privileged user.
-    run_as_user = cfg.get('general', 'run_as_user', 'nobody')
-    uid = pwd.getpwnam(run_as_user)[2]
-
-    try:
-        # Write pid number into pid file.
-        f = open(cfg.get('general', 'pid_file', '/var/run/iredapd.pid'), 'w')
-        f.write(str(os.getpid()))
-        f.close()
-
-        # Set uid.
-        os.setuid(uid)
-
-        # Starting loop.
-        asyncore.loop()
-    except KeyboardInterrupt:
-        pass
-
-if __name__ == '__main__':
-    main()
 
 
 class PolicyChannel(asynchat.async_chat):
-    def __init__(self, conn, remote_addr):
+    def __init__(self,
+                 conn,
+                 plugins=[],
+                 plugins_for_sender=[],
+                 plugins_for_recipient=[],
+                 plugins_for_misc=[],
+                 sender_search_attrlist=None,
+                 recipient_search_attrlist=None,
+                ):
         asynchat.async_chat.__init__(self, conn)
-        self.remote_addr = remote_addr
         self.buffer = []
-        self.smtp_attrs = {}
+        self.smtp_session_map = {}
         self.set_terminator('\n')
-        logging.debug("Connect from %s, port %s." % self.remote_addr)
+
+        self.plugins = plugins
+        self.plugins_for_sender = plugins_for_sender
+        self.plugins_for_recipient = plugins_for_recipient
+        self.plugins_for_misc = plugins_for_misc
+        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')
             if line.find('=') != -1:
                 key = line.split('=')[0]
                 value = line.split('=', 1)[1]
-                self.smtp_attrs[key] = value
-        elif len(self.smtp_attrs) != 0:
+                self.smtp_session_map[key] = value
+        elif len(self.smtp_session_map) != 0:
             try:
                 modeler = Modeler()
-
-                result = modeler.handle_data(self.smtp_attrs)
+                result = modeler.handle_data(smtp_session_map=self.smtp_session_map,
+                                             plugins=self.plugins,
+                                             plugins_for_sender=self.plugins_for_sender,
+                                             plugins_for_recipient=self.plugins_for_recipient,
+                                             plugins_for_misc=self.plugins_for_misc,
+                                             sender_search_attrlist=self.sender_search_attrlist,
+                                             recipient_search_attrlist=self.recipient_search_attrlist,
+                                            )
                 if result:
                     action = result
                 else:
                     action = SMTP_ACTIONS['default']
-                logging.debug("Final action: %s." % str(result))
             except Exception, e:
                 action = SMTP_ACTIONS['default']
-                logging.debug('Error: %s. Use default action instead: %s' %
-                        (str(e), str(action)))
+                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_attrs['client_address'],
-                                                self.smtp_attrs['sender'],
-                                                self.smtp_attrs['recipient'],
+            logging.info('[%s] %s -> %s, %s' % (self.smtp_session_map['client_address'],
+                                                self.smtp_session_map['sender'],
+                                                self.smtp_session_map['recipient'],
                                                 action,
                                                ))
 
         else:
             action = SMTP_ACTIONS['defer']
             logging.debug("replying: " + action)
-            self.push(action + '\n')
+            self.push('action=' + action + '\n')
             asynchat.async_chat.handle_close(self)
             logging.debug("Connection closed")
 
 
 class DaemonSocket(asyncore.dispatcher):
-    def __init__(self, localaddr):
+    def __init__(self, localaddr, plugins=[]):
         asyncore.dispatcher.__init__(self)
         self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
         self.set_reuse_addr()
         self.listen(5)
         ip, port = localaddr
         logging.info("Starting iRedAPD (version %s, %s backend), listening on %s:%d." % (__version__, settings.backend, ip, port))
-        logging.info("Enabled plugin(s): %s." % (', '.join(settings.plugins)))
+
+        # 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.plugins_for_sender = [plugin
+                                   for plugin in self.loaded_plugins
+                                   if plugin.REQUIRE_LOCAL_SENDER]
+
+        self.plugins_for_recipient = [plugin
+                                   for plugin in self.loaded_plugins
+                                   if plugin.REQUIRE_LOCAL_RECIPIENT]
+
+        self.plugins_for_misc = [plugin for plugin in self.loaded_plugins
+                                 if plugin not in self.plugins_for_sender
+                                 and plugin not in self.plugins_for_recipient]
+
+        self.sender_search_attrlist = ['objectClass']
+        for plugin in self.plugins_for_sender:
+            self.sender_search_attrlist += plugin.SENDER_SEARCH_ATTRLIST
+
+        self.recipient_search_attrlist = ['objectClass']
+        for plugin in self.plugins_for_recipient:
+            self.recipient_search_attrlist += plugin.RECIPIENT_SEARCH_ATTRLIST
 
     def handle_accept(self):
         conn, remote_addr = self.accept()
-        channel = PolicyChannel(conn, remote_addr)
+        logging.debug("Connect from %s, port %s." % remote_addr)
+        channel = PolicyChannel(conn,
+                                plugins=self.loaded_plugins,
+                                plugins_for_sender=self.plugins_for_sender,
+                                plugins_for_recipient=self.plugins_for_recipient,
+                                plugins_for_misc=self.plugins_for_misc,
+                                sender_search_attrlist=self.sender_search_attrlist,
+                                recipient_search_attrlist=self.recipient_search_attrlist,
+                               )
 
 
 def main():
                 'reject': 'REJECT Not authorized',
                 'default': 'DUNNO',
                }
+
+ACCESS_POLICIES_OF_MAIL_LIST = {
+    'public': 'Unrestricted',
+    'domain': 'Only users under same domain are allowed',
+    'subdomain': 'Only users under same domain and sub domains are allowed',
+    'membersonly': 'Only members are allowed',
+    'members': 'Only members are allowed',
+    'moderatorsonly': 'Only moderators are allowed',
+    'moderators': 'Only moderators are allowed',
+    'allowedonly': 'Only moderators are allowed',
+    'membersandmoderatorsonly': 'Only members and moderators are allowed',
+}

libs/ldap_conn_utils.py

+# Author: Zhang Huangbin <zhb _at_ iredmail.org>
+
+import logging
+import ldap
+import settings
+from libs import SMTP_ACTIONS
+
+def get_account_ldif(conn, account, attrlist=None):
+    logging.debug('[+] Getting LDIF data of account: %s' % account)
+
+    ldap_filter = '(&(|(mail=%s)(shadowAddress=%s))(|(objectClass=mailUser)(objectClass=mailList)(objectClass=mailAlias)))' % (account, account)
+
+    logging.debug('search filter: %s' % ldap_filter)
+    logging.debug('search attributes: %s' % str(attrlist))
+    #if attrlist:
+    #    logging.debug('search attributes: %s' % str(attrlist))
+    #else:
+    #    # Attribute list must be None if it's a empty list.
+    #    attrlist = None
+
+    try:
+        result = conn.search_s(settings.ldap_basedn,
+                               ldap.SCOPE_SUBTREE,
+                               ldap_filter,
+                               attrlist)
+
+        if len(result) == 1:
+            logging.debug('result: %s' % str(result))
+            dn, entry = result[0]
+            return (dn, entry)
+        else:
+            logging.debug('Not a local account.')
+            return (None, None)
+    except Exception, e:
+        logging.debug('!!! ERROR !!! result: %s' % str(e))
+        return (None, None)
+
+def get_allowed_senders_of_mail_list(conn,
+                                     base_dn,
+                                     dn_of_mail_list,
+                                     sender,
+                                     recipient,
+                                     policy):
+    """return search_result_list_based_on_access_policy"""
+
+    logging.debug('[+] Getting allowed senders of mail list: %s' % recipient)
+    recipient_domain = recipient.split('@', 1)[-1]
+
+    # Set base dn as domain dn.
+    domaindn = 'domainName=' + recipient_domain + ',' + base_dn
+
+    # Default search scope. 2==ldap.SCOPE_SUBTREE
+    searchScope = 2
+
+    # Set search filter, attributes based on policy.
+    # Override base dn, scope if necessary.
+    if policy in ['membersonly', 'members']:
+        basedn = domaindn
+        # Filter: get mail list members.
+        searchFilter = "(&(|(objectclass=mailUser)(objectClass=mailExternalUser))(accountStatus=active)(memberOfGroup=%s))" % (recipient, )
+
+        # Get both mail and shadowAddress.
+        searchAttrs = ['mail', 'shadowAddress',]
+
+    elif policy in ['allowedonly', 'moderatorsonly', 'moderators']:
+        # Get mail list moderators.
+        basedn = dn_of_mail_list
+        searchScope = 0     # Use ldap.SCOPE_BASE to improve performance.
+        searchFilter = "(&(objectclass=mailList)(mail=%s))" % (recipient, )
+        searchAttrs = ['listAllowedUser']
+
+    else:
+        basedn = domaindn
+        # Policy: policy==membersAndModeratorsOnly or not set.
+        # Filter used to get both members and moderators.
+        searchFilter = "(|(&(|(objectClass=mailUser)(objectClass=mailExternalUser))(memberOfGroup=%s))(&(objectclass=mailList)(mail=%s)))" % (recipient, recipient, )
+        searchAttrs = ['mail', 'shadowAddress', 'listAllowedUser',]
+
+    logging.debug('base dn: %s' % basedn)
+    logging.debug('search scope: %s' % searchScope)
+    logging.debug('search filter: %s' % searchFilter)
+    logging.debug('search attributes: %s' % ', '.join(searchAttrs))
+
+    try:
+        result = conn.search_s(basedn, searchScope, searchFilter, searchAttrs)
+        userList = []
+        for obj in result:
+            for k in searchAttrs:
+                if k in obj[1].keys():
+                    # Example of result data:
+                    # [('dn', {'listAllowedUser': ['user@domain.ltd']})]
+                    userList += obj[1][k]
+                else:
+                    pass
+
+        # Exclude mail list itself.
+        if recipient in userList:
+            userList.remove(recipient)
+
+        logging.debug('result: %s' % str(userList))
+
+        # Query once more to get 'shadowAddress'.
+        if len(userList) > 0 and (policy == 'allowedonly' or policy == 'moderatorsonly'):
+            logging.debug('Addition query to get user aliases...')
+
+            basedn = 'ou=Users,' + domaindn
+            searchFilter = '(&(objectClass=mailUser)(enabledService=shadowaddress)(|'
+            for i in userList:
+                searchFilter += '(mail=%s)' % i
+            searchFilter += '))'
+
+            searchAttrs = ['shadowAddress',]
+
+            logging.debug('base dn: %s' % basedn)
+            logging.debug('search scope: 2 (ldap.SCOPE_SUBTREE)')
+            logging.debug('search filter: %s' % searchFilter)
+            logging.debug('search attributes: %s' % ', '.join(searchAttrs))
+
+            try:
+                resultOfShadowAddresses = conn.search_s(
+                    'ou=Users,'+domaindn,
+                    2,  # ldap.SCOPE_SUBTREE
+                    searchFilter,
+                    ['mail', 'shadowAddress',],
+                )
+
+                for obj in resultOfShadowAddresses:
+                    for k in searchAttrs:
+                        if k in obj[1].keys():
+                            # Example of result data:
+                            # [('dn', {'listAllowedUser': ['user@domain.ltd']})]
+                            userList += obj[1][k]
+                        else:
+                            pass
+
+                logging.debug('final result: %s' % str(userList))
+
+            except Exception, e:
+                logging.debug('Error: %s' % str(e))
+
+        return userList
+    except Exception, e:
+        logging.debug('Error: %s' % str(e))
+        return []
+
+def apply_plugin(plugin, **kwargs):
+    action = SMTP_ACTIONS['default']
+
+    logging.debug('--> Apply plugin: %s' % plugin.__name__)
+    try:
+        action = plugin.restriction(**kwargs)
+        logging.debug('<-- Result: %s' % action)
+    except Exception, e:
+        logging.debug('<!> Error: %s' % str(e))
+
+    return action
+
 import ldap
 import logging
 import settings
-from libs import SMTP_ACTIONS
+from libs import SMTP_ACTIONS, ldap_conn_utils
 
 
 class LDAPModeler:
         except Exception, e:
             logging.debug('Error while closing connection: %s' % str(e))
 
-    def __get_recipient_dn_ldif(self, recipient):
-        logging.debug('__get_recipient_dn_ldif (recipient): %s' % recipient)
-        try:
-            filter = '(&(|(mail=%s)(shadowAddress=%s))(|(objectClass=mailUser)(objectClass=mailList)(objectClass=mailAlias)))' % (recipient, recipient)
-            logging.debug('__get_recipient_dn_ldif (ldap query filter): %s' % filter)
-
-            result = self.conn.search_s(settings.ldap_basedn, ldap.SCOPE_SUBTREE, filter)
-
-            if len(result) == 1:
-                logging.debug('__get_recipient_dn_ldif (ldap query result): %s' % str(result))
-                dn, entry = result[0]
-                return (dn, entry)
-            else:
-                logging.debug('__get_recipient_dn_ldif: Can not find recipient in LDAP server.')
-                return (None, None)
-        except Exception, e:
-            logging.debug('!!! ERROR !!! __get_recipient_dn_ldif (result): %s' % str(e))
-            return (None, None)
-
-    def __get_access_policy(self, recipient):
-        """Get access policy of mail list.
-
-        return (dn_of_mail_list, value_of_access_policy,)"""
-
-        logging.debug('__get_access_policy (list): %s' % recipient)
-
-        # Replace 'recipient' placehold in config file with mail list address.
-        try:
-            self.cfg.set('ldap', "recipient", recipient)
-        except Exception, e:
-            logging.error("""Error while replacing 'recipient': %s""" % (str(e)))
-
-        # Search mail list object.
-        searchBasedn = 'mail=%s,ou=Groups,domainName=%s,%s' % (recipient, recipient.split('@')[1], settings.ldap_basedn)
-        searchScope = ldap.SCOPE_BASE
-        searchFilter = self.cfg.get('ldap', 'filter_maillist')
-        searchAttr = self.cfg.get('ldap', 'attr_access_policy', 'accessPolicy')
-
-        logging.debug('__get_access_policy (searchBasedn): %s' % searchBasedn)
-        logging.debug('__get_access_policy (searchScope): %s' % searchScope)
-        logging.debug('__get_access_policy (searchFilter): %s' % searchFilter)
-        logging.debug('__get_access_policy (searchAttr): %s' % searchAttr)
-
-        try:
-            result = self.conn.search_s(searchBasedn, searchScope, searchFilter, [searchAttr])
-            logging.debug('__get_access_policy (search result): %s' % str(result))
-        except ldap.NO_SUCH_OBJECT:
-            logging.debug('__get_access_policy (not a mail list: %s) Returned (None)' % recipient)
-            return (None, None)
-        except Exception, e:
-            logging.debug('__get_access_policy (ERROR while searching list): %s' % str(e))
-            return (None, None)
-
-        if len(result) != 1:
-            return (None, None)
-        else:
-            # Example of result data:
-            # [('dn', {'accessPolicy': ['value']})]
-            listdn = result[0][0]
-            listpolicy = result[0][1][searchAttr][0]
-            returnVal = (listdn, listpolicy)
-
-            logging.debug('__get_access_policy (returned): %s' % str(returnVal))
-            return returnVal
-
-    def __get_allowed_senders(self, listdn, recipient, listpolicy, sender=''):
-        """return search_result_list_based_on_access_policy"""
-        logging.debug('__get_allowed_senders (listpolicy): %s' % listpolicy)
-
-        # Replace 'recipient' and 'sender' with email addresses.
-        #self.cfg.set("ldap", "recipient", recipient)
-        #self.cfg.set("ldap", "sender", sender)
-
-        # Set search base dn, scope, filter and attribute list based on access policy.
-        if listpolicy == 'membersOnly':
-            baseDN = settings.ldap_basedn
-            searchScope = ldap.SCOPE_SUBTREE
-            # Filter used to get mail list members.
-            #searchFilter = self.cfg.get("ldap", "filter_member")
-            #searchAttr = self.cfg.get("ldap", "attr_member")
-        else:
-            baseDN = listdn
-            # Use SCOPE_BASE to improve performance.
-            searchScope = ldap.SCOPE_BASE
-            # Filter used to get mail list moderators.
-            #searchFilter = self.cfg.get("ldap", "filter_allowed_senders")
-            #searchAttr = self.cfg.get("ldap", "attr_moderator")
-
-        logging.debug('__get_allowed_senders (baseDN): %s' % baseDN)
-        logging.debug('__get_allowed_senders (searchScope): %s' % searchScope)
-        logging.debug('__get_allowed_senders (searchFilter): %s' % searchFilter)
-        logging.debug('__get_allowed_senders (searchAttr): %s' % searchAttr)
-
-        try:
-            result = self.conn.search_s(baseDN, searchScope, searchFilter, [searchAttr])
-            logging.debug('__get_allowed_senders (search result): %s' % str(result))
-        except ldap.NO_SUCH_OBJECT:
-            logging.debug('__get_allowed_senders (not a mail list: %s) Returned (None)' % recipient)
-            return None
-        except Exception, e:
-            logging.debug('__get_allowed_senders (ERROR while searching list): %s' % str(e))
-            return None
-
-        if len(result) != 1:
-            return None
-        else:
-            # Example of result data:
-            # [('dn', {'listAllowedUser': ['user@domain.ltd']})]
-            return result[0][1][searchAttr]
-
-    def __get_smtp_action(self, recipient, sender):
-        """return smtp_action"""
-        listdn, listpolicy = self.__get_access_policy(recipient)
-
-        if listdn is None or listpolicy is None:
-            return None
-        else:
-            if listpolicy == "public":
-                # No restriction.
-                return SMTP_ACTIONS['accept']
-            elif listpolicy == "domain":
-                # Allow all users under the same domain.
-                if sender.split('@')[1] == recipient.split('@')[1]:
-                    return SMTP_ACTIONS['accept']
-                else:
-                    return SMTP_ACTIONS['reject']
-            elif listpolicy == "allowedOnly":
-                # Bypass allowed users only.
-                allowed_senders = self.__get_allowed_senders(listdn, recipient, 'allowedOnly', sender)
-
-                if allowed_senders is not None:
-                    addresses = set(allowed_senders)    # Remove duplicate addresses.
-                    if sender in addresses:
-                        return SMTP_ACTIONS['accept']
-                    else:
-                        return SMTP_ACTIONS['reject']
-                else:
-                    return SMTP_ACTIONS['reject']
-            elif listpolicy == "membersOnly":
-                allowed_senders = self.__get_allowed_senders(listdn, recipient, 'membersOnly', sender)
-
-                if allowed_senders is not None:
-                    addresses = set(allowed_senders)
-                    if sender in addresses:
-                        return SMTP_ACTIONS['accept']
-                    else:
-                        return SMTP_ACTIONS['reject']
-                else:
-                    return SMTP_ACTIONS['reject']
-
-    def handle_data(self, map):
-        if 'sender' in map.keys() and 'recipient' in map.keys():
-            if len(map['sender']) < 6:
-                # Not a valid email address.
-                return 'DUNNO'
-
-            if settings.plugins:
-                # Get account dn and LDIF data.
-                recipientDn, recipientLdif = self.__get_recipient_dn_ldif(map['recipient'])
-
-                # Return if recipient account doesn't exist.
-                if recipientDn is None or recipientLdif is None:
-                    logging.debug('Recipient DN or LDIF is None.')
-                    return SMTP_ACTIONS['default']
-
-                #
-                # Import plugin modules.
-                #
-                self.modules = []
-
-                # Load plugin module.
-                for plugin in settings.plugins:
-                    try:
-                        self.modules.append(__import__(plugin))
-                    except ImportError:
-                        # Print error message if plugin module doesn't exist.
-                        # Use logging.info to let admin know this critical error.
-                        logging.info('Error: plugin %s.py not exist.' % plugin)
-                    except Exception, e:
-                        logging.debug('Error while importing plugin module (%s): %s' % (plugin, str(e)))
-
-                #
-                # Apply plugins.
-                #
-                self.action = ''
-                for module in self.modules:
-                    try:
-                        logging.debug('Apply plugin: %s.' % (module.__name__, ))
-                        pluginAction = module.restriction(
-                            ldapConn=self.conn,
-                            ldapBaseDn=settings.ldap_basedn,
-                            ldapRecipientDn=recipientDn,
-                            ldapRecipientLdif=recipientLdif,
-                            smtpSessionData=map,
-                            logger=logging,
-                        )
-
-                        logging.debug('Response from plugin (%s): %s' % (module.__name__, pluginAction))
-                        if not pluginAction.startswith('DUNNO'):
-                            logging.info('Response from plugin (%s): %s' % (module.__name__, pluginAction))
-                            return pluginAction
-                    except Exception, e:
-                        logging.debug('Error while apply plugin (%s): %s' % (module, str(e)))
-
-            else:
-                # No plugins available.
-                return 'DUNNO'
-        else:
+    def handle_data(self, smtp_session_map,
+                    plugins=[],
+                    plugins_for_sender=[],
+                    plugins_for_recipient=[],
+                    plugins_for_misc=[],
+                    sender_search_attrlist=None,
+                    recipient_search_attrlist=None,
+                   ):
+        # No sender or recipient in smtp session.
+        if not 'sender' in smtp_session_map or not 'recipient' in smtp_session_map:
             return SMTP_ACTIONS['defer']
 
+        # Not a valid email address.
+        if len(smtp_session_map['sender']) < 6:
+            return 'DUNNO'
+
+        # No plugins available.
+        if not plugins:
+            return 'DUNNO'
+
+        # Check whether we should get sender/recipient LDIF data first
+        get_sender_ldif = False
+        get_recipient_ldif = False
+        if plugins_for_sender:
+            get_sender_ldif = True
+
+        if plugins_for_recipient:
+            get_recipient_ldif = True
+
+        # Get account dn and LDIF data.
+        plugin_kwargs = {'smtpSessionData': smtp_session_map,
+                         'conn': self.conn,
+                         'baseDn': settings.ldap_basedn,
+                         'senderDn': None,
+                         'senderLdif': None,
+                         'recipientDn': None,
+                         'recipientLdif': None,
+                        }
+
+        if get_sender_ldif:
+            senderDn, senderLdif = ldap_conn_utils.get_account_ldif(
+                conn=self.conn,
+                account=smtp_session_map['sender'],
+                attrlist=sender_search_attrlist,
+            )
+            plugin_kwargs['senderDn'] = senderDn
+            plugin_kwargs['senderLdif'] = senderLdif
+
+            for plugin in plugins_for_sender:
+                action = ldap_conn_utils.apply_plugin(plugin, **plugin_kwargs)
+                if not action.startswith('DUNNO'):
+                    return action
+
+        if get_recipient_ldif:
+            recipientDn, recipientLdif = ldap_conn_utils.get_account_ldif(
+                conn=self.conn,
+                account=smtp_session_map['recipient'],
+                attrlist=recipient_search_attrlist,
+            )
+            plugin_kwargs['recipientDn'] = recipientDn
+            plugin_kwargs['recipientLdif'] = recipientLdif
+
+            for plugin in plugins_for_recipient:
+                action = ldap_conn_utils.apply_plugin(plugin, **plugin_kwargs)
+                if not action.startswith('DUNNO'):
+                    return action
+
+        for plugin in plugins_for_misc:
+            action = ldap_conn_utils.apply_plugin(plugin, **plugin_kwargs)
+            if not action.startswith('DUNNO'):
+                return action
+
+        return SMTP_ACTIONS['default']
+
         except Exception, e:
             logging.debug('Error while closing connection: %s' % str(e))
 
-    def handle_data(self, map):
-        if 'sender' in map.keys() and 'recipient' in map.keys():
-            if len(map['sender']) < 6:
+    def handle_data(self, smtp_session_map, plugins=[]):
+        if 'sender' in smtp_session_map.keys() and 'recipient' in smtp_session_map.keys():
+            if len(smtp_session_map['sender']) < 6:
                 # Not a valid email address.
                 return 'DUNNO'
 
             # Sender/recipient are used almost in all plugins, so store them
             # a dict and pass to plugins.
             senderReceiver = {
-                'sender': map['sender'],
-                'recipient': map['recipient'],
-                'sender_domain': map['sender'].split('@')[-1],
-                'recipient_domain': map['recipient'].split('@')[-1],
+                'sender': smtp_session_map['sender'],
+                'recipient': smtp_session_map['recipient'],
+                'sender_domain': smtp_session_map['sender'].split('@')[-1],
+                'recipient_domain': smtp_session_map['recipient'].split('@')[-1],
             }
 
             if len(self.plugins) > 0:
                         pluginAction = module.restriction(
                             dbConn=self.cursor,
                             senderReceiver=senderReceiver,
-                            smtpSessionData=map,
-                            logger=logging,
+                            smtpSessionData=smtp_session_map,
                         )
 
                         logging.debug('Response from plugin (%s): %s' % (module.__name__, pluginAction))
Add a comment to this file

plugins-rr/__init__.py

Empty file removed.

plugins-rr/ldap_expired_password.py

-# Author:   Zhang Huangbin <zhb _at_ iredmail.org>
-# Purpose:  Force user to change account password in 90 days.
-
-import datetime
-from libs import SMTP_ACTIONS
-
-# Force mail user to change password in how many days. Default is 90.
-EXPIRED_DAYS = 90
-
-def restriction(smtpSessionData, ldapSenderLdif, **kargs):
-    # Check password last change days
-    last_changed_day = int(ldapSenderLdif.get('shadowLastChange', [0])[0])
-
-    # Convert today to shadowLastChange
-    today = datetime.date.today()
-    changed_days_of_today = (datetime.date(today.year, today.month, today.day) - datetime.date(1970, 1, 1)).days
-
-    if (last_changed_day + EXPIRED_DAYS) < changed_days_of_today:
-        return 'REJECT Password expired, please change the password before sending email.'
-
-    return SMTP_ACTIONS['default']
-

plugins-rr/ldap_recipient_restrictions.py

-# Author:   Zhang Huangbin <zhb _at_ iredmail.org>
-# Date:     2010-04-20
-# Purpose:  Per-user whitelist/blacklist for recipient restrictions.
-#           Bypass all whitelisted recipients, reject all blacklisted recipients.
-
-# ------------- Addition configure required ------------
-# * In postfix main.cf:
-#
-#   smtpd_sender_restrictions =
-#           check_policy_service inet:127.0.0.1:7778,
-#           [YOUR OTHER RESTRICTIONS HERE]
-#
-#   Here, ip address '127.0.0.1' and port number '7778' are set in iRedAPD-RR
-#   config file: iredapd-rr.ini.
-# ------------------------------------------------------
-
-# Value of mailWhitelistRecipient and mailBlacklistRecipient:
-#   - Single address:   user@domain.ltd
-#   - Whole domain:     @domain.ltd
-#   - Whole Domain and its sub-domains: @.domain.ltd
-#   - All recipient:       @.
-
-def restriction(smtpSessionData, ldapSenderLdif, **kargs):
-    # Get recipient address.
-    smtpRecipient = smtpSessionData.get('recipient').lower()
-    splited_recipient_domain = str(smtpRecipient.split('@')[-1]).split('.')
-
-    # Get correct domain name and sub-domain name.
-    # Sample recipient domain: sub2.sub1.com.cn
-    #   -> sub2.sub1.com.cn
-    #   -> .sub2.sub1.com.cn
-    #   -> .sub1.com.cn
-    #   -> .com.cn
-    #   -> .cn
-    recipients = ['@.', smtpRecipient, '@' + smtpRecipient.split('@')[-1],]
-    for counter in range(len(splited_recipient_domain)):
-        # Append domain and sub-domain.
-        recipients += ['@.' + '.'.join(splited_recipient_domain)]
-        splited_recipient_domain.pop(0)
-
-    # Get value of mailBlacklistedRecipient, mailWhitelistRecipient.
-    blRecipients = [v.lower()
-            for v in ldapSenderLdif.get('mailBlacklistRecipient', [])
-            ]
-
-    wlRecipients = [v.lower()
-            for v in ldapSenderLdif.get('mailWhitelistRecipient', [])
-            ]
-
-    # Bypass whitelisted recipients if has intersection set.
-    if len(set(recipients) & set(wlRecipients)) > 0:
-        return 'DUNNO'
-
-    # Reject blacklisted recipients if has intersection set.
-    if len(set(recipients) & set(blRecipients)) > 0 or '@.' in blRecipients:
-        return 'REJECT Permission denied'
-
-    # If not matched bl/wl list:
-    return 'DUNNO'

plugins/amavisd_block_blacklisted_senders.py

+# Author: Zhang Huangbin <zhb _at_ iredmail.org>
+
+# Priority: whitelist first, then blacklist.
+
+import logging
+from libs import SMTP_ACTIONS
+
+REQUIRE_LOCAL_SENDER = False
+REQUIRE_LOCAL_RECIPIENT = False
+SENDER_SEARCH_ATTRLIST = []
+RECIPIENT_SEARCH_ATTRLIST = ['amavisBlacklistSender', 'amavisWhitelistSender']
+
+def restriction(**kwargs):
+    smtpSessionData = kwargs['smtpSessionData']
+    ldapRecipientLdif = kwargs['recipientLdif']
+    # Get sender address.
+    sender = smtpSessionData.get('sender').lower()
+
+    # Get valid Amavisd sender, sender domain and sub-domain(s).
+    # - Sample user: user@sub2.sub1.com.cn
+    # - Valid Amavisd senders:
+    #   -> user@sub2.sub1.com.cn
+    #   -> @sub2.sub1.com.cn
+    #   -> @.sub2.sub1.com.cn
+    #   -> @.sub1.com.cn
+    #   -> @.com.cn
+    #   -> @.cn
+    splited_sender_domain = str(sender.split('@', 1)[-1]).split('.')
+
+    # Default senders (user@domain.ltd): ['@.', 'user@domain.ltd', @domain.ltd']
+    valid_amavisd_senders = set(['@.', sender, '@'+sender.split('@', 1)[-1],])
+    for counter in range(len(splited_sender_domain)):
+        # Append domain and sub-domain.
+        valid_amavisd_senders.update(['@.' + '.'.join(splited_sender_domain)])
+        splited_sender_domain.pop(0)
+
+    # Get list of amavisBlacklistedSender.
+    blSenders = set([v.lower() for v in ldapRecipientLdif.get('amavisBlacklistSender', [])])
+
+    # Get list of amavisWhitelistSender.
+    wlSenders = set([v.lower() for v in ldapRecipientLdif.get('amavisWhitelistSender', [])])
+
+    logging.debug('Sender: %s' % sender)
+    logging.debug('Whitelisted senders: %s' % str(wlSenders))
+    logging.debug('Blacklisted senders: %s' % str(blSenders))
+
+    # Bypass whitelisted senders.
+    if len(valid_amavisd_senders & wlSenders) > 0:
+        return SMTP_ACTIONS['accept']
+
+    # Reject blacklisted senders.
+    if len(valid_amavisd_senders & blSenders) > 0:
+        return 'REJECT Blacklisted'
+
+    # Neither blacklisted nor whitelisted.
+    return 'DUNNO (No white/blacklist records found)'

plugins/block_amavisd_blacklisted_senders.py

-# Author: Zhang Huangbin <zhb _at_ iredmail.org>
-
-# Priority: whitelist first, then blacklist.
-
-PLUGIN_NAME = 'block_amavisd_blacklisted_senders'
-
-from libs import SMTP_ACTIONS
-
-def restriction(smtpSessionData, ldapRecipientLdif, logger, **kargs):
-    # Get sender address.
-    sender = smtpSessionData.get('sender').lower()
-
-    # Get valid Amavisd sender, sender domain and sub-domain(s).
-    # - Sample user: user@sub2.sub1.com.cn
-    # - Valid Amavisd senders:
-    #   -> user@sub2.sub1.com.cn
-    #   -> @sub2.sub1.com.cn
-    #   -> @.sub2.sub1.com.cn
-    #   -> @.sub1.com.cn
-    #   -> @.com.cn
-    #   -> @.cn
-    splited_sender_domain = str(sender.split('@', 1)[-1]).split('.')
-
-    # Default senders (user@domain.ltd): ['@.', 'user@domain.ltd', @domain.ltd']
-    valid_amavisd_senders = set(['@.', sender, '@'+sender.split('@', 1)[-1],])
-    for counter in range(len(splited_sender_domain)):
-        # Append domain and sub-domain.
-        valid_amavisd_senders.update(['@.' + '.'.join(splited_sender_domain)])
-        splited_sender_domain.pop(0)
-
-    # Get list of amavisBlacklistedSender.
-    blSenders = set([v.lower() for v in ldapRecipientLdif.get('amavisBlacklistSender', [])])
-
-    # Get list of amavisWhitelistSender.
-    wlSenders = set([v.lower() for v in ldapRecipientLdif.get('amavisWhitelistSender', [])])
-
-    logger.debug('(%s) Sender: %s' % (PLUGIN_NAME, sender))
-    logger.debug('(%s) Whitelisted senders: %s' % (PLUGIN_NAME, str(wlSenders)))
-    logger.debug('(%s) Blacklisted senders: %s' % (PLUGIN_NAME, str(blSenders)))
-
-    # Bypass whitelisted senders.
-    if len(valid_amavisd_senders & wlSenders) > 0:
-        return SMTP_ACTIONS['accept']
-
-    # Reject blacklisted senders.
-    if len(valid_amavisd_senders & blSenders) > 0:
-        return 'REJECT Blacklisted'
-
-    # Neither blacklisted nor whitelisted.
-    return 'DUNNO No white-/blacklist records found.'

plugins/ldap_domain_wblist.py

 # This plugin is used for per-domain white-/blacklist.
 # ----------------------------------------------------------------------------
 
-PLUGIN_NAME = 'ldap_domain_wblist'
+import logging
 
-def restriction(ldapConn, ldapBaseDn, smtpSessionData, logger, **kargs):
+REQUIRE_LOCAL_SENDER_= False
+REQUIRE_LOCAL_RECIPIENT = False
+SENDER_SEARCH_ATTRLIST = []
+RECIPIENT_SEARCH_ATTRLIST = []
+
+def restriction(**kwargs):
+    ldapConn = kwargs['conn']
+    ldapBaseDn = kwargs['baseDn']
+    smtpSessionData = kwargs['smtpSessionData']
+
     sender = smtpSessionData['sender'].lower()
     splitedSenderDomain = str(sender.split('@')[-1]).split('.')
 
     recipient = smtpSessionData['recipient'].lower()
     recipientDomain = recipient.split('@')[-1]
 
-    logger.debug('(%s) Sender: %s' % (PLUGIN_NAME, sender))
-    logger.debug('(%s) Recipient: %s' % (PLUGIN_NAME, recipient))
+    logging.debug('Sender: %s' % sender)
+    logging.debug('Recipient: %s' % recipient)
 
     # Query ldap to get domain dn, with domain alias support.
     try:
             ['dn'],
         )
         dnOfRecipientDomain = resultDnOfDomain[0][0]
-        logger.debug('(%s) DN of recipient domain: %s' % (PLUGIN_NAME, dnOfRecipientDomain))
+        logging.debug('DN of recipient domain: %s' % dnOfRecipientDomain)
     except Exception, e:
-        return 'DUNNO Error while fetching domain dn: %s' % (str(e))
+        return 'DUNNO (Error while fetching domain dn: %s)' % (str(e))
 
     # Get list of restricted ip addresses.
     senderIP = smtpSessionData['client_address']
 
         if len(resultWblists) == 0:
             # No white/blacklist available.
-            return 'DUNNO No white-/blacklist found.'
+            return 'DUNNO (No white/blacklist found)'
 
         ###################
         # Whitelist first.
         whitelistedSenders = resultWblists[0][1].get('domainWhitelistSender', [])
         whitelistedIPAddresses = resultWblists[0][1].get('domainWhitelistIP', [])
 
-        logger.debug('(%s) Whitelisted senders: %s' % (PLUGIN_NAME, ', '.join(whitelistedSenders)))
-        logger.debug('(%s) Whitelisted IP addresses: %s' % (PLUGIN_NAME, ', '.join(whitelistedIPAddresses)))
+        logging.debug('Whitelisted senders: %s' % ', '.join(whitelistedSenders))
+        logging.debug('Whitelisted IP addresses: %s' % ', '.join(whitelistedIPAddresses))
 
         if len(set(listOfRestrictedSenders) & set(whitelistedSenders)) > 0 or \
            len(set(listOfRestrictedIPAddresses) & set(whitelistedIPAddresses)) > 0:
-            return 'DUNNO Whitelisted.'
+            return 'DUNNO (Whitelisted)'
 
         ###################
         # Blacklist.
         blacklistedSenders = resultWblists[0][1].get('domainBlacklistSender', [])
         blacklistedIPAddresses = resultWblists[0][1].get('domainBlacklistIP', [])
 
-        logger.debug('(%s) Blacklisted senders: %s' % (PLUGIN_NAME, ', '.join(blacklistedSenders)))
-        logger.debug('(%s) Blacklisted IP addresses: %s' % (PLUGIN_NAME, ', '.join(blacklistedIPAddresses)))
+        logging.debug('Blacklisted senders: %s' % ', '.join(blacklistedSenders))
+        logging.debug('Blacklisted IP addresses: %s' % ', '.join(blacklistedIPAddresses))
 
         if len(set(listOfRestrictedSenders) & set(blacklistedSenders)) > 0 or \
            len(set(listOfRestrictedIPAddresses) & set(blacklistedIPAddresses)) > 0:
             return 'REJECT Blacklisted'
 
-        return 'DUNNO Not listed in white-/blacklist records.'
+        return 'DUNNO (Not listed in white/blacklist records)'
     except Exception, e:
         # Error while quering LDAP server, return 'DUNNO' instead of rejecting emails.
-        return 'DUNNO Error while fetching white-/blacklist records: %s' % (str(e))
+        return 'DUNNO (Error while fetching white/blacklist records: %s)' % (str(e))

plugins/ldap_expired_password.py

+# Author:   Zhang Huangbin <zhb _at_ iredmail.org>
+# Purpose:  Force user to change account password in 90 days.
+
+import datetime
+from libs import SMTP_ACTIONS
+
+REQUIRE_LOCAL_SENDER_= True
+REQUIRE_LOCAL_RECIPIENT = False
+SENDER_SEARCH_ATTRLIST = ['shadowLastChange']
+RECIPIENT_SEARCH_ATTRLIST = []
+
+# Force mail user to change password in how many days. Default is 90.
+EXPIRED_DAYS = 90
+
+def restriction(smtpSessionData, ldapSenderLdif, **kargs):
+    # Check password last change days
+    last_changed_day = int(ldapSenderLdif.get('shadowLastChange', [0])[0])
+
+    # Convert today to shadowLastChange
+    today = datetime.date.today()
+    changed_days_of_today = (datetime.date(today.year, today.month, today.day) - datetime.date(1970, 1, 1)).days
+
+    if (last_changed_day + EXPIRED_DAYS) < changed_days_of_today:
+        return 'REJECT Password expired, please change the password before sending email.'
+
+    return SMTP_ACTIONS['default']
+

plugins/ldap_maillist_access_policy.py

 # Author: Zhang Huangbin <zhb _at_ iredmail.org>
+# Purpose: Restrict who can send email to mail list.
+# Note: Access policy is defined in libs/__init__.py.
 
-# ----------------------------------------------------------------------------
-# This plugin is used for mail deliver restriction.
-#
-# Available access policies:
-#   - public:   Unrestricted
-#   - domain:   Only users under same domain are allowed.
-#   - subdomain:    Only users under same domain and sub domains are allowed.
-#   - membersOnly or members:  Only members are allowed.
-#   - moderatorsOnly or moderators:   Only moderators are allowed.
-#   - membersAndModeratorsOnly: Only members and moderators are allowed.
+import logging
+from libs import SMTP_ACTIONS, ACCESS_POLICIES_OF_MAIL_LIST, ldap_conn_utils
 
-# ----------------------------------------------------------------------------
+REQUIRE_LOCAL_SENDER = False
+REQUIRE_LOCAL_RECIPIENT = True
+SENDER_SEARCH_ATTRLIST = []
+RECIPIENT_SEARCH_ATTRLIST = ['accessPolicy']
 
-from libs import SMTP_ACTIONS
+def restriction(**kwargs):
+    smtpSessionData = kwargs['smtpSessionData']
+    ldapConn = kwargs['conn']
+    ldapBaseDn = kwargs['baseDn']
+    ldapRecipientDn = kwargs['recipientDn']
+    ldapRecipientLdif = kwargs['recipientLdif']
 
-PLUGIN_NAME = 'ldap_maillist_access_policy'
-
-def __get_allowed_senders(ldapConn, ldapBaseDn, listDn, sender, recipient, policy, logger, *kw, **kargs):
-    """return search_result_list_based_on_access_policy"""
-
-    logger.debug('(%s) Get allowed senders...' % (PLUGIN_NAME))
-
-    recipient_domain = recipient.split('@', 1)[-1]
-
-    # Set base dn as domain dn.
-    domaindn = 'domainName=' + recipient_domain + ',' + ldapBaseDn
-
-    # Default search scope. 2==ldap.SCOPE_SUBTREE
-    searchScope = 2
-
-    # Set search filter, attributes based on policy.
-    # Override base dn, scope if necessary.
-    if policy in ['membersonly', 'members']:
-        basedn = domaindn
-        # Filter: get mail list members.
-        searchFilter = "(&(|(objectclass=mailUser)(objectClass=mailExternalUser))(accountStatus=active)(memberOfGroup=%s))" % (recipient, )
-
-        # Get both mail and shadowAddress.
-        searchAttrs = ['mail', 'shadowAddress',]
-
-    elif policy in ['allowedonly', 'moderatorsonly', 'moderators']:
-        # Get mail list moderators.
-        basedn = listDn
-        searchScope = 0     # Use ldap.SCOPE_BASE to improve performance.
-        searchFilter = "(&(objectclass=mailList)(mail=%s))" % (recipient, )
-        searchAttrs = ['listAllowedUser']
-
-    else:
-        basedn = domaindn
-        # Policy: policy==membersAndModeratorsOnly or not set.
-        # Filter used to get both members and moderators.
-        searchFilter = "(|(&(|(objectClass=mailUser)(objectClass=mailExternalUser))(memberOfGroup=%s))(&(objectclass=mailList)(mail=%s)))" % (recipient, recipient, )
-        searchAttrs = ['mail', 'shadowAddress', 'listAllowedUser',]
-
-    logger.debug('(%s) base dn: %s' % (PLUGIN_NAME, basedn))
-    logger.debug('(%s) search scope: %s' % (PLUGIN_NAME, searchScope))
-    logger.debug('(%s) search filter: %s' % (PLUGIN_NAME, searchFilter))
-    logger.debug('(%s) search attributes: %s' % (PLUGIN_NAME, ', '.join(searchAttrs)))
-
-    try:
-        result = ldapConn.search_s(basedn, searchScope, searchFilter, searchAttrs)
-        userList = []
-        for obj in result:
-            for k in searchAttrs:
-                if k in obj[1].keys():
-                    # Example of result data:
-                    # [('dn', {'listAllowedUser': ['user@domain.ltd']})]
-                    userList += obj[1][k]
-                else:
-                    pass
-
-        # Exclude mail list itself.
-        if recipient in userList:
-            userList.remove(recipient)
-
-        logger.debug('(%s) search result: %s' % (PLUGIN_NAME, str(userList)))
-
-        # Query once more to get 'shadowAddress'.
-        if len(userList) > 0 and (policy == 'allowedonly' or policy == 'moderatorsonly'):
-            logger.debug('(%s) Addition query to get user aliases...' % (PLUGIN_NAME))
-
-            basedn = 'ou=Users,' + domaindn
-            searchFilter = '(&(objectClass=mailUser)(enabledService=shadowaddress)(|'
-            for i in userList:
-                searchFilter += '(mail=%s)' % i
-            searchFilter += '))'
-
-            searchAttrs = ['shadowAddress',]
-
-            logger.debug('(%s) base dn: %s' % (PLUGIN_NAME, basedn))
-            logger.debug('(%s) search scope: 2 (ldap.SCOPE_SUBTREE)' % (PLUGIN_NAME))
-            logger.debug('(%s) search filter: %s' % (PLUGIN_NAME, searchFilter))
-            logger.debug('(%s) search attributes: %s' % (PLUGIN_NAME, ', '.join(searchAttrs)))
-
-            try:
-                resultOfShadowAddresses = ldapConn.search_s(
-                    'ou=Users,'+domaindn,
-                    2,  # ldap.SCOPE_SUBTREE
-                    searchFilter,
-                    ['mail', 'shadowAddress',],
-                )
-
-                for obj in resultOfShadowAddresses:
-                    for k in searchAttrs:
-                        if k in obj[1].keys():
-                            # Example of result data:
-                            # [('dn', {'listAllowedUser': ['user@domain.ltd']})]
-                            userList += obj[1][k]
-                        else:
-                            pass
-
-                logger.debug('(%s) final result: %s' % (PLUGIN_NAME, str(userList)))
-
-            except Exception, e:
-                logger.debug(str(e))
-
-        return userList
-    except Exception, e:
-        logger.debug('(%s) Error: %s' % (PLUGIN_NAME, str(e)))
-        return []
-
-def restriction(ldapConn, ldapBaseDn, ldapRecipientDn, ldapRecipientLdif, smtpSessionData, logger, **kargs):
     # Return if recipient is not a mail list object.
     if 'maillist' not in [v.lower() for v in ldapRecipientLdif['objectClass']]:
-        return 'DUNNO Not a mail list account.'
+        return 'DUNNO (Not mail list)'
 
     sender = smtpSessionData['sender'].lower()
     sender_domain = sender.split('@')[-1]
             if len(qr) > 0:
                 recipient_alias_domains = qr[0][1].get('domainName', []) + qr[0][1].get('domainAliasName', [])
         except Exception, e:
-            logger.debug('(%s) Error while fetch domainAliasName: %s' % (PLUGIN_NAME, str(e),))
+            logging.debug('Error while fetch domainAliasName: %s' % str(e))
 
-        logger.debug('(%s) Recipient domain and alias domains: %s' % (PLUGIN_NAME, ','.join(recipient_alias_domains)))
+        logging.debug('Recipient domain and alias domains: %s' % ','.join(recipient_alias_domains))
 
-    logger.debug('(%s) %s -> %s, policy: %s' % (PLUGIN_NAME, sender, recipient, policy))
+    logging.debug('%s -> %s, access policy: %s (%s)' % (
+        sender, recipient, policy,
+        ACCESS_POLICIES_OF_MAIL_LIST.get(policy, 'no description'))
+    )
 
     if policy == 'public':
         # No restriction.
-        return 'DUNNO Access policy: public.'
+        return 'DUNNO (Access policy: public)'
     elif policy == "domain":
         # Bypass all users under the same domain.
         if sender_domain in recipient_alias_domains:
-            return 'DUNNO Access policy: domain'
+            return 'DUNNO (Access policy: domain)'
         else:
             return SMTP_ACTIONS['reject']
     elif policy == "subdomain":
         returned = False
         for d in recipient_alias_domains:
             if sender.endswith(d) or sender.endswith('.' + d):
-                return 'DUNNO Access policy: subdomain (%s)' % (d)
+                return 'DUNNO (Access policy: subdomain (%s))' % (d)
 
         if returned is False:
             return SMTP_ACTIONS['reject']
     else:
         # Handle other access policies: membersOnly, allowedOnly, membersAndModeratorsOnly.
-        allowedSenders = __get_allowed_senders(
-            ldapConn=ldapConn,
-            ldapBaseDn=ldapBaseDn,
-            listDn=ldapRecipientDn,
+        allowedSenders = ldap_conn_utils.get_allowed_senders_of_mail_list(
+            conn=ldapConn,
+            base_dn=ldapBaseDn,
+            dn_of_mail_list=ldapRecipientDn,
             sender=sender,
             recipient=recipient,
             policy=policy,
-            logger=logger,
         )
 
         if sender.lower() in [v.lower() for v in allowedSenders]:
-            return 'DUNNO Allowed sender.'
+            return 'DUNNO (Sender is allowed)'
         else:
             return SMTP_ACTIONS['reject']

plugins/ldap_recipient_restrictions.py

+# Author:   Zhang Huangbin <zhb _at_ iredmail.org>
+# Updated:  2012-12-30
+# Purpose:  Check whether local user (sender) is allowed to mail to recipient
+
+# Value of mailWhitelistRecipient and mailBlacklistRecipient:
+#   - Single address:   user@domain.ltd
+#   - Whole domain:     @domain.ltd
+#   - Whole Domain and its sub-domains: @.domain.ltd
+#   - All recipient:       @.
+
+import logging
+
+REQUIRE_LOCAL_SENDER = True
+REQUIRE_LOCAL_RECIPIENT = False
+SENDER_SEARCH_ATTRLIST = ['mailBlacklistedRecipient', 'mailWhitelistRecipient']
+RECIPIENT_SEARCH_ATTRLIST = []
+
+def restriction(**kwargs):
+    ldapSenderLdif = kwargs['senderLdif']
+    smtpSessionData = kwargs['smtpSessionData']
+
+    # Get recipient address.
+    smtpRecipient = smtpSessionData.get('recipient').lower()
+    splited_recipient_domain = str(smtpRecipient.split('@')[-1]).split('.')
+
+    # Get correct domain name and sub-domain name.
+    # Sample recipient domain: sub2.sub1.com.cn
+    #   -> sub2.sub1.com.cn
+    #   -> .sub2.sub1.com.cn
+    #   -> .sub1.com.cn
+    #   -> .com.cn
+    #   -> .cn
+    recipients = ['@.', smtpRecipient, '@' + smtpRecipient.split('@')[-1],]
+    for counter in range(len(splited_recipient_domain)):
+        # Append domain and sub-domain.
+        recipients += ['@.' + '.'.join(splited_recipient_domain)]
+        splited_recipient_domain.pop(0)
+
+    # Get value of mailBlacklistedRecipient, mailWhitelistRecipient.
+    blacklisted_rcpts = [v.lower() for v in ldapSenderLdif.get('mailBlacklistRecipient', [])]
+    whitelisted_rcpts = [v.lower() for v in ldapSenderLdif.get('mailWhitelistRecipient', [])]
+
+    # Bypass whitelisted recipients if has intersection set.
+    if len(set(recipients) & set(whitelisted_rcpts)) > 0:
+        return 'DUNNO (Whitelisted)'
+
+    # Reject blacklisted recipients if has intersection set.
+    if len(set(recipients) & set(blacklisted_rcpts)) > 0 \
+       or '@.' in blacklisted_rcpts:
+        return 'REJECT Permission denied'
+
+    # If not matched bl/wl list:
+    return 'DUNNO (Not listed in either white/blacklists)'

plugins/sql_alias_access_policy.py

 #   - membersOnly:  Only members are allowed.
 #   - moderatorsOnly:   Only moderators are allowed.
 #   - membersAndModeratorsOnly: Only members and moderators are allowed.
+
+import logging
 from web import sqlquote
 from libs import SMTP_ACTIONS
 
-PLUGIN_NAME = 'sql_alias_access_policy'
+REQUIRE_LOCAL_SENDER_= False
+REQUIRE_LOCAL_RECIPIENT = True
 
 # Policies. MUST be defined in lower case.
 POLICY_PUBLIC = 'public'
 POLICY_ALLOWEDONLY = 'allowedonly'      # Same as @POLICY_MODERATORSONLY
 POLICY_MEMBERSANDMODERATORSONLY = 'membersandmoderatorsonly'
 
-def restriction(dbConn, senderReceiver, smtpSessionData, logger, **kargs):
+def restriction(dbConn, senderReceiver, smtpSessionData, **kargs):
 
     sql = '''SELECT accesspolicy, goto, moderators
             FROM alias
     ''' % (sqlquote(senderReceiver.get('recipient')),
            sqlquote(senderReceiver.get('recipient_domain')),
           )
-    logger.debug('SQL: %s' % sql)
+    logging.debug('SQL: %s' % sql)
 
     dbConn.execute(sql)
     sqlRecord = dbConn.fetchone()
-    logger.debug('SQL Record: %s' % str(sqlRecord))
+    logging.debug('SQL Record: %s' % str(sqlRecord))
 
     # Recipient account doesn't exist.
     if sqlRecord is None:
-        return 'DUNNO Not an alias account.'
+        return 'DUNNO (Not mail alias)'
 
     policy = str(sqlRecord[0]).lower()
 
     members = [str(v.lower()) for v in str(sqlRecord[1]).split(',')]
     moderators = [str(v.lower()) for v in str(sqlRecord[2]).split(',')]
 
-    logger.debug('(%s) policy: %s' % (PLUGIN_NAME, policy))
-    logger.debug('(%s) members: %s' % (PLUGIN_NAME, ', '.join(members)))
-    logger.debug('(%s) moderators: %s' % (PLUGIN_NAME, ', '.join(moderators)))
+    logging.debug('policy: %s' % policy)
+    logging.debug('members: %s' % ', '.join(members))
+    logging.debug('moderators: %s' % ', '.join(moderators))
 
     if not len(policy) > 0:
-        return 'DUNNO No access policy defined.'
+        return 'DUNNO (No access policy)'
 
     if policy == POLICY_PUBLIC:
         # Return if no access policy available or policy is @POLICY_PUBLIC.
             return SMTP_ACTIONS['reject']
     else:
         # Bypass all if policy is not defined in this plugin.
-        return 'DUNNO Policy is not defined in plugin (%s): %s.' % (PLUGIN_NAME, policy)
+        return 'DUNNO (Policy is not defined: %s)' % policy

plugins/sql_user_restrictions.py

 #   - .domain.com:  single domain and its all sub-domains
 #   - user@domain.com:  single email address
 
+import logging
 from web import sqlquote
 from libs import SMTP_ACTIONS
 
-PLUGIN_NAME = 'sql_user_restrictions'
+REQUIRE_LOCAL_SENDER_= False
+REQUIRE_LOCAL_RECIPIENT = False
 
-def restriction(dbConn, senderReceiver, smtpSessionData, logger, **kargs):
+def restriction(dbConn, senderReceiver, smtpSessionData, **kargs):
     # Get restriction rules for sender
     sql = '''
         SELECT
         WHERE username=%s
         LIMIT 1
     ''' % sqlquote(senderReceiver['sender'])
-    logger.debug('SQL to get restriction rules of sender (%s): %s' % (senderReceiver['sender'], sql))
+    logging.debug('SQL to get restriction rules of sender (%s): %s' % (senderReceiver['sender'], sql))
 
     dbConn.execute(sql)
     sql_record = dbConn.fetchone()
-    logger.debug('Returned SQL Record: %s' % str(sql_record))
+    logging.debug('Returned SQL Record: %s' % str(sql_record))
 
     # Sender account exists, perform recipient restrictions
     if sql_record:
 
         # If it does have restrictions
         if not allowed_recipients and not rejected_recipients:
-            logger.debug('No restrictions of sender.')
+            logging.debug('No restrictions of sender.')
         else:
             # Allowed first
             # single recipient, domain, sub-domain, catch-all
             all_allowed_recipients = [s.lower().strip() for s in allowed_recipients.split(',')]
-            logger.debug('All allowed recipient: %s' % str(all_allowed_recipients))
+            logging.debug('All allowed recipient: %s' % str(all_allowed_recipients))
 
             if all_allowed_recipients:
                 if senderReceiver['recipient'] in all_allowed_recipients \
                     return SMTP_ACTIONS['accept']
 
             all_rejected_recipients = [s.lower().strip() for s in rejected_recipients.split(',')]
-            logger.debug('All rejected recipient: %s' % str(all_rejected_recipients))
+            logging.debug('All rejected recipient: %s' % str(all_rejected_recipients))
 
             if all_rejected_recipients:
                 if senderReceiver['recipient'] in all_rejected_recipients \
             WHERE username=%s
             LIMIT 1
         ''' % sqlquote(senderReceiver['recipient'])
-        logger.debug('SQL to get restriction rules of recipient (%s): %s' % (senderReceiver['recipient'], sql))
+        logging.debug('SQL to get restriction rules of recipient (%s): %s' % (senderReceiver['recipient'], sql))
 
         dbConn.execute(sql)
         sql_record = dbConn.fetchone()
-        logger.debug('Returned SQL Record: %s' % str(sql_record))
+        logging.debug('Returned SQL Record: %s' % str(sql_record))
 
     # Recipient account exists, perform sender restrictions
     if sql_record:
 
         # If it does have restrictions
         if not allowed_senders and not rejected_senders:
-            logger.debug('No restrictions of recipient.')
+            logging.debug('No restrictions of recipient.')
         else:
             # Allowed first
             # single recipient, domain, sub-domain, catch-all
             all_allowed_senders = [s.lower().strip() for s in allowed_senders.split(',')]
-            logger.debug('All allowed senders: %s' % str(all_allowed_senders))
+            logging.debug('All allowed senders: %s' % str(all_allowed_senders))
 
             if all_allowed_senders:
                 if senderReceiver['sender'] in all_allowed_senders \
                     return SMTP_ACTIONS['accept']
 
             all_rejected_senders = [s.lower().strip() for s in rejected_senders.split(',')]
-            logger.debug('All rejected senders: %s' % str(all_rejected_senders))
+            logging.debug('All rejected senders: %s' % str(all_rejected_senders))
 
             if all_rejected_senders:
                 if senderReceiver['sender'] in all_rejected_senders \

rc_scripts/iredapd-rr.debian

-#!/usr/bin/env bash
-
-# Author: Zhang Huangbin (zhb@iredmail.org)
-
-### BEGIN INIT INFO
-# Provides:          iredapd 
-# Required-Start:    $network $syslog
-# Required-Stop:     $network $syslog
-# Default-Start:     2 3 4 5
-# Default-Stop:      0 1 6
-# Short-Description: Control iredapd daemon.
-# Description: Control iredapd daemon.
-### END INIT INFO
-
-PROG='iredapd'
-BINPATH='/opt/iredapd/iredapd-rr.py'
-CONFIG='/opt/iredapd/etc/iredapd-rr.ini'
-PIDFILE='/var/run/iredapd-rr.pid'
-
-check_status() {
-    # Usage: check_status pid_number
-    PID="${1}"
-    l=$(ps -p ${PID} | wc -l | awk '{print $1}')
-    if [ X"$l" == X"2" ]; then
-        echo "running"
-    else
-        echo "stopped"
-    fi
-}
-
-start() {
-    if [ -f ${PIDFILE} ]; then
-        PID="$(cat ${PIDFILE})"
-        s="$(check_status ${PID})"
-
-        if [ X"$s" == X"running" ]; then
-            echo "${PROG} is already running."
-        else
-            rm -f ${PIDFILE} >/dev/null 2>&1
-            echo "Starting ${PROG} ..."
-            python ${BINPATH} ${CONFIG}
-        fi
-    else
-        echo "Starting ${PROG} ..."
-        python ${BINPATH} ${CONFIG}
-    fi
-    unset s
-}
-
-stop() {
-    if [ -f ${PIDFILE} ]; then
-        PID="$(cat ${PIDFILE})"
-        s="$(check_status ${PID})"
-
-        if [ X"$s" == X"running" ]; then
-            echo "Stopping ${PROG} ..."
-            kill -9 ${PID}
-            if [ X"$?" == X"0" ]; then
-                rm -f ${PIDFILE} >/dev/null 2>&1
-            else
-                echo -e "\t\t[ FAILED ]"
-            fi
-        else
-            echo "${PROG} is already stopped."
-            rm -f ${PIDFILE} >/dev/null 2>&1
-        fi
-    else
-        echo "${PROG} is already stopped."
-    fi
-    unset s
-}
-
-status() {
-    if [ -f ${PIDFILE} ]; then
-        PID="$(cat ${PIDFILE})"
-        s="$(check_status ${PID})"
-
-        if [ X"$s" == X"running" ]; then
-            echo "${PROG} is running."
-        else
-            echo "${PROG} is stopped."
-        fi
-    else
-        echo "${PROG} is stopped."
-    fi
-    unset s
-}
-
-case "$1" in
-    start) start ;;
-    stop) stop ;;
-    status) status ;;
-    restart) stop && start ;;
-    *)
-        echo $"Usage: $0 {start|stop|restart|status}"
-        RETVAL=1
-        ;;
-esac

rc_scripts/iredapd-rr.freebsd

-#!/usr/local/bin/bash
-
-# Author: Zhang Huangbin (zhb@iredmail.org)
-
-# PROVIDE: iredapd
-# REQUIRE: DAEMON
-# KEYWORD: shutdown
-
-. /etc/rc.subr
-name='iredapd'
-rcvar=`set_rcvar`
-
-BINPATH='/opt/iredapd/iredapd-rr.py'
-CONFIG='/opt/iredapd/etc/iredapd-rr.ini'
-pidfile='/var/run/iredapd-rr.pid'
-
-check_status() {
-    # Usage: check_status pid_number
-    PID="${1}"
-    l=$(ps -p ${PID} | wc -l | awk '{print $1}')
-    if [ X"$l" == X"2" ]; then
-        echo "running"
-    else
-        echo "stopped"
-    fi
-}
-
-start() {
-    if [ -f ${pidfile} ]; then
-        PID="$(cat ${pidfile})"
-        s="$(check_status ${PID})"
-
-        if [ X"$s" == X"running" ]; then
-            echo "${name} is already running."
-        else
-            rm -f ${pidfile} >/dev/null 2>&1
-            echo "Starting ${name} ..."
-            /usr/local/bin/python ${BINPATH} ${CONFIG}
-        fi
-    else
-        echo "Starting ${name} ..."
-        /usr/local/bin/python ${BINPATH} ${CONFIG}
-    fi
-    unset s
-}
-
-stop() {
-    if [ -f ${pidfile} ]; then
-        PID="$(cat ${pidfile})"
-        s="$(check_status ${PID})"
-
-        if [ X"$s" == X"running" ]; then
-            echo "Stopping ${name} ..."
-            kill -9 ${PID}
-            if [ X"$?" == X"0" ]; then
-                rm -f ${pidfile} >/dev/null 2>&1
-            else
-                echo -e "\t\t[ FAILED ]"
-            fi
-        else
-            echo "${name} is already stopped."
-            rm -f ${pidfile} >/dev/null 2>&1
-        fi
-    else
-        echo "${name} is already stopped."
-    fi
-    unset s
-}
-
-status() {
-    if [ -f ${pidfile} ]; then
-        PID="$(cat ${pidfile})"
-        s="$(check_status ${PID})"
-
-        if [ X"$s" == X"running" ]; then
-            echo "${name} is running."
-        else
-            echo "${name} is stopped."
-        fi
-    else
-        echo "${name} is stopped."
-    fi
-    unset s
-}
-
-empty(){
-case "$1" in
-    start) start ;;
-    stop) stop ;;
-    status) status ;;
-    restart) stop && start ;;
-    *)
-        echo $"Usage: $0 {start|stop|restart|status}"
-        RETVAL=1
-        ;;
-esac
-}
-
-start_cmd="start"
-stop_cmd="stop"
-status_cmd="status"
-restart_cmd="stop && start"
-
-command="start"
-load_rc_config ${name}
-run_rc_command "$1"

rc_scripts/iredapd-rr.openbsd

-#!/bin/sh
-
-daemon='python /opt/iredapd/iredapd-rr.py /opt/iredapd/etc/iredapd-rr.ini'
-
-. /etc/rc.d/rc.subr
-
-rc_reload=NO
-rc_cmd $1

rc_scripts/iredapd-rr.opensuse

-#!/usr/bin/env bash
-
-# Author: Zhang Huangbin (zhb@iredmail.org)
-
-### BEGIN INIT INFO
-# Provides:             iredapd
-# Required-Start:       network
-# Should-Start:         mysql ldap
-# Required-Stop:        mysql ldap
-# Should-Stop:          isdn openibd SuSEfirewall2_init
-# Default-Start:        2 3 5
-# Default-Stop:
-# Short-Description:    Postfix policy daemon.
-# Description:          iRedAPD is a small postfix policy daemon.
-### END INIT INFO
-
-PROG='iredapd'
-BINPATH='/opt/iredapd/iredapd-rr.py'
-CONFIG='/opt/iredapd/etc/iredapd-rr.ini'
-PIDFILE='/var/run/iredapd-rr.pid'
-
-check_status() {
-    # Usage: check_status pid_number
-    PID="${1}"
-    l=$(ps -p ${PID} | wc -l | awk '{print $1}')
-    if [ X"$l" == X"2" ]; then
-        echo "running"
-    else
-        echo "stopped"
-    fi
-}
-
-start() {
-    if [ -f ${PIDFILE} ]; then
-        PID="$(cat ${PIDFILE})"
-        s="$(check_status ${PID})"
-
-        if [ X"$s" == X"running" ]; then
-            echo "${PROG} is already running."
-        else
-            rm -f ${PIDFILE} >/dev/null 2>&1
-            echo "Starting ${PROG} ..."
-            python ${BINPATH} ${CONFIG}
-        fi
-    else
-        echo "Starting ${PROG} ..."
-        python ${BINPATH} ${CONFIG}
-    fi
-    unset s
-}
-
-stop() {
-    if [ -f ${PIDFILE} ]; then
-        PID="$(cat ${PIDFILE})"
-        s="$(check_status ${PID})"
-
-        if [ X"$s" == X"running" ]; then
-            echo "Stopping ${PROG} ..."
-            kill -9 ${PID}
-            if [ X"$?" == X"0" ]; then
-                rm -f ${PIDFILE} >/dev/null 2>&1
-            else
-                echo -e "\t\t[ FAILED ]"
-            fi
-        else
-            echo "${PROG} is already stopped."
-            rm -f ${PIDFILE} >/dev/null 2>&1
-        fi
-    else
-        echo "${PROG} is already stopped."
-    fi
-    unset s
-}
-
-status() {
-    if [ -f ${PIDFILE} ]; then
-        PID="$(cat ${PIDFILE})"
-        s="$(check_status ${PID})"
-
-        if [ X"$s" == X"running" ]; then
-            echo "${PROG} is running."
-        else
-            echo "${PROG} is stopped."
-        fi
-    else
-        echo "${PROG} is stopped."
-    fi
-    unset s
-}
-