mirror of
https://github.com/coddingtonbear/django-mailbox.git
synced 2026-07-09 22:38:19 +02:00
Merge branch 'master' into utf8-failing
This commit is contained in:
commit
0449e994d4
13 changed files with 64 additions and 18 deletions
2
.flake8
Normal file
2
.flake8
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
[flake8]
|
||||||
|
exclude = django_mailbox/migrations, django_mailbox/south_migrations
|
||||||
|
|
@ -3,6 +3,7 @@ python:
|
||||||
- '2.7'
|
- '2.7'
|
||||||
- '3.4'
|
- '3.4'
|
||||||
- '3.5'
|
- '3.5'
|
||||||
|
- '3.6'
|
||||||
env:
|
env:
|
||||||
matrix:
|
matrix:
|
||||||
- DJANGO='>=1.4,<1.5'
|
- DJANGO='>=1.4,<1.5'
|
||||||
|
|
@ -10,6 +11,7 @@ env:
|
||||||
- DJANGO='>=1.8,<1.9'
|
- DJANGO='>=1.8,<1.9'
|
||||||
- DJANGO='>=1.9,<1.10'
|
- DJANGO='>=1.9,<1.10'
|
||||||
- DJANGO='>=1.10,<1.11'
|
- DJANGO='>=1.10,<1.11'
|
||||||
|
- DJANGO='>=1.11,<2.0'
|
||||||
global:
|
global:
|
||||||
- secure: aswHU7pQroGM+GHoYlhXzzk2FYfqxXJORjqXPsbgoHAIu4Bssaj8+YAzIcdy3j9kSt4I8VBpjnn8H/wzQXki75JBZOosQrIeMK018+uR+ZMONBLqDYW/N7EJHLgZt9QXxQNKeZygrD4GN/Dc4gLHGvPQC/RfPuuHcnF0Liaahoo=
|
- secure: aswHU7pQroGM+GHoYlhXzzk2FYfqxXJORjqXPsbgoHAIu4Bssaj8+YAzIcdy3j9kSt4I8VBpjnn8H/wzQXki75JBZOosQrIeMK018+uR+ZMONBLqDYW/N7EJHLgZt9QXxQNKeZygrD4GN/Dc4gLHGvPQC/RfPuuHcnF0Liaahoo=
|
||||||
- secure: RZ6M6984P885GRoyx9n/WCCWGoFAzYpS8sZkXu3e/HK9oPXfaM2IEHjkq03jIC/FcWn/QMtFjOUBqQU94rnqdivFdFkeZHk1WUQgC0hztH3Qhh9zu2PNIrYUDpVD5dJqBpprWbSwFP8yNsJlP9A2RUubTlZblKHuaBhhiuNN+kU=
|
- secure: RZ6M6984P885GRoyx9n/WCCWGoFAzYpS8sZkXu3e/HK9oPXfaM2IEHjkq03jIC/FcWn/QMtFjOUBqQU94rnqdivFdFkeZHk1WUQgC0hztH3Qhh9zu2PNIrYUDpVD5dJqBpprWbSwFP8yNsJlP9A2RUubTlZblKHuaBhhiuNN+kU=
|
||||||
|
|
@ -23,10 +25,14 @@ matrix:
|
||||||
python: '3.4'
|
python: '3.4'
|
||||||
- env: DJANGO='>=1.4,<1.5'
|
- env: DJANGO='>=1.4,<1.5'
|
||||||
python: '3.5'
|
python: '3.5'
|
||||||
|
- env: DJANGO='>=1.4,<1.5'
|
||||||
|
python: '3.6'
|
||||||
- env: DJANGO='>=1.7,<1.8'
|
- env: DJANGO='>=1.7,<1.8'
|
||||||
python: '2.6'
|
python: '2.6'
|
||||||
- env: DJANGO='>=1.7,<1.8'
|
- env: DJANGO='>=1.7,<1.8'
|
||||||
python: '3.5'
|
python: '3.5'
|
||||||
|
- env: DJANGO='>=1.7,<1.8'
|
||||||
|
python: '3.6'
|
||||||
- env: DJANGO='>=1.8,<1.9'
|
- env: DJANGO='>=1.8,<1.9'
|
||||||
python: '2.6'
|
python: '2.6'
|
||||||
- env: DJANGO='>=1.9,<1.10'
|
- env: DJANGO='>=1.9,<1.10'
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,8 @@ def get_new_mail(mailbox_admin, request, queryset):
|
||||||
for mailbox in queryset.all():
|
for mailbox in queryset.all():
|
||||||
logger.debug('Receiving mail for %s' % mailbox)
|
logger.debug('Receiving mail for %s' % mailbox)
|
||||||
mailbox.get_new_mail()
|
mailbox.get_new_mail()
|
||||||
|
|
||||||
|
|
||||||
get_new_mail.short_description = 'Get new mail'
|
get_new_mail.short_description = 'Get new mail'
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -103,6 +105,7 @@ class MessageAdmin(admin.ModelAdmin):
|
||||||
)
|
)
|
||||||
actions = [resend_message_received_signal]
|
actions = [resend_message_received_signal]
|
||||||
|
|
||||||
|
|
||||||
if getattr(settings, 'DJANGO_MAILBOX_ADMIN_ENABLED', True):
|
if getattr(settings, 'DJANGO_MAILBOX_ADMIN_ENABLED', True):
|
||||||
admin.site.register(Message, MessageAdmin)
|
admin.site.register(Message, MessageAdmin)
|
||||||
admin.site.register(MessageAttachment, MessageAttachmentAdmin)
|
admin.site.register(MessageAttachment, MessageAttachmentAdmin)
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ class Command(BaseCommand):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
def handle(self, *args, **options):
|
def handle(self, *args, **options):
|
||||||
ATTACHMENT_HASH_MAP = {}
|
attachment_hash_map = {}
|
||||||
|
|
||||||
attachments_without_messages = MessageAttachment.objects.filter(
|
attachments_without_messages = MessageAttachment.objects.filter(
|
||||||
message=None
|
message=None
|
||||||
|
|
@ -38,7 +38,7 @@ class Command(BaseCommand):
|
||||||
md5 = hashlib.md5()
|
md5 = hashlib.md5()
|
||||||
for chunk in attachment.document.file.chunks():
|
for chunk in attachment.document.file.chunks():
|
||||||
md5.update(chunk)
|
md5.update(chunk)
|
||||||
ATTACHMENT_HASH_MAP[md5.hexdigest()] = attachment.pk
|
attachment_hash_map[md5.hexdigest()] = attachment.pk
|
||||||
|
|
||||||
for message_record in Message.objects.all().order_by('id'):
|
for message_record in Message.objects.all().order_by('id'):
|
||||||
message = email.message_from_string(message_record.body)
|
message = email.message_from_string(message_record.body)
|
||||||
|
|
@ -51,9 +51,9 @@ class Command(BaseCommand):
|
||||||
md5 = hashlib.md5()
|
md5 = hashlib.md5()
|
||||||
md5.update(part.get_payload(decode=True))
|
md5.update(part.get_payload(decode=True))
|
||||||
digest = md5.hexdigest()
|
digest = md5.hexdigest()
|
||||||
if digest in ATTACHMENT_HASH_MAP:
|
if digest in attachment_hash_map:
|
||||||
attachment = MessageAttachment.objects.get(
|
attachment = MessageAttachment.objects.get(
|
||||||
pk=ATTACHMENT_HASH_MAP[digest]
|
pk=attachment_hash_map[digest]
|
||||||
)
|
)
|
||||||
attachment.message = message_record
|
attachment.message = message_record
|
||||||
attachment.save()
|
attachment.save()
|
||||||
|
|
|
||||||
|
|
@ -225,6 +225,8 @@ class Mailbox(models.Model):
|
||||||
def process_incoming_message(self, message):
|
def process_incoming_message(self, message):
|
||||||
"""Process a message incoming to this mailbox."""
|
"""Process a message incoming to this mailbox."""
|
||||||
msg = self._process_message(message)
|
msg = self._process_message(message)
|
||||||
|
if msg is None:
|
||||||
|
return None
|
||||||
msg.outgoing = False
|
msg.outgoing = False
|
||||||
msg.save()
|
msg.save()
|
||||||
|
|
||||||
|
|
@ -235,6 +237,8 @@ class Mailbox(models.Model):
|
||||||
def record_outgoing_message(self, message):
|
def record_outgoing_message(self, message):
|
||||||
"""Record an outgoing message associated with this mailbox."""
|
"""Record an outgoing message associated with this mailbox."""
|
||||||
msg = self._process_message(message)
|
msg = self._process_message(message)
|
||||||
|
if msg is None:
|
||||||
|
return None
|
||||||
msg.outgoing = True
|
msg.outgoing = True
|
||||||
msg.save()
|
msg.save()
|
||||||
return msg
|
return msg
|
||||||
|
|
@ -363,7 +367,14 @@ class Mailbox(models.Model):
|
||||||
)
|
)
|
||||||
msg.save()
|
msg.save()
|
||||||
message = self._get_dehydrated_message(message, msg)
|
message = self._get_dehydrated_message(message, msg)
|
||||||
msg.set_body(message.as_string())
|
try:
|
||||||
|
body = message.as_string()
|
||||||
|
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
|
||||||
|
msg.set_body(body)
|
||||||
if message['in-reply-to']:
|
if message['in-reply-to']:
|
||||||
try:
|
try:
|
||||||
msg.in_reply_to = Message.objects.filter(
|
msg.in_reply_to = Message.objects.filter(
|
||||||
|
|
@ -382,6 +393,7 @@ class Mailbox(models.Model):
|
||||||
return new_mail
|
return new_mail
|
||||||
for message in connection.get_message(condition):
|
for message in connection.get_message(condition):
|
||||||
msg = self.process_incoming_message(message)
|
msg = self.process_incoming_message(message)
|
||||||
|
if not msg is None:
|
||||||
new_mail.append(msg)
|
new_mail.append(msg)
|
||||||
self.last_polling = now()
|
self.last_polling = now()
|
||||||
if django.VERSION >= (1, 5): # Django 1.5 introduces update_fields
|
if django.VERSION >= (1, 5): # Django 1.5 introduces update_fields
|
||||||
|
|
@ -734,7 +746,6 @@ class MessageAttachment(models.Model):
|
||||||
return EmailMessage()
|
return EmailMessage()
|
||||||
if not six.PY3:
|
if not six.PY3:
|
||||||
headers = headers.encode(settings['default_charset'], 'replace')
|
headers = headers.encode(settings['default_charset'], 'replace')
|
||||||
|
|
||||||
return email.message_from_string(headers)
|
return email.message_from_string(headers)
|
||||||
|
|
||||||
def _set_dehydrated_headers(self, email_object):
|
def _set_dehydrated_headers(self, email_object):
|
||||||
|
|
|
||||||
|
|
@ -171,7 +171,7 @@ class EmailMessageTestCase(TestCase):
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
def assertEqual(self, left, right):
|
def assertEqual(self, left, right): # noqa: N802
|
||||||
if not isinstance(left, email.message.Message):
|
if not isinstance(left, email.message.Message):
|
||||||
return super(EmailMessageTestCase, self).assertEqual(left, right)
|
return super(EmailMessageTestCase, self).assertEqual(left, right)
|
||||||
return self.compare_email_objects(left, right)
|
return self.compare_email_objects(left, right)
|
||||||
|
|
|
||||||
|
|
@ -240,7 +240,7 @@ class TestProcessEmail(EmailMessageTestCase):
|
||||||
|
|
||||||
success = True
|
success = True
|
||||||
try:
|
try:
|
||||||
msg = self.mailbox.process_incoming_message(email_object)
|
self.mailbox.process_incoming_message(email_object)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
success = False
|
success = False
|
||||||
|
|
||||||
|
|
@ -259,7 +259,8 @@ class TestProcessEmail(EmailMessageTestCase):
|
||||||
# this is the code of _process_message()
|
# this is the code of _process_message()
|
||||||
msg = Message()
|
msg = Message()
|
||||||
# if STORE_ORIGINAL_MESSAGE:
|
# if STORE_ORIGINAL_MESSAGE:
|
||||||
# msg.eml.save('%s.eml' % uuid.uuid4(), ContentFile(message), save=False)
|
# msg.eml.save('%s.eml' % uuid.uuid4(), ContentFile(message),
|
||||||
|
# save=False)
|
||||||
msg.mailbox = self.mailbox
|
msg.mailbox = self.mailbox
|
||||||
if 'subject' in message:
|
if 'subject' in message:
|
||||||
msg.subject = convert_header_to_unicode(message['subject'])[0:255]
|
msg.subject = convert_header_to_unicode(message['subject'])[0:255]
|
||||||
|
|
@ -385,7 +386,8 @@ class TestProcessEmail(EmailMessageTestCase):
|
||||||
self.assertEqual(expected_from, actual_from)
|
self.assertEqual(expected_from, actual_from)
|
||||||
|
|
||||||
def test_message_reply(self):
|
def test_message_reply(self):
|
||||||
email_object = EmailMessage('Test subject', # subject
|
email_object = EmailMessage(
|
||||||
|
'Test subject', # subject
|
||||||
'Test body', # body
|
'Test body', # body
|
||||||
'username@example.com', # from
|
'username@example.com', # from
|
||||||
['mr.test32@mail.ru'], # to
|
['mr.test32@mail.ru'], # to
|
||||||
|
|
|
||||||
|
|
@ -59,6 +59,7 @@ class IMAPTestCase(EmailMessageTestCase):
|
||||||
return FAKE_UID_FETCH_SIZES
|
return FAKE_UID_FETCH_SIZES
|
||||||
if arg2 == '(RFC822)':
|
if arg2 == '(RFC822)':
|
||||||
return FAKE_UID_FETCH_MSG
|
return FAKE_UID_FETCH_MSG
|
||||||
|
|
||||||
def imap_server_list_method(pattern=None):
|
def imap_server_list_method(pattern=None):
|
||||||
return FAKE_LIST_ARCHIVE_FOLDERS_ANSWERS
|
return FAKE_LIST_ARCHIVE_FOLDERS_ANSWERS
|
||||||
|
|
||||||
|
|
@ -88,6 +89,7 @@ class TestImapTransport(IMAPTestCase):
|
||||||
expected_message = self._get_email_object('generic_message.eml')
|
expected_message = self._get_email_object('generic_message.eml')
|
||||||
self.assertEqual(expected_message, actual_message)
|
self.assertEqual(expected_message, actual_message)
|
||||||
|
|
||||||
|
|
||||||
class TestImapArchivedTransport(TestImapTransport):
|
class TestImapArchivedTransport(TestImapTransport):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(TestImapArchivedTransport, self).setUp()
|
super(TestImapArchivedTransport, self).setUp()
|
||||||
|
|
@ -100,6 +102,7 @@ class TestImapArchivedTransport(TestImapTransport):
|
||||||
)
|
)
|
||||||
self.transport.server = self.imap_server
|
self.transport.server = self.imap_server
|
||||||
|
|
||||||
|
|
||||||
class TestMaxSizeImapTransport(TestImapTransport):
|
class TestMaxSizeImapTransport(TestImapTransport):
|
||||||
|
|
||||||
@override_settings(DJANGO_MAILBOX_MAX_MESSAGE_SIZE=5807)
|
@override_settings(DJANGO_MAILBOX_MAX_MESSAGE_SIZE=5807)
|
||||||
|
|
@ -115,7 +118,9 @@ class TestMaxSizeImapTransport(TestImapTransport):
|
||||||
|
|
||||||
def test_size_limit(self):
|
def test_size_limit(self):
|
||||||
all_message_ids = self.transport._get_all_message_ids()
|
all_message_ids = self.transport._get_all_message_ids()
|
||||||
small_message_ids = self.transport._get_small_message_ids(all_message_ids)
|
small_message_ids = self.transport._get_small_message_ids(
|
||||||
|
all_message_ids,
|
||||||
|
)
|
||||||
self.assertEqual(len(small_message_ids), 1)
|
self.assertEqual(len(small_message_ids), 1)
|
||||||
|
|
||||||
def test_get_email_message(self):
|
def test_get_email_message(self):
|
||||||
|
|
@ -163,4 +168,3 @@ class TestPop3Transport(EmailMessageTestCase):
|
||||||
expected_message = self._get_email_object('generic_message.eml')
|
expected_message = self._get_email_object('generic_message.eml')
|
||||||
|
|
||||||
self.assertEqual(expected_message, actual_message)
|
self.assertEqual(expected_message, actual_message)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
# all imports below are only used by external modules
|
||||||
|
# flake8: noqa
|
||||||
from django_mailbox.transports.imap import ImapTransport
|
from django_mailbox.transports.imap import ImapTransport
|
||||||
from django_mailbox.transports.pop3 import Pop3Transport
|
from django_mailbox.transports.pop3 import Pop3Transport
|
||||||
from django_mailbox.transports.maildir import MaildirTransport
|
from django_mailbox.transports.maildir import MaildirTransport
|
||||||
|
|
|
||||||
|
|
@ -4,9 +4,9 @@ import six
|
||||||
|
|
||||||
# Do *not* remove this, we need to use this in subclasses of EmailTransport
|
# Do *not* remove this, we need to use this in subclasses of EmailTransport
|
||||||
if six.PY3:
|
if six.PY3:
|
||||||
from email.errors import MessageParseError
|
from email.errors import MessageParseError # noqa: F401
|
||||||
else:
|
else:
|
||||||
from email.Errors import MessageParseError
|
from email.Errors import MessageParseError # noqa: F401
|
||||||
|
|
||||||
|
|
||||||
class EmailTransport(object):
|
class EmailTransport(object):
|
||||||
|
|
|
||||||
|
|
@ -89,7 +89,7 @@ def convert_header_to_unicode(header):
|
||||||
header,
|
header,
|
||||||
default_charset,
|
default_charset,
|
||||||
)
|
)
|
||||||
return unicode(header, default_charset, 'replace')
|
return header.decode(default_charset, 'replace')
|
||||||
|
|
||||||
|
|
||||||
def get_body_from_message(message, maintype, subtype):
|
def get_body_from_message(message, maintype, subtype):
|
||||||
|
|
|
||||||
12
setup.py
12
setup.py
|
|
@ -31,10 +31,22 @@ setup(
|
||||||
'Intended Audience :: Developers',
|
'Intended Audience :: Developers',
|
||||||
'License :: OSI Approved :: MIT License',
|
'License :: OSI Approved :: MIT License',
|
||||||
'Operating System :: OS Independent',
|
'Operating System :: OS Independent',
|
||||||
|
'Framework :: Django',
|
||||||
|
'Framework :: Django :: 1.4',
|
||||||
|
'Framework :: Django :: 1.5',
|
||||||
|
'Framework :: Django :: 1.6',
|
||||||
|
'Framework :: Django :: 1.7',
|
||||||
|
'Framework :: Django :: 1.8',
|
||||||
|
'Framework :: Django :: 1.9',
|
||||||
|
'Framework :: Django :: 1.10',
|
||||||
|
'Framework :: Django :: 1.11',
|
||||||
'Programming Language :: Python',
|
'Programming Language :: Python',
|
||||||
'Programming Language :: Python :: 2.6',
|
'Programming Language :: Python :: 2.6',
|
||||||
'Programming Language :: Python :: 2.7',
|
'Programming Language :: Python :: 2.7',
|
||||||
'Programming Language :: Python :: 3',
|
'Programming Language :: Python :: 3',
|
||||||
|
'Programming Language :: Python :: 3.4',
|
||||||
|
'Programming Language :: Python :: 3.5',
|
||||||
|
'Programming Language :: Python :: 3.6',
|
||||||
'Topic :: Communications :: Email',
|
'Topic :: Communications :: Email',
|
||||||
'Topic :: Communications :: Email :: Post-Office',
|
'Topic :: Communications :: Email :: Post-Office',
|
||||||
'Topic :: Communications :: Email :: Post-Office :: IMAP',
|
'Topic :: Communications :: Email :: Post-Office :: IMAP',
|
||||||
|
|
|
||||||
6
tox.ini
6
tox.ini
|
|
@ -1,5 +1,5 @@
|
||||||
[tox]
|
[tox]
|
||||||
envlist=py27,py34,py35
|
envlist=py27,py34,py35,flake8
|
||||||
|
|
||||||
[testenv]
|
[testenv]
|
||||||
deps=
|
deps=
|
||||||
|
|
@ -8,3 +8,7 @@ deps=
|
||||||
sitepackages=False
|
sitepackages=False
|
||||||
commands=
|
commands=
|
||||||
py.test {posargs}
|
py.test {posargs}
|
||||||
|
|
||||||
|
[testenv:flake8]
|
||||||
|
deps=flake8
|
||||||
|
commands=flake8 django_mailbox
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue