Exemple #1
0
def get_existing_maillists(domains=None, conn=None):
    """Get existing mailing lists.

    :param domains: a list/tuple/set of valid domain names.
                    Used if you want to get mailing lists under given domains.
    :param conn: sql connection cursor.
    """
    if domains:
        domains = [str(d).lower() for d in domains if utils.is_domain(d)]

    if not conn:
        _wrap = LDAPWrap()
        conn = _wrap.conn

    _filter = '(&(objectClass=mailList)(enabledService=mlmmj))'
    if domains:
        _f = '(|'
        for d in domains:
            _f += '(mail=*@%s)(shadowAddress=*@%s)' % (d, d)
        _f += ')'
        _filter = '(&' + _filter + _f + ')'

    existing_lists = set()
    try:
        qr = conn.search_s(settings.iredmail_ldap_basedn, ldap.SCOPE_SUBTREE,
                           _filter, ['mail'])

        for (_dn, _ldif) in qr:
            _addresses = _ldif.get('mail', [])
            _addresses = [str(i).lower() for i in _addresses]
            existing_lists.update(_addresses)

        return (True, existing_lists)
    except Exception, e:
        return (False, repr(e))
Exemple #2
0
def __get_primary_and_alias_domains(domain, with_primary_domain=True, conn=None):
    """Get list of domainName and domainAliasName by quering domainName.

    >>> __get_primary_and_alias_domains(domain='example.com')
    (True, ['example.com', 'aliasdomain01.com', 'aliasdomain02.com', ...])
    """
    domain = domain.strip().lower()
    if not utils.is_domain(domain):
        return (False, 'INVALID_DOMAIN_NAME')

    try:
        if not conn:
            _wrap = LDAPWrap()
            conn = _wrap.conn

        dn = 'domainName=%s,%s' % (domain, settings.iredmail_ldap_basedn)
        qr = conn.search_s(dn,
                           ldap.SCOPE_BASE,
                           '(&(objectClass=mailDomain)(domainName=%s))' % domain,
                           ['domainName', 'domainAliasName'])

        if qr:
            (_dn, _ldif) = qr[0]
            _ldif = utils.bytes2str(_ldif)
            all_domains = _ldif.get('domainName', []) + _ldif.get('domainAliasName', [])
            if not with_primary_domain:
                all_domains.remove(domain)
            return (True, all_domains)
        else:
            return (False, 'INVALID_DOMAIN_NAME')
    except Exception as e:
        return (False, repr(e))
Exemple #3
0
def get_existing_maillists(domains=None, conn=None):
    """Get existing mailing lists.

    :param domains: a list/tuple/set of valid domain names.
                    Used if you want to get mailing lists under given domains.
    :param conn: sql connection cursor.
    """
    if domains:
        domains = [str(d).lower() for d in domains if utils.is_domain(d)]

    if not conn:
        _wrap = SQLWrap()
        conn = _wrap.conn

    existing_lists = set()
    try:
        if domains:
            qr = conn.select('maillists',
                             vars={'domains': domains},
                             what='address',
                             where='domain IN $domains',
                             group='address')
        else:
            qr = conn.select('maillists',
                             what='address',
                             group='address')

        for i in qr:
            _addr = str(i.address).lower()
            existing_lists.add(_addr)

        return (True, list(existing_lists))
    except Exception as e:
        return (False, repr(e))
Exemple #4
0
def get_alias_target_domain(alias_domain, conn, include_backupmx=True):
    """Query target domain of given alias domain name."""
    alias_domain = str(alias_domain).lower()

    if not utils.is_domain(alias_domain):
        logger.debug("Given alias_domain {0} is not an valid domain name.".format(alias_domain))
        return None

    try:
        _filter = '(&(objectClass=mailDomain)(accountStatus=active)'
        _filter += '(domainAliasName=%s)' % alias_domain

        if not include_backupmx:
            _filter += '(!(domainBackupMX=yes))'

        _filter += ')'

        logger.debug("[LDAP] query target domain of given alias domain: {0}\n"
                     "[LDAP] query filter: {1}".format(alias_domain, _filter))
        qr = conn.search_s(settings.ldap_basedn,
                           1,   # 1 == ldap.SCOPE_ONELEVEL
                           _filter,
                           ['domainName'])

        logger.debug("result: {0}".format(repr(qr)))
        if qr:
            (_dn, _ldif) = qr[0]
            _domain = _ldif['domainName'][0]
            return _domain
    except ldap.NO_SUCH_OBJECT:
        pass
    except Exception as e:
        logger.error("<!> Error while querying alias domain: {0}".format(repr(e)))

    return None
Exemple #5
0
def get_primary_and_alias_domains(conn, domain):
    """Query LDAP to get all available alias domain names of given domain.

    Return list of alias domain names.

    @conn -- ldap connection cursor
    @domain -- domain name
    """
    if not utils.is_domain(domain):
        return []

    try:
        _f = "(&(objectClass=mailDomain)(|(domainName=%s)(domainAliasName=%s)))" % (
            domain, domain)
        qr = conn.search_s(
            settings.ldap_basedn,
            1,  # 1 == ldap.SCOPE_ONELEVEL
            _f,
            ['domainName', 'domainAliasName'])
        if qr:
            (_dn, _ldif) = qr[0]
            _all_domains = _ldif.get('domainName', []) + _ldif.get(
                'domainAliasName', [])

            return list(set(_all_domains))
    except Exception as e:
        # Log and return if LDAP error occurs
        logger.error('Error while querying alias domains of domain (%s): %s' %
                     (domain, repr(e)))
        return []
Exemple #6
0
def is_local_domain(conn,
                    domain,
                    include_alias_domain=True,
                    include_backupmx=True):
    if not utils.is_domain(domain):
        return False

    if utils.is_server_hostname(domain):
        return True

    try:
        _filter = '(&(objectClass=mailDomain)(accountStatus=active)'

        if include_alias_domain:
            _filter += '(|(domainName=%s)(domainAliasName=%s))' % (domain, domain)
        else:
            _filter += '(domainName=%s)' % domain

        if not include_backupmx:
            _filter += '(!(domainBackupMX=yes))'

        _filter += ')'

        qr = conn.search_s(settings.ldap_basedn,
                           1,   # 1 == ldap.SCOPE_ONELEVEL
                           _filter,
                           ['dn'])
        if qr:
            return True
    except ldap.NO_SUCH_OBJECT:
        return False
    except Exception as e:
        logger.error("<!> Error while querying local domain: {0}".format(repr(e)))
        return False
Exemple #7
0
def get_alias_target_domain(alias_domain, conn):
    """Query target domain of given alias domain name."""
    alias_domain = str(alias_domain).lower()
    if not utils.is_domain(alias_domain):
        logger.debug(
            "Given alias domain ({}) is not a valid domain name.".format(
                alias_domain))
        return None

    sql = """SELECT alias_domain.target_domain
               FROM alias_domain, domain
              WHERE domain.active=1
                    AND domain.domain=alias_domain.target_domain
                    AND alias_domain.alias_domain=%s
              LIMIT 1""" % sqlquote(alias_domain)

    logger.debug(
        "[SQL] query target domain of given alias domain ({}): \n{}".format(
            alias_domain, repr(sql)))

    qr = conn.execute(sql)
    sql_record = qr.fetchone()
    logger.debug("[SQL] query result: {}".format(repr(sql_record)))

    if sql_record:
        target_domain = str(sql_record[0]).lower()
        return target_domain
    else:
        return None
Exemple #8
0
def is_domain_exists(domain, conn=None):
    # Return True if account is invalid or exist.
    domain = str(domain).strip().lower()

    if not utils.is_domain(domain):
        # Return False if invalid.
        return False

    if not conn:
        _wrap = LDAPWrap()
        conn = _wrap.conn

    query_filter = '(&'
    query_filter += '(objectClass=mailDomain)'
    query_filter += '(|(domainName=%s)(domainAliasName=%s))' % (domain, domain)
    query_filter += ')'

    # Check domainName and domainAliasName.
    try:
        qr = conn.search_s(settings.iredmail_ldap_basedn,
                           ldap.SCOPE_ONELEVEL,
                           query_filter,
                           ['dn'])

        if qr:
            # Domain name exist.
            return True
        else:
            return False
    except:
        # Account 'EXISTS' (fake) if lookup failed.
        return True
Exemple #9
0
def get_existing_maillists(domains=None, *args, **kw):
    """Get existing mailing lists.

    :param domains: a list/tuple/set of valid domain names.
                    Used if you want to get mailing lists under given domains.
    """
    if domains:
        domains = [str(d).lower() for d in domains if utils.is_domain(d)]

    # Get all directories which store mailing list accounts.
    parent_dirs = []

    if domains:
        for d in domains:
            _dir = os.path.join(settings.MLMMJ_SPOOL_DIR, d)
            parent_dirs.append(_dir)
    else:
        try:
            fns = os.listdir(settings.MLMMJ_SPOOL_DIR)
        except Exception, e:
            return (False, repr(e))

        for fn in fns:
            _dir = os.path.join(settings.MLMMJ_SPOOL_DIR, fn)
            parent_dirs.append(_dir)
Exemple #10
0
def get_existing_maillists(domains=None, *args, **kw):
    """Get existing mailing lists.

    :param domains: a list/tuple/set of valid domain names.
                    Used if you want to get mailing lists under given domains.
    """
    if domains:
        domains = [str(d).lower() for d in domains if utils.is_domain(d)]

    # Get all directories which store mailing list accounts.
    parent_dirs = []

    if domains:
        for d in domains:
            _dir = os.path.join(settings.MLMMJ_SPOOL_DIR, d)
            parent_dirs.append(_dir)
    else:
        try:
            fns = os.listdir(settings.MLMMJ_SPOOL_DIR)

            # Remove names which starts with a dot.
            fns = [i for i in fns if not i.startswith('.')]
        except OSError:
            # No such directory.
            return (True, [])
        except Exception as e:
            return (False, repr(e))

        for fn in fns:
            _dir = os.path.join(settings.MLMMJ_SPOOL_DIR, fn)

            # Remove path which is not a directory
            if not os.path.isdir(_dir):
                continue

            parent_dirs.append(_dir)

    # Get all mailing lists.
    all_lists = []

    # List all directories
    for d in parent_dirs:
        try:
            fns = os.listdir(d)

            # Construct email address
            for fn in fns:
                mail = fn + '@' + os.path.basename(d)
                all_lists.append(mail)
        except OSError:
            # No such directory.
            pass
        except Exception as e:
            return (False, repr(e))

    all_lists.sort()

    return (True, all_lists)
Exemple #11
0
def __ldif_ml(mail,
              mlid,
              name=None,
              access_policy=None,
              max_message_size=None,
              alias_domains=None,
              domain_status=None,
              moderators=None):
    """Generate LDIF (a dict) for a new (mlmmj) mailing list account.

    :param mail: mail address of new (mlmmj) mailing list account
    :param mlid: a server-wide unique id for each (mlmmj) mailing list
    :param name: short description of mailing list
    :param access_policy: access policy of mailing list
    :param alias_domains: a list/tuple/set of alias domains
    :param domain_status: status of primary domain: active, disabled
    :param moderators: a list/tuple/set of moderator email addresses
    """
    mail = str(mail).lower()
    listname, domain = mail.split('@', 1)
    transport = '%s:%s/%s' % (settings.MTA_TRANSPORT_NAME, domain, listname)

    ldif = __attrs_ldif({
        'objectClass': 'mailList',
        'mail': mail,
        'mtaTransport': transport,
        'mailingListID': mlid,
        'accountStatus': 'active',
        'enabledService': ['mail', 'deliver', 'mlmmj'],
    })

    if name:
        ldif += __attr_ldif('cn', name)

    if access_policy:
        p = str(access_policy).lower()
        ldif += __attr_ldif('accessPolicy', p)

    if max_message_size and isinstance(max_message_size, int):
        ldif += __attr_ldif('maxMessageSize', max_message_size)

    if alias_domains:
        alias_domains = [str(d).lower() for d in alias_domains if utils.is_domain(d)]
        shadow_addresses = [listname + '@' + d for d in alias_domains]

        if shadow_addresses:
            ldif += __attr_ldif('shadowAddress', shadow_addresses)

    if domain_status != 'active':
        ldif += __attr_ldif('domainStatus', 'disabled')

    if moderators:
        _addresses = [str(i).strip().lower() for i in moderators if utils.is_email(i)]
        if _addresses:
            ldif += __attr_ldif('listAllowedUser', _addresses)

    return ldif
Exemple #12
0
def add_whitelist_domain(conn, domain):
    # Insert domain into sql table `iredapd.greylisting_whitelist_domains`
    if not utils.is_domain(domain):
        return (False, 'INVALID_DOMAIN')

    try:
        sql = """INSERT INTO greylisting_whitelist_domains (domain) VALUES ('%s')""" % domain
        conn.execute(sql)
    except Exception as e:
        error = str(e).lower()
        if 'duplicate key' in error or 'duplicate entry' in error:
            pass
        else:
            return (False, str(e))

    return (True, )
Exemple #13
0
def query_mx(domains, queried_domains=None, returned_ips=None):
    """
    Return a list of IP addresses/networks defined in MX record of mail domain
    names.

    @domains - a list/tuple/set of mail domain names
    @queried_domains - a set of mail domain names which already queried spf
    @returned_ips - a set of IP addr/networks of queried mail domain names
    """
    ips = set()

    queried_domains = queried_domains or set()
    returned_ips = returned_ips or set()

    hostnames = set()

    domains = [d for d in domains if d not in queried_domains]
    for domain in domains:
        try:
            qr = resv.query(domain, 'MX')
            if qr:
                for r in qr:
                    hostname = str(r).split()[-1].rstrip('.')
                    logger.debug('[SPF][{0}] MX: {1}'.format(domain, hostname))
                    if utils.is_domain(hostname):
                        hostnames.add(hostname)

            if hostnames:
                qr = query_a(domains=hostnames,
                             queried_domains=queried_domains,
                             returned_ips=returned_ips)

                ips_a = qr['ips']
                queried_domains = qr['queried_domains']
                returned_ips = qr['returned_ips']

                ips.update(ips_a)

            queried_domains.add('mx:' + domain)
        except:
            pass

    return {
        'ips': ips,
        'queried_domains': queried_domains,
        'returned_ips': returned_ips
    }
Exemple #14
0
def remove_whitelisted_domain(domain, conn):
    # Insert domain into sql table `iredapd.greylisting_whitelist_domains`
    if not utils.is_domain(domain):
        return (False, 'INVALID_DOMAIN')

    try:
        sql = """DELETE FROM greylisting_whitelist_domains WHERE domain='%s'""" % domain
        conn.execute(sql)

        sql = """DELETE FROM greylisting_whitelists WHERE COMMENT='AUTO-UPDATE: %s'""" % domain
        conn.execute(sql)
    except Exception as e:
        error = str(e).lower()
        if 'duplicate key' in error or 'duplicate entry' in error:
            pass
        else:
            return (False, str(e))

    return (True, )
Exemple #15
0
def is_domain_exists(domain, conn=None):
    # Return True if account is invalid or exist.
    if not utils.is_domain(domain):
        return True

    if not conn:
        _wrap = SQLWrap()
        conn = _wrap.conn

    sql_vars = {'domain': domain}

    try:
        # Query normal mail domain
        qr = conn.select('domain',
                         vars=sql_vars,
                         what='domain',
                         where='domain=$domain',
                         limit=1)

        if qr:
            return True

        # Query alias domain
        qr = conn.select('alias_domain',
                         vars=sql_vars,
                         what='alias_domain',
                         where='alias_domain=$domain',
                         limit=1)

        if qr:
            return True

        # Domain not exist
        return False
    except Exception as e:
        # Return True as exist to not allow to create new domain/account.
        logger.error("SQL error: {0}".format(e))
        return True
Exemple #16
0
    elif backend == "ldap":
        _filter = "(&(objectClass=mailList)(accountStatus=active)(enabledService=mlmmj))"
        try:
            qr = conn.search_s(settings.iredmail_ldap_basedn,
                               ldap.SCOPE_SUBTREE, _filter, ["mail"])
            for (_dn, _ldif) in qr:
                mls += [bytes2str(i).lower() for i in _ldif["mail"]]
        except Exception as e:
            print("Error while querying: {}".format(repr(e)))

    if not mls:
        print("No mailing list found. Abort.")
        sys.exit()
else:
    # Get domain names and mailing lists.
    domains = [i.lower() for i in args if is_domain(i)]
    mls = [i.lower() for i in args if is_email(i)]

    # Query SQL/LDAP to get all mailing lists under specified domains.
    if domains:
        print("Querying mailing lists under given domain(s): {}".format(
            ", ".join(domains)))

        if backend == "sql":
            qr = conn.select("maillists",
                             vars={'domains': domains},
                             what="address",
                             where="domain IN $domains AND active=1")
            for i in qr:
                addr = i["address"].lower()
                mls.append(addr)
Exemple #17
0
        quotas_ids.append(_id)

        _name = rcd.name
        _period = rcd.period

        if _name.startswith('inbound_'):
            _account = _name.split('inbound_', 1)[-1]
            inout_type = 'inbound'
        else:
            _account = _name.split('outbound_', 1)[-1]
            inout_type = 'outbound'

        priority = ACCOUNT_PRIORITIES['catchall']
        if is_email(_account):
            priority = ACCOUNT_PRIORITIES['email']
        elif is_domain(_account):
            _account = '@' + _account
            priority = ACCOUNT_PRIORITIES['domain']

        t_settings[_id] = {'account': _account,
                           'inout_type': inout_type,
                           'period': _period,
                           'priority': priority}

if not quotas_ids:
    sys.exit('No throttle settings found. Exit.')

# Get detailed throttle settings.
qr = conn.select('quotas_limits',
                 vars={'quotas_ids': quotas_ids},
                 what='quotasid, type, counterlimit',
Exemple #18
0
    sys.argv.remove('--submit')

conn = get_db_conn('iredapd')

if len(sys.argv) == 1:
    logger.info('* Query SQL server to get mail domain names.')

    domains = []

    qr = conn.select('greylisting_whitelist_domains', what='domain')
    for r in qr:
        domains.append(r.domain)
else:
    domains = sys.argv[1:]

domains = [str(d).lower() for d in domains if utils.is_domain(d)]
if not domains:
    logger.info('* No valid domain names. Abort.')
    sys.exit()

logger.info("* {} mail domains in total.".format(len(domains)))

all_ips = set()
domain_ips = {}
queried_domains = set()
returned_ips = set()

for domain in domains:
    if 'spf:' + domain in queried_domains:
        continue
Exemple #19
0
def add_maillist(mail, form, conn=None):
    """Add required LDAP object to add a mailing list account."""
    mail = str(mail).lower()
    (_, domain) = mail.split('@', 1)

    if not utils.is_email(mail):
        return (False, 'INVALID_EMAIL')

    if not conn:
        _wrap = LDAPWrap()
        conn = _wrap.conn

    if not is_domain_exists(domain=domain):
        return (False, 'NO_SUCH_DOMAIN')

    if is_email_exists(mail=mail):
        return (False, 'ALREADY_EXISTS')

    name = form.get('name', '')
    mlid = __get_new_mlid(conn=conn)

    domain_status = 'disabled'
    alias_domains = []

    dn_domain = 'domainName=%s,%s' % (domain, settings.iredmail_ldap_basedn)
    # Get domain profile.
    try:
        qr = conn.search_s(dn_domain,
                           ldap.SCOPE_BASE,
                           '(objectClass=*)')

        if qr:
            (_dn, _ldif) = qr[0]
            _ldif = utils.bytes2str(_ldif)
            domain_status = _ldif.get('accountStatus', ['active'])[0]

            alias_domains = _ldif.get('domainAliasName', [])
            alias_domains = [str(i).lower() for i in alias_domains if utils.is_domain(i)]
            alias_domains = list(set(alias_domains))

        if 'only_moderator_can_post' in form:
            access_policy = 'moderatorsonly'
        elif 'only_subscriber_can_post' in form:
            access_policy = 'moderatorsonly'
        else:
            access_policy = None

        max_message_size = form_utils.get_max_message_size(form)

        moderators = form.get('moderators', '').split(',')

        dn_ml = 'mail=%s,ou=Groups,domainName=%s,%s' % (mail, domain, settings.iredmail_ldap_basedn)
        ldif_ml = __ldif_ml(mail=mail,
                            mlid=mlid,
                            name=name,
                            access_policy=access_policy,
                            max_message_size=max_message_size,
                            alias_domains=alias_domains,
                            domain_status=domain_status,
                            moderators=moderators)

        conn.add_s(dn_ml, ldif_ml)
        logger.info('Created: {0}.'.format(mail))
        return (True,)
    except Exception as e:
        logger.error('Error while creating {0}: {1}'.format(mail, e))
        return (False, repr(e))
Exemple #20
0
def is_local_domain(conn,
                    domain,
                    include_alias_domain=True,
                    include_backupmx=True):
    """Check whether given domain name is hosted on localhost and not disabled.

    @conn -- SQL connection cursor
    @domain -- a domain name
    @include_backupmx -- whether we should include backup mx domain names in
                         query result.
    """
    if not utils.is_domain(domain):
        return False

    if utils.is_server_hostname(domain):
        return True

    sql_quote_domain = sqlquote(domain)
    try:
        # include backup mx domains by default.
        sql_backupmx = ''
        if not include_backupmx:
            sql_backupmx = 'AND backupmx=0'

        sql = """SELECT domain
                   FROM domain
                  WHERE domain=%s AND active=1 %s
                  LIMIT 1""" % (sql_quote_domain, sql_backupmx)
        logger.debug("[SQL] query local domain ({}): \n{}".format(domain, sql))

        qr = conn.execute(sql)
        sql_record = qr.fetchone()
        logger.debug("SQL query result: {}".format(repr(sql_record)))

        if sql_record:
            return True
    except Exception as e:
        logger.error("<!> Error while querying domain: {}".format(repr(e)))

    # Query alias domain
    try:
        if include_alias_domain:
            sql = """SELECT alias_domain.alias_domain
                       FROM alias_domain, domain
                      WHERE domain.active=1
                            AND domain.domain=alias_domain.target_domain
                            AND alias_domain.alias_domain=%s
                      LIMIT 1""" % sql_quote_domain

            logger.debug("[SQL] query alias domain ({}): \n{}".format(
                domain, repr(sql)))

            qr = conn.execute(sql)
            sql_record = qr.fetchone()
            logger.debug("[SQL] query result: {}".format(repr(sql_record)))

            if sql_record:
                return True
    except Exception as e:
        logger.error("<!> Error while querying alias domain: {}".format(
            repr(e)))

    return False