Skip to content

Commit

Permalink
added mimetype guess. Closes #239 (#272)
Browse files Browse the repository at this point in the history
* added mimetype guess. Closes #239

* updated changelog

* added mimetype tests
  • Loading branch information
liiight committed Jun 24, 2019
1 parent d8d2a2e commit d3b1225
Show file tree
Hide file tree
Showing 3 changed files with 40 additions and 10 deletions.
29 changes: 19 additions & 10 deletions notifiers/providers/email.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import getpass
import mimetypes
import smtplib
import socket
from email.message import EmailMessage
from email.mime.application import MIMEApplication
from email.utils import formatdate
from pathlib import Path
from smtplib import SMTPAuthenticationError, SMTPServerDisconnected, SMTPSenderRefused
from typing import Tuple, List

from ..core import Provider, Response
from ..utils.schema.helpers import one_or_more, list_to_commas
Expand Down Expand Up @@ -82,6 +83,17 @@ class SMTP(Provider):
"additionalProperties": False,
}

@staticmethod
def _get_mimetype(attachment: Path) -> Tuple[str, str]:
"""Taken from https://docs.python.org/3/library/email.examples.html"""
ctype, encoding = mimetypes.guess_type(str(attachment))
if ctype is None or encoding is not None:
# No guess could be made, or the file is encoded (compressed), so
# use a generic bag-of-bits type.
ctype = 'application/octet-stream'
maintype, subtype = ctype.split('/', 1)
return maintype, subtype

def __init__(self):
super().__init__()
self.smtp_server = None
Expand Down Expand Up @@ -119,14 +131,11 @@ def _build_email(data: dict) -> EmailMessage:
email.add_alternative(data["message"], subtype=content_type)
return email

@staticmethod
def _add_attachments(data: dict, email: EmailMessage) -> EmailMessage:
for attachment in data["attachments"]:
file = Path(attachment).read_bytes()
part = MIMEApplication(file)
part.add_header("Content-Disposition", "attachment", filename=attachment)
email.attach(part)
return email
def _add_attachments(self, attachments: List[str], email: EmailMessage):
for attachment in attachments:
attachment = Path(attachment)
maintype, subtype = self._get_mimetype(attachment)
email.add_attachment(attachment.read_bytes(), maintype=maintype, subtype=subtype, filename=attachment.name)

def _connect_to_server(self, data: dict):
self.smtp_server = smtplib.SMTP_SSL if data["ssl"] else smtplib.SMTP
Expand Down Expand Up @@ -155,7 +164,7 @@ def _send_notification(self, data: dict) -> Response:
self._connect_to_server(data)
email = self._build_email(data)
if data.get("attachments"):
email = self._add_attachments(data, email)
self._add_attachments(data['attachments'], email)
self.smtp_server.send_message(email)
except (
SMTPServerDisconnected,
Expand Down
3 changes: 3 additions & 0 deletions source/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ Changelog
------------------

- Added ability to cancel login to SMTP/GMAIL if credentials are used (`#210 <https://github.com/notifiers/notifiers/issues/210>`_, `#266 <https://github.com/notifiers/notifiers/pull/266>`_)
- Loosened dependencies (`#209 <https://github.com/notifiers/notifiers/issues/209>`_, `#266 <https://github.com/notifiers/notifiers/pull/271>`_)
- Added mimetype guessing for email (`#239 <https://github.com/notifiers/notifiers/issues/239>`_, `#266 <https://github.com/notifiers/notifiers/pull/271>`_)


1.0.4
------
Expand Down
18 changes: 18 additions & 0 deletions tests/providers/test_smtp.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from email.message import EmailMessage

import pytest

from notifiers.exceptions import BadArguments, NotificationError
Expand Down Expand Up @@ -64,6 +66,22 @@ def test_attachment(self, provider, tmpdir):
)
assert rsp.data["attachments"] == attachments

def test_attachment_mimetypes(self, provider, tmpdir):
dir_ = tmpdir.mkdir("sub")
file_1 = dir_.join("foo.txt")
file_1.write("foo")
file_2 = dir_.join("bar.jpg")
file_2.write("foo")
file_3 = dir_.join("baz.pdf")
file_3.write("foo")
attachments = [str(file_1), str(file_2), str(file_3)]
email = EmailMessage()
provider._add_attachments(attachments=attachments, email=email)
attach1, attach2, attach3 = email.iter_attachments()
assert attach1.get_content_type() == 'text/plain'
assert attach2.get_content_type() == 'image/jpeg'
assert attach3.get_content_type() == 'application/pdf'

@pytest.mark.online
def test_smtp_sanity(self, provider, test_message):
"""using Gmail SMTP"""
Expand Down

0 comments on commit d3b1225

Please sign in to comment.