diff --git a/django_mailbox/__init__.py b/django_mailbox/__init__.py index feda890b..3b7b8a34 100644 --- a/django_mailbox/__init__.py +++ b/django_mailbox/__init__.py @@ -1,3 +1,3 @@ -__version__ = '4.8.2' +__version__ = "4.8.2" -default_app_config = 'django_mailbox.apps.MailBoxConfig' +default_app_config = "django_mailbox.apps.MailBoxConfig" diff --git a/django_mailbox/models.py b/django_mailbox/models.py index 0f0ac9d2..30364407 100644 --- a/django_mailbox/models.py +++ b/django_mailbox/models.py @@ -31,7 +31,7 @@ from django_mailbox.signals import message_received from django_mailbox.transports import Pop3Transport, ImapTransport, \ MaildirTransport, MboxTransport, BabylTransport, MHTransport, \ - MMDFTransport, GmailImapTransport + MMDFTransport, GmailImapTransport, Office365Transport logger = logging.getLogger(__name__) @@ -216,6 +216,14 @@ def get_connection(self): archive=self.archive ) conn.connect(self.username, self.password) + elif self.type == 'office365': + conn = Office365Transport( + self.location, + self.username, + folder=self.folder, + archive=self.archive + ) + conn.connect() elif self.type == 'pop3': conn = Pop3Transport( self.location, @@ -382,8 +390,13 @@ def _process_message(self, message): except KeyError as exc: # email.message.replace_header may raise 'KeyError' if the header # 'content-transfer-encoding' is missing - logger.warning("Failed to parse message: %s", exc,) - return None + try: + # Before we give up, let's try mailman's approach: + # https://bugs.python.org/msg308362 + body = message.as_bytes(self).decode('ascii', 'replace') + except KeyError as exc: + logger.warning("Failed to parse message: %s", exc,) + return None msg.set_body(body) if message['in-reply-to']: try: diff --git a/django_mailbox/signals.py b/django_mailbox/signals.py index b445183e..9af4af77 100644 --- a/django_mailbox/signals.py +++ b/django_mailbox/signals.py @@ -1,3 +1,3 @@ from django.dispatch.dispatcher import Signal -message_received = Signal(providing_args=['message']) +message_received = Signal() diff --git a/django_mailbox/south_migrations/0006_auto__add_field_message_in_reply_to.py b/django_mailbox/south_migrations/0006_auto__add_field_message_in_reply_to.py index 4287fb26..9c17019a 100644 --- a/django_mailbox/south_migrations/0006_auto__add_field_message_in_reply_to.py +++ b/django_mailbox/south_migrations/0006_auto__add_field_message_in_reply_to.py @@ -15,8 +15,8 @@ def forwards(self, orm): # Adding M2M table for field references on 'Message' db.create_table('django_mailbox_message_references', ( ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)), - ('from_message', models.ForeignKey(orm['django_mailbox.message'], null=False)), - ('to_message', models.ForeignKey(orm['django_mailbox.message'], null=False)) + ('from_message', models.ForeignKey(orm['django_mailbox.message'], on_delete=models.CASCADE, null=False)), + ('to_message', models.ForeignKey(orm['django_mailbox.message'], on_delete=models.CASCADE, null=False)) )) db.create_unique('django_mailbox_message_references', ['from_message_id', 'to_message_id']) diff --git a/django_mailbox/south_migrations/0009_remove_references_table.py b/django_mailbox/south_migrations/0009_remove_references_table.py index 38685851..70cf0dec 100644 --- a/django_mailbox/south_migrations/0009_remove_references_table.py +++ b/django_mailbox/south_migrations/0009_remove_references_table.py @@ -15,8 +15,8 @@ def backwards(self, orm): # Adding M2M table for field references on 'Message' db.create_table('django_mailbox_message_references', ( ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)), - ('from_message', models.ForeignKey(orm['django_mailbox.message'], null=False)), - ('to_message', models.ForeignKey(orm['django_mailbox.message'], null=False)) + ('from_message', models.ForeignKey(orm['django_mailbox.message'], on_delete=models.CASCADE, null=False)), + ('to_message', models.ForeignKey(orm['django_mailbox.message'], on_delete=models.CASCADE, null=False)) )) db.create_unique('django_mailbox_message_references', ['from_message_id', 'to_message_id']) diff --git a/django_mailbox/south_migrations/0012_auto__add_messageattachment.py b/django_mailbox/south_migrations/0012_auto__add_messageattachment.py index b62e50e9..945be81b 100644 --- a/django_mailbox/south_migrations/0012_auto__add_messageattachment.py +++ b/django_mailbox/south_migrations/0012_auto__add_messageattachment.py @@ -17,8 +17,8 @@ def forwards(self, orm): # Adding M2M table for field attachments on 'Message' db.create_table('django_mailbox_message_attachments', ( ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)), - ('message', models.ForeignKey(orm['django_mailbox.message'], null=False)), - ('messageattachment', models.ForeignKey(orm['django_mailbox.messageattachment'], null=False)) + ('message', models.ForeignKey(orm['django_mailbox.message'], on_delete=models.CASCADE, null=False)), + ('messageattachment', models.ForeignKey(orm['django_mailbox.messageattachment'], on_delete=models.CASCADE, null=False)) )) db.create_unique('django_mailbox_message_attachments', ['message_id', 'messageattachment_id']) diff --git a/django_mailbox/south_migrations/0015_auto__add_field_messageattachment_headers.py b/django_mailbox/south_migrations/0015_auto__add_field_messageattachment_headers.py index 71ecfbdc..da15c664 100644 --- a/django_mailbox/south_migrations/0015_auto__add_field_messageattachment_headers.py +++ b/django_mailbox/south_migrations/0015_auto__add_field_messageattachment_headers.py @@ -20,8 +20,8 @@ def backwards(self, orm): # Adding M2M table for field attachments on 'Message' db.create_table('django_mailbox_message_attachments', ( ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)), - ('message', models.ForeignKey(orm['django_mailbox.message'], null=False)), - ('messageattachment', models.ForeignKey(orm['django_mailbox.messageattachment'], null=False)) + ('message', models.ForeignKey(orm['django_mailbox.message'], on_delete=models.CASCADE, null=False)), + ('messageattachment', models.ForeignKey(orm['django_mailbox.messageattachment'], on_delete=models.CASCADE, null=False)) )) db.create_unique('django_mailbox_message_attachments', ['message_id', 'messageattachment_id']) diff --git a/django_mailbox/tests/test_process_email.py b/django_mailbox/tests/test_process_email.py index a6dbd044..ee71f4cc 100644 --- a/django_mailbox/tests/test_process_email.py +++ b/django_mailbox/tests/test_process_email.py @@ -9,7 +9,7 @@ from django_mailbox.utils import convert_header_to_unicode from django_mailbox import utils from django_mailbox.tests.base import EmailMessageTestCase -from django.utils.encoding import force_text +from django.utils.encoding import force_str from django.core.mail import EmailMessage __all__ = ['TestProcessEmail'] @@ -367,12 +367,12 @@ def test_message_with_long_content(self): email_object = self._get_email_object( 'message_with_long_content.eml', ) - size = len(force_text(email_object.as_string())) + size = len(force_str(email_object.as_string())) msg = self.mailbox.process_incoming_message(email_object) self.assertEqual(size, - len(force_text(msg.get_email_object().as_string()))) + len(force_str(msg.get_email_object().as_string()))) def test_message_saved(self): message = self._get_email_object('generic_message.eml') diff --git a/django_mailbox/transports/__init__.py b/django_mailbox/transports/__init__.py index 7aeef95e..9f733e34 100644 --- a/django_mailbox/transports/__init__.py +++ b/django_mailbox/transports/__init__.py @@ -8,3 +8,4 @@ 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.office365 import Office365Transport diff --git a/django_mailbox/transports/office365.py b/django_mailbox/transports/office365.py new file mode 100644 index 00000000..053fc8dd --- /dev/null +++ b/django_mailbox/transports/office365.py @@ -0,0 +1,70 @@ +import logging +import os + +from django.conf import settings + +from .base import EmailTransport, MessageParseError + +logger = logging.getLogger(__name__) + + +class Office365Transport(EmailTransport): + def __init__( + self, hostname, username, archive='', folder=None + ): + self.integration_testing_subject = getattr( + settings, + 'DJANGO_MAILBOX_INTEGRATION_TESTING_SUBJECT', + None + ) + self.hostname = hostname + self.username = username + self.archive = archive + self.folder = folder + + def connect(self): + try: + import O365 + except ImportError: + raise ValueError( + "Install o365 to use oauth2 auth for office365" + ) + + credentials = (settings.MICROSOFT_O365_CLIENT_ID, settings.MICROSOFT_O365_CLIENT_SECRET) + + if not os.path.isdir(settings.O365_TOKEN_PATH): + os.mkdir(settings.O365_TOKEN_PATH) + + token_path=os.path.join(settings.O365_TOKEN_PATH, 'token.txt') + self.account = O365.Account(credentials, auth_flow_type='credentials', tenant_id=settings.MICROSOFT_O365_TENENT_ID, token_path=token_path) + self.account.authenticate() + + self.mailbox = self.account.mailbox(resource=self.username) + self.mailbox_folder = self.mailbox.inbox_folder() + if self.folder: + self.mailbox_folder = self.mailbox.get_folder(folder_name=self.folder) + + def get_message(self, condition=None): + archive_folder = None + if self.archive: + archive_folder = self.mailbox.get_folder(folder_name=self.archive) + if not archive_folder: + archive_folder = self.mailbox.create_child_folder(self.archive) + + for o365message in self.mailbox_folder.get_messages(order_by='receivedDateTime'): + try: + mime_content = o365message.get_mime_content() + message = self.get_email_from_bytes(mime_content) + + if condition and not condition(message): + continue + + yield message + except MessageParseError: + continue + + if self.archive and archive_folder: + o365message.copy(archive_folder) + + o365message.delete() + return diff --git a/docs/conf.py b/docs/conf.py index 974706f8..c015980d 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -244,7 +244,7 @@ import inspect import django from django.utils.html import strip_tags -from django.utils.encoding import force_text +from django.utils.encoding import force_str from django.conf import settings settings.configure(INSTALLED_APPS=['django_mailbox', ]) django.setup() @@ -265,11 +265,11 @@ def process_docstring(app, what, name, obj, options, lines): continue # Decode and strip any html out of the field's help text - help_text = strip_tags(force_text(field.help_text)) + help_text = strip_tags(force_str(field.help_text)) # Decode and capitalize the verbose name, for use if there isn't # any help text - verbose_name = force_text(field.verbose_name).capitalize() + verbose_name = force_str(field.verbose_name).capitalize() if help_text: # Add the model field to the end of the docstring as a param