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))
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))
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))
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
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 []
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
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
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
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)
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)
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
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, )
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 }
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, )
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
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)
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',
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
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))
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