summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAnton Khirnov <anton@khirnov.net>2021-01-26 12:30:28 +0100
committerAnton Khirnov <anton@khirnov.net>2021-01-26 12:30:28 +0100
commitfe1450cba0cc808dd843f327c4383930a78effca (patch)
treea3ca9b5bc89d7af3d93213d02f7384cb8c12b4d7
parente688d4876a761f9c89e427e9c451fd2968123909 (diff)
helper: move mimewrap() to the only place where it is called
-rw-r--r--alot/db/envelope.py113
-rw-r--r--alot/helper.py116
2 files changed, 112 insertions, 117 deletions
diff --git a/alot/db/envelope.py b/alot/db/envelope.py
index d5b84b3e..8340ba4e 100644
--- a/alot/db/envelope.py
+++ b/alot/db/envelope.py
@@ -8,6 +8,9 @@ import re
import email
import email.policy
from email.encoders import encode_7or8bit
+from email.mime.audio import MIMEAudio
+from email.mime.base import MIMEBase
+from email.mime.image import MIMEImage
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.mime.application import MIMEApplication
@@ -15,6 +18,7 @@ import email.charset as charset
from urllib.parse import unquote
import gpg
+import magic
from .attachment import Attachment
from .. import __version__
@@ -25,6 +29,113 @@ from ..errors import GPGProblem, GPGCode
charset.add_charset('utf-8', charset.QP, charset.QP, 'utf-8')
+def _libmagic_version_at_least(version):
+ """
+ checks if the libmagic library installed is more recent than a given
+ version.
+
+ :param version: minimum version expected in the form XYY (i.e. 5.14 -> 514)
+ with XYY >= 513
+ """
+ if hasattr(magic, 'open'):
+ magic_wrapper = magic._libraries['magic']
+ elif hasattr(magic, 'from_buffer'):
+ magic_wrapper = magic.libmagic
+ else:
+ raise Exception('Unknown magic API')
+
+ if not hasattr(magic_wrapper, 'magic_version'):
+ # The magic_version function has been introduced in libmagic 5.13,
+ # if it's not present, we can't guess right, so let's assume False
+ return False
+
+ # Depending on the libmagic/ctypes version, magic_version is a function or
+ # a callable:
+ if callable(magic_wrapper.magic_version):
+ return magic_wrapper.magic_version() >= version
+
+ return magic_wrapper.magic_version >= version
+
+def _guess_encoding(blob):
+ """
+ uses file magic to determine the encoding of the given data blob.
+
+ :param blob: file content as read by file.read()
+ :type blob: data
+ :returns: encoding
+ :rtype: str
+ """
+ # this is a bit of a hack to support different versions of python magic.
+ # Hopefully at some point this will no longer be necessary
+ #
+ # the version with open() is the bindings shipped with the file source from
+ # http://darwinsys.com/file/ - this is what is used by the python-magic
+ # package on Debian/Ubuntu. However it is not available on pypi/via pip.
+ #
+ # the version with from_buffer() is available at
+ # https://github.com/ahupp/python-magic and directly installable via pip.
+ #
+ # for more detail see https://github.com/pazz/alot/pull/588
+ if hasattr(magic, 'open'):
+ m = magic.open(magic.MAGIC_MIME_ENCODING)
+ m.load()
+ return m.buffer(blob)
+ elif hasattr(magic, 'from_buffer'):
+ m = magic.Magic(mime_encoding=True)
+ return m.from_buffer(blob)
+ else:
+ raise Exception('Unknown magic API')
+
+# TODO: make this work on blobs, not paths
+def _mimewrap(path, filename, ctype):
+ """Take the contents of the given path and wrap them into an email MIME
+ part according to the content type. The content type is auto detected from
+ the actual file contents and the file name if it is not given.
+
+ :param path: the path to the file contents
+ :type path: str
+ :param filename: the file name to use in the generated MIME part
+ :type filename: str or None
+ :param ctype: the content type of the file contents in path
+ :type ctype: str or None
+ :returns: the message MIME part storing the data from path
+ :rtype: subclasses of email.mime.base.MIMEBase
+ """
+
+ with open(path, 'rb') as f:
+ content = f.read()
+ if not ctype:
+ ctype = helper.guess_mimetype(content)
+ # libmagic < 5.12 incorrectly detects excel/powerpoint files as
+ # 'application/msword' (see #179 and #186 in libmagic bugtracker)
+ # This is a workaround, based on file extension, useful as long
+ # as distributions still ship libmagic 5.11.
+ if (ctype == 'application/msword' and
+ not _libmagic_version_at_least(513)):
+ mimetype, _ = mimetypes.guess_type(path)
+ if mimetype:
+ ctype = mimetype
+
+ maintype, subtype = ctype.split('/', 1)
+ if maintype == 'text':
+ part = MIMEText(content.decode(_guess_encoding(content), 'replace'),
+ _subtype=subtype,
+ _charset='utf-8')
+ elif maintype == 'image':
+ part = MIMEImage(content, _subtype=subtype)
+ elif maintype == 'audio':
+ part = MIMEAudio(content, _subtype=subtype)
+ else:
+ part = MIMEBase(maintype, subtype)
+ part.set_payload(content)
+ # Encode the payload using Base64
+ email.encoders.encode_base64(part)
+ # Set the filename parameter
+ if not filename:
+ filename = os.path.basename(path)
+ part.add_header('Content-Disposition', 'attachment',
+ filename=filename)
+ return part
class Envelope:
"""
@@ -170,7 +281,7 @@ class Envelope:
self.attachments.append(attachment)
elif isinstance(attachment, str):
path = os.path.expanduser(attachment)
- part = helper.mimewrap(path, filename, ctype)
+ part = _mimewrap(path, filename, ctype)
self.attachments.append(Attachment(part))
else:
raise TypeError('attach accepts an Attachment or str')
diff --git a/alot/helper.py b/alot/helper.py
index 4283faea..0664f71e 100644
--- a/alot/helper.py
+++ b/alot/helper.py
@@ -10,10 +10,6 @@ import os
import re
import shlex
import email
-from email.mime.audio import MIMEAudio
-from email.mime.base import MIMEBase
-from email.mime.image import MIMEImage
-from email.mime.text import MIMEText
import magic
@@ -163,118 +159,6 @@ def guess_mimetype(blob):
mimetype = magictype
return mimetype
-
-def guess_encoding(blob):
- """
- uses file magic to determine the encoding of the given data blob.
-
- :param blob: file content as read by file.read()
- :type blob: data
- :returns: encoding
- :rtype: str
- """
- # this is a bit of a hack to support different versions of python magic.
- # Hopefully at some point this will no longer be necessary
- #
- # the version with open() is the bindings shipped with the file source from
- # http://darwinsys.com/file/ - this is what is used by the python-magic
- # package on Debian/Ubuntu. However it is not available on pypi/via pip.
- #
- # the version with from_buffer() is available at
- # https://github.com/ahupp/python-magic and directly installable via pip.
- #
- # for more detail see https://github.com/pazz/alot/pull/588
- if hasattr(magic, 'open'):
- m = magic.open(magic.MAGIC_MIME_ENCODING)
- m.load()
- return m.buffer(blob)
- elif hasattr(magic, 'from_buffer'):
- m = magic.Magic(mime_encoding=True)
- return m.from_buffer(blob)
- else:
- raise Exception('Unknown magic API')
-
-
-def libmagic_version_at_least(version):
- """
- checks if the libmagic library installed is more recent than a given
- version.
-
- :param version: minimum version expected in the form XYY (i.e. 5.14 -> 514)
- with XYY >= 513
- """
- if hasattr(magic, 'open'):
- magic_wrapper = magic._libraries['magic']
- elif hasattr(magic, 'from_buffer'):
- magic_wrapper = magic.libmagic
- else:
- raise Exception('Unknown magic API')
-
- if not hasattr(magic_wrapper, 'magic_version'):
- # The magic_version function has been introduced in libmagic 5.13,
- # if it's not present, we can't guess right, so let's assume False
- return False
-
- # Depending on the libmagic/ctypes version, magic_version is a function or
- # a callable:
- if callable(magic_wrapper.magic_version):
- return magic_wrapper.magic_version() >= version
-
- return magic_wrapper.magic_version >= version
-
-
-# TODO: make this work on blobs, not paths
-def mimewrap(path, filename=None, ctype=None):
- """Take the contents of the given path and wrap them into an email MIME
- part according to the content type. The content type is auto detected from
- the actual file contents and the file name if it is not given.
-
- :param path: the path to the file contents
- :type path: str
- :param filename: the file name to use in the generated MIME part
- :type filename: str or None
- :param ctype: the content type of the file contents in path
- :type ctype: str or None
- :returns: the message MIME part storing the data from path
- :rtype: subclasses of email.mime.base.MIMEBase
- """
-
- with open(path, 'rb') as f:
- content = f.read()
- if not ctype:
- ctype = guess_mimetype(content)
- # libmagic < 5.12 incorrectly detects excel/powerpoint files as
- # 'application/msword' (see #179 and #186 in libmagic bugtracker)
- # This is a workaround, based on file extension, useful as long
- # as distributions still ship libmagic 5.11.
- if (ctype == 'application/msword' and
- not libmagic_version_at_least(513)):
- mimetype, _ = mimetypes.guess_type(path)
- if mimetype:
- ctype = mimetype
-
- maintype, subtype = ctype.split('/', 1)
- if maintype == 'text':
- part = MIMEText(content.decode(guess_encoding(content), 'replace'),
- _subtype=subtype,
- _charset='utf-8')
- elif maintype == 'image':
- part = MIMEImage(content, _subtype=subtype)
- elif maintype == 'audio':
- part = MIMEAudio(content, _subtype=subtype)
- else:
- part = MIMEBase(maintype, subtype)
- part.set_payload(content)
- # Encode the payload using Base64
- email.encoders.encode_base64(part)
- # Set the filename parameter
- if not filename:
- filename = os.path.basename(path)
- part.add_header('Content-Disposition', 'attachment',
- filename=filename)
- return part
-
-
def shell_quote(text):
"""Escape the given text for passing it to the shell for interpretation.
The resulting string will be parsed into one "word" (in the sense used in