From 5179cf06182683f39f0144bc1c44d8360fba3e49 Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Thu, 28 Jul 2022 16:20:01 +0200 Subject: [PATCH 1/2] Fix localpart splitting and make code more readable. --- core/admin/mailu/models.py | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/core/admin/mailu/models.py b/core/admin/mailu/models.py index 08738600..70a9528f 100644 --- a/core/admin/mailu/models.py +++ b/core/admin/mailu/models.py @@ -439,10 +439,15 @@ class Email(object): localpart_stripped = None stripped_alias = None - delim = os.environ.get('RECIPIENT_DELIMITER') - if delim in localpart: - localpart_stripped = localpart.rsplit(delim, 1)[0] + if delims := os.environ.get('RECIPIENT_DELIMITER'): + try: + pos = next(i for i, c in enumerate(localpart) if c in delims) + except StopIteration: + pass + else: + localpart_stripped = localpart[:pos] + # is localpart@domain_name or localpart_stripped@domain_name an user? user = User.query.get(f'{localpart}@{domain_name}') if not user and localpart_stripped: user = User.query.get(f'{localpart_stripped}@{domain_name}') @@ -450,19 +455,18 @@ class Email(object): if user: email = f'{localpart}@{domain_name}' - if user.forward_enabled: - destination = user.forward_destination - if user.forward_keep or ignore_forward_keep: - destination.append(email) - else: - destination = [email] + if not user.forward_enabled: + return [email] + destination = user.forward_destination + if user.forward_keep or ignore_forward_keep: + destination.append(email) return destination - pure_alias = Alias.resolve(localpart, domain_name) - - if pure_alias and not pure_alias.wildcard: - return pure_alias.destination + # is localpart, domain_name or localpart_stripped@domain_name an alias? + if pure_alias := Alias.resolve(localpart, domain_name): + if not pure_alias.wildcard: + return pure_alias.destination if stripped_alias := Alias.resolve(localpart_stripped, domain_name): return stripped_alias.destination From c478e26d689ca6868eb8f13c78c4b8282f62a7fe Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Thu, 28 Jul 2022 16:21:34 +0200 Subject: [PATCH 2/2] Encode domain part of email addresses before returning. --- core/admin/mailu/internal/views/postfix.py | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/core/admin/mailu/internal/views/postfix.py b/core/admin/mailu/internal/views/postfix.py index 3482b290..f8346bb1 100644 --- a/core/admin/mailu/internal/views/postfix.py +++ b/core/admin/mailu/internal/views/postfix.py @@ -33,8 +33,9 @@ def postfix_alias_map(alias): localpart, domain_name = models.Email.resolve_domain(alias) if localpart is None: return flask.jsonify(domain_name) - destination = models.Email.resolve_destination(localpart, domain_name) - return flask.jsonify(",".join(destination)) if destination else flask.abort(404) + if destinations := models.Email.resolve_destination(localpart, domain_name): + return flask.jsonify(",".join(idna_encode(destinations))) + return flask.abort(404) @internal.route("/postfix/transport/") def postfix_transport(email): @@ -142,9 +143,11 @@ def postfix_sender_login(sender): if localpart is None: return flask.jsonify(",".join(wildcard_senders)) if wildcard_senders else flask.abort(404) localpart = localpart[:next((i for i, ch in enumerate(localpart) if ch in flask.current_app.config.get('RECIPIENT_DELIMITER')), None)] - destination = models.Email.resolve_destination(localpart, domain_name, True) - destination = [*destination, *wildcard_senders] if destination else [*wildcard_senders] - return flask.jsonify(",".join(destination)) if destination else flask.abort(404) + destinations = models.Email.resolve_destination(localpart, domain_name, True) or [] + destinations.extend(wildcard_senders) + if destinations: + return flask.jsonify(",".join(idna_encode(destinations))) + return flask.abort(404) @internal.route("/postfix/sender/rate/") def postfix_sender_rate(sender): @@ -169,3 +172,11 @@ def postfix_sender_access(sender): except sqlalchemy.exc.StatementError: pass return flask.abort(404) + +# idna encode domain part of each address in list of addresses +def idna_encode(addresses): + return [ + f"{localpart}@{idna.encode(domain).decode('ascii')}" + for (localpart, domain) in + (address.rsplit("@", 1) for address in addresses) + ]