[#8241] Send mails using quoted-printable if the lines are longer than SMTP allows
diff --git a/Allura/allura/lib/mail_util.py b/Allura/allura/lib/mail_util.py
index bb768a1..ae72f93 100644
--- a/Allura/allura/lib/mail_util.py
+++ b/Allura/allura/lib/mail_util.py
@@ -175,11 +175,35 @@
     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:
-        return MIMEText(content.encode('ascii'), content_type, 'ascii')
-    except:
-        return MIMEText(content.encode('utf-8'), content_type, 'utf-8')
+        # simplest email - plain ascii
+        encoded_content = content.encode('ascii')
+        encoding = 'ascii'
+    except Exception:
+        # utf8 will get base64 encoded so we only do it if ascii fails
+        encoded_content = content.encode('utf-8')
+        encoding = 'utf-8'
+
+    for line in encoded_content.splitlines():
+        if len(line) > MAX_MAIL_LINE_OCTETS:
+            # switch to Quoted-Printable encoding to avoid too-long lines
+            # we could always Quoted-Printabl, but it makes the output a little messier and less human-readable
+            # the particular order of all these operations seems to be very important for everything to end up right
+            msg = MIMEText(None, content_type)
+            msg.replace_header('content-transfer-encoding', 'quoted-printable')
+            cs = email.charset.Charset('utf-8')
+            cs.header_encoding = email.charset.QP
+            cs.body_encoding = email.charset.QP
+            payload = cs.body_encode(content.encode('utf-8'))
+            msg.set_payload(payload, 'utf-8')
+            return msg
+    else:
+        return MIMEText(encoded_content, content_type, encoding)
 
 
 def make_multipart_message(*parts):
diff --git a/Allura/allura/tests/test_tasks.py b/Allura/allura/tests/test_tasks.py
index 7aefa9c..2d27256 100644
--- a/Allura/allura/tests/test_tasks.py
+++ b/Allura/allura/tests/test_tasks.py
@@ -28,7 +28,7 @@
 import mock
 from pylons import tmpl_context as c, app_globals as g
 from datadiff.tools import assert_equal
-from nose.tools import assert_in
+from nose.tools import assert_in, assert_less
 from ming.orm import FieldProperty, Mapper
 from ming.orm import ThreadLocalORMSession
 from testfixtures import LogCapture
@@ -423,6 +423,28 @@
             return_path, rcpts, body = _client.sendmail.call_args[0]
             assert_in('From: "Test Admin" <test-admin@users.localhost>', body)
 
+    def test_send_email_long_lines_use_quoted_printable(self):
+        with mock.patch.object(mail_tasks.smtp_client, '_client') as _client:
+            mail_tasks.sendsimplemail(
+                fromaddr=u'"По" <foo@bar.com>',
+                toaddr='blah@blah.com',
+                text=(u'0123456789' * 100) + u'\n\n' + (u'Громады стро ' * 100),
+                reply_to=g.noreply,
+                subject=u'По оживлённым берегам',
+                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)
+
+            # plain text
+            assert_in('012345678901234567890123456789012345678901234567890123456789012345678901234=', body)
+            assert_in('=D0=93=D1=80=D0=BE=D0=BC=D0=B0=D0=B4=D1=8B =D1=81=D1=82=D1=80=D0=BE =D0=93=', body)
+            # html
+            assert_in('<div class=3D"markdown_content"><p>0123456789012345678901234567890123456789=', body)
+            assert_in('<p>=D0=93=D1=80=D0=BE=D0=BC=D0=B0=D0=B4=D1=8B =D1=81=D1=82=D1=80=D0=BE =D0=', body)
+
     @td.with_wiki
     def test_receive_email_ok(self):
         c.user = M.User.by_username('test-admin')