1
0
Fork 1
mirror of https://github.com/coddingtonbear/django-mailbox.git synced 2026-07-09 22:38:19 +02:00

adds testable repo with added features

This commit is contained in:
Joe 2021-02-03 23:05:48 -05:00
parent 62805f324e
commit c7997ebd9b
58 changed files with 2421 additions and 1541 deletions

View file

@ -1,7 +0,0 @@
[bumpversion]
current_version = 4.8.2
commit = True
tag = True
tag_name = {new_version}
[bumpversion:file:django_mailbox/__init__.py]

View file

@ -1,2 +1,4 @@
[flake8]
exclude = django_mailbox/migrations, django_mailbox/south_migrations
max-line-length = 200
exclude = docs, django_mailbox/migrations, django_mailbox/south_migrations
ignore = E231,W503

1
.gitignore vendored
View file

@ -16,3 +16,4 @@ messages
manage.py
old/
poetry.lock
websrv

View file

@ -1,46 +0,0 @@
version: ~> 1.0
os: linux
language: python
jobs:
- env: TOX_ENV=docs
python: 3.8
# sort by django version, next to by python version
# Django 1.11 support all supported Python interpreter
- env: TOX_ENV=django111-py35
python: 3.5
- env: TOX_ENV=django111-py36
python: 3.6
# Django 2.0 support 3.5, 3.6
- env: TOX_ENV=django20-py35
python: 3.5
- env: TOX_ENV=django20-py36
python: 3.6
# Django 2.1 support Python 3.5, 3.6, 3.7
- env: TOX_ENV=django21-py35
python: 3.5
- env: TOX_ENV=django21-py36
python: 3.6
- env: TOX_ENV=django21-py37
python: 3.7
# Django 2.2 support Python 3.5, 3.6, 3.7
- env: TOX_ENV=django22-py35
python: 3.5
- env: TOX_ENV=django22-py36
python: 3.6
- env: TOX_ENV=django22-py37
python: 3.7
# Django 3.0 (beta) support Python 3.6, 3.7, 3.8
- env: TOX_ENV=django30-py36
python: 3.6
- env: TOX_ENV=django30-py37
python: 3.7
- env: TOX_ENV=django30-py38 INTEGRATION=1
python: 3.8
install:
- pip install tox
script:
- tox -e $TOX_ENV

View file

@ -8,10 +8,11 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
## [0.1.1] - 2021-02-03
### Added
- adds modified documenation
- adds testable repo with added features
## [0.1.0] - 2021-02-03
### Added
- bot - adds changelog
### Changed
- changes repo to customized solution
- changes repo to customized solution

View file

@ -1,9 +0,0 @@
include *.rst
include .travis.yml
include MANIFEST
recursive-include django_mailbox *.eml
recursive-include docs *.py
recursive-include docs *.rst
recursive-include docs Makefile
recursive-include django_mailbox/locale *.po
recursive-include django_mailbox/locale *.mo

View file

@ -1,3 +1,3 @@
__version__ = '4.8.2'
__version__ = "4.8.2"
default_app_config = 'django_mailbox.apps.MailBoxConfig'
default_app_config = "django_mailbox.apps.MailBoxConfig"

View file

@ -23,35 +23,38 @@ def get_new_mail(mailbox_admin, request, queryset):
queryset.get_new_mail()
get_new_mail.short_description = _('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)
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')
)
resend_message_received_signal.short_description = _("Re-send message received signal")
class MailboxAdmin(admin.ModelAdmin):
list_display = (
'name',
'uri',
'from_email',
'active',
'last_polling',
"name",
"uri",
"from_email",
"active",
"last_polling",
)
readonly_fields = ['last_polling', ]
readonly_fields = [
"last_polling",
]
actions = [get_new_mail]
class MessageAttachmentAdmin(admin.ModelAdmin):
raw_id_fields = ('message', )
list_display = ('message', 'document',)
raw_id_fields = ("message",)
list_display = (
"message",
"document",
)
class MessageAttachmentInline(admin.TabularInline):
@ -63,50 +66,44 @@ class MessageAdmin(admin.ModelAdmin):
def attachment_count(self, msg):
return msg.attachments.count()
attachment_count.short_description = _('Attachment 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()]
)
return "\n".join([("{}: {}".format(h, v)) for h, v in email.items()])
inlines = [
MessageAttachmentInline,
]
list_display = (
'subject',
'processed',
'read',
'mailbox',
'outgoing',
'attachment_count',
"subject",
"processed",
"read",
"mailbox",
"outgoing",
"attachment_count",
)
ordering = ['-processed']
ordering = ["-processed"]
list_filter = (
'mailbox',
'outgoing',
'processed',
'read',
)
exclude = (
'body',
)
raw_id_fields = (
'in_reply_to',
"mailbox",
"outgoing",
"processed",
"read",
)
exclude = ("body",)
raw_id_fields = ("in_reply_to",)
readonly_fields = (
'envelope_headers',
'text',
'html',
"envelope_headers",
"text",
"html",
)
actions = [resend_message_received_signal]
if getattr(settings, 'DJANGO_MAILBOX_ADMIN_ENABLED', True):
if getattr(settings, "DJANGO_MAILBOX_ADMIN_ENABLED", True):
admin.site.register(Message, MessageAdmin)
admin.site.register(MessageAttachment, MessageAttachmentAdmin)
admin.site.register(Mailbox, MailboxAdmin)

View file

@ -3,5 +3,5 @@ from django.utils.translation import gettext_lazy as _
class MailBoxConfig(AppConfig):
name = 'django_mailbox'
name = "django_mailbox"
verbose_name = _("Mail Box")

View file

@ -28,7 +28,7 @@ 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']
return me.extra_data["access_token"]
except (UserSocialAuth.DoesNotExist, KeyError):
raise AccessTokenNotFound
@ -45,7 +45,7 @@ def update_google_extra_data(email, extra_data):
def get_google_refresh_token(email):
try:
me = UserSocialAuth.objects.get(uid=email, provider="google-oauth2")
return me.extra_data['refresh_token']
return me.extra_data["refresh_token"]
except (UserSocialAuth.DoesNotExist, KeyError):
raise RefreshTokenNotFound
@ -72,9 +72,11 @@ 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),
))
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)
@ -92,20 +94,20 @@ def refresh_authorization(email):
refresh_token=refresh_token,
client_id=get_google_consumer_key(),
client_secret=get_google_consumer_secret(),
grant_type='refresh_token',
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})
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"
email, "https://www.googleapis.com/oauth2/v1/userinfo?alt=json"
)
return result

View file

@ -13,18 +13,11 @@ class Command(BaseCommand):
def handle(self, *args, **options):
mailboxes = Mailbox.active_mailboxes.all()
if args:
mailboxes = mailboxes.filter(
name=' '.join(args)
)
mailboxes = mailboxes.filter(name=" ".join(args))
for mailbox in mailboxes:
logger.info(
'Gathering messages for %s',
mailbox.name
)
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
"Received %s (from %s)", message.subject, message.from_address
)

View file

@ -1,6 +1,7 @@
import email
import logging
import sys
try:
from email import utils
except ImportError:
@ -21,9 +22,9 @@ class Command(BaseCommand):
def add_arguments(self, parser):
parser.add_argument(
'mailbox_name',
nargs='?',
help="The name of the mailbox that will receive the message"
"mailbox_name",
nargs="?",
help="The name of the mailbox that will receive the message",
)
def handle(self, mailbox_name=None, *args, **options):
@ -34,10 +35,7 @@ class Command(BaseCommand):
else:
mailbox = self.get_mailbox_for_message(message)
mailbox.process_incoming_message(message)
logger.info(
"Message received from %s",
message['from']
)
logger.info("Message received from %s", message["from"])
else:
logger.warning("Message not processable.")
@ -48,5 +46,5 @@ class Command(BaseCommand):
return mailbox
def get_mailbox_for_message(self, message):
email_address = utils.parseaddr(message['to'])[1][0:255]
email_address = utils.parseaddr(message["to"])[1][0:255]
return self.get_mailbox_by_name(email_address)

View file

@ -11,7 +11,7 @@ logging.basicConfig(level=logging.INFO)
class Command(BaseCommand):
""" Briefly, a bug existed in a migration that may have caused message
"""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.
@ -22,14 +22,13 @@ class Command(BaseCommand):
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'
)
).order_by("id")
if attachments_without_messages.count() < 1:
return
@ -40,13 +39,13 @@ class Command(BaseCommand):
md5.update(chunk)
attachment_hash_map[md5.hexdigest()] = attachment.pk
for message_record in Message.objects.all().order_by('id'):
for message_record in Message.objects.all().order_by("id"):
message = email.message_from_string(message_record.body)
if message.is_multipart():
for part in message.walk():
if part.get_content_maintype() == 'multipart':
if part.get_content_maintype() == "multipart":
continue
if part.get('Content-Disposition') is None:
if part.get("Content-Disposition") is None:
continue
md5 = hashlib.md5()
md5.update(part.get_payload(decode=True))
@ -61,11 +60,11 @@ class Command(BaseCommand):
"Associated message %s with attachment %s (%s)",
message_record.pk,
attachment.pk,
digest
digest,
)
else:
logger.info(
"%s(%s) not found in currently-stored attachments",
part.get_filename(),
digest
digest,
)

View file

@ -3,54 +3,163 @@ from django.db import models, migrations
class Migration(migrations.Migration):
dependencies = [
]
dependencies = []
operations = [
migrations.CreateModel(
name='Mailbox',
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 <br /><br />Internet transports include 'imap' and 'pop3'; common local file transports include 'maildir', 'mbox', and less commonly 'babyl', 'mh', and 'mmdf'. <br /><br />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 &lt;mailbot@yourdomain.com&gt;<br />'From' header to set for outgoing email.<br /><br />If you do not use this e-mail inbox for outgoing mail, this setting is unnecessary.<br />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')),
(
"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 <br /><br />Internet transports include 'imap' and 'pop3'; common local file transports include 'maildir', 'mbox', and less commonly 'babyl', 'mh', and 'mmdf'. <br /><br />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 &lt;mailbot@yourdomain.com&gt;<br />'From' header to set for outgoing email.<br /><br />If you do not use this e-mail inbox for outgoing mail, this setting is unnecessary.<br />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',
"verbose_name_plural": "Mailboxes",
},
bases=(models.Model,),
),
migrations.CreateModel(
name='Message',
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)),
(
"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={
},
options={},
bases=(models.Model,),
),
migrations.CreateModel(
name='MessageAttachment',
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)),
(
"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={
},
options={},
bases=(models.Model,),
),
]

View file

@ -4,14 +4,19 @@ from django.db import models, migrations
class Migration(migrations.Migration):
dependencies = [
('django_mailbox', '0001_initial'),
("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'),
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,
),
]

View file

@ -4,13 +4,18 @@ from django.db import models, migrations
class Migration(migrations.Migration):
dependencies = [
('django_mailbox', '0002_add_eml_to_message'),
("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'),
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",
),
),
]

View file

@ -4,18 +4,25 @@ from django.db import models, migrations
class Migration(migrations.Migration):
dependencies = [
('django_mailbox', '0003_auto_20150409_0316'),
("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'),
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/'),
model_name="messageattachment",
name="document",
field=models.FileField(
verbose_name="Document", upload_to="mailbox_attachments/%Y/%m/%d/"
),
),
]

View file

@ -5,13 +5,16 @@ import django_mailbox.utils
class Migration(migrations.Migration):
dependencies = [
('django_mailbox', '0004_bytestring_to_unicode'),
("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'),
model_name="messageattachment",
name="document",
field=models.FileField(
upload_to=django_mailbox.utils.get_attachment_save_path,
verbose_name="Document",
),
),
]

View file

@ -6,13 +6,18 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('django_mailbox', '0005_auto_20160523_2240'),
("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'),
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",
),
),
]

View file

@ -6,20 +6,26 @@ from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('django_mailbox', '0006_mailbox_last_polling'),
("django_mailbox", "0006_mailbox_last_polling"),
]
operations = [
migrations.AlterModelOptions(
name='mailbox',
options={'verbose_name': 'Mailbox', 'verbose_name_plural': 'Mailboxes'},
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'},
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'},
name="messageattachment",
options={
"verbose_name": "Message attachment",
"verbose_name_plural": "Message attachments",
},
),
]

View file

@ -6,18 +6,25 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('django_mailbox', '0007_auto_20180421_0026'),
("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'),
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'),
model_name="message",
name="outgoing",
field=models.BooleanField(
blank=True, default=False, verbose_name="Outgoing"
),
),
]

View file

@ -15,7 +15,8 @@ import email
import logging
import mimetypes
import os.path
import sys
# import sys
import uuid
from tempfile import NamedTemporaryFile
@ -29,9 +30,16 @@ 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
from django_mailbox.transports import (
Pop3Transport,
ImapTransport,
MaildirTransport,
MboxTransport,
BabylTransport,
MHTransport,
MMDFTransport,
GmailImapTransport,
)
logger = logging.getLogger(__name__)
@ -40,9 +48,9 @@ class MailboxQuerySet(models.QuerySet):
def get_new_mail(self):
count = 0
for mailbox in self.all():
logger.debug('Receiving mail for %s' % mailbox)
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')
logger.debug("Received %d %s.", count, "mails" if count != 1 else "mail")
class MailboxManager(models.Manager):
@ -52,72 +60,86 @@ class MailboxManager(models.Manager):
class ActiveMailboxManager(MailboxManager):
def get_queryset(self):
return super().get_queryset().filter(
active=True,
return (
super()
.get_queryset()
.filter(
active=True,
)
)
class Mailbox(models.Model):
name = models.CharField(
_('Name'),
_("Name"),
max_length=255,
)
uri = models.CharField(
_('URI'),
_("URI"),
max_length=255,
help_text=(_(
"Example: imap+ssl://myusername:mypassword@someserver <br />"
"<br />"
"Internet transports include 'imap' and 'pop3'; "
"common local file transports include 'maildir', 'mbox', "
"and less commonly 'babyl', 'mh', and 'mmdf'. <br />"
"<br />"
"Be sure to urlencode your username and password should they "
"contain illegal characters (like @, :, etc)."
)),
help_text=(
_(
"Example: imap+ssl://myusername:mypassword@someserver <br />"
"<br />"
"Internet transports include 'imap' and 'pop3'; "
"common local file transports include 'maildir', 'mbox', "
"and less commonly 'babyl', 'mh', and 'mmdf'. <br />"
"<br />"
"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'),
_("From email"),
max_length=255,
help_text=(_(
"Example: MailBot &lt;mailbot@yourdomain.com&gt;<br />"
"'From' header to set for outgoing email.<br />"
"<br />"
"If you do not use this e-mail inbox for outgoing mail, this "
"setting is unnecessary.<br />"
"If you send e-mail without setting this, your 'From' header will'"
"be set to match the setting `DEFAULT_FROM_EMAIL`."
)),
help_text=(
_(
"Example: MailBot &lt;mailbot@yourdomain.com&gt;<br />"
"'From' header to set for outgoing email.<br />"
"<br />"
"If you do not use this e-mail inbox for outgoing mail, this "
"setting is unnecessary.<br />"
"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. "
)),
_("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.")),
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
null=True,
)
objects = MailboxManager()
@ -153,30 +175,30 @@ class Mailbox(models.Model):
@property
def location(self):
"""Returns the location (domain and path) of messages."""
return self._domain if self._domain else '' + self._protocol_info.path
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]
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()
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()
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)
archive_folder = self._query_string.get("archive", None)
if not archive_folder:
return None
return archive_folder[0]
@ -184,7 +206,7 @@ class Mailbox(models.Model):
@property
def folder(self):
"""Returns (if specified) the folder to fetch mail from."""
folder = self._query_string.get('folder', None)
folder = self._query_string.get("folder", None)
if not folder:
return None
return folder[0]
@ -198,40 +220,38 @@ class Mailbox(models.Model):
"""
if not self.uri:
return None
elif self.type == 'imap':
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
folder=self.folder,
)
conn.connect(self.username, self.password)
elif self.type == 'gmail':
elif self.type == "gmail":
conn = GmailImapTransport(
self.location,
port=self.port if self.port else None,
ssl=True,
archive=self.archive
archive=self.archive,
)
conn.connect(self.username, self.password)
elif self.type == 'pop3':
elif self.type == "pop3":
conn = Pop3Transport(
self.location,
port=self.port if self.port else None,
ssl=self.use_ssl
self.location, port=self.port if self.port else None, ssl=self.use_ssl
)
conn.connect(self.username, self.password)
elif self.type == 'maildir':
elif self.type == "maildir":
conn = MaildirTransport(self.location)
elif self.type == 'mbox':
elif self.type == "mbox":
conn = MboxTransport(self.location)
elif self.type == 'babyl':
elif self.type == "babyl":
conn = BabylTransport(self.location)
elif self.type == 'mh':
elif self.type == "mh":
conn = MHTransport(self.location)
elif self.type == 'mmdf':
elif self.type == "mmdf":
conn = MMDFTransport(self.location)
return conn
@ -264,29 +284,22 @@ class Mailbox(models.Model):
for header, value in msg.items():
new[header] = value
for part in msg.get_payload():
new.attach(
self._get_dehydrated_message(part, record)
)
new.attach(self._get_dehydrated_message(part, record))
elif (
settings['strip_unallowed_mimetypes']
and not msg.get_content_type() in settings['allowed_mimetypes']
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', ''))
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()
@ -297,17 +310,13 @@ class Mailbox(models.Model):
else:
_, extension = os.path.splitext(filename)
if not extension:
extension = '.bin'
extension = ".bin"
attachment = MessageAttachment()
attachment.document.save(
uuid.uuid4().hex + extension,
ContentFile(
BytesIO(
msg.get_payload(decode=True)
).getvalue()
)
ContentFile(BytesIO(msg.get_payload(decode=True)).getvalue()),
)
attachment.message = record
for key, value in msg.items():
@ -315,14 +324,14 @@ class Mailbox(models.Model):
attachment.save()
placeholder = EmailMessage()
placeholder[
settings['attachment_interpolation_header']
] = str(attachment.pk)
placeholder[settings["attachment_interpolation_header"]] = str(
attachment.pk
)
new = placeholder
else:
content_charset = msg.get_content_charset()
if not content_charset:
content_charset = 'ascii'
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
@ -330,26 +339,15 @@ class Mailbox(models.Model):
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'
)
"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'
)
content_charset,
)
msg.set_payload(msg.get_payload(decode=True).decode("ascii", "ignore"))
new = msg
return new
@ -358,23 +356,19 @@ class Mailbox(models.Model):
msg._email_object = message
settings = utils.get_settings()
if settings['store_original_message']:
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']
)
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:
@ -382,13 +376,16 @@ class Mailbox(models.Model):
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,)
logger.warning(
"Failed to parse message: %s",
exc,
)
return None
msg.set_body(body)
if message['in-reply-to']:
if message["in-reply-to"]:
try:
msg.in_reply_to = Message.objects.filter(
message_id=message['in-reply-to'].strip()
message_id=message["in-reply-to"].strip()
)[0]
except IndexError:
pass
@ -397,36 +394,31 @@ class Mailbox(models.Model):
def _process_save_original_message(self, message, msg):
settings = utils.get_settings()
if settings['compress_original_message']:
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
)
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
"%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 = []
# Delete this
# 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:
if msg is not None:
yield msg
self.last_polling = now()
if django.VERSION >= (1, 5): # Django 1.5 introduces update_fields
self.save(update_fields=['last_polling'])
self.save(update_fields=["last_polling"])
else:
self.save()
@ -434,100 +426,97 @@ class Mailbox(models.Model):
return self.name
class Meta:
verbose_name = _('Mailbox')
verbose_name_plural = _('Mailboxes')
verbose_name = _("Mailbox")
verbose_name_plural = _("Mailboxes")
class IncomingMessageManager(models.Manager):
def get_queryset(self):
return super().get_queryset().filter(
outgoing=False,
return (
super()
.get_queryset()
.filter(
outgoing=False,
)
)
class OutgoingMessageManager(models.Manager):
def get_queryset(self):
return super().get_queryset().filter(
outgoing=True,
return (
super()
.get_queryset()
.filter(
outgoing=True,
)
)
class UnreadMessageManager(models.Manager):
def get_queryset(self):
return super().get_queryset().filter(
read=None
)
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
related_name="messages",
verbose_name=_("Mailbox"),
on_delete=models.CASCADE,
)
subject = models.CharField(
_('Subject'),
max_length=255
)
subject = models.CharField(_("Subject"), max_length=255)
message_id = models.CharField(
_('Message ID'),
max_length=255
)
message_id = models.CharField(_("Message ID"), max_length=255)
in_reply_to = models.ForeignKey(
'django_mailbox.Message',
related_name='replies',
"django_mailbox.Message",
related_name="replies",
blank=True,
null=True,
verbose_name=_('In reply to'),
on_delete=models.CASCADE
verbose_name=_("In reply to"),
on_delete=models.CASCADE,
)
from_header = models.CharField(
_('From header'),
_("From header"),
max_length=255,
)
to_header = models.TextField(
_('To header'),
_("To header"),
)
outgoing = models.BooleanField(
_('Outgoing'),
_("Outgoing"),
default=False,
blank=True,
)
body = models.TextField(
_('Body'),
_("Body"),
)
encoded = models.BooleanField(
_('Encoded'),
_("Encoded"),
default=False,
help_text=_('True if the e-mail body is Base64 encoded'),
help_text=_("True if the e-mail body is Base64 encoded"),
)
processed = models.DateTimeField(
_('Processed'),
auto_now_add=True
)
processed = models.DateTimeField(_("Processed"), auto_now_add=True)
read = models.DateTimeField(
_('Read'),
_("Read"),
default=None,
blank=True,
null=True,
)
eml = models.FileField(
_('Raw message contents'),
_("Raw message contents"),
null=True,
upload_to="messages",
help_text=_('Original full content of message')
help_text=_("Original full content of message"),
)
objects = models.Manager()
unread_messages = UnreadMessageManager()
@ -570,13 +559,9 @@ class Message(models.Model):
def to_addresses(self):
"""Returns a list of addresses to which this message was sent."""
addresses = []
for address in self.to_header.split(','):
for address in self.to_header.split(","):
if address:
addresses.append(
parseaddr(
address
)[1].lower()
)
addresses.append(parseaddr(address)[1].lower())
return addresses
def reply(self, message):
@ -593,14 +578,12 @@ class Message(models.Model):
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.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()
)
email.message_from_string(message.message().as_string())
)
@property
@ -608,18 +591,22 @@ class Message(models.Model):
"""
Returns the message body matching content type 'text/plain'.
"""
return utils.get_body_from_message(
self.get_email_object(), 'text', 'plain'
).replace('=\n', '').strip()
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()
return (
utils.get_body_from_message(self.get_email_object(), "text", "html")
.replace("\n", "")
.strip()
)
def _rehydrate(self, msg):
new = EmailMessage()
@ -629,53 +616,43 @@ class Message(models.Model):
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():
new.attach(self._rehydrate(part))
elif settings["attachment_interpolation_header"] in msg.keys():
try:
attachment = MessageAttachment.objects.get(
pk=msg[settings['attachment_interpolation_header']]
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':
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()
),
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'
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']
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[
settings["altered_message_header"]
] = "Missing; Attachment %s not found" % (
msg[settings["attachment_interpolation_header"]]
)
new.set_payload('')
new.set_payload("")
else:
for header, value in msg.items():
new[header] = value
new.set_payload(
msg.get_payload()
)
new.set_payload(msg.get_payload())
return new
def get_body(self):
@ -686,8 +663,8 @@ class Message(models.Model):
"""
if self.encoded:
return base64.b64decode(self.body.encode('ascii'))
return self.body.encode('utf-8')
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.
@ -698,7 +675,7 @@ class Message(models.Model):
"""
self.encoded = True
self.body = base64.b64encode(body.encode('utf-8')).decode('ascii')
self.body = base64.b64encode(body.encode("utf-8")).decode("ascii")
def get_email_object(self):
"""Returns an `email.message.EmailMessage` instance representing the
@ -719,9 +696,9 @@ class Message(models.Model):
(https://docs.python.org/3/library/email.message.html)
"""
if not hasattr(self, '_email_object'): # Cache fill
if not hasattr(self, "_email_object"): # Cache fill
if self.eml:
if self.eml.name.endswith('.gz'):
if self.eml.name.endswith(".gz"):
body = gzip.GzipFile(fileobj=self.eml).read()
else:
self.eml.open()
@ -744,28 +721,28 @@ class Message(models.Model):
return self.subject
class Meta:
verbose_name = _('E-mail message')
verbose_name_plural = _('E-mail messages')
verbose_name = _("E-mail message")
verbose_name_plural = _("E-mail messages")
class MessageAttachment(models.Model):
message = models.ForeignKey(
Message,
related_name='attachments',
related_name="attachments",
null=True,
blank=True,
verbose_name=_('Message'),
on_delete=models.CASCADE
verbose_name=_("Message"),
on_delete=models.CASCADE,
)
headers = models.TextField(
_('Headers'),
_("Headers"),
null=True,
blank=True,
)
document = models.FileField(
_('Document'),
_("Document"),
upload_to=utils.get_attachment_save_path,
)
@ -810,12 +787,12 @@ class MessageAttachment(models.Model):
def __getitem__(self, name):
value = self._get_rehydrated_headers()[name]
if value is None:
raise KeyError('Header %s does not exist' % name)
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')
verbose_name = _("Message attachment")
verbose_name_plural = _("Message attachments")

View file

@ -1,3 +1,3 @@
from django.dispatch.dispatcher import Signal
message_received = Signal(providing_args=['message'])
message_received = Signal(providing_args=["message"])

View file

@ -5,54 +5,92 @@ 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'])
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'])
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')
db.delete_table("django_mailbox_mailbox")
# Deleting model 'Message'
db.delete_table('django_mailbox_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.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"}),
},
'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']
complete_apps = ["django_mailbox"]

View file

@ -5,34 +5,64 @@ 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))
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.")
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.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"}),
},
'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']
complete_apps = ["django_mailbox"]

View file

@ -5,37 +5,62 @@ 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)
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')
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.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"}),
},
'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']
complete_apps = ["django_mailbox"]

View file

@ -5,38 +5,67 @@ 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)
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')
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.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"}),
},
'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']
complete_apps = ["django_mailbox"]

View file

@ -5,34 +5,58 @@ 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')
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')
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.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"}),
},
'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']
complete_apps = ["django_mailbox"]

View file

@ -5,51 +5,116 @@ 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)
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'])
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')
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')
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.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"}),
},
'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']
complete_apps = ["django_mailbox"]

View file

@ -5,55 +5,108 @@ from django.db import models
class Migration(SchemaMigration):
def forwards(self, orm):
# Deleting field 'Message.address'
db.delete_column('django_mailbox_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)
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)
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.")
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')
db.delete_column("django_mailbox_message", "from_header")
# Deleting field 'Message.to_header'
db.delete_column('django_mailbox_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.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", [], {}),
},
'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']
complete_apps = ["django_mailbox"]

View file

@ -4,43 +4,89 @@ from south.db import db
from south.v2 import DataMigration
from django.db import models
class Migration(DataMigration):
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']
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.')
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.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", [], {}),
},
'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']
complete_apps = ["django_mailbox"]
symmetrical = True

View file

@ -5,43 +5,94 @@ 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')
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'])
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.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", [], {}),
},
'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']
complete_apps = ["django_mailbox"]

View file

@ -5,41 +5,90 @@ 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)
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')
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.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", [], {}),
},
'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']
complete_apps = ["django_mailbox"]

View file

@ -5,42 +5,95 @@ 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)
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')
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.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.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']
complete_apps = ["django_mailbox"]

View file

@ -3,64 +3,148 @@ from south.db import db
from south.v2 import SchemaMigration
from django.db import models
class Migration(SchemaMigration):
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'])
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'])
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')
db.delete_table("django_mailbox_messageattachment")
# Removing M2M table for field attachments on 'Message'
db.delete_table('django_mailbox_message_attachments')
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.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.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"}),
},
'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']
complete_apps = ["django_mailbox"]

View file

@ -5,49 +5,127 @@ 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)
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')
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.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.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']",
},
),
},
'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']
complete_apps = ["django_mailbox"]

View file

@ -6,49 +6,121 @@ 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 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():
for attachment in orm["django_mailbox.MessageAttachment"].objects.all():
if attachment.message:
attachment.message.attachments.add(
attachment
)
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.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.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']",
},
),
},
'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']
complete_apps = ["django_mailbox"]

View file

@ -5,60 +5,146 @@ 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')
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)
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'])
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')
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.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.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']",
},
),
},
'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']
complete_apps = ["django_mailbox"]

View file

@ -5,50 +5,122 @@ 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)
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')
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.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.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']",
},
),
},
'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']
complete_apps = ["django_mailbox"]

View file

@ -5,51 +5,129 @@ 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)
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')
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.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.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']",
},
),
},
'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']
complete_apps = ["django_mailbox"]

View file

@ -17,39 +17,37 @@ def get_email_as_text(name):
with open(
os.path.join(
os.path.dirname(__file__),
'messages',
"messages",
name,
),
'rb'
"rb",
) as f:
return f.read()
class EmailMessageTestCase(TestCase):
ALLOWED_EXTRA_HEADERS = [
'MIME-Version',
'Content-Transfer-Encoding',
"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._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.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.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_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
@ -80,10 +78,10 @@ class EmailMessageTestCase(TestCase):
with open(
os.path.join(
os.path.dirname(__file__),
'messages',
"messages",
name,
),
'rb'
"rb",
) as f:
return f.read()
@ -92,26 +90,26 @@ class EmailMessageTestCase(TestCase):
return email.message_from_bytes(copy)
def _headers_identical(self, left, right, header=None):
""" Check if headers are (close enough to) identical.
"""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.
* 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':
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 ', ' ')
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
@ -152,10 +150,7 @@ class EmailMessageTestCase(TestCase):
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]
)
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:
@ -170,8 +165,7 @@ class EmailMessageTestCase(TestCase):
def _raise_mismatched(self, left, right):
raise AssertionError(
"Message payloads do not match:\n{}\n{}".format(
left.as_string(),
right.as_string()
left.as_string(), right.as_string()
)
)

View file

@ -1,12 +1,12 @@
DATABASES = {
'default': {
'NAME': 'db.sqlite3',
'ENGINE': 'django.db.backends.sqlite3',
"default": {
"NAME": "db.sqlite3",
"ENGINE": "django.db.backends.sqlite3",
},
}
INSTALLED_APPS = [
'django.contrib.auth',
'django.contrib.contenttypes',
'django_mailbox',
"django.contrib.auth",
"django.contrib.contenttypes",
"django_mailbox",
]
SECRET_KEY = 'beepboop'
SECRET_KEY = "beepboop"

View file

@ -8,16 +8,14 @@ from django_mailbox.models import Mailbox
from django_mailbox.tests.base import EmailMessageTestCase
__all__ = ['TestImap']
__all__ = ["TestImap"]
class TestImap(EmailMessageTestCase):
def setUp(self):
super().setUp()
self.test_imap_server = (
os.environ.get('EMAIL_IMAP_SERVER')
)
self.test_imap_server = os.environ.get("EMAIL_IMAP_SERVER")
required_settings = [
self.test_imap_server,
@ -35,8 +33,7 @@ class TestImap(EmailMessageTestCase):
)
self.mailbox = Mailbox.objects.create(
name='Integration Test Imap',
uri=self.get_connection_string()
name="Integration Test Imap", uri=self.get_connection_string()
)
self.arbitrary_identifier = str(uuid.uuid4())
@ -48,20 +45,19 @@ class TestImap(EmailMessageTestCase):
)
def test_get_imap_message(self):
text_content = 'This is some content'
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
self.mailbox, condition=lambda m: m["subject"] == self.arbitrary_identifier
)
message = next(messages)

View file

@ -5,15 +5,15 @@ from django.test import TestCase
from django_mailbox.models import Mailbox
__all__ = ['TestMailbox']
__all__ = ["TestMailbox"]
class TestMailbox(TestCase):
def test_protocol_info(self):
mailbox = Mailbox()
mailbox.uri = 'alpha://test.com'
mailbox.uri = "alpha://test.com"
expected_protocol = 'alpha'
expected_protocol = "alpha"
actual_protocol = mailbox._protocol_info.scheme
self.assertEqual(
@ -23,24 +23,30 @@ class TestMailbox(TestCase):
def test_last_polling_field_exists(self):
mailbox = Mailbox()
self.assertTrue(hasattr(mailbox, 'last_polling'))
self.assertTrue(hasattr(mailbox, "last_polling"))
def test_get_new_mail_update_last_polling(self):
mailbox = Mailbox.objects.create(uri="mbox://" + os.path.join(
mailbox = Mailbox.objects.create(
uri="mbox://"
+ os.path.join(
os.path.dirname(__file__),
'messages',
'generic_message.eml',
))
"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(
mailbox = Mailbox.objects.create(
uri="mbox://"
+ os.path.join(
os.path.dirname(__file__),
'messages',
'generic_message.eml',
))
"messages",
"generic_message.eml",
)
)
Mailbox.objects.filter(pk=mailbox.pk).get_new_mail()
mailbox.refresh_from_db()
self.assertNotEqual(mailbox.last_polling, None)

View file

@ -7,20 +7,20 @@ from django_mailbox.models import Message
from django_mailbox.tests.base import EmailMessageTestCase
__all__ = ['TestMessageFlattening']
__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',
"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',
"message_with_many_multiparts.eml",
)
models.TEXT_STORED_MIMETYPES = ['text/plain']
models.TEXT_STORED_MIMETYPES = ["text/plain"]
msg = self.mailbox.process_incoming_message(incoming_email_object)
@ -33,12 +33,12 @@ class TestMessageFlattening(EmailMessageTestCase):
def test_base64_message_is_properly_rehydrated(self):
incoming_email_object = self._get_email_object(
'message_with_attachment.eml',
"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',
"message_with_attachment.eml",
)
msg = self.mailbox.process_incoming_message(incoming_email_object)
@ -52,10 +52,10 @@ class TestMessageFlattening(EmailMessageTestCase):
def test_message_handles_rehydration_problems(self):
incoming_email_object = self._get_email_object(
'message_with_defective_attachment_association.eml',
"message_with_defective_attachment_association.eml",
)
expected_email_object = self._get_email_object(
'message_with_defective_attachment_association_result.eml',
"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
@ -74,17 +74,17 @@ class TestMessageFlattening(EmailMessageTestCase):
def test_message_content_type_stripping(self):
incoming_email_object = self._get_email_object(
'message_with_many_multiparts.eml',
"message_with_many_multiparts.eml",
)
expected_email_object = self._get_email_object(
'message_with_many_multiparts_stripped_html.eml',
"message_with_many_multiparts_stripped_html.eml",
)
default_settings = utils.get_settings()
with mock.patch('django_mailbox.utils.get_settings') as 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']
altered["strip_unallowed_mimetypes"] = True
altered["allowed_mimetypes"] = ["text/plain"]
get_settings.return_value = altered
@ -100,7 +100,7 @@ class TestMessageFlattening(EmailMessageTestCase):
def test_message_processing_unknown_encoding(self):
incoming_email_object = self._get_email_object(
'message_with_invalid_encoding.eml',
"message_with_invalid_encoding.eml",
)
msg = self.mailbox.process_incoming_message(incoming_email_object)

View file

@ -1,6 +1,7 @@
import gzip
import os.path
import sys
# import sys
import copy
from unittest import mock
@ -12,64 +13,56 @@ from django_mailbox.tests.base import EmailMessageTestCase
from django.utils.encoding import force_text
from django.core.mail import EmailMessage
__all__ = ['TestProcessEmail']
__all__ = ["TestProcessEmail"]
class TestProcessEmail(EmailMessageTestCase):
def test_message_without_attachments(self):
message = self._get_email_object('generic_message.eml')
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.mailbox, mailbox)
self.assertEqual(msg.subject, "Message Without Attachment")
self.assertEqual(
msg.message_id,
(
'<CAMdmm+hGH8Dgn-_0xnXJCd=PhyNAiouOYm5zFP0z'
'-foqTO60zA@mail.gmail.com>'
)
("<CAMdmm+hGH8Dgn-_0xnXJCd=PhyNAiouOYm5zFP0z" "-foqTO60zA@mail.gmail.com>"),
)
self.assertEqual(
msg.from_header,
'Adam Coddington <test@adamcoddington.net>',
"Adam Coddington <test@adamcoddington.net>",
)
self.assertEqual(
msg.to_header,
'Adam Coddington <test@adamcoddington.net>',
"Adam Coddington <test@adamcoddington.net>",
)
def test_message_with_encoded_attachment_filenames(self):
message = self._get_email_object(
'message_with_koi8r_filename_attachments.eml'
)
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()
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()
"\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()
"\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()
"\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')
message = self._get_email_object("message_with_attachment.eml")
mailbox = Mailbox.objects.create()
msg = mailbox.process_incoming_message(message)
@ -85,16 +78,16 @@ class TestProcessEmail(EmailMessageTestCase):
attachment = msg.attachments.all()[0]
self.assertEqual(
attachment.get_filename(),
'heart.png',
"heart.png",
)
def test_message_with_utf8_attachment_header(self):
""" Ensure that we properly handle UTF-8 encoded attachment
"""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',
"message_with_utf8_attachment.eml",
)
mailbox = Mailbox.objects.create()
msg = mailbox.process_incoming_message(email_object)
@ -108,24 +101,18 @@ class TestProcessEmail(EmailMessageTestCase):
)
attachment = msg.attachments.all()[0]
self.assertEqual(
attachment.get_filename(),
'pi\u0142kochwyty.jpg'
)
self.assertEqual(attachment.get_filename(), "pi\u0142kochwyty.jpg")
attachment = msg.attachments.all()[1]
self.assertEqual(
attachment.get_filename(),
'odpowied\u017a Burmistrza.jpg'
)
self.assertEqual(attachment.get_filename(), "odpowied\u017a Burmistrza.jpg")
def test_message_get_text_body(self):
message = self._get_email_object('multipart_text.eml')
message = self._get_email_object("multipart_text.eml")
mailbox = Mailbox.objects.create()
msg = mailbox.process_incoming_message(message)
expected_results = 'Hello there!'
expected_results = "Hello there!"
actual_results = msg.text.strip()
self.assertEqual(
@ -135,29 +122,21 @@ class TestProcessEmail(EmailMessageTestCase):
def test_get_text_body_properly_recomposes_line_continuations(self):
message = Message()
email_object = self._get_email_object(
'message_with_long_text_lines.eml'
)
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.'
"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
)
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'
)
os.path.join(os.path.dirname(__file__), "messages/generic_message.eml")
) as f:
unicode_body = f.read()
@ -167,23 +146,18 @@ class TestProcessEmail(EmailMessageTestCase):
expected_body = unicode_body
actual_body = message.get_email_object().as_string()
self.assertEqual(
expected_body,
actual_body
)
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'
"""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'
except Exception:
it = "do not works"
success = True
try:
@ -191,15 +165,15 @@ class TestProcessEmail(EmailMessageTestCase):
except ValueError:
success = False
self.assertEqual(it, 'works')
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
"""Ensure that the email object is good before and after
calling _get_dehydrated_message()
"""
message = self._get_email_object('email_issue_82.eml')
message = self._get_email_object("email_issue_82.eml")
success = True
@ -209,16 +183,16 @@ class TestProcessEmail(EmailMessageTestCase):
# 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'])
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
@ -227,14 +201,14 @@ class TestProcessEmail(EmailMessageTestCase):
try:
# here as_string raises UnicodeEncodeError
str_msg = message.as_string()
except:
except Exception:
success = False
msg.set_body(str_msg)
if message['in-reply-to']:
if message["in-reply-to"]:
try:
msg.in_reply_to = Message.objects.filter(
message_id=message['in-reply-to']
message_id=message["in-reply-to"]
)[0]
except IndexError:
pass
@ -243,18 +217,20 @@ class TestProcessEmail(EmailMessageTestCase):
self.assertEqual(True, success)
def test_message_with_misplaced_utf8_content(self):
""" Ensure that we properly handle incorrectly encoded messages
"""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')
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".'
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(
@ -263,7 +239,7 @@ class TestProcessEmail(EmailMessageTestCase):
)
def test_message_with_invalid_content_for_declared_encoding(self):
""" Ensure that we gracefully handle mis-encoded bodies.
"""Ensure that we gracefully handle mis-encoded bodies.
Should a payload body be misencoded, we should:
@ -275,7 +251,7 @@ class TestProcessEmail(EmailMessageTestCase):
"""
email_object = self._get_email_object(
'message_with_invalid_content_for_declared_encoding.eml',
"message_with_invalid_content_for_declared_encoding.eml",
)
msg = self.mailbox.process_incoming_message(email_object)
@ -284,18 +260,20 @@ class TestProcessEmail(EmailMessageTestCase):
def test_message_with_valid_content_in_single_byte_encoding(self):
email_object = self._get_email_object(
'message_with_single_byte_encoding.eml',
"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.'
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,
@ -304,42 +282,44 @@ class TestProcessEmail(EmailMessageTestCase):
def test_message_with_single_byte_subject_encoding(self):
email_object = self._get_email_object(
'message_with_single_byte_extended_subject_encoding.eml',
"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!'
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 <mr.test32@mail.ru>'
expected_from = "test test <mr.test32@mail.ru>"
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
"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'
actual_from = "username@example.com"
reply_email_object = EmailMessage(
'Test subject', # subject
'Test body', # body
"Test subject", # subject
"Test body", # body
actual_from, # from
['mr.test32@mail.ru'], # to
["mr.test32@mail.ru"], # to
)
with mock.patch.object(reply_email_object, 'send'):
with mock.patch.object(reply_email_object, "send"):
reply_msg = msg.reply(reply_email_object)
self.assertEqual(reply_msg.in_reply_to, msg)
@ -348,62 +328,58 @@ class TestProcessEmail(EmailMessageTestCase):
reply_email_object.from_email = None
with mock.patch.object(reply_email_object, 'send'):
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',
"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)
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',
"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())))
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')
message = self._get_email_object("generic_message.eml")
default_settings = utils.get_settings()
with mock.patch('django_mailbox.utils.get_settings') as get_settings:
with mock.patch("django_mailbox.utils.get_settings") as get_settings:
altered = copy.deepcopy(default_settings)
altered['store_original_message'] = True
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'))
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')
)
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')
message = self._get_email_object("generic_message.eml")
default_settings = utils.get_settings()
with mock.patch('django_mailbox.utils.get_settings') as get_settings:
with mock.patch("django_mailbox.utils.get_settings") as get_settings:
altered = copy.deepcopy(default_settings)
altered['store_original_message'] = False
altered["store_original_message"] = False
get_settings.return_value = altered
msg = self.mailbox.process_incoming_message(message)
@ -411,22 +387,21 @@ class TestProcessEmail(EmailMessageTestCase):
self.assertEquals(msg.eml, None)
def test_message_compressed(self):
message = self._get_email_object('generic_message.eml')
message = self._get_email_object("generic_message.eml")
default_settings = utils.get_settings()
with mock.patch('django_mailbox.utils.get_settings') as 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
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()
print(actual_email_object)
self.assertTrue(msg.eml.name.endswith(".eml.gz"))
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'))
with gzip.open(msg.eml.name, "rb") as f:
self.assertEqual(f.read(), self._get_email_as_text("generic_message.eml"))

View file

@ -5,23 +5,26 @@ 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:
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')
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)
self.assertEqual(kwargs["mailbox_name"], mailbox_name)
except KeyError:
# Handle Django 1.7
# It uses optparse instead of argparse, so instead of being
@ -29,19 +32,20 @@ class CommandsTestCase(TestCase):
# 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'
mailbox_name = "foo_mailbox"
with mock.patch('django_mailbox.management.commands.processincomingmessage.Command.handle') as handle:
with mock.patch(
"django_mailbox.management.commands.processincomingmessage.Command.handle"
) as handle:
handle.return_value = None
call_command('processincomingmessage', mailbox_name)
call_command("processincomingmessage", mailbox_name)
args, kwargs = handle.call_args
try:
self.assertEqual(kwargs['mailbox_name'], mailbox_name)
self.assertEqual(kwargs["mailbox_name"], mailbox_name)
except (AssertionError, KeyError):
# Handle Django 1.7
# It uses optparse instead of argparse, so instead of being
@ -55,11 +59,12 @@ class CommandsTestCase(TestCase):
# 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')):
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')
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'):
elif LooseVersion(django.get_version()) >= LooseVersion("1.10"):
with self.assertRaises(CommandError):
call_command('processincomingmessage', 'foo_mailbox', 'invalid_arg')
call_command("processincomingmessage", "foo_mailbox", "invalid_arg")

View file

@ -1,44 +1,32 @@
from unittest import mock
from os.path import exists
from os import remove
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',
"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'
]
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)'
]
"OK",
[b"1 (UID 18 RFC822.SIZE 58070000000)", b"2 (UID 19 RFC822.SIZE 2593)"],
)
FAKE_UID_FETCH_MSG = (
'OK',
"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)'
]
(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"'
]
"OK",
[b'(\\HasNoChildren \\All) "/" "[Gmail]/All Mail"'],
)
@ -47,14 +35,14 @@ class IMAPTestCase(EmailMessageTestCase):
def imap_server_uid_method(*args):
cmd = args[0]
arg2 = args[2]
if cmd == 'search':
if cmd == "search":
return FAKE_UID_SEARCH_ANSWER
if cmd == 'copy':
if cmd == "copy":
return FAKE_UID_COPY_MSG
if cmd == 'fetch':
if arg2 == '(RFC822.SIZE)':
if cmd == "fetch":
if arg2 == "(RFC822.SIZE)":
return FAKE_UID_FETCH_SIZES
if arg2 == '(RFC822)':
if arg2 == "(RFC822)":
return FAKE_UID_FETCH_MSG
def imap_server_list_method(pattern=None):
@ -69,39 +57,35 @@ class IMAPTestCase(EmailMessageTestCase):
class TestImapTransport(IMAPTestCase):
def setUp(self):
super().setUp()
self.arbitrary_hostname = 'one.two.three'
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.arbitrary_hostname, self.arbitrary_port, self.ssl
)
self.transport.server = self.imap_server
def test_get_email_message(self):
if exists("store.json"):
remove("store.json")
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')
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.archive = "Archive"
self.transport = ImapTransport(
self.arbitrary_hostname,
self.arbitrary_port,
self.ssl,
self.archive
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()
@ -121,38 +105,40 @@ class TestMaxSizeImapTransport(TestImapTransport):
self.assertEqual(len(small_message_ids), 1)
def test_get_email_message(self):
if exists("store.json"):
remove("store.json")
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')
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_hostname = "one.two.three"
self.arbitrary_port = 100
self.ssl = False
self.transport = Pop3Transport(
self.arbitrary_hostname,
self.arbitrary_port,
self.ssl
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:
if exists("store.json"):
remove("store.json")
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.list.return_value = [None, ["some_msg"]]
server.retr.return_value = [
'+OK message follows',
"+OK message follows",
[
line.encode('ascii')
for line in self._get_email_as_text(
'generic_message.eml'
).decode('ascii').split('\n')
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
]
@ -162,6 +148,6 @@ class TestPop3Transport(EmailMessageTestCase):
self.assertEqual(len(actual_messages), 1)
actual_message = actual_messages[0]
expected_message = self._get_email_object('generic_message.eml')
expected_message = self._get_email_object("generic_message.eml")
self.assertEqual(expected_message, actual_message)

View file

@ -1,4 +1,4 @@
import sys
# import sys
from .base import EmailTransport

View file

@ -7,7 +7,6 @@ logger = logging.getLogger(__name__)
class GmailImapTransport(ImapTransport):
def connect(self, username, password):
# Try to use oauth2 first. It's much safer
try:
@ -28,30 +27,25 @@ class GmailImapTransport(ImapTransport):
AccessTokenNotFound,
)
except ImportError:
raise ValueError(
"Install python-social-auth to use oauth2 auth for gmail"
)
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']
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
)
"No Token available in python-social-auth for %s" % (username)
)
auth_string = 'user={}\1auth=Bearer {}\1\1'.format(
google_email_address,
access_token
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.authenticate("XOAUTH2", lambda x: auth_string)
self.server.select()

View file

@ -1,6 +1,7 @@
import imaplib
import logging
from os.path import exists
import json
from django.conf import settings
from .base import EmailTransport, MessageParseError
@ -18,28 +19,25 @@ logger = logging.getLogger(__name__)
class ImapTransport(EmailTransport):
def __init__(
self, hostname, port=None, ssl=False, tls=False,
archive='', folder=None,
self,
hostname,
port=None,
ssl=False,
tls=False,
archive="",
folder=None,
):
self.max_message_size = getattr(
settings,
'DJANGO_MAILBOX_MAX_MESSAGE_SIZE',
False
settings, "DJANGO_MAILBOX_MAX_MESSAGE_SIZE", False
)
self.integration_testing_subject = getattr(
settings,
'DJANGO_MAILBOX_INTEGRATION_TESTING_SUBJECT',
None
settings, "DJANGO_MAILBOX_INTEGRATION_TESTING_SUBJECT", None
)
self.delete_message_from_server = getattr(
settings,
'DJANGO_MAILBOX_DELETE_MESSAGE_FROM_SERVER',
True
settings, "DJANGO_MAILBOX_DELETE_MESSAGE_FROM_SERVER", True
)
self.delete_message_store = getattr(
settings,
'DJANGO_MAILBOX_DELETE_MESSAGE_ID_STORE',
"/tmp/store.json"
settings, "DJANGO_MAILBOX_DELETE_MESSAGE_ID_STORE", "/tmp/store.json"
)
self.hostname = hostname
self.port = port
@ -68,13 +66,13 @@ class ImapTransport(EmailTransport):
def _get_all_message_ids(self):
# Fetch all the message uids
response, message_ids = self.server.uid('search', None, 'ALL')
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 message_id_string.decode().split(" ")
return []
def _get_small_message_ids(self, message_ids):
@ -83,23 +81,17 @@ class ImapTransport(EmailTransport):
# limit
safe_message_ids = []
status, data = self.server.uid(
'fetch',
','.join(message_ids),
'(RFC822.SIZE)'
)
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(')')
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])
)
logger.warning("ValueError: {} working on {}".format(e, each_msg[0]))
pass
return safe_message_ids
@ -120,30 +112,29 @@ class ImapTransport(EmailTransport):
self.server.create(self.archive)
# PoC of alternative method of storing email
if not self.delete_from_server:
j = {"uids":[]}
if os.path.exists("store.json"):
with open("store.json","r") as f:
j = json.load(f)
if not self.delete_message_from_server:
j = {"uids": []}
if exists("store.json"):
with open("store.json", "r") as f:
j = json.load(f)
else:
with open("store.json","w") as f:
json.dump(j,f)
with open("store.json", "w") as f:
json.dump(j, f)
# PoC of alternative method of storing email
for uid in message_ids:
# PoC of alternative method of storing email
if not self.delete_from_server:
if uid in j["uids"]:
continue
else:
j["uids"].append(uid)
with open("store.json","w") as f:
json.dump(j,f)
if not self.delete_message_from_server:
if uid in j["uids"]:
continue
else:
j["uids"].append(uid)
with open("store.json", "w") as f:
json.dump(j, f)
# PoC of alternative method of storing email
try:
typ, msg_contents = self.server.uid('fetch', uid, '(RFC822)')
typ, msg_contents = self.server.uid("fetch", uid, "(RFC822)")
if not msg_contents:
continue
try:
@ -162,8 +153,9 @@ class ImapTransport(EmailTransport):
continue
if self.archive:
self.server.uid('copy', uid, self.archive)
self.delete_message_from_server:
self.server.uid('store', uid, "+FLAGS", "(\\Deleted)")
self.server.uid("copy", uid, self.archive)
if self.delete_message_from_server:
self.server.uid("store", uid, "+FLAGS", "(\\Deleted)")
self.server.expunge()
return

View file

@ -22,15 +22,13 @@ class Pop3Transport(EmailTransport):
self.server.pass_(password)
def get_message_body(self, message_lines):
return bytes('\r\n', 'ascii').join(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]
)
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):

View file

@ -11,127 +11,112 @@ logger = logging.getLogger(__name__)
def get_settings():
return {
'strip_unallowed_mimetypes': getattr(
settings,
'DJANGO_MAILBOX_STRIP_UNALLOWED_MIMETYPES',
False
"strip_unallowed_mimetypes": getattr(
settings, "DJANGO_MAILBOX_STRIP_UNALLOWED_MIMETYPES", False
),
'allowed_mimetypes': getattr(
settings,
'DJANGO_MAILBOX_ALLOWED_MIMETYPES',
[
'text/plain',
'text/html'
]
"allowed_mimetypes": getattr(
settings, "DJANGO_MAILBOX_ALLOWED_MIMETYPES", ["text/plain", "text/html"]
),
'text_stored_mimetypes': getattr(
"text_stored_mimetypes": getattr(
settings,
'DJANGO_MAILBOX_TEXT_STORED_MIMETYPES',
[
'text/plain',
'text/html'
]
"DJANGO_MAILBOX_TEXT_STORED_MIMETYPES",
["text/plain", "text/html"],
),
'altered_message_header': getattr(
"altered_message_header": getattr(
settings,
'DJANGO_MAILBOX_ALTERED_MESSAGE_HEADER',
'X-Django-Mailbox-Altered-Message'
"DJANGO_MAILBOX_ALTERED_MESSAGE_HEADER",
"X-Django-Mailbox-Altered-Message",
),
'attachment_interpolation_header': getattr(
"attachment_interpolation_header": getattr(
settings,
'DJANGO_MAILBOX_ATTACHMENT_INTERPOLATION_HEADER',
'X-Django-Mailbox-Interpolate-Attachment'
"DJANGO_MAILBOX_ATTACHMENT_INTERPOLATION_HEADER",
"X-Django-Mailbox-Interpolate-Attachment",
),
'attachment_upload_to': getattr(
"attachment_upload_to": getattr(
settings,
'DJANGO_MAILBOX_ATTACHMENT_UPLOAD_TO',
'mailbox_attachments/%Y/%m/%d/'
"DJANGO_MAILBOX_ATTACHMENT_UPLOAD_TO",
"mailbox_attachments/%Y/%m/%d/",
),
'store_original_message': getattr(
settings,
'DJANGO_MAILBOX_STORE_ORIGINAL_MESSAGE',
False
"store_original_message": getattr(
settings, "DJANGO_MAILBOX_STORE_ORIGINAL_MESSAGE", False
),
'compress_original_message': getattr(
settings,
'DJANGO_MAILBOX_COMPRESS_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
"original_message_compression": getattr(
settings, "DJANGO_MAILBOX_ORIGINAL_MESSAGE_COMPRESSION", 6
),
'default_charset': getattr(
"default_charset": getattr(
settings,
'DJANGO_MAILBOX_default_charset',
'iso8859-1',
)
"DJANGO_MAILBOX_default_charset",
"iso8859-1",
),
}
def convert_header_to_unicode(header):
default_charset = get_settings()['default_charset']
default_charset = get_settings()["default_charset"]
def _decode(value, encoding):
if isinstance(value, str):
return value
if not encoding or encoding == 'unknown-8bit':
if not encoding or encoding == "unknown-8bit":
encoding = default_charset
return value.decode(encoding, 'replace')
return value.decode(encoding, "replace")
try:
return ''.join(
return "".join(
[
(
_decode(bytestr, encoding)
) for bytestr, encoding in email.header.decode_header(header)
(_decode(bytestr, encoding))
for bytestr, encoding in email.header.decode_header(header)
]
)
except UnicodeDecodeError:
logger.exception(
'Errors encountered decoding header %s into encoding %s.',
"Errors encountered decoding header %s into encoding %s.",
header,
default_charset,
)
return header.decode(default_charset, 'replace')
return header.decode(default_charset, "replace")
def get_body_from_message(message, maintype, subtype):
"""
Fetchs the body message matching main/sub content type.
"""
body = ''
body = ""
for part in message.walk():
if part.get('content-disposition', '').startswith('attachment;'):
if part.get("content-disposition", "").startswith("attachment;"):
continue
if part.get_content_maintype() == maintype and \
part.get_content_subtype() == subtype:
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')
this_part = this_part.decode(charset, "replace")
except LookupError:
this_part = this_part.decode('ascii', 'replace')
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
"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')
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.'
"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')
this_part = this_part.decode("ascii", "replace")
body += this_part
@ -141,8 +126,8 @@ def get_body_from_message(message, maintype, subtype):
def get_attachment_save_path(instance, filename):
settings = get_settings()
path = settings['attachment_upload_to']
if '%' in path:
path = settings["attachment_upload_to"]
if "%" in path:
path = datetime.datetime.utcnow().strftime(path)
return os.path.join(

View file

@ -15,106 +15,106 @@ import sys, os
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#sys.path.insert(0, os.path.abspath('.'))
# sys.path.insert(0, os.path.abspath('.'))
# -- General configuration -----------------------------------------------------
# If your documentation needs a minimal Sphinx version, state it here.
#needs_sphinx = '1.0'
# needs_sphinx = '1.0'
# Add any Sphinx extension module names here, as strings. They can be extensions
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
extensions = ['sphinx.ext.autodoc', 'sphinx.ext.viewcode']
extensions = ["sphinx.ext.autodoc", "sphinx.ext.viewcode"]
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
templates_path = ["_templates"]
# The suffix of source filenames.
source_suffix = '.rst'
source_suffix = ".rst"
# The encoding of source files.
#source_encoding = 'utf-8-sig'
# source_encoding = 'utf-8-sig'
# The master toctree document.
master_doc = 'index'
master_doc = "index"
# General information about the project.
project = 'django-mailbox'
copyright = '2020, Adam Coddington'
project = "django-mailbox"
copyright = "2020, Adam Coddington"
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
version = '3.3'
version = "3.3"
# The full version, including alpha/beta/rc tags.
release = '3.3'
release = "3.3"
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#language = None
# language = None
# There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used:
#today = ''
# today = ''
# Else, today_fmt is used as the format for a strftime call.
#today_fmt = '%B %d, %Y'
# today_fmt = '%B %d, %Y'
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
exclude_patterns = ['_build']
exclude_patterns = ["_build"]
# The reST default role (used for this markup: `text`) to use for all documents.
#default_role = None
# default_role = None
# If true, '()' will be appended to :func: etc. cross-reference text.
#add_function_parentheses = True
# add_function_parentheses = True
# If true, the current module name will be prepended to all description
# unit titles (such as .. function::).
#add_module_names = True
# add_module_names = True
# If true, sectionauthor and moduleauthor directives will be shown in the
# output. They are ignored by default.
#show_authors = False
# show_authors = False
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
pygments_style = "sphinx"
# A list of ignored prefixes for module index sorting.
#modindex_common_prefix = []
# modindex_common_prefix = []
# -- Options for HTML output ---------------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
html_theme = 'default'
html_theme = "default"
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
# documentation.
#html_theme_options = {}
# html_theme_options = {}
# Add any paths that contain custom themes here, relative to this directory.
#html_theme_path = []
# html_theme_path = []
# The name for this set of Sphinx documents. If None, it defaults to
# "<project> v<release> documentation".
#html_title = None
# html_title = None
# A shorter title for the navigation bar. Default is the same as html_title.
#html_short_title = None
# html_short_title = None
# The name of an image file (relative to this directory) to place at the top
# of the sidebar.
#html_logo = None
# html_logo = None
# The name of an image file (within the static path) to use as favicon of the
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
# pixels large.
#html_favicon = None
# html_favicon = None
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
@ -123,88 +123,91 @@ html_theme = 'default'
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
# using the given strftime format.
#html_last_updated_fmt = '%b %d, %Y'
# html_last_updated_fmt = '%b %d, %Y'
# If true, SmartyPants will be used to convert quotes and dashes to
# typographically correct entities.
#html_use_smartypants = True
# html_use_smartypants = True
# Custom sidebar templates, maps document names to template names.
#html_sidebars = {}
# html_sidebars = {}
# Additional templates that should be rendered to pages, maps page names to
# template names.
#html_additional_pages = {}
# html_additional_pages = {}
# If false, no module index is generated.
#html_domain_indices = True
# html_domain_indices = True
# If false, no index is generated.
#html_use_index = True
# html_use_index = True
# If true, the index is split into individual pages for each letter.
#html_split_index = False
# html_split_index = False
# If true, links to the reST sources are added to the pages.
#html_show_sourcelink = True
# html_show_sourcelink = True
# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
#html_show_sphinx = True
# html_show_sphinx = True
# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
#html_show_copyright = True
# html_show_copyright = True
# If true, an OpenSearch description file will be output, and all pages will
# contain a <link> tag referring to it. The value of this option must be the
# base URL from which the finished HTML is served.
#html_use_opensearch = ''
# html_use_opensearch = ''
# This is the file name suffix for HTML files (e.g. ".xhtml").
#html_file_suffix = None
# html_file_suffix = None
# Output file base name for HTML help builder.
htmlhelp_basename = 'django-mailboxdoc'
htmlhelp_basename = "django-mailboxdoc"
# -- Options for LaTeX output --------------------------------------------------
latex_elements = {
# The paper size ('letterpaper' or 'a4paper').
#'papersize': 'letterpaper',
# The font size ('10pt', '11pt' or '12pt').
#'pointsize': '10pt',
# Additional stuff for the LaTeX preamble.
#'preamble': '',
# The paper size ('letterpaper' or 'a4paper').
#'papersize': 'letterpaper',
# The font size ('10pt', '11pt' or '12pt').
#'pointsize': '10pt',
# Additional stuff for the LaTeX preamble.
#'preamble': '',
}
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title, author, documentclass [howto/manual]).
latex_documents = [
('index', 'django-mailbox.tex', 'django-mailbox Documentation',
'Adam Coddington', 'manual'),
(
"index",
"django-mailbox.tex",
"django-mailbox Documentation",
"Adam Coddington",
"manual",
),
]
# The name of an image file (relative to this directory) to place at the top of
# the title page.
#latex_logo = None
# latex_logo = None
# For "manual" documents, if this is true, then toplevel headings are parts,
# not chapters.
#latex_use_parts = False
# latex_use_parts = False
# If true, show page references after internal links.
#latex_show_pagerefs = False
# latex_show_pagerefs = False
# If true, show URL addresses after external links.
#latex_show_urls = False
# latex_show_urls = False
# Documents to append as an appendix to all manuals.
#latex_appendices = []
# latex_appendices = []
# If false, no module index is generated.
#latex_domain_indices = True
# latex_domain_indices = True
# -- Options for manual page output --------------------------------------------
@ -212,12 +215,11 @@ latex_documents = [
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
('index', 'django-mailbox', 'django-mailbox Documentation',
['Adam Coddington'], 1)
("index", "django-mailbox", "django-mailbox Documentation", ["Adam Coddington"], 1)
]
# If true, show URL addresses after external links.
#man_show_urls = False
# man_show_urls = False
# -- Options for Texinfo output ------------------------------------------------
@ -226,29 +228,41 @@ man_pages = [
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
('index', 'django-mailbox', 'django-mailbox Documentation',
'Adam Coddington', 'django-mailbox', 'One line description of project.',
'Miscellaneous'),
(
"index",
"django-mailbox",
"django-mailbox Documentation",
"Adam Coddington",
"django-mailbox",
"One line description of project.",
"Miscellaneous",
),
]
# Documents to append as an appendix to all manuals.
#texinfo_appendices = []
# texinfo_appendices = []
# If false, no module index is generated.
#texinfo_domain_indices = True
# texinfo_domain_indices = True
# How to display URL addresses: 'footnote', 'no', or 'inline'.
#texinfo_show_urls = 'footnote'
# texinfo_show_urls = 'footnote'
sys.path.insert(0, os.path.abspath('..'))
sys.path.insert(0, os.path.abspath(".."))
import inspect
import django
from django.utils.html import strip_tags
from django.utils.encoding import force_text
from django.conf import settings
settings.configure(INSTALLED_APPS=['django_mailbox', ])
settings.configure(
INSTALLED_APPS=[
"django_mailbox",
]
)
django.setup()
def process_docstring(app, what, name, obj, options, lines):
# Source: https://gist.github.com/abulka/48b54ea4cbc7eb014308
# This causes import errors if left outside the function
@ -261,7 +275,7 @@ def process_docstring(app, what, name, obj, options, lines):
for field in fields:
# Skip ManyToOneRel and ManyToManyRel fields which have no 'verbose_name' or 'help_text'
if not hasattr(field, 'verbose_name'):
if not hasattr(field, "verbose_name"):
continue
# Decode and strip any html out of the field's help text
@ -274,22 +288,27 @@ def process_docstring(app, what, name, obj, options, lines):
if help_text:
# Add the model field to the end of the docstring as a param
# using the help text as the description
lines.append(':param {}: {}'.format(field.attname, help_text))
lines.append(":param {}: {}".format(field.attname, help_text))
else:
# Add the model field to the end of the docstring as a param
# using the verbose name as the description
lines.append(':param {}: {}'.format(field.attname, verbose_name))
lines.append(":param {}: {}".format(field.attname, verbose_name))
# Add the field's type to the docstring
if isinstance(field, models.ForeignKey):
to = field.related_model
lines.append(':type {}: {} to :class:`~{}.{}`'.format(field.attname, type(field).__name__, to.__module__, to.__name__))
lines.append(
":type {}: {} to :class:`~{}.{}`".format(
field.attname, type(field).__name__, to.__module__, to.__name__
)
)
else:
lines.append(':type {}: {}'.format(field.attname, type(field).__name__))
lines.append(":type {}: {}".format(field.attname, type(field).__name__))
# Return the extended docstring
return lines
def setup(app):
# Register the docstring processor with sphinx
app.connect('autodoc-process-docstring', process_docstring)
app.connect("autodoc-process-docstring", process_docstring)

View file

@ -11,6 +11,8 @@ Django = "^3.1.6"
[tool.poetry.dev-dependencies]
pytest = "^6.2.2"
pytest-django = "^4.1.0"
flake8 = "^3.8.4"
black = "^20.8b1"
[build-system]
requires = ["poetry-core>=1.0.0"]

View file

@ -1,3 +1,7 @@
Django Mailbox2
===============
This is a modified version of the original.
Easily ingest messages from POP3, IMAP, or local mailboxes into your Django application.

View file

@ -1,4 +0,0 @@
[pytest]
norecursedirs=env docs lib .eggs
DJANGO_SETTINGS_MODULE=django_mailbox.tests.settings
addopts = --tb=short -rxs

View file

@ -1,60 +0,0 @@
from setuptools import find_packages, setup
from django_mailbox import __version__ as version_string
tests_require = [
'django',
'mock',
'unittest2',
]
gmail_oauth2_require = [
'python-social-auth',
]
setup(
name='django-mailbox',
version=version_string,
url='http://github.com/coddingtonbear/django-mailbox/',
description=(
'Import mail from POP3, IMAP, local mailboxes or directly from '
'Postfix or Exim4 into your Django application automatically.'
),
license='MIT',
author='Adam Coddington',
author_email='me@adamcoddington.net',
extras_require={
'gmail-oauth2': gmail_oauth2_require
},
python_requires=">=3",
classifiers=[
'Framework :: Django',
'Intended Audience :: Developers',
'License :: OSI Approved :: MIT License',
'Operating System :: OS Independent',
'Framework :: Django',
'Framework :: Django :: 1.11',
'Framework :: Django :: 2.0',
'Framework :: Django :: 2.1',
'Framework :: Django :: 2.2',
'Framework :: Django :: 3.0',
'Programming Language :: Python',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.4',
'Programming Language :: Python :: 3.5',
'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Topic :: Communications :: Email',
'Topic :: Communications :: Email :: Post-Office',
'Topic :: Communications :: Email :: Post-Office :: IMAP',
'Topic :: Communications :: Email :: Post-Office :: POP3',
'Topic :: Communications :: Email :: Email Clients (MUA)',
],
packages=find_packages(),
include_package_data=True,
install_requires=[
'six>=1.6.1'
]
)