summaryrefslogtreecommitdiff
path: root/alot
diff options
context:
space:
mode:
authorPatrick Totzke <patricktotzke@gmail.com>2018-12-08 22:30:39 +0000
committerPatrick Totzke <patricktotzke@gmail.com>2019-08-15 11:33:04 +0100
commit21c399ee485f448d069de440d1522407339e77f3 (patch)
treeb73ebd9c53fad13731c842afbe4eb5be269d214b /alot
parent7a883108b1bc6d8b59ac5a14ebf6265312135be2 (diff)
Update to new (3.6) email message API
This uses email.message.EmailMessage.get_body() to find the best candidate for a "body" message part and replaces our own ad-hoc solution in alot.db.utils.extract_bodytext, which was based on a walk through all parts.
Diffstat (limited to 'alot')
-rw-r--r--alot/db/message.py18
-rw-r--r--alot/db/utils.py81
2 files changed, 41 insertions, 58 deletions
diff --git a/alot/db/message.py b/alot/db/message.py
index 76bb9dd0..8b93f1ce 100644
--- a/alot/db/message.py
+++ b/alot/db/message.py
@@ -271,16 +271,20 @@ class Message:
"""
# TODO: allow toggle commands to decide which part is considered body
eml = self.get_email()
- bodytext = extract_body(eml)
-
- # check if extracted body is empty but msg contains html parts
- if (not bodytext and
- 'text/html' in (part.get_content_type() for part in eml.walk())):
- return MISSING_HTML_MSG
+ bodytext = self.get_text_content()
+
+ # check if extracted body is empty dispite having a text/html body part
+ if eml is not None:
+ bodypart = eml.get_body()
+ if (bodypart and
+ bodypart.get_payload() and
+ eml.get_content_type() == 'text/html' and
+ not bodytext):
+ bodytext = MISSING_HTML_MSG
return bodytext
def get_text_content(self):
- return extract_body(self.get_email(), types=['text/plain'])
+ return extract_body(self.get_email())
def matches(self, querystring):
"""tests if this messages is in the resultset for `querystring`"""
diff --git a/alot/db/utils.py b/alot/db/utils.py
index 49d5b6d2..da8f3df7 100644
--- a/alot/db/utils.py
+++ b/alot/db/utils.py
@@ -211,7 +211,9 @@ def _handle_encrypted(original, message, session_keys=None):
if malformed:
msg = u'Malformed OpenPGP message: {0}'.format(malformed)
- content = email.message_from_string(msg, policy=email.policy.SMTP)
+ content = email.message_from_string(msg,
+ _class=email.message.EmailMessage,
+ policy=email.policy.SMTP)
content.set_charset('utf-8')
original.attach(content)
@@ -228,7 +230,8 @@ def decrypted_message_from_file(handle, session_keys=None):
:returns: :class:`email.message.Message` possibly augmented with
decrypted data
'''
- return decrypted_message_from_message(email.message_from_file(handle),
+ return decrypted_message_from_message(email.message_from_file(handle,
+ _class=email.message.EmailMessage),
session_keys)
@@ -298,7 +301,9 @@ def decrypted_message_from_bytes(bytestring, session_keys=None):
:param session_keys: a list OpenPGP session keys
"""
return decrypted_message_from_message(
- email.message_from_bytes(bytestring, policy=email.policy.SMTP),
+ email.message_from_bytes(bytestring,
+ _class=email.message.EmailMessage,
+ policy=email.policy.SMTP),
session_keys)
@@ -453,63 +458,37 @@ def remove_cte(part, as_string=False):
return bp
-def extract_body(mail, types=None, field_key='copiousoutput'):
+def extract_body(mail):
"""Returns a string view of a Message.
- If the `types` argument is set then any encoding types there will be used
- as the prefered encoding to extract. If `types` is None then
- :ref:`prefer_plaintext <prefer-plaintext>` will be consulted; if it is True
- then text/plain parts will be returned, if it is false then text/html will
- be returned if present or text/plain if there are no text/html parts.
+ This consults :ref:`prefer_plaintext <prefer-plaintext>`
+ to determine if a "text/plain" alternative is preferred over a "text/html"
+ part.
:param mail: the mail to use
:type mail: :class:`email.Message`
- :param types: mime content types to use for body string
- :type types: list[str]
:returns: The combined text of any parts to be used
:rtype: str
"""
- preferred = 'text/plain' if settings.get(
- 'prefer_plaintext') else 'text/html'
- has_preferred = False
-
- # see if the mail has our preferred type
- if types is None:
- has_preferred = list(typed_subpart_iterator(
- mail, *preferred.split('/')))
-
- body_parts = []
- for part in mail.walk():
- # skip non-leaf nodes in the mail tree
- if part.is_multipart():
- continue
-
- ctype = part.get_content_type()
-
- if types is not None:
- if ctype not in types:
- continue
- cd = part.get('Content-Disposition', '')
- if cd.startswith('attachment'):
- continue
- # if the mail has our preferred type, we only keep this type
- # note that if types != None, has_preferred always stays False
- if has_preferred and ctype != preferred:
- continue
-
- if ctype == 'text/plain':
- body_parts.append(string_sanitize(remove_cte(part, as_string=True)))
- else:
- rendered_payload = render_part(part)
- if rendered_payload: # handler had output
- body_parts.append(string_sanitize(rendered_payload))
- # mark as attachment
- elif cd:
- part.replace_header('Content-Disposition', 'attachment; ' + cd)
- else:
- part.add_header('Content-Disposition', 'attachment;')
- return u'\n\n'.join(body_parts)
+ if settings.get('prefer_plaintext'):
+ preferencelist = ('plain',)
+ else:
+ preferencelist = ('html', 'plain')
+
+ body_part = mail.get_body(preferencelist)
+ if body_part is None: # if no part matching preferredlist was found
+ return ""
+
+ displaystring = ""
+
+ if body_part.get_content_type() == 'text/plain':
+ displaystring = string_sanitize(remove_cte(body_part, as_string=True))
+ else:
+ rendered_payload = render_part(body_part)
+ if rendered_payload: # handler had output
+ displaystring = string_sanitize(rendered_payload)
+ return displaystring
def formataddr(pair):