From 41dd2ec645477e383b82f740831f8ebedb44914b Mon Sep 17 00:00:00 2001 From: Jabrail Lezgintsev Date: Wed, 23 May 2018 11:53:49 +0300 Subject: [PATCH 1/4] Added initial smtp transport. --- .gitignore | 2 ++ django_mailbox/transports/smtp.py | 49 +++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+) create mode 100644 django_mailbox/transports/smtp.py diff --git a/.gitignore b/.gitignore index 804fef4..5180805 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,5 @@ dummy_project/* .tox/ messages *.sqlite3 +venv/* +.idea/ diff --git a/django_mailbox/transports/smtp.py b/django_mailbox/transports/smtp.py new file mode 100644 index 0000000..c6ed772 --- /dev/null +++ b/django_mailbox/transports/smtp.py @@ -0,0 +1,49 @@ +import smtplib + +from email.mime.multipart import MIMEMultipart +from email.mime.text import MIMEText + +# from django.conf import settings +# from django.core.mail import EmailMessage + +from django_mailbox.transports.base import EmailTransport, MessageParseError + + +class SMTPTransport(EmailTransport): + def __init__(self, + hostname, port=None, ssl=False, tls=False): + self.hostname = hostname + self.port = port + self.tls = tls + + if ssl: + self.transport = smtplib.SMTP_SSL + if not self.port: + self.port = 465 + else: + self.transport = smtplib.SMTP + if not self.port: + self.port = 25 + + def connect(self, username, password): + self.server = self.transport(self.hostname, self.port) + if self.tls: + self.server.starttls() + + typ, msg = self.server.login(user=username, password=password) + + # TODO Add abilities for html, image sending. + def send_message(self, subject, message, from_email, recipient_list): + msg = MIMEMultipart(message) + + msg_text = MIMEText(message, 'plain') + + msg.attach(msg_text) + + msg['From'] = from_email + msg['To'] = recipient_list + msg['Subject'] = subject + + self.server.sendmail(msg['From'], msg['To'], msg.as_string()) + self.server.quit() + From 304bebf1476012afa92e7df594cc0c0c24c84462 Mon Sep 17 00:00:00 2001 From: Jabrail Lezgintsev Date: Wed, 23 May 2018 14:25:44 +0300 Subject: [PATCH 2/4] Minor refactoring, added logic for sending mails to models.py. --- django_mailbox/models.py | 19 ++++++++++++++++++- django_mailbox/transports/__init__.py | 1 + django_mailbox/transports/smtp.py | 10 +++++----- 3 files changed, 24 insertions(+), 6 deletions(-) diff --git a/django_mailbox/models.py b/django_mailbox/models.py index 9f7ffc2..b4ee357 100644 --- a/django_mailbox/models.py +++ b/django_mailbox/models.py @@ -34,7 +34,7 @@ from django_mailbox import utils from django_mailbox.signals import message_received from django_mailbox.transports import Pop3Transport, ImapTransport, \ MaildirTransport, MboxTransport, BabylTransport, MHTransport, \ - MMDFTransport, GmailImapTransport + MMDFTransport, GmailImapTransport, SMTPTransport logger = logging.getLogger(__name__) @@ -213,6 +213,14 @@ class Mailbox(models.Model): ssl=self.use_ssl ) conn.connect(self.username, self.password) + elif self.type == 'smtp': + conn = SMTPTransport( + self.location, + port=self.port if self.port else None, + ssl=self.use_ssl, + tls=self.use_tls + ) + conn.connect(self.username, self.password) elif self.type == 'maildir': conn = MaildirTransport(self.location) elif self.type == 'mbox': @@ -420,6 +428,14 @@ class Mailbox(models.Model): else: self.save() + def send_mail(self, subject, to, body): + connection = self.get_connection() + if not connection: + return + connection.connect(self.username, self.password) + connection.send_message(subject, body, self.username, to) + return 'Success' + def __str__(self): return self.name @@ -821,3 +837,4 @@ class MessageAttachment(models.Model): class Meta: verbose_name = _('Message attachment') verbose_name_plural = _('Message attachments') + diff --git a/django_mailbox/transports/__init__.py b/django_mailbox/transports/__init__.py index 7aeef95..f2cf3e4 100644 --- a/django_mailbox/transports/__init__.py +++ b/django_mailbox/transports/__init__.py @@ -8,3 +8,4 @@ from django_mailbox.transports.babyl import BabylTransport from django_mailbox.transports.mh import MHTransport from django_mailbox.transports.mmdf import MMDFTransport from django_mailbox.transports.gmail import GmailImapTransport +from django_mailbox.transports.smtp import SMTPTransport diff --git a/django_mailbox/transports/smtp.py b/django_mailbox/transports/smtp.py index c6ed772..fafb642 100644 --- a/django_mailbox/transports/smtp.py +++ b/django_mailbox/transports/smtp.py @@ -6,15 +6,16 @@ from email.mime.text import MIMEText # from django.conf import settings # from django.core.mail import EmailMessage -from django_mailbox.transports.base import EmailTransport, MessageParseError +from django_mailbox.transports.base import EmailTransport class SMTPTransport(EmailTransport): - def __init__(self, - hostname, port=None, ssl=False, tls=False): + def __init__(self, hostname, port=None, + ssl=False, tls=False): self.hostname = hostname self.port = port self.tls = tls + self.server = None if ssl: self.transport = smtplib.SMTP_SSL @@ -30,7 +31,7 @@ class SMTPTransport(EmailTransport): if self.tls: self.server.starttls() - typ, msg = self.server.login(user=username, password=password) + _ = self.server.login(user=username, password=password) # TODO Add abilities for html, image sending. def send_message(self, subject, message, from_email, recipient_list): @@ -46,4 +47,3 @@ class SMTPTransport(EmailTransport): self.server.sendmail(msg['From'], msg['To'], msg.as_string()) self.server.quit() - From e3c57c3c14023e58d9b4a718b4d38bbed40d6b0b Mon Sep 17 00:00:00 2001 From: Jabrail Lezgintsev Date: Wed, 23 May 2018 15:56:12 +0300 Subject: [PATCH 3/4] Added custom command for mail sending --- .../management/commands/sendmail.py | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 django_mailbox/management/commands/sendmail.py diff --git a/django_mailbox/management/commands/sendmail.py b/django_mailbox/management/commands/sendmail.py new file mode 100644 index 0000000..f78bb07 --- /dev/null +++ b/django_mailbox/management/commands/sendmail.py @@ -0,0 +1,27 @@ +from django.core.management.base import BaseCommand + +from django_mailbox.models import Mailbox + + +class Command(BaseCommand): + help = 'Sends an e-mail to specified recipient' + + def add_arguments(self, parser): + parser.add_argument('from', type=int, help='ID (pk) for the mailbox you want to use to send an e-mail') + parser.add_argument('to', help='E-mail address of recipient') + parser.add_argument('subject', help='Subject of letter') + parser.add_argument('body', help='Message') + + def handle(self, *args, **options): + + from_email = options.get('from') + to = options.get('to') + subject = options.get('subject') + body = options.get('body') + + mailbox = Mailbox.objects.get(pk=from_email) + self.stdout.write(self.style.WARNING("-"*36 + "\nFrom: %s \nTo: %s \nSubject: %s \nMessage: %s \n" % + (mailbox.from_email, + to, subject, body) + '-'*36)) + if mailbox.send_mail(options.get('subject'), options.get('to'), options.get('body')): + self.stdout.write(self.style.SUCCESS('The letter is sent.')) From 9bc97119e37f25e9329d96ae3c259ddf14d2ea22 Mon Sep 17 00:00:00 2001 From: Jabrail Lezgintsev Date: Wed, 23 May 2018 16:49:26 +0300 Subject: [PATCH 4/4] Added tests, refactoring --- .../management/commands/sendmail.py | 13 +++++----- django_mailbox/tests/test_transports.py | 24 ++++++++++++++++++- django_mailbox/transports/smtp.py | 22 +++++++++-------- 3 files changed, 41 insertions(+), 18 deletions(-) diff --git a/django_mailbox/management/commands/sendmail.py b/django_mailbox/management/commands/sendmail.py index f78bb07..87fdf57 100644 --- a/django_mailbox/management/commands/sendmail.py +++ b/django_mailbox/management/commands/sendmail.py @@ -7,13 +7,12 @@ class Command(BaseCommand): help = 'Sends an e-mail to specified recipient' def add_arguments(self, parser): - parser.add_argument('from', type=int, help='ID (pk) for the mailbox you want to use to send an e-mail') - parser.add_argument('to', help='E-mail address of recipient') - parser.add_argument('subject', help='Subject of letter') - parser.add_argument('body', help='Message') + parser.add_argument('from', type=int, help='ID (pk) for the mailbox you want to use to send an e-mail') + parser.add_argument('to', type=str, help='E-mail address of recipient') + parser.add_argument('subject', type=str, help='Subject of letter') + parser.add_argument('body', type=str, help='Message') def handle(self, *args, **options): - from_email = options.get('from') to = options.get('to') subject = options.get('subject') @@ -22,6 +21,6 @@ class Command(BaseCommand): mailbox = Mailbox.objects.get(pk=from_email) self.stdout.write(self.style.WARNING("-"*36 + "\nFrom: %s \nTo: %s \nSubject: %s \nMessage: %s \n" % (mailbox.from_email, - to, subject, body) + '-'*36)) - if mailbox.send_mail(options.get('subject'), options.get('to'), options.get('body')): + to, subject, body) + '-'*36)) + if mailbox.send_mail(subject, to, body): self.stdout.write(self.style.SUCCESS('The letter is sent.')) diff --git a/django_mailbox/tests/test_transports.py b/django_mailbox/tests/test_transports.py index 1d3c68f..a679bd6 100644 --- a/django_mailbox/tests/test_transports.py +++ b/django_mailbox/tests/test_transports.py @@ -4,7 +4,7 @@ import six from django.test.utils import override_settings from django_mailbox.tests.base import EmailMessageTestCase, get_email_as_text -from django_mailbox.transports import ImapTransport, Pop3Transport +from django_mailbox.transports import ImapTransport, Pop3Transport, SMTPTransport FAKE_UID_SEARCH_ANSWER = ( 'OK', @@ -168,3 +168,25 @@ class TestPop3Transport(EmailMessageTestCase): expected_message = self._get_email_object('generic_message.eml') self.assertEqual(expected_message, actual_message) + + +class TestSMTPTransport(EmailMessageTestCase): + class TestSMTPTransport(EmailMessageTestCase): + def setUp(self): + self.arbitrary_hostname = '' + self.arbitrary_port = 465 + self.ssl = True + self.transport = SMTPTransport( + self.arbitrary_hostname, + self.arbitrary_port, + self.ssl + ) + + def test_sending_messages(self): + with mock.patch('SMTPTransport') as mock_smtp: + from_address = 'from@domain.com' + to_address = 'to@domain.com' + + smtp = mock_smtp('smtp.domain.com') + + smtp.send_message(from_address, [to_address], 'subject', 'message') diff --git a/django_mailbox/transports/smtp.py b/django_mailbox/transports/smtp.py index fafb642..8a80225 100644 --- a/django_mailbox/transports/smtp.py +++ b/django_mailbox/transports/smtp.py @@ -1,10 +1,8 @@ -import smtplib +from smtplib import SMTP, SMTP_SSL from email.mime.multipart import MIMEMultipart from email.mime.text import MIMEText - -# from django.conf import settings -# from django.core.mail import EmailMessage +from email.mime.image import MIMEImage from django_mailbox.transports.base import EmailTransport @@ -18,11 +16,11 @@ class SMTPTransport(EmailTransport): self.server = None if ssl: - self.transport = smtplib.SMTP_SSL + self.transport = SMTP_SSL if not self.port: self.port = 465 else: - self.transport = smtplib.SMTP + self.transport = SMTP if not self.port: self.port = 25 @@ -33,11 +31,15 @@ class SMTPTransport(EmailTransport): _ = self.server.login(user=username, password=password) - # TODO Add abilities for html, image sending. - def send_message(self, subject, message, from_email, recipient_list): - msg = MIMEMultipart(message) + def send_message(self, subject, message, from_email, recipient_list, img=None, html=False): + msg = MIMEMultipart() - msg_text = MIMEText(message, 'plain') + msg_text = MIMEText(message, 'plain' if not html else 'html') + + if img: + with open(img, 'rb') as fp: + msg_img = MIMEImage(fp.read()) + msg.attach(msg_img) msg.attach(msg_text)