From 46bb4539c58b231ae8153df30a49a8dca2660f93 Mon Sep 17 00:00:00 2001 From: Henrik Levkowetz Date: Mon, 4 Mar 2019 20:06:44 +0000 Subject: [PATCH] Added two new functions to replace email From: of users with On-behalf-of addresses, with associated values in settings. Added a catchall stanza in the mail sending pipeline to catch anything not already changed to On-behalf-of addresses. - Legacy-Id: 15987 --- ietf/settings.py | 2 ++ ietf/utils/mail.py | 47 +++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 46 insertions(+), 3 deletions(-) diff --git a/ietf/settings.py b/ietf/settings.py index f276a4a58..cc2bc2576 100644 --- a/ietf/settings.py +++ b/ietf/settings.py @@ -77,6 +77,8 @@ TOOLS_ID_HTML_URL = TOOLS_SERVER_URL + '/html/' SERVER_EMAIL = 'Django Server ' DEFAULT_FROM_EMAIL = 'IETF Secretariat ' +UTILS_ON_BEHALF_EMAIL = 'ietf-secretariat-reply@' + IETF_DOMAIN +UTILS_FROM_EMAIL_DOMAINS = [ 'ietf.org', 'iab.org', 'tools.ietf.org', ] MANAGERS = ADMINS diff --git a/ietf/utils/mail.py b/ietf/utils/mail.py index 161f924fb..0d2bd1ceb 100644 --- a/ietf/utils/mail.py +++ b/ietf/utils/mail.py @@ -8,11 +8,11 @@ import textwrap import time import traceback -from email.utils import make_msgid, formatdate, formataddr as simple_formataddr, parseaddr, getaddresses +from email.utils import make_msgid, formatdate, formataddr as simple_formataddr, parseaddr as simple_parseaddr, getaddresses from email.mime.text import MIMEText from email.mime.message import MIMEMessage from email.mime.multipart import MIMEMultipart -from email.header import Header +from email.header import Header, decode_header from email import message_from_string from email import charset as Charset @@ -26,7 +26,7 @@ from django.template import Context,RequestContext import debug # pyflakes:ignore import ietf -from ietf.utils.log import log +from ietf.utils.log import log, unreachable from ietf.utils.text import isascii # Testing mode: @@ -184,6 +184,30 @@ def send_mail_text(request, to, frm, subject, txt, cc=None, extra=None, toUser=F msg = encode_message(txt) return send_mail_mime(request, to, frm, subject, msg, cc, extra, toUser, bcc, copy=copy) +def on_behalf_of(frm): + if isinstance(frm, tuple): + name, addr = frm + else: + name, addr = parseaddr(frm) + domain = addr.rsplit('@', 1)[-1] + if domain in settings.UTILS_FROM_EMAIL_DOMAINS: + raise ValueError("Using send_mail_on_behalf_of() with an address (%s) in %s is misleading. Please use send_mail()" % (addr, settings.UTILS_FROM_EMAIL_DOMAINS)) + if not name: + name = addr + name = "Datatracker on behalf of %s" % name + addr = settings.UTILS_ON_BEHALF_EMAIL + return formataddr((name, addr)) + +def maybe_on_behalf_of(frm): + if isinstance(frm, tuple): + name, addr = frm + else: + name, addr = parseaddr(frm) + domain = addr.rsplit('@', 1)[-1] + if not domain in settings.UTILS_FROM_EMAIL_DOMAINS: + frm = on_behalf_of(frm) + return frm + def formataddr(addrtuple): """ Takes a name and email address, and inspects the name to see if it needs @@ -196,6 +220,18 @@ def formataddr(addrtuple): name = str(Header(name, 'utf-8')) return simple_formataddr((name, addr)) +def parseaddr(addr): + """ + Takes an address (which should be the value of some address-containing + field such as To or Cc) into its constituent realname and email address + parts. Returns a tuple of that information, unless the parse fails, in + which case a 2-tuple of ('', '') is returned. + + """ + addr = u''.join( [ s.decode(m) if m else unicode(s) for (s,m) in decode_header(addr) ] ) + name, addr = simple_parseaddr(addr) + return name, addr + def condition_message(to, frm, subject, msg, cc, extra): if isinstance(frm, tuple): @@ -205,6 +241,11 @@ def condition_message(to, frm, subject, msg, cc, extra): if isinstance(cc, list) or isinstance(cc, tuple): cc = ", ".join([isinstance(addr, tuple) and formataddr(addr) or addr for addr in cc if addr]) if frm: + n, a = parseaddr(frm) + domain = a.rsplit('@', 1)[-1] + if not domain in settings.UTILS_FROM_EMAIL_DOMAINS: + unreachable('2019-03-04') + frm = on_behalf_of(frm) msg['From'] = frm if extra and 'Reply-To' in extra: reply_to = extra['Reply-To']