From 5efd5abbe43fcb3ef0e4acbe5ab74a88cef880bd Mon Sep 17 00:00:00 2001 From: Joshua Tauberer Date: Tue, 21 Apr 2015 14:43:12 +0000 Subject: [PATCH] move the email address syntax validation for users and aliases into my new email_validator library (https://github.com/JoshData/python-email-validator) --- management/mailconfig.py | 62 +++++++++------------------------------- setup/management.sh | 2 +- 2 files changed, 15 insertions(+), 49 deletions(-) diff --git a/management/mailconfig.py b/management/mailconfig.py index ad4cb7b7..48f87e4a 100755 --- a/management/mailconfig.py +++ b/management/mailconfig.py @@ -2,6 +2,7 @@ import subprocess, shutil, os, sqlite3, re import utils +from email_validator import validate_email as validate_email_, EmailNotValidError def validate_email(email, mode=None): # Checks that an email address is syntactically valid. Returns True/False. @@ -15,60 +16,25 @@ def validate_email(email, mode=None): # When mode=="alias", we're allowing anything that can be in a Postfix # alias table, i.e. omitting the local part ("@domain.tld") is OK. - # Check that the address isn't absurdly long. - if len(email) > 255: return False + # Check the syntax of the address. + try: + validate_email_(email, + allow_smtputf8=False, + check_deliverability=False, + allow_empty_local=(mode=="alias") + ) + except EmailNotValidError: + return False if mode == 'user': # There are a lot of characters permitted in email addresses, but - # Dovecot's sqlite driver seems to get confused if there are any + # Dovecot's sqlite auth driver seems to get confused if there are any # unusual characters in the address. Bah. Also note that since # the mailbox path name is based on the email address, the address # shouldn't be absurdly long and must not have a forward slash. - ATEXT = r'[a-zA-Z0-9_\-]+' - elif mode in (None, 'alias'): - # For aliases, we can allow any valid email address. - # Based on RFC 2822 and https://github.com/SyrusAkbary/validate_email/blob/master/validate_email.py, - # these characters are permitted in email addresses. - ATEXT = r'[a-zA-Z0-9_!#$%&\'\*\+\-/=\?\^`\{\|\}~]+' # see 3.2.4 - else: - raise ValueError(mode) - - # per RFC 2822 3.2.4 - DOT_ATOM_TEXT_LOCAL = ATEXT + r'(?:\.' + ATEXT + r')*' - if mode == 'alias': - # For aliases, Postfix accepts '@domain.tld' format for - # catch-all addresses on the source side and domain aliases - # on the destination side. Make the local part optional. - DOT_ATOM_TEXT_LOCAL = '(?:' + DOT_ATOM_TEXT_LOCAL + ')?' - - # The domain part of the email address has a few more restrictions. - - # In addition to the characters allowed by RFC 2822, the domain part - # must also satisfy the requirements of RFC 952/RFC 1123 which restrict - # the allowed characters of hostnames further. These are a subset of - # the Dovecot-allowed characters, fortunately. The hyphen cannot be at - # the beginning or end of a component of a hostname either, but we aren't - # testing that. - ATEXT2 = r'[a-zA-Z0-9\-]+' - - # We can require that the host part have at least one period in it. - # We also know that all TLDs are at least two characters and end with a letter. - DOT_ATOM_TEXT_HOST = ATEXT2 + r'(?:\.' + ATEXT2 + r')*' + r'(?:\.' + ATEXT2 + r'[A-Za-z])' - - # per RFC 2822 3.4.1 - ADDR_SPEC = '^(%s)@(%s)$' % (DOT_ATOM_TEXT_LOCAL, DOT_ATOM_TEXT_HOST) - - # Check the regular expression. - m = re.match(ADDR_SPEC, email) - if not m: return False - - # Check that the domain part is valid IDNA. - localpart, domainpart = m.groups() - try: - domainpart.encode('ascii').decode("idna") - except: - # Domain is not valid IDNA. - return False + if len(email) > 255: return False + if re.search(r'[^\@\.a-zA-Z0-9_\-]+', email): + return False # Everything looks good. return True diff --git a/setup/management.sh b/setup/management.sh index fe71385f..88b40e26 100755 --- a/setup/management.sh +++ b/setup/management.sh @@ -3,7 +3,7 @@ source setup/functions.sh apt_install python3-flask links duplicity libyaml-dev python3-dnspython python3-dateutil -hide_output pip3 install rtyaml +hide_output pip3 install rtyaml "email_validator==0.1.0-rc1" # Create a backup directory and a random key for encrypting backups. mkdir -p $STORAGE_ROOT/backup