Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Handle missing content-transfer-encoding better. #202

Open
wants to merge 20 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions django_mailbox/__init__.py
Original file line number Diff line number Diff line change
@@ -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"
19 changes: 16 additions & 3 deletions django_mailbox/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -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__)

Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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:
Expand Down
2 changes: 1 addition & 1 deletion django_mailbox/signals.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
from django.dispatch.dispatcher import Signal

message_received = Signal(providing_args=['message'])
message_received = Signal()
Original file line number Diff line number Diff line change
Expand Up @@ -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'])

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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'])

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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'])

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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'])

Expand Down
6 changes: 3 additions & 3 deletions django_mailbox/tests/test_process_email.py
Original file line number Diff line number Diff line change
Expand Up @@ -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']
Expand Down Expand Up @@ -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')
Expand Down
1 change: 1 addition & 0 deletions django_mailbox/transports/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
70 changes: 70 additions & 0 deletions django_mailbox/transports/office365.py
Original file line number Diff line number Diff line change
@@ -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
6 changes: 3 additions & 3 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand All @@ -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
Expand Down