diff --git a/core/admin/mailu/configuration.py b/core/admin/mailu/configuration.py index 7dcd7c3a..66b0b832 100644 --- a/core/admin/mailu/configuration.py +++ b/core/admin/mailu/configuration.py @@ -67,6 +67,7 @@ DEFAULT_CONFIG = { 'HOST_REDIS': 'redis', 'HOST_FRONT': 'front', 'SUBNET': '192.168.203.0/24', + 'SUBNET6': None, 'POD_ADDRESS_RANGE': None } diff --git a/core/admin/mailu/internal/views/dovecot.py b/core/admin/mailu/internal/views/dovecot.py index f44f59bc..9c665977 100644 --- a/core/admin/mailu/internal/views/dovecot.py +++ b/core/admin/mailu/internal/views/dovecot.py @@ -11,6 +11,8 @@ def dovecot_passdb_dict(user_email): user = models.User.query.get(user_email) or flask.abort(404) allow_nets = [] allow_nets.append(app.config["SUBNET"]) + if app.config["SUBNET6"]: + allow_nets.append(app.config["SUBNET6"]) if app.config["POD_ADDRESS_RANGE"]: allow_nets.append(app.config["POD_ADDRESS_RANGE"]) return flask.jsonify({ diff --git a/core/admin/mailu/internal/views/postfix.py b/core/admin/mailu/internal/views/postfix.py index 76d33fca..a5507830 100644 --- a/core/admin/mailu/internal/views/postfix.py +++ b/core/admin/mailu/internal/views/postfix.py @@ -37,7 +37,11 @@ def postfix_transport(email): return flask.abort(404) localpart, domain_name = models.Email.resolve_domain(email) relay = models.Relay.query.get(domain_name) or flask.abort(404) - return flask.jsonify("smtp:[{}]".format(relay.smtp)) + ret = "smtp:[{0}]".format(relay.smtp) + if ":" in relay.smtp: + split = relay.smtp.split(':') + ret = "smtp:[{0}]:{1}".format(split[0], split[1]) + return flask.jsonify(ret) @internal.route("/postfix/recipient/map/") diff --git a/core/admin/requirements-prod.txt b/core/admin/requirements-prod.txt index 1fcffbcd..e04089c4 100644 --- a/core/admin/requirements-prod.txt +++ b/core/admin/requirements-prod.txt @@ -44,7 +44,7 @@ SQLAlchemy==1.3.3 srslib==0.1.4 tabulate==0.8.3 tenacity==5.0.4 -validators==0.12.5 +validators==0.12.6 visitor==0.1.3 Werkzeug==0.15.3 WTForms==2.2.1 diff --git a/optional/fetchmail/fetchmail.py b/optional/fetchmail/fetchmail.py index f3ee6a70..fb0c6f04 100755 --- a/optional/fetchmail/fetchmail.py +++ b/optional/fetchmail/fetchmail.py @@ -8,6 +8,7 @@ import subprocess import re import requests import sys +import traceback FETCHMAIL = """ @@ -45,47 +46,50 @@ def fetchmail(fetchmailrc): def run(debug): - fetches = requests.get("http://admin/internal/fetch").json() - smtphost, smtpport = extract_host_port(os.environ.get("HOST_SMTP", "smtp"), None) - if smtpport is None: - smtphostport = smtphost - else: - smtphostport = "%s/%d" % (smtphost, smtpport) - for fetch in fetches: - fetchmailrc = "" - options = "options antispam 501, 504, 550, 553, 554" - options += " sslmode wrapped" if fetch["tls"] else "" - options += " keep" if fetch["keep"] else " fetchall" - fetchmailrc += RC_LINE.format( - user_email=escape_rc_string(fetch["user_email"]), - protocol=fetch["protocol"], - host=escape_rc_string(fetch["host"]), - port=fetch["port"], - smtphost=smtphostport, - username=escape_rc_string(fetch["username"]), - password=escape_rc_string(fetch["password"]), - options=options - ) - if debug: - print(fetchmailrc) - try: - print(fetchmail(fetchmailrc)) - error_message = "" - except subprocess.CalledProcessError as error: - error_message = error.output.decode("utf8") - # No mail is not an error - if not error_message.startswith("fetchmail: No mail"): - print(error_message) - user_info = "for %s at %s" % (fetch["user_email"], fetch["host"]) - # Number of messages seen is not a error as well - if ("messages" in error_message and - "(seen " in error_message and - user_info in error_message): - print(error_message) - finally: - requests.post("http://admin/internal/fetch/{}".format(fetch["id"]), - json=error_message.split("\n")[0] + try: + fetches = requests.get("http://admin/internal/fetch").json() + smtphost, smtpport = extract_host_port(os.environ.get("HOST_SMTP", "smtp"), None) + if smtpport is None: + smtphostport = smtphost + else: + smtphostport = "%s/%d" % (smtphost, smtpport) + for fetch in fetches: + fetchmailrc = "" + options = "options antispam 501, 504, 550, 553, 554" + options += " sslmode wrapped" if fetch["tls"] else "" + options += " keep" if fetch["keep"] else " fetchall" + fetchmailrc += RC_LINE.format( + user_email=escape_rc_string(fetch["user_email"]), + protocol=fetch["protocol"], + host=escape_rc_string(fetch["host"]), + port=fetch["port"], + smtphost=smtphostport, + username=escape_rc_string(fetch["username"]), + password=escape_rc_string(fetch["password"]), + options=options ) + if debug: + print(fetchmailrc) + try: + print(fetchmail(fetchmailrc)) + error_message = "" + except subprocess.CalledProcessError as error: + error_message = error.output.decode("utf8") + # No mail is not an error + if not error_message.startswith("fetchmail: No mail"): + print(error_message) + user_info = "for %s at %s" % (fetch["user_email"], fetch["host"]) + # Number of messages seen is not a error as well + if ("messages" in error_message and + "(seen " in error_message and + user_info in error_message): + print(error_message) + finally: + requests.post("http://admin/internal/fetch/{}".format(fetch["id"]), + json=error_message.split("\n")[0] + ) + except Exception: + traceback.print_exc() if __name__ == "__main__": diff --git a/towncrier/newsfragments/1357.feature b/towncrier/newsfragments/1357.feature new file mode 100644 index 00000000..56fa443f --- /dev/null +++ b/towncrier/newsfragments/1357.feature @@ -0,0 +1 @@ +Relay a domain to a nonstandard SMTP port by adding ":" to the remote hostname or IP address. \ No newline at end of file