From e22324adcd56f145cb55d0e2545471f30a39b9e1 Mon Sep 17 00:00:00 2001 From: Dario Ernst Date: Sun, 14 Apr 2019 11:37:09 +0000 Subject: [PATCH] Make aliases case-insensitive (too) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Even though RFC5321 2.4 explains that local-parts are to be case-sensitive, this does not seem to be how EMail is used today. Thus, instead of reverting user-emails back to being case sensitive, let’s make aliases case-insensitive too. Not only more consistent, this also allows users to enjoy receiving EMails from large airlines or car-rental agencies onto their already existing aliases. For the rare case of case sensitive aliases existing, let’s query for the forced-lowercase alias only in the event that the preserved-case one isn’t found … closes #867 --- core/admin/mailu/models.py | 39 +++++++++++++++++++++--------- towncrier/newsfragments/867.bugfix | 1 + 2 files changed, 29 insertions(+), 11 deletions(-) create mode 100644 towncrier/newsfragments/867.bugfix diff --git a/core/admin/mailu/models.py b/core/admin/mailu/models.py index 8bfb12fb..95dc16a6 100644 --- a/core/admin/mailu/models.py +++ b/core/admin/mailu/models.py @@ -436,20 +436,37 @@ class Alias(Base, Email): @classmethod def resolve(cls, localpart, domain_name): - return cls.query.filter( - sqlalchemy.and_(cls.domain_name == domain_name, - sqlalchemy.or_( - sqlalchemy.and_( - cls.wildcard == False, - cls.localpart == localpart - ), sqlalchemy.and_( - cls.wildcard == True, - sqlalchemy.bindparam("l", localpart).like(cls.localpart) + alias_preserve_case = cls.query.filter( + sqlalchemy.and_(cls.domain_name == domain_name, + sqlalchemy.or_( + sqlalchemy.and_( + cls.wildcard == False, + cls.localpart == localpart + ), sqlalchemy.and_( + cls.wildcard == True, + sqlalchemy.bindparam("l", localpart).like(cls.localpart) + ) ) ) - ) - ).order_by(cls.wildcard, sqlalchemy.func.char_length(cls.localpart).desc()).first() + ).order_by(cls.wildcard, sqlalchemy.func.char_length(cls.localpart).desc()).first() + if alias_preserve_case: + return alias_preserve_case + if localpart: + localpart = localpart.lower() + return cls.query.filter( + sqlalchemy.and_(cls.domain_name == domain_name, + sqlalchemy.or_( + sqlalchemy.and_( + cls.wildcard == False, + sqlalchemy.func.lower(cls.localpart) == localpart + ), sqlalchemy.and_( + cls.wildcard == True, + sqlalchemy.bindparam("l", localpart).like(sqlalchemy.func.lower(cls.localpart)) + ) + ) + ) + ).order_by(cls.wildcard, sqlalchemy.func.char_length(sqlalchemy.func.lower(cls.localpart)).desc()).first() class Token(Base): """ A token is an application password for a given user. diff --git a/towncrier/newsfragments/867.bugfix b/towncrier/newsfragments/867.bugfix new file mode 100644 index 00000000..ef9b04a7 --- /dev/null +++ b/towncrier/newsfragments/867.bugfix @@ -0,0 +1 @@ +Make aliases optionally case-insensitive: After attempting to resolve an alias in its preserved case, also attempt to match it case-insensitively