Internet "
+"transports include 'imap' and 'pop3'; common local file transports include "
+"'maildir', 'mbox', and less commonly 'babyl', 'mh', and 'mmdf'.
Be sure to urlencode your username and password should they contain illegal "
+"characters (like @, :, etc)."
+msgstr ""
+"Пример: imap+ssl://myusername:mypassword@someserver
Интернет-"
+"транспорт может быть 'imap' или 'pop3'; поддерживаются локальные файловые "
+"транспорты 'maildir', 'mbox', а также 'babyl', 'mh', and 'mmdf'.
Используйте urlencode, если имя пользователя или пароль содержат "
+"недопустипые символы (@, :, и т.д.)."
+
+#: django_mailbox/models.py:72
+msgid "From email"
+msgstr "От"
+
+#: django_mailbox/models.py:75
+msgid ""
+"Example: MailBot <mailbot@yourdomain.com> 'From' header to set "
+"for outgoing email.
If you do not use this e-mail inbox for "
+"outgoing mail, this setting is unnecessary. If you send e-mail without "
+"setting this, your 'From' header will'be set to match the setting "
+"`DEFAULT_FROM_EMAIL`."
+msgstr ""
+"Пример: MailBot <mailbot@yourdomain.com> Исходящая электронная "
+"почта.
Если вы не используете этот почтовый ящик для исходящей "
+"почты, этот параметр не нужен. Если вы не указали данный параметр, "
+"будет использоваться указанный в настройках `DEFAULT_FROM_EMAIL`."
+
+#: django_mailbox/models.py:89
+msgid "Active"
+msgstr "Активный"
+
+#: django_mailbox/models.py:91
+msgid ""
+"Check this e-mail inbox for new e-mail messages during polling cycles. This "
+"checkbox does not have an effect upon whether mail is collected here when "
+"this mailbox receives mail from a pipe, and does not affect whether e-mail "
+"messages can be dispatched from this mailbox. "
+msgstr ""
+"Параметр указывает на необходимость проверки почтового ящика на наличие "
+"новых сообщений в цикле опроса. Этот флажок не влияет на сбор почты, когда "
+"этот почтовый ящик получает почту из канала и не влияет на отправку "
+"сообщений электронной почты из этого почтового ящика."
+
+#: django_mailbox/models.py:102
+msgid "Last polling"
+msgstr "Последний опрос"
+
+#: django_mailbox/models.py:103
+msgid ""
+"The time of last successful polling for messages.It is blank for new "
+"mailboxes and is not set for mailboxes that only receive messages via a pipe."
+msgstr ""
+"Время последнего успешного опроса сообщений. Для нового почтового ящика не "
+"установлено. Также не устанавливается для почтовых ящиков "
+"обновляющих сообщения через pipe."
+
+#: django_mailbox/models.py:409 django_mailbox/models.py:438
+msgid "Mailbox"
+msgstr "Почтовый ящик"
+
+#: django_mailbox/models.py:410
+msgid "Mailboxes"
+msgstr "Почтовые ящики"
+
+#: django_mailbox/models.py:442
+msgid "Subject"
+msgstr "Тема"
+
+#: django_mailbox/models.py:447
+msgid "Message ID"
+msgstr "Идентификатор сообщения"
+
+#: django_mailbox/models.py:456
+msgid "In reply to"
+msgstr "В ответ на"
+
+#: django_mailbox/models.py:460
+msgid "From header"
+msgstr "От(From)"
+
+#: django_mailbox/models.py:465
+msgid "To header"
+msgstr "Кому(To)"
+
+#: django_mailbox/models.py:469
+msgid "Outgoing"
+msgstr "Исходящее"
+
+#: django_mailbox/models.py:475
+msgid "Body"
+msgstr "Тело"
+
+#: django_mailbox/models.py:479
+msgid "Encoded"
+msgstr "Закодировано"
+
+#: django_mailbox/models.py:481
+msgid "True if the e-mail body is Base64 encoded"
+msgstr "True если тело сообщения закодировано в Base64"
+
+#: django_mailbox/models.py:485
+msgid "Processed"
+msgstr "Обработано"
+
+#: django_mailbox/models.py:490
+msgid "Read"
+msgstr "Прочитано"
+
+#: django_mailbox/models.py:497
+msgid "Raw message contents"
+msgstr "Исходное содержимое сообщения"
+
+#: django_mailbox/models.py:500
+msgid "Original full content of message"
+msgstr "Полное содержимое сообщения"
+
+#: django_mailbox/models.py:716
+msgid "E-mail message"
+msgstr "Сообщение"
+
+#: django_mailbox/models.py:717
+msgid "E-mail messages"
+msgstr "Сообщения"
+
+#: django_mailbox/models.py:726
+msgid "Message"
+msgstr "Сообщение"
+
+#: django_mailbox/models.py:730
+msgid "Headers"
+msgstr "Заголовки"
+
+#: django_mailbox/models.py:736
+msgid "Document"
+msgstr "Документ"
+
+#: django_mailbox/models.py:793
+msgid "Message attachment"
+msgstr "Вложение"
+
+#: django_mailbox/models.py:794
+msgid "Message attachments"
+msgstr "Вложения"
diff --git a/build/lib/django_mailbox/management/__init__.py b/build/lib/django_mailbox/management/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/build/lib/django_mailbox/management/commands/__init__.py b/build/lib/django_mailbox/management/commands/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/build/lib/django_mailbox/management/commands/getmail.py b/build/lib/django_mailbox/management/commands/getmail.py
new file mode 100644
index 00000000..31b2dbdc
--- /dev/null
+++ b/build/lib/django_mailbox/management/commands/getmail.py
@@ -0,0 +1,30 @@
+import logging
+
+from django.core.management.base import BaseCommand
+
+from django_mailbox.models import Mailbox
+
+
+logger = logging.getLogger(__name__)
+logging.basicConfig(level=logging.INFO)
+
+
+class Command(BaseCommand):
+ def handle(self, *args, **options):
+ mailboxes = Mailbox.active_mailboxes.all()
+ if args:
+ mailboxes = mailboxes.filter(
+ name=' '.join(args)
+ )
+ for mailbox in mailboxes:
+ logger.info(
+ 'Gathering messages for %s',
+ mailbox.name
+ )
+ messages = mailbox.get_new_mail()
+ for message in messages:
+ logger.info(
+ 'Received %s (from %s)',
+ message.subject,
+ message.from_address
+ )
diff --git a/build/lib/django_mailbox/management/commands/processincomingmessage.py b/build/lib/django_mailbox/management/commands/processincomingmessage.py
new file mode 100644
index 00000000..ec344111
--- /dev/null
+++ b/build/lib/django_mailbox/management/commands/processincomingmessage.py
@@ -0,0 +1,52 @@
+import email
+import logging
+import sys
+try:
+ from email import utils
+except ImportError:
+ import rfc822 as utils
+
+from django.core.management.base import BaseCommand
+
+from django_mailbox.models import Mailbox
+
+
+logger = logging.getLogger(__name__)
+logging.basicConfig(level=logging.INFO)
+
+
+class Command(BaseCommand):
+ args = "<[Mailbox Name (optional)]>"
+ help = "Receive incoming mail via stdin"
+
+ def add_arguments(self, parser):
+ parser.add_argument(
+ 'mailbox_name',
+ nargs='?',
+ help="The name of the mailbox that will receive the message"
+ )
+
+ def handle(self, mailbox_name=None, *args, **options):
+ message = email.message_from_string(sys.stdin.read())
+ if message:
+ if mailbox_name:
+ mailbox = self.get_mailbox_by_name(mailbox_name)
+ else:
+ mailbox = self.get_mailbox_for_message(message)
+ mailbox.process_incoming_message(message)
+ logger.info(
+ "Message received from %s",
+ message['from']
+ )
+ else:
+ logger.warning("Message not processable.")
+
+ def get_mailbox_by_name(self, name):
+ mailbox, created = Mailbox.objects.get_or_create(
+ name=name,
+ )
+ return mailbox
+
+ def get_mailbox_for_message(self, message):
+ email_address = utils.parseaddr(message['to'])[1][0:255]
+ return self.get_mailbox_by_name(email_address)
diff --git a/build/lib/django_mailbox/management/commands/rebuildmessageattachments.py b/build/lib/django_mailbox/management/commands/rebuildmessageattachments.py
new file mode 100644
index 00000000..6bfc24f6
--- /dev/null
+++ b/build/lib/django_mailbox/management/commands/rebuildmessageattachments.py
@@ -0,0 +1,71 @@
+import email
+import hashlib
+import logging
+
+from django.core.management.base import BaseCommand
+
+from django_mailbox.models import MessageAttachment, Message
+
+logger = logging.getLogger(__name__)
+logging.basicConfig(level=logging.INFO)
+
+
+class Command(BaseCommand):
+ """ Briefly, a bug existed in a migration that may have caused message
+ attachments to become disassociated with their messages. This management
+ command will read through existing message attachments and attempt to
+ re-associate them with their original message.
+
+ This isn't foolproof, I'm afraid. If an attachment exists twice, it will
+ be associated only with the most recent e-mail message. That said,
+ I'm quite sure that the bug in the migration is gone (and you'd have to
+ have been quite unlucky to have ran the bad migration).
+
+ """
+ def handle(self, *args, **options):
+ attachment_hash_map = {}
+
+ attachments_without_messages = MessageAttachment.objects.filter(
+ message=None
+ ).order_by(
+ 'id'
+ )
+
+ if attachments_without_messages.count() < 1:
+ return
+
+ for attachment in attachments_without_messages:
+ md5 = hashlib.md5()
+ for chunk in attachment.document.file.chunks():
+ md5.update(chunk)
+ attachment_hash_map[md5.hexdigest()] = attachment.pk
+
+ for message_record in Message.objects.all().order_by('id'):
+ message = email.message_from_string(message_record.body)
+ if message.is_multipart():
+ for part in message.walk():
+ if part.get_content_maintype() == 'multipart':
+ continue
+ if part.get('Content-Disposition') is None:
+ continue
+ md5 = hashlib.md5()
+ md5.update(part.get_payload(decode=True))
+ digest = md5.hexdigest()
+ if digest in attachment_hash_map:
+ attachment = MessageAttachment.objects.get(
+ pk=attachment_hash_map[digest]
+ )
+ attachment.message = message_record
+ attachment.save()
+ logger.info(
+ "Associated message %s with attachment %s (%s)",
+ message_record.pk,
+ attachment.pk,
+ digest
+ )
+ else:
+ logger.info(
+ "%s(%s) not found in currently-stored attachments",
+ part.get_filename(),
+ digest
+ )
diff --git a/build/lib/django_mailbox/migrations/0001_initial.py b/build/lib/django_mailbox/migrations/0001_initial.py
new file mode 100644
index 00000000..13bb802d
--- /dev/null
+++ b/build/lib/django_mailbox/migrations/0001_initial.py
@@ -0,0 +1,56 @@
+from django.db import models, migrations
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='Mailbox',
+ fields=[
+ ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
+ ('name', models.CharField(max_length=255, verbose_name='Name')),
+ ('uri', models.CharField(default=None, max_length=255, blank=True, help_text="Example: imap+ssl://myusername:mypassword@someserver
Internet transports include 'imap' and 'pop3'; common local file transports include 'maildir', 'mbox', and less commonly 'babyl', 'mh', and 'mmdf'.
Be sure to urlencode your username and password should they contain illegal characters (like @, :, etc).", null=True, verbose_name='URI')),
+ ('from_email', models.CharField(default=None, max_length=255, blank=True, help_text="Example: MailBot <mailbot@yourdomain.com> 'From' header to set for outgoing email.
If you do not use this e-mail inbox for outgoing mail, this setting is unnecessary. If you send e-mail without setting this, your 'From' header will'be set to match the setting `DEFAULT_FROM_EMAIL`.", null=True, verbose_name='From email')),
+ ('active', models.BooleanField(default=True, help_text='Check this e-mail inbox for new e-mail messages during polling cycles. This checkbox does not have an effect upon whether mail is collected here when this mailbox receives mail from a pipe, and does not affect whether e-mail messages can be dispatched from this mailbox. ', verbose_name='Active')),
+ ],
+ options={
+ 'verbose_name_plural': 'Mailboxes',
+ },
+ bases=(models.Model,),
+ ),
+ migrations.CreateModel(
+ name='Message',
+ fields=[
+ ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
+ ('subject', models.CharField(max_length=255, verbose_name='Subject')),
+ ('message_id', models.CharField(max_length=255, verbose_name='Message ID')),
+ ('from_header', models.CharField(max_length=255, verbose_name='From header')),
+ ('to_header', models.TextField(verbose_name='To header')),
+ ('outgoing', models.BooleanField(default=False, verbose_name='Outgoing')),
+ ('body', models.TextField(verbose_name='Body')),
+ ('encoded', models.BooleanField(default=False, help_text='True if the e-mail body is Base64 encoded', verbose_name='Encoded')),
+ ('processed', models.DateTimeField(auto_now_add=True, verbose_name='Processed')),
+ ('read', models.DateTimeField(default=None, null=True, verbose_name='Read', blank=True)),
+ ('in_reply_to', models.ForeignKey(related_name='replies', verbose_name='In reply to', blank=True, to='django_mailbox.Message', null=True, on_delete=models.CASCADE)),
+ ('mailbox', models.ForeignKey(related_name='messages', verbose_name='Mailbox', to='django_mailbox.Mailbox', on_delete=models.CASCADE)),
+ ],
+ options={
+ },
+ bases=(models.Model,),
+ ),
+ migrations.CreateModel(
+ name='MessageAttachment',
+ fields=[
+ ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
+ ('headers', models.TextField(null=True, verbose_name='Headers', blank=True)),
+ ('document', models.FileField(upload_to=b'mailbox_attachments/%Y/%m/%d/', verbose_name='Document')),
+ ('message', models.ForeignKey(related_name='attachments', verbose_name='Message', blank=True, to='django_mailbox.Message', null=True, on_delete=models.CASCADE)),
+ ],
+ options={
+ },
+ bases=(models.Model,),
+ ),
+ ]
diff --git a/build/lib/django_mailbox/migrations/0002_add_eml_to_message.py b/build/lib/django_mailbox/migrations/0002_add_eml_to_message.py
new file mode 100644
index 00000000..4204efb2
--- /dev/null
+++ b/build/lib/django_mailbox/migrations/0002_add_eml_to_message.py
@@ -0,0 +1,17 @@
+from django.db import models, migrations
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('django_mailbox', '0001_initial'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='message',
+ name='eml',
+ field=models.FileField(help_text='Original full content of message', upload_to=b'messages', null=True, verbose_name='Message as a file'),
+ preserve_default=True,
+ ),
+ ]
diff --git a/build/lib/django_mailbox/migrations/0003_auto_20150409_0316.py b/build/lib/django_mailbox/migrations/0003_auto_20150409_0316.py
new file mode 100644
index 00000000..1b5def07
--- /dev/null
+++ b/build/lib/django_mailbox/migrations/0003_auto_20150409_0316.py
@@ -0,0 +1,16 @@
+from django.db import models, migrations
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('django_mailbox', '0002_add_eml_to_message'),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name='message',
+ name='eml',
+ field=models.FileField(help_text='Original full content of message', upload_to=b'messages', null=True, verbose_name='Raw message contents'),
+ ),
+ ]
diff --git a/build/lib/django_mailbox/migrations/0004_bytestring_to_unicode.py b/build/lib/django_mailbox/migrations/0004_bytestring_to_unicode.py
new file mode 100644
index 00000000..7716f2f5
--- /dev/null
+++ b/build/lib/django_mailbox/migrations/0004_bytestring_to_unicode.py
@@ -0,0 +1,21 @@
+from django.db import models, migrations
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('django_mailbox', '0003_auto_20150409_0316'),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name='message',
+ name='eml',
+ field=models.FileField(verbose_name='Raw message contents', upload_to='messages', null=True, help_text='Original full content of message'),
+ ),
+ migrations.AlterField(
+ model_name='messageattachment',
+ name='document',
+ field=models.FileField(verbose_name='Document', upload_to='mailbox_attachments/%Y/%m/%d/'),
+ ),
+ ]
diff --git a/build/lib/django_mailbox/migrations/0005_auto_20160523_2240.py b/build/lib/django_mailbox/migrations/0005_auto_20160523_2240.py
new file mode 100644
index 00000000..f4bdae65
--- /dev/null
+++ b/build/lib/django_mailbox/migrations/0005_auto_20160523_2240.py
@@ -0,0 +1,17 @@
+from django.db import migrations, models
+import django_mailbox.utils
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('django_mailbox', '0004_bytestring_to_unicode'),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name='messageattachment',
+ name='document',
+ field=models.FileField(upload_to=django_mailbox.utils.get_attachment_save_path, verbose_name='Document'),
+ ),
+ ]
diff --git a/build/lib/django_mailbox/migrations/0006_mailbox_last_polling.py b/build/lib/django_mailbox/migrations/0006_mailbox_last_polling.py
new file mode 100644
index 00000000..5f1524ec
--- /dev/null
+++ b/build/lib/django_mailbox/migrations/0006_mailbox_last_polling.py
@@ -0,0 +1,18 @@
+# Generated by Django 1.9.8 on 2016-08-15 22:39
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('django_mailbox', '0005_auto_20160523_2240'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='mailbox',
+ name='last_polling',
+ field=models.DateTimeField(blank=True, help_text='The time of last successful polling for messages.It is blank for new mailboxes and is not set for mailboxes that only receive messages via a pipe.', null=True, verbose_name='Last polling'),
+ ),
+ ]
diff --git a/build/lib/django_mailbox/migrations/0007_auto_20180421_0026.py b/build/lib/django_mailbox/migrations/0007_auto_20180421_0026.py
new file mode 100644
index 00000000..2e984d0d
--- /dev/null
+++ b/build/lib/django_mailbox/migrations/0007_auto_20180421_0026.py
@@ -0,0 +1,25 @@
+# Generated by Django 1.10.7 on 2018-04-21 00:26
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('django_mailbox', '0006_mailbox_last_polling'),
+ ]
+
+ operations = [
+ migrations.AlterModelOptions(
+ name='mailbox',
+ options={'verbose_name': 'Mailbox', 'verbose_name_plural': 'Mailboxes'},
+ ),
+ migrations.AlterModelOptions(
+ name='message',
+ options={'verbose_name': 'E-mail message', 'verbose_name_plural': 'E-mail messages'},
+ ),
+ migrations.AlterModelOptions(
+ name='messageattachment',
+ options={'verbose_name': 'Message attachment', 'verbose_name_plural': 'Message attachments'},
+ ),
+ ]
diff --git a/build/lib/django_mailbox/migrations/0008_auto_20190219_1553.py b/build/lib/django_mailbox/migrations/0008_auto_20190219_1553.py
new file mode 100644
index 00000000..e307c258
--- /dev/null
+++ b/build/lib/django_mailbox/migrations/0008_auto_20190219_1553.py
@@ -0,0 +1,23 @@
+# Generated by Django 2.1.7 on 2019-02-19 14:53
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('django_mailbox', '0007_auto_20180421_0026'),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name='mailbox',
+ name='active',
+ field=models.BooleanField(blank=True, default=True, help_text='Check this e-mail inbox for new e-mail messages during polling cycles. This checkbox does not have an effect upon whether mail is collected here when this mailbox receives mail from a pipe, and does not affect whether e-mail messages can be dispatched from this mailbox. ', verbose_name='Active'),
+ ),
+ migrations.AlterField(
+ model_name='message',
+ name='outgoing',
+ field=models.BooleanField(blank=True, default=False, verbose_name='Outgoing'),
+ ),
+ ]
diff --git a/build/lib/django_mailbox/migrations/__init__.py b/build/lib/django_mailbox/migrations/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/build/lib/django_mailbox/models.py b/build/lib/django_mailbox/models.py
new file mode 100644
index 00000000..52e4c1e7
--- /dev/null
+++ b/build/lib/django_mailbox/models.py
@@ -0,0 +1,855 @@
+#!/usr/bin/env python
+
+"""
+Models declaration for application ``django_mailbox``.
+"""
+import gzip
+from email.encoders import encode_base64
+from email.message import Message as EmailMessage
+from email.utils import formatdate, parseaddr
+from urllib.parse import parse_qs, unquote, urlparse
+from quopri import encode as encode_quopri
+from io import BytesIO
+import base64
+import email
+import logging
+import mimetypes
+import os.path
+import sys
+import uuid
+from tempfile import NamedTemporaryFile
+
+import django
+from django.conf import settings as django_settings
+from django.core.files.base import ContentFile, File
+from django.core.mail.message import make_msgid
+from django.db import models
+from django.utils.translation import gettext_lazy as _
+from django.utils.timezone import now
+
+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, Office365Transport
+
+logger = logging.getLogger(__name__)
+
+
+class MailboxQuerySet(models.QuerySet):
+ def get_new_mail(self):
+ count = 0
+ for mailbox in self.all():
+ logger.debug('Receiving mail for %s' % mailbox)
+ count += sum(1 for i in mailbox.get_new_mail())
+ logger.debug('Received %d %s.', count, 'mails' if count != 1 else 'mail')
+
+
+class MailboxManager(models.Manager):
+ def get_queryset(self):
+ return MailboxQuerySet(self.model, using=self._db)
+
+
+class ActiveMailboxManager(MailboxManager):
+ def get_queryset(self):
+ return super().get_queryset().filter(
+ active=True,
+ )
+
+
+class Mailbox(models.Model):
+ name = models.CharField(
+ _('Name'),
+ max_length=255,
+ )
+
+ uri = models.CharField(
+ _('URI'),
+ max_length=255,
+ help_text=(_(
+ "Example: imap+ssl://myusername:mypassword@someserver "
+ " "
+ "Internet transports include 'imap' and 'pop3'; "
+ "common local file transports include 'maildir', 'mbox', "
+ "and less commonly 'babyl', 'mh', and 'mmdf'. "
+ " "
+ "Be sure to urlencode your username and password should they "
+ "contain illegal characters (like @, :, etc)."
+ )),
+ blank=True,
+ null=True,
+ default=None,
+ )
+
+ from_email = models.CharField(
+ _('From email'),
+ max_length=255,
+ help_text=(_(
+ "Example: MailBot <mailbot@yourdomain.com> "
+ "'From' header to set for outgoing email. "
+ " "
+ "If you do not use this e-mail inbox for outgoing mail, this "
+ "setting is unnecessary. "
+ "If you send e-mail without setting this, your 'From' header will'"
+ "be set to match the setting `DEFAULT_FROM_EMAIL`."
+ )),
+ blank=True,
+ null=True,
+ default=None,
+ )
+
+ active = models.BooleanField(
+ _('Active'),
+ help_text=(_(
+ "Check this e-mail inbox for new e-mail messages during polling "
+ "cycles. This checkbox does not have an effect upon whether "
+ "mail is collected here when this mailbox receives mail from a "
+ "pipe, and does not affect whether e-mail messages can be "
+ "dispatched from this mailbox. "
+ )),
+ blank=True,
+ default=True,
+ )
+
+ last_polling = models.DateTimeField(
+ _("Last polling"),
+ help_text=(_("The time of last successful polling for messages."
+ "It is blank for new mailboxes and is not set for "
+ "mailboxes that only receive messages via a pipe.")),
+ blank=True,
+ null=True
+ )
+
+ objects = MailboxManager()
+ active_mailboxes = ActiveMailboxManager()
+
+ @property
+ def _protocol_info(self):
+ return urlparse(self.uri)
+
+ @property
+ def _query_string(self):
+ return parse_qs(self._protocol_info.query)
+
+ @property
+ def _domain(self):
+ return self._protocol_info.hostname
+
+ @property
+ def port(self):
+ """Returns the port to use for fetching messages."""
+ return self._protocol_info.port
+
+ @property
+ def username(self):
+ """Returns the username to use for fetching messages."""
+ return unquote(self._protocol_info.username)
+
+ @property
+ def password(self):
+ """Returns the password to use for fetching messages."""
+ return unquote(self._protocol_info.password)
+
+ @property
+ def location(self):
+ """Returns the location (domain and path) of messages."""
+ return self._domain if self._domain else '' + self._protocol_info.path
+
+ @property
+ def type(self):
+ """Returns the 'transport' name for this mailbox."""
+ scheme = self._protocol_info.scheme.lower()
+ if '+' in scheme:
+ return scheme.split('+')[0]
+ return scheme
+
+ @property
+ def use_ssl(self):
+ """Returns whether or not this mailbox's connection uses SSL."""
+ return '+ssl' in self._protocol_info.scheme.lower()
+
+ @property
+ def use_tls(self):
+ """Returns whether or not this mailbox's connection uses STARTTLS."""
+ return '+tls' in self._protocol_info.scheme.lower()
+
+ @property
+ def archive(self):
+ """Returns (if specified) the folder to archive messages to."""
+ archive_folder = self._query_string.get('archive', None)
+ if not archive_folder:
+ return None
+ return archive_folder[0]
+
+ @property
+ def folder(self):
+ """Returns (if specified) the folder to fetch mail from."""
+ folder = self._query_string.get('folder', None)
+ if not folder:
+ return None
+ return folder[0]
+
+ @property
+ def client_id(self):
+ """Returns (if specified) the client id for Office365."""
+ client_id = self._query_string.get('client_id', None)
+ if not client_id:
+ return None
+ return client_id[0]
+
+ @property
+ def client_secret(self):
+ """Returns (if specified) the client secret for Office365."""
+ client_secret = self._query_string.get('client_secret', None)
+ if not client_secret:
+ return None
+ return client_secret[0]
+
+ @property
+ def tenant_id(self):
+ """Returns (if specified) the tenant id for Office365."""
+ tenant_id = self._query_string.get('tentant_id', None)
+ if not tenant_id:
+ return None
+ return tenant_id[0]
+
+ def get_connection(self):
+ """Returns the transport instance for this mailbox.
+
+ These will always be instances of
+ `django_mailbox.transports.base.EmailTransport`.
+
+ """
+ if not self.uri:
+ return None
+ elif self.type == 'imap':
+ conn = ImapTransport(
+ self.location,
+ port=self.port if self.port else None,
+ ssl=self.use_ssl,
+ tls=self.use_tls,
+ archive=self.archive,
+ folder=self.folder
+ )
+ conn.connect(self.username, self.password)
+ elif self.type == 'gmail':
+ conn = GmailImapTransport(
+ self.location,
+ port=self.port if self.port else None,
+ ssl=True,
+ archive=self.archive
+ )
+ conn.connect(self.username, self.password)
+ elif self.type == 'pop3':
+ conn = Pop3Transport(
+ self.location,
+ port=self.port if self.port else None,
+ ssl=self.use_ssl
+ )
+ conn.connect(self.username, self.password)
+ elif self.type == 'office365':
+ conn = Office365Transport(
+ port=self.port if self.port else None,
+ ssl=True,
+ archive=self.archive,
+ client_id=self.client_id,
+ client_secret=self.client_secret,
+ tenant_id=self.tenant_id
+ )
+ conn.connect(self.username, self.password)
+ elif self.type == 'maildir':
+ conn = MaildirTransport(self.location)
+ elif self.type == 'mbox':
+ conn = MboxTransport(self.location)
+ elif self.type == 'babyl':
+ conn = BabylTransport(self.location)
+ elif self.type == 'mh':
+ conn = MHTransport(self.location)
+ elif self.type == 'mmdf':
+ conn = MMDFTransport(self.location)
+ return conn
+
+ def process_incoming_message(self, message):
+ """Process a message incoming to this mailbox."""
+ msg = self._process_message(message)
+ if msg is None:
+ return None
+ msg.outgoing = False
+ msg.save()
+
+ message_received.send(sender=self, message=msg)
+
+ return msg
+
+ def record_outgoing_message(self, message):
+ """Record an outgoing message associated with this mailbox."""
+ msg = self._process_message(message)
+ if msg is None:
+ return None
+ msg.outgoing = True
+ msg.save()
+ return msg
+
+ def _get_dehydrated_message(self, msg, record):
+ settings = utils.get_settings()
+
+ new = EmailMessage()
+ if msg.is_multipart():
+ for header, value in msg.items():
+ new[header] = value
+ for part in msg.get_payload():
+ new.attach(
+ self._get_dehydrated_message(part, record)
+ )
+ elif (
+ settings['strip_unallowed_mimetypes']
+ and not msg.get_content_type() in settings['allowed_mimetypes']
+ ):
+ for header, value in msg.items():
+ new[header] = value
+ # Delete header, otherwise when attempting to deserialize the
+ # payload, it will be expecting a body for this.
+ del new['Content-Transfer-Encoding']
+ new[settings['altered_message_header']] = (
+ 'Stripped; Content type %s not allowed' % (
+ msg.get_content_type()
+ )
+ )
+ new.set_payload('')
+ elif (
+ (
+ msg.get_content_type() not in settings['text_stored_mimetypes']
+ ) or
+ ('attachment' in msg.get('Content-Disposition', ''))
+ ):
+ filename = None
+ raw_filename = msg.get_filename()
+ if raw_filename:
+ filename = utils.convert_header_to_unicode(raw_filename)
+ if not filename:
+ extension = mimetypes.guess_extension(msg.get_content_type())
+ else:
+ _, extension = os.path.splitext(filename)
+ if not extension:
+ extension = '.bin'
+
+ attachment = MessageAttachment()
+
+ attachment.document.save(
+ uuid.uuid4().hex + extension,
+ ContentFile(
+ BytesIO(
+ msg.get_payload(decode=True)
+ ).getvalue()
+ )
+ )
+ attachment.message = record
+ for key, value in msg.items():
+ attachment[key] = value
+ attachment.save()
+
+ placeholder = EmailMessage()
+ placeholder[
+ settings['attachment_interpolation_header']
+ ] = str(attachment.pk)
+ new = placeholder
+ else:
+ content_charset = msg.get_content_charset()
+ if not content_charset:
+ content_charset = 'ascii'
+ try:
+ # Make sure that the payload can be properly decoded in the
+ # defined charset, if it can't, let's mash some things
+ # inside the payload :-\
+ msg.get_payload(decode=True).decode(content_charset)
+ except LookupError:
+ logger.warning(
+ "Unknown encoding %s; interpreting as ASCII!",
+ content_charset
+ )
+ msg.set_payload(
+ msg.get_payload(decode=True).decode(
+ 'ascii',
+ 'ignore'
+ )
+ )
+ except ValueError:
+ logger.warning(
+ "Decoding error encountered; interpreting %s as ASCII!",
+ content_charset
+ )
+ msg.set_payload(
+ msg.get_payload(decode=True).decode(
+ 'ascii',
+ 'ignore'
+ )
+ )
+ new = msg
+ return new
+
+ def _process_message(self, message):
+ msg = Message()
+ msg._email_object = message
+ settings = utils.get_settings()
+
+ if settings['store_original_message']:
+ self._process_save_original_message(message, msg)
+ msg.mailbox = self
+ if 'subject' in message:
+ msg.subject = (
+ utils.convert_header_to_unicode(message['subject'])[0:255]
+ )
+ if 'message-id' in message:
+ msg.message_id = message['message-id'][0:255].strip()
+ if 'from' in message:
+ msg.from_header = utils.convert_header_to_unicode(message['from'])
+ if 'to' in message:
+ msg.to_header = utils.convert_header_to_unicode(message['to'])
+ elif 'Delivered-To' in message:
+ msg.to_header = utils.convert_header_to_unicode(
+ message['Delivered-To']
+ )
+ msg.save()
+ message = self._get_dehydrated_message(message, msg)
+ 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']:
+ try:
+ msg.in_reply_to = Message.objects.filter(
+ message_id=message['in-reply-to'].strip()
+ )[0]
+ except IndexError:
+ pass
+ msg.save()
+ return msg
+
+ def _process_save_original_message(self, message, msg):
+ settings = utils.get_settings()
+ if settings['compress_original_message']:
+ with NamedTemporaryFile(suffix=".eml.gz") as fp_tmp:
+ with gzip.GzipFile(fileobj=fp_tmp, mode="w") as fp:
+ fp.write(message.as_string().encode('utf-8'))
+ msg.eml.save(
+ "{}.eml.gz".format(uuid.uuid4()),
+ File(fp_tmp),
+ save=False
+ )
+
+ else:
+ msg.eml.save(
+ '%s.eml' % uuid.uuid4(),
+ ContentFile(message.as_string()),
+ save=False
+ )
+
+ def get_new_mail(self, condition=None):
+ """Connect to this transport and fetch new messages."""
+ new_mail = []
+ connection = self.get_connection()
+ if not connection:
+ return
+ for message in connection.get_message(condition):
+ msg = self.process_incoming_message(message)
+ if not msg is None:
+ yield msg
+ self.last_polling = now()
+ if django.VERSION >= (1, 5): # Django 1.5 introduces update_fields
+ self.save(update_fields=['last_polling'])
+ else:
+ self.save()
+
+ def __str__(self):
+ return self.name
+
+ class Meta:
+ verbose_name = _('Mailbox')
+ verbose_name_plural = _('Mailboxes')
+
+
+class IncomingMessageManager(models.Manager):
+ def get_queryset(self):
+ return super().get_queryset().filter(
+ outgoing=False,
+ )
+
+
+class OutgoingMessageManager(models.Manager):
+ def get_queryset(self):
+ return super().get_queryset().filter(
+ outgoing=True,
+ )
+
+
+class UnreadMessageManager(models.Manager):
+ def get_queryset(self):
+ return super().get_queryset().filter(
+ read=None
+ )
+
+
+class Message(models.Model):
+ mailbox = models.ForeignKey(
+ Mailbox,
+ related_name='messages',
+ verbose_name=_('Mailbox'),
+ on_delete=models.CASCADE
+ )
+
+ subject = models.CharField(
+ _('Subject'),
+ max_length=255
+ )
+
+ message_id = models.CharField(
+ _('Message ID'),
+ max_length=255
+ )
+
+ in_reply_to = models.ForeignKey(
+ 'django_mailbox.Message',
+ related_name='replies',
+ blank=True,
+ null=True,
+ verbose_name=_('In reply to'),
+ on_delete=models.CASCADE
+ )
+
+ from_header = models.CharField(
+ _('From header'),
+ max_length=255,
+ )
+
+ to_header = models.TextField(
+ _('To header'),
+ )
+
+ outgoing = models.BooleanField(
+ _('Outgoing'),
+ default=False,
+ blank=True,
+ )
+
+ body = models.TextField(
+ _('Body'),
+ )
+
+ encoded = models.BooleanField(
+ _('Encoded'),
+ default=False,
+ help_text=_('True if the e-mail body is Base64 encoded'),
+ )
+
+ processed = models.DateTimeField(
+ _('Processed'),
+ auto_now_add=True
+ )
+
+ read = models.DateTimeField(
+ _('Read'),
+ default=None,
+ blank=True,
+ null=True,
+ )
+
+ eml = models.FileField(
+ _('Raw message contents'),
+ null=True,
+ upload_to="messages",
+ help_text=_('Original full content of message')
+ )
+ objects = models.Manager()
+ unread_messages = UnreadMessageManager()
+ incoming_messages = IncomingMessageManager()
+ outgoing_messages = OutgoingMessageManager()
+
+ @property
+ def address(self):
+ """Property allowing one to get the relevant address(es).
+
+ In earlier versions of this library, the model had an `address` field
+ storing the e-mail address from which a message was received. During
+ later refactorings, it became clear that perhaps storing sent messages
+ would also be useful, so the address field was replaced with two
+ separate fields.
+
+ """
+ addresses = []
+ addresses = self.to_addresses + self.from_address
+ return addresses
+
+ @property
+ def from_address(self):
+ """Returns the address (as a list) from which this message was received
+
+ .. note::
+
+ This was once (and probably should be) a string rather than a list,
+ but in a pull request received long, long ago it was changed;
+ presumably to make the interface identical to that of
+ `to_addresses`.
+
+ """
+ if self.from_header:
+ return [parseaddr(self.from_header)[1].lower()]
+ else:
+ return []
+
+ @property
+ def to_addresses(self):
+ """Returns a list of addresses to which this message was sent."""
+ addresses = []
+ for address in self.to_header.split(','):
+ if address:
+ addresses.append(
+ parseaddr(
+ address
+ )[1].lower()
+ )
+ return addresses
+
+ def reply(self, message):
+ """Sends a message as a reply to this message instance.
+
+ Although Django's e-mail processing will set both Message-ID
+ and Date upon generating the e-mail message, we will not be able
+ to retrieve that information through normal channels, so we must
+ pre-set it.
+
+ """
+ if not message.from_email:
+ if self.mailbox.from_email:
+ message.from_email = self.mailbox.from_email
+ else:
+ message.from_email = django_settings.DEFAULT_FROM_EMAIL
+ message.extra_headers['Message-ID'] = make_msgid()
+ message.extra_headers['Date'] = formatdate()
+ message.extra_headers['In-Reply-To'] = self.message_id.strip()
+ message.send()
+ return self.mailbox.record_outgoing_message(
+ email.message_from_string(
+ message.message().as_string()
+ )
+ )
+
+ @property
+ def text(self):
+ """
+ Returns the message body matching content type 'text/plain'.
+ """
+ return utils.get_body_from_message(
+ self.get_email_object(), 'text', 'plain'
+ ).replace('=\n', '').strip()
+
+ @property
+ def html(self):
+ """
+ Returns the message body matching content type 'text/html'.
+ """
+ return utils.get_body_from_message(
+ self.get_email_object(), 'text', 'html'
+ ).replace('\n', '').strip()
+
+ def _rehydrate(self, msg):
+ new = EmailMessage()
+ settings = utils.get_settings()
+
+ if msg.is_multipart():
+ for header, value in msg.items():
+ new[header] = value
+ for part in msg.get_payload():
+ new.attach(
+ self._rehydrate(part)
+ )
+ elif settings['attachment_interpolation_header'] in msg.keys():
+ try:
+ attachment = MessageAttachment.objects.get(
+ pk=msg[settings['attachment_interpolation_header']]
+ )
+ for header, value in attachment.items():
+ new[header] = value
+ encoding = new['Content-Transfer-Encoding']
+ if encoding and encoding.lower() == 'quoted-printable':
+ # Cannot use `email.encoders.encode_quopri due to
+ # bug 14360: http://bugs.python.org/issue14360
+ output = BytesIO()
+ encode_quopri(
+ BytesIO(
+ attachment.document.read()
+ ),
+ output,
+ quotetabs=True,
+ header=False,
+ )
+ new.set_payload(
+ output.getvalue().decode().replace(' ', '=20')
+ )
+ del new['Content-Transfer-Encoding']
+ new['Content-Transfer-Encoding'] = 'quoted-printable'
+ else:
+ new.set_payload(
+ attachment.document.read()
+ )
+ del new['Content-Transfer-Encoding']
+ encode_base64(new)
+ except MessageAttachment.DoesNotExist:
+ new[settings['altered_message_header']] = (
+ 'Missing; Attachment %s not found' % (
+ msg[settings['attachment_interpolation_header']]
+ )
+ )
+ new.set_payload('')
+ else:
+ for header, value in msg.items():
+ new[header] = value
+ new.set_payload(
+ msg.get_payload()
+ )
+ return new
+
+ def get_body(self):
+ """Returns the `body` field of this record.
+
+ This will automatically base64-decode the message contents
+ if they are encoded as such.
+
+ """
+ if self.encoded:
+ return base64.b64decode(self.body.encode('ascii'))
+ return self.body.encode('utf-8')
+
+ def set_body(self, body):
+ """Set the `body` field of this record.
+
+ This will automatically base64-encode the message contents to
+ circumvent a limitation in earlier versions of Django in which
+ no fields existed for storing arbitrary bytes.
+
+ """
+ self.encoded = True
+ self.body = base64.b64encode(body.encode('utf-8')).decode('ascii')
+
+ def get_email_object(self):
+ """Returns an `email.message.EmailMessage` instance representing the
+ contents of this message and all attachments.
+
+ See [email.message.EmailMessage]_ for more information as to what methods
+ and properties are available on `email.message.EmailMessage` instances.
+
+ .. note::
+
+ Depending upon the storage methods in use (specifically --
+ whether ``DJANGO_MAILBOX_STORE_ORIGINAL_MESSAGE`` is set
+ to ``True``, this may either create a "rehydrated" message
+ using stored attachments, or read the message contents stored
+ on-disk.
+
+ .. [email.message.EmailMessage] Python's `email.message.EmailMessage` docs
+ (https://docs.python.org/3/library/email.message.html)
+
+ """
+ if not hasattr(self, '_email_object'): # Cache fill
+ if self.eml:
+ if self.eml.name.endswith('.gz'):
+ body = gzip.GzipFile(fileobj=self.eml).read()
+ else:
+ self.eml.open()
+ body = self.eml.file.read()
+ self.eml.close()
+ else:
+ body = self.get_body()
+ flat = email.message_from_bytes(body)
+ self._email_object = self._rehydrate(flat)
+ return self._email_object
+
+ def delete(self, *args, **kwargs):
+ """Delete this message and all stored attachments."""
+ for attachment in self.attachments.all():
+ # This attachment is attached only to this message.
+ attachment.delete()
+ return super().delete(*args, **kwargs)
+
+ def __str__(self):
+ return self.subject
+
+ class Meta:
+ verbose_name = _('E-mail message')
+ verbose_name_plural = _('E-mail messages')
+
+
+class MessageAttachment(models.Model):
+ message = models.ForeignKey(
+ Message,
+ related_name='attachments',
+ null=True,
+ blank=True,
+ verbose_name=_('Message'),
+ on_delete=models.CASCADE
+ )
+
+ headers = models.TextField(
+ _('Headers'),
+ null=True,
+ blank=True,
+ )
+
+ document = models.FileField(
+ _('Document'),
+ upload_to=utils.get_attachment_save_path,
+ )
+
+ def delete(self, *args, **kwargs):
+ """Deletes the attachment."""
+ self.document.delete()
+ return super().delete(*args, **kwargs)
+
+ def _get_rehydrated_headers(self):
+ headers = self.headers
+ if headers is None:
+ return EmailMessage()
+ return email.message_from_string(headers)
+
+ def _set_dehydrated_headers(self, email_object):
+ self.headers = email_object.as_string()
+
+ def __delitem__(self, name):
+ rehydrated = self._get_rehydrated_headers()
+ del rehydrated[name]
+ self._set_dehydrated_headers(rehydrated)
+
+ def __setitem__(self, name, value):
+ rehydrated = self._get_rehydrated_headers()
+ rehydrated[name] = value
+ self._set_dehydrated_headers(rehydrated)
+
+ def get_filename(self):
+ """Returns the original filename of this attachment."""
+ file_name = self._get_rehydrated_headers().get_filename()
+ if isinstance(file_name, str):
+ result = utils.convert_header_to_unicode(file_name)
+ if result is None:
+ return file_name
+ return result
+ else:
+ return None
+
+ def items(self):
+ return self._get_rehydrated_headers().items()
+
+ def __getitem__(self, name):
+ value = self._get_rehydrated_headers()[name]
+ if value is None:
+ raise KeyError('Header %s does not exist' % name)
+ return value
+
+ def __str__(self):
+ return self.document.url
+
+ class Meta:
+ verbose_name = _('Message attachment')
+ verbose_name_plural = _('Message attachments')
diff --git a/build/lib/django_mailbox/signals.py b/build/lib/django_mailbox/signals.py
new file mode 100644
index 00000000..b445183e
--- /dev/null
+++ b/build/lib/django_mailbox/signals.py
@@ -0,0 +1,3 @@
+from django.dispatch.dispatcher import Signal
+
+message_received = Signal(providing_args=['message'])
diff --git a/build/lib/django_mailbox/south_migrations/0001_initial.py b/build/lib/django_mailbox/south_migrations/0001_initial.py
new file mode 100644
index 00000000..dcfdf602
--- /dev/null
+++ b/build/lib/django_mailbox/south_migrations/0001_initial.py
@@ -0,0 +1,58 @@
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+
+class Migration(SchemaMigration):
+
+ def forwards(self, orm):
+ # Adding model 'Mailbox'
+ db.create_table('django_mailbox_mailbox', (
+ ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+ ('name', self.gf('django.db.models.fields.CharField')(max_length=255)),
+ ('uri', self.gf('django.db.models.fields.CharField')(max_length=255)),
+ ))
+ db.send_create_signal('django_mailbox', ['Mailbox'])
+
+ # Adding model 'Message'
+ db.create_table('django_mailbox_message', (
+ ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+ ('mailbox', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['django_mailbox.Mailbox'])),
+ ('subject', self.gf('django.db.models.fields.CharField')(max_length=255)),
+ ('message_id', self.gf('django.db.models.fields.CharField')(max_length=255)),
+ ('from_address', self.gf('django.db.models.fields.CharField')(max_length=255)),
+ ('body', self.gf('django.db.models.fields.TextField')()),
+ ('received', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True, blank=True)),
+ ))
+ db.send_create_signal('django_mailbox', ['Message'])
+
+
+ def backwards(self, orm):
+ # Deleting model 'Mailbox'
+ db.delete_table('django_mailbox_mailbox')
+
+ # Deleting model 'Message'
+ db.delete_table('django_mailbox_message')
+
+
+ models = {
+ 'django_mailbox.mailbox': {
+ 'Meta': {'object_name': 'Mailbox'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'uri': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+ },
+ 'django_mailbox.message': {
+ 'Meta': {'object_name': 'Message'},
+ 'body': ('django.db.models.fields.TextField', [], {}),
+ 'from_address': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'mailbox': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['django_mailbox.Mailbox']"}),
+ 'message_id': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'received': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'subject': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+ }
+ }
+
+ complete_apps = ['django_mailbox']
\ No newline at end of file
diff --git a/build/lib/django_mailbox/south_migrations/0002_auto__chg_field_mailbox_uri.py b/build/lib/django_mailbox/south_migrations/0002_auto__chg_field_mailbox_uri.py
new file mode 100644
index 00000000..47517b31
--- /dev/null
+++ b/build/lib/django_mailbox/south_migrations/0002_auto__chg_field_mailbox_uri.py
@@ -0,0 +1,38 @@
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+
+class Migration(SchemaMigration):
+
+ def forwards(self, orm):
+
+ # Changing field 'Mailbox.uri'
+ db.alter_column('django_mailbox_mailbox', 'uri', self.gf('django.db.models.fields.CharField')(max_length=255, null=True))
+
+ def backwards(self, orm):
+
+ # User chose to not deal with backwards NULL issues for 'Mailbox.uri'
+ raise RuntimeError("Cannot reverse this migration. 'Mailbox.uri' and its values cannot be restored.")
+
+ models = {
+ 'django_mailbox.mailbox': {
+ 'Meta': {'object_name': 'Mailbox'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'uri': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '255', 'null': 'True', 'blank': 'True'})
+ },
+ 'django_mailbox.message': {
+ 'Meta': {'object_name': 'Message'},
+ 'body': ('django.db.models.fields.TextField', [], {}),
+ 'from_address': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'mailbox': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'messages'", 'to': "orm['django_mailbox.Mailbox']"}),
+ 'message_id': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'received': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'subject': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+ }
+ }
+
+ complete_apps = ['django_mailbox']
\ No newline at end of file
diff --git a/build/lib/django_mailbox/south_migrations/0003_auto__add_field_mailbox_active.py b/build/lib/django_mailbox/south_migrations/0003_auto__add_field_mailbox_active.py
new file mode 100644
index 00000000..30e43542
--- /dev/null
+++ b/build/lib/django_mailbox/south_migrations/0003_auto__add_field_mailbox_active.py
@@ -0,0 +1,41 @@
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+
+class Migration(SchemaMigration):
+
+ def forwards(self, orm):
+ # Adding field 'Mailbox.active'
+ db.add_column('django_mailbox_mailbox', 'active',
+ self.gf('django.db.models.fields.BooleanField')(default=True),
+ keep_default=False)
+
+
+ def backwards(self, orm):
+ # Deleting field 'Mailbox.active'
+ db.delete_column('django_mailbox_mailbox', 'active')
+
+
+ models = {
+ 'django_mailbox.mailbox': {
+ 'Meta': {'object_name': 'Mailbox'},
+ 'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'uri': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '255', 'null': 'True', 'blank': 'True'})
+ },
+ 'django_mailbox.message': {
+ 'Meta': {'object_name': 'Message'},
+ 'body': ('django.db.models.fields.TextField', [], {}),
+ 'from_address': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'mailbox': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'messages'", 'to': "orm['django_mailbox.Mailbox']"}),
+ 'message_id': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'received': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'subject': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+ }
+ }
+
+ complete_apps = ['django_mailbox']
\ No newline at end of file
diff --git a/build/lib/django_mailbox/south_migrations/0004_auto__add_field_message_outgoing.py b/build/lib/django_mailbox/south_migrations/0004_auto__add_field_message_outgoing.py
new file mode 100644
index 00000000..245c43bb
--- /dev/null
+++ b/build/lib/django_mailbox/south_migrations/0004_auto__add_field_message_outgoing.py
@@ -0,0 +1,42 @@
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+
+class Migration(SchemaMigration):
+
+ def forwards(self, orm):
+ # Adding field 'Message.outgoing'
+ db.add_column('django_mailbox_message', 'outgoing',
+ self.gf('django.db.models.fields.BooleanField')(default=False),
+ keep_default=False)
+
+
+ def backwards(self, orm):
+ # Deleting field 'Message.outgoing'
+ db.delete_column('django_mailbox_message', 'outgoing')
+
+
+ models = {
+ 'django_mailbox.mailbox': {
+ 'Meta': {'object_name': 'Mailbox'},
+ 'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'uri': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '255', 'null': 'True', 'blank': 'True'})
+ },
+ 'django_mailbox.message': {
+ 'Meta': {'object_name': 'Message'},
+ 'body': ('django.db.models.fields.TextField', [], {}),
+ 'from_address': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'mailbox': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'messages'", 'to': "orm['django_mailbox.Mailbox']"}),
+ 'message_id': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'outgoing': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'received': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'subject': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+ }
+ }
+
+ complete_apps = ['django_mailbox']
diff --git a/build/lib/django_mailbox/south_migrations/0005_rename_fields.py b/build/lib/django_mailbox/south_migrations/0005_rename_fields.py
new file mode 100644
index 00000000..72cb4372
--- /dev/null
+++ b/build/lib/django_mailbox/south_migrations/0005_rename_fields.py
@@ -0,0 +1,38 @@
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+
+class Migration(SchemaMigration):
+
+ def forwards(self, orm):
+ db.rename_column('django_mailbox_message', 'from_address', 'address')
+ db.rename_column('django_mailbox_message', 'received', 'processed')
+
+ def backwards(self, orm):
+ db.rename_column('django_mailbox_message', 'address', 'from_address')
+ db.rename_column('django_mailbox_message', 'processed', 'received')
+
+ models = {
+ 'django_mailbox.mailbox': {
+ 'Meta': {'object_name': 'Mailbox'},
+ 'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'uri': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '255', 'null': 'True', 'blank': 'True'})
+ },
+ 'django_mailbox.message': {
+ 'Meta': {'object_name': 'Message'},
+ 'address': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'body': ('django.db.models.fields.TextField', [], {}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'mailbox': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'messages'", 'to': "orm['django_mailbox.Mailbox']"}),
+ 'message_id': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'outgoing': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'processed': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'subject': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+ }
+ }
+
+ complete_apps = ['django_mailbox']
diff --git a/build/lib/django_mailbox/south_migrations/0006_auto__add_field_message_in_reply_to.py b/build/lib/django_mailbox/south_migrations/0006_auto__add_field_message_in_reply_to.py
new file mode 100644
index 00000000..4287fb26
--- /dev/null
+++ b/build/lib/django_mailbox/south_migrations/0006_auto__add_field_message_in_reply_to.py
@@ -0,0 +1,55 @@
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+
+class Migration(SchemaMigration):
+
+ def forwards(self, orm):
+ # Adding field 'Message.in_reply_to'
+ db.add_column('django_mailbox_message', 'in_reply_to',
+ self.gf('django.db.models.fields.related.ForeignKey')(blank=True, related_name='replies', null=True, to=orm['django_mailbox.Message']),
+ keep_default=False)
+
+ # 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))
+ ))
+ db.create_unique('django_mailbox_message_references', ['from_message_id', 'to_message_id'])
+
+
+ def backwards(self, orm):
+ # Deleting field 'Message.in_reply_to'
+ db.delete_column('django_mailbox_message', 'in_reply_to_id')
+
+ # Removing M2M table for field references on 'Message'
+ db.delete_table('django_mailbox_message_references')
+
+
+ models = {
+ 'django_mailbox.mailbox': {
+ 'Meta': {'object_name': 'Mailbox'},
+ 'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'uri': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '255', 'null': 'True', 'blank': 'True'})
+ },
+ 'django_mailbox.message': {
+ 'Meta': {'object_name': 'Message'},
+ 'address': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'body': ('django.db.models.fields.TextField', [], {}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'in_reply_to': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'replies'", 'null': 'True', 'to': "orm['django_mailbox.Message']"}),
+ 'mailbox': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'messages'", 'to': "orm['django_mailbox.Mailbox']"}),
+ 'message_id': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'outgoing': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'processed': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'references': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'referenced_by'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['django_mailbox.Message']"}),
+ 'subject': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+ }
+ }
+
+ complete_apps = ['django_mailbox']
\ No newline at end of file
diff --git a/build/lib/django_mailbox/south_migrations/0007_auto__del_field_message_address__add_field_message_from_header__add_fi.py b/build/lib/django_mailbox/south_migrations/0007_auto__del_field_message_address__add_field_message_from_header__add_fi.py
new file mode 100644
index 00000000..cfa7d6f0
--- /dev/null
+++ b/build/lib/django_mailbox/south_migrations/0007_auto__del_field_message_address__add_field_message_from_header__add_fi.py
@@ -0,0 +1,59 @@
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+
+class Migration(SchemaMigration):
+
+ def forwards(self, orm):
+ # Deleting field 'Message.address'
+ db.delete_column('django_mailbox_message', 'address')
+
+ # Adding field 'Message.from_header'
+ db.add_column('django_mailbox_message', 'from_header',
+ self.gf('django.db.models.fields.CharField')(default='', max_length=255),
+ keep_default=False)
+
+ # Adding field 'Message.to_header'
+ db.add_column('django_mailbox_message', 'to_header',
+ self.gf('django.db.models.fields.TextField')(default=''),
+ keep_default=False)
+
+
+ def backwards(self, orm):
+
+ # User chose to not deal with backwards NULL issues for 'Message.address'
+ raise RuntimeError("Cannot reverse this migration. 'Message.address' and its values cannot be restored.")
+ # Deleting field 'Message.from_header'
+ db.delete_column('django_mailbox_message', 'from_header')
+
+ # Deleting field 'Message.to_header'
+ db.delete_column('django_mailbox_message', 'to_header')
+
+
+ models = {
+ 'django_mailbox.mailbox': {
+ 'Meta': {'object_name': 'Mailbox'},
+ 'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'uri': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '255', 'null': 'True', 'blank': 'True'})
+ },
+ 'django_mailbox.message': {
+ 'Meta': {'object_name': 'Message'},
+ 'body': ('django.db.models.fields.TextField', [], {}),
+ 'from_header': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'in_reply_to': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'replies'", 'null': 'True', 'to': "orm['django_mailbox.Message']"}),
+ 'mailbox': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'messages'", 'to': "orm['django_mailbox.Mailbox']"}),
+ 'message_id': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'outgoing': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'processed': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'references': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'referenced_by'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['django_mailbox.Message']"}),
+ 'subject': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'to_header': ('django.db.models.fields.TextField', [], {})
+ }
+ }
+
+ complete_apps = ['django_mailbox']
\ No newline at end of file
diff --git a/build/lib/django_mailbox/south_migrations/0008_populate_from_to_fields.py b/build/lib/django_mailbox/south_migrations/0008_populate_from_to_fields.py
new file mode 100644
index 00000000..e047f223
--- /dev/null
+++ b/build/lib/django_mailbox/south_migrations/0008_populate_from_to_fields.py
@@ -0,0 +1,46 @@
+import datetime
+import email
+from south.db import db
+from south.v2 import DataMigration
+from django.db import models
+
+class Migration(DataMigration):
+
+ def forwards(self, orm):
+ for message in orm['django_mailbox.message'].objects.all():
+ msg_object = email.message_from_string(
+ message.body
+ )
+ message.from_header = msg_object['from']
+ message.to_header = msg_object['to']
+ message.save()
+
+ def backwards(self, orm):
+ raise RuntimeError('Cannot reverse this migration.')
+
+ models = {
+ 'django_mailbox.mailbox': {
+ 'Meta': {'object_name': 'Mailbox'},
+ 'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'uri': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '255', 'null': 'True', 'blank': 'True'})
+ },
+ 'django_mailbox.message': {
+ 'Meta': {'object_name': 'Message'},
+ 'body': ('django.db.models.fields.TextField', [], {}),
+ 'from_header': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'in_reply_to': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'replies'", 'null': 'True', 'to': "orm['django_mailbox.Message']"}),
+ 'mailbox': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'messages'", 'to': "orm['django_mailbox.Mailbox']"}),
+ 'message_id': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'outgoing': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'processed': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'references': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'referenced_by'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['django_mailbox.Message']"}),
+ 'subject': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'to_header': ('django.db.models.fields.TextField', [], {})
+ }
+ }
+
+ complete_apps = ['django_mailbox']
+ symmetrical = True
diff --git a/build/lib/django_mailbox/south_migrations/0009_remove_references_table.py b/build/lib/django_mailbox/south_migrations/0009_remove_references_table.py
new file mode 100644
index 00000000..38685851
--- /dev/null
+++ b/build/lib/django_mailbox/south_migrations/0009_remove_references_table.py
@@ -0,0 +1,47 @@
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+
+class Migration(SchemaMigration):
+
+ def forwards(self, orm):
+ # Removing M2M table for field references on 'Message'
+ db.delete_table('django_mailbox_message_references')
+
+
+ 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))
+ ))
+ db.create_unique('django_mailbox_message_references', ['from_message_id', 'to_message_id'])
+
+
+ models = {
+ 'django_mailbox.mailbox': {
+ 'Meta': {'object_name': 'Mailbox'},
+ 'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'uri': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '255', 'null': 'True', 'blank': 'True'})
+ },
+ 'django_mailbox.message': {
+ 'Meta': {'object_name': 'Message'},
+ 'body': ('django.db.models.fields.TextField', [], {}),
+ 'from_header': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'in_reply_to': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'replies'", 'null': 'True', 'to': "orm['django_mailbox.Message']"}),
+ 'mailbox': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'messages'", 'to': "orm['django_mailbox.Mailbox']"}),
+ 'message_id': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'outgoing': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'processed': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'subject': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'to_header': ('django.db.models.fields.TextField', [], {})
+ }
+ }
+
+ complete_apps = ['django_mailbox']
diff --git a/build/lib/django_mailbox/south_migrations/0010_auto__add_field_mailbox_from_email.py b/build/lib/django_mailbox/south_migrations/0010_auto__add_field_mailbox_from_email.py
new file mode 100644
index 00000000..a988e5eb
--- /dev/null
+++ b/build/lib/django_mailbox/south_migrations/0010_auto__add_field_mailbox_from_email.py
@@ -0,0 +1,45 @@
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+
+class Migration(SchemaMigration):
+
+ def forwards(self, orm):
+ # Adding field 'Mailbox.from_email'
+ db.add_column('django_mailbox_mailbox', 'from_email',
+ self.gf('django.db.models.fields.CharField')(default=None, max_length=255, null=True, blank=True),
+ keep_default=False)
+
+
+ def backwards(self, orm):
+ # Deleting field 'Mailbox.from_email'
+ db.delete_column('django_mailbox_mailbox', 'from_email')
+
+
+ models = {
+ 'django_mailbox.mailbox': {
+ 'Meta': {'object_name': 'Mailbox'},
+ 'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'from_email': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '255', 'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'uri': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '255', 'null': 'True', 'blank': 'True'})
+ },
+ 'django_mailbox.message': {
+ 'Meta': {'object_name': 'Message'},
+ 'body': ('django.db.models.fields.TextField', [], {}),
+ 'from_header': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'in_reply_to': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'replies'", 'null': 'True', 'to': "orm['django_mailbox.Message']"}),
+ 'mailbox': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'messages'", 'to': "orm['django_mailbox.Mailbox']"}),
+ 'message_id': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'outgoing': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'processed': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'subject': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'to_header': ('django.db.models.fields.TextField', [], {})
+ }
+ }
+
+ complete_apps = ['django_mailbox']
\ No newline at end of file
diff --git a/build/lib/django_mailbox/south_migrations/0011_auto__add_field_message_read.py b/build/lib/django_mailbox/south_migrations/0011_auto__add_field_message_read.py
new file mode 100644
index 00000000..c75db341
--- /dev/null
+++ b/build/lib/django_mailbox/south_migrations/0011_auto__add_field_message_read.py
@@ -0,0 +1,46 @@
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+
+class Migration(SchemaMigration):
+
+ def forwards(self, orm):
+ # Adding field 'Message.read'
+ db.add_column('django_mailbox_message', 'read',
+ self.gf('django.db.models.fields.DateTimeField')(default=None, null=True, blank=True),
+ keep_default=False)
+
+
+ def backwards(self, orm):
+ # Deleting field 'Message.read'
+ db.delete_column('django_mailbox_message', 'read')
+
+
+ models = {
+ 'django_mailbox.mailbox': {
+ 'Meta': {'object_name': 'Mailbox'},
+ 'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'from_email': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '255', 'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'uri': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '255', 'null': 'True', 'blank': 'True'})
+ },
+ 'django_mailbox.message': {
+ 'Meta': {'object_name': 'Message'},
+ 'body': ('django.db.models.fields.TextField', [], {}),
+ 'from_header': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'in_reply_to': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'replies'", 'null': 'True', 'to': "orm['django_mailbox.Message']"}),
+ 'mailbox': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'messages'", 'to': "orm['django_mailbox.Mailbox']"}),
+ 'message_id': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'outgoing': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'processed': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'read': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True', 'blank': 'True'}),
+ 'subject': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'to_header': ('django.db.models.fields.TextField', [], {})
+ }
+ }
+
+ complete_apps = ['django_mailbox']
\ No newline at end of file
diff --git a/build/lib/django_mailbox/south_migrations/0012_auto__add_messageattachment.py b/build/lib/django_mailbox/south_migrations/0012_auto__add_messageattachment.py
new file mode 100644
index 00000000..b62e50e9
--- /dev/null
+++ b/build/lib/django_mailbox/south_migrations/0012_auto__add_messageattachment.py
@@ -0,0 +1,66 @@
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+class Migration(SchemaMigration):
+
+ def forwards(self, orm):
+
+ # Adding model 'MessageAttachment'
+ db.create_table('django_mailbox_messageattachment', (
+ ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+ ('document', self.gf('django.db.models.fields.files.FileField')(max_length=100)),
+ ))
+ db.send_create_signal('django_mailbox', ['MessageAttachment'])
+
+ # 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))
+ ))
+ db.create_unique('django_mailbox_message_attachments', ['message_id', 'messageattachment_id'])
+
+
+ def backwards(self, orm):
+
+ # Deleting model 'MessageAttachment'
+ db.delete_table('django_mailbox_messageattachment')
+
+ # Removing M2M table for field attachments on 'Message'
+ db.delete_table('django_mailbox_message_attachments')
+
+
+ models = {
+ 'django_mailbox.mailbox': {
+ 'Meta': {'object_name': 'Mailbox'},
+ 'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'from_email': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '255', 'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'uri': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '255', 'null': 'True', 'blank': 'True'})
+ },
+ 'django_mailbox.message': {
+ 'Meta': {'object_name': 'Message'},
+ 'attachments': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['django_mailbox.MessageAttachment']", 'symmetrical': 'False'}),
+ 'body': ('django.db.models.fields.TextField', [], {}),
+ 'from_header': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'in_reply_to': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'replies'", 'null': 'True', 'to': "orm['django_mailbox.Message']"}),
+ 'mailbox': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'messages'", 'to': "orm['django_mailbox.Mailbox']"}),
+ 'message_id': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'outgoing': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'processed': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'read': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True', 'blank': 'True'}),
+ 'subject': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'to_header': ('django.db.models.fields.TextField', [], {})
+ },
+ 'django_mailbox.messageattachment': {
+ 'Meta': {'object_name': 'MessageAttachment'},
+ 'document': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
+ }
+ }
+
+ complete_apps = ['django_mailbox']
diff --git a/build/lib/django_mailbox/south_migrations/0013_auto__add_field_messageattachment_message.py b/build/lib/django_mailbox/south_migrations/0013_auto__add_field_messageattachment_message.py
new file mode 100644
index 00000000..01c80a2e
--- /dev/null
+++ b/build/lib/django_mailbox/south_migrations/0013_auto__add_field_messageattachment_message.py
@@ -0,0 +1,53 @@
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+
+class Migration(SchemaMigration):
+
+ def forwards(self, orm):
+ # Adding field 'MessageAttachment.message'
+ db.add_column('django_mailbox_messageattachment', 'message',
+ self.gf('django.db.models.fields.related.ForeignKey')(blank=True, related_name='attachments', null=True, to=orm['django_mailbox.Message']),
+ keep_default=False)
+
+
+ def backwards(self, orm):
+ # Deleting field 'MessageAttachment.message'
+ db.delete_column('django_mailbox_messageattachment', 'message_id')
+
+
+ models = {
+ 'django_mailbox.mailbox': {
+ 'Meta': {'object_name': 'Mailbox'},
+ 'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'from_email': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '255', 'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'uri': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '255', 'null': 'True', 'blank': 'True'})
+ },
+ 'django_mailbox.message': {
+ 'Meta': {'object_name': 'Message'},
+ 'attachments': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'messages_old'", 'blank': 'True', 'to': "orm['django_mailbox.MessageAttachment']"}),
+ 'body': ('django.db.models.fields.TextField', [], {}),
+ 'from_header': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'in_reply_to': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'replies'", 'null': 'True', 'to': "orm['django_mailbox.Message']"}),
+ 'mailbox': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'messages'", 'to': "orm['django_mailbox.Mailbox']"}),
+ 'message_id': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'outgoing': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'processed': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'read': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True', 'blank': 'True'}),
+ 'subject': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'to_header': ('django.db.models.fields.TextField', [], {})
+ },
+ 'django_mailbox.messageattachment': {
+ 'Meta': {'object_name': 'MessageAttachment'},
+ 'document': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'message': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'attachments_new'", 'null': 'True', 'to': "orm['django_mailbox.Message']"})
+ }
+ }
+
+ complete_apps = ['django_mailbox']
diff --git a/build/lib/django_mailbox/south_migrations/0014_migrate_message_attachments.py b/build/lib/django_mailbox/south_migrations/0014_migrate_message_attachments.py
new file mode 100644
index 00000000..b018915e
--- /dev/null
+++ b/build/lib/django_mailbox/south_migrations/0014_migrate_message_attachments.py
@@ -0,0 +1,54 @@
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+
+class Migration(SchemaMigration):
+ no_dry_run = True
+ def forwards(self, orm):
+ for message in orm['django_mailbox.Message'].objects.all():
+ for attachment in message.attachments.all():
+ attachment.message = message
+ attachment.save()
+
+ def backwards(self, orm):
+ for attachment in orm['django_mailbox.MessageAttachment'].objects.all():
+ if attachment.message:
+ attachment.message.attachments.add(
+ attachment
+ )
+
+ models = {
+ 'django_mailbox.mailbox': {
+ 'Meta': {'object_name': 'Mailbox'},
+ 'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'from_email': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '255', 'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'uri': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '255', 'null': 'True', 'blank': 'True'})
+ },
+ 'django_mailbox.message': {
+ 'Meta': {'object_name': 'Message'},
+ 'attachments': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'messages_old'", 'blank': 'True', 'to': "orm['django_mailbox.MessageAttachment']"}),
+ 'body': ('django.db.models.fields.TextField', [], {}),
+ 'from_header': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'in_reply_to': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'replies'", 'null': 'True', 'to': "orm['django_mailbox.Message']"}),
+ 'mailbox': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'messages'", 'to': "orm['django_mailbox.Mailbox']"}),
+ 'message_id': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'outgoing': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'processed': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'read': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True', 'blank': 'True'}),
+ 'subject': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'to_header': ('django.db.models.fields.TextField', [], {})
+ },
+ 'django_mailbox.messageattachment': {
+ 'Meta': {'object_name': 'MessageAttachment'},
+ 'document': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'message': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'attachments_new'", 'null': 'True', 'to': "orm['django_mailbox.Message']"})
+ }
+ }
+
+ complete_apps = ['django_mailbox']
diff --git a/build/lib/django_mailbox/south_migrations/0015_auto__add_field_messageattachment_headers.py b/build/lib/django_mailbox/south_migrations/0015_auto__add_field_messageattachment_headers.py
new file mode 100644
index 00000000..71ecfbdc
--- /dev/null
+++ b/build/lib/django_mailbox/south_migrations/0015_auto__add_field_messageattachment_headers.py
@@ -0,0 +1,64 @@
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+
+class Migration(SchemaMigration):
+
+ def forwards(self, orm):
+ # Removing M2M table for field attachments on 'Message'
+ db.delete_table('django_mailbox_message_attachments')
+
+ # Adding field 'MessageAttachment.headers'
+ db.add_column('django_mailbox_messageattachment', 'headers',
+ self.gf('django.db.models.fields.TextField')(null=True, blank=True),
+ keep_default=False)
+
+
+ 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))
+ ))
+ db.create_unique('django_mailbox_message_attachments', ['message_id', 'messageattachment_id'])
+
+ # Deleting field 'MessageAttachment.headers'
+ db.delete_column('django_mailbox_messageattachment', 'headers')
+
+
+ models = {
+ 'django_mailbox.mailbox': {
+ 'Meta': {'object_name': 'Mailbox'},
+ 'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'from_email': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '255', 'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'uri': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '255', 'null': 'True', 'blank': 'True'})
+ },
+ 'django_mailbox.message': {
+ 'Meta': {'object_name': 'Message'},
+ 'body': ('django.db.models.fields.TextField', [], {}),
+ 'from_header': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'in_reply_to': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'replies'", 'null': 'True', 'to': "orm['django_mailbox.Message']"}),
+ 'mailbox': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'messages'", 'to': "orm['django_mailbox.Mailbox']"}),
+ 'message_id': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'outgoing': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'processed': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'read': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True', 'blank': 'True'}),
+ 'subject': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'to_header': ('django.db.models.fields.TextField', [], {})
+ },
+ 'django_mailbox.messageattachment': {
+ 'Meta': {'object_name': 'MessageAttachment'},
+ 'document': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}),
+ 'headers': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'message': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'attachments'", 'null': 'True', 'to': "orm['django_mailbox.Message']"})
+ }
+ }
+
+ complete_apps = ['django_mailbox']
\ No newline at end of file
diff --git a/build/lib/django_mailbox/south_migrations/0016_auto__add_field_message_encoded.py b/build/lib/django_mailbox/south_migrations/0016_auto__add_field_message_encoded.py
new file mode 100644
index 00000000..cd4c6787
--- /dev/null
+++ b/build/lib/django_mailbox/south_migrations/0016_auto__add_field_message_encoded.py
@@ -0,0 +1,54 @@
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+
+class Migration(SchemaMigration):
+
+ def forwards(self, orm):
+ # Adding field 'Message.encoded'
+ db.add_column('django_mailbox_message', 'encoded',
+ self.gf('django.db.models.fields.BooleanField')(default=False),
+ keep_default=False)
+
+
+ def backwards(self, orm):
+ # Deleting field 'Message.encoded'
+ db.delete_column('django_mailbox_message', 'encoded')
+
+
+ models = {
+ 'django_mailbox.mailbox': {
+ 'Meta': {'object_name': 'Mailbox'},
+ 'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'from_email': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '255', 'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'uri': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '255', 'null': 'True', 'blank': 'True'})
+ },
+ 'django_mailbox.message': {
+ 'Meta': {'object_name': 'Message'},
+ 'body': ('django.db.models.fields.TextField', [], {}),
+ 'encoded': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'from_header': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'in_reply_to': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'replies'", 'null': 'True', 'to': "orm['django_mailbox.Message']"}),
+ 'mailbox': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'messages'", 'to': "orm['django_mailbox.Mailbox']"}),
+ 'message_id': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'outgoing': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'processed': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'read': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True', 'blank': 'True'}),
+ 'subject': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'to_header': ('django.db.models.fields.TextField', [], {})
+ },
+ 'django_mailbox.messageattachment': {
+ 'Meta': {'object_name': 'MessageAttachment'},
+ 'document': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}),
+ 'headers': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'message': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'attachments'", 'null': 'True', 'to': "orm['django_mailbox.Message']"})
+ }
+ }
+
+ complete_apps = ['django_mailbox']
\ No newline at end of file
diff --git a/build/lib/django_mailbox/south_migrations/0017_auto__add_field_message_eml.py b/build/lib/django_mailbox/south_migrations/0017_auto__add_field_message_eml.py
new file mode 100644
index 00000000..186c8bd4
--- /dev/null
+++ b/build/lib/django_mailbox/south_migrations/0017_auto__add_field_message_eml.py
@@ -0,0 +1,55 @@
+from south.utils import datetime_utils as datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+
+class Migration(SchemaMigration):
+
+ def forwards(self, orm):
+ # Adding field 'Message.eml'
+ db.add_column('django_mailbox_message', 'eml',
+ self.gf('django.db.models.fields.files.FileField')(max_length=100, null=True),
+ keep_default=False)
+
+
+ def backwards(self, orm):
+ # Deleting field 'Message.eml'
+ db.delete_column('django_mailbox_message', 'eml')
+
+
+ models = {
+ 'django_mailbox.mailbox': {
+ 'Meta': {'object_name': 'Mailbox'},
+ 'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'from_email': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '255', 'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'uri': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '255', 'null': 'True', 'blank': 'True'})
+ },
+ 'django_mailbox.message': {
+ 'Meta': {'object_name': 'Message'},
+ 'body': ('django.db.models.fields.TextField', [], {}),
+ 'eml': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'null': 'True'}),
+ 'encoded': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'from_header': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'in_reply_to': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'replies'", 'null': 'True', 'to': "orm['django_mailbox.Message']"}),
+ 'mailbox': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'messages'", 'to': "orm['django_mailbox.Mailbox']"}),
+ 'message_id': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'outgoing': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'processed': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'read': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True', 'blank': 'True'}),
+ 'subject': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'to_header': ('django.db.models.fields.TextField', [], {})
+ },
+ 'django_mailbox.messageattachment': {
+ 'Meta': {'object_name': 'MessageAttachment'},
+ 'document': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}),
+ 'headers': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'message': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'attachments'", 'null': 'True', 'to': "orm['django_mailbox.Message']"})
+ }
+ }
+
+ complete_apps = ['django_mailbox']
\ No newline at end of file
diff --git a/build/lib/django_mailbox/south_migrations/__init__.py b/build/lib/django_mailbox/south_migrations/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/build/lib/django_mailbox/tests/__init__.py b/build/lib/django_mailbox/tests/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/build/lib/django_mailbox/tests/base.py b/build/lib/django_mailbox/tests/base.py
new file mode 100644
index 00000000..b1215c5e
--- /dev/null
+++ b/build/lib/django_mailbox/tests/base.py
@@ -0,0 +1,191 @@
+import email
+import os.path
+import time
+
+from django.conf import settings
+from django.test import TestCase
+
+from django_mailbox import models, utils
+from django_mailbox.models import Mailbox, Message
+
+
+class EmailIntegrationTimeout(Exception):
+ pass
+
+
+def get_email_as_text(name):
+ with open(
+ os.path.join(
+ os.path.dirname(__file__),
+ 'messages',
+ name,
+ ),
+ 'rb'
+ ) as f:
+ return f.read()
+
+
+class EmailMessageTestCase(TestCase):
+ ALLOWED_EXTRA_HEADERS = [
+ 'MIME-Version',
+ 'Content-Transfer-Encoding',
+ ]
+
+ def setUp(self):
+ dm_settings = utils.get_settings()
+
+ self._ALLOWED_MIMETYPES = dm_settings['allowed_mimetypes']
+ self._STRIP_UNALLOWED_MIMETYPES = (
+ dm_settings['strip_unallowed_mimetypes']
+ )
+ self._TEXT_STORED_MIMETYPES = dm_settings['text_stored_mimetypes']
+
+ self.mailbox = Mailbox.objects.create(from_email='from@example.com')
+
+ self.test_account = os.environ.get('EMAIL_ACCOUNT')
+ self.test_password = os.environ.get('EMAIL_PASSWORD')
+ self.test_smtp_server = os.environ.get('EMAIL_SMTP_SERVER')
+ self.test_from_email = 'nobody@nowhere.com'
+
+ self.maximum_wait_seconds = 60 * 5
+
+ settings.EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
+ settings.EMAIL_HOST = self.test_smtp_server
+ settings.EMAIL_PORT = 587
+ settings.EMAIL_HOST_USER = self.test_account
+ settings.EMAIL_HOST_PASSWORD = self.test_password
+ settings.EMAIL_USE_TLS = True
+ super().setUp()
+
+ def _get_new_messages(self, mailbox, condition=None):
+ start_time = time.time()
+ # wait until there is at least one message
+ while time.time() - start_time < self.maximum_wait_seconds:
+
+ messages = self.mailbox.get_new_mail(condition)
+
+ try:
+ # check if generator contains at least one element
+ message = next(messages)
+ yield message
+ yield from messages
+ return
+
+ except StopIteration:
+ time.sleep(5)
+
+ raise EmailIntegrationTimeout()
+
+ def _get_email_as_text(self, name):
+ with open(
+ os.path.join(
+ os.path.dirname(__file__),
+ 'messages',
+ name,
+ ),
+ 'rb'
+ ) as f:
+ return f.read()
+
+ def _get_email_object(self, name):
+ copy = self._get_email_as_text(name)
+ return email.message_from_bytes(copy)
+
+ def _headers_identical(self, left, right, header=None):
+ """ Check if headers are (close enough to) identical.
+
+ * This is particularly tricky because Python 2.6, Python 2.7 and
+ Python 3 each handle header strings slightly differently. This
+ should mash away all of the differences, though.
+ * This also has a small loophole in that when re-writing e-mail
+ payload encodings, we re-build the Content-Type header, so if the
+ header was originally unquoted, it will be quoted when rehydrating
+ the e-mail message.
+
+ """
+ if header.lower() == 'content-type':
+ # Special case; given that we re-write the header, we'll be quoting
+ # the new content type; we need to make sure that doesn't cause
+ # this comparison to fail. Also, the case of the encoding could
+ # be changed, etc. etc. etc.
+ left = left.replace('"', '').upper()
+ right = right.replace('"', '').upper()
+ left = left.replace('\n\t', ' ').replace('\n ', ' ')
+ right = right.replace('\n\t', ' ').replace('\n ', ' ')
+ if right != left:
+ return False
+ return True
+
+ def compare_email_objects(self, left, right):
+ # Compare headers
+ for key, value in left.items():
+ if not right[key] and key in self.ALLOWED_EXTRA_HEADERS:
+ continue
+ if not right[key]:
+ raise AssertionError("Extra header '%s'" % key)
+ if not self._headers_identical(right[key], value, header=key):
+ raise AssertionError(
+ "Header '{}' unequal:\n{}\n{}".format(
+ key,
+ repr(value),
+ repr(right[key]),
+ )
+ )
+ for key, value in right.items():
+ if not left[key] and key in self.ALLOWED_EXTRA_HEADERS:
+ continue
+ if not left[key]:
+ raise AssertionError("Extra header '%s'" % key)
+ if not self._headers_identical(left[key], value, header=key):
+ raise AssertionError(
+ "Header '{}' unequal:\n{}\n{}".format(
+ key,
+ repr(value),
+ repr(right[key]),
+ )
+ )
+ if left.is_multipart() != right.is_multipart():
+ self._raise_mismatched(left, right)
+ if left.is_multipart():
+ left_payloads = left.get_payload()
+ right_payloads = right.get_payload()
+ if len(left_payloads) != len(right_payloads):
+ self._raise_mismatched(left, right)
+ for n in range(len(left_payloads)):
+ self.compare_email_objects(
+ left_payloads[n],
+ right_payloads[n]
+ )
+ else:
+ if left.get_payload() is None or right.get_payload() is None:
+ if left.get_payload() is None:
+ if right.get_payload is not None:
+ self._raise_mismatched(left, right)
+ if right.get_payload() is None:
+ if left.get_payload is not None:
+ self._raise_mismatched(left, right)
+ elif left.get_payload().strip() != right.get_payload().strip():
+ self._raise_mismatched(left, right)
+
+ def _raise_mismatched(self, left, right):
+ raise AssertionError(
+ "Message payloads do not match:\n{}\n{}".format(
+ left.as_string(),
+ right.as_string()
+ )
+ )
+
+ def assertEqual(self, left, right): # noqa: N802
+ if not isinstance(left, email.message.Message):
+ return super().assertEqual(left, right)
+ return self.compare_email_objects(left, right)
+
+ def tearDown(self):
+ for message in Message.objects.all():
+ message.delete()
+ models.ALLOWED_MIMETYPES = self._ALLOWED_MIMETYPES
+ models.STRIP_UNALLOWED_MIMETYPES = self._STRIP_UNALLOWED_MIMETYPES
+ models.TEXT_STORED_MIMETYPES = self._TEXT_STORED_MIMETYPES
+
+ self.mailbox.delete()
+ super().tearDown()
diff --git a/build/lib/django_mailbox/tests/settings.py b/build/lib/django_mailbox/tests/settings.py
new file mode 100644
index 00000000..66c7168e
--- /dev/null
+++ b/build/lib/django_mailbox/tests/settings.py
@@ -0,0 +1,12 @@
+DATABASES = {
+ 'default': {
+ 'NAME': 'db.sqlite3',
+ 'ENGINE': 'django.db.backends.sqlite3',
+ },
+}
+INSTALLED_APPS = [
+ 'django.contrib.auth',
+ 'django.contrib.contenttypes',
+ 'django_mailbox',
+]
+SECRET_KEY = 'beepboop'
diff --git a/build/lib/django_mailbox/tests/test_integration_imap.py b/build/lib/django_mailbox/tests/test_integration_imap.py
new file mode 100644
index 00000000..6f875b74
--- /dev/null
+++ b/build/lib/django_mailbox/tests/test_integration_imap.py
@@ -0,0 +1,70 @@
+import os
+import uuid
+from urllib import parse
+
+from django.core.mail import EmailMultiAlternatives
+
+from django_mailbox.models import Mailbox
+from django_mailbox.tests.base import EmailMessageTestCase
+
+
+__all__ = ['TestImap']
+
+
+class TestImap(EmailMessageTestCase):
+ def setUp(self):
+ super().setUp()
+
+ self.test_imap_server = (
+ os.environ.get('EMAIL_IMAP_SERVER')
+ )
+
+ required_settings = [
+ self.test_imap_server,
+ self.test_account,
+ self.test_password,
+ self.test_smtp_server,
+ self.test_from_email,
+ ]
+ if not all(required_settings):
+ self.skipTest(
+ "Integration tests are not available without having "
+ "the the following environment variables set: "
+ "EMAIL_ACCOUNT, EMAIL_PASSWORD, EMAIL_SMTP_SERVER, "
+ "EMAIL_IMAP_SERVER."
+ )
+
+ self.mailbox = Mailbox.objects.create(
+ name='Integration Test Imap',
+ uri=self.get_connection_string()
+ )
+ self.arbitrary_identifier = str(uuid.uuid4())
+
+ def get_connection_string(self):
+ return "imap+ssl://{account}:{password}@{server}".format(
+ account=parse.quote(self.test_account),
+ password=parse.quote(self.test_password),
+ server=self.test_imap_server,
+ )
+
+ def test_get_imap_message(self):
+ text_content = 'This is some content'
+ msg = EmailMultiAlternatives(
+ self.arbitrary_identifier,
+ text_content,
+ self.test_from_email,
+ [
+ self.test_account,
+ ]
+ )
+ msg.send()
+
+ messages = self._get_new_messages(
+ self.mailbox,
+ condition=lambda m: m['subject'] == self.arbitrary_identifier
+ )
+ message = next(messages)
+
+ self.assertEqual(message.subject, self.arbitrary_identifier)
+ self.assertEqual(message.text, text_content)
+ self.assertEqual(0, len(list(messages)))
diff --git a/build/lib/django_mailbox/tests/test_mailbox.py b/build/lib/django_mailbox/tests/test_mailbox.py
new file mode 100644
index 00000000..e233f753
--- /dev/null
+++ b/build/lib/django_mailbox/tests/test_mailbox.py
@@ -0,0 +1,46 @@
+import os
+
+from django.test import TestCase
+
+from django_mailbox.models import Mailbox
+
+
+__all__ = ['TestMailbox']
+
+
+class TestMailbox(TestCase):
+ def test_protocol_info(self):
+ mailbox = Mailbox()
+ mailbox.uri = 'alpha://test.com'
+
+ expected_protocol = 'alpha'
+ actual_protocol = mailbox._protocol_info.scheme
+
+ self.assertEqual(
+ expected_protocol,
+ actual_protocol,
+ )
+
+ def test_last_polling_field_exists(self):
+ mailbox = Mailbox()
+ self.assertTrue(hasattr(mailbox, 'last_polling'))
+
+ def test_get_new_mail_update_last_polling(self):
+ mailbox = Mailbox.objects.create(uri="mbox://" + os.path.join(
+ os.path.dirname(__file__),
+ 'messages',
+ 'generic_message.eml',
+ ))
+ self.assertEqual(mailbox.last_polling, None)
+ list(mailbox.get_new_mail())
+ self.assertNotEqual(mailbox.last_polling, None)
+
+ def test_queryset_get_new_mail(self):
+ mailbox = Mailbox.objects.create(uri="mbox://" + os.path.join(
+ os.path.dirname(__file__),
+ 'messages',
+ 'generic_message.eml',
+ ))
+ Mailbox.objects.filter(pk=mailbox.pk).get_new_mail()
+ mailbox.refresh_from_db()
+ self.assertNotEqual(mailbox.last_polling, None)
diff --git a/build/lib/django_mailbox/tests/test_message_flattening.py b/build/lib/django_mailbox/tests/test_message_flattening.py
new file mode 100644
index 00000000..669433ed
--- /dev/null
+++ b/build/lib/django_mailbox/tests/test_message_flattening.py
@@ -0,0 +1,116 @@
+import copy
+
+from unittest import mock
+
+from django_mailbox import models, utils
+from django_mailbox.models import Message
+from django_mailbox.tests.base import EmailMessageTestCase
+
+
+__all__ = ['TestMessageFlattening']
+
+
+class TestMessageFlattening(EmailMessageTestCase):
+ def test_quopri_message_is_properly_rehydrated(self):
+ incoming_email_object = self._get_email_object(
+ 'message_with_many_multiparts.eml',
+ )
+ # Note: this is identical to the above, but it appears that
+ # while reading-in an e-mail message, we do alter it slightly
+ expected_email_object = self._get_email_object(
+ 'message_with_many_multiparts.eml',
+ )
+ models.TEXT_STORED_MIMETYPES = ['text/plain']
+
+ msg = self.mailbox.process_incoming_message(incoming_email_object)
+
+ actual_email_object = msg.get_email_object()
+
+ self.assertEqual(
+ actual_email_object,
+ expected_email_object,
+ )
+
+ def test_base64_message_is_properly_rehydrated(self):
+ incoming_email_object = self._get_email_object(
+ 'message_with_attachment.eml',
+ )
+ # Note: this is identical to the above, but it appears that
+ # while reading-in an e-mail message, we do alter it slightly
+ expected_email_object = self._get_email_object(
+ 'message_with_attachment.eml',
+ )
+
+ msg = self.mailbox.process_incoming_message(incoming_email_object)
+
+ actual_email_object = msg.get_email_object()
+
+ self.assertEqual(
+ actual_email_object,
+ expected_email_object,
+ )
+
+ def test_message_handles_rehydration_problems(self):
+ incoming_email_object = self._get_email_object(
+ 'message_with_defective_attachment_association.eml',
+ )
+ expected_email_object = self._get_email_object(
+ 'message_with_defective_attachment_association_result.eml',
+ )
+ # Note: this is identical to the above, but it appears that
+ # while reading-in an e-mail message, we do alter it slightly
+ message = Message()
+ message.body = incoming_email_object.as_string()
+
+ msg = self.mailbox.process_incoming_message(incoming_email_object)
+
+ del msg._email_object # Cache flush
+ actual_email_object = msg.get_email_object()
+
+ self.assertEqual(
+ actual_email_object,
+ expected_email_object,
+ )
+
+ def test_message_content_type_stripping(self):
+ incoming_email_object = self._get_email_object(
+ 'message_with_many_multiparts.eml',
+ )
+ expected_email_object = self._get_email_object(
+ 'message_with_many_multiparts_stripped_html.eml',
+ )
+ default_settings = utils.get_settings()
+
+ with mock.patch('django_mailbox.utils.get_settings') as get_settings:
+ altered = copy.deepcopy(default_settings)
+ altered['strip_unallowed_mimetypes'] = True
+ altered['allowed_mimetypes'] = ['text/plain']
+
+ get_settings.return_value = altered
+
+ msg = self.mailbox.process_incoming_message(incoming_email_object)
+
+ del msg._email_object # Cache flush
+ actual_email_object = msg.get_email_object()
+
+ self.assertEqual(
+ actual_email_object,
+ expected_email_object,
+ )
+
+ def test_message_processing_unknown_encoding(self):
+ incoming_email_object = self._get_email_object(
+ 'message_with_invalid_encoding.eml',
+ )
+
+ msg = self.mailbox.process_incoming_message(incoming_email_object)
+
+ expected_text = (
+ "We offer loans to private individuals and corporate "
+ "organizations at 2% interest rate. Interested serious "
+ "applicants should apply via email with details of their "
+ "requirements.\n\nWarm Regards,\nLoan Team"
+ )
+ actual_text = msg.text
+
+ self.assertEqual(actual_text, expected_text)
diff --git a/build/lib/django_mailbox/tests/test_process_email.py b/build/lib/django_mailbox/tests/test_process_email.py
new file mode 100644
index 00000000..a6dbd044
--- /dev/null
+++ b/build/lib/django_mailbox/tests/test_process_email.py
@@ -0,0 +1,432 @@
+import gzip
+import os.path
+import sys
+
+import copy
+from unittest import mock
+
+from django_mailbox.models import Mailbox, Message
+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.core.mail import EmailMessage
+
+__all__ = ['TestProcessEmail']
+
+
+class TestProcessEmail(EmailMessageTestCase):
+ def test_message_without_attachments(self):
+ message = self._get_email_object('generic_message.eml')
+
+ mailbox = Mailbox.objects.create()
+ msg = mailbox.process_incoming_message(message)
+
+ self.assertEqual(
+ msg.mailbox,
+ mailbox
+ )
+ self.assertEqual(msg.subject, 'Message Without Attachment')
+ self.assertEqual(
+ msg.message_id,
+ (
+ ''
+ )
+ )
+ self.assertEqual(
+ msg.from_header,
+ 'Adam Coddington ',
+ )
+ self.assertEqual(
+ msg.to_header,
+ 'Adam Coddington ',
+ )
+
+ def test_message_with_encoded_attachment_filenames(self):
+ message = self._get_email_object(
+ 'message_with_koi8r_filename_attachments.eml'
+ )
+
+ mailbox = Mailbox.objects.create()
+ msg = mailbox.process_incoming_message(message)
+
+ attachments = msg.attachments.order_by('pk').all()
+ self.assertEqual(
+ '\u041f\u0430\u043a\u0435\u0442 \u043f\u0440\u0435\u0434\u043b'
+ '\u043e\u0436\u0435\u043d\u0438\u0439 HSE Career Fair 8 \u0430'
+ '\u043f\u0440\u0435\u043b\u044f 2016.pdf',
+ attachments[0].get_filename()
+ )
+ self.assertEqual(
+ '\u0412\u0435\u0434\u043e\u043c\u043e\u0441\u0442\u0438.pdf',
+ attachments[1].get_filename()
+ )
+ self.assertEqual(
+ '\u041f\u0430\u043a\u0435\u0442 \u043f\u0440\u0435\u0434\u043b'
+ '\u043e\u0436\u0435\u043d\u0438\u0439 2016.pptx',
+ attachments[2].get_filename()
+ )
+
+ def test_message_with_attachments(self):
+ message = self._get_email_object('message_with_attachment.eml')
+
+ mailbox = Mailbox.objects.create()
+ msg = mailbox.process_incoming_message(message)
+
+ expected_count = 1
+ actual_count = msg.attachments.count()
+
+ self.assertEqual(
+ expected_count,
+ actual_count,
+ )
+
+ attachment = msg.attachments.all()[0]
+ self.assertEqual(
+ attachment.get_filename(),
+ 'heart.png',
+ )
+
+ def test_message_with_utf8_attachment_header(self):
+ """ Ensure that we properly handle UTF-8 encoded attachment
+
+ Safe for regress of #104 too
+ """
+ email_object = self._get_email_object(
+ 'message_with_utf8_attachment.eml',
+ )
+ mailbox = Mailbox.objects.create()
+ msg = mailbox.process_incoming_message(email_object)
+
+ expected_count = 2
+ actual_count = msg.attachments.count()
+
+ self.assertEqual(
+ expected_count,
+ actual_count,
+ )
+
+ attachment = msg.attachments.all()[0]
+ self.assertEqual(
+ attachment.get_filename(),
+ 'pi\u0142kochwyty.jpg'
+ )
+
+ attachment = msg.attachments.all()[1]
+ self.assertEqual(
+ attachment.get_filename(),
+ 'odpowied\u017a Burmistrza.jpg'
+ )
+
+ def test_message_get_text_body(self):
+ message = self._get_email_object('multipart_text.eml')
+
+ mailbox = Mailbox.objects.create()
+ msg = mailbox.process_incoming_message(message)
+
+ expected_results = 'Hello there!'
+ actual_results = msg.text.strip()
+
+ self.assertEqual(
+ expected_results,
+ actual_results,
+ )
+
+ def test_get_text_body_properly_recomposes_line_continuations(self):
+ message = Message()
+ email_object = self._get_email_object(
+ 'message_with_long_text_lines.eml'
+ )
+
+ message.get_email_object = lambda: email_object
+
+ actual_text = message.text
+ expected_text = (
+ 'The one of us with a bike pump is far ahead, '
+ 'but a man stopped to help us and gave us his pump.'
+ )
+
+ self.assertEqual(
+ actual_text,
+ expected_text
+ )
+
+ def test_get_body_properly_handles_unicode_body(self):
+ with open(
+ os.path.join(
+ os.path.dirname(__file__),
+ 'messages/generic_message.eml'
+ )
+ ) as f:
+ unicode_body = f.read()
+
+ message = Message()
+ message.body = unicode_body
+
+ expected_body = unicode_body
+ actual_body = message.get_email_object().as_string()
+
+ self.assertEqual(
+ expected_body,
+ actual_body
+ )
+
+ def test_message_issue_82(self):
+ """ Ensure that we properly handle incorrectly encoded messages
+
+ """
+ email_object = self._get_email_object('email_issue_82.eml')
+ it = 'works'
+ try:
+ # it's ok to call as_string() before passing email_object
+ # to _get_dehydrated_message()
+ email_object.as_string()
+ except:
+ it = 'do not works'
+
+ success = True
+ try:
+ self.mailbox.process_incoming_message(email_object)
+ except ValueError:
+ success = False
+
+ self.assertEqual(it, 'works')
+ self.assertEqual(True, success)
+
+ def test_message_issue_82_bis(self):
+ """ Ensure that the email object is good before and after
+ calling _get_dehydrated_message()
+
+ """
+ message = self._get_email_object('email_issue_82.eml')
+
+ success = True
+
+ # this is the code of _process_message()
+ msg = Message()
+ # if STORE_ORIGINAL_MESSAGE:
+ # msg.eml.save('%s.eml' % uuid.uuid4(), ContentFile(message),
+ # save=False)
+ msg.mailbox = self.mailbox
+ if 'subject' in message:
+ msg.subject = convert_header_to_unicode(message['subject'])[0:255]
+ if 'message-id' in message:
+ msg.message_id = message['message-id'][0:255]
+ if 'from' in message:
+ msg.from_header = convert_header_to_unicode(message['from'])
+ if 'to' in message:
+ msg.to_header = convert_header_to_unicode(message['to'])
+ elif 'Delivered-To' in message:
+ msg.to_header = convert_header_to_unicode(message['Delivered-To'])
+ msg.save()
+
+ # here the message is ok
+ str_msg = message.as_string()
+ message = self.mailbox._get_dehydrated_message(message, msg)
+ try:
+ # here as_string raises UnicodeEncodeError
+ str_msg = message.as_string()
+ except:
+ success = False
+
+ msg.set_body(str_msg)
+ if message['in-reply-to']:
+ try:
+ msg.in_reply_to = Message.objects.filter(
+ message_id=message['in-reply-to']
+ )[0]
+ except IndexError:
+ pass
+ msg.save()
+
+ self.assertEqual(True, success)
+
+ def test_message_with_misplaced_utf8_content(self):
+ """ Ensure that we properly handle incorrectly encoded messages
+
+ ``message_with_utf8_char.eml``'s primary text payload is marked
+ as being iso-8859-1 data, but actually contains UTF-8 bytes.
+
+ """
+ email_object = self._get_email_object('message_with_utf8_char.eml')
+
+ msg = self.mailbox.process_incoming_message(email_object)
+
+ expected_text = 'This message contains funny UTF16 characters ' + \
+ 'like this one: "\xc2\xa0" and this one "\xe2\x9c\xbf".'
+ actual_text = msg.text
+
+ self.assertEqual(
+ expected_text,
+ actual_text,
+ )
+
+ def test_message_with_invalid_content_for_declared_encoding(self):
+ """ Ensure that we gracefully handle mis-encoded bodies.
+
+ Should a payload body be misencoded, we should:
+
+ - Not explode
+
+ Note: there is (intentionally) no assertion below; the only guarantee
+ we make via this library is that processing this e-mail message will
+ not cause an exception to be raised.
+
+ """
+ email_object = self._get_email_object(
+ 'message_with_invalid_content_for_declared_encoding.eml',
+ )
+
+ msg = self.mailbox.process_incoming_message(email_object)
+
+ msg.text
+
+ def test_message_with_valid_content_in_single_byte_encoding(self):
+ email_object = self._get_email_object(
+ 'message_with_single_byte_encoding.eml',
+ )
+
+ msg = self.mailbox.process_incoming_message(email_object)
+
+ actual_text = msg.text
+ expected_body = '\u042d\u0442\u043e ' + \
+ '\u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435 ' + \
+ '\u0438\u043c\u0435\u0435\u0442 ' + \
+ '\u043d\u0435\u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d' + \
+ '\u0443\u044e ' + \
+ '\u043a\u043e\u0434\u0438\u0440\u043e\u0432\u043a\u0430.'
+
+ self.assertEqual(
+ actual_text,
+ expected_body,
+ )
+
+ def test_message_with_single_byte_subject_encoding(self):
+ email_object = self._get_email_object(
+ 'message_with_single_byte_extended_subject_encoding.eml',
+ )
+
+ msg = self.mailbox.process_incoming_message(email_object)
+
+ expected_subject = '\u00D3\u00E7\u00ED\u00E0\u00E9 \u00EA\u00E0\u00EA ' + \
+ '\u00E7\u00E0\u00F0\u00E0\u00E1\u00E0\u00F2\u00FB\u00E2' + \
+ '\u00E0\u00F2\u00FC \u00EE\u00F2 1000$ \u00E2 ' + \
+ '\u00ED\u00E5\u00E4\u00E5\u00EB\u00FE!'
+ actual_subject = msg.subject
+ self.assertEqual(actual_subject, expected_subject)
+
+ expected_from = 'test test '
+ actual_from = msg.from_header
+ self.assertEqual(expected_from, actual_from)
+
+ def test_message_reply(self):
+ email_object = EmailMessage(
+ 'Test subject', # subject
+ 'Test body', # body
+ 'username@example.com', # from
+ ['mr.test32@mail.ru'], # to
+ )
+ msg = self.mailbox.record_outgoing_message(email_object.message())
+
+ self.assertTrue(msg.outgoing)
+
+ actual_from = 'username@example.com'
+ reply_email_object = EmailMessage(
+ 'Test subject', # subject
+ 'Test body', # body
+ actual_from, # from
+ ['mr.test32@mail.ru'], # to
+ )
+
+ with mock.patch.object(reply_email_object, 'send'):
+ reply_msg = msg.reply(reply_email_object)
+
+ self.assertEqual(reply_msg.in_reply_to, msg)
+
+ self.assertEqual(actual_from, msg.from_header)
+
+ reply_email_object.from_email = None
+
+ with mock.patch.object(reply_email_object, 'send'):
+ second_reply_msg = msg.reply(reply_email_object)
+
+ self.assertEqual(self.mailbox.from_email, second_reply_msg.from_header)
+
+ def test_message_with_text_attachment(self):
+ email_object = self._get_email_object(
+ 'message_with_text_attachment.eml',
+ )
+
+ msg = self.mailbox.process_incoming_message(email_object)
+
+ self.assertEqual(msg.attachments.all().count(), 1)
+ self.assertEqual('Has an attached text document, too!', msg.text)
+
+ 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()))
+
+ msg = self.mailbox.process_incoming_message(email_object)
+
+ self.assertEqual(size,
+ len(force_text(msg.get_email_object().as_string())))
+
+ def test_message_saved(self):
+ message = self._get_email_object('generic_message.eml')
+
+ default_settings = utils.get_settings()
+
+ with mock.patch('django_mailbox.utils.get_settings') as get_settings:
+ altered = copy.deepcopy(default_settings)
+ altered['store_original_message'] = True
+ get_settings.return_value = altered
+
+ msg = self.mailbox.process_incoming_message(message)
+
+ self.assertNotEquals(msg.eml, None)
+
+ self.assertTrue(msg.eml.name.endswith('.eml'))
+
+ with open(msg.eml.name, 'rb') as f:
+ self.assertEqual(
+ f.read(),
+ self._get_email_as_text('generic_message.eml')
+ )
+
+ def test_message_saving_ignored(self):
+ message = self._get_email_object('generic_message.eml')
+
+ default_settings = utils.get_settings()
+
+ with mock.patch('django_mailbox.utils.get_settings') as get_settings:
+ altered = copy.deepcopy(default_settings)
+ altered['store_original_message'] = False
+ get_settings.return_value = altered
+
+ msg = self.mailbox.process_incoming_message(message)
+
+ self.assertEquals(msg.eml, None)
+
+ def test_message_compressed(self):
+ message = self._get_email_object('generic_message.eml')
+
+ default_settings = utils.get_settings()
+
+ with mock.patch('django_mailbox.utils.get_settings') as get_settings:
+ altered = copy.deepcopy(default_settings)
+ altered['compress_original_message'] = True
+ altered['store_original_message'] = True
+ get_settings.return_value = altered
+
+ msg = self.mailbox.process_incoming_message(message)
+
+ actual_email_object = msg.get_email_object()
+
+ self.assertTrue(msg.eml.name.endswith('.eml.gz'))
+
+ with gzip.open(msg.eml.name, 'rb') as f:
+ self.assertEqual(f.read(),
+ self._get_email_as_text('generic_message.eml'))
diff --git a/build/lib/django_mailbox/tests/test_processincomingmessage.py b/build/lib/django_mailbox/tests/test_processincomingmessage.py
new file mode 100644
index 00000000..8ba2fb29
--- /dev/null
+++ b/build/lib/django_mailbox/tests/test_processincomingmessage.py
@@ -0,0 +1,65 @@
+from distutils.version import LooseVersion
+from unittest import mock
+
+from django.core.management import call_command, CommandError
+from django.test import TestCase
+import django
+
+class CommandsTestCase(TestCase):
+ def test_processincomingmessage_no_args(self):
+ """Check that processincomingmessage works with no args"""
+
+ mailbox_name = None
+ # Mock handle so that the test doesn't hang waiting for input. Note that we are only testing
+ # the argument parsing here -- functionality should be tested elsewhere
+ with mock.patch('django_mailbox.management.commands.processincomingmessage.Command.handle') as handle:
+ # Don't care about the return value
+ handle.return_value = None
+
+ call_command('processincomingmessage')
+ args, kwargs = handle.call_args
+
+ # Make sure that we called with the right arguments
+ try:
+ self.assertEqual(kwargs['mailbox_name'], mailbox_name)
+ except KeyError:
+ # Handle Django 1.7
+ # It uses optparse instead of argparse, so instead of being
+ # set to None, mailbox_name is simply left out altogether
+ # Thus we expect an empty tuple here
+ self.assertEqual(args, tuple())
+
+
+ def test_processincomingmessage_with_arg(self):
+ """Check that processincomingmessage works with mailbox_name given"""
+
+ mailbox_name = 'foo_mailbox'
+
+ with mock.patch('django_mailbox.management.commands.processincomingmessage.Command.handle') as handle:
+ handle.return_value = None
+
+ call_command('processincomingmessage', mailbox_name)
+ args, kwargs = handle.call_args
+ try:
+ self.assertEqual(kwargs['mailbox_name'], mailbox_name)
+ except (AssertionError, KeyError):
+ # Handle Django 1.7
+ # It uses optparse instead of argparse, so instead of being
+ # in kwargs, mailbox_name is in args
+ self.assertEqual(args[0], mailbox_name)
+
+ def test_processincomingmessage_too_many_args(self):
+ """Check that processincomingmessage raises an error if too many args"""
+ # Only perform this test for Django versions greater than 1.7.*. This
+ # is because, with optparse, too many arguments doesn't result in an
+ # error, which means this test is worthless anyway
+ # For the "compatibility" versions, unexpected arguments aren't handled
+ # very well, and result in a TypeError
+ if (LooseVersion(django.get_version()) >= LooseVersion('1.8') and
+ LooseVersion(django.get_version()) < LooseVersion('1.10')):
+ with self.assertRaises(TypeError):
+ call_command('processincomingmessage', 'foo_mailbox', 'invalid_arg')
+ # In 1.10 and later a proper CommandError should be raised
+ elif LooseVersion(django.get_version()) >= LooseVersion('1.10'):
+ with self.assertRaises(CommandError):
+ call_command('processincomingmessage', 'foo_mailbox', 'invalid_arg')
diff --git a/build/lib/django_mailbox/tests/test_transports.py b/build/lib/django_mailbox/tests/test_transports.py
new file mode 100644
index 00000000..403a3069
--- /dev/null
+++ b/build/lib/django_mailbox/tests/test_transports.py
@@ -0,0 +1,167 @@
+from unittest import mock
+
+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
+
+FAKE_UID_SEARCH_ANSWER = (
+ 'OK',
+ [
+ b'18 19 20 21 22 23 24 25 26 27 28 29 ' +
+ b'30 31 32 33 34 35 36 37 38 39 40 41 42 43 44'
+ ]
+)
+FAKE_UID_FETCH_SIZES = (
+ 'OK',
+ [
+ b'1 (UID 18 RFC822.SIZE 58070000000)',
+ b'2 (UID 19 RFC822.SIZE 2593)'
+ ]
+)
+FAKE_UID_FETCH_MSG = (
+ 'OK',
+ [
+ (
+ b'1 (UID 18 RFC822 {5807}',
+ get_email_as_text('generic_message.eml')
+ ),
+ ]
+)
+FAKE_UID_COPY_MSG = (
+ 'OK',
+ [
+ b'[COPYUID 1 2 2] (Success)'
+ ]
+)
+FAKE_LIST_ARCHIVE_FOLDERS_ANSWERS = (
+ 'OK',
+ [
+ b'(\\HasNoChildren \\All) "/" "[Gmail]/All Mail"'
+ ]
+)
+
+
+class IMAPTestCase(EmailMessageTestCase):
+ def setUp(self):
+ def imap_server_uid_method(*args):
+ cmd = args[0]
+ arg2 = args[2]
+ if cmd == 'search':
+ return FAKE_UID_SEARCH_ANSWER
+ if cmd == 'copy':
+ return FAKE_UID_COPY_MSG
+ if cmd == 'fetch':
+ if arg2 == '(RFC822.SIZE)':
+ return FAKE_UID_FETCH_SIZES
+ if arg2 == '(RFC822)':
+ return FAKE_UID_FETCH_MSG
+
+ def imap_server_list_method(pattern=None):
+ return FAKE_LIST_ARCHIVE_FOLDERS_ANSWERS
+
+ self.imap_server = mock.Mock()
+ self.imap_server.uid = imap_server_uid_method
+ self.imap_server.list = imap_server_list_method
+ super().setUp()
+
+
+class TestImapTransport(IMAPTestCase):
+ def setUp(self):
+ super().setUp()
+ self.arbitrary_hostname = 'one.two.three'
+ self.arbitrary_port = 100
+ self.ssl = False
+ self.transport = ImapTransport(
+ self.arbitrary_hostname,
+ self.arbitrary_port,
+ self.ssl
+ )
+ self.transport.server = self.imap_server
+
+ def test_get_email_message(self):
+ actual_messages = list(self.transport.get_message())
+ self.assertEqual(len(actual_messages), 27)
+ actual_message = actual_messages[0]
+ expected_message = self._get_email_object('generic_message.eml')
+ self.assertEqual(expected_message, actual_message)
+
+
+class TestImapArchivedTransport(TestImapTransport):
+ def setUp(self):
+ super().setUp()
+ self.archive = 'Archive'
+ self.transport = ImapTransport(
+ self.arbitrary_hostname,
+ self.arbitrary_port,
+ self.ssl,
+ self.archive
+ )
+ self.transport.server = self.imap_server
+
+
+class TestMaxSizeImapTransport(TestImapTransport):
+
+ @override_settings(DJANGO_MAILBOX_MAX_MESSAGE_SIZE=5807)
+ def setUp(self):
+ super().setUp()
+
+ self.transport = ImapTransport(
+ self.arbitrary_hostname,
+ self.arbitrary_port,
+ self.ssl,
+ )
+ self.transport.server = self.imap_server
+
+ def test_size_limit(self):
+ all_message_ids = self.transport._get_all_message_ids()
+ small_message_ids = self.transport._get_small_message_ids(
+ all_message_ids,
+ )
+ self.assertEqual(len(small_message_ids), 1)
+
+ def test_get_email_message(self):
+ actual_messages = list(self.transport.get_message())
+ self.assertEqual(len(actual_messages), 1)
+ actual_message = actual_messages[0]
+ expected_message = self._get_email_object('generic_message.eml')
+ self.assertEqual(expected_message, actual_message)
+
+
+class TestPop3Transport(EmailMessageTestCase):
+ def setUp(self):
+ self.arbitrary_hostname = 'one.two.three'
+ self.arbitrary_port = 100
+ self.ssl = False
+ self.transport = Pop3Transport(
+ self.arbitrary_hostname,
+ self.arbitrary_port,
+ self.ssl
+ )
+ self.transport.server = None
+ super().setUp()
+
+ def test_get_email_message(self):
+ with mock.patch.object(self.transport, 'server') as server:
+ # Consider this value arbitrary, the second parameter
+ # should have one entry per message in the inbox
+ server.list.return_value = [None, ['some_msg']]
+ server.retr.return_value = [
+ '+OK message follows',
+ [
+ line.encode('ascii')
+ for line in self._get_email_as_text(
+ 'generic_message.eml'
+ ).decode('ascii').split('\n')
+ ],
+ 10018, # Some arbitrary size, ideally matching the above
+ ]
+
+ actual_messages = list(self.transport.get_message())
+
+ self.assertEqual(len(actual_messages), 1)
+
+ actual_message = actual_messages[0]
+ expected_message = self._get_email_object('generic_message.eml')
+
+ self.assertEqual(expected_message, actual_message)
diff --git a/build/lib/django_mailbox/transports/__init__.py b/build/lib/django_mailbox/transports/__init__.py
new file mode 100644
index 00000000..9f733e34
--- /dev/null
+++ b/build/lib/django_mailbox/transports/__init__.py
@@ -0,0 +1,11 @@
+# all imports below are only used by external modules
+# flake8: noqa
+from django_mailbox.transports.imap import ImapTransport
+from django_mailbox.transports.pop3 import Pop3Transport
+from django_mailbox.transports.maildir import MaildirTransport
+from django_mailbox.transports.mbox import MboxTransport
+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.office365 import Office365Transport
diff --git a/build/lib/django_mailbox/transports/babyl.py b/build/lib/django_mailbox/transports/babyl.py
new file mode 100644
index 00000000..eb888a6d
--- /dev/null
+++ b/build/lib/django_mailbox/transports/babyl.py
@@ -0,0 +1,6 @@
+from mailbox import Babyl
+from django_mailbox.transports.generic import GenericFileMailbox
+
+
+class BabylTransport(GenericFileMailbox):
+ _variant = Babyl
diff --git a/build/lib/django_mailbox/transports/base.py b/build/lib/django_mailbox/transports/base.py
new file mode 100644
index 00000000..a90af9f7
--- /dev/null
+++ b/build/lib/django_mailbox/transports/base.py
@@ -0,0 +1,11 @@
+import email
+
+# Do *not* remove this, we need to use this in subclasses of EmailTransport
+from email.errors import MessageParseError # noqa: F401
+
+
+class EmailTransport:
+ def get_email_from_bytes(self, contents):
+ message = email.message_from_bytes(contents)
+
+ return message
diff --git a/build/lib/django_mailbox/transports/generic.py b/build/lib/django_mailbox/transports/generic.py
new file mode 100644
index 00000000..5832fda2
--- /dev/null
+++ b/build/lib/django_mailbox/transports/generic.py
@@ -0,0 +1,26 @@
+import sys
+
+from .base import EmailTransport
+
+
+class GenericFileMailbox(EmailTransport):
+ _variant = None
+ _path = None
+
+ def __init__(self, path):
+ super().__init__()
+ self._path = path
+
+ def get_instance(self):
+ return self._variant(self._path)
+
+ def get_message(self, condition=None):
+ repository = self.get_instance()
+ repository.lock()
+ for key, message in repository.items():
+ if condition and not condition(message):
+ continue
+ repository.remove(key)
+ yield message
+ repository.flush()
+ repository.unlock()
diff --git a/build/lib/django_mailbox/transports/gmail.py b/build/lib/django_mailbox/transports/gmail.py
new file mode 100644
index 00000000..99e8189a
--- /dev/null
+++ b/build/lib/django_mailbox/transports/gmail.py
@@ -0,0 +1,57 @@
+import logging
+
+from django_mailbox.transports.imap import ImapTransport
+
+
+logger = logging.getLogger(__name__)
+
+
+class GmailImapTransport(ImapTransport):
+
+ def connect(self, username, password):
+ # Try to use oauth2 first. It's much safer
+ try:
+ self._connect_oauth(username)
+ except (TypeError, ValueError) as e:
+ logger.warning("Couldn't do oauth2 because %s" % e)
+ self.server = self.transport(self.hostname, self.port)
+ typ, msg = self.server.login(username, password)
+ self.server.select()
+
+ def _connect_oauth(self, username):
+ # username should be an email address that has already been authorized
+ # for gmail access
+ try:
+ from django_mailbox.google_utils import (
+ get_google_access_token,
+ fetch_user_info,
+ AccessTokenNotFound,
+ )
+ except ImportError:
+ raise ValueError(
+ "Install python-social-auth to use oauth2 auth for gmail"
+ )
+
+ access_token = None
+ while access_token is None:
+ try:
+ access_token = get_google_access_token(username)
+ google_email_address = fetch_user_info(username)['email']
+ except TypeError:
+ # This means that the google process took too long
+ # Trying again is the right thing to do
+ pass
+ except AccessTokenNotFound:
+ raise ValueError(
+ "No Token available in python-social-auth for %s" % (
+ username
+ )
+ )
+
+ auth_string = 'user={}\1auth=Bearer {}\1\1'.format(
+ google_email_address,
+ access_token
+ )
+ self.server = self.transport(self.hostname, self.port)
+ self.server.authenticate('XOAUTH2', lambda x: auth_string)
+ self.server.select()
diff --git a/build/lib/django_mailbox/transports/imap.py b/build/lib/django_mailbox/transports/imap.py
new file mode 100644
index 00000000..2599adad
--- /dev/null
+++ b/build/lib/django_mailbox/transports/imap.py
@@ -0,0 +1,137 @@
+import imaplib
+import logging
+
+from django.conf import settings
+
+from .base import EmailTransport, MessageParseError
+
+
+# By default, imaplib will raise an exception if it encounters more
+# than 10k bytes; sometimes users attempt to consume mailboxes that
+# have a more, and modern computers are skookum-enough to handle just
+# a *few* more messages without causing any sort of problem.
+imaplib._MAXLINE = 1000000
+
+
+logger = logging.getLogger(__name__)
+
+
+class ImapTransport(EmailTransport):
+ def __init__(
+ self, hostname, port=None, ssl=False, tls=False,
+ archive='', folder=None,
+ ):
+ self.max_message_size = getattr(
+ settings,
+ 'DJANGO_MAILBOX_MAX_MESSAGE_SIZE',
+ False
+ )
+ self.integration_testing_subject = getattr(
+ settings,
+ 'DJANGO_MAILBOX_INTEGRATION_TESTING_SUBJECT',
+ None
+ )
+ self.hostname = hostname
+ self.port = port
+ self.archive = archive
+ self.folder = folder
+ self.tls = tls
+ if ssl:
+ self.transport = imaplib.IMAP4_SSL
+ if not self.port:
+ self.port = 993
+ else:
+ self.transport = imaplib.IMAP4
+ if not self.port:
+ self.port = 143
+
+ def connect(self, username, password):
+ self.server = self.transport(self.hostname, self.port)
+ if self.tls:
+ self.server.starttls()
+ typ, msg = self.server.login(username, password)
+
+ if self.folder:
+ self.server.select(self.folder)
+ else:
+ self.server.select()
+
+ def _get_all_message_ids(self):
+ # Fetch all the message uids
+ response, message_ids = self.server.uid('search', None, 'ALL')
+ message_id_string = message_ids[0].strip()
+ # Usually `message_id_string` will be a list of space-separated
+ # ids; we must make sure that it isn't an empty string before
+ # splitting into individual UIDs.
+ if message_id_string:
+ return message_id_string.decode().split(' ')
+ return []
+
+ def _get_small_message_ids(self, message_ids):
+ # Using existing message uids, get the sizes and
+ # return only those that are under the size
+ # limit
+ safe_message_ids = []
+
+ status, data = self.server.uid(
+ 'fetch',
+ ','.join(message_ids),
+ '(RFC822.SIZE)'
+ )
+
+ for each_msg in data:
+ each_msg = each_msg.decode()
+ try:
+ uid = each_msg.split(' ')[2]
+ size = each_msg.split(' ')[4].rstrip(')')
+ if int(size) <= int(self.max_message_size):
+ safe_message_ids.append(uid)
+ except ValueError as e:
+ logger.warning(
+ "ValueError: {} working on {}".format(e, each_msg[0])
+ )
+ pass
+ return safe_message_ids
+
+ def get_message(self, condition=None):
+ message_ids = self._get_all_message_ids()
+
+ if not message_ids:
+ return
+
+ # Limit the uids to the small ones if we care about that
+ if self.max_message_size:
+ message_ids = self._get_small_message_ids(message_ids)
+
+ if self.archive:
+ typ, folders = self.server.list(pattern=self.archive)
+ if folders[0] is None:
+ # If the archive folder does not exist, create it
+ self.server.create(self.archive)
+
+ for uid in message_ids:
+ try:
+ typ, msg_contents = self.server.uid('fetch', uid, '(RFC822)')
+ if not msg_contents:
+ continue
+ try:
+ message = self.get_email_from_bytes(msg_contents[0][1])
+ except TypeError:
+ # This happens if another thread/process deletes the
+ # message between our generating the ID list and our
+ # processing it here.
+ continue
+
+ if condition and not condition(message):
+ continue
+
+ yield message
+ except MessageParseError:
+ continue
+
+ if self.archive:
+ self.server.uid('copy', uid, self.archive)
+
+ self.server.uid('store', uid, "+FLAGS", "(\\Deleted)")
+ self.server.expunge()
+ return
diff --git a/build/lib/django_mailbox/transports/maildir.py b/build/lib/django_mailbox/transports/maildir.py
new file mode 100644
index 00000000..2d4af53d
--- /dev/null
+++ b/build/lib/django_mailbox/transports/maildir.py
@@ -0,0 +1,9 @@
+from mailbox import Maildir
+from django_mailbox.transports.generic import GenericFileMailbox
+
+
+class MaildirTransport(GenericFileMailbox):
+ _variant = Maildir
+
+ def get_instance(self):
+ return self._variant(self._path, None)
diff --git a/build/lib/django_mailbox/transports/mbox.py b/build/lib/django_mailbox/transports/mbox.py
new file mode 100644
index 00000000..d610deb8
--- /dev/null
+++ b/build/lib/django_mailbox/transports/mbox.py
@@ -0,0 +1,6 @@
+from mailbox import mbox
+from django_mailbox.transports.generic import GenericFileMailbox
+
+
+class MboxTransport(GenericFileMailbox):
+ _variant = mbox
diff --git a/build/lib/django_mailbox/transports/mh.py b/build/lib/django_mailbox/transports/mh.py
new file mode 100644
index 00000000..33fdc254
--- /dev/null
+++ b/build/lib/django_mailbox/transports/mh.py
@@ -0,0 +1,6 @@
+from mailbox import MH
+from django_mailbox.transports.generic import GenericFileMailbox
+
+
+class MHTransport(GenericFileMailbox):
+ _variant = MH
diff --git a/build/lib/django_mailbox/transports/mmdf.py b/build/lib/django_mailbox/transports/mmdf.py
new file mode 100644
index 00000000..270e4f34
--- /dev/null
+++ b/build/lib/django_mailbox/transports/mmdf.py
@@ -0,0 +1,6 @@
+from mailbox import MMDF
+from django_mailbox.transports.generic import GenericFileMailbox
+
+
+class MMDFTransport(GenericFileMailbox):
+ _variant = MMDF
diff --git a/build/lib/django_mailbox/transports/office365.py b/build/lib/django_mailbox/transports/office365.py
new file mode 100644
index 00000000..f98f8c14
--- /dev/null
+++ b/build/lib/django_mailbox/transports/office365.py
@@ -0,0 +1,131 @@
+import O365
+import logging
+
+from django.conf import settings
+
+from .base import EmailTransport, MessageParseError
+
+logger = logging.getLogger(__name__)
+
+
+class Office365Transport(EmailTransport):
+ def __init__(
+ self, hostname, port=None, ssl=False, tls=False,
+ archive='', folder=None, client_id=None, client_secret=None, tenant_id=None
+ ):
+ self.max_message_size = getattr(
+ settings,
+ 'DJANGO_MAILBOX_MAX_MESSAGE_SIZE',
+ False
+ )
+ self.integration_testing_subject = getattr(
+ settings,
+ 'DJANGO_MAILBOX_INTEGRATION_TESTING_SUBJECT',
+ None
+ )
+ self.hostname = hostname
+ self.port = port
+ self.archive = archive
+ self.folder = folder
+ self.tls = tls
+ if ssl:
+ #self.transport = imaplib.IMAP4_SSL
+ if not self.port:
+ self.port = 993
+ else:
+ #self.transport = imaplib.IMAP4
+ if not self.port:
+ self.port = 143
+ self.client_id = client_id
+ self.client_secret = client_secret
+ self.tenant_id = tenant_id
+
+ def connect(self, username, password):
+ credentials = (self.client_id, self.client_secret)
+
+ # the default protocol will be Microsoft Graph
+ # the default authentication method will be "on behalf of a user"
+
+ self.server = O365.Account(credentials, auth_flow_type='credentials', tenant_id=self.tenant_id)
+ if self.server.authenticate(scopes=['mailbox']):
+ print('Authenticated!')
+
+ def _get_all_message_ids(self):
+ # # Fetch all the message uids
+ # response, message_ids = self.server.uid('search', None, 'ALL')
+ # message_id_string = message_ids[0].strip()
+ # # Usually `message_id_string` will be a list of space-separated
+ # # ids; we must make sure that it isn't an empty string before
+ # # splitting into individual UIDs.
+ # if message_id_string:
+ # return message_id_string.decode().split(' ')
+ # return []
+
+ def _get_small_message_ids(self, message_ids):
+ # Using existing message uids, get the sizes and
+ # return only those that are under the size
+ # limit
+ safe_message_ids = []
+
+ status, data = self.server.uid(
+ 'fetch',
+ ','.join(message_ids),
+ '(RFC822.SIZE)'
+ )
+
+ for each_msg in data:
+ each_msg = each_msg.decode()
+ try:
+ uid = each_msg.split(' ')[2]
+ size = each_msg.split(' ')[4].rstrip(')')
+ if int(size) <= int(self.max_message_size):
+ safe_message_ids.append(uid)
+ except ValueError as e:
+ logger.warning(
+ "ValueError: {} working on {}".format(e, each_msg[0])
+ )
+ pass
+ return safe_message_ids
+
+ def get_message(self, condition=None):
+ message_ids = self._get_all_message_ids()
+
+ if not message_ids:
+ return
+
+ # Limit the uids to the small ones if we care about that
+ if self.max_message_size:
+ message_ids = self._get_small_message_ids(message_ids)
+
+ if self.archive:
+ typ, folders = self.server.list(pattern=self.archive)
+ if folders[0] is None:
+ # If the archive folder does not exist, create it
+ self.server.create(self.archive)
+
+ for uid in message_ids:
+ try:
+ typ, msg_contents = self.server.uid('fetch', uid, '(RFC822)')
+ if not msg_contents:
+ continue
+ try:
+ message = self.get_email_from_bytes(msg_contents[0][1])
+ except TypeError:
+ # This happens if another thread/process deletes the
+ # message between our generating the ID list and our
+ # processing it here.
+ continue
+
+ if condition and not condition(message):
+ continue
+
+ yield message
+ except MessageParseError:
+ continue
+
+ if self.archive:
+ self.server.uid('copy', uid, self.archive)
+
+ self.server.uid('store', uid, "+FLAGS", "(\\Deleted)")
+ self.server.expunge()
+ return
diff --git a/build/lib/django_mailbox/transports/pop3.py b/build/lib/django_mailbox/transports/pop3.py
new file mode 100644
index 00000000..6dbe9953
--- /dev/null
+++ b/build/lib/django_mailbox/transports/pop3.py
@@ -0,0 +1,44 @@
+from poplib import POP3, POP3_SSL
+
+from .base import EmailTransport, MessageParseError
+
+
+class Pop3Transport(EmailTransport):
+ def __init__(self, hostname, port=None, ssl=False):
+ self.hostname = hostname
+ self.port = port
+ if ssl:
+ self.transport = POP3_SSL
+ if not self.port:
+ self.port = 995
+ else:
+ self.transport = POP3
+ if not self.port:
+ self.port = 110
+
+ def connect(self, username, password):
+ self.server = self.transport(self.hostname, self.port)
+ self.server.user(username)
+ self.server.pass_(password)
+
+ def get_message_body(self, message_lines):
+ return bytes('\r\n', 'ascii').join(message_lines)
+
+ def get_message(self, condition=None):
+ message_count = len(self.server.list()[1])
+ for i in range(message_count):
+ try:
+ msg_contents = self.get_message_body(
+ self.server.retr(i + 1)[1]
+ )
+ message = self.get_email_from_bytes(msg_contents)
+
+ if condition and not condition(message):
+ continue
+
+ yield message
+ except MessageParseError:
+ continue
+ self.server.dele(i + 1)
+ self.server.quit()
+ return
diff --git a/build/lib/django_mailbox/utils.py b/build/lib/django_mailbox/utils.py
new file mode 100644
index 00000000..6603612f
--- /dev/null
+++ b/build/lib/django_mailbox/utils.py
@@ -0,0 +1,151 @@
+import datetime
+import email.header
+import logging
+import os
+
+from django.conf import settings
+
+
+logger = logging.getLogger(__name__)
+
+
+def get_settings():
+ return {
+ 'strip_unallowed_mimetypes': getattr(
+ settings,
+ 'DJANGO_MAILBOX_STRIP_UNALLOWED_MIMETYPES',
+ False
+ ),
+ 'allowed_mimetypes': getattr(
+ settings,
+ 'DJANGO_MAILBOX_ALLOWED_MIMETYPES',
+ [
+ 'text/plain',
+ 'text/html'
+ ]
+ ),
+ 'text_stored_mimetypes': getattr(
+ settings,
+ 'DJANGO_MAILBOX_TEXT_STORED_MIMETYPES',
+ [
+ 'text/plain',
+ 'text/html'
+ ]
+ ),
+ 'altered_message_header': getattr(
+ settings,
+ 'DJANGO_MAILBOX_ALTERED_MESSAGE_HEADER',
+ 'X-Django-Mailbox-Altered-Message'
+ ),
+ 'attachment_interpolation_header': getattr(
+ settings,
+ 'DJANGO_MAILBOX_ATTACHMENT_INTERPOLATION_HEADER',
+ 'X-Django-Mailbox-Interpolate-Attachment'
+ ),
+ 'attachment_upload_to': getattr(
+ settings,
+ 'DJANGO_MAILBOX_ATTACHMENT_UPLOAD_TO',
+ 'mailbox_attachments/%Y/%m/%d/'
+ ),
+ 'store_original_message': getattr(
+ settings,
+ 'DJANGO_MAILBOX_STORE_ORIGINAL_MESSAGE',
+ False
+ ),
+ 'compress_original_message': getattr(
+ settings,
+ 'DJANGO_MAILBOX_COMPRESS_ORIGINAL_MESSAGE',
+ False
+ ),
+ 'original_message_compression': getattr(
+ settings,
+ 'DJANGO_MAILBOX_ORIGINAL_MESSAGE_COMPRESSION',
+ 6
+ ),
+ 'default_charset': getattr(
+ settings,
+ 'DJANGO_MAILBOX_default_charset',
+ 'iso8859-1',
+ )
+ }
+
+
+def convert_header_to_unicode(header):
+ default_charset = get_settings()['default_charset']
+
+ def _decode(value, encoding):
+ if isinstance(value, str):
+ return value
+ if not encoding or encoding == 'unknown-8bit':
+ encoding = default_charset
+ return value.decode(encoding, 'replace')
+
+ try:
+ return ''.join(
+ [
+ (
+ _decode(bytestr, encoding)
+ ) for bytestr, encoding in email.header.decode_header(header)
+ ]
+ )
+ except UnicodeDecodeError:
+ logger.exception(
+ 'Errors encountered decoding header %s into encoding %s.',
+ header,
+ default_charset,
+ )
+ return header.decode(default_charset, 'replace')
+
+
+def get_body_from_message(message, maintype, subtype):
+ """
+ Fetchs the body message matching main/sub content type.
+ """
+ body = ''
+ for part in message.walk():
+ if part.get('content-disposition', '').startswith('attachment;'):
+ continue
+ if part.get_content_maintype() == maintype and \
+ part.get_content_subtype() == subtype:
+ charset = part.get_content_charset()
+ this_part = part.get_payload(decode=True)
+ if charset:
+ try:
+ this_part = this_part.decode(charset, 'replace')
+ except LookupError:
+ this_part = this_part.decode('ascii', 'replace')
+ logger.warning(
+ 'Unknown encoding %s encountered while decoding '
+ 'text payload. Interpreting as ASCII with '
+ 'replacement, but some data may not be '
+ 'represented as the sender intended.',
+ charset
+ )
+ except ValueError:
+ this_part = this_part.decode('ascii', 'replace')
+ logger.warning(
+ 'Error encountered while decoding text '
+ 'payload from an incorrectly-constructed '
+ 'e-mail; payload was converted to ASCII with '
+ 'replacement, but some data may not be '
+ 'represented as the sender intended.'
+ )
+ else:
+ this_part = this_part.decode('ascii', 'replace')
+
+ body += this_part
+
+ return body
+
+
+def get_attachment_save_path(instance, filename):
+ settings = get_settings()
+
+ path = settings['attachment_upload_to']
+ if '%' in path:
+ path = datetime.datetime.utcnow().strftime(path)
+
+ return os.path.join(
+ path,
+ filename,
+ )
From 912f091d3f840508e530ae623dc3f2476b1315a9 Mon Sep 17 00:00:00 2001
From: Pietro Mingo
Date: Wed, 27 Jul 2022 10:58:48 +0200
Subject: [PATCH 06/18] Update models.py
---
django_mailbox/models.py | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/django_mailbox/models.py b/django_mailbox/models.py
index 52e4c1e7..28c2bb54 100644
--- a/django_mailbox/models.py
+++ b/django_mailbox/models.py
@@ -249,12 +249,13 @@ def get_connection(self):
conn.connect(self.username, self.password)
elif self.type == 'office365':
conn = Office365Transport(
+ self.location,
port=self.port if self.port else None,
ssl=True,
- archive=self.archive,
client_id=self.client_id,
client_secret=self.client_secret,
- tenant_id=self.tenant_id
+ tenant_id=self.tenant_id,
+ folder=self.folder
)
conn.connect(self.username, self.password)
elif self.type == 'maildir':
From dca20e1e1c2fd91a47e562ebb021d9784b0ec255 Mon Sep 17 00:00:00 2001
From: Serafim Bordei
Date: Wed, 27 Jul 2022 11:49:48 +0200
Subject: [PATCH 07/18] Update office365.py and models.py
---
build/lib/django_mailbox/transports/office365.py | 5 ++---
django_mailbox/models.py | 2 +-
2 files changed, 3 insertions(+), 4 deletions(-)
diff --git a/build/lib/django_mailbox/transports/office365.py b/build/lib/django_mailbox/transports/office365.py
index f98f8c14..674e910d 100644
--- a/build/lib/django_mailbox/transports/office365.py
+++ b/build/lib/django_mailbox/transports/office365.py
@@ -10,7 +10,7 @@
class Office365Transport(EmailTransport):
def __init__(
- self, hostname, port=None, ssl=False, tls=False,
+ self, port=None, ssl=False, tls=False,
archive='', folder=None, client_id=None, client_secret=None, tenant_id=None
):
self.max_message_size = getattr(
@@ -23,7 +23,6 @@ def __init__(
'DJANGO_MAILBOX_INTEGRATION_TESTING_SUBJECT',
None
)
- self.hostname = hostname
self.port = port
self.archive = archive
self.folder = folder
@@ -59,7 +58,7 @@ def _get_all_message_ids(self):
# # splitting into individual UIDs.
# if message_id_string:
# return message_id_string.decode().split(' ')
- # return []
+ return []
def _get_small_message_ids(self, message_ids):
# Using existing message uids, get the sizes and
diff --git a/django_mailbox/models.py b/django_mailbox/models.py
index 28c2bb54..1606f8b8 100644
--- a/django_mailbox/models.py
+++ b/django_mailbox/models.py
@@ -208,7 +208,7 @@ def client_secret(self):
@property
def tenant_id(self):
"""Returns (if specified) the tenant id for Office365."""
- tenant_id = self._query_string.get('tentant_id', None)
+ tenant_id = self._query_string.get('tenant_id', None)
if not tenant_id:
return None
return tenant_id[0]
From ced6b0997b4cdab60daded44e4b560a510fe391d Mon Sep 17 00:00:00 2001
From: Pietro Mingo
Date: Fri, 29 Jul 2022 16:01:30 +0200
Subject: [PATCH 08/18] office365 implementation
---
django_mailbox/models.py | 6 +-
django_mailbox/transports/base.py | 5 ++
django_mailbox/transports/office365.py | 111 +++++++------------------
3 files changed, 38 insertions(+), 84 deletions(-)
diff --git a/django_mailbox/models.py b/django_mailbox/models.py
index 1606f8b8..b9f84cc5 100644
--- a/django_mailbox/models.py
+++ b/django_mailbox/models.py
@@ -250,14 +250,12 @@ def get_connection(self):
elif self.type == 'office365':
conn = Office365Transport(
self.location,
+ self.username,
port=self.port if self.port else None,
ssl=True,
- client_id=self.client_id,
- client_secret=self.client_secret,
- tenant_id=self.tenant_id,
folder=self.folder
)
- conn.connect(self.username, self.password)
+ conn.connect(self.client_id, self.client_secret, self.tenant_id)
elif self.type == 'maildir':
conn = MaildirTransport(self.location)
elif self.type == 'mbox':
diff --git a/django_mailbox/transports/base.py b/django_mailbox/transports/base.py
index a90af9f7..9d507a23 100644
--- a/django_mailbox/transports/base.py
+++ b/django_mailbox/transports/base.py
@@ -9,3 +9,8 @@ def get_email_from_bytes(self, contents):
message = email.message_from_bytes(contents)
return message
+
+ def get_email_from_string(self, contents):
+ message = email.message_from_string(contents)
+
+ return message
diff --git a/django_mailbox/transports/office365.py b/django_mailbox/transports/office365.py
index f98f8c14..bc8a2c68 100644
--- a/django_mailbox/transports/office365.py
+++ b/django_mailbox/transports/office365.py
@@ -10,8 +10,7 @@
class Office365Transport(EmailTransport):
def __init__(
- self, hostname, port=None, ssl=False, tls=False,
- archive='', folder=None, client_id=None, client_secret=None, tenant_id=None
+ self, hostname, username, port=None, ssl=False, tls=False, folder=None
):
self.max_message_size = getattr(
settings,
@@ -24,8 +23,8 @@ def __init__(
None
)
self.hostname = hostname
+ self.username = username
self.port = port
- self.archive = archive
self.folder = folder
self.tls = tls
if ssl:
@@ -36,85 +35,29 @@ def __init__(
#self.transport = imaplib.IMAP4
if not self.port:
self.port = 143
- self.client_id = client_id
- self.client_secret = client_secret
- self.tenant_id = tenant_id
-
- def connect(self, username, password):
- credentials = (self.client_id, self.client_secret)
-
- # the default protocol will be Microsoft Graph
- # the default authentication method will be "on behalf of a user"
-
- self.server = O365.Account(credentials, auth_flow_type='credentials', tenant_id=self.tenant_id)
- if self.server.authenticate(scopes=['mailbox']):
- print('Authenticated!')
-
- def _get_all_message_ids(self):
- # # Fetch all the message uids
- # response, message_ids = self.server.uid('search', None, 'ALL')
- # message_id_string = message_ids[0].strip()
- # # Usually `message_id_string` will be a list of space-separated
- # # ids; we must make sure that it isn't an empty string before
- # # splitting into individual UIDs.
- # if message_id_string:
- # return message_id_string.decode().split(' ')
- # return []
-
- def _get_small_message_ids(self, message_ids):
- # Using existing message uids, get the sizes and
- # return only those that are under the size
- # limit
- safe_message_ids = []
-
- status, data = self.server.uid(
- 'fetch',
- ','.join(message_ids),
- '(RFC822.SIZE)'
- )
- for each_msg in data:
- each_msg = each_msg.decode()
- try:
- uid = each_msg.split(' ')[2]
- size = each_msg.split(' ')[4].rstrip(')')
- if int(size) <= int(self.max_message_size):
- safe_message_ids.append(uid)
- except ValueError as e:
- logger.warning(
- "ValueError: {} working on {}".format(e, each_msg[0])
- )
- pass
- return safe_message_ids
-
- def get_message(self, condition=None):
- message_ids = self._get_all_message_ids()
+ def connect(self, client_id, client_secret, tenant_id):
+ credentials = (client_id, client_secret)
- if not message_ids:
- return
+ self.account = O365.Account(credentials, auth_flow_type='credentials', tenant_id=tenant_id)
+ self.account.authenticate()
- # Limit the uids to the small ones if we care about that
- if self.max_message_size:
- message_ids = self._get_small_message_ids(message_ids)
+ 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)
- if self.archive:
- typ, folders = self.server.list(pattern=self.archive)
- if folders[0] is None:
- # If the archive folder does not exist, create it
- self.server.create(self.archive)
+ def get_message_body(self, message_lines):
+ return bytes('\r\n', 'ascii').join(message_lines)
- for uid in message_ids:
+ def get_message(self, condition=None):
+ message_count = len(self.server.list()[1])
+ for i in range(message_count):
try:
- typ, msg_contents = self.server.uid('fetch', uid, '(RFC822)')
- if not msg_contents:
- continue
- try:
- message = self.get_email_from_bytes(msg_contents[0][1])
- except TypeError:
- # This happens if another thread/process deletes the
- # message between our generating the ID list and our
- # processing it here.
- continue
+ msg_contents = self.get_message_body(
+ self.server.retr(i + 1)[1]
+ )
+ message = self.get_email_from_bytes(msg_contents)
if condition and not condition(message):
continue
@@ -122,10 +65,18 @@ def get_message(self, condition=None):
yield message
except MessageParseError:
continue
+ self.server.dele(i + 1)
- if self.archive:
- self.server.uid('copy', uid, self.archive)
+ for message in self.mailbox.get_messages(order_by='receivedDateTime'):
+ try:
+ mime_content = message.get_mime_content()
+ message = self.get_email_from_string(mime_content)
+
+ if condition and not condition(message):
+ continue
- self.server.uid('store', uid, "+FLAGS", "(\\Deleted)")
- self.server.expunge()
+ yield message
+ except MessageParseError:
+ continue
return
+
From b02fd73d1c4577e4cb66fe9227080d7fd15bd707 Mon Sep 17 00:00:00 2001
From: Pietro Mingo
Date: Fri, 29 Jul 2022 16:06:30 +0200
Subject: [PATCH 09/18] Update office365.py
---
django_mailbox/transports/office365.py | 19 -------------------
1 file changed, 19 deletions(-)
diff --git a/django_mailbox/transports/office365.py b/django_mailbox/transports/office365.py
index bc8a2c68..e7f4fab3 100644
--- a/django_mailbox/transports/office365.py
+++ b/django_mailbox/transports/office365.py
@@ -47,26 +47,7 @@ def connect(self, client_id, client_secret, tenant_id):
if self.folder:
self.mailbox_folder = self.mailbox.get_folder(folder_name=self.folder)
- def get_message_body(self, message_lines):
- return bytes('\r\n', 'ascii').join(message_lines)
-
def get_message(self, condition=None):
- message_count = len(self.server.list()[1])
- for i in range(message_count):
- try:
- msg_contents = self.get_message_body(
- self.server.retr(i + 1)[1]
- )
- message = self.get_email_from_bytes(msg_contents)
-
- if condition and not condition(message):
- continue
-
- yield message
- except MessageParseError:
- continue
- self.server.dele(i + 1)
-
for message in self.mailbox.get_messages(order_by='receivedDateTime'):
try:
mime_content = message.get_mime_content()
From 8f944190adbdd4f5ded2e048cb19f25364b6b42c Mon Sep 17 00:00:00 2001
From: Pietro Mingo
Date: Fri, 29 Jul 2022 16:20:38 +0200
Subject: [PATCH 10/18] clear code
---
build/lib/django_mailbox/models.py | 11 +--
build/lib/django_mailbox/transports/base.py | 5 +
.../django_mailbox/transports/office365.py | 97 +++----------------
django_mailbox/transports/base.py | 4 -
django_mailbox/transports/office365.py | 2 +-
5 files changed, 26 insertions(+), 93 deletions(-)
diff --git a/build/lib/django_mailbox/models.py b/build/lib/django_mailbox/models.py
index 52e4c1e7..b9f84cc5 100644
--- a/build/lib/django_mailbox/models.py
+++ b/build/lib/django_mailbox/models.py
@@ -208,7 +208,7 @@ def client_secret(self):
@property
def tenant_id(self):
"""Returns (if specified) the tenant id for Office365."""
- tenant_id = self._query_string.get('tentant_id', None)
+ tenant_id = self._query_string.get('tenant_id', None)
if not tenant_id:
return None
return tenant_id[0]
@@ -249,14 +249,13 @@ def get_connection(self):
conn.connect(self.username, self.password)
elif self.type == 'office365':
conn = Office365Transport(
+ self.location,
+ self.username,
port=self.port if self.port else None,
ssl=True,
- archive=self.archive,
- client_id=self.client_id,
- client_secret=self.client_secret,
- tenant_id=self.tenant_id
+ folder=self.folder
)
- conn.connect(self.username, self.password)
+ conn.connect(self.client_id, self.client_secret, self.tenant_id)
elif self.type == 'maildir':
conn = MaildirTransport(self.location)
elif self.type == 'mbox':
diff --git a/build/lib/django_mailbox/transports/base.py b/build/lib/django_mailbox/transports/base.py
index a90af9f7..9d507a23 100644
--- a/build/lib/django_mailbox/transports/base.py
+++ b/build/lib/django_mailbox/transports/base.py
@@ -9,3 +9,8 @@ def get_email_from_bytes(self, contents):
message = email.message_from_bytes(contents)
return message
+
+ def get_email_from_string(self, contents):
+ message = email.message_from_string(contents)
+
+ return message
diff --git a/build/lib/django_mailbox/transports/office365.py b/build/lib/django_mailbox/transports/office365.py
index 674e910d..e60531b7 100644
--- a/build/lib/django_mailbox/transports/office365.py
+++ b/build/lib/django_mailbox/transports/office365.py
@@ -10,8 +10,7 @@
class Office365Transport(EmailTransport):
def __init__(
- self, port=None, ssl=False, tls=False,
- archive='', folder=None, client_id=None, client_secret=None, tenant_id=None
+ self, hostname, username, port=None, ssl=False, tls=False, folder=None
):
self.max_message_size = getattr(
settings,
@@ -23,8 +22,9 @@ def __init__(
'DJANGO_MAILBOX_INTEGRATION_TESTING_SUBJECT',
None
)
+ self.hostname = hostname
+ self.username = username
self.port = port
- self.archive = archive
self.folder = folder
self.tls = tls
if ssl:
@@ -35,85 +35,23 @@ def __init__(
#self.transport = imaplib.IMAP4
if not self.port:
self.port = 143
- self.client_id = client_id
- self.client_secret = client_secret
- self.tenant_id = tenant_id
- def connect(self, username, password):
- credentials = (self.client_id, self.client_secret)
+ def connect(self, client_id, client_secret, tenant_id):
+ credentials = (client_id, client_secret)
- # the default protocol will be Microsoft Graph
- # the default authentication method will be "on behalf of a user"
+ self.account = O365.Account(credentials, auth_flow_type='credentials', tenant_id=tenant_id)
+ self.account.authenticate()
- self.server = O365.Account(credentials, auth_flow_type='credentials', tenant_id=self.tenant_id)
- if self.server.authenticate(scopes=['mailbox']):
- print('Authenticated!')
-
- def _get_all_message_ids(self):
- # # Fetch all the message uids
- # response, message_ids = self.server.uid('search', None, 'ALL')
- # message_id_string = message_ids[0].strip()
- # # Usually `message_id_string` will be a list of space-separated
- # # ids; we must make sure that it isn't an empty string before
- # # splitting into individual UIDs.
- # if message_id_string:
- # return message_id_string.decode().split(' ')
- return []
-
- def _get_small_message_ids(self, message_ids):
- # Using existing message uids, get the sizes and
- # return only those that are under the size
- # limit
- safe_message_ids = []
-
- status, data = self.server.uid(
- 'fetch',
- ','.join(message_ids),
- '(RFC822.SIZE)'
- )
-
- for each_msg in data:
- each_msg = each_msg.decode()
- try:
- uid = each_msg.split(' ')[2]
- size = each_msg.split(' ')[4].rstrip(')')
- if int(size) <= int(self.max_message_size):
- safe_message_ids.append(uid)
- except ValueError as e:
- logger.warning(
- "ValueError: {} working on {}".format(e, each_msg[0])
- )
- pass
- return safe_message_ids
+ 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):
- message_ids = self._get_all_message_ids()
-
- if not message_ids:
- return
-
- # Limit the uids to the small ones if we care about that
- if self.max_message_size:
- message_ids = self._get_small_message_ids(message_ids)
-
- if self.archive:
- typ, folders = self.server.list(pattern=self.archive)
- if folders[0] is None:
- # If the archive folder does not exist, create it
- self.server.create(self.archive)
-
- for uid in message_ids:
+ for message in self.mailbox.get_messages(order_by='receivedDateTime'):
try:
- typ, msg_contents = self.server.uid('fetch', uid, '(RFC822)')
- if not msg_contents:
- continue
- try:
- message = self.get_email_from_bytes(msg_contents[0][1])
- except TypeError:
- # This happens if another thread/process deletes the
- # message between our generating the ID list and our
- # processing it here.
- continue
+ mime_content = message.get_mime_content()
+ message = self.get_email_from_bytes(mime_content)
if condition and not condition(message):
continue
@@ -121,10 +59,5 @@ def get_message(self, condition=None):
yield message
except MessageParseError:
continue
-
- if self.archive:
- self.server.uid('copy', uid, self.archive)
-
- self.server.uid('store', uid, "+FLAGS", "(\\Deleted)")
- self.server.expunge()
return
+
diff --git a/django_mailbox/transports/base.py b/django_mailbox/transports/base.py
index 9d507a23..a3a2bc01 100644
--- a/django_mailbox/transports/base.py
+++ b/django_mailbox/transports/base.py
@@ -10,7 +10,3 @@ def get_email_from_bytes(self, contents):
return message
- def get_email_from_string(self, contents):
- message = email.message_from_string(contents)
-
- return message
diff --git a/django_mailbox/transports/office365.py b/django_mailbox/transports/office365.py
index e7f4fab3..e60531b7 100644
--- a/django_mailbox/transports/office365.py
+++ b/django_mailbox/transports/office365.py
@@ -51,7 +51,7 @@ def get_message(self, condition=None):
for message in self.mailbox.get_messages(order_by='receivedDateTime'):
try:
mime_content = message.get_mime_content()
- message = self.get_email_from_string(mime_content)
+ message = self.get_email_from_bytes(mime_content)
if condition and not condition(message):
continue
From 9bfc3e5df115d6d154568af327b898a4888d2839 Mon Sep 17 00:00:00 2001
From: Pietro Mingo
Date: Fri, 29 Jul 2022 17:26:28 +0200
Subject: [PATCH 11/18] fix import
---
build/lib/django_mailbox/models.py | 2 --
build/lib/django_mailbox/transports/base.py | 4 ---
.../django_mailbox/transports/office365.py | 25 +++++-----------
django_mailbox/models.py | 2 --
django_mailbox/transports/office365.py | 29 ++++++++-----------
5 files changed, 20 insertions(+), 42 deletions(-)
diff --git a/build/lib/django_mailbox/models.py b/build/lib/django_mailbox/models.py
index b9f84cc5..c7c03f32 100644
--- a/build/lib/django_mailbox/models.py
+++ b/build/lib/django_mailbox/models.py
@@ -251,8 +251,6 @@ def get_connection(self):
conn = Office365Transport(
self.location,
self.username,
- port=self.port if self.port else None,
- ssl=True,
folder=self.folder
)
conn.connect(self.client_id, self.client_secret, self.tenant_id)
diff --git a/build/lib/django_mailbox/transports/base.py b/build/lib/django_mailbox/transports/base.py
index 9d507a23..a3a2bc01 100644
--- a/build/lib/django_mailbox/transports/base.py
+++ b/build/lib/django_mailbox/transports/base.py
@@ -10,7 +10,3 @@ def get_email_from_bytes(self, contents):
return message
- def get_email_from_string(self, contents):
- message = email.message_from_string(contents)
-
- return message
diff --git a/build/lib/django_mailbox/transports/office365.py b/build/lib/django_mailbox/transports/office365.py
index e60531b7..47c86d62 100644
--- a/build/lib/django_mailbox/transports/office365.py
+++ b/build/lib/django_mailbox/transports/office365.py
@@ -1,4 +1,3 @@
-import O365
import logging
from django.conf import settings
@@ -10,13 +9,8 @@
class Office365Transport(EmailTransport):
def __init__(
- self, hostname, username, port=None, ssl=False, tls=False, folder=None
+ self, hostname, username, folder=None
):
- self.max_message_size = getattr(
- settings,
- 'DJANGO_MAILBOX_MAX_MESSAGE_SIZE',
- False
- )
self.integration_testing_subject = getattr(
settings,
'DJANGO_MAILBOX_INTEGRATION_TESTING_SUBJECT',
@@ -24,19 +18,16 @@ def __init__(
)
self.hostname = hostname
self.username = username
- self.port = port
self.folder = folder
- self.tls = tls
- if ssl:
- #self.transport = imaplib.IMAP4_SSL
- if not self.port:
- self.port = 993
- else:
- #self.transport = imaplib.IMAP4
- if not self.port:
- self.port = 143
def connect(self, client_id, client_secret, tenant_id):
+ try:
+ import O365
+ except ImportError:
+ raise ValueError(
+ "Install o365 to use oauth2 auth for office365"
+ )
+
credentials = (client_id, client_secret)
self.account = O365.Account(credentials, auth_flow_type='credentials', tenant_id=tenant_id)
diff --git a/django_mailbox/models.py b/django_mailbox/models.py
index b9f84cc5..c7c03f32 100644
--- a/django_mailbox/models.py
+++ b/django_mailbox/models.py
@@ -251,8 +251,6 @@ def get_connection(self):
conn = Office365Transport(
self.location,
self.username,
- port=self.port if self.port else None,
- ssl=True,
folder=self.folder
)
conn.connect(self.client_id, self.client_secret, self.tenant_id)
diff --git a/django_mailbox/transports/office365.py b/django_mailbox/transports/office365.py
index e60531b7..2c137b35 100644
--- a/django_mailbox/transports/office365.py
+++ b/django_mailbox/transports/office365.py
@@ -1,4 +1,3 @@
-import O365
import logging
from django.conf import settings
@@ -10,13 +9,8 @@
class Office365Transport(EmailTransport):
def __init__(
- self, hostname, username, port=None, ssl=False, tls=False, folder=None
+ self, hostname, username, folder=None
):
- self.max_message_size = getattr(
- settings,
- 'DJANGO_MAILBOX_MAX_MESSAGE_SIZE',
- False
- )
self.integration_testing_subject = getattr(
settings,
'DJANGO_MAILBOX_INTEGRATION_TESTING_SUBJECT',
@@ -24,19 +18,16 @@ def __init__(
)
self.hostname = hostname
self.username = username
- self.port = port
self.folder = folder
- self.tls = tls
- if ssl:
- #self.transport = imaplib.IMAP4_SSL
- if not self.port:
- self.port = 993
- else:
- #self.transport = imaplib.IMAP4
- if not self.port:
- self.port = 143
def connect(self, client_id, client_secret, tenant_id):
+ try:
+ import O365
+ except ImportError:
+ raise ValueError(
+ "Install o365 to use oauth2 auth for office365"
+ )
+
credentials = (client_id, client_secret)
self.account = O365.Account(credentials, auth_flow_type='credentials', tenant_id=tenant_id)
@@ -56,8 +47,12 @@ def get_message(self, condition=None):
if condition and not condition(message):
continue
+ #TODO: implement archive function
+
yield message
except MessageParseError:
continue
+
+ # TODO: delete all messages
return
From 1f6014918cd2e5e00ac2c47f1b00c719cb0b1b8e Mon Sep 17 00:00:00 2001
From: Pietro Mingo
Date: Fri, 29 Jul 2022 17:32:15 +0200
Subject: [PATCH 12/18] Update readme.rst
---
readme.rst | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/readme.rst b/readme.rst
index 0aac06b1..50342d6a 100644
--- a/readme.rst
+++ b/readme.rst
@@ -8,7 +8,7 @@
:target: https://pypi.python.org/pypi/django-mailbox
-Easily ingest messages from POP3, IMAP, or local mailboxes into your Django application.
+Easily ingest messages from POP3, IMAP, Office365 or local mailboxes into your Django application.
This app allows you to either ingest e-mail content from common e-mail services (as long as the service provides POP3 or IMAP support),
or directly receive e-mail messages from ``stdin`` (for locally processing messages from Postfix or Exim4).
From b39299692a4bd8f58b4593818881c346ea4975f9 Mon Sep 17 00:00:00 2001
From: Pietro Mingo
Date: Mon, 1 Aug 2022 12:39:09 +0200
Subject: [PATCH 13/18] archive and delete
---
build/lib/django_mailbox/models.py | 1 +
build/lib/django_mailbox/transports/office365.py | 11 ++++++++++-
django_mailbox/transports/office365.py | 13 +++++++++----
readme.rst | 2 +-
4 files changed, 21 insertions(+), 6 deletions(-)
diff --git a/build/lib/django_mailbox/models.py b/build/lib/django_mailbox/models.py
index c7c03f32..cbd3d181 100644
--- a/build/lib/django_mailbox/models.py
+++ b/build/lib/django_mailbox/models.py
@@ -251,6 +251,7 @@ def get_connection(self):
conn = Office365Transport(
self.location,
self.username,
+ archive=self.archive,
folder=self.folder
)
conn.connect(self.client_id, self.client_secret, self.tenant_id)
diff --git a/build/lib/django_mailbox/transports/office365.py b/build/lib/django_mailbox/transports/office365.py
index 47c86d62..bca9b748 100644
--- a/build/lib/django_mailbox/transports/office365.py
+++ b/build/lib/django_mailbox/transports/office365.py
@@ -9,7 +9,7 @@
class Office365Transport(EmailTransport):
def __init__(
- self, hostname, username, folder=None
+ self, hostname, username, archive='', folder=None
):
self.integration_testing_subject = getattr(
settings,
@@ -18,6 +18,7 @@ def __init__(
)
self.hostname = hostname
self.username = username
+ self.archive = archive
self.folder = folder
def connect(self, client_id, client_secret, tenant_id):
@@ -39,6 +40,9 @@ def connect(self, client_id, client_secret, tenant_id):
self.mailbox_folder = self.mailbox.get_folder(folder_name=self.folder)
def get_message(self, condition=None):
+ if self.archive:
+ self.mailbox.create_child_folder(self.archive)
+
for message in self.mailbox.get_messages(order_by='receivedDateTime'):
try:
mime_content = message.get_mime_content()
@@ -50,5 +54,10 @@ def get_message(self, condition=None):
yield message
except MessageParseError:
continue
+
+ if self.archive:
+ message.copy(self.archive)
+
+ message.delete()
return
diff --git a/django_mailbox/transports/office365.py b/django_mailbox/transports/office365.py
index 2c137b35..bca9b748 100644
--- a/django_mailbox/transports/office365.py
+++ b/django_mailbox/transports/office365.py
@@ -9,7 +9,7 @@
class Office365Transport(EmailTransport):
def __init__(
- self, hostname, username, folder=None
+ self, hostname, username, archive='', folder=None
):
self.integration_testing_subject = getattr(
settings,
@@ -18,6 +18,7 @@ def __init__(
)
self.hostname = hostname
self.username = username
+ self.archive = archive
self.folder = folder
def connect(self, client_id, client_secret, tenant_id):
@@ -39,6 +40,9 @@ def connect(self, client_id, client_secret, tenant_id):
self.mailbox_folder = self.mailbox.get_folder(folder_name=self.folder)
def get_message(self, condition=None):
+ if self.archive:
+ self.mailbox.create_child_folder(self.archive)
+
for message in self.mailbox.get_messages(order_by='receivedDateTime'):
try:
mime_content = message.get_mime_content()
@@ -47,12 +51,13 @@ def get_message(self, condition=None):
if condition and not condition(message):
continue
- #TODO: implement archive function
-
yield message
except MessageParseError:
continue
- # TODO: delete all messages
+ if self.archive:
+ message.copy(self.archive)
+
+ message.delete()
return
diff --git a/readme.rst b/readme.rst
index 50342d6a..f03459e7 100644
--- a/readme.rst
+++ b/readme.rst
@@ -8,7 +8,7 @@
:target: https://pypi.python.org/pypi/django-mailbox
-Easily ingest messages from POP3, IMAP, Office365 or local mailboxes into your Django application.
+Easily ingest messages from POP3, IMAP, Office365 API or local mailboxes into your Django application.
This app allows you to either ingest e-mail content from common e-mail services (as long as the service provides POP3 or IMAP support),
or directly receive e-mail messages from ``stdin`` (for locally processing messages from Postfix or Exim4).
From 9f314a38f2ec000e4daf19ea86165886df2033ad Mon Sep 17 00:00:00 2001
From: Pietro Mingo
Date: Mon, 1 Aug 2022 15:03:11 +0200
Subject: [PATCH 14/18] fix and documentation
---
.../django_mailbox/transports/office365.py | 15 ++++++++------
django_mailbox/transports/office365.py | 15 ++++++++------
docs/topics/mailbox_types.rst | 20 +++++++++++++++++++
3 files changed, 38 insertions(+), 12 deletions(-)
diff --git a/build/lib/django_mailbox/transports/office365.py b/build/lib/django_mailbox/transports/office365.py
index bca9b748..4f748b2f 100644
--- a/build/lib/django_mailbox/transports/office365.py
+++ b/build/lib/django_mailbox/transports/office365.py
@@ -40,12 +40,15 @@ def connect(self, client_id, client_secret, tenant_id):
self.mailbox_folder = self.mailbox.get_folder(folder_name=self.folder)
def get_message(self, condition=None):
+ archive_folder = None
if self.archive:
- self.mailbox.create_child_folder(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 message in self.mailbox.get_messages(order_by='receivedDateTime'):
+ for o365message in self.mailbox_folder.get_messages(order_by='receivedDateTime'):
try:
- mime_content = message.get_mime_content()
+ mime_content = o365message.get_mime_content()
message = self.get_email_from_bytes(mime_content)
if condition and not condition(message):
@@ -55,9 +58,9 @@ def get_message(self, condition=None):
except MessageParseError:
continue
- if self.archive:
- message.copy(self.archive)
+ if self.archive and archive_folder:
+ o365message.copy(archive_folder)
- message.delete()
+ o365message.delete()
return
diff --git a/django_mailbox/transports/office365.py b/django_mailbox/transports/office365.py
index bca9b748..4f748b2f 100644
--- a/django_mailbox/transports/office365.py
+++ b/django_mailbox/transports/office365.py
@@ -40,12 +40,15 @@ def connect(self, client_id, client_secret, tenant_id):
self.mailbox_folder = self.mailbox.get_folder(folder_name=self.folder)
def get_message(self, condition=None):
+ archive_folder = None
if self.archive:
- self.mailbox.create_child_folder(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 message in self.mailbox.get_messages(order_by='receivedDateTime'):
+ for o365message in self.mailbox_folder.get_messages(order_by='receivedDateTime'):
try:
- mime_content = message.get_mime_content()
+ mime_content = o365message.get_mime_content()
message = self.get_email_from_bytes(mime_content)
if condition and not condition(message):
@@ -55,9 +58,9 @@ def get_message(self, condition=None):
except MessageParseError:
continue
- if self.archive:
- message.copy(self.archive)
+ if self.archive and archive_folder:
+ o365message.copy(archive_folder)
- message.delete()
+ o365message.delete()
return
diff --git a/docs/topics/mailbox_types.rst b/docs/topics/mailbox_types.rst
index ad8c9ff8..2d5a4573 100644
--- a/docs/topics/mailbox_types.rst
+++ b/docs/topics/mailbox_types.rst
@@ -13,6 +13,7 @@ POP3 and IMAP as well as local file-based mailboxes.
POP3 ``pop3://`` Can also specify SSL with ``pop3+ssl://``
IMAP ``imap://`` Can also specify SSL with ``imap+ssl://`` or STARTTLS with ``imap+tls``; additional configuration is also possible: see :ref:`pop3-and-imap-mailboxes` for details.
Gmail IMAP ``gmail+ssl://`` Uses OAuth authentication for Gmail's IMAP transport. See :ref:`gmail-oauth` for details.
+ Office365 API``office365://`` Uses OAuth authentication for Office365 API transport. See :ref:`office365-oauth` for details.
Maildir ``maildir://``
Mbox ``mbox://``
Babyl ``babyl://``
@@ -115,6 +116,25 @@ Build your URI accordingly::
gmail+ssl://youremailaddress%40gmail.com:oauth2@imap.gmail.com?archive=Archived
+.. _office365-oauth:
+Office 365 API
+-------------------------------------
+
+Office 365 allows through the API to read a mailbox with Oauth.
+The O365_ library is used.
+
+.. _O365: https://github.com/O365/python-o365
+.. _configuration: https://github.com/O365/python-o365#authentication
+
+For the configuration_ you need to register an application and get a client_id, client_secret and tenant_id.
+
+This implementation uses the client credentials grant flow and the password you specify will be ignored.
+
+Build your URI accordingly::
+
+ office365://youremailaddress%40yourdomain.com:oauth2@outlook.office365.com?client_id=client_id&client_secret=client_secret&tenant_id=tenant_id&archive=Archived
+
+
Local File-based Mailboxes
--------------------------
From 70aa88bbd6d2f339540b62c8f0f2900fe43f12dd Mon Sep 17 00:00:00 2001
From: Pietro Mingo
Date: Mon, 1 Aug 2022 17:21:50 +0200
Subject: [PATCH 15/18] removed build folder
---
build/lib/django_mailbox/__init__.py | 3 -
build/lib/django_mailbox/admin.py | 112 ---
build/lib/django_mailbox/apps.py | 7 -
build/lib/django_mailbox/google_utils.py | 111 ---
.../locale/ru_RU/LC_MESSAGES/django.mo | Bin 5329 -> 0 bytes
.../locale/ru_RU/LC_MESSAGES/django.po | 193 ----
.../lib/django_mailbox/management/__init__.py | 0
.../management/commands/__init__.py | 0
.../management/commands/getmail.py | 30 -
.../commands/processincomingmessage.py | 52 --
.../commands/rebuildmessageattachments.py | 71 --
.../django_mailbox/migrations/0001_initial.py | 56 --
.../migrations/0002_add_eml_to_message.py | 17 -
.../migrations/0003_auto_20150409_0316.py | 16 -
.../migrations/0004_bytestring_to_unicode.py | 21 -
.../migrations/0005_auto_20160523_2240.py | 17 -
.../migrations/0006_mailbox_last_polling.py | 18 -
.../migrations/0007_auto_20180421_0026.py | 25 -
.../migrations/0008_auto_20190219_1553.py | 23 -
.../lib/django_mailbox/migrations/__init__.py | 0
build/lib/django_mailbox/models.py | 853 ------------------
build/lib/django_mailbox/signals.py | 3 -
.../south_migrations/0001_initial.py | 58 --
.../0002_auto__chg_field_mailbox_uri.py | 38 -
.../0003_auto__add_field_mailbox_active.py | 41 -
.../0004_auto__add_field_message_outgoing.py | 42 -
.../south_migrations/0005_rename_fields.py | 38 -
...006_auto__add_field_message_in_reply_to.py | 55 --
...__add_field_message_from_header__add_fi.py | 59 --
.../0008_populate_from_to_fields.py | 46 -
.../0009_remove_references_table.py | 47 -
...0010_auto__add_field_mailbox_from_email.py | 45 -
.../0011_auto__add_field_message_read.py | 46 -
.../0012_auto__add_messageattachment.py | 66 --
...to__add_field_messageattachment_message.py | 53 --
.../0014_migrate_message_attachments.py | 54 --
...to__add_field_messageattachment_headers.py | 64 --
.../0016_auto__add_field_message_encoded.py | 54 --
.../0017_auto__add_field_message_eml.py | 55 --
.../south_migrations/__init__.py | 0
build/lib/django_mailbox/tests/__init__.py | 0
build/lib/django_mailbox/tests/base.py | 191 ----
build/lib/django_mailbox/tests/settings.py | 12 -
.../tests/test_integration_imap.py | 70 --
.../lib/django_mailbox/tests/test_mailbox.py | 46 -
.../tests/test_message_flattening.py | 116 ---
.../tests/test_process_email.py | 432 ---------
.../tests/test_processincomingmessage.py | 65 --
.../django_mailbox/tests/test_transports.py | 167 ----
.../lib/django_mailbox/transports/__init__.py | 11 -
build/lib/django_mailbox/transports/babyl.py | 6 -
build/lib/django_mailbox/transports/base.py | 12 -
.../lib/django_mailbox/transports/generic.py | 26 -
build/lib/django_mailbox/transports/gmail.py | 57 --
build/lib/django_mailbox/transports/imap.py | 137 ---
.../lib/django_mailbox/transports/maildir.py | 9 -
build/lib/django_mailbox/transports/mbox.py | 6 -
build/lib/django_mailbox/transports/mh.py | 6 -
build/lib/django_mailbox/transports/mmdf.py | 6 -
.../django_mailbox/transports/office365.py | 66 --
build/lib/django_mailbox/transports/pop3.py | 44 -
build/lib/django_mailbox/utils.py | 151 ----
62 files changed, 4125 deletions(-)
delete mode 100644 build/lib/django_mailbox/__init__.py
delete mode 100644 build/lib/django_mailbox/admin.py
delete mode 100644 build/lib/django_mailbox/apps.py
delete mode 100644 build/lib/django_mailbox/google_utils.py
delete mode 100644 build/lib/django_mailbox/locale/ru_RU/LC_MESSAGES/django.mo
delete mode 100644 build/lib/django_mailbox/locale/ru_RU/LC_MESSAGES/django.po
delete mode 100644 build/lib/django_mailbox/management/__init__.py
delete mode 100644 build/lib/django_mailbox/management/commands/__init__.py
delete mode 100644 build/lib/django_mailbox/management/commands/getmail.py
delete mode 100644 build/lib/django_mailbox/management/commands/processincomingmessage.py
delete mode 100644 build/lib/django_mailbox/management/commands/rebuildmessageattachments.py
delete mode 100644 build/lib/django_mailbox/migrations/0001_initial.py
delete mode 100644 build/lib/django_mailbox/migrations/0002_add_eml_to_message.py
delete mode 100644 build/lib/django_mailbox/migrations/0003_auto_20150409_0316.py
delete mode 100644 build/lib/django_mailbox/migrations/0004_bytestring_to_unicode.py
delete mode 100644 build/lib/django_mailbox/migrations/0005_auto_20160523_2240.py
delete mode 100644 build/lib/django_mailbox/migrations/0006_mailbox_last_polling.py
delete mode 100644 build/lib/django_mailbox/migrations/0007_auto_20180421_0026.py
delete mode 100644 build/lib/django_mailbox/migrations/0008_auto_20190219_1553.py
delete mode 100644 build/lib/django_mailbox/migrations/__init__.py
delete mode 100644 build/lib/django_mailbox/models.py
delete mode 100644 build/lib/django_mailbox/signals.py
delete mode 100644 build/lib/django_mailbox/south_migrations/0001_initial.py
delete mode 100644 build/lib/django_mailbox/south_migrations/0002_auto__chg_field_mailbox_uri.py
delete mode 100644 build/lib/django_mailbox/south_migrations/0003_auto__add_field_mailbox_active.py
delete mode 100644 build/lib/django_mailbox/south_migrations/0004_auto__add_field_message_outgoing.py
delete mode 100644 build/lib/django_mailbox/south_migrations/0005_rename_fields.py
delete mode 100644 build/lib/django_mailbox/south_migrations/0006_auto__add_field_message_in_reply_to.py
delete mode 100644 build/lib/django_mailbox/south_migrations/0007_auto__del_field_message_address__add_field_message_from_header__add_fi.py
delete mode 100644 build/lib/django_mailbox/south_migrations/0008_populate_from_to_fields.py
delete mode 100644 build/lib/django_mailbox/south_migrations/0009_remove_references_table.py
delete mode 100644 build/lib/django_mailbox/south_migrations/0010_auto__add_field_mailbox_from_email.py
delete mode 100644 build/lib/django_mailbox/south_migrations/0011_auto__add_field_message_read.py
delete mode 100644 build/lib/django_mailbox/south_migrations/0012_auto__add_messageattachment.py
delete mode 100644 build/lib/django_mailbox/south_migrations/0013_auto__add_field_messageattachment_message.py
delete mode 100644 build/lib/django_mailbox/south_migrations/0014_migrate_message_attachments.py
delete mode 100644 build/lib/django_mailbox/south_migrations/0015_auto__add_field_messageattachment_headers.py
delete mode 100644 build/lib/django_mailbox/south_migrations/0016_auto__add_field_message_encoded.py
delete mode 100644 build/lib/django_mailbox/south_migrations/0017_auto__add_field_message_eml.py
delete mode 100644 build/lib/django_mailbox/south_migrations/__init__.py
delete mode 100644 build/lib/django_mailbox/tests/__init__.py
delete mode 100644 build/lib/django_mailbox/tests/base.py
delete mode 100644 build/lib/django_mailbox/tests/settings.py
delete mode 100644 build/lib/django_mailbox/tests/test_integration_imap.py
delete mode 100644 build/lib/django_mailbox/tests/test_mailbox.py
delete mode 100644 build/lib/django_mailbox/tests/test_message_flattening.py
delete mode 100644 build/lib/django_mailbox/tests/test_process_email.py
delete mode 100644 build/lib/django_mailbox/tests/test_processincomingmessage.py
delete mode 100644 build/lib/django_mailbox/tests/test_transports.py
delete mode 100644 build/lib/django_mailbox/transports/__init__.py
delete mode 100644 build/lib/django_mailbox/transports/babyl.py
delete mode 100644 build/lib/django_mailbox/transports/base.py
delete mode 100644 build/lib/django_mailbox/transports/generic.py
delete mode 100644 build/lib/django_mailbox/transports/gmail.py
delete mode 100644 build/lib/django_mailbox/transports/imap.py
delete mode 100644 build/lib/django_mailbox/transports/maildir.py
delete mode 100644 build/lib/django_mailbox/transports/mbox.py
delete mode 100644 build/lib/django_mailbox/transports/mh.py
delete mode 100644 build/lib/django_mailbox/transports/mmdf.py
delete mode 100644 build/lib/django_mailbox/transports/office365.py
delete mode 100644 build/lib/django_mailbox/transports/pop3.py
delete mode 100644 build/lib/django_mailbox/utils.py
diff --git a/build/lib/django_mailbox/__init__.py b/build/lib/django_mailbox/__init__.py
deleted file mode 100644
index feda890b..00000000
--- a/build/lib/django_mailbox/__init__.py
+++ /dev/null
@@ -1,3 +0,0 @@
-__version__ = '4.8.2'
-
-default_app_config = 'django_mailbox.apps.MailBoxConfig'
diff --git a/build/lib/django_mailbox/admin.py b/build/lib/django_mailbox/admin.py
deleted file mode 100644
index 4d489b1e..00000000
--- a/build/lib/django_mailbox/admin.py
+++ /dev/null
@@ -1,112 +0,0 @@
-#!/usr/bin/env python
-
-"""
-Model configuration in application ``django_mailbox`` for administration
-console.
-"""
-
-import logging
-
-from django.conf import settings
-from django.contrib import admin
-from django.utils.translation import gettext_lazy as _
-
-from django_mailbox.models import MessageAttachment, Message, Mailbox
-from django_mailbox.signals import message_received
-from django_mailbox.utils import convert_header_to_unicode
-
-
-logger = logging.getLogger(__name__)
-
-
-def get_new_mail(mailbox_admin, request, queryset):
- queryset.get_new_mail()
-
-
-get_new_mail.short_description = _('Get new mail')
-
-
-def resend_message_received_signal(message_admin, request, queryset):
- for message in queryset.all():
- logger.debug('Resending \'message_received\' signal for %s' % message)
- message_received.send(sender=message_admin, message=message)
-
-
-resend_message_received_signal.short_description = (
- _('Re-send message received signal')
-)
-
-
-class MailboxAdmin(admin.ModelAdmin):
- list_display = (
- 'name',
- 'uri',
- 'from_email',
- 'active',
- 'last_polling',
- )
- readonly_fields = ['last_polling', ]
- actions = [get_new_mail]
-
-
-class MessageAttachmentAdmin(admin.ModelAdmin):
- raw_id_fields = ('message', )
- list_display = ('message', 'document',)
-
-
-class MessageAttachmentInline(admin.TabularInline):
- model = MessageAttachment
- extra = 0
-
-
-class MessageAdmin(admin.ModelAdmin):
- def attachment_count(self, msg):
- return msg.attachments.count()
-
- attachment_count.short_description = _('Attachment count')
-
- def subject(self, msg):
- return convert_header_to_unicode(msg.subject)
-
- def envelope_headers(self, msg):
- email = msg.get_email_object()
- return '\n'.join(
- [('{}: {}'.format(h, v)) for h, v in email.items()]
- )
-
- inlines = [
- MessageAttachmentInline,
- ]
- list_display = (
- 'subject',
- 'processed',
- 'read',
- 'mailbox',
- 'outgoing',
- 'attachment_count',
- )
- ordering = ['-processed']
- list_filter = (
- 'mailbox',
- 'outgoing',
- 'processed',
- 'read',
- )
- exclude = (
- 'body',
- )
- raw_id_fields = (
- 'in_reply_to',
- )
- readonly_fields = (
- 'envelope_headers',
- 'text',
- 'html',
- )
- actions = [resend_message_received_signal]
-
-
-if getattr(settings, 'DJANGO_MAILBOX_ADMIN_ENABLED', True):
- admin.site.register(Message, MessageAdmin)
- admin.site.register(MessageAttachment, MessageAttachmentAdmin)
- admin.site.register(Mailbox, MailboxAdmin)
diff --git a/build/lib/django_mailbox/apps.py b/build/lib/django_mailbox/apps.py
deleted file mode 100644
index 13b7bf3c..00000000
--- a/build/lib/django_mailbox/apps.py
+++ /dev/null
@@ -1,7 +0,0 @@
-from django.apps import AppConfig
-from django.utils.translation import gettext_lazy as _
-
-
-class MailBoxConfig(AppConfig):
- name = 'django_mailbox'
- verbose_name = _("Mail Box")
diff --git a/build/lib/django_mailbox/google_utils.py b/build/lib/django_mailbox/google_utils.py
deleted file mode 100644
index b834f59c..00000000
--- a/build/lib/django_mailbox/google_utils.py
+++ /dev/null
@@ -1,111 +0,0 @@
-import logging
-
-from django.conf import settings
-import requests
-from social.apps.django_app.default.models import UserSocialAuth
-
-
-logger = logging.getLogger(__name__)
-
-
-class AccessTokenNotFound(Exception):
- pass
-
-
-class RefreshTokenNotFound(Exception):
- pass
-
-
-def get_google_consumer_key():
- return settings.SOCIAL_AUTH_GOOGLE_OAUTH2_KEY
-
-
-def get_google_consumer_secret():
- return settings.SOCIAL_AUTH_GOOGLE_OAUTH2_SECRET
-
-
-def get_google_access_token(email):
- # TODO: This should be cacheable
- try:
- me = UserSocialAuth.objects.get(uid=email, provider="google-oauth2")
- return me.extra_data['access_token']
- except (UserSocialAuth.DoesNotExist, KeyError):
- raise AccessTokenNotFound
-
-
-def update_google_extra_data(email, extra_data):
- try:
- me = UserSocialAuth.objects.get(uid=email, provider="google-oauth2")
- me.extra_data = extra_data
- me.save()
- except (UserSocialAuth.DoesNotExist, KeyError):
- raise AccessTokenNotFound
-
-
-def get_google_refresh_token(email):
- try:
- me = UserSocialAuth.objects.get(uid=email, provider="google-oauth2")
- return me.extra_data['refresh_token']
- except (UserSocialAuth.DoesNotExist, KeyError):
- raise RefreshTokenNotFound
-
-
-def google_api_get(email, url):
- headers = dict(
- Authorization="Bearer %s" % get_google_access_token(email),
- )
- r = requests.get(url, headers=headers)
- logger.info("I got a %s", r.status_code)
- if r.status_code == 401:
- # Go use the refresh token
- refresh_authorization(email)
- r = requests.get(url, headers=headers)
- logger.info("I got a %s", r.status_code)
- if r.status_code == 200:
- try:
- return r.json()
- except ValueError:
- return r.text
-
-
-def google_api_post(email, url, post_data, authorized=True):
- # TODO: Make this a lot less ugly. especially the 401 handling
- headers = dict()
- if authorized is True:
- headers.update(dict(
- Authorization="Bearer %s" % get_google_access_token(email),
- ))
- r = requests.post(url, headers=headers, data=post_data)
- if r.status_code == 401:
- refresh_authorization(email)
- r = requests.post(url, headers=headers, data=post_data)
- if r.status_code == 200:
- try:
- return r.json()
- except ValueError:
- return r.text
-
-
-def refresh_authorization(email):
- refresh_token = get_google_refresh_token(email)
- post_data = dict(
- refresh_token=refresh_token,
- client_id=get_google_consumer_key(),
- client_secret=get_google_consumer_secret(),
- grant_type='refresh_token',
- )
- results = google_api_post(
- email,
- "https://accounts.google.com/o/oauth2/token?access_type=offline",
- post_data,
- authorized=False)
- results.update({'refresh_token': refresh_token})
- update_google_extra_data(email, results)
-
-
-def fetch_user_info(email):
- result = google_api_get(
- email,
- "https://www.googleapis.com/oauth2/v1/userinfo?alt=json"
- )
- return result
diff --git a/build/lib/django_mailbox/locale/ru_RU/LC_MESSAGES/django.mo b/build/lib/django_mailbox/locale/ru_RU/LC_MESSAGES/django.mo
deleted file mode 100644
index 06dd2c9e8c0550671d97cef5a6fb48e395c4f0e7..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 5329
zcmbVQQEVJX8J0SBK~zHGrP3w%8rw-+9*}qf?Mg_zph`SdLV`!sN4{@n_k5Q)
zDCOk4Z+GUOfByfQZ~htilgA!>U*NMH-|yo)^neh1fvU0L}xy3_K4UfZavl81Ro@5~2s~kD&8wz$btY19t#vzb9=k
zr|qLa`ac20pLh*FUj^Ffy9cDdIpAZ!v%qfwF9R{9_#IFJKLpaxUw|C{H{iE{{{TJ%
z{11@!kHZL$;z{5o;B!FQe+29W{sD-T#Xo_cA)kK($;+n*g8Y33M3dNtJr-AH$
z`0FOVr&25ezl-+6z(2w6HQ?WY+d&@Vc;Hc!*F(Sp-X8@rzk?~B06vBHY2XvUb17a0
zK8g2LAnpGO%mY6KGLCI%B<@UcA8-KguK@8Ueu^LR@*Z#y_*>v8@Dm{Z#14?l{04y~
z#Cr(H^$RiRC~y%-e*O$Zx4^Vpe#klFl=zZ2&iOrjnG>b=9el|H=VZKGgY3h6`C%NL
zAI?h+a{WN3))%1tVVv9XCGXTUKhNR&IKI@-)A&N3`a=zG!S-CxI-ykgn&r6C@uvJUa@r51r{=T9nhHZ}MupOD
z1dcZ&>%Qv}s|!_Eg#{@m>7hzz9BunB_54WAT60QTo>bG*sv5~g-S_1Dtcqq;AkAzT
z1z+e?wnVF9w`T&P0lfvPs*piwMob5OO_nD*HDajRo_;&7&&9r>aHrwaYQ?ely|z(e&a4W
zxZpPe+sB(%sQR^?GtvH`sX*>NkQ+r3Ipk~EAR_W*s3J|d--u>>607K)P3qWRNyk@Y~G1x;fG=Aapb6v(+9FU
z^m)g1b0C%RBaIq~#gw{1k9_^e$mpSyM<-8>P8=IQH8Or^?C9%-P12m2RewGV-SY0;
zwFTr9cvel7YYTNN4Cnp8J{bB~02R!ErYTDhs0_%D9`n)mMfrWZiH40T
zog;`WXT94}XJKJ1;?|gQE$?)@@{u}lfwDstpquq9+ozkI(X54pD2+6vEcQ^&IaXSz
z3S!bvce|Jj8cI6TdY7czZ3>l`GYngydSO5sx!B_5#29H|O8GH6|3fIi@x8L_nNU=G
zemtCU?EG+JCd^OzXg+>yGJiNwRs^g35i3Gb+EXm;%NOzYqAcz$_rH)Y?kyH!n4eH{
z4!zxNSL!eK?|Z)34?Bt^KgpfqT9F@=<=C97=HwCOA=n|?$*bTe2Nyt(I#UQ52Vn2b
zG;mDhCzVx0K-qg)28~k_Cws=n#z!{OC>4r5hm9iglM8i38>utV?z#){W6MJIV7qd1
zax_1hN=feA
zDfw7@wo)p|mt;}G9PbAzd$O*f$^akrh~iLbk9^||Gvq*Npcg|*>5xjL$S!Snpt5&!
z_=~;mcKF3K*_=Zacz8+ffrI|u{XIX*zpOkJ7;2CE%61}IDhw9(6-qrKelK26mg3d;
zdVD*%5U+{&m+?k?GmrNTiJzNz`zUV3w_$?L5AcX45&t@FCW~=1z7@BUrDRbi%kes1
zZ@|ZOAQ~mcH2Kqli)+c7_+5dMTk%G67DL{a@yGD60rM7cJzhlv`pzR7Hg3efM+C{m
z>$bZl0$5GXqqW60*d`Z}H)V1bkDTbOELwywlb`9)H)*?JCWK+LAma}?528%Y
z!CwW47mp5#dd_rg+7U(7~={;OWsMHb!I>|+wtp>@y%ozfv4=G
z$tKGZA>N?7CK+QiXt`G?j9KT=1V`%_yhYy0J8;tCx@v-NYnIkJf;Tgos5Q-;)1_vLR1aP$1+4t%yV1l~ARa9x
zt&C=J5#-;MEt}gZHM_1i@%tbQ0>UaM7gCZ}(S+5&^1KZJFLT|Ym-|`N6^y}DaL^G{
z3+tZEPN8%<>fSUUgBr$^dW)rjZV_U(yR`os(T&(-%D0}g_@!k$Val2*SbZ5W?7O&))JHr
zlElYr*zc?#=>-fz;(BIxFujs1I{ihAU}0+}?_vNmh9!hd7O~W%56gTGuWPykieLkG
zP1o!YN{%OS_oa>dehG%_jO%_cY}9X;*4DJv+Lt#hUMmwQHb|;tqs(meum_FmjE>Fn
zYxSC9z?Em}SZFP9aKX~I%tZwsADb-C>J7*`dlUKXGM7E@m@F0Il>)AO@l|eg{Osc8
zua_mCwya>Qnt!a8F23aKj^dkbew1*-ELd6<@g)>t#K4MgjtA{j=i5S*`i$wQ)225)
z5W33g{r4cY>sTa>LUl!_MMDx{!~8idGVzg1F~bUN9A*g{I34NUu2ki-p&PNxG4
zO`GN_JjkT0qQK8p2%o~_NkrZ0LfW?byc000>n2eSH4DQ7ndgdGU*x}v
z(?Op}=+`wZ6@a-9fKV24oY@?ag~Thut+)wl(1*uV!yT*KGDkZEiLKB|ey(HZX^)un
zod6x!Tz6>y6sN;m`jvV8`+5bt_rkHR<=7
g`#TKGX$5-q#p^Cs$nFiD#a+iN??2|Mudn$31sHQPiU0rr
diff --git a/build/lib/django_mailbox/locale/ru_RU/LC_MESSAGES/django.po b/build/lib/django_mailbox/locale/ru_RU/LC_MESSAGES/django.po
deleted file mode 100644
index d84c3cb8..00000000
--- a/build/lib/django_mailbox/locale/ru_RU/LC_MESSAGES/django.po
+++ /dev/null
@@ -1,193 +0,0 @@
-# SOME DESCRIPTIVE TITLE.
-# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
-# This file is distributed under the same license as the PACKAGE package.
-# FIRST AUTHOR , YEAR.
-#
-msgid ""
-msgstr ""
-"Project-Id-Version: \n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2017-07-09 05:36-0500\n"
-"PO-Revision-Date: 2017-07-09 13:37+0300\n"
-"Last-Translator: Ivlev Denis \n"
-"Language-Team: \n"
-"Language: ru_RU\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
-"%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n"
-"%100>=11 && n%100<=14)? 2 : 3);\n"
-"X-Generator: Poedit 1.8.7.1\n"
-
-#: django_mailbox/admin.py:29
-msgid "Get new mail"
-msgstr "Получить новые сообщения"
-
-#: django_mailbox/admin.py:39
-msgid "Re-send message received signal"
-msgstr "Повторно отправить сигнал о получении"
-
-#: django_mailbox/admin.py:69
-msgid "Attachment count"
-msgstr "Кол-во вложений"
-
-#: django_mailbox/apps.py:7
-msgid "Mail Box"
-msgstr "Почтовый ящик"
-
-#: django_mailbox/models.py:49
-msgid "Name"
-msgstr "Название"
-
-#: django_mailbox/models.py:54
-msgid "URI"
-msgstr "URI"
-
-#: django_mailbox/models.py:57
-msgid ""
-"Example: imap+ssl://myusername:mypassword@someserver
Internet "
-"transports include 'imap' and 'pop3'; common local file transports include "
-"'maildir', 'mbox', and less commonly 'babyl', 'mh', and 'mmdf'.
Be sure to urlencode your username and password should they contain illegal "
-"characters (like @, :, etc)."
-msgstr ""
-"Пример: imap+ssl://myusername:mypassword@someserver
Интернет-"
-"транспорт может быть 'imap' или 'pop3'; поддерживаются локальные файловые "
-"транспорты 'maildir', 'mbox', а также 'babyl', 'mh', and 'mmdf'.
Используйте urlencode, если имя пользователя или пароль содержат "
-"недопустипые символы (@, :, и т.д.)."
-
-#: django_mailbox/models.py:72
-msgid "From email"
-msgstr "От"
-
-#: django_mailbox/models.py:75
-msgid ""
-"Example: MailBot <mailbot@yourdomain.com> 'From' header to set "
-"for outgoing email.
If you do not use this e-mail inbox for "
-"outgoing mail, this setting is unnecessary. If you send e-mail without "
-"setting this, your 'From' header will'be set to match the setting "
-"`DEFAULT_FROM_EMAIL`."
-msgstr ""
-"Пример: MailBot <mailbot@yourdomain.com> Исходящая электронная "
-"почта.
Если вы не используете этот почтовый ящик для исходящей "
-"почты, этот параметр не нужен. Если вы не указали данный параметр, "
-"будет использоваться указанный в настройках `DEFAULT_FROM_EMAIL`."
-
-#: django_mailbox/models.py:89
-msgid "Active"
-msgstr "Активный"
-
-#: django_mailbox/models.py:91
-msgid ""
-"Check this e-mail inbox for new e-mail messages during polling cycles. This "
-"checkbox does not have an effect upon whether mail is collected here when "
-"this mailbox receives mail from a pipe, and does not affect whether e-mail "
-"messages can be dispatched from this mailbox. "
-msgstr ""
-"Параметр указывает на необходимость проверки почтового ящика на наличие "
-"новых сообщений в цикле опроса. Этот флажок не влияет на сбор почты, когда "
-"этот почтовый ящик получает почту из канала и не влияет на отправку "
-"сообщений электронной почты из этого почтового ящика."
-
-#: django_mailbox/models.py:102
-msgid "Last polling"
-msgstr "Последний опрос"
-
-#: django_mailbox/models.py:103
-msgid ""
-"The time of last successful polling for messages.It is blank for new "
-"mailboxes and is not set for mailboxes that only receive messages via a pipe."
-msgstr ""
-"Время последнего успешного опроса сообщений. Для нового почтового ящика не "
-"установлено. Также не устанавливается для почтовых ящиков "
-"обновляющих сообщения через pipe."
-
-#: django_mailbox/models.py:409 django_mailbox/models.py:438
-msgid "Mailbox"
-msgstr "Почтовый ящик"
-
-#: django_mailbox/models.py:410
-msgid "Mailboxes"
-msgstr "Почтовые ящики"
-
-#: django_mailbox/models.py:442
-msgid "Subject"
-msgstr "Тема"
-
-#: django_mailbox/models.py:447
-msgid "Message ID"
-msgstr "Идентификатор сообщения"
-
-#: django_mailbox/models.py:456
-msgid "In reply to"
-msgstr "В ответ на"
-
-#: django_mailbox/models.py:460
-msgid "From header"
-msgstr "От(From)"
-
-#: django_mailbox/models.py:465
-msgid "To header"
-msgstr "Кому(To)"
-
-#: django_mailbox/models.py:469
-msgid "Outgoing"
-msgstr "Исходящее"
-
-#: django_mailbox/models.py:475
-msgid "Body"
-msgstr "Тело"
-
-#: django_mailbox/models.py:479
-msgid "Encoded"
-msgstr "Закодировано"
-
-#: django_mailbox/models.py:481
-msgid "True if the e-mail body is Base64 encoded"
-msgstr "True если тело сообщения закодировано в Base64"
-
-#: django_mailbox/models.py:485
-msgid "Processed"
-msgstr "Обработано"
-
-#: django_mailbox/models.py:490
-msgid "Read"
-msgstr "Прочитано"
-
-#: django_mailbox/models.py:497
-msgid "Raw message contents"
-msgstr "Исходное содержимое сообщения"
-
-#: django_mailbox/models.py:500
-msgid "Original full content of message"
-msgstr "Полное содержимое сообщения"
-
-#: django_mailbox/models.py:716
-msgid "E-mail message"
-msgstr "Сообщение"
-
-#: django_mailbox/models.py:717
-msgid "E-mail messages"
-msgstr "Сообщения"
-
-#: django_mailbox/models.py:726
-msgid "Message"
-msgstr "Сообщение"
-
-#: django_mailbox/models.py:730
-msgid "Headers"
-msgstr "Заголовки"
-
-#: django_mailbox/models.py:736
-msgid "Document"
-msgstr "Документ"
-
-#: django_mailbox/models.py:793
-msgid "Message attachment"
-msgstr "Вложение"
-
-#: django_mailbox/models.py:794
-msgid "Message attachments"
-msgstr "Вложения"
diff --git a/build/lib/django_mailbox/management/__init__.py b/build/lib/django_mailbox/management/__init__.py
deleted file mode 100644
index e69de29b..00000000
diff --git a/build/lib/django_mailbox/management/commands/__init__.py b/build/lib/django_mailbox/management/commands/__init__.py
deleted file mode 100644
index e69de29b..00000000
diff --git a/build/lib/django_mailbox/management/commands/getmail.py b/build/lib/django_mailbox/management/commands/getmail.py
deleted file mode 100644
index 31b2dbdc..00000000
--- a/build/lib/django_mailbox/management/commands/getmail.py
+++ /dev/null
@@ -1,30 +0,0 @@
-import logging
-
-from django.core.management.base import BaseCommand
-
-from django_mailbox.models import Mailbox
-
-
-logger = logging.getLogger(__name__)
-logging.basicConfig(level=logging.INFO)
-
-
-class Command(BaseCommand):
- def handle(self, *args, **options):
- mailboxes = Mailbox.active_mailboxes.all()
- if args:
- mailboxes = mailboxes.filter(
- name=' '.join(args)
- )
- for mailbox in mailboxes:
- logger.info(
- 'Gathering messages for %s',
- mailbox.name
- )
- messages = mailbox.get_new_mail()
- for message in messages:
- logger.info(
- 'Received %s (from %s)',
- message.subject,
- message.from_address
- )
diff --git a/build/lib/django_mailbox/management/commands/processincomingmessage.py b/build/lib/django_mailbox/management/commands/processincomingmessage.py
deleted file mode 100644
index ec344111..00000000
--- a/build/lib/django_mailbox/management/commands/processincomingmessage.py
+++ /dev/null
@@ -1,52 +0,0 @@
-import email
-import logging
-import sys
-try:
- from email import utils
-except ImportError:
- import rfc822 as utils
-
-from django.core.management.base import BaseCommand
-
-from django_mailbox.models import Mailbox
-
-
-logger = logging.getLogger(__name__)
-logging.basicConfig(level=logging.INFO)
-
-
-class Command(BaseCommand):
- args = "<[Mailbox Name (optional)]>"
- help = "Receive incoming mail via stdin"
-
- def add_arguments(self, parser):
- parser.add_argument(
- 'mailbox_name',
- nargs='?',
- help="The name of the mailbox that will receive the message"
- )
-
- def handle(self, mailbox_name=None, *args, **options):
- message = email.message_from_string(sys.stdin.read())
- if message:
- if mailbox_name:
- mailbox = self.get_mailbox_by_name(mailbox_name)
- else:
- mailbox = self.get_mailbox_for_message(message)
- mailbox.process_incoming_message(message)
- logger.info(
- "Message received from %s",
- message['from']
- )
- else:
- logger.warning("Message not processable.")
-
- def get_mailbox_by_name(self, name):
- mailbox, created = Mailbox.objects.get_or_create(
- name=name,
- )
- return mailbox
-
- def get_mailbox_for_message(self, message):
- email_address = utils.parseaddr(message['to'])[1][0:255]
- return self.get_mailbox_by_name(email_address)
diff --git a/build/lib/django_mailbox/management/commands/rebuildmessageattachments.py b/build/lib/django_mailbox/management/commands/rebuildmessageattachments.py
deleted file mode 100644
index 6bfc24f6..00000000
--- a/build/lib/django_mailbox/management/commands/rebuildmessageattachments.py
+++ /dev/null
@@ -1,71 +0,0 @@
-import email
-import hashlib
-import logging
-
-from django.core.management.base import BaseCommand
-
-from django_mailbox.models import MessageAttachment, Message
-
-logger = logging.getLogger(__name__)
-logging.basicConfig(level=logging.INFO)
-
-
-class Command(BaseCommand):
- """ Briefly, a bug existed in a migration that may have caused message
- attachments to become disassociated with their messages. This management
- command will read through existing message attachments and attempt to
- re-associate them with their original message.
-
- This isn't foolproof, I'm afraid. If an attachment exists twice, it will
- be associated only with the most recent e-mail message. That said,
- I'm quite sure that the bug in the migration is gone (and you'd have to
- have been quite unlucky to have ran the bad migration).
-
- """
- def handle(self, *args, **options):
- attachment_hash_map = {}
-
- attachments_without_messages = MessageAttachment.objects.filter(
- message=None
- ).order_by(
- 'id'
- )
-
- if attachments_without_messages.count() < 1:
- return
-
- for attachment in attachments_without_messages:
- md5 = hashlib.md5()
- for chunk in attachment.document.file.chunks():
- md5.update(chunk)
- attachment_hash_map[md5.hexdigest()] = attachment.pk
-
- for message_record in Message.objects.all().order_by('id'):
- message = email.message_from_string(message_record.body)
- if message.is_multipart():
- for part in message.walk():
- if part.get_content_maintype() == 'multipart':
- continue
- if part.get('Content-Disposition') is None:
- continue
- md5 = hashlib.md5()
- md5.update(part.get_payload(decode=True))
- digest = md5.hexdigest()
- if digest in attachment_hash_map:
- attachment = MessageAttachment.objects.get(
- pk=attachment_hash_map[digest]
- )
- attachment.message = message_record
- attachment.save()
- logger.info(
- "Associated message %s with attachment %s (%s)",
- message_record.pk,
- attachment.pk,
- digest
- )
- else:
- logger.info(
- "%s(%s) not found in currently-stored attachments",
- part.get_filename(),
- digest
- )
diff --git a/build/lib/django_mailbox/migrations/0001_initial.py b/build/lib/django_mailbox/migrations/0001_initial.py
deleted file mode 100644
index 13bb802d..00000000
--- a/build/lib/django_mailbox/migrations/0001_initial.py
+++ /dev/null
@@ -1,56 +0,0 @@
-from django.db import models, migrations
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ]
-
- operations = [
- migrations.CreateModel(
- name='Mailbox',
- fields=[
- ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
- ('name', models.CharField(max_length=255, verbose_name='Name')),
- ('uri', models.CharField(default=None, max_length=255, blank=True, help_text="Example: imap+ssl://myusername:mypassword@someserver
Internet transports include 'imap' and 'pop3'; common local file transports include 'maildir', 'mbox', and less commonly 'babyl', 'mh', and 'mmdf'.
Be sure to urlencode your username and password should they contain illegal characters (like @, :, etc).", null=True, verbose_name='URI')),
- ('from_email', models.CharField(default=None, max_length=255, blank=True, help_text="Example: MailBot <mailbot@yourdomain.com> 'From' header to set for outgoing email.
If you do not use this e-mail inbox for outgoing mail, this setting is unnecessary. If you send e-mail without setting this, your 'From' header will'be set to match the setting `DEFAULT_FROM_EMAIL`.", null=True, verbose_name='From email')),
- ('active', models.BooleanField(default=True, help_text='Check this e-mail inbox for new e-mail messages during polling cycles. This checkbox does not have an effect upon whether mail is collected here when this mailbox receives mail from a pipe, and does not affect whether e-mail messages can be dispatched from this mailbox. ', verbose_name='Active')),
- ],
- options={
- 'verbose_name_plural': 'Mailboxes',
- },
- bases=(models.Model,),
- ),
- migrations.CreateModel(
- name='Message',
- fields=[
- ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
- ('subject', models.CharField(max_length=255, verbose_name='Subject')),
- ('message_id', models.CharField(max_length=255, verbose_name='Message ID')),
- ('from_header', models.CharField(max_length=255, verbose_name='From header')),
- ('to_header', models.TextField(verbose_name='To header')),
- ('outgoing', models.BooleanField(default=False, verbose_name='Outgoing')),
- ('body', models.TextField(verbose_name='Body')),
- ('encoded', models.BooleanField(default=False, help_text='True if the e-mail body is Base64 encoded', verbose_name='Encoded')),
- ('processed', models.DateTimeField(auto_now_add=True, verbose_name='Processed')),
- ('read', models.DateTimeField(default=None, null=True, verbose_name='Read', blank=True)),
- ('in_reply_to', models.ForeignKey(related_name='replies', verbose_name='In reply to', blank=True, to='django_mailbox.Message', null=True, on_delete=models.CASCADE)),
- ('mailbox', models.ForeignKey(related_name='messages', verbose_name='Mailbox', to='django_mailbox.Mailbox', on_delete=models.CASCADE)),
- ],
- options={
- },
- bases=(models.Model,),
- ),
- migrations.CreateModel(
- name='MessageAttachment',
- fields=[
- ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
- ('headers', models.TextField(null=True, verbose_name='Headers', blank=True)),
- ('document', models.FileField(upload_to=b'mailbox_attachments/%Y/%m/%d/', verbose_name='Document')),
- ('message', models.ForeignKey(related_name='attachments', verbose_name='Message', blank=True, to='django_mailbox.Message', null=True, on_delete=models.CASCADE)),
- ],
- options={
- },
- bases=(models.Model,),
- ),
- ]
diff --git a/build/lib/django_mailbox/migrations/0002_add_eml_to_message.py b/build/lib/django_mailbox/migrations/0002_add_eml_to_message.py
deleted file mode 100644
index 4204efb2..00000000
--- a/build/lib/django_mailbox/migrations/0002_add_eml_to_message.py
+++ /dev/null
@@ -1,17 +0,0 @@
-from django.db import models, migrations
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('django_mailbox', '0001_initial'),
- ]
-
- operations = [
- migrations.AddField(
- model_name='message',
- name='eml',
- field=models.FileField(help_text='Original full content of message', upload_to=b'messages', null=True, verbose_name='Message as a file'),
- preserve_default=True,
- ),
- ]
diff --git a/build/lib/django_mailbox/migrations/0003_auto_20150409_0316.py b/build/lib/django_mailbox/migrations/0003_auto_20150409_0316.py
deleted file mode 100644
index 1b5def07..00000000
--- a/build/lib/django_mailbox/migrations/0003_auto_20150409_0316.py
+++ /dev/null
@@ -1,16 +0,0 @@
-from django.db import models, migrations
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('django_mailbox', '0002_add_eml_to_message'),
- ]
-
- operations = [
- migrations.AlterField(
- model_name='message',
- name='eml',
- field=models.FileField(help_text='Original full content of message', upload_to=b'messages', null=True, verbose_name='Raw message contents'),
- ),
- ]
diff --git a/build/lib/django_mailbox/migrations/0004_bytestring_to_unicode.py b/build/lib/django_mailbox/migrations/0004_bytestring_to_unicode.py
deleted file mode 100644
index 7716f2f5..00000000
--- a/build/lib/django_mailbox/migrations/0004_bytestring_to_unicode.py
+++ /dev/null
@@ -1,21 +0,0 @@
-from django.db import models, migrations
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('django_mailbox', '0003_auto_20150409_0316'),
- ]
-
- operations = [
- migrations.AlterField(
- model_name='message',
- name='eml',
- field=models.FileField(verbose_name='Raw message contents', upload_to='messages', null=True, help_text='Original full content of message'),
- ),
- migrations.AlterField(
- model_name='messageattachment',
- name='document',
- field=models.FileField(verbose_name='Document', upload_to='mailbox_attachments/%Y/%m/%d/'),
- ),
- ]
diff --git a/build/lib/django_mailbox/migrations/0005_auto_20160523_2240.py b/build/lib/django_mailbox/migrations/0005_auto_20160523_2240.py
deleted file mode 100644
index f4bdae65..00000000
--- a/build/lib/django_mailbox/migrations/0005_auto_20160523_2240.py
+++ /dev/null
@@ -1,17 +0,0 @@
-from django.db import migrations, models
-import django_mailbox.utils
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('django_mailbox', '0004_bytestring_to_unicode'),
- ]
-
- operations = [
- migrations.AlterField(
- model_name='messageattachment',
- name='document',
- field=models.FileField(upload_to=django_mailbox.utils.get_attachment_save_path, verbose_name='Document'),
- ),
- ]
diff --git a/build/lib/django_mailbox/migrations/0006_mailbox_last_polling.py b/build/lib/django_mailbox/migrations/0006_mailbox_last_polling.py
deleted file mode 100644
index 5f1524ec..00000000
--- a/build/lib/django_mailbox/migrations/0006_mailbox_last_polling.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# Generated by Django 1.9.8 on 2016-08-15 22:39
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('django_mailbox', '0005_auto_20160523_2240'),
- ]
-
- operations = [
- migrations.AddField(
- model_name='mailbox',
- name='last_polling',
- field=models.DateTimeField(blank=True, help_text='The time of last successful polling for messages.It is blank for new mailboxes and is not set for mailboxes that only receive messages via a pipe.', null=True, verbose_name='Last polling'),
- ),
- ]
diff --git a/build/lib/django_mailbox/migrations/0007_auto_20180421_0026.py b/build/lib/django_mailbox/migrations/0007_auto_20180421_0026.py
deleted file mode 100644
index 2e984d0d..00000000
--- a/build/lib/django_mailbox/migrations/0007_auto_20180421_0026.py
+++ /dev/null
@@ -1,25 +0,0 @@
-# Generated by Django 1.10.7 on 2018-04-21 00:26
-
-from django.db import migrations
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('django_mailbox', '0006_mailbox_last_polling'),
- ]
-
- operations = [
- migrations.AlterModelOptions(
- name='mailbox',
- options={'verbose_name': 'Mailbox', 'verbose_name_plural': 'Mailboxes'},
- ),
- migrations.AlterModelOptions(
- name='message',
- options={'verbose_name': 'E-mail message', 'verbose_name_plural': 'E-mail messages'},
- ),
- migrations.AlterModelOptions(
- name='messageattachment',
- options={'verbose_name': 'Message attachment', 'verbose_name_plural': 'Message attachments'},
- ),
- ]
diff --git a/build/lib/django_mailbox/migrations/0008_auto_20190219_1553.py b/build/lib/django_mailbox/migrations/0008_auto_20190219_1553.py
deleted file mode 100644
index e307c258..00000000
--- a/build/lib/django_mailbox/migrations/0008_auto_20190219_1553.py
+++ /dev/null
@@ -1,23 +0,0 @@
-# Generated by Django 2.1.7 on 2019-02-19 14:53
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('django_mailbox', '0007_auto_20180421_0026'),
- ]
-
- operations = [
- migrations.AlterField(
- model_name='mailbox',
- name='active',
- field=models.BooleanField(blank=True, default=True, help_text='Check this e-mail inbox for new e-mail messages during polling cycles. This checkbox does not have an effect upon whether mail is collected here when this mailbox receives mail from a pipe, and does not affect whether e-mail messages can be dispatched from this mailbox. ', verbose_name='Active'),
- ),
- migrations.AlterField(
- model_name='message',
- name='outgoing',
- field=models.BooleanField(blank=True, default=False, verbose_name='Outgoing'),
- ),
- ]
diff --git a/build/lib/django_mailbox/migrations/__init__.py b/build/lib/django_mailbox/migrations/__init__.py
deleted file mode 100644
index e69de29b..00000000
diff --git a/build/lib/django_mailbox/models.py b/build/lib/django_mailbox/models.py
deleted file mode 100644
index cbd3d181..00000000
--- a/build/lib/django_mailbox/models.py
+++ /dev/null
@@ -1,853 +0,0 @@
-#!/usr/bin/env python
-
-"""
-Models declaration for application ``django_mailbox``.
-"""
-import gzip
-from email.encoders import encode_base64
-from email.message import Message as EmailMessage
-from email.utils import formatdate, parseaddr
-from urllib.parse import parse_qs, unquote, urlparse
-from quopri import encode as encode_quopri
-from io import BytesIO
-import base64
-import email
-import logging
-import mimetypes
-import os.path
-import sys
-import uuid
-from tempfile import NamedTemporaryFile
-
-import django
-from django.conf import settings as django_settings
-from django.core.files.base import ContentFile, File
-from django.core.mail.message import make_msgid
-from django.db import models
-from django.utils.translation import gettext_lazy as _
-from django.utils.timezone import now
-
-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, Office365Transport
-
-logger = logging.getLogger(__name__)
-
-
-class MailboxQuerySet(models.QuerySet):
- def get_new_mail(self):
- count = 0
- for mailbox in self.all():
- logger.debug('Receiving mail for %s' % mailbox)
- count += sum(1 for i in mailbox.get_new_mail())
- logger.debug('Received %d %s.', count, 'mails' if count != 1 else 'mail')
-
-
-class MailboxManager(models.Manager):
- def get_queryset(self):
- return MailboxQuerySet(self.model, using=self._db)
-
-
-class ActiveMailboxManager(MailboxManager):
- def get_queryset(self):
- return super().get_queryset().filter(
- active=True,
- )
-
-
-class Mailbox(models.Model):
- name = models.CharField(
- _('Name'),
- max_length=255,
- )
-
- uri = models.CharField(
- _('URI'),
- max_length=255,
- help_text=(_(
- "Example: imap+ssl://myusername:mypassword@someserver "
- " "
- "Internet transports include 'imap' and 'pop3'; "
- "common local file transports include 'maildir', 'mbox', "
- "and less commonly 'babyl', 'mh', and 'mmdf'. "
- " "
- "Be sure to urlencode your username and password should they "
- "contain illegal characters (like @, :, etc)."
- )),
- blank=True,
- null=True,
- default=None,
- )
-
- from_email = models.CharField(
- _('From email'),
- max_length=255,
- help_text=(_(
- "Example: MailBot <mailbot@yourdomain.com> "
- "'From' header to set for outgoing email. "
- " "
- "If you do not use this e-mail inbox for outgoing mail, this "
- "setting is unnecessary. "
- "If you send e-mail without setting this, your 'From' header will'"
- "be set to match the setting `DEFAULT_FROM_EMAIL`."
- )),
- blank=True,
- null=True,
- default=None,
- )
-
- active = models.BooleanField(
- _('Active'),
- help_text=(_(
- "Check this e-mail inbox for new e-mail messages during polling "
- "cycles. This checkbox does not have an effect upon whether "
- "mail is collected here when this mailbox receives mail from a "
- "pipe, and does not affect whether e-mail messages can be "
- "dispatched from this mailbox. "
- )),
- blank=True,
- default=True,
- )
-
- last_polling = models.DateTimeField(
- _("Last polling"),
- help_text=(_("The time of last successful polling for messages."
- "It is blank for new mailboxes and is not set for "
- "mailboxes that only receive messages via a pipe.")),
- blank=True,
- null=True
- )
-
- objects = MailboxManager()
- active_mailboxes = ActiveMailboxManager()
-
- @property
- def _protocol_info(self):
- return urlparse(self.uri)
-
- @property
- def _query_string(self):
- return parse_qs(self._protocol_info.query)
-
- @property
- def _domain(self):
- return self._protocol_info.hostname
-
- @property
- def port(self):
- """Returns the port to use for fetching messages."""
- return self._protocol_info.port
-
- @property
- def username(self):
- """Returns the username to use for fetching messages."""
- return unquote(self._protocol_info.username)
-
- @property
- def password(self):
- """Returns the password to use for fetching messages."""
- return unquote(self._protocol_info.password)
-
- @property
- def location(self):
- """Returns the location (domain and path) of messages."""
- return self._domain if self._domain else '' + self._protocol_info.path
-
- @property
- def type(self):
- """Returns the 'transport' name for this mailbox."""
- scheme = self._protocol_info.scheme.lower()
- if '+' in scheme:
- return scheme.split('+')[0]
- return scheme
-
- @property
- def use_ssl(self):
- """Returns whether or not this mailbox's connection uses SSL."""
- return '+ssl' in self._protocol_info.scheme.lower()
-
- @property
- def use_tls(self):
- """Returns whether or not this mailbox's connection uses STARTTLS."""
- return '+tls' in self._protocol_info.scheme.lower()
-
- @property
- def archive(self):
- """Returns (if specified) the folder to archive messages to."""
- archive_folder = self._query_string.get('archive', None)
- if not archive_folder:
- return None
- return archive_folder[0]
-
- @property
- def folder(self):
- """Returns (if specified) the folder to fetch mail from."""
- folder = self._query_string.get('folder', None)
- if not folder:
- return None
- return folder[0]
-
- @property
- def client_id(self):
- """Returns (if specified) the client id for Office365."""
- client_id = self._query_string.get('client_id', None)
- if not client_id:
- return None
- return client_id[0]
-
- @property
- def client_secret(self):
- """Returns (if specified) the client secret for Office365."""
- client_secret = self._query_string.get('client_secret', None)
- if not client_secret:
- return None
- return client_secret[0]
-
- @property
- def tenant_id(self):
- """Returns (if specified) the tenant id for Office365."""
- tenant_id = self._query_string.get('tenant_id', None)
- if not tenant_id:
- return None
- return tenant_id[0]
-
- def get_connection(self):
- """Returns the transport instance for this mailbox.
-
- These will always be instances of
- `django_mailbox.transports.base.EmailTransport`.
-
- """
- if not self.uri:
- return None
- elif self.type == 'imap':
- conn = ImapTransport(
- self.location,
- port=self.port if self.port else None,
- ssl=self.use_ssl,
- tls=self.use_tls,
- archive=self.archive,
- folder=self.folder
- )
- conn.connect(self.username, self.password)
- elif self.type == 'gmail':
- conn = GmailImapTransport(
- self.location,
- port=self.port if self.port else None,
- ssl=True,
- archive=self.archive
- )
- conn.connect(self.username, self.password)
- elif self.type == 'pop3':
- conn = Pop3Transport(
- self.location,
- port=self.port if self.port else None,
- ssl=self.use_ssl
- )
- conn.connect(self.username, self.password)
- elif self.type == 'office365':
- conn = Office365Transport(
- self.location,
- self.username,
- archive=self.archive,
- folder=self.folder
- )
- conn.connect(self.client_id, self.client_secret, self.tenant_id)
- elif self.type == 'maildir':
- conn = MaildirTransport(self.location)
- elif self.type == 'mbox':
- conn = MboxTransport(self.location)
- elif self.type == 'babyl':
- conn = BabylTransport(self.location)
- elif self.type == 'mh':
- conn = MHTransport(self.location)
- elif self.type == 'mmdf':
- conn = MMDFTransport(self.location)
- return conn
-
- def process_incoming_message(self, message):
- """Process a message incoming to this mailbox."""
- msg = self._process_message(message)
- if msg is None:
- return None
- msg.outgoing = False
- msg.save()
-
- message_received.send(sender=self, message=msg)
-
- return msg
-
- def record_outgoing_message(self, message):
- """Record an outgoing message associated with this mailbox."""
- msg = self._process_message(message)
- if msg is None:
- return None
- msg.outgoing = True
- msg.save()
- return msg
-
- def _get_dehydrated_message(self, msg, record):
- settings = utils.get_settings()
-
- new = EmailMessage()
- if msg.is_multipart():
- for header, value in msg.items():
- new[header] = value
- for part in msg.get_payload():
- new.attach(
- self._get_dehydrated_message(part, record)
- )
- elif (
- settings['strip_unallowed_mimetypes']
- and not msg.get_content_type() in settings['allowed_mimetypes']
- ):
- for header, value in msg.items():
- new[header] = value
- # Delete header, otherwise when attempting to deserialize the
- # payload, it will be expecting a body for this.
- del new['Content-Transfer-Encoding']
- new[settings['altered_message_header']] = (
- 'Stripped; Content type %s not allowed' % (
- msg.get_content_type()
- )
- )
- new.set_payload('')
- elif (
- (
- msg.get_content_type() not in settings['text_stored_mimetypes']
- ) or
- ('attachment' in msg.get('Content-Disposition', ''))
- ):
- filename = None
- raw_filename = msg.get_filename()
- if raw_filename:
- filename = utils.convert_header_to_unicode(raw_filename)
- if not filename:
- extension = mimetypes.guess_extension(msg.get_content_type())
- else:
- _, extension = os.path.splitext(filename)
- if not extension:
- extension = '.bin'
-
- attachment = MessageAttachment()
-
- attachment.document.save(
- uuid.uuid4().hex + extension,
- ContentFile(
- BytesIO(
- msg.get_payload(decode=True)
- ).getvalue()
- )
- )
- attachment.message = record
- for key, value in msg.items():
- attachment[key] = value
- attachment.save()
-
- placeholder = EmailMessage()
- placeholder[
- settings['attachment_interpolation_header']
- ] = str(attachment.pk)
- new = placeholder
- else:
- content_charset = msg.get_content_charset()
- if not content_charset:
- content_charset = 'ascii'
- try:
- # Make sure that the payload can be properly decoded in the
- # defined charset, if it can't, let's mash some things
- # inside the payload :-\
- msg.get_payload(decode=True).decode(content_charset)
- except LookupError:
- logger.warning(
- "Unknown encoding %s; interpreting as ASCII!",
- content_charset
- )
- msg.set_payload(
- msg.get_payload(decode=True).decode(
- 'ascii',
- 'ignore'
- )
- )
- except ValueError:
- logger.warning(
- "Decoding error encountered; interpreting %s as ASCII!",
- content_charset
- )
- msg.set_payload(
- msg.get_payload(decode=True).decode(
- 'ascii',
- 'ignore'
- )
- )
- new = msg
- return new
-
- def _process_message(self, message):
- msg = Message()
- msg._email_object = message
- settings = utils.get_settings()
-
- if settings['store_original_message']:
- self._process_save_original_message(message, msg)
- msg.mailbox = self
- if 'subject' in message:
- msg.subject = (
- utils.convert_header_to_unicode(message['subject'])[0:255]
- )
- if 'message-id' in message:
- msg.message_id = message['message-id'][0:255].strip()
- if 'from' in message:
- msg.from_header = utils.convert_header_to_unicode(message['from'])
- if 'to' in message:
- msg.to_header = utils.convert_header_to_unicode(message['to'])
- elif 'Delivered-To' in message:
- msg.to_header = utils.convert_header_to_unicode(
- message['Delivered-To']
- )
- msg.save()
- message = self._get_dehydrated_message(message, msg)
- 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']:
- try:
- msg.in_reply_to = Message.objects.filter(
- message_id=message['in-reply-to'].strip()
- )[0]
- except IndexError:
- pass
- msg.save()
- return msg
-
- def _process_save_original_message(self, message, msg):
- settings = utils.get_settings()
- if settings['compress_original_message']:
- with NamedTemporaryFile(suffix=".eml.gz") as fp_tmp:
- with gzip.GzipFile(fileobj=fp_tmp, mode="w") as fp:
- fp.write(message.as_string().encode('utf-8'))
- msg.eml.save(
- "{}.eml.gz".format(uuid.uuid4()),
- File(fp_tmp),
- save=False
- )
-
- else:
- msg.eml.save(
- '%s.eml' % uuid.uuid4(),
- ContentFile(message.as_string()),
- save=False
- )
-
- def get_new_mail(self, condition=None):
- """Connect to this transport and fetch new messages."""
- new_mail = []
- connection = self.get_connection()
- if not connection:
- return
- for message in connection.get_message(condition):
- msg = self.process_incoming_message(message)
- if not msg is None:
- yield msg
- self.last_polling = now()
- if django.VERSION >= (1, 5): # Django 1.5 introduces update_fields
- self.save(update_fields=['last_polling'])
- else:
- self.save()
-
- def __str__(self):
- return self.name
-
- class Meta:
- verbose_name = _('Mailbox')
- verbose_name_plural = _('Mailboxes')
-
-
-class IncomingMessageManager(models.Manager):
- def get_queryset(self):
- return super().get_queryset().filter(
- outgoing=False,
- )
-
-
-class OutgoingMessageManager(models.Manager):
- def get_queryset(self):
- return super().get_queryset().filter(
- outgoing=True,
- )
-
-
-class UnreadMessageManager(models.Manager):
- def get_queryset(self):
- return super().get_queryset().filter(
- read=None
- )
-
-
-class Message(models.Model):
- mailbox = models.ForeignKey(
- Mailbox,
- related_name='messages',
- verbose_name=_('Mailbox'),
- on_delete=models.CASCADE
- )
-
- subject = models.CharField(
- _('Subject'),
- max_length=255
- )
-
- message_id = models.CharField(
- _('Message ID'),
- max_length=255
- )
-
- in_reply_to = models.ForeignKey(
- 'django_mailbox.Message',
- related_name='replies',
- blank=True,
- null=True,
- verbose_name=_('In reply to'),
- on_delete=models.CASCADE
- )
-
- from_header = models.CharField(
- _('From header'),
- max_length=255,
- )
-
- to_header = models.TextField(
- _('To header'),
- )
-
- outgoing = models.BooleanField(
- _('Outgoing'),
- default=False,
- blank=True,
- )
-
- body = models.TextField(
- _('Body'),
- )
-
- encoded = models.BooleanField(
- _('Encoded'),
- default=False,
- help_text=_('True if the e-mail body is Base64 encoded'),
- )
-
- processed = models.DateTimeField(
- _('Processed'),
- auto_now_add=True
- )
-
- read = models.DateTimeField(
- _('Read'),
- default=None,
- blank=True,
- null=True,
- )
-
- eml = models.FileField(
- _('Raw message contents'),
- null=True,
- upload_to="messages",
- help_text=_('Original full content of message')
- )
- objects = models.Manager()
- unread_messages = UnreadMessageManager()
- incoming_messages = IncomingMessageManager()
- outgoing_messages = OutgoingMessageManager()
-
- @property
- def address(self):
- """Property allowing one to get the relevant address(es).
-
- In earlier versions of this library, the model had an `address` field
- storing the e-mail address from which a message was received. During
- later refactorings, it became clear that perhaps storing sent messages
- would also be useful, so the address field was replaced with two
- separate fields.
-
- """
- addresses = []
- addresses = self.to_addresses + self.from_address
- return addresses
-
- @property
- def from_address(self):
- """Returns the address (as a list) from which this message was received
-
- .. note::
-
- This was once (and probably should be) a string rather than a list,
- but in a pull request received long, long ago it was changed;
- presumably to make the interface identical to that of
- `to_addresses`.
-
- """
- if self.from_header:
- return [parseaddr(self.from_header)[1].lower()]
- else:
- return []
-
- @property
- def to_addresses(self):
- """Returns a list of addresses to which this message was sent."""
- addresses = []
- for address in self.to_header.split(','):
- if address:
- addresses.append(
- parseaddr(
- address
- )[1].lower()
- )
- return addresses
-
- def reply(self, message):
- """Sends a message as a reply to this message instance.
-
- Although Django's e-mail processing will set both Message-ID
- and Date upon generating the e-mail message, we will not be able
- to retrieve that information through normal channels, so we must
- pre-set it.
-
- """
- if not message.from_email:
- if self.mailbox.from_email:
- message.from_email = self.mailbox.from_email
- else:
- message.from_email = django_settings.DEFAULT_FROM_EMAIL
- message.extra_headers['Message-ID'] = make_msgid()
- message.extra_headers['Date'] = formatdate()
- message.extra_headers['In-Reply-To'] = self.message_id.strip()
- message.send()
- return self.mailbox.record_outgoing_message(
- email.message_from_string(
- message.message().as_string()
- )
- )
-
- @property
- def text(self):
- """
- Returns the message body matching content type 'text/plain'.
- """
- return utils.get_body_from_message(
- self.get_email_object(), 'text', 'plain'
- ).replace('=\n', '').strip()
-
- @property
- def html(self):
- """
- Returns the message body matching content type 'text/html'.
- """
- return utils.get_body_from_message(
- self.get_email_object(), 'text', 'html'
- ).replace('\n', '').strip()
-
- def _rehydrate(self, msg):
- new = EmailMessage()
- settings = utils.get_settings()
-
- if msg.is_multipart():
- for header, value in msg.items():
- new[header] = value
- for part in msg.get_payload():
- new.attach(
- self._rehydrate(part)
- )
- elif settings['attachment_interpolation_header'] in msg.keys():
- try:
- attachment = MessageAttachment.objects.get(
- pk=msg[settings['attachment_interpolation_header']]
- )
- for header, value in attachment.items():
- new[header] = value
- encoding = new['Content-Transfer-Encoding']
- if encoding and encoding.lower() == 'quoted-printable':
- # Cannot use `email.encoders.encode_quopri due to
- # bug 14360: http://bugs.python.org/issue14360
- output = BytesIO()
- encode_quopri(
- BytesIO(
- attachment.document.read()
- ),
- output,
- quotetabs=True,
- header=False,
- )
- new.set_payload(
- output.getvalue().decode().replace(' ', '=20')
- )
- del new['Content-Transfer-Encoding']
- new['Content-Transfer-Encoding'] = 'quoted-printable'
- else:
- new.set_payload(
- attachment.document.read()
- )
- del new['Content-Transfer-Encoding']
- encode_base64(new)
- except MessageAttachment.DoesNotExist:
- new[settings['altered_message_header']] = (
- 'Missing; Attachment %s not found' % (
- msg[settings['attachment_interpolation_header']]
- )
- )
- new.set_payload('')
- else:
- for header, value in msg.items():
- new[header] = value
- new.set_payload(
- msg.get_payload()
- )
- return new
-
- def get_body(self):
- """Returns the `body` field of this record.
-
- This will automatically base64-decode the message contents
- if they are encoded as such.
-
- """
- if self.encoded:
- return base64.b64decode(self.body.encode('ascii'))
- return self.body.encode('utf-8')
-
- def set_body(self, body):
- """Set the `body` field of this record.
-
- This will automatically base64-encode the message contents to
- circumvent a limitation in earlier versions of Django in which
- no fields existed for storing arbitrary bytes.
-
- """
- self.encoded = True
- self.body = base64.b64encode(body.encode('utf-8')).decode('ascii')
-
- def get_email_object(self):
- """Returns an `email.message.EmailMessage` instance representing the
- contents of this message and all attachments.
-
- See [email.message.EmailMessage]_ for more information as to what methods
- and properties are available on `email.message.EmailMessage` instances.
-
- .. note::
-
- Depending upon the storage methods in use (specifically --
- whether ``DJANGO_MAILBOX_STORE_ORIGINAL_MESSAGE`` is set
- to ``True``, this may either create a "rehydrated" message
- using stored attachments, or read the message contents stored
- on-disk.
-
- .. [email.message.EmailMessage] Python's `email.message.EmailMessage` docs
- (https://docs.python.org/3/library/email.message.html)
-
- """
- if not hasattr(self, '_email_object'): # Cache fill
- if self.eml:
- if self.eml.name.endswith('.gz'):
- body = gzip.GzipFile(fileobj=self.eml).read()
- else:
- self.eml.open()
- body = self.eml.file.read()
- self.eml.close()
- else:
- body = self.get_body()
- flat = email.message_from_bytes(body)
- self._email_object = self._rehydrate(flat)
- return self._email_object
-
- def delete(self, *args, **kwargs):
- """Delete this message and all stored attachments."""
- for attachment in self.attachments.all():
- # This attachment is attached only to this message.
- attachment.delete()
- return super().delete(*args, **kwargs)
-
- def __str__(self):
- return self.subject
-
- class Meta:
- verbose_name = _('E-mail message')
- verbose_name_plural = _('E-mail messages')
-
-
-class MessageAttachment(models.Model):
- message = models.ForeignKey(
- Message,
- related_name='attachments',
- null=True,
- blank=True,
- verbose_name=_('Message'),
- on_delete=models.CASCADE
- )
-
- headers = models.TextField(
- _('Headers'),
- null=True,
- blank=True,
- )
-
- document = models.FileField(
- _('Document'),
- upload_to=utils.get_attachment_save_path,
- )
-
- def delete(self, *args, **kwargs):
- """Deletes the attachment."""
- self.document.delete()
- return super().delete(*args, **kwargs)
-
- def _get_rehydrated_headers(self):
- headers = self.headers
- if headers is None:
- return EmailMessage()
- return email.message_from_string(headers)
-
- def _set_dehydrated_headers(self, email_object):
- self.headers = email_object.as_string()
-
- def __delitem__(self, name):
- rehydrated = self._get_rehydrated_headers()
- del rehydrated[name]
- self._set_dehydrated_headers(rehydrated)
-
- def __setitem__(self, name, value):
- rehydrated = self._get_rehydrated_headers()
- rehydrated[name] = value
- self._set_dehydrated_headers(rehydrated)
-
- def get_filename(self):
- """Returns the original filename of this attachment."""
- file_name = self._get_rehydrated_headers().get_filename()
- if isinstance(file_name, str):
- result = utils.convert_header_to_unicode(file_name)
- if result is None:
- return file_name
- return result
- else:
- return None
-
- def items(self):
- return self._get_rehydrated_headers().items()
-
- def __getitem__(self, name):
- value = self._get_rehydrated_headers()[name]
- if value is None:
- raise KeyError('Header %s does not exist' % name)
- return value
-
- def __str__(self):
- return self.document.url
-
- class Meta:
- verbose_name = _('Message attachment')
- verbose_name_plural = _('Message attachments')
diff --git a/build/lib/django_mailbox/signals.py b/build/lib/django_mailbox/signals.py
deleted file mode 100644
index b445183e..00000000
--- a/build/lib/django_mailbox/signals.py
+++ /dev/null
@@ -1,3 +0,0 @@
-from django.dispatch.dispatcher import Signal
-
-message_received = Signal(providing_args=['message'])
diff --git a/build/lib/django_mailbox/south_migrations/0001_initial.py b/build/lib/django_mailbox/south_migrations/0001_initial.py
deleted file mode 100644
index dcfdf602..00000000
--- a/build/lib/django_mailbox/south_migrations/0001_initial.py
+++ /dev/null
@@ -1,58 +0,0 @@
-import datetime
-from south.db import db
-from south.v2 import SchemaMigration
-from django.db import models
-
-
-class Migration(SchemaMigration):
-
- def forwards(self, orm):
- # Adding model 'Mailbox'
- db.create_table('django_mailbox_mailbox', (
- ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
- ('name', self.gf('django.db.models.fields.CharField')(max_length=255)),
- ('uri', self.gf('django.db.models.fields.CharField')(max_length=255)),
- ))
- db.send_create_signal('django_mailbox', ['Mailbox'])
-
- # Adding model 'Message'
- db.create_table('django_mailbox_message', (
- ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
- ('mailbox', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['django_mailbox.Mailbox'])),
- ('subject', self.gf('django.db.models.fields.CharField')(max_length=255)),
- ('message_id', self.gf('django.db.models.fields.CharField')(max_length=255)),
- ('from_address', self.gf('django.db.models.fields.CharField')(max_length=255)),
- ('body', self.gf('django.db.models.fields.TextField')()),
- ('received', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True, blank=True)),
- ))
- db.send_create_signal('django_mailbox', ['Message'])
-
-
- def backwards(self, orm):
- # Deleting model 'Mailbox'
- db.delete_table('django_mailbox_mailbox')
-
- # Deleting model 'Message'
- db.delete_table('django_mailbox_message')
-
-
- models = {
- 'django_mailbox.mailbox': {
- 'Meta': {'object_name': 'Mailbox'},
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
- 'uri': ('django.db.models.fields.CharField', [], {'max_length': '255'})
- },
- 'django_mailbox.message': {
- 'Meta': {'object_name': 'Message'},
- 'body': ('django.db.models.fields.TextField', [], {}),
- 'from_address': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'mailbox': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['django_mailbox.Mailbox']"}),
- 'message_id': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
- 'received': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
- 'subject': ('django.db.models.fields.CharField', [], {'max_length': '255'})
- }
- }
-
- complete_apps = ['django_mailbox']
\ No newline at end of file
diff --git a/build/lib/django_mailbox/south_migrations/0002_auto__chg_field_mailbox_uri.py b/build/lib/django_mailbox/south_migrations/0002_auto__chg_field_mailbox_uri.py
deleted file mode 100644
index 47517b31..00000000
--- a/build/lib/django_mailbox/south_migrations/0002_auto__chg_field_mailbox_uri.py
+++ /dev/null
@@ -1,38 +0,0 @@
-import datetime
-from south.db import db
-from south.v2 import SchemaMigration
-from django.db import models
-
-
-class Migration(SchemaMigration):
-
- def forwards(self, orm):
-
- # Changing field 'Mailbox.uri'
- db.alter_column('django_mailbox_mailbox', 'uri', self.gf('django.db.models.fields.CharField')(max_length=255, null=True))
-
- def backwards(self, orm):
-
- # User chose to not deal with backwards NULL issues for 'Mailbox.uri'
- raise RuntimeError("Cannot reverse this migration. 'Mailbox.uri' and its values cannot be restored.")
-
- models = {
- 'django_mailbox.mailbox': {
- 'Meta': {'object_name': 'Mailbox'},
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
- 'uri': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '255', 'null': 'True', 'blank': 'True'})
- },
- 'django_mailbox.message': {
- 'Meta': {'object_name': 'Message'},
- 'body': ('django.db.models.fields.TextField', [], {}),
- 'from_address': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'mailbox': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'messages'", 'to': "orm['django_mailbox.Mailbox']"}),
- 'message_id': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
- 'received': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
- 'subject': ('django.db.models.fields.CharField', [], {'max_length': '255'})
- }
- }
-
- complete_apps = ['django_mailbox']
\ No newline at end of file
diff --git a/build/lib/django_mailbox/south_migrations/0003_auto__add_field_mailbox_active.py b/build/lib/django_mailbox/south_migrations/0003_auto__add_field_mailbox_active.py
deleted file mode 100644
index 30e43542..00000000
--- a/build/lib/django_mailbox/south_migrations/0003_auto__add_field_mailbox_active.py
+++ /dev/null
@@ -1,41 +0,0 @@
-import datetime
-from south.db import db
-from south.v2 import SchemaMigration
-from django.db import models
-
-
-class Migration(SchemaMigration):
-
- def forwards(self, orm):
- # Adding field 'Mailbox.active'
- db.add_column('django_mailbox_mailbox', 'active',
- self.gf('django.db.models.fields.BooleanField')(default=True),
- keep_default=False)
-
-
- def backwards(self, orm):
- # Deleting field 'Mailbox.active'
- db.delete_column('django_mailbox_mailbox', 'active')
-
-
- models = {
- 'django_mailbox.mailbox': {
- 'Meta': {'object_name': 'Mailbox'},
- 'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
- 'uri': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '255', 'null': 'True', 'blank': 'True'})
- },
- 'django_mailbox.message': {
- 'Meta': {'object_name': 'Message'},
- 'body': ('django.db.models.fields.TextField', [], {}),
- 'from_address': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'mailbox': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'messages'", 'to': "orm['django_mailbox.Mailbox']"}),
- 'message_id': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
- 'received': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
- 'subject': ('django.db.models.fields.CharField', [], {'max_length': '255'})
- }
- }
-
- complete_apps = ['django_mailbox']
\ No newline at end of file
diff --git a/build/lib/django_mailbox/south_migrations/0004_auto__add_field_message_outgoing.py b/build/lib/django_mailbox/south_migrations/0004_auto__add_field_message_outgoing.py
deleted file mode 100644
index 245c43bb..00000000
--- a/build/lib/django_mailbox/south_migrations/0004_auto__add_field_message_outgoing.py
+++ /dev/null
@@ -1,42 +0,0 @@
-import datetime
-from south.db import db
-from south.v2 import SchemaMigration
-from django.db import models
-
-
-class Migration(SchemaMigration):
-
- def forwards(self, orm):
- # Adding field 'Message.outgoing'
- db.add_column('django_mailbox_message', 'outgoing',
- self.gf('django.db.models.fields.BooleanField')(default=False),
- keep_default=False)
-
-
- def backwards(self, orm):
- # Deleting field 'Message.outgoing'
- db.delete_column('django_mailbox_message', 'outgoing')
-
-
- models = {
- 'django_mailbox.mailbox': {
- 'Meta': {'object_name': 'Mailbox'},
- 'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
- 'uri': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '255', 'null': 'True', 'blank': 'True'})
- },
- 'django_mailbox.message': {
- 'Meta': {'object_name': 'Message'},
- 'body': ('django.db.models.fields.TextField', [], {}),
- 'from_address': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'mailbox': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'messages'", 'to': "orm['django_mailbox.Mailbox']"}),
- 'message_id': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
- 'outgoing': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
- 'received': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
- 'subject': ('django.db.models.fields.CharField', [], {'max_length': '255'})
- }
- }
-
- complete_apps = ['django_mailbox']
diff --git a/build/lib/django_mailbox/south_migrations/0005_rename_fields.py b/build/lib/django_mailbox/south_migrations/0005_rename_fields.py
deleted file mode 100644
index 72cb4372..00000000
--- a/build/lib/django_mailbox/south_migrations/0005_rename_fields.py
+++ /dev/null
@@ -1,38 +0,0 @@
-import datetime
-from south.db import db
-from south.v2 import SchemaMigration
-from django.db import models
-
-
-class Migration(SchemaMigration):
-
- def forwards(self, orm):
- db.rename_column('django_mailbox_message', 'from_address', 'address')
- db.rename_column('django_mailbox_message', 'received', 'processed')
-
- def backwards(self, orm):
- db.rename_column('django_mailbox_message', 'address', 'from_address')
- db.rename_column('django_mailbox_message', 'processed', 'received')
-
- models = {
- 'django_mailbox.mailbox': {
- 'Meta': {'object_name': 'Mailbox'},
- 'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
- 'uri': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '255', 'null': 'True', 'blank': 'True'})
- },
- 'django_mailbox.message': {
- 'Meta': {'object_name': 'Message'},
- 'address': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
- 'body': ('django.db.models.fields.TextField', [], {}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'mailbox': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'messages'", 'to': "orm['django_mailbox.Mailbox']"}),
- 'message_id': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
- 'outgoing': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
- 'processed': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
- 'subject': ('django.db.models.fields.CharField', [], {'max_length': '255'})
- }
- }
-
- complete_apps = ['django_mailbox']
diff --git a/build/lib/django_mailbox/south_migrations/0006_auto__add_field_message_in_reply_to.py b/build/lib/django_mailbox/south_migrations/0006_auto__add_field_message_in_reply_to.py
deleted file mode 100644
index 4287fb26..00000000
--- a/build/lib/django_mailbox/south_migrations/0006_auto__add_field_message_in_reply_to.py
+++ /dev/null
@@ -1,55 +0,0 @@
-import datetime
-from south.db import db
-from south.v2 import SchemaMigration
-from django.db import models
-
-
-class Migration(SchemaMigration):
-
- def forwards(self, orm):
- # Adding field 'Message.in_reply_to'
- db.add_column('django_mailbox_message', 'in_reply_to',
- self.gf('django.db.models.fields.related.ForeignKey')(blank=True, related_name='replies', null=True, to=orm['django_mailbox.Message']),
- keep_default=False)
-
- # 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))
- ))
- db.create_unique('django_mailbox_message_references', ['from_message_id', 'to_message_id'])
-
-
- def backwards(self, orm):
- # Deleting field 'Message.in_reply_to'
- db.delete_column('django_mailbox_message', 'in_reply_to_id')
-
- # Removing M2M table for field references on 'Message'
- db.delete_table('django_mailbox_message_references')
-
-
- models = {
- 'django_mailbox.mailbox': {
- 'Meta': {'object_name': 'Mailbox'},
- 'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
- 'uri': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '255', 'null': 'True', 'blank': 'True'})
- },
- 'django_mailbox.message': {
- 'Meta': {'object_name': 'Message'},
- 'address': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
- 'body': ('django.db.models.fields.TextField', [], {}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'in_reply_to': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'replies'", 'null': 'True', 'to': "orm['django_mailbox.Message']"}),
- 'mailbox': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'messages'", 'to': "orm['django_mailbox.Mailbox']"}),
- 'message_id': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
- 'outgoing': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
- 'processed': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
- 'references': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'referenced_by'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['django_mailbox.Message']"}),
- 'subject': ('django.db.models.fields.CharField', [], {'max_length': '255'})
- }
- }
-
- complete_apps = ['django_mailbox']
\ No newline at end of file
diff --git a/build/lib/django_mailbox/south_migrations/0007_auto__del_field_message_address__add_field_message_from_header__add_fi.py b/build/lib/django_mailbox/south_migrations/0007_auto__del_field_message_address__add_field_message_from_header__add_fi.py
deleted file mode 100644
index cfa7d6f0..00000000
--- a/build/lib/django_mailbox/south_migrations/0007_auto__del_field_message_address__add_field_message_from_header__add_fi.py
+++ /dev/null
@@ -1,59 +0,0 @@
-import datetime
-from south.db import db
-from south.v2 import SchemaMigration
-from django.db import models
-
-
-class Migration(SchemaMigration):
-
- def forwards(self, orm):
- # Deleting field 'Message.address'
- db.delete_column('django_mailbox_message', 'address')
-
- # Adding field 'Message.from_header'
- db.add_column('django_mailbox_message', 'from_header',
- self.gf('django.db.models.fields.CharField')(default='', max_length=255),
- keep_default=False)
-
- # Adding field 'Message.to_header'
- db.add_column('django_mailbox_message', 'to_header',
- self.gf('django.db.models.fields.TextField')(default=''),
- keep_default=False)
-
-
- def backwards(self, orm):
-
- # User chose to not deal with backwards NULL issues for 'Message.address'
- raise RuntimeError("Cannot reverse this migration. 'Message.address' and its values cannot be restored.")
- # Deleting field 'Message.from_header'
- db.delete_column('django_mailbox_message', 'from_header')
-
- # Deleting field 'Message.to_header'
- db.delete_column('django_mailbox_message', 'to_header')
-
-
- models = {
- 'django_mailbox.mailbox': {
- 'Meta': {'object_name': 'Mailbox'},
- 'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
- 'uri': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '255', 'null': 'True', 'blank': 'True'})
- },
- 'django_mailbox.message': {
- 'Meta': {'object_name': 'Message'},
- 'body': ('django.db.models.fields.TextField', [], {}),
- 'from_header': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'in_reply_to': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'replies'", 'null': 'True', 'to': "orm['django_mailbox.Message']"}),
- 'mailbox': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'messages'", 'to': "orm['django_mailbox.Mailbox']"}),
- 'message_id': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
- 'outgoing': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
- 'processed': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
- 'references': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'referenced_by'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['django_mailbox.Message']"}),
- 'subject': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
- 'to_header': ('django.db.models.fields.TextField', [], {})
- }
- }
-
- complete_apps = ['django_mailbox']
\ No newline at end of file
diff --git a/build/lib/django_mailbox/south_migrations/0008_populate_from_to_fields.py b/build/lib/django_mailbox/south_migrations/0008_populate_from_to_fields.py
deleted file mode 100644
index e047f223..00000000
--- a/build/lib/django_mailbox/south_migrations/0008_populate_from_to_fields.py
+++ /dev/null
@@ -1,46 +0,0 @@
-import datetime
-import email
-from south.db import db
-from south.v2 import DataMigration
-from django.db import models
-
-class Migration(DataMigration):
-
- def forwards(self, orm):
- for message in orm['django_mailbox.message'].objects.all():
- msg_object = email.message_from_string(
- message.body
- )
- message.from_header = msg_object['from']
- message.to_header = msg_object['to']
- message.save()
-
- def backwards(self, orm):
- raise RuntimeError('Cannot reverse this migration.')
-
- models = {
- 'django_mailbox.mailbox': {
- 'Meta': {'object_name': 'Mailbox'},
- 'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
- 'uri': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '255', 'null': 'True', 'blank': 'True'})
- },
- 'django_mailbox.message': {
- 'Meta': {'object_name': 'Message'},
- 'body': ('django.db.models.fields.TextField', [], {}),
- 'from_header': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'in_reply_to': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'replies'", 'null': 'True', 'to': "orm['django_mailbox.Message']"}),
- 'mailbox': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'messages'", 'to': "orm['django_mailbox.Mailbox']"}),
- 'message_id': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
- 'outgoing': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
- 'processed': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
- 'references': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'referenced_by'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['django_mailbox.Message']"}),
- 'subject': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
- 'to_header': ('django.db.models.fields.TextField', [], {})
- }
- }
-
- complete_apps = ['django_mailbox']
- symmetrical = True
diff --git a/build/lib/django_mailbox/south_migrations/0009_remove_references_table.py b/build/lib/django_mailbox/south_migrations/0009_remove_references_table.py
deleted file mode 100644
index 38685851..00000000
--- a/build/lib/django_mailbox/south_migrations/0009_remove_references_table.py
+++ /dev/null
@@ -1,47 +0,0 @@
-import datetime
-from south.db import db
-from south.v2 import SchemaMigration
-from django.db import models
-
-
-class Migration(SchemaMigration):
-
- def forwards(self, orm):
- # Removing M2M table for field references on 'Message'
- db.delete_table('django_mailbox_message_references')
-
-
- 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))
- ))
- db.create_unique('django_mailbox_message_references', ['from_message_id', 'to_message_id'])
-
-
- models = {
- 'django_mailbox.mailbox': {
- 'Meta': {'object_name': 'Mailbox'},
- 'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
- 'uri': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '255', 'null': 'True', 'blank': 'True'})
- },
- 'django_mailbox.message': {
- 'Meta': {'object_name': 'Message'},
- 'body': ('django.db.models.fields.TextField', [], {}),
- 'from_header': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'in_reply_to': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'replies'", 'null': 'True', 'to': "orm['django_mailbox.Message']"}),
- 'mailbox': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'messages'", 'to': "orm['django_mailbox.Mailbox']"}),
- 'message_id': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
- 'outgoing': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
- 'processed': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
- 'subject': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
- 'to_header': ('django.db.models.fields.TextField', [], {})
- }
- }
-
- complete_apps = ['django_mailbox']
diff --git a/build/lib/django_mailbox/south_migrations/0010_auto__add_field_mailbox_from_email.py b/build/lib/django_mailbox/south_migrations/0010_auto__add_field_mailbox_from_email.py
deleted file mode 100644
index a988e5eb..00000000
--- a/build/lib/django_mailbox/south_migrations/0010_auto__add_field_mailbox_from_email.py
+++ /dev/null
@@ -1,45 +0,0 @@
-import datetime
-from south.db import db
-from south.v2 import SchemaMigration
-from django.db import models
-
-
-class Migration(SchemaMigration):
-
- def forwards(self, orm):
- # Adding field 'Mailbox.from_email'
- db.add_column('django_mailbox_mailbox', 'from_email',
- self.gf('django.db.models.fields.CharField')(default=None, max_length=255, null=True, blank=True),
- keep_default=False)
-
-
- def backwards(self, orm):
- # Deleting field 'Mailbox.from_email'
- db.delete_column('django_mailbox_mailbox', 'from_email')
-
-
- models = {
- 'django_mailbox.mailbox': {
- 'Meta': {'object_name': 'Mailbox'},
- 'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
- 'from_email': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '255', 'null': 'True', 'blank': 'True'}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
- 'uri': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '255', 'null': 'True', 'blank': 'True'})
- },
- 'django_mailbox.message': {
- 'Meta': {'object_name': 'Message'},
- 'body': ('django.db.models.fields.TextField', [], {}),
- 'from_header': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'in_reply_to': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'replies'", 'null': 'True', 'to': "orm['django_mailbox.Message']"}),
- 'mailbox': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'messages'", 'to': "orm['django_mailbox.Mailbox']"}),
- 'message_id': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
- 'outgoing': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
- 'processed': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
- 'subject': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
- 'to_header': ('django.db.models.fields.TextField', [], {})
- }
- }
-
- complete_apps = ['django_mailbox']
\ No newline at end of file
diff --git a/build/lib/django_mailbox/south_migrations/0011_auto__add_field_message_read.py b/build/lib/django_mailbox/south_migrations/0011_auto__add_field_message_read.py
deleted file mode 100644
index c75db341..00000000
--- a/build/lib/django_mailbox/south_migrations/0011_auto__add_field_message_read.py
+++ /dev/null
@@ -1,46 +0,0 @@
-import datetime
-from south.db import db
-from south.v2 import SchemaMigration
-from django.db import models
-
-
-class Migration(SchemaMigration):
-
- def forwards(self, orm):
- # Adding field 'Message.read'
- db.add_column('django_mailbox_message', 'read',
- self.gf('django.db.models.fields.DateTimeField')(default=None, null=True, blank=True),
- keep_default=False)
-
-
- def backwards(self, orm):
- # Deleting field 'Message.read'
- db.delete_column('django_mailbox_message', 'read')
-
-
- models = {
- 'django_mailbox.mailbox': {
- 'Meta': {'object_name': 'Mailbox'},
- 'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
- 'from_email': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '255', 'null': 'True', 'blank': 'True'}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
- 'uri': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '255', 'null': 'True', 'blank': 'True'})
- },
- 'django_mailbox.message': {
- 'Meta': {'object_name': 'Message'},
- 'body': ('django.db.models.fields.TextField', [], {}),
- 'from_header': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'in_reply_to': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'replies'", 'null': 'True', 'to': "orm['django_mailbox.Message']"}),
- 'mailbox': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'messages'", 'to': "orm['django_mailbox.Mailbox']"}),
- 'message_id': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
- 'outgoing': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
- 'processed': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
- 'read': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True', 'blank': 'True'}),
- 'subject': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
- 'to_header': ('django.db.models.fields.TextField', [], {})
- }
- }
-
- complete_apps = ['django_mailbox']
\ No newline at end of file
diff --git a/build/lib/django_mailbox/south_migrations/0012_auto__add_messageattachment.py b/build/lib/django_mailbox/south_migrations/0012_auto__add_messageattachment.py
deleted file mode 100644
index b62e50e9..00000000
--- a/build/lib/django_mailbox/south_migrations/0012_auto__add_messageattachment.py
+++ /dev/null
@@ -1,66 +0,0 @@
-import datetime
-from south.db import db
-from south.v2 import SchemaMigration
-from django.db import models
-
-class Migration(SchemaMigration):
-
- def forwards(self, orm):
-
- # Adding model 'MessageAttachment'
- db.create_table('django_mailbox_messageattachment', (
- ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
- ('document', self.gf('django.db.models.fields.files.FileField')(max_length=100)),
- ))
- db.send_create_signal('django_mailbox', ['MessageAttachment'])
-
- # 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))
- ))
- db.create_unique('django_mailbox_message_attachments', ['message_id', 'messageattachment_id'])
-
-
- def backwards(self, orm):
-
- # Deleting model 'MessageAttachment'
- db.delete_table('django_mailbox_messageattachment')
-
- # Removing M2M table for field attachments on 'Message'
- db.delete_table('django_mailbox_message_attachments')
-
-
- models = {
- 'django_mailbox.mailbox': {
- 'Meta': {'object_name': 'Mailbox'},
- 'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
- 'from_email': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '255', 'null': 'True', 'blank': 'True'}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
- 'uri': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '255', 'null': 'True', 'blank': 'True'})
- },
- 'django_mailbox.message': {
- 'Meta': {'object_name': 'Message'},
- 'attachments': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['django_mailbox.MessageAttachment']", 'symmetrical': 'False'}),
- 'body': ('django.db.models.fields.TextField', [], {}),
- 'from_header': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'in_reply_to': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'replies'", 'null': 'True', 'to': "orm['django_mailbox.Message']"}),
- 'mailbox': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'messages'", 'to': "orm['django_mailbox.Mailbox']"}),
- 'message_id': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
- 'outgoing': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
- 'processed': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
- 'read': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True', 'blank': 'True'}),
- 'subject': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
- 'to_header': ('django.db.models.fields.TextField', [], {})
- },
- 'django_mailbox.messageattachment': {
- 'Meta': {'object_name': 'MessageAttachment'},
- 'document': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
- }
- }
-
- complete_apps = ['django_mailbox']
diff --git a/build/lib/django_mailbox/south_migrations/0013_auto__add_field_messageattachment_message.py b/build/lib/django_mailbox/south_migrations/0013_auto__add_field_messageattachment_message.py
deleted file mode 100644
index 01c80a2e..00000000
--- a/build/lib/django_mailbox/south_migrations/0013_auto__add_field_messageattachment_message.py
+++ /dev/null
@@ -1,53 +0,0 @@
-import datetime
-from south.db import db
-from south.v2 import SchemaMigration
-from django.db import models
-
-
-class Migration(SchemaMigration):
-
- def forwards(self, orm):
- # Adding field 'MessageAttachment.message'
- db.add_column('django_mailbox_messageattachment', 'message',
- self.gf('django.db.models.fields.related.ForeignKey')(blank=True, related_name='attachments', null=True, to=orm['django_mailbox.Message']),
- keep_default=False)
-
-
- def backwards(self, orm):
- # Deleting field 'MessageAttachment.message'
- db.delete_column('django_mailbox_messageattachment', 'message_id')
-
-
- models = {
- 'django_mailbox.mailbox': {
- 'Meta': {'object_name': 'Mailbox'},
- 'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
- 'from_email': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '255', 'null': 'True', 'blank': 'True'}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
- 'uri': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '255', 'null': 'True', 'blank': 'True'})
- },
- 'django_mailbox.message': {
- 'Meta': {'object_name': 'Message'},
- 'attachments': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'messages_old'", 'blank': 'True', 'to': "orm['django_mailbox.MessageAttachment']"}),
- 'body': ('django.db.models.fields.TextField', [], {}),
- 'from_header': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'in_reply_to': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'replies'", 'null': 'True', 'to': "orm['django_mailbox.Message']"}),
- 'mailbox': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'messages'", 'to': "orm['django_mailbox.Mailbox']"}),
- 'message_id': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
- 'outgoing': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
- 'processed': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
- 'read': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True', 'blank': 'True'}),
- 'subject': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
- 'to_header': ('django.db.models.fields.TextField', [], {})
- },
- 'django_mailbox.messageattachment': {
- 'Meta': {'object_name': 'MessageAttachment'},
- 'document': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'message': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'attachments_new'", 'null': 'True', 'to': "orm['django_mailbox.Message']"})
- }
- }
-
- complete_apps = ['django_mailbox']
diff --git a/build/lib/django_mailbox/south_migrations/0014_migrate_message_attachments.py b/build/lib/django_mailbox/south_migrations/0014_migrate_message_attachments.py
deleted file mode 100644
index b018915e..00000000
--- a/build/lib/django_mailbox/south_migrations/0014_migrate_message_attachments.py
+++ /dev/null
@@ -1,54 +0,0 @@
-import datetime
-from south.db import db
-from south.v2 import SchemaMigration
-from django.db import models
-
-
-class Migration(SchemaMigration):
- no_dry_run = True
- def forwards(self, orm):
- for message in orm['django_mailbox.Message'].objects.all():
- for attachment in message.attachments.all():
- attachment.message = message
- attachment.save()
-
- def backwards(self, orm):
- for attachment in orm['django_mailbox.MessageAttachment'].objects.all():
- if attachment.message:
- attachment.message.attachments.add(
- attachment
- )
-
- models = {
- 'django_mailbox.mailbox': {
- 'Meta': {'object_name': 'Mailbox'},
- 'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
- 'from_email': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '255', 'null': 'True', 'blank': 'True'}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
- 'uri': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '255', 'null': 'True', 'blank': 'True'})
- },
- 'django_mailbox.message': {
- 'Meta': {'object_name': 'Message'},
- 'attachments': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'messages_old'", 'blank': 'True', 'to': "orm['django_mailbox.MessageAttachment']"}),
- 'body': ('django.db.models.fields.TextField', [], {}),
- 'from_header': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'in_reply_to': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'replies'", 'null': 'True', 'to': "orm['django_mailbox.Message']"}),
- 'mailbox': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'messages'", 'to': "orm['django_mailbox.Mailbox']"}),
- 'message_id': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
- 'outgoing': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
- 'processed': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
- 'read': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True', 'blank': 'True'}),
- 'subject': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
- 'to_header': ('django.db.models.fields.TextField', [], {})
- },
- 'django_mailbox.messageattachment': {
- 'Meta': {'object_name': 'MessageAttachment'},
- 'document': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'message': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'attachments_new'", 'null': 'True', 'to': "orm['django_mailbox.Message']"})
- }
- }
-
- complete_apps = ['django_mailbox']
diff --git a/build/lib/django_mailbox/south_migrations/0015_auto__add_field_messageattachment_headers.py b/build/lib/django_mailbox/south_migrations/0015_auto__add_field_messageattachment_headers.py
deleted file mode 100644
index 71ecfbdc..00000000
--- a/build/lib/django_mailbox/south_migrations/0015_auto__add_field_messageattachment_headers.py
+++ /dev/null
@@ -1,64 +0,0 @@
-import datetime
-from south.db import db
-from south.v2 import SchemaMigration
-from django.db import models
-
-
-class Migration(SchemaMigration):
-
- def forwards(self, orm):
- # Removing M2M table for field attachments on 'Message'
- db.delete_table('django_mailbox_message_attachments')
-
- # Adding field 'MessageAttachment.headers'
- db.add_column('django_mailbox_messageattachment', 'headers',
- self.gf('django.db.models.fields.TextField')(null=True, blank=True),
- keep_default=False)
-
-
- 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))
- ))
- db.create_unique('django_mailbox_message_attachments', ['message_id', 'messageattachment_id'])
-
- # Deleting field 'MessageAttachment.headers'
- db.delete_column('django_mailbox_messageattachment', 'headers')
-
-
- models = {
- 'django_mailbox.mailbox': {
- 'Meta': {'object_name': 'Mailbox'},
- 'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
- 'from_email': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '255', 'null': 'True', 'blank': 'True'}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
- 'uri': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '255', 'null': 'True', 'blank': 'True'})
- },
- 'django_mailbox.message': {
- 'Meta': {'object_name': 'Message'},
- 'body': ('django.db.models.fields.TextField', [], {}),
- 'from_header': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'in_reply_to': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'replies'", 'null': 'True', 'to': "orm['django_mailbox.Message']"}),
- 'mailbox': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'messages'", 'to': "orm['django_mailbox.Mailbox']"}),
- 'message_id': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
- 'outgoing': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
- 'processed': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
- 'read': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True', 'blank': 'True'}),
- 'subject': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
- 'to_header': ('django.db.models.fields.TextField', [], {})
- },
- 'django_mailbox.messageattachment': {
- 'Meta': {'object_name': 'MessageAttachment'},
- 'document': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}),
- 'headers': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'message': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'attachments'", 'null': 'True', 'to': "orm['django_mailbox.Message']"})
- }
- }
-
- complete_apps = ['django_mailbox']
\ No newline at end of file
diff --git a/build/lib/django_mailbox/south_migrations/0016_auto__add_field_message_encoded.py b/build/lib/django_mailbox/south_migrations/0016_auto__add_field_message_encoded.py
deleted file mode 100644
index cd4c6787..00000000
--- a/build/lib/django_mailbox/south_migrations/0016_auto__add_field_message_encoded.py
+++ /dev/null
@@ -1,54 +0,0 @@
-import datetime
-from south.db import db
-from south.v2 import SchemaMigration
-from django.db import models
-
-
-class Migration(SchemaMigration):
-
- def forwards(self, orm):
- # Adding field 'Message.encoded'
- db.add_column('django_mailbox_message', 'encoded',
- self.gf('django.db.models.fields.BooleanField')(default=False),
- keep_default=False)
-
-
- def backwards(self, orm):
- # Deleting field 'Message.encoded'
- db.delete_column('django_mailbox_message', 'encoded')
-
-
- models = {
- 'django_mailbox.mailbox': {
- 'Meta': {'object_name': 'Mailbox'},
- 'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
- 'from_email': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '255', 'null': 'True', 'blank': 'True'}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
- 'uri': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '255', 'null': 'True', 'blank': 'True'})
- },
- 'django_mailbox.message': {
- 'Meta': {'object_name': 'Message'},
- 'body': ('django.db.models.fields.TextField', [], {}),
- 'encoded': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
- 'from_header': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'in_reply_to': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'replies'", 'null': 'True', 'to': "orm['django_mailbox.Message']"}),
- 'mailbox': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'messages'", 'to': "orm['django_mailbox.Mailbox']"}),
- 'message_id': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
- 'outgoing': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
- 'processed': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
- 'read': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True', 'blank': 'True'}),
- 'subject': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
- 'to_header': ('django.db.models.fields.TextField', [], {})
- },
- 'django_mailbox.messageattachment': {
- 'Meta': {'object_name': 'MessageAttachment'},
- 'document': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}),
- 'headers': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'message': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'attachments'", 'null': 'True', 'to': "orm['django_mailbox.Message']"})
- }
- }
-
- complete_apps = ['django_mailbox']
\ No newline at end of file
diff --git a/build/lib/django_mailbox/south_migrations/0017_auto__add_field_message_eml.py b/build/lib/django_mailbox/south_migrations/0017_auto__add_field_message_eml.py
deleted file mode 100644
index 186c8bd4..00000000
--- a/build/lib/django_mailbox/south_migrations/0017_auto__add_field_message_eml.py
+++ /dev/null
@@ -1,55 +0,0 @@
-from south.utils import datetime_utils as datetime
-from south.db import db
-from south.v2 import SchemaMigration
-from django.db import models
-
-
-class Migration(SchemaMigration):
-
- def forwards(self, orm):
- # Adding field 'Message.eml'
- db.add_column('django_mailbox_message', 'eml',
- self.gf('django.db.models.fields.files.FileField')(max_length=100, null=True),
- keep_default=False)
-
-
- def backwards(self, orm):
- # Deleting field 'Message.eml'
- db.delete_column('django_mailbox_message', 'eml')
-
-
- models = {
- 'django_mailbox.mailbox': {
- 'Meta': {'object_name': 'Mailbox'},
- 'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
- 'from_email': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '255', 'null': 'True', 'blank': 'True'}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
- 'uri': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '255', 'null': 'True', 'blank': 'True'})
- },
- 'django_mailbox.message': {
- 'Meta': {'object_name': 'Message'},
- 'body': ('django.db.models.fields.TextField', [], {}),
- 'eml': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'null': 'True'}),
- 'encoded': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
- 'from_header': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'in_reply_to': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'replies'", 'null': 'True', 'to': "orm['django_mailbox.Message']"}),
- 'mailbox': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'messages'", 'to': "orm['django_mailbox.Mailbox']"}),
- 'message_id': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
- 'outgoing': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
- 'processed': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
- 'read': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True', 'blank': 'True'}),
- 'subject': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
- 'to_header': ('django.db.models.fields.TextField', [], {})
- },
- 'django_mailbox.messageattachment': {
- 'Meta': {'object_name': 'MessageAttachment'},
- 'document': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}),
- 'headers': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'message': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'attachments'", 'null': 'True', 'to': "orm['django_mailbox.Message']"})
- }
- }
-
- complete_apps = ['django_mailbox']
\ No newline at end of file
diff --git a/build/lib/django_mailbox/south_migrations/__init__.py b/build/lib/django_mailbox/south_migrations/__init__.py
deleted file mode 100644
index e69de29b..00000000
diff --git a/build/lib/django_mailbox/tests/__init__.py b/build/lib/django_mailbox/tests/__init__.py
deleted file mode 100644
index e69de29b..00000000
diff --git a/build/lib/django_mailbox/tests/base.py b/build/lib/django_mailbox/tests/base.py
deleted file mode 100644
index b1215c5e..00000000
--- a/build/lib/django_mailbox/tests/base.py
+++ /dev/null
@@ -1,191 +0,0 @@
-import email
-import os.path
-import time
-
-from django.conf import settings
-from django.test import TestCase
-
-from django_mailbox import models, utils
-from django_mailbox.models import Mailbox, Message
-
-
-class EmailIntegrationTimeout(Exception):
- pass
-
-
-def get_email_as_text(name):
- with open(
- os.path.join(
- os.path.dirname(__file__),
- 'messages',
- name,
- ),
- 'rb'
- ) as f:
- return f.read()
-
-
-class EmailMessageTestCase(TestCase):
- ALLOWED_EXTRA_HEADERS = [
- 'MIME-Version',
- 'Content-Transfer-Encoding',
- ]
-
- def setUp(self):
- dm_settings = utils.get_settings()
-
- self._ALLOWED_MIMETYPES = dm_settings['allowed_mimetypes']
- self._STRIP_UNALLOWED_MIMETYPES = (
- dm_settings['strip_unallowed_mimetypes']
- )
- self._TEXT_STORED_MIMETYPES = dm_settings['text_stored_mimetypes']
-
- self.mailbox = Mailbox.objects.create(from_email='from@example.com')
-
- self.test_account = os.environ.get('EMAIL_ACCOUNT')
- self.test_password = os.environ.get('EMAIL_PASSWORD')
- self.test_smtp_server = os.environ.get('EMAIL_SMTP_SERVER')
- self.test_from_email = 'nobody@nowhere.com'
-
- self.maximum_wait_seconds = 60 * 5
-
- settings.EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
- settings.EMAIL_HOST = self.test_smtp_server
- settings.EMAIL_PORT = 587
- settings.EMAIL_HOST_USER = self.test_account
- settings.EMAIL_HOST_PASSWORD = self.test_password
- settings.EMAIL_USE_TLS = True
- super().setUp()
-
- def _get_new_messages(self, mailbox, condition=None):
- start_time = time.time()
- # wait until there is at least one message
- while time.time() - start_time < self.maximum_wait_seconds:
-
- messages = self.mailbox.get_new_mail(condition)
-
- try:
- # check if generator contains at least one element
- message = next(messages)
- yield message
- yield from messages
- return
-
- except StopIteration:
- time.sleep(5)
-
- raise EmailIntegrationTimeout()
-
- def _get_email_as_text(self, name):
- with open(
- os.path.join(
- os.path.dirname(__file__),
- 'messages',
- name,
- ),
- 'rb'
- ) as f:
- return f.read()
-
- def _get_email_object(self, name):
- copy = self._get_email_as_text(name)
- return email.message_from_bytes(copy)
-
- def _headers_identical(self, left, right, header=None):
- """ Check if headers are (close enough to) identical.
-
- * This is particularly tricky because Python 2.6, Python 2.7 and
- Python 3 each handle header strings slightly differently. This
- should mash away all of the differences, though.
- * This also has a small loophole in that when re-writing e-mail
- payload encodings, we re-build the Content-Type header, so if the
- header was originally unquoted, it will be quoted when rehydrating
- the e-mail message.
-
- """
- if header.lower() == 'content-type':
- # Special case; given that we re-write the header, we'll be quoting
- # the new content type; we need to make sure that doesn't cause
- # this comparison to fail. Also, the case of the encoding could
- # be changed, etc. etc. etc.
- left = left.replace('"', '').upper()
- right = right.replace('"', '').upper()
- left = left.replace('\n\t', ' ').replace('\n ', ' ')
- right = right.replace('\n\t', ' ').replace('\n ', ' ')
- if right != left:
- return False
- return True
-
- def compare_email_objects(self, left, right):
- # Compare headers
- for key, value in left.items():
- if not right[key] and key in self.ALLOWED_EXTRA_HEADERS:
- continue
- if not right[key]:
- raise AssertionError("Extra header '%s'" % key)
- if not self._headers_identical(right[key], value, header=key):
- raise AssertionError(
- "Header '{}' unequal:\n{}\n{}".format(
- key,
- repr(value),
- repr(right[key]),
- )
- )
- for key, value in right.items():
- if not left[key] and key in self.ALLOWED_EXTRA_HEADERS:
- continue
- if not left[key]:
- raise AssertionError("Extra header '%s'" % key)
- if not self._headers_identical(left[key], value, header=key):
- raise AssertionError(
- "Header '{}' unequal:\n{}\n{}".format(
- key,
- repr(value),
- repr(right[key]),
- )
- )
- if left.is_multipart() != right.is_multipart():
- self._raise_mismatched(left, right)
- if left.is_multipart():
- left_payloads = left.get_payload()
- right_payloads = right.get_payload()
- if len(left_payloads) != len(right_payloads):
- self._raise_mismatched(left, right)
- for n in range(len(left_payloads)):
- self.compare_email_objects(
- left_payloads[n],
- right_payloads[n]
- )
- else:
- if left.get_payload() is None or right.get_payload() is None:
- if left.get_payload() is None:
- if right.get_payload is not None:
- self._raise_mismatched(left, right)
- if right.get_payload() is None:
- if left.get_payload is not None:
- self._raise_mismatched(left, right)
- elif left.get_payload().strip() != right.get_payload().strip():
- self._raise_mismatched(left, right)
-
- def _raise_mismatched(self, left, right):
- raise AssertionError(
- "Message payloads do not match:\n{}\n{}".format(
- left.as_string(),
- right.as_string()
- )
- )
-
- def assertEqual(self, left, right): # noqa: N802
- if not isinstance(left, email.message.Message):
- return super().assertEqual(left, right)
- return self.compare_email_objects(left, right)
-
- def tearDown(self):
- for message in Message.objects.all():
- message.delete()
- models.ALLOWED_MIMETYPES = self._ALLOWED_MIMETYPES
- models.STRIP_UNALLOWED_MIMETYPES = self._STRIP_UNALLOWED_MIMETYPES
- models.TEXT_STORED_MIMETYPES = self._TEXT_STORED_MIMETYPES
-
- self.mailbox.delete()
- super().tearDown()
diff --git a/build/lib/django_mailbox/tests/settings.py b/build/lib/django_mailbox/tests/settings.py
deleted file mode 100644
index 66c7168e..00000000
--- a/build/lib/django_mailbox/tests/settings.py
+++ /dev/null
@@ -1,12 +0,0 @@
-DATABASES = {
- 'default': {
- 'NAME': 'db.sqlite3',
- 'ENGINE': 'django.db.backends.sqlite3',
- },
-}
-INSTALLED_APPS = [
- 'django.contrib.auth',
- 'django.contrib.contenttypes',
- 'django_mailbox',
-]
-SECRET_KEY = 'beepboop'
diff --git a/build/lib/django_mailbox/tests/test_integration_imap.py b/build/lib/django_mailbox/tests/test_integration_imap.py
deleted file mode 100644
index 6f875b74..00000000
--- a/build/lib/django_mailbox/tests/test_integration_imap.py
+++ /dev/null
@@ -1,70 +0,0 @@
-import os
-import uuid
-from urllib import parse
-
-from django.core.mail import EmailMultiAlternatives
-
-from django_mailbox.models import Mailbox
-from django_mailbox.tests.base import EmailMessageTestCase
-
-
-__all__ = ['TestImap']
-
-
-class TestImap(EmailMessageTestCase):
- def setUp(self):
- super().setUp()
-
- self.test_imap_server = (
- os.environ.get('EMAIL_IMAP_SERVER')
- )
-
- required_settings = [
- self.test_imap_server,
- self.test_account,
- self.test_password,
- self.test_smtp_server,
- self.test_from_email,
- ]
- if not all(required_settings):
- self.skipTest(
- "Integration tests are not available without having "
- "the the following environment variables set: "
- "EMAIL_ACCOUNT, EMAIL_PASSWORD, EMAIL_SMTP_SERVER, "
- "EMAIL_IMAP_SERVER."
- )
-
- self.mailbox = Mailbox.objects.create(
- name='Integration Test Imap',
- uri=self.get_connection_string()
- )
- self.arbitrary_identifier = str(uuid.uuid4())
-
- def get_connection_string(self):
- return "imap+ssl://{account}:{password}@{server}".format(
- account=parse.quote(self.test_account),
- password=parse.quote(self.test_password),
- server=self.test_imap_server,
- )
-
- def test_get_imap_message(self):
- text_content = 'This is some content'
- msg = EmailMultiAlternatives(
- self.arbitrary_identifier,
- text_content,
- self.test_from_email,
- [
- self.test_account,
- ]
- )
- msg.send()
-
- messages = self._get_new_messages(
- self.mailbox,
- condition=lambda m: m['subject'] == self.arbitrary_identifier
- )
- message = next(messages)
-
- self.assertEqual(message.subject, self.arbitrary_identifier)
- self.assertEqual(message.text, text_content)
- self.assertEqual(0, len(list(messages)))
diff --git a/build/lib/django_mailbox/tests/test_mailbox.py b/build/lib/django_mailbox/tests/test_mailbox.py
deleted file mode 100644
index e233f753..00000000
--- a/build/lib/django_mailbox/tests/test_mailbox.py
+++ /dev/null
@@ -1,46 +0,0 @@
-import os
-
-from django.test import TestCase
-
-from django_mailbox.models import Mailbox
-
-
-__all__ = ['TestMailbox']
-
-
-class TestMailbox(TestCase):
- def test_protocol_info(self):
- mailbox = Mailbox()
- mailbox.uri = 'alpha://test.com'
-
- expected_protocol = 'alpha'
- actual_protocol = mailbox._protocol_info.scheme
-
- self.assertEqual(
- expected_protocol,
- actual_protocol,
- )
-
- def test_last_polling_field_exists(self):
- mailbox = Mailbox()
- self.assertTrue(hasattr(mailbox, 'last_polling'))
-
- def test_get_new_mail_update_last_polling(self):
- mailbox = Mailbox.objects.create(uri="mbox://" + os.path.join(
- os.path.dirname(__file__),
- 'messages',
- 'generic_message.eml',
- ))
- self.assertEqual(mailbox.last_polling, None)
- list(mailbox.get_new_mail())
- self.assertNotEqual(mailbox.last_polling, None)
-
- def test_queryset_get_new_mail(self):
- mailbox = Mailbox.objects.create(uri="mbox://" + os.path.join(
- os.path.dirname(__file__),
- 'messages',
- 'generic_message.eml',
- ))
- Mailbox.objects.filter(pk=mailbox.pk).get_new_mail()
- mailbox.refresh_from_db()
- self.assertNotEqual(mailbox.last_polling, None)
diff --git a/build/lib/django_mailbox/tests/test_message_flattening.py b/build/lib/django_mailbox/tests/test_message_flattening.py
deleted file mode 100644
index 669433ed..00000000
--- a/build/lib/django_mailbox/tests/test_message_flattening.py
+++ /dev/null
@@ -1,116 +0,0 @@
-import copy
-
-from unittest import mock
-
-from django_mailbox import models, utils
-from django_mailbox.models import Message
-from django_mailbox.tests.base import EmailMessageTestCase
-
-
-__all__ = ['TestMessageFlattening']
-
-
-class TestMessageFlattening(EmailMessageTestCase):
- def test_quopri_message_is_properly_rehydrated(self):
- incoming_email_object = self._get_email_object(
- 'message_with_many_multiparts.eml',
- )
- # Note: this is identical to the above, but it appears that
- # while reading-in an e-mail message, we do alter it slightly
- expected_email_object = self._get_email_object(
- 'message_with_many_multiparts.eml',
- )
- models.TEXT_STORED_MIMETYPES = ['text/plain']
-
- msg = self.mailbox.process_incoming_message(incoming_email_object)
-
- actual_email_object = msg.get_email_object()
-
- self.assertEqual(
- actual_email_object,
- expected_email_object,
- )
-
- def test_base64_message_is_properly_rehydrated(self):
- incoming_email_object = self._get_email_object(
- 'message_with_attachment.eml',
- )
- # Note: this is identical to the above, but it appears that
- # while reading-in an e-mail message, we do alter it slightly
- expected_email_object = self._get_email_object(
- 'message_with_attachment.eml',
- )
-
- msg = self.mailbox.process_incoming_message(incoming_email_object)
-
- actual_email_object = msg.get_email_object()
-
- self.assertEqual(
- actual_email_object,
- expected_email_object,
- )
-
- def test_message_handles_rehydration_problems(self):
- incoming_email_object = self._get_email_object(
- 'message_with_defective_attachment_association.eml',
- )
- expected_email_object = self._get_email_object(
- 'message_with_defective_attachment_association_result.eml',
- )
- # Note: this is identical to the above, but it appears that
- # while reading-in an e-mail message, we do alter it slightly
- message = Message()
- message.body = incoming_email_object.as_string()
-
- msg = self.mailbox.process_incoming_message(incoming_email_object)
-
- del msg._email_object # Cache flush
- actual_email_object = msg.get_email_object()
-
- self.assertEqual(
- actual_email_object,
- expected_email_object,
- )
-
- def test_message_content_type_stripping(self):
- incoming_email_object = self._get_email_object(
- 'message_with_many_multiparts.eml',
- )
- expected_email_object = self._get_email_object(
- 'message_with_many_multiparts_stripped_html.eml',
- )
- default_settings = utils.get_settings()
-
- with mock.patch('django_mailbox.utils.get_settings') as get_settings:
- altered = copy.deepcopy(default_settings)
- altered['strip_unallowed_mimetypes'] = True
- altered['allowed_mimetypes'] = ['text/plain']
-
- get_settings.return_value = altered
-
- msg = self.mailbox.process_incoming_message(incoming_email_object)
-
- del msg._email_object # Cache flush
- actual_email_object = msg.get_email_object()
-
- self.assertEqual(
- actual_email_object,
- expected_email_object,
- )
-
- def test_message_processing_unknown_encoding(self):
- incoming_email_object = self._get_email_object(
- 'message_with_invalid_encoding.eml',
- )
-
- msg = self.mailbox.process_incoming_message(incoming_email_object)
-
- expected_text = (
- "We offer loans to private individuals and corporate "
- "organizations at 2% interest rate. Interested serious "
- "applicants should apply via email with details of their "
- "requirements.\n\nWarm Regards,\nLoan Team"
- )
- actual_text = msg.text
-
- self.assertEqual(actual_text, expected_text)
diff --git a/build/lib/django_mailbox/tests/test_process_email.py b/build/lib/django_mailbox/tests/test_process_email.py
deleted file mode 100644
index a6dbd044..00000000
--- a/build/lib/django_mailbox/tests/test_process_email.py
+++ /dev/null
@@ -1,432 +0,0 @@
-import gzip
-import os.path
-import sys
-
-import copy
-from unittest import mock
-
-from django_mailbox.models import Mailbox, Message
-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.core.mail import EmailMessage
-
-__all__ = ['TestProcessEmail']
-
-
-class TestProcessEmail(EmailMessageTestCase):
- def test_message_without_attachments(self):
- message = self._get_email_object('generic_message.eml')
-
- mailbox = Mailbox.objects.create()
- msg = mailbox.process_incoming_message(message)
-
- self.assertEqual(
- msg.mailbox,
- mailbox
- )
- self.assertEqual(msg.subject, 'Message Without Attachment')
- self.assertEqual(
- msg.message_id,
- (
- ''
- )
- )
- self.assertEqual(
- msg.from_header,
- 'Adam Coddington ',
- )
- self.assertEqual(
- msg.to_header,
- 'Adam Coddington ',
- )
-
- def test_message_with_encoded_attachment_filenames(self):
- message = self._get_email_object(
- 'message_with_koi8r_filename_attachments.eml'
- )
-
- mailbox = Mailbox.objects.create()
- msg = mailbox.process_incoming_message(message)
-
- attachments = msg.attachments.order_by('pk').all()
- self.assertEqual(
- '\u041f\u0430\u043a\u0435\u0442 \u043f\u0440\u0435\u0434\u043b'
- '\u043e\u0436\u0435\u043d\u0438\u0439 HSE Career Fair 8 \u0430'
- '\u043f\u0440\u0435\u043b\u044f 2016.pdf',
- attachments[0].get_filename()
- )
- self.assertEqual(
- '\u0412\u0435\u0434\u043e\u043c\u043e\u0441\u0442\u0438.pdf',
- attachments[1].get_filename()
- )
- self.assertEqual(
- '\u041f\u0430\u043a\u0435\u0442 \u043f\u0440\u0435\u0434\u043b'
- '\u043e\u0436\u0435\u043d\u0438\u0439 2016.pptx',
- attachments[2].get_filename()
- )
-
- def test_message_with_attachments(self):
- message = self._get_email_object('message_with_attachment.eml')
-
- mailbox = Mailbox.objects.create()
- msg = mailbox.process_incoming_message(message)
-
- expected_count = 1
- actual_count = msg.attachments.count()
-
- self.assertEqual(
- expected_count,
- actual_count,
- )
-
- attachment = msg.attachments.all()[0]
- self.assertEqual(
- attachment.get_filename(),
- 'heart.png',
- )
-
- def test_message_with_utf8_attachment_header(self):
- """ Ensure that we properly handle UTF-8 encoded attachment
-
- Safe for regress of #104 too
- """
- email_object = self._get_email_object(
- 'message_with_utf8_attachment.eml',
- )
- mailbox = Mailbox.objects.create()
- msg = mailbox.process_incoming_message(email_object)
-
- expected_count = 2
- actual_count = msg.attachments.count()
-
- self.assertEqual(
- expected_count,
- actual_count,
- )
-
- attachment = msg.attachments.all()[0]
- self.assertEqual(
- attachment.get_filename(),
- 'pi\u0142kochwyty.jpg'
- )
-
- attachment = msg.attachments.all()[1]
- self.assertEqual(
- attachment.get_filename(),
- 'odpowied\u017a Burmistrza.jpg'
- )
-
- def test_message_get_text_body(self):
- message = self._get_email_object('multipart_text.eml')
-
- mailbox = Mailbox.objects.create()
- msg = mailbox.process_incoming_message(message)
-
- expected_results = 'Hello there!'
- actual_results = msg.text.strip()
-
- self.assertEqual(
- expected_results,
- actual_results,
- )
-
- def test_get_text_body_properly_recomposes_line_continuations(self):
- message = Message()
- email_object = self._get_email_object(
- 'message_with_long_text_lines.eml'
- )
-
- message.get_email_object = lambda: email_object
-
- actual_text = message.text
- expected_text = (
- 'The one of us with a bike pump is far ahead, '
- 'but a man stopped to help us and gave us his pump.'
- )
-
- self.assertEqual(
- actual_text,
- expected_text
- )
-
- def test_get_body_properly_handles_unicode_body(self):
- with open(
- os.path.join(
- os.path.dirname(__file__),
- 'messages/generic_message.eml'
- )
- ) as f:
- unicode_body = f.read()
-
- message = Message()
- message.body = unicode_body
-
- expected_body = unicode_body
- actual_body = message.get_email_object().as_string()
-
- self.assertEqual(
- expected_body,
- actual_body
- )
-
- def test_message_issue_82(self):
- """ Ensure that we properly handle incorrectly encoded messages
-
- """
- email_object = self._get_email_object('email_issue_82.eml')
- it = 'works'
- try:
- # it's ok to call as_string() before passing email_object
- # to _get_dehydrated_message()
- email_object.as_string()
- except:
- it = 'do not works'
-
- success = True
- try:
- self.mailbox.process_incoming_message(email_object)
- except ValueError:
- success = False
-
- self.assertEqual(it, 'works')
- self.assertEqual(True, success)
-
- def test_message_issue_82_bis(self):
- """ Ensure that the email object is good before and after
- calling _get_dehydrated_message()
-
- """
- message = self._get_email_object('email_issue_82.eml')
-
- success = True
-
- # this is the code of _process_message()
- msg = Message()
- # if STORE_ORIGINAL_MESSAGE:
- # msg.eml.save('%s.eml' % uuid.uuid4(), ContentFile(message),
- # save=False)
- msg.mailbox = self.mailbox
- if 'subject' in message:
- msg.subject = convert_header_to_unicode(message['subject'])[0:255]
- if 'message-id' in message:
- msg.message_id = message['message-id'][0:255]
- if 'from' in message:
- msg.from_header = convert_header_to_unicode(message['from'])
- if 'to' in message:
- msg.to_header = convert_header_to_unicode(message['to'])
- elif 'Delivered-To' in message:
- msg.to_header = convert_header_to_unicode(message['Delivered-To'])
- msg.save()
-
- # here the message is ok
- str_msg = message.as_string()
- message = self.mailbox._get_dehydrated_message(message, msg)
- try:
- # here as_string raises UnicodeEncodeError
- str_msg = message.as_string()
- except:
- success = False
-
- msg.set_body(str_msg)
- if message['in-reply-to']:
- try:
- msg.in_reply_to = Message.objects.filter(
- message_id=message['in-reply-to']
- )[0]
- except IndexError:
- pass
- msg.save()
-
- self.assertEqual(True, success)
-
- def test_message_with_misplaced_utf8_content(self):
- """ Ensure that we properly handle incorrectly encoded messages
-
- ``message_with_utf8_char.eml``'s primary text payload is marked
- as being iso-8859-1 data, but actually contains UTF-8 bytes.
-
- """
- email_object = self._get_email_object('message_with_utf8_char.eml')
-
- msg = self.mailbox.process_incoming_message(email_object)
-
- expected_text = 'This message contains funny UTF16 characters ' + \
- 'like this one: "\xc2\xa0" and this one "\xe2\x9c\xbf".'
- actual_text = msg.text
-
- self.assertEqual(
- expected_text,
- actual_text,
- )
-
- def test_message_with_invalid_content_for_declared_encoding(self):
- """ Ensure that we gracefully handle mis-encoded bodies.
-
- Should a payload body be misencoded, we should:
-
- - Not explode
-
- Note: there is (intentionally) no assertion below; the only guarantee
- we make via this library is that processing this e-mail message will
- not cause an exception to be raised.
-
- """
- email_object = self._get_email_object(
- 'message_with_invalid_content_for_declared_encoding.eml',
- )
-
- msg = self.mailbox.process_incoming_message(email_object)
-
- msg.text
-
- def test_message_with_valid_content_in_single_byte_encoding(self):
- email_object = self._get_email_object(
- 'message_with_single_byte_encoding.eml',
- )
-
- msg = self.mailbox.process_incoming_message(email_object)
-
- actual_text = msg.text
- expected_body = '\u042d\u0442\u043e ' + \
- '\u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435 ' + \
- '\u0438\u043c\u0435\u0435\u0442 ' + \
- '\u043d\u0435\u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d' + \
- '\u0443\u044e ' + \
- '\u043a\u043e\u0434\u0438\u0440\u043e\u0432\u043a\u0430.'
-
- self.assertEqual(
- actual_text,
- expected_body,
- )
-
- def test_message_with_single_byte_subject_encoding(self):
- email_object = self._get_email_object(
- 'message_with_single_byte_extended_subject_encoding.eml',
- )
-
- msg = self.mailbox.process_incoming_message(email_object)
-
- expected_subject = '\u00D3\u00E7\u00ED\u00E0\u00E9 \u00EA\u00E0\u00EA ' + \
- '\u00E7\u00E0\u00F0\u00E0\u00E1\u00E0\u00F2\u00FB\u00E2' + \
- '\u00E0\u00F2\u00FC \u00EE\u00F2 1000$ \u00E2 ' + \
- '\u00ED\u00E5\u00E4\u00E5\u00EB\u00FE!'
- actual_subject = msg.subject
- self.assertEqual(actual_subject, expected_subject)
-
- expected_from = 'test test '
- actual_from = msg.from_header
- self.assertEqual(expected_from, actual_from)
-
- def test_message_reply(self):
- email_object = EmailMessage(
- 'Test subject', # subject
- 'Test body', # body
- 'username@example.com', # from
- ['mr.test32@mail.ru'], # to
- )
- msg = self.mailbox.record_outgoing_message(email_object.message())
-
- self.assertTrue(msg.outgoing)
-
- actual_from = 'username@example.com'
- reply_email_object = EmailMessage(
- 'Test subject', # subject
- 'Test body', # body
- actual_from, # from
- ['mr.test32@mail.ru'], # to
- )
-
- with mock.patch.object(reply_email_object, 'send'):
- reply_msg = msg.reply(reply_email_object)
-
- self.assertEqual(reply_msg.in_reply_to, msg)
-
- self.assertEqual(actual_from, msg.from_header)
-
- reply_email_object.from_email = None
-
- with mock.patch.object(reply_email_object, 'send'):
- second_reply_msg = msg.reply(reply_email_object)
-
- self.assertEqual(self.mailbox.from_email, second_reply_msg.from_header)
-
- def test_message_with_text_attachment(self):
- email_object = self._get_email_object(
- 'message_with_text_attachment.eml',
- )
-
- msg = self.mailbox.process_incoming_message(email_object)
-
- self.assertEqual(msg.attachments.all().count(), 1)
- self.assertEqual('Has an attached text document, too!', msg.text)
-
- 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()))
-
- msg = self.mailbox.process_incoming_message(email_object)
-
- self.assertEqual(size,
- len(force_text(msg.get_email_object().as_string())))
-
- def test_message_saved(self):
- message = self._get_email_object('generic_message.eml')
-
- default_settings = utils.get_settings()
-
- with mock.patch('django_mailbox.utils.get_settings') as get_settings:
- altered = copy.deepcopy(default_settings)
- altered['store_original_message'] = True
- get_settings.return_value = altered
-
- msg = self.mailbox.process_incoming_message(message)
-
- self.assertNotEquals(msg.eml, None)
-
- self.assertTrue(msg.eml.name.endswith('.eml'))
-
- with open(msg.eml.name, 'rb') as f:
- self.assertEqual(
- f.read(),
- self._get_email_as_text('generic_message.eml')
- )
-
- def test_message_saving_ignored(self):
- message = self._get_email_object('generic_message.eml')
-
- default_settings = utils.get_settings()
-
- with mock.patch('django_mailbox.utils.get_settings') as get_settings:
- altered = copy.deepcopy(default_settings)
- altered['store_original_message'] = False
- get_settings.return_value = altered
-
- msg = self.mailbox.process_incoming_message(message)
-
- self.assertEquals(msg.eml, None)
-
- def test_message_compressed(self):
- message = self._get_email_object('generic_message.eml')
-
- default_settings = utils.get_settings()
-
- with mock.patch('django_mailbox.utils.get_settings') as get_settings:
- altered = copy.deepcopy(default_settings)
- altered['compress_original_message'] = True
- altered['store_original_message'] = True
- get_settings.return_value = altered
-
- msg = self.mailbox.process_incoming_message(message)
-
- actual_email_object = msg.get_email_object()
-
- self.assertTrue(msg.eml.name.endswith('.eml.gz'))
-
- with gzip.open(msg.eml.name, 'rb') as f:
- self.assertEqual(f.read(),
- self._get_email_as_text('generic_message.eml'))
diff --git a/build/lib/django_mailbox/tests/test_processincomingmessage.py b/build/lib/django_mailbox/tests/test_processincomingmessage.py
deleted file mode 100644
index 8ba2fb29..00000000
--- a/build/lib/django_mailbox/tests/test_processincomingmessage.py
+++ /dev/null
@@ -1,65 +0,0 @@
-from distutils.version import LooseVersion
-from unittest import mock
-
-from django.core.management import call_command, CommandError
-from django.test import TestCase
-import django
-
-class CommandsTestCase(TestCase):
- def test_processincomingmessage_no_args(self):
- """Check that processincomingmessage works with no args"""
-
- mailbox_name = None
- # Mock handle so that the test doesn't hang waiting for input. Note that we are only testing
- # the argument parsing here -- functionality should be tested elsewhere
- with mock.patch('django_mailbox.management.commands.processincomingmessage.Command.handle') as handle:
- # Don't care about the return value
- handle.return_value = None
-
- call_command('processincomingmessage')
- args, kwargs = handle.call_args
-
- # Make sure that we called with the right arguments
- try:
- self.assertEqual(kwargs['mailbox_name'], mailbox_name)
- except KeyError:
- # Handle Django 1.7
- # It uses optparse instead of argparse, so instead of being
- # set to None, mailbox_name is simply left out altogether
- # Thus we expect an empty tuple here
- self.assertEqual(args, tuple())
-
-
- def test_processincomingmessage_with_arg(self):
- """Check that processincomingmessage works with mailbox_name given"""
-
- mailbox_name = 'foo_mailbox'
-
- with mock.patch('django_mailbox.management.commands.processincomingmessage.Command.handle') as handle:
- handle.return_value = None
-
- call_command('processincomingmessage', mailbox_name)
- args, kwargs = handle.call_args
- try:
- self.assertEqual(kwargs['mailbox_name'], mailbox_name)
- except (AssertionError, KeyError):
- # Handle Django 1.7
- # It uses optparse instead of argparse, so instead of being
- # in kwargs, mailbox_name is in args
- self.assertEqual(args[0], mailbox_name)
-
- def test_processincomingmessage_too_many_args(self):
- """Check that processincomingmessage raises an error if too many args"""
- # Only perform this test for Django versions greater than 1.7.*. This
- # is because, with optparse, too many arguments doesn't result in an
- # error, which means this test is worthless anyway
- # For the "compatibility" versions, unexpected arguments aren't handled
- # very well, and result in a TypeError
- if (LooseVersion(django.get_version()) >= LooseVersion('1.8') and
- LooseVersion(django.get_version()) < LooseVersion('1.10')):
- with self.assertRaises(TypeError):
- call_command('processincomingmessage', 'foo_mailbox', 'invalid_arg')
- # In 1.10 and later a proper CommandError should be raised
- elif LooseVersion(django.get_version()) >= LooseVersion('1.10'):
- with self.assertRaises(CommandError):
- call_command('processincomingmessage', 'foo_mailbox', 'invalid_arg')
diff --git a/build/lib/django_mailbox/tests/test_transports.py b/build/lib/django_mailbox/tests/test_transports.py
deleted file mode 100644
index 403a3069..00000000
--- a/build/lib/django_mailbox/tests/test_transports.py
+++ /dev/null
@@ -1,167 +0,0 @@
-from unittest import mock
-
-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
-
-FAKE_UID_SEARCH_ANSWER = (
- 'OK',
- [
- b'18 19 20 21 22 23 24 25 26 27 28 29 ' +
- b'30 31 32 33 34 35 36 37 38 39 40 41 42 43 44'
- ]
-)
-FAKE_UID_FETCH_SIZES = (
- 'OK',
- [
- b'1 (UID 18 RFC822.SIZE 58070000000)',
- b'2 (UID 19 RFC822.SIZE 2593)'
- ]
-)
-FAKE_UID_FETCH_MSG = (
- 'OK',
- [
- (
- b'1 (UID 18 RFC822 {5807}',
- get_email_as_text('generic_message.eml')
- ),
- ]
-)
-FAKE_UID_COPY_MSG = (
- 'OK',
- [
- b'[COPYUID 1 2 2] (Success)'
- ]
-)
-FAKE_LIST_ARCHIVE_FOLDERS_ANSWERS = (
- 'OK',
- [
- b'(\\HasNoChildren \\All) "/" "[Gmail]/All Mail"'
- ]
-)
-
-
-class IMAPTestCase(EmailMessageTestCase):
- def setUp(self):
- def imap_server_uid_method(*args):
- cmd = args[0]
- arg2 = args[2]
- if cmd == 'search':
- return FAKE_UID_SEARCH_ANSWER
- if cmd == 'copy':
- return FAKE_UID_COPY_MSG
- if cmd == 'fetch':
- if arg2 == '(RFC822.SIZE)':
- return FAKE_UID_FETCH_SIZES
- if arg2 == '(RFC822)':
- return FAKE_UID_FETCH_MSG
-
- def imap_server_list_method(pattern=None):
- return FAKE_LIST_ARCHIVE_FOLDERS_ANSWERS
-
- self.imap_server = mock.Mock()
- self.imap_server.uid = imap_server_uid_method
- self.imap_server.list = imap_server_list_method
- super().setUp()
-
-
-class TestImapTransport(IMAPTestCase):
- def setUp(self):
- super().setUp()
- self.arbitrary_hostname = 'one.two.three'
- self.arbitrary_port = 100
- self.ssl = False
- self.transport = ImapTransport(
- self.arbitrary_hostname,
- self.arbitrary_port,
- self.ssl
- )
- self.transport.server = self.imap_server
-
- def test_get_email_message(self):
- actual_messages = list(self.transport.get_message())
- self.assertEqual(len(actual_messages), 27)
- actual_message = actual_messages[0]
- expected_message = self._get_email_object('generic_message.eml')
- self.assertEqual(expected_message, actual_message)
-
-
-class TestImapArchivedTransport(TestImapTransport):
- def setUp(self):
- super().setUp()
- self.archive = 'Archive'
- self.transport = ImapTransport(
- self.arbitrary_hostname,
- self.arbitrary_port,
- self.ssl,
- self.archive
- )
- self.transport.server = self.imap_server
-
-
-class TestMaxSizeImapTransport(TestImapTransport):
-
- @override_settings(DJANGO_MAILBOX_MAX_MESSAGE_SIZE=5807)
- def setUp(self):
- super().setUp()
-
- self.transport = ImapTransport(
- self.arbitrary_hostname,
- self.arbitrary_port,
- self.ssl,
- )
- self.transport.server = self.imap_server
-
- def test_size_limit(self):
- all_message_ids = self.transport._get_all_message_ids()
- small_message_ids = self.transport._get_small_message_ids(
- all_message_ids,
- )
- self.assertEqual(len(small_message_ids), 1)
-
- def test_get_email_message(self):
- actual_messages = list(self.transport.get_message())
- self.assertEqual(len(actual_messages), 1)
- actual_message = actual_messages[0]
- expected_message = self._get_email_object('generic_message.eml')
- self.assertEqual(expected_message, actual_message)
-
-
-class TestPop3Transport(EmailMessageTestCase):
- def setUp(self):
- self.arbitrary_hostname = 'one.two.three'
- self.arbitrary_port = 100
- self.ssl = False
- self.transport = Pop3Transport(
- self.arbitrary_hostname,
- self.arbitrary_port,
- self.ssl
- )
- self.transport.server = None
- super().setUp()
-
- def test_get_email_message(self):
- with mock.patch.object(self.transport, 'server') as server:
- # Consider this value arbitrary, the second parameter
- # should have one entry per message in the inbox
- server.list.return_value = [None, ['some_msg']]
- server.retr.return_value = [
- '+OK message follows',
- [
- line.encode('ascii')
- for line in self._get_email_as_text(
- 'generic_message.eml'
- ).decode('ascii').split('\n')
- ],
- 10018, # Some arbitrary size, ideally matching the above
- ]
-
- actual_messages = list(self.transport.get_message())
-
- self.assertEqual(len(actual_messages), 1)
-
- actual_message = actual_messages[0]
- expected_message = self._get_email_object('generic_message.eml')
-
- self.assertEqual(expected_message, actual_message)
diff --git a/build/lib/django_mailbox/transports/__init__.py b/build/lib/django_mailbox/transports/__init__.py
deleted file mode 100644
index 9f733e34..00000000
--- a/build/lib/django_mailbox/transports/__init__.py
+++ /dev/null
@@ -1,11 +0,0 @@
-# all imports below are only used by external modules
-# flake8: noqa
-from django_mailbox.transports.imap import ImapTransport
-from django_mailbox.transports.pop3 import Pop3Transport
-from django_mailbox.transports.maildir import MaildirTransport
-from django_mailbox.transports.mbox import MboxTransport
-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.office365 import Office365Transport
diff --git a/build/lib/django_mailbox/transports/babyl.py b/build/lib/django_mailbox/transports/babyl.py
deleted file mode 100644
index eb888a6d..00000000
--- a/build/lib/django_mailbox/transports/babyl.py
+++ /dev/null
@@ -1,6 +0,0 @@
-from mailbox import Babyl
-from django_mailbox.transports.generic import GenericFileMailbox
-
-
-class BabylTransport(GenericFileMailbox):
- _variant = Babyl
diff --git a/build/lib/django_mailbox/transports/base.py b/build/lib/django_mailbox/transports/base.py
deleted file mode 100644
index a3a2bc01..00000000
--- a/build/lib/django_mailbox/transports/base.py
+++ /dev/null
@@ -1,12 +0,0 @@
-import email
-
-# Do *not* remove this, we need to use this in subclasses of EmailTransport
-from email.errors import MessageParseError # noqa: F401
-
-
-class EmailTransport:
- def get_email_from_bytes(self, contents):
- message = email.message_from_bytes(contents)
-
- return message
-
diff --git a/build/lib/django_mailbox/transports/generic.py b/build/lib/django_mailbox/transports/generic.py
deleted file mode 100644
index 5832fda2..00000000
--- a/build/lib/django_mailbox/transports/generic.py
+++ /dev/null
@@ -1,26 +0,0 @@
-import sys
-
-from .base import EmailTransport
-
-
-class GenericFileMailbox(EmailTransport):
- _variant = None
- _path = None
-
- def __init__(self, path):
- super().__init__()
- self._path = path
-
- def get_instance(self):
- return self._variant(self._path)
-
- def get_message(self, condition=None):
- repository = self.get_instance()
- repository.lock()
- for key, message in repository.items():
- if condition and not condition(message):
- continue
- repository.remove(key)
- yield message
- repository.flush()
- repository.unlock()
diff --git a/build/lib/django_mailbox/transports/gmail.py b/build/lib/django_mailbox/transports/gmail.py
deleted file mode 100644
index 99e8189a..00000000
--- a/build/lib/django_mailbox/transports/gmail.py
+++ /dev/null
@@ -1,57 +0,0 @@
-import logging
-
-from django_mailbox.transports.imap import ImapTransport
-
-
-logger = logging.getLogger(__name__)
-
-
-class GmailImapTransport(ImapTransport):
-
- def connect(self, username, password):
- # Try to use oauth2 first. It's much safer
- try:
- self._connect_oauth(username)
- except (TypeError, ValueError) as e:
- logger.warning("Couldn't do oauth2 because %s" % e)
- self.server = self.transport(self.hostname, self.port)
- typ, msg = self.server.login(username, password)
- self.server.select()
-
- def _connect_oauth(self, username):
- # username should be an email address that has already been authorized
- # for gmail access
- try:
- from django_mailbox.google_utils import (
- get_google_access_token,
- fetch_user_info,
- AccessTokenNotFound,
- )
- except ImportError:
- raise ValueError(
- "Install python-social-auth to use oauth2 auth for gmail"
- )
-
- access_token = None
- while access_token is None:
- try:
- access_token = get_google_access_token(username)
- google_email_address = fetch_user_info(username)['email']
- except TypeError:
- # This means that the google process took too long
- # Trying again is the right thing to do
- pass
- except AccessTokenNotFound:
- raise ValueError(
- "No Token available in python-social-auth for %s" % (
- username
- )
- )
-
- auth_string = 'user={}\1auth=Bearer {}\1\1'.format(
- google_email_address,
- access_token
- )
- self.server = self.transport(self.hostname, self.port)
- self.server.authenticate('XOAUTH2', lambda x: auth_string)
- self.server.select()
diff --git a/build/lib/django_mailbox/transports/imap.py b/build/lib/django_mailbox/transports/imap.py
deleted file mode 100644
index 2599adad..00000000
--- a/build/lib/django_mailbox/transports/imap.py
+++ /dev/null
@@ -1,137 +0,0 @@
-import imaplib
-import logging
-
-from django.conf import settings
-
-from .base import EmailTransport, MessageParseError
-
-
-# By default, imaplib will raise an exception if it encounters more
-# than 10k bytes; sometimes users attempt to consume mailboxes that
-# have a more, and modern computers are skookum-enough to handle just
-# a *few* more messages without causing any sort of problem.
-imaplib._MAXLINE = 1000000
-
-
-logger = logging.getLogger(__name__)
-
-
-class ImapTransport(EmailTransport):
- def __init__(
- self, hostname, port=None, ssl=False, tls=False,
- archive='', folder=None,
- ):
- self.max_message_size = getattr(
- settings,
- 'DJANGO_MAILBOX_MAX_MESSAGE_SIZE',
- False
- )
- self.integration_testing_subject = getattr(
- settings,
- 'DJANGO_MAILBOX_INTEGRATION_TESTING_SUBJECT',
- None
- )
- self.hostname = hostname
- self.port = port
- self.archive = archive
- self.folder = folder
- self.tls = tls
- if ssl:
- self.transport = imaplib.IMAP4_SSL
- if not self.port:
- self.port = 993
- else:
- self.transport = imaplib.IMAP4
- if not self.port:
- self.port = 143
-
- def connect(self, username, password):
- self.server = self.transport(self.hostname, self.port)
- if self.tls:
- self.server.starttls()
- typ, msg = self.server.login(username, password)
-
- if self.folder:
- self.server.select(self.folder)
- else:
- self.server.select()
-
- def _get_all_message_ids(self):
- # Fetch all the message uids
- response, message_ids = self.server.uid('search', None, 'ALL')
- message_id_string = message_ids[0].strip()
- # Usually `message_id_string` will be a list of space-separated
- # ids; we must make sure that it isn't an empty string before
- # splitting into individual UIDs.
- if message_id_string:
- return message_id_string.decode().split(' ')
- return []
-
- def _get_small_message_ids(self, message_ids):
- # Using existing message uids, get the sizes and
- # return only those that are under the size
- # limit
- safe_message_ids = []
-
- status, data = self.server.uid(
- 'fetch',
- ','.join(message_ids),
- '(RFC822.SIZE)'
- )
-
- for each_msg in data:
- each_msg = each_msg.decode()
- try:
- uid = each_msg.split(' ')[2]
- size = each_msg.split(' ')[4].rstrip(')')
- if int(size) <= int(self.max_message_size):
- safe_message_ids.append(uid)
- except ValueError as e:
- logger.warning(
- "ValueError: {} working on {}".format(e, each_msg[0])
- )
- pass
- return safe_message_ids
-
- def get_message(self, condition=None):
- message_ids = self._get_all_message_ids()
-
- if not message_ids:
- return
-
- # Limit the uids to the small ones if we care about that
- if self.max_message_size:
- message_ids = self._get_small_message_ids(message_ids)
-
- if self.archive:
- typ, folders = self.server.list(pattern=self.archive)
- if folders[0] is None:
- # If the archive folder does not exist, create it
- self.server.create(self.archive)
-
- for uid in message_ids:
- try:
- typ, msg_contents = self.server.uid('fetch', uid, '(RFC822)')
- if not msg_contents:
- continue
- try:
- message = self.get_email_from_bytes(msg_contents[0][1])
- except TypeError:
- # This happens if another thread/process deletes the
- # message between our generating the ID list and our
- # processing it here.
- continue
-
- if condition and not condition(message):
- continue
-
- yield message
- except MessageParseError:
- continue
-
- if self.archive:
- self.server.uid('copy', uid, self.archive)
-
- self.server.uid('store', uid, "+FLAGS", "(\\Deleted)")
- self.server.expunge()
- return
diff --git a/build/lib/django_mailbox/transports/maildir.py b/build/lib/django_mailbox/transports/maildir.py
deleted file mode 100644
index 2d4af53d..00000000
--- a/build/lib/django_mailbox/transports/maildir.py
+++ /dev/null
@@ -1,9 +0,0 @@
-from mailbox import Maildir
-from django_mailbox.transports.generic import GenericFileMailbox
-
-
-class MaildirTransport(GenericFileMailbox):
- _variant = Maildir
-
- def get_instance(self):
- return self._variant(self._path, None)
diff --git a/build/lib/django_mailbox/transports/mbox.py b/build/lib/django_mailbox/transports/mbox.py
deleted file mode 100644
index d610deb8..00000000
--- a/build/lib/django_mailbox/transports/mbox.py
+++ /dev/null
@@ -1,6 +0,0 @@
-from mailbox import mbox
-from django_mailbox.transports.generic import GenericFileMailbox
-
-
-class MboxTransport(GenericFileMailbox):
- _variant = mbox
diff --git a/build/lib/django_mailbox/transports/mh.py b/build/lib/django_mailbox/transports/mh.py
deleted file mode 100644
index 33fdc254..00000000
--- a/build/lib/django_mailbox/transports/mh.py
+++ /dev/null
@@ -1,6 +0,0 @@
-from mailbox import MH
-from django_mailbox.transports.generic import GenericFileMailbox
-
-
-class MHTransport(GenericFileMailbox):
- _variant = MH
diff --git a/build/lib/django_mailbox/transports/mmdf.py b/build/lib/django_mailbox/transports/mmdf.py
deleted file mode 100644
index 270e4f34..00000000
--- a/build/lib/django_mailbox/transports/mmdf.py
+++ /dev/null
@@ -1,6 +0,0 @@
-from mailbox import MMDF
-from django_mailbox.transports.generic import GenericFileMailbox
-
-
-class MMDFTransport(GenericFileMailbox):
- _variant = MMDF
diff --git a/build/lib/django_mailbox/transports/office365.py b/build/lib/django_mailbox/transports/office365.py
deleted file mode 100644
index 4f748b2f..00000000
--- a/build/lib/django_mailbox/transports/office365.py
+++ /dev/null
@@ -1,66 +0,0 @@
-import logging
-
-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, client_id, client_secret, tenant_id):
- try:
- import O365
- except ImportError:
- raise ValueError(
- "Install o365 to use oauth2 auth for office365"
- )
-
- credentials = (client_id, client_secret)
-
- self.account = O365.Account(credentials, auth_flow_type='credentials', tenant_id=tenant_id)
- 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/build/lib/django_mailbox/transports/pop3.py b/build/lib/django_mailbox/transports/pop3.py
deleted file mode 100644
index 6dbe9953..00000000
--- a/build/lib/django_mailbox/transports/pop3.py
+++ /dev/null
@@ -1,44 +0,0 @@
-from poplib import POP3, POP3_SSL
-
-from .base import EmailTransport, MessageParseError
-
-
-class Pop3Transport(EmailTransport):
- def __init__(self, hostname, port=None, ssl=False):
- self.hostname = hostname
- self.port = port
- if ssl:
- self.transport = POP3_SSL
- if not self.port:
- self.port = 995
- else:
- self.transport = POP3
- if not self.port:
- self.port = 110
-
- def connect(self, username, password):
- self.server = self.transport(self.hostname, self.port)
- self.server.user(username)
- self.server.pass_(password)
-
- def get_message_body(self, message_lines):
- return bytes('\r\n', 'ascii').join(message_lines)
-
- def get_message(self, condition=None):
- message_count = len(self.server.list()[1])
- for i in range(message_count):
- try:
- msg_contents = self.get_message_body(
- self.server.retr(i + 1)[1]
- )
- message = self.get_email_from_bytes(msg_contents)
-
- if condition and not condition(message):
- continue
-
- yield message
- except MessageParseError:
- continue
- self.server.dele(i + 1)
- self.server.quit()
- return
diff --git a/build/lib/django_mailbox/utils.py b/build/lib/django_mailbox/utils.py
deleted file mode 100644
index 6603612f..00000000
--- a/build/lib/django_mailbox/utils.py
+++ /dev/null
@@ -1,151 +0,0 @@
-import datetime
-import email.header
-import logging
-import os
-
-from django.conf import settings
-
-
-logger = logging.getLogger(__name__)
-
-
-def get_settings():
- return {
- 'strip_unallowed_mimetypes': getattr(
- settings,
- 'DJANGO_MAILBOX_STRIP_UNALLOWED_MIMETYPES',
- False
- ),
- 'allowed_mimetypes': getattr(
- settings,
- 'DJANGO_MAILBOX_ALLOWED_MIMETYPES',
- [
- 'text/plain',
- 'text/html'
- ]
- ),
- 'text_stored_mimetypes': getattr(
- settings,
- 'DJANGO_MAILBOX_TEXT_STORED_MIMETYPES',
- [
- 'text/plain',
- 'text/html'
- ]
- ),
- 'altered_message_header': getattr(
- settings,
- 'DJANGO_MAILBOX_ALTERED_MESSAGE_HEADER',
- 'X-Django-Mailbox-Altered-Message'
- ),
- 'attachment_interpolation_header': getattr(
- settings,
- 'DJANGO_MAILBOX_ATTACHMENT_INTERPOLATION_HEADER',
- 'X-Django-Mailbox-Interpolate-Attachment'
- ),
- 'attachment_upload_to': getattr(
- settings,
- 'DJANGO_MAILBOX_ATTACHMENT_UPLOAD_TO',
- 'mailbox_attachments/%Y/%m/%d/'
- ),
- 'store_original_message': getattr(
- settings,
- 'DJANGO_MAILBOX_STORE_ORIGINAL_MESSAGE',
- False
- ),
- 'compress_original_message': getattr(
- settings,
- 'DJANGO_MAILBOX_COMPRESS_ORIGINAL_MESSAGE',
- False
- ),
- 'original_message_compression': getattr(
- settings,
- 'DJANGO_MAILBOX_ORIGINAL_MESSAGE_COMPRESSION',
- 6
- ),
- 'default_charset': getattr(
- settings,
- 'DJANGO_MAILBOX_default_charset',
- 'iso8859-1',
- )
- }
-
-
-def convert_header_to_unicode(header):
- default_charset = get_settings()['default_charset']
-
- def _decode(value, encoding):
- if isinstance(value, str):
- return value
- if not encoding or encoding == 'unknown-8bit':
- encoding = default_charset
- return value.decode(encoding, 'replace')
-
- try:
- return ''.join(
- [
- (
- _decode(bytestr, encoding)
- ) for bytestr, encoding in email.header.decode_header(header)
- ]
- )
- except UnicodeDecodeError:
- logger.exception(
- 'Errors encountered decoding header %s into encoding %s.',
- header,
- default_charset,
- )
- return header.decode(default_charset, 'replace')
-
-
-def get_body_from_message(message, maintype, subtype):
- """
- Fetchs the body message matching main/sub content type.
- """
- body = ''
- for part in message.walk():
- if part.get('content-disposition', '').startswith('attachment;'):
- continue
- if part.get_content_maintype() == maintype and \
- part.get_content_subtype() == subtype:
- charset = part.get_content_charset()
- this_part = part.get_payload(decode=True)
- if charset:
- try:
- this_part = this_part.decode(charset, 'replace')
- except LookupError:
- this_part = this_part.decode('ascii', 'replace')
- logger.warning(
- 'Unknown encoding %s encountered while decoding '
- 'text payload. Interpreting as ASCII with '
- 'replacement, but some data may not be '
- 'represented as the sender intended.',
- charset
- )
- except ValueError:
- this_part = this_part.decode('ascii', 'replace')
- logger.warning(
- 'Error encountered while decoding text '
- 'payload from an incorrectly-constructed '
- 'e-mail; payload was converted to ASCII with '
- 'replacement, but some data may not be '
- 'represented as the sender intended.'
- )
- else:
- this_part = this_part.decode('ascii', 'replace')
-
- body += this_part
-
- return body
-
-
-def get_attachment_save_path(instance, filename):
- settings = get_settings()
-
- path = settings['attachment_upload_to']
- if '%' in path:
- path = datetime.datetime.utcnow().strftime(path)
-
- return os.path.join(
- path,
- filename,
- )
From bd4d80a2b737b165b39498afebf689dab8108cf6 Mon Sep 17 00:00:00 2001
From: Pietro Mingo
Date: Tue, 30 Aug 2022 11:59:35 +0200
Subject: [PATCH 16/18] missing archive
---
django_mailbox/models.py | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/django_mailbox/models.py b/django_mailbox/models.py
index c7c03f32..42296e98 100644
--- a/django_mailbox/models.py
+++ b/django_mailbox/models.py
@@ -251,7 +251,8 @@ def get_connection(self):
conn = Office365Transport(
self.location,
self.username,
- folder=self.folder
+ folder=self.folder,
+ archive=self.archive
)
conn.connect(self.client_id, self.client_secret, self.tenant_id)
elif self.type == 'maildir':
From 21faa22404e23cd0535e0b15ff0cad1d0a5c0535 Mon Sep 17 00:00:00 2001
From: Pietro Mingo
Date: Sun, 24 Dec 2023 12:53:18 +0100
Subject: [PATCH 17/18] MailboxAttachment __str__ change
---
django_mailbox/models.py | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/django_mailbox/models.py b/django_mailbox/models.py
index 4e0c94b4..c623cdf3 100644
--- a/django_mailbox/models.py
+++ b/django_mailbox/models.py
@@ -881,7 +881,8 @@ def __getitem__(self, name):
return value
def __str__(self):
- return self.document.url
+ return f'{self.message}: {self.document.url if self.document else None}'
+
class Meta:
verbose_name = _('Message attachment')
From 7570a0d37a05951b8f2211de72713bd5d7248a01 Mon Sep 17 00:00:00 2001
From: Pietro
Date: Wed, 27 Dec 2023 00:40:48 +0100
Subject: [PATCH 18/18] Update django_mailbox/models.py
Co-authored-by: Pascal Fouque
---
django_mailbox/models.py | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/django_mailbox/models.py b/django_mailbox/models.py
index c623cdf3..a27b5b25 100644
--- a/django_mailbox/models.py
+++ b/django_mailbox/models.py
@@ -881,7 +881,9 @@ def __getitem__(self, name):
return value
def __str__(self):
- return f'{self.message}: {self.document.url if self.document else None}'
+ if self.document:
+ return f'{self.get_filename()}: {self.document.url}'
+ return self.get_filename()
class Meta: