Skip to content

Commit

Permalink
Merge pull request #1281 from dcbaker/fix-1257
Browse files Browse the repository at this point in the history
helper: replace email_as_* with email builtins
  • Loading branch information
dcbaker committed Aug 2, 2018
2 parents 834a658 + 293bf7a commit 29eb198
Show file tree
Hide file tree
Showing 8 changed files with 16 additions and 75 deletions.
1 change: 1 addition & 0 deletions NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
* Port to python 3. Python 2.x no longer supported
* feature: Add a new 'namedqueries' buffer type for displaying named queries.
* feature: Replace twisted with asyncio
* bug fix: correct handling of subparts with different encodings

0.7:
* info: missing html mailcap entry now reported as mail body text
Expand Down
7 changes: 4 additions & 3 deletions alot/commands/envelope.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import argparse
import datetime
import email
import email.policy
import glob
import logging
import os
Expand All @@ -22,7 +23,6 @@
from ..account import SendingMailFailed, StoreMailError
from ..db.errors import DatabaseError
from ..errors import GPGProblem
from ..helper import email_as_string
from ..helper import string_decode
from ..settings.const import settings
from ..settings.errors import NoMatchingAccount
Expand Down Expand Up @@ -130,7 +130,8 @@ async def apply(self, ui):
# store mail locally
# add Date header
mail['Date'] = email.utils.formatdate(localtime=True)
path = account.store_draft_mail(email_as_string(mail))
path = account.store_draft_mail(
mail.as_string(policy=email.policy.SMTP))

msg = 'draft saved successfully'

Expand Down Expand Up @@ -210,7 +211,7 @@ async def apply(self, ui):
try:
self.mail = self.envelope.construct_mail()
self.mail['Date'] = email.utils.formatdate(localtime=True)
self.mail = email_as_string(self.mail)
self.mail = self.mail.as_string(policy=email.policy.SMTP)
except GPGProblem as e:
ui.clear_notify([clearme])
ui.notify(str(e), priority='error')
Expand Down
5 changes: 3 additions & 2 deletions alot/commands/thread.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
import re
import subprocess
import tempfile
import email
import email.policy
from email.utils import getaddresses, parseaddr, formataddr
from email.message import Message

Expand All @@ -34,7 +36,6 @@
from ..settings.const import settings
from ..helper import parse_mailcap_nametemplate
from ..helper import split_commandstring
from ..helper import email_as_string
from ..utils import argparse as cargparse
from ..widgets.globals import AttachmentWidget

Expand Down Expand Up @@ -382,7 +383,7 @@ async def apply(self, ui):
original_mail = Message()
original_mail.set_type('message/rfc822')
original_mail['Content-Disposition'] = 'attachment'
original_mail.set_payload(email_as_string(mail))
original_mail.set_payload(mail.as_string(policy=email.policy.SMTP))
envelope.attach(Attachment(original_mail))

# copy subject
Expand Down
5 changes: 3 additions & 2 deletions alot/db/envelope.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import os
import re
import email
import email.policy
from email.encoders import encode_7or8bit
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
Expand Down Expand Up @@ -191,7 +192,7 @@ def construct_mail(self):
inner_msg = textpart

if self.sign:
plaintext = helper.email_as_bytes(inner_msg)
plaintext = inner_msg.as_bytes(policy=email.policy.SMTP)
logging.debug('signing plaintext: %s', plaintext)

try:
Expand Down Expand Up @@ -236,7 +237,7 @@ def construct_mail(self):
unencrypted_msg = inner_msg

if self.encrypt:
plaintext = helper.email_as_bytes(unencrypted_msg)
plaintext = unencrypted_msg.as_bytes(policy=email.policy.SMTP)
logging.debug('encrypting plaintext: %s', plaintext)

try:
Expand Down
3 changes: 2 additions & 1 deletion alot/db/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import email.charset as charset
from email.header import Header
from email.iterators import typed_subpart_iterator
import email.policy
import email.utils
import tempfile
import re
Expand Down Expand Up @@ -132,7 +133,7 @@ def _handle_signatures(original, message, params):
if not malformed:
try:
sigs = crypto.verify_detached(
helper.email_as_bytes(message.get_payload(0)),
message.get_payload(0).as_bytes(policy=email.policy.SMTP),
message.get_payload(1).get_payload(decode=True))
except GPGProblem as e:
malformed = str(e)
Expand Down
43 changes: 0 additions & 43 deletions alot/helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -595,49 +595,6 @@ def RFC3156_canonicalize(text):
return text


def email_as_string(mail):
"""
Converts the given message to a string, without mangling "From" lines
(like as_string() does).
:param mail: email to convert to string
:rtype: str
"""
fp = StringIO()
g = Generator(fp, mangle_from_=False, maxheaderlen=78)
g.flatten(mail)
as_string = RFC3156_canonicalize(fp.getvalue())
return as_string


def email_as_bytes(mail):
string = email_as_string(mail)
charset = mail.get_charset()
if charset:
charset = str(charset)
else:
charsets = set(mail.get_charsets())
if None in charsets:
# None is equal to US-ASCII
charsets.discard(None)
charsets.add('ascii')

if len(charsets) == 1:
charset = list(charsets)[0]
elif 'ascii' in charsets:
# If we get here and the assert triggers it means that different
# parts of the email are encoded differently. I don't think we're
# likely to see that, but it's possible
if not {'utf-8', 'ascii', 'us-ascii'}.issuperset(charsets):
raise RuntimeError(
"different encodings detected: {}".format(charsets))
charset = 'utf-8' # It's a strict super-set
else:
charset = 'utf-8'

return string.encode(charset)


def get_xdg_env(env_name, fallback):
""" Used for XDG_* env variables to return fallback if unset *or* empty """
env = os.environ.get(env_name)
Expand Down
5 changes: 3 additions & 2 deletions tests/db/utils_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import email
import email.header
import email.mime.application
import email.policy
import io
import os
import os.path
Expand Down Expand Up @@ -465,7 +466,7 @@ def _make_signed(self):
text = b'This is some text'
t = email.mime.text.MIMEText(text, 'plain', 'utf-8')
_, sig = crypto.detached_signature_for(
helper.email_as_bytes(t), self.keys)
t.as_bytes(policy=email.policy.SMTP), self.keys)
s = email.mime.application.MIMEApplication(
sig, 'pgp-signature', email.encoders.encode_7or8bit)
m = email.mime.multipart.MIMEMultipart('signed', None, [t, s])
Expand Down Expand Up @@ -555,7 +556,7 @@ def _make_encrypted(self, signed=False):
else:
text = b'This is some text'
t = email.mime.text.MIMEText(text, 'plain', 'utf-8')
enc = crypto.encrypt(helper.email_as_bytes(t), self.keys)
enc = crypto.encrypt(t.as_bytes(policy=email.policy.SMTP), self.keys)
e = email.mime.application.MIMEApplication(
enc, 'octet-stream', email.encoders.encode_7or8bit)

Expand Down
22 changes: 0 additions & 22 deletions tests/helper_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -383,28 +383,6 @@ def test_os_errors_from_popen_are_caught(self):
self.assertEqual(code, 42)


class TestEmailAsString(unittest.TestCase):

def test_empty_message(self):
message = email.message.Message()
actual = helper.email_as_string(message)
expected = '\r\n'
self.assertEqual(actual, expected)

def test_empty_message_with_unicode_header(self):
"""Test if unicode header keys can be used in an email that is
converted to string with email_as_string()."""
# This is what alot.db.envelope.Envelope.construct_mail() currently
# does: It constructs a message object and then copies all headers from
# the envelope to the message object. Some header names are stored as
# unicode in the envelope.
message = email.message.Message()
message[u'X-Unicode-Header'] = 'dummy value'
actual = helper.email_as_string(message)
expected = 'X-Unicode-Header: dummy value\r\n\r\n'
self.assertEqual(actual, expected)


class TestShorten(unittest.TestCase):

def test_lt_maxlen(self):
Expand Down

0 comments on commit 29eb198

Please sign in to comment.