You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@allura.apache.org by di...@apache.org on 2021/12/16 17:30:40 UTC

[allura] 01/01: fix email headers being longer than SMTP limit

This is an automated email from the ASF dual-hosted git repository.

dill0wn pushed a commit to branch dw/smtp_line_length
in repository https://gitbox.apache.org/repos/asf/allura.git

commit ac7e02c01b7a643157f8fb62aa384b0d3cdd1aab
Author: Dillon Walls <di...@slashdotmedia.com>
AuthorDate: Thu Dec 16 17:30:33 2021 +0000

    fix email headers being longer than SMTP limit
---
 Allura/allura/lib/mail_util.py    | 27 +++++++++++++++++++++------
 Allura/allura/tests/test_tasks.py |  6 ++++--
 2 files changed, 25 insertions(+), 8 deletions(-)

diff --git a/Allura/allura/lib/mail_util.py b/Allura/allura/lib/mail_util.py
index 9b50e46..36a3b19 100644
--- a/Allura/allura/lib/mail_util.py
+++ b/Allura/allura/lib/mail_util.py
@@ -17,6 +17,7 @@
 
 from __future__ import unicode_literals
 from __future__ import absolute_import
+from __future__ import annotations
 import re
 import logging
 import smtplib
@@ -24,6 +25,7 @@ import email.parser
 from six.moves.email_mime_multipart import MIMEMultipart
 from six.moves.email_mime_text import MIMEText
 from email import header
+import typing
 
 import six
 import tg
@@ -37,6 +39,9 @@ from allura.lib import exceptions as exc
 from allura.lib import helpers as h
 from six.moves import map
 
+if typing.TYPE_CHECKING:
+    from email.message import EmailMessage
+
 log = logging.getLogger(__name__)
 
 RE_MESSAGE_ID = re.compile(r'<(?:[^>]*/)?([^>]*)>')
@@ -47,6 +52,9 @@ config = ConfigProxy(
 )
 EMAIL_VALIDATOR = fev.Email(not_empty=True)
 
+# http://www.jebriggs.com/blog/2010/07/smtp-maximum-line-lengths/
+MAX_MAIL_LINE_OCTETS = 998  # RFC 1000 - len(CRLF)
+
 
 def Header(text, *more_text):
     '''Helper to make sure we encode headers properly'''
@@ -190,10 +198,6 @@ def identify_sender(peer, email_address, headers, msg):
     return M.User.anonymous()
 
 
-# http://www.jebriggs.com/blog/2010/07/smtp-maximum-line-lengths/
-MAX_MAIL_LINE_OCTETS = 990
-
-
 def encode_email_part(content, content_type):
     try:
         # simplest email - plain ascii
@@ -264,7 +268,7 @@ class SMTPClient(object):
         self._client = None
 
     def sendmail(
-            self, addrs, fromaddr, reply_to, subject, message_id, in_reply_to, message,
+            self, addrs, fromaddr, reply_to, subject, message_id, in_reply_to, message: EmailMessage,
             sender=None, references=None, cc=None, to=None):
         if not addrs:
             return
@@ -291,7 +295,18 @@ class SMTPClient(object):
         if references:
             references = ['<%s>' % r for r in aslist(references)]
             message['References'] = Header(*references)
-        content = message.as_string()
+
+        # Kind of Hacky, but...
+        #   Certain headers, like 'References' can become very long when sent via reply
+        #   from deep inside a ticket thread. message.as_string allows you to pass a
+        #   maxheaderlen which splits long lines for you to fit inside your exim constraints.
+        #   HOWEVER, that flag doesn't take the header name length into account. So, this
+        #   somewhat hacky code approximates the longest 'Header-Name: ' prefix and makes sure
+        #   the line octet length takes that into account.
+        longest_header_len = max(len(h[0]) for h in message._headers)
+        max_header_len = MAX_MAIL_LINE_OCTETS - (2 + longest_header_len)
+
+        content = message.as_string(maxheaderlen=max_header_len)
         smtp_addrs = list(map(_parse_smtp_addr, addrs))
         smtp_addrs = [a for a in smtp_addrs if isvalid(a)]
         if not smtp_addrs:
diff --git a/Allura/allura/tests/test_tasks.py b/Allura/allura/tests/test_tasks.py
index 7c73399..6ac5691 100644
--- a/Allura/allura/tests/test_tasks.py
+++ b/Allura/allura/tests/test_tasks.py
@@ -34,7 +34,7 @@ import mock
 from tg import tmpl_context as c, app_globals as g
 
 from datadiff.tools import assert_equal
-from nose.tools import assert_in, assert_less
+from nose.tools import assert_in, assert_less, assert_less_equal
 from ming.orm import FieldProperty, Mapper
 from ming.orm import ThreadLocalORMSession
 from testfixtures import LogCapture
@@ -46,6 +46,7 @@ from allura.command.taskd import TaskdCommand
 from allura.lib import helpers as h
 from allura.lib import search
 from allura.lib.exceptions import CompoundError
+from allura.lib.mail_util import MAX_MAIL_LINE_OCTETS
 from allura.tasks import event_tasks
 from allura.tasks import index_tasks
 from allura.tasks import mail_tasks
@@ -475,12 +476,13 @@ class TestMailTasks(unittest.TestCase):
                 text=('0123456789' * 100) + '\n\n' + ('Громады стро ' * 100),
                 reply_to=g.noreply,
                 subject='По оживлённым берегам',
+                references=['foo@example.com'] * 100,  # needs to handle really long headers as well
                 message_id=h.gen_message_id())
             return_path, rcpts, body = _client.sendmail.call_args[0]
             body = body.split('\n')
 
             for line in body:
-                assert_less(len(line), 991)
+                assert_less_equal(len(line), MAX_MAIL_LINE_OCTETS)
 
             # plain text
             assert_in('012345678901234567890123456789012345678901234567890123456789012345678901234=', body)