improve e-mail address splitting functions

Needed for modoboa/modoboa-amavis#89
This commit is contained in:
Andrew Fyfe
2018-01-19 20:15:13 +00:00
parent 5b279f533a
commit 7e5a9b9ebb
2 changed files with 94 additions and 17 deletions
+46 -16
View File
@@ -10,6 +10,7 @@ import re
import smtplib
import time
from django.utils import six
from django.utils.encoding import smart_str, smart_text
from django.utils.html import conditional_escape, escape
from django.template.loader import render_to_string
@@ -262,6 +263,47 @@ class Email(object):
return render_to_string("common/mailheaders.html", context)
def split_address(address):
"""Split an e-mail address into local part and domain."""
assert isinstance(address, six.text_type),\
"address should be of type %s" % six.text_type.__name__
if "@" not in address:
local_part = address
domain = None
else:
local_part, domain = address.split("@", 1)
return (local_part, domain)
def split_local_part(local_part, delimiter=None):
"""Split a local part into local part and extension."""
assert isinstance(local_part, six.text_type),\
"local_part should be of type %s" % six.text_type.__name__
assert isinstance(delimiter, six.text_type) or delimiter is None,\
"delimiter should be of type %s" % six.text_type.__name__
extension = None
if local_part.lower() in ["mailer-daemon", "double-bounce"]:
# never split these special addresses
pass
elif (
delimiter == "-"
and (local_part.startswith("owner-")
or local_part.endswith("-request"))
):
# don't split owner-* or *-request if - is the delimiter, they are
# special addresses used by mail lists.
pass
elif (
delimiter
and delimiter in local_part
and local_part[0] != delimiter
and local_part[-1] != delimiter
):
local_part, extension = local_part.split(delimiter, 1)
return (local_part, extension)
def split_mailbox(mailbox, return_extension=False):
"""Try to split an address into parts (local part and domain name).
@@ -271,23 +313,11 @@ def split_mailbox(mailbox, return_extension=False):
:return: a tuple (local part, domain<, extension>)
"""
domain = None
if "@" not in mailbox:
localpart = mailbox
else:
parts = mailbox.split("@")
if len(parts) == 2:
localpart = parts[0]
domain = parts[1]
else:
domain = parts[-1]
localpart = "@".join(parts[:-1])
local_part, domain = split_address(mailbox)
if not return_extension:
return (localpart, domain)
extension = None
if "+" in localpart:
localpart, extension = localpart.split("+", 1)
return (localpart, domain, extension)
return (local_part, domain)
local_part, extension = split_local_part(local_part, delimiter="+")
return (local_part, domain, extension)
def decode(string, encodings=None, charset=None):
+48 -1
View File
@@ -9,7 +9,7 @@ import os
from django.test import SimpleTestCase
from django.utils.encoding import smart_bytes, smart_text
from ..email_utils import Email
from ..email_utils import Email, split_address, split_local_part
SAMPLES_DIR = os.path.realpath(
os.path.join(os.path.dirname(__file__), "sample_messages"))
@@ -106,3 +106,50 @@ class EmailTests(SimpleTestCase):
def test_email_multipart_without_links(self):
"""display the text/html part of a multipart message with links removed"""
self._test_email("multipart", dformat="html", links=False)
class EmailAddressParserTests(SimpleTestCase):
"""Tests for split_address() and split_local_part()."""
def test_split_address_with_domain(self):
"""Split an e-mail address with domain."""
address = "User@sub.exAMPLE.COM"
expected_output = ("User", "sub.exAMPLE.COM")
output = split_address(address)
self.assertEqual(output, expected_output)
def test_split_address_without_domain(self):
"""Split an e-mail address with domain."""
address = "User"
expected_output = ("User", None)
output = split_address(address)
self.assertEqual(output, expected_output)
def test_split_local_part_without_delimiter(self):
"""Split a local part without delimiter."""
local_part = "User+Foo"
expected_output = ("User+Foo", None)
output = split_local_part(local_part)
self.assertEqual(output, expected_output)
def test_split_local_part_with_delimiter(self):
"""Split a local part with delimiter."""
local_part = "User+Foo"
expected_output = ("User", "Foo")
output = split_local_part(local_part, "+")
self.assertEqual(output, expected_output)
def test_split_local_part_mailing_list_address(self):
"""Check special case addresses used by mailing lists."""
local_part = "owner-modoboa"
expected_output = ("owner-modoboa", None)
output = split_local_part(local_part, "-")
self.assertEqual(output, expected_output)
def test_split_local_part_special_address(self):
"""Check special case addresses."""
local_part = "mailer-daemon"
expected_output = ("mailer-daemon", None)
output = split_local_part(local_part, "-")
self.assertEqual(output, expected_output)