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:
parent
62805f324e
commit
c7997ebd9b
58 changed files with 2421 additions and 1541 deletions
|
|
@ -1,7 +0,0 @@
|
||||||
[bumpversion]
|
|
||||||
current_version = 4.8.2
|
|
||||||
commit = True
|
|
||||||
tag = True
|
|
||||||
tag_name = {new_version}
|
|
||||||
|
|
||||||
[bumpversion:file:django_mailbox/__init__.py]
|
|
||||||
4
.flake8
4
.flake8
|
|
@ -1,2 +1,4 @@
|
||||||
[flake8]
|
[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
1
.gitignore
vendored
|
|
@ -16,3 +16,4 @@ messages
|
||||||
manage.py
|
manage.py
|
||||||
old/
|
old/
|
||||||
poetry.lock
|
poetry.lock
|
||||||
|
websrv
|
||||||
|
|
|
||||||
46
.travis.yml
46
.travis.yml
|
|
@ -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
|
|
||||||
|
|
@ -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
|
## [0.1.1] - 2021-02-03
|
||||||
### Added
|
### Added
|
||||||
- adds modified documenation
|
- adds modified documenation
|
||||||
|
- adds testable repo with added features
|
||||||
|
|
||||||
## [0.1.0] - 2021-02-03
|
## [0.1.0] - 2021-02-03
|
||||||
### Added
|
### Added
|
||||||
- bot - adds changelog
|
- bot - adds changelog
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
- changes repo to customized solution
|
- changes repo to customized solution
|
||||||
|
|
@ -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
|
|
||||||
|
|
@ -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"
|
||||||
|
|
|
||||||
|
|
@ -23,35 +23,38 @@ def get_new_mail(mailbox_admin, request, queryset):
|
||||||
queryset.get_new_mail()
|
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):
|
def resend_message_received_signal(message_admin, request, queryset):
|
||||||
for message in queryset.all():
|
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)
|
message_received.send(sender=message_admin, message=message)
|
||||||
|
|
||||||
|
|
||||||
resend_message_received_signal.short_description = (
|
resend_message_received_signal.short_description = _("Re-send message received signal")
|
||||||
_('Re-send message received signal')
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class MailboxAdmin(admin.ModelAdmin):
|
class MailboxAdmin(admin.ModelAdmin):
|
||||||
list_display = (
|
list_display = (
|
||||||
'name',
|
"name",
|
||||||
'uri',
|
"uri",
|
||||||
'from_email',
|
"from_email",
|
||||||
'active',
|
"active",
|
||||||
'last_polling',
|
"last_polling",
|
||||||
)
|
)
|
||||||
readonly_fields = ['last_polling', ]
|
readonly_fields = [
|
||||||
|
"last_polling",
|
||||||
|
]
|
||||||
actions = [get_new_mail]
|
actions = [get_new_mail]
|
||||||
|
|
||||||
|
|
||||||
class MessageAttachmentAdmin(admin.ModelAdmin):
|
class MessageAttachmentAdmin(admin.ModelAdmin):
|
||||||
raw_id_fields = ('message', )
|
raw_id_fields = ("message",)
|
||||||
list_display = ('message', 'document',)
|
list_display = (
|
||||||
|
"message",
|
||||||
|
"document",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class MessageAttachmentInline(admin.TabularInline):
|
class MessageAttachmentInline(admin.TabularInline):
|
||||||
|
|
@ -63,50 +66,44 @@ class MessageAdmin(admin.ModelAdmin):
|
||||||
def attachment_count(self, msg):
|
def attachment_count(self, msg):
|
||||||
return msg.attachments.count()
|
return msg.attachments.count()
|
||||||
|
|
||||||
attachment_count.short_description = _('Attachment count')
|
attachment_count.short_description = _("Attachment count")
|
||||||
|
|
||||||
def subject(self, msg):
|
def subject(self, msg):
|
||||||
return convert_header_to_unicode(msg.subject)
|
return convert_header_to_unicode(msg.subject)
|
||||||
|
|
||||||
def envelope_headers(self, msg):
|
def envelope_headers(self, msg):
|
||||||
email = msg.get_email_object()
|
email = msg.get_email_object()
|
||||||
return '\n'.join(
|
return "\n".join([("{}: {}".format(h, v)) for h, v in email.items()])
|
||||||
[('{}: {}'.format(h, v)) for h, v in email.items()]
|
|
||||||
)
|
|
||||||
|
|
||||||
inlines = [
|
inlines = [
|
||||||
MessageAttachmentInline,
|
MessageAttachmentInline,
|
||||||
]
|
]
|
||||||
list_display = (
|
list_display = (
|
||||||
'subject',
|
"subject",
|
||||||
'processed',
|
"processed",
|
||||||
'read',
|
"read",
|
||||||
'mailbox',
|
"mailbox",
|
||||||
'outgoing',
|
"outgoing",
|
||||||
'attachment_count',
|
"attachment_count",
|
||||||
)
|
)
|
||||||
ordering = ['-processed']
|
ordering = ["-processed"]
|
||||||
list_filter = (
|
list_filter = (
|
||||||
'mailbox',
|
"mailbox",
|
||||||
'outgoing',
|
"outgoing",
|
||||||
'processed',
|
"processed",
|
||||||
'read',
|
"read",
|
||||||
)
|
|
||||||
exclude = (
|
|
||||||
'body',
|
|
||||||
)
|
|
||||||
raw_id_fields = (
|
|
||||||
'in_reply_to',
|
|
||||||
)
|
)
|
||||||
|
exclude = ("body",)
|
||||||
|
raw_id_fields = ("in_reply_to",)
|
||||||
readonly_fields = (
|
readonly_fields = (
|
||||||
'envelope_headers',
|
"envelope_headers",
|
||||||
'text',
|
"text",
|
||||||
'html',
|
"html",
|
||||||
)
|
)
|
||||||
actions = [resend_message_received_signal]
|
actions = [resend_message_received_signal]
|
||||||
|
|
||||||
|
|
||||||
if getattr(settings, 'DJANGO_MAILBOX_ADMIN_ENABLED', True):
|
if getattr(settings, "DJANGO_MAILBOX_ADMIN_ENABLED", True):
|
||||||
admin.site.register(Message, MessageAdmin)
|
admin.site.register(Message, MessageAdmin)
|
||||||
admin.site.register(MessageAttachment, MessageAttachmentAdmin)
|
admin.site.register(MessageAttachment, MessageAttachmentAdmin)
|
||||||
admin.site.register(Mailbox, MailboxAdmin)
|
admin.site.register(Mailbox, MailboxAdmin)
|
||||||
|
|
|
||||||
|
|
@ -3,5 +3,5 @@ from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
|
|
||||||
class MailBoxConfig(AppConfig):
|
class MailBoxConfig(AppConfig):
|
||||||
name = 'django_mailbox'
|
name = "django_mailbox"
|
||||||
verbose_name = _("Mail Box")
|
verbose_name = _("Mail Box")
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ def get_google_access_token(email):
|
||||||
# TODO: This should be cacheable
|
# TODO: This should be cacheable
|
||||||
try:
|
try:
|
||||||
me = UserSocialAuth.objects.get(uid=email, provider="google-oauth2")
|
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):
|
except (UserSocialAuth.DoesNotExist, KeyError):
|
||||||
raise AccessTokenNotFound
|
raise AccessTokenNotFound
|
||||||
|
|
||||||
|
|
@ -45,7 +45,7 @@ def update_google_extra_data(email, extra_data):
|
||||||
def get_google_refresh_token(email):
|
def get_google_refresh_token(email):
|
||||||
try:
|
try:
|
||||||
me = UserSocialAuth.objects.get(uid=email, provider="google-oauth2")
|
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):
|
except (UserSocialAuth.DoesNotExist, KeyError):
|
||||||
raise RefreshTokenNotFound
|
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
|
# TODO: Make this a lot less ugly. especially the 401 handling
|
||||||
headers = dict()
|
headers = dict()
|
||||||
if authorized is True:
|
if authorized is True:
|
||||||
headers.update(dict(
|
headers.update(
|
||||||
Authorization="Bearer %s" % get_google_access_token(email),
|
dict(
|
||||||
))
|
Authorization="Bearer %s" % get_google_access_token(email),
|
||||||
|
)
|
||||||
|
)
|
||||||
r = requests.post(url, headers=headers, data=post_data)
|
r = requests.post(url, headers=headers, data=post_data)
|
||||||
if r.status_code == 401:
|
if r.status_code == 401:
|
||||||
refresh_authorization(email)
|
refresh_authorization(email)
|
||||||
|
|
@ -92,20 +94,20 @@ def refresh_authorization(email):
|
||||||
refresh_token=refresh_token,
|
refresh_token=refresh_token,
|
||||||
client_id=get_google_consumer_key(),
|
client_id=get_google_consumer_key(),
|
||||||
client_secret=get_google_consumer_secret(),
|
client_secret=get_google_consumer_secret(),
|
||||||
grant_type='refresh_token',
|
grant_type="refresh_token",
|
||||||
)
|
)
|
||||||
results = google_api_post(
|
results = google_api_post(
|
||||||
email,
|
email,
|
||||||
"https://accounts.google.com/o/oauth2/token?access_type=offline",
|
"https://accounts.google.com/o/oauth2/token?access_type=offline",
|
||||||
post_data,
|
post_data,
|
||||||
authorized=False)
|
authorized=False,
|
||||||
results.update({'refresh_token': refresh_token})
|
)
|
||||||
|
results.update({"refresh_token": refresh_token})
|
||||||
update_google_extra_data(email, results)
|
update_google_extra_data(email, results)
|
||||||
|
|
||||||
|
|
||||||
def fetch_user_info(email):
|
def fetch_user_info(email):
|
||||||
result = google_api_get(
|
result = google_api_get(
|
||||||
email,
|
email, "https://www.googleapis.com/oauth2/v1/userinfo?alt=json"
|
||||||
"https://www.googleapis.com/oauth2/v1/userinfo?alt=json"
|
|
||||||
)
|
)
|
||||||
return result
|
return result
|
||||||
|
|
|
||||||
|
|
@ -13,18 +13,11 @@ class Command(BaseCommand):
|
||||||
def handle(self, *args, **options):
|
def handle(self, *args, **options):
|
||||||
mailboxes = Mailbox.active_mailboxes.all()
|
mailboxes = Mailbox.active_mailboxes.all()
|
||||||
if args:
|
if args:
|
||||||
mailboxes = mailboxes.filter(
|
mailboxes = mailboxes.filter(name=" ".join(args))
|
||||||
name=' '.join(args)
|
|
||||||
)
|
|
||||||
for mailbox in mailboxes:
|
for mailbox in mailboxes:
|
||||||
logger.info(
|
logger.info("Gathering messages for %s", mailbox.name)
|
||||||
'Gathering messages for %s',
|
|
||||||
mailbox.name
|
|
||||||
)
|
|
||||||
messages = mailbox.get_new_mail()
|
messages = mailbox.get_new_mail()
|
||||||
for message in messages:
|
for message in messages:
|
||||||
logger.info(
|
logger.info(
|
||||||
'Received %s (from %s)',
|
"Received %s (from %s)", message.subject, message.from_address
|
||||||
message.subject,
|
|
||||||
message.from_address
|
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import email
|
import email
|
||||||
import logging
|
import logging
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from email import utils
|
from email import utils
|
||||||
except ImportError:
|
except ImportError:
|
||||||
|
|
@ -21,9 +22,9 @@ class Command(BaseCommand):
|
||||||
|
|
||||||
def add_arguments(self, parser):
|
def add_arguments(self, parser):
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'mailbox_name',
|
"mailbox_name",
|
||||||
nargs='?',
|
nargs="?",
|
||||||
help="The name of the mailbox that will receive the message"
|
help="The name of the mailbox that will receive the message",
|
||||||
)
|
)
|
||||||
|
|
||||||
def handle(self, mailbox_name=None, *args, **options):
|
def handle(self, mailbox_name=None, *args, **options):
|
||||||
|
|
@ -34,10 +35,7 @@ class Command(BaseCommand):
|
||||||
else:
|
else:
|
||||||
mailbox = self.get_mailbox_for_message(message)
|
mailbox = self.get_mailbox_for_message(message)
|
||||||
mailbox.process_incoming_message(message)
|
mailbox.process_incoming_message(message)
|
||||||
logger.info(
|
logger.info("Message received from %s", message["from"])
|
||||||
"Message received from %s",
|
|
||||||
message['from']
|
|
||||||
)
|
|
||||||
else:
|
else:
|
||||||
logger.warning("Message not processable.")
|
logger.warning("Message not processable.")
|
||||||
|
|
||||||
|
|
@ -48,5 +46,5 @@ class Command(BaseCommand):
|
||||||
return mailbox
|
return mailbox
|
||||||
|
|
||||||
def get_mailbox_for_message(self, message):
|
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)
|
return self.get_mailbox_by_name(email_address)
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ logging.basicConfig(level=logging.INFO)
|
||||||
|
|
||||||
|
|
||||||
class Command(BaseCommand):
|
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
|
attachments to become disassociated with their messages. This management
|
||||||
command will read through existing message attachments and attempt to
|
command will read through existing message attachments and attempt to
|
||||||
re-associate them with their original message.
|
re-associate them with their original message.
|
||||||
|
|
@ -22,14 +22,13 @@ class Command(BaseCommand):
|
||||||
have been quite unlucky to have ran the bad migration).
|
have been quite unlucky to have ran the bad migration).
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def handle(self, *args, **options):
|
def handle(self, *args, **options):
|
||||||
attachment_hash_map = {}
|
attachment_hash_map = {}
|
||||||
|
|
||||||
attachments_without_messages = MessageAttachment.objects.filter(
|
attachments_without_messages = MessageAttachment.objects.filter(
|
||||||
message=None
|
message=None
|
||||||
).order_by(
|
).order_by("id")
|
||||||
'id'
|
|
||||||
)
|
|
||||||
|
|
||||||
if attachments_without_messages.count() < 1:
|
if attachments_without_messages.count() < 1:
|
||||||
return
|
return
|
||||||
|
|
@ -40,13 +39,13 @@ class Command(BaseCommand):
|
||||||
md5.update(chunk)
|
md5.update(chunk)
|
||||||
attachment_hash_map[md5.hexdigest()] = attachment.pk
|
attachment_hash_map[md5.hexdigest()] = attachment.pk
|
||||||
|
|
||||||
for message_record in Message.objects.all().order_by('id'):
|
for message_record in Message.objects.all().order_by("id"):
|
||||||
message = email.message_from_string(message_record.body)
|
message = email.message_from_string(message_record.body)
|
||||||
if message.is_multipart():
|
if message.is_multipart():
|
||||||
for part in message.walk():
|
for part in message.walk():
|
||||||
if part.get_content_maintype() == 'multipart':
|
if part.get_content_maintype() == "multipart":
|
||||||
continue
|
continue
|
||||||
if part.get('Content-Disposition') is None:
|
if part.get("Content-Disposition") is None:
|
||||||
continue
|
continue
|
||||||
md5 = hashlib.md5()
|
md5 = hashlib.md5()
|
||||||
md5.update(part.get_payload(decode=True))
|
md5.update(part.get_payload(decode=True))
|
||||||
|
|
@ -61,11 +60,11 @@ class Command(BaseCommand):
|
||||||
"Associated message %s with attachment %s (%s)",
|
"Associated message %s with attachment %s (%s)",
|
||||||
message_record.pk,
|
message_record.pk,
|
||||||
attachment.pk,
|
attachment.pk,
|
||||||
digest
|
digest,
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
logger.info(
|
logger.info(
|
||||||
"%s(%s) not found in currently-stored attachments",
|
"%s(%s) not found in currently-stored attachments",
|
||||||
part.get_filename(),
|
part.get_filename(),
|
||||||
digest
|
digest,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -3,54 +3,163 @@ from django.db import models, migrations
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
dependencies = [
|
dependencies = []
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='Mailbox',
|
name="Mailbox",
|
||||||
fields=[
|
fields=[
|
||||||
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
(
|
||||||
('name', models.CharField(max_length=255, verbose_name='Name')),
|
"id",
|
||||||
('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')),
|
models.AutoField(
|
||||||
('from_email', models.CharField(default=None, max_length=255, blank=True, help_text="Example: MailBot <mailbot@yourdomain.com><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')),
|
verbose_name="ID",
|
||||||
('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')),
|
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 <mailbot@yourdomain.com><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={
|
options={
|
||||||
'verbose_name_plural': 'Mailboxes',
|
"verbose_name_plural": "Mailboxes",
|
||||||
},
|
},
|
||||||
bases=(models.Model,),
|
bases=(models.Model,),
|
||||||
),
|
),
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='Message',
|
name="Message",
|
||||||
fields=[
|
fields=[
|
||||||
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
(
|
||||||
('subject', models.CharField(max_length=255, verbose_name='Subject')),
|
"id",
|
||||||
('message_id', models.CharField(max_length=255, verbose_name='Message ID')),
|
models.AutoField(
|
||||||
('from_header', models.CharField(max_length=255, verbose_name='From header')),
|
verbose_name="ID",
|
||||||
('to_header', models.TextField(verbose_name='To header')),
|
serialize=False,
|
||||||
('outgoing', models.BooleanField(default=False, verbose_name='Outgoing')),
|
auto_created=True,
|
||||||
('body', models.TextField(verbose_name='Body')),
|
primary_key=True,
|
||||||
('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)),
|
("subject", models.CharField(max_length=255, verbose_name="Subject")),
|
||||||
('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)),
|
"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,),
|
bases=(models.Model,),
|
||||||
),
|
),
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='MessageAttachment',
|
name="MessageAttachment",
|
||||||
fields=[
|
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)),
|
"id",
|
||||||
('document', models.FileField(upload_to=b'mailbox_attachments/%Y/%m/%d/', verbose_name='Document')),
|
models.AutoField(
|
||||||
('message', models.ForeignKey(related_name='attachments', verbose_name='Message', blank=True, to='django_mailbox.Message', null=True, on_delete=models.CASCADE)),
|
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,),
|
bases=(models.Model,),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -4,14 +4,19 @@ from django.db import models, migrations
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
('django_mailbox', '0001_initial'),
|
("django_mailbox", "0001_initial"),
|
||||||
]
|
]
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='message',
|
model_name="message",
|
||||||
name='eml',
|
name="eml",
|
||||||
field=models.FileField(help_text='Original full content of message', upload_to=b'messages', null=True, verbose_name='Message as a file'),
|
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,
|
preserve_default=True,
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -4,13 +4,18 @@ from django.db import models, migrations
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
('django_mailbox', '0002_add_eml_to_message'),
|
("django_mailbox", "0002_add_eml_to_message"),
|
||||||
]
|
]
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.AlterField(
|
migrations.AlterField(
|
||||||
model_name='message',
|
model_name="message",
|
||||||
name='eml',
|
name="eml",
|
||||||
field=models.FileField(help_text='Original full content of message', upload_to=b'messages', null=True, verbose_name='Raw message contents'),
|
field=models.FileField(
|
||||||
|
help_text="Original full content of message",
|
||||||
|
upload_to=b"messages",
|
||||||
|
null=True,
|
||||||
|
verbose_name="Raw message contents",
|
||||||
|
),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -4,18 +4,25 @@ from django.db import models, migrations
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
('django_mailbox', '0003_auto_20150409_0316'),
|
("django_mailbox", "0003_auto_20150409_0316"),
|
||||||
]
|
]
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.AlterField(
|
migrations.AlterField(
|
||||||
model_name='message',
|
model_name="message",
|
||||||
name='eml',
|
name="eml",
|
||||||
field=models.FileField(verbose_name='Raw message contents', upload_to='messages', null=True, help_text='Original full content of message'),
|
field=models.FileField(
|
||||||
|
verbose_name="Raw message contents",
|
||||||
|
upload_to="messages",
|
||||||
|
null=True,
|
||||||
|
help_text="Original full content of message",
|
||||||
|
),
|
||||||
),
|
),
|
||||||
migrations.AlterField(
|
migrations.AlterField(
|
||||||
model_name='messageattachment',
|
model_name="messageattachment",
|
||||||
name='document',
|
name="document",
|
||||||
field=models.FileField(verbose_name='Document', upload_to='mailbox_attachments/%Y/%m/%d/'),
|
field=models.FileField(
|
||||||
|
verbose_name="Document", upload_to="mailbox_attachments/%Y/%m/%d/"
|
||||||
|
),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -5,13 +5,16 @@ import django_mailbox.utils
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
('django_mailbox', '0004_bytestring_to_unicode'),
|
("django_mailbox", "0004_bytestring_to_unicode"),
|
||||||
]
|
]
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.AlterField(
|
migrations.AlterField(
|
||||||
model_name='messageattachment',
|
model_name="messageattachment",
|
||||||
name='document',
|
name="document",
|
||||||
field=models.FileField(upload_to=django_mailbox.utils.get_attachment_save_path, verbose_name='Document'),
|
field=models.FileField(
|
||||||
|
upload_to=django_mailbox.utils.get_attachment_save_path,
|
||||||
|
verbose_name="Document",
|
||||||
|
),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -6,13 +6,18 @@ from django.db import migrations, models
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
('django_mailbox', '0005_auto_20160523_2240'),
|
("django_mailbox", "0005_auto_20160523_2240"),
|
||||||
]
|
]
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='mailbox',
|
model_name="mailbox",
|
||||||
name='last_polling',
|
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'),
|
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",
|
||||||
|
),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -6,20 +6,26 @@ from django.db import migrations
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
('django_mailbox', '0006_mailbox_last_polling'),
|
("django_mailbox", "0006_mailbox_last_polling"),
|
||||||
]
|
]
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.AlterModelOptions(
|
migrations.AlterModelOptions(
|
||||||
name='mailbox',
|
name="mailbox",
|
||||||
options={'verbose_name': 'Mailbox', 'verbose_name_plural': 'Mailboxes'},
|
options={"verbose_name": "Mailbox", "verbose_name_plural": "Mailboxes"},
|
||||||
),
|
),
|
||||||
migrations.AlterModelOptions(
|
migrations.AlterModelOptions(
|
||||||
name='message',
|
name="message",
|
||||||
options={'verbose_name': 'E-mail message', 'verbose_name_plural': 'E-mail messages'},
|
options={
|
||||||
|
"verbose_name": "E-mail message",
|
||||||
|
"verbose_name_plural": "E-mail messages",
|
||||||
|
},
|
||||||
),
|
),
|
||||||
migrations.AlterModelOptions(
|
migrations.AlterModelOptions(
|
||||||
name='messageattachment',
|
name="messageattachment",
|
||||||
options={'verbose_name': 'Message attachment', 'verbose_name_plural': 'Message attachments'},
|
options={
|
||||||
|
"verbose_name": "Message attachment",
|
||||||
|
"verbose_name_plural": "Message attachments",
|
||||||
|
},
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -6,18 +6,25 @@ from django.db import migrations, models
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
('django_mailbox', '0007_auto_20180421_0026'),
|
("django_mailbox", "0007_auto_20180421_0026"),
|
||||||
]
|
]
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.AlterField(
|
migrations.AlterField(
|
||||||
model_name='mailbox',
|
model_name="mailbox",
|
||||||
name='active',
|
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'),
|
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(
|
migrations.AlterField(
|
||||||
model_name='message',
|
model_name="message",
|
||||||
name='outgoing',
|
name="outgoing",
|
||||||
field=models.BooleanField(blank=True, default=False, verbose_name='Outgoing'),
|
field=models.BooleanField(
|
||||||
|
blank=True, default=False, verbose_name="Outgoing"
|
||||||
|
),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,8 @@ import email
|
||||||
import logging
|
import logging
|
||||||
import mimetypes
|
import mimetypes
|
||||||
import os.path
|
import os.path
|
||||||
import sys
|
|
||||||
|
# import sys
|
||||||
import uuid
|
import uuid
|
||||||
from tempfile import NamedTemporaryFile
|
from tempfile import NamedTemporaryFile
|
||||||
|
|
||||||
|
|
@ -29,9 +30,16 @@ from django.utils.timezone import now
|
||||||
|
|
||||||
from django_mailbox import utils
|
from django_mailbox import utils
|
||||||
from django_mailbox.signals import message_received
|
from django_mailbox.signals import message_received
|
||||||
from django_mailbox.transports import Pop3Transport, ImapTransport, \
|
from django_mailbox.transports import (
|
||||||
MaildirTransport, MboxTransport, BabylTransport, MHTransport, \
|
Pop3Transport,
|
||||||
MMDFTransport, GmailImapTransport
|
ImapTransport,
|
||||||
|
MaildirTransport,
|
||||||
|
MboxTransport,
|
||||||
|
BabylTransport,
|
||||||
|
MHTransport,
|
||||||
|
MMDFTransport,
|
||||||
|
GmailImapTransport,
|
||||||
|
)
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
@ -40,9 +48,9 @@ class MailboxQuerySet(models.QuerySet):
|
||||||
def get_new_mail(self):
|
def get_new_mail(self):
|
||||||
count = 0
|
count = 0
|
||||||
for mailbox in self.all():
|
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())
|
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):
|
class MailboxManager(models.Manager):
|
||||||
|
|
@ -52,72 +60,86 @@ class MailboxManager(models.Manager):
|
||||||
|
|
||||||
class ActiveMailboxManager(MailboxManager):
|
class ActiveMailboxManager(MailboxManager):
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
return super().get_queryset().filter(
|
return (
|
||||||
active=True,
|
super()
|
||||||
|
.get_queryset()
|
||||||
|
.filter(
|
||||||
|
active=True,
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class Mailbox(models.Model):
|
class Mailbox(models.Model):
|
||||||
name = models.CharField(
|
name = models.CharField(
|
||||||
_('Name'),
|
_("Name"),
|
||||||
max_length=255,
|
max_length=255,
|
||||||
)
|
)
|
||||||
|
|
||||||
uri = models.CharField(
|
uri = models.CharField(
|
||||||
_('URI'),
|
_("URI"),
|
||||||
max_length=255,
|
max_length=255,
|
||||||
help_text=(_(
|
help_text=(
|
||||||
"Example: imap+ssl://myusername:mypassword@someserver <br />"
|
_(
|
||||||
"<br />"
|
"Example: imap+ssl://myusername:mypassword@someserver <br />"
|
||||||
"Internet transports include 'imap' and 'pop3'; "
|
"<br />"
|
||||||
"common local file transports include 'maildir', 'mbox', "
|
"Internet transports include 'imap' and 'pop3'; "
|
||||||
"and less commonly 'babyl', 'mh', and 'mmdf'. <br />"
|
"common local file transports include 'maildir', 'mbox', "
|
||||||
"<br />"
|
"and less commonly 'babyl', 'mh', and 'mmdf'. <br />"
|
||||||
"Be sure to urlencode your username and password should they "
|
"<br />"
|
||||||
"contain illegal characters (like @, :, etc)."
|
"Be sure to urlencode your username and password should they "
|
||||||
)),
|
"contain illegal characters (like @, :, etc)."
|
||||||
|
)
|
||||||
|
),
|
||||||
blank=True,
|
blank=True,
|
||||||
null=True,
|
null=True,
|
||||||
default=None,
|
default=None,
|
||||||
)
|
)
|
||||||
|
|
||||||
from_email = models.CharField(
|
from_email = models.CharField(
|
||||||
_('From email'),
|
_("From email"),
|
||||||
max_length=255,
|
max_length=255,
|
||||||
help_text=(_(
|
help_text=(
|
||||||
"Example: MailBot <mailbot@yourdomain.com><br />"
|
_(
|
||||||
"'From' header to set for outgoing email.<br />"
|
"Example: MailBot <mailbot@yourdomain.com><br />"
|
||||||
"<br />"
|
"'From' header to set for outgoing email.<br />"
|
||||||
"If you do not use this e-mail inbox for outgoing mail, this "
|
"<br />"
|
||||||
"setting is unnecessary.<br />"
|
"If you do not use this e-mail inbox for outgoing mail, this "
|
||||||
"If you send e-mail without setting this, your 'From' header will'"
|
"setting is unnecessary.<br />"
|
||||||
"be set to match the setting `DEFAULT_FROM_EMAIL`."
|
"If you send e-mail without setting this, your 'From' header will'"
|
||||||
)),
|
"be set to match the setting `DEFAULT_FROM_EMAIL`."
|
||||||
|
)
|
||||||
|
),
|
||||||
blank=True,
|
blank=True,
|
||||||
null=True,
|
null=True,
|
||||||
default=None,
|
default=None,
|
||||||
)
|
)
|
||||||
|
|
||||||
active = models.BooleanField(
|
active = models.BooleanField(
|
||||||
_('Active'),
|
_("Active"),
|
||||||
help_text=(_(
|
help_text=(
|
||||||
"Check this e-mail inbox for new e-mail messages during polling "
|
_(
|
||||||
"cycles. This checkbox does not have an effect upon whether "
|
"Check this e-mail inbox for new e-mail messages during polling "
|
||||||
"mail is collected here when this mailbox receives mail from a "
|
"cycles. This checkbox does not have an effect upon whether "
|
||||||
"pipe, and does not affect whether e-mail messages can be "
|
"mail is collected here when this mailbox receives mail from a "
|
||||||
"dispatched from this mailbox. "
|
"pipe, and does not affect whether e-mail messages can be "
|
||||||
)),
|
"dispatched from this mailbox. "
|
||||||
|
)
|
||||||
|
),
|
||||||
blank=True,
|
blank=True,
|
||||||
default=True,
|
default=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
last_polling = models.DateTimeField(
|
last_polling = models.DateTimeField(
|
||||||
_("Last polling"),
|
_("Last polling"),
|
||||||
help_text=(_("The time of last successful polling for messages."
|
help_text=(
|
||||||
"It is blank for new mailboxes and is not set for "
|
_(
|
||||||
"mailboxes that only receive messages via a pipe.")),
|
"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,
|
blank=True,
|
||||||
null=True
|
null=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
objects = MailboxManager()
|
objects = MailboxManager()
|
||||||
|
|
@ -153,30 +175,30 @@ class Mailbox(models.Model):
|
||||||
@property
|
@property
|
||||||
def location(self):
|
def location(self):
|
||||||
"""Returns the location (domain and path) of messages."""
|
"""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
|
@property
|
||||||
def type(self):
|
def type(self):
|
||||||
"""Returns the 'transport' name for this mailbox."""
|
"""Returns the 'transport' name for this mailbox."""
|
||||||
scheme = self._protocol_info.scheme.lower()
|
scheme = self._protocol_info.scheme.lower()
|
||||||
if '+' in scheme:
|
if "+" in scheme:
|
||||||
return scheme.split('+')[0]
|
return scheme.split("+")[0]
|
||||||
return scheme
|
return scheme
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def use_ssl(self):
|
def use_ssl(self):
|
||||||
"""Returns whether or not this mailbox's connection uses SSL."""
|
"""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
|
@property
|
||||||
def use_tls(self):
|
def use_tls(self):
|
||||||
"""Returns whether or not this mailbox's connection uses STARTTLS."""
|
"""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
|
@property
|
||||||
def archive(self):
|
def archive(self):
|
||||||
"""Returns (if specified) the folder to archive messages to."""
|
"""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:
|
if not archive_folder:
|
||||||
return None
|
return None
|
||||||
return archive_folder[0]
|
return archive_folder[0]
|
||||||
|
|
@ -184,7 +206,7 @@ class Mailbox(models.Model):
|
||||||
@property
|
@property
|
||||||
def folder(self):
|
def folder(self):
|
||||||
"""Returns (if specified) the folder to fetch mail from."""
|
"""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:
|
if not folder:
|
||||||
return None
|
return None
|
||||||
return folder[0]
|
return folder[0]
|
||||||
|
|
@ -198,40 +220,38 @@ class Mailbox(models.Model):
|
||||||
"""
|
"""
|
||||||
if not self.uri:
|
if not self.uri:
|
||||||
return None
|
return None
|
||||||
elif self.type == 'imap':
|
elif self.type == "imap":
|
||||||
conn = ImapTransport(
|
conn = ImapTransport(
|
||||||
self.location,
|
self.location,
|
||||||
port=self.port if self.port else None,
|
port=self.port if self.port else None,
|
||||||
ssl=self.use_ssl,
|
ssl=self.use_ssl,
|
||||||
tls=self.use_tls,
|
tls=self.use_tls,
|
||||||
archive=self.archive,
|
archive=self.archive,
|
||||||
folder=self.folder
|
folder=self.folder,
|
||||||
)
|
)
|
||||||
conn.connect(self.username, self.password)
|
conn.connect(self.username, self.password)
|
||||||
elif self.type == 'gmail':
|
elif self.type == "gmail":
|
||||||
conn = GmailImapTransport(
|
conn = GmailImapTransport(
|
||||||
self.location,
|
self.location,
|
||||||
port=self.port if self.port else None,
|
port=self.port if self.port else None,
|
||||||
ssl=True,
|
ssl=True,
|
||||||
archive=self.archive
|
archive=self.archive,
|
||||||
)
|
)
|
||||||
conn.connect(self.username, self.password)
|
conn.connect(self.username, self.password)
|
||||||
elif self.type == 'pop3':
|
elif self.type == "pop3":
|
||||||
conn = Pop3Transport(
|
conn = Pop3Transport(
|
||||||
self.location,
|
self.location, port=self.port if self.port else None, ssl=self.use_ssl
|
||||||
port=self.port if self.port else None,
|
|
||||||
ssl=self.use_ssl
|
|
||||||
)
|
)
|
||||||
conn.connect(self.username, self.password)
|
conn.connect(self.username, self.password)
|
||||||
elif self.type == 'maildir':
|
elif self.type == "maildir":
|
||||||
conn = MaildirTransport(self.location)
|
conn = MaildirTransport(self.location)
|
||||||
elif self.type == 'mbox':
|
elif self.type == "mbox":
|
||||||
conn = MboxTransport(self.location)
|
conn = MboxTransport(self.location)
|
||||||
elif self.type == 'babyl':
|
elif self.type == "babyl":
|
||||||
conn = BabylTransport(self.location)
|
conn = BabylTransport(self.location)
|
||||||
elif self.type == 'mh':
|
elif self.type == "mh":
|
||||||
conn = MHTransport(self.location)
|
conn = MHTransport(self.location)
|
||||||
elif self.type == 'mmdf':
|
elif self.type == "mmdf":
|
||||||
conn = MMDFTransport(self.location)
|
conn = MMDFTransport(self.location)
|
||||||
return conn
|
return conn
|
||||||
|
|
||||||
|
|
@ -264,29 +284,22 @@ class Mailbox(models.Model):
|
||||||
for header, value in msg.items():
|
for header, value in msg.items():
|
||||||
new[header] = value
|
new[header] = value
|
||||||
for part in msg.get_payload():
|
for part in msg.get_payload():
|
||||||
new.attach(
|
new.attach(self._get_dehydrated_message(part, record))
|
||||||
self._get_dehydrated_message(part, record)
|
|
||||||
)
|
|
||||||
elif (
|
elif (
|
||||||
settings['strip_unallowed_mimetypes']
|
settings["strip_unallowed_mimetypes"]
|
||||||
and not msg.get_content_type() in settings['allowed_mimetypes']
|
and not msg.get_content_type() in settings["allowed_mimetypes"]
|
||||||
):
|
):
|
||||||
for header, value in msg.items():
|
for header, value in msg.items():
|
||||||
new[header] = value
|
new[header] = value
|
||||||
# Delete header, otherwise when attempting to deserialize the
|
# Delete header, otherwise when attempting to deserialize the
|
||||||
# payload, it will be expecting a body for this.
|
# payload, it will be expecting a body for this.
|
||||||
del new['Content-Transfer-Encoding']
|
del new["Content-Transfer-Encoding"]
|
||||||
new[settings['altered_message_header']] = (
|
new[
|
||||||
'Stripped; Content type %s not allowed' % (
|
settings["altered_message_header"]
|
||||||
msg.get_content_type()
|
] = "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 (
|
||||||
new.set_payload('')
|
"attachment" in msg.get("Content-Disposition", "")
|
||||||
elif (
|
|
||||||
(
|
|
||||||
msg.get_content_type() not in settings['text_stored_mimetypes']
|
|
||||||
) or
|
|
||||||
('attachment' in msg.get('Content-Disposition', ''))
|
|
||||||
):
|
):
|
||||||
filename = None
|
filename = None
|
||||||
raw_filename = msg.get_filename()
|
raw_filename = msg.get_filename()
|
||||||
|
|
@ -297,17 +310,13 @@ class Mailbox(models.Model):
|
||||||
else:
|
else:
|
||||||
_, extension = os.path.splitext(filename)
|
_, extension = os.path.splitext(filename)
|
||||||
if not extension:
|
if not extension:
|
||||||
extension = '.bin'
|
extension = ".bin"
|
||||||
|
|
||||||
attachment = MessageAttachment()
|
attachment = MessageAttachment()
|
||||||
|
|
||||||
attachment.document.save(
|
attachment.document.save(
|
||||||
uuid.uuid4().hex + extension,
|
uuid.uuid4().hex + extension,
|
||||||
ContentFile(
|
ContentFile(BytesIO(msg.get_payload(decode=True)).getvalue()),
|
||||||
BytesIO(
|
|
||||||
msg.get_payload(decode=True)
|
|
||||||
).getvalue()
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
attachment.message = record
|
attachment.message = record
|
||||||
for key, value in msg.items():
|
for key, value in msg.items():
|
||||||
|
|
@ -315,14 +324,14 @@ class Mailbox(models.Model):
|
||||||
attachment.save()
|
attachment.save()
|
||||||
|
|
||||||
placeholder = EmailMessage()
|
placeholder = EmailMessage()
|
||||||
placeholder[
|
placeholder[settings["attachment_interpolation_header"]] = str(
|
||||||
settings['attachment_interpolation_header']
|
attachment.pk
|
||||||
] = str(attachment.pk)
|
)
|
||||||
new = placeholder
|
new = placeholder
|
||||||
else:
|
else:
|
||||||
content_charset = msg.get_content_charset()
|
content_charset = msg.get_content_charset()
|
||||||
if not content_charset:
|
if not content_charset:
|
||||||
content_charset = 'ascii'
|
content_charset = "ascii"
|
||||||
try:
|
try:
|
||||||
# Make sure that the payload can be properly decoded in the
|
# Make sure that the payload can be properly decoded in the
|
||||||
# defined charset, if it can't, let's mash some things
|
# 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)
|
msg.get_payload(decode=True).decode(content_charset)
|
||||||
except LookupError:
|
except LookupError:
|
||||||
logger.warning(
|
logger.warning(
|
||||||
"Unknown encoding %s; interpreting as ASCII!",
|
"Unknown encoding %s; interpreting as ASCII!", content_charset
|
||||||
content_charset
|
|
||||||
)
|
|
||||||
msg.set_payload(
|
|
||||||
msg.get_payload(decode=True).decode(
|
|
||||||
'ascii',
|
|
||||||
'ignore'
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
msg.set_payload(msg.get_payload(decode=True).decode("ascii", "ignore"))
|
||||||
except ValueError:
|
except ValueError:
|
||||||
logger.warning(
|
logger.warning(
|
||||||
"Decoding error encountered; interpreting %s as ASCII!",
|
"Decoding error encountered; interpreting %s as ASCII!",
|
||||||
content_charset
|
content_charset,
|
||||||
)
|
|
||||||
msg.set_payload(
|
|
||||||
msg.get_payload(decode=True).decode(
|
|
||||||
'ascii',
|
|
||||||
'ignore'
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
msg.set_payload(msg.get_payload(decode=True).decode("ascii", "ignore"))
|
||||||
new = msg
|
new = msg
|
||||||
return new
|
return new
|
||||||
|
|
||||||
|
|
@ -358,23 +356,19 @@ class Mailbox(models.Model):
|
||||||
msg._email_object = message
|
msg._email_object = message
|
||||||
settings = utils.get_settings()
|
settings = utils.get_settings()
|
||||||
|
|
||||||
if settings['store_original_message']:
|
if settings["store_original_message"]:
|
||||||
self._process_save_original_message(message, msg)
|
self._process_save_original_message(message, msg)
|
||||||
msg.mailbox = self
|
msg.mailbox = self
|
||||||
if 'subject' in message:
|
if "subject" in message:
|
||||||
msg.subject = (
|
msg.subject = utils.convert_header_to_unicode(message["subject"])[0:255]
|
||||||
utils.convert_header_to_unicode(message['subject'])[0:255]
|
if "message-id" in message:
|
||||||
)
|
msg.message_id = message["message-id"][0:255].strip()
|
||||||
if 'message-id' in message:
|
if "from" in message:
|
||||||
msg.message_id = message['message-id'][0:255].strip()
|
msg.from_header = utils.convert_header_to_unicode(message["from"])
|
||||||
if 'from' in message:
|
if "to" in message:
|
||||||
msg.from_header = utils.convert_header_to_unicode(message['from'])
|
msg.to_header = utils.convert_header_to_unicode(message["to"])
|
||||||
if 'to' in message:
|
elif "Delivered-To" in message:
|
||||||
msg.to_header = utils.convert_header_to_unicode(message['to'])
|
msg.to_header = utils.convert_header_to_unicode(message["Delivered-To"])
|
||||||
elif 'Delivered-To' in message:
|
|
||||||
msg.to_header = utils.convert_header_to_unicode(
|
|
||||||
message['Delivered-To']
|
|
||||||
)
|
|
||||||
msg.save()
|
msg.save()
|
||||||
message = self._get_dehydrated_message(message, msg)
|
message = self._get_dehydrated_message(message, msg)
|
||||||
try:
|
try:
|
||||||
|
|
@ -382,13 +376,16 @@ class Mailbox(models.Model):
|
||||||
except KeyError as exc:
|
except KeyError as exc:
|
||||||
# email.message.replace_header may raise 'KeyError' if the header
|
# email.message.replace_header may raise 'KeyError' if the header
|
||||||
# 'content-transfer-encoding' is missing
|
# 'content-transfer-encoding' is missing
|
||||||
logger.warning("Failed to parse message: %s", exc,)
|
logger.warning(
|
||||||
|
"Failed to parse message: %s",
|
||||||
|
exc,
|
||||||
|
)
|
||||||
return None
|
return None
|
||||||
msg.set_body(body)
|
msg.set_body(body)
|
||||||
if message['in-reply-to']:
|
if message["in-reply-to"]:
|
||||||
try:
|
try:
|
||||||
msg.in_reply_to = Message.objects.filter(
|
msg.in_reply_to = Message.objects.filter(
|
||||||
message_id=message['in-reply-to'].strip()
|
message_id=message["in-reply-to"].strip()
|
||||||
)[0]
|
)[0]
|
||||||
except IndexError:
|
except IndexError:
|
||||||
pass
|
pass
|
||||||
|
|
@ -397,36 +394,31 @@ class Mailbox(models.Model):
|
||||||
|
|
||||||
def _process_save_original_message(self, message, msg):
|
def _process_save_original_message(self, message, msg):
|
||||||
settings = utils.get_settings()
|
settings = utils.get_settings()
|
||||||
if settings['compress_original_message']:
|
if settings["compress_original_message"]:
|
||||||
with NamedTemporaryFile(suffix=".eml.gz") as fp_tmp:
|
with NamedTemporaryFile(suffix=".eml.gz") as fp_tmp:
|
||||||
with gzip.GzipFile(fileobj=fp_tmp, mode="w") as fp:
|
with gzip.GzipFile(fileobj=fp_tmp, mode="w") as fp:
|
||||||
fp.write(message.as_string().encode('utf-8'))
|
fp.write(message.as_string().encode("utf-8"))
|
||||||
msg.eml.save(
|
msg.eml.save("{}.eml.gz".format(uuid.uuid4()), File(fp_tmp), save=False)
|
||||||
"{}.eml.gz".format(uuid.uuid4()),
|
|
||||||
File(fp_tmp),
|
|
||||||
save=False
|
|
||||||
)
|
|
||||||
|
|
||||||
else:
|
else:
|
||||||
msg.eml.save(
|
msg.eml.save(
|
||||||
'%s.eml' % uuid.uuid4(),
|
"%s.eml" % uuid.uuid4(), ContentFile(message.as_string()), save=False
|
||||||
ContentFile(message.as_string()),
|
|
||||||
save=False
|
|
||||||
)
|
)
|
||||||
|
|
||||||
def get_new_mail(self, condition=None):
|
def get_new_mail(self, condition=None):
|
||||||
"""Connect to this transport and fetch new messages."""
|
"""Connect to this transport and fetch new messages."""
|
||||||
new_mail = []
|
# Delete this
|
||||||
|
# new_mail = []
|
||||||
connection = self.get_connection()
|
connection = self.get_connection()
|
||||||
if not connection:
|
if not connection:
|
||||||
return
|
return
|
||||||
for message in connection.get_message(condition):
|
for message in connection.get_message(condition):
|
||||||
msg = self.process_incoming_message(message)
|
msg = self.process_incoming_message(message)
|
||||||
if not msg is None:
|
if msg is not None:
|
||||||
yield msg
|
yield msg
|
||||||
self.last_polling = now()
|
self.last_polling = now()
|
||||||
if django.VERSION >= (1, 5): # Django 1.5 introduces update_fields
|
if django.VERSION >= (1, 5): # Django 1.5 introduces update_fields
|
||||||
self.save(update_fields=['last_polling'])
|
self.save(update_fields=["last_polling"])
|
||||||
else:
|
else:
|
||||||
self.save()
|
self.save()
|
||||||
|
|
||||||
|
|
@ -434,100 +426,97 @@ class Mailbox(models.Model):
|
||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name = _('Mailbox')
|
verbose_name = _("Mailbox")
|
||||||
verbose_name_plural = _('Mailboxes')
|
verbose_name_plural = _("Mailboxes")
|
||||||
|
|
||||||
|
|
||||||
class IncomingMessageManager(models.Manager):
|
class IncomingMessageManager(models.Manager):
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
return super().get_queryset().filter(
|
return (
|
||||||
outgoing=False,
|
super()
|
||||||
|
.get_queryset()
|
||||||
|
.filter(
|
||||||
|
outgoing=False,
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class OutgoingMessageManager(models.Manager):
|
class OutgoingMessageManager(models.Manager):
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
return super().get_queryset().filter(
|
return (
|
||||||
outgoing=True,
|
super()
|
||||||
|
.get_queryset()
|
||||||
|
.filter(
|
||||||
|
outgoing=True,
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class UnreadMessageManager(models.Manager):
|
class UnreadMessageManager(models.Manager):
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
return super().get_queryset().filter(
|
return super().get_queryset().filter(read=None)
|
||||||
read=None
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class Message(models.Model):
|
class Message(models.Model):
|
||||||
mailbox = models.ForeignKey(
|
mailbox = models.ForeignKey(
|
||||||
Mailbox,
|
Mailbox,
|
||||||
related_name='messages',
|
related_name="messages",
|
||||||
verbose_name=_('Mailbox'),
|
verbose_name=_("Mailbox"),
|
||||||
on_delete=models.CASCADE
|
on_delete=models.CASCADE,
|
||||||
)
|
)
|
||||||
|
|
||||||
subject = models.CharField(
|
subject = models.CharField(_("Subject"), max_length=255)
|
||||||
_('Subject'),
|
|
||||||
max_length=255
|
|
||||||
)
|
|
||||||
|
|
||||||
message_id = models.CharField(
|
message_id = models.CharField(_("Message ID"), max_length=255)
|
||||||
_('Message ID'),
|
|
||||||
max_length=255
|
|
||||||
)
|
|
||||||
|
|
||||||
in_reply_to = models.ForeignKey(
|
in_reply_to = models.ForeignKey(
|
||||||
'django_mailbox.Message',
|
"django_mailbox.Message",
|
||||||
related_name='replies',
|
related_name="replies",
|
||||||
blank=True,
|
blank=True,
|
||||||
null=True,
|
null=True,
|
||||||
verbose_name=_('In reply to'),
|
verbose_name=_("In reply to"),
|
||||||
on_delete=models.CASCADE
|
on_delete=models.CASCADE,
|
||||||
)
|
)
|
||||||
|
|
||||||
from_header = models.CharField(
|
from_header = models.CharField(
|
||||||
_('From header'),
|
_("From header"),
|
||||||
max_length=255,
|
max_length=255,
|
||||||
)
|
)
|
||||||
|
|
||||||
to_header = models.TextField(
|
to_header = models.TextField(
|
||||||
_('To header'),
|
_("To header"),
|
||||||
)
|
)
|
||||||
|
|
||||||
outgoing = models.BooleanField(
|
outgoing = models.BooleanField(
|
||||||
_('Outgoing'),
|
_("Outgoing"),
|
||||||
default=False,
|
default=False,
|
||||||
blank=True,
|
blank=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
body = models.TextField(
|
body = models.TextField(
|
||||||
_('Body'),
|
_("Body"),
|
||||||
)
|
)
|
||||||
|
|
||||||
encoded = models.BooleanField(
|
encoded = models.BooleanField(
|
||||||
_('Encoded'),
|
_("Encoded"),
|
||||||
default=False,
|
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 = models.DateTimeField(_("Processed"), auto_now_add=True)
|
||||||
_('Processed'),
|
|
||||||
auto_now_add=True
|
|
||||||
)
|
|
||||||
|
|
||||||
read = models.DateTimeField(
|
read = models.DateTimeField(
|
||||||
_('Read'),
|
_("Read"),
|
||||||
default=None,
|
default=None,
|
||||||
blank=True,
|
blank=True,
|
||||||
null=True,
|
null=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
eml = models.FileField(
|
eml = models.FileField(
|
||||||
_('Raw message contents'),
|
_("Raw message contents"),
|
||||||
null=True,
|
null=True,
|
||||||
upload_to="messages",
|
upload_to="messages",
|
||||||
help_text=_('Original full content of message')
|
help_text=_("Original full content of message"),
|
||||||
)
|
)
|
||||||
objects = models.Manager()
|
objects = models.Manager()
|
||||||
unread_messages = UnreadMessageManager()
|
unread_messages = UnreadMessageManager()
|
||||||
|
|
@ -570,13 +559,9 @@ class Message(models.Model):
|
||||||
def to_addresses(self):
|
def to_addresses(self):
|
||||||
"""Returns a list of addresses to which this message was sent."""
|
"""Returns a list of addresses to which this message was sent."""
|
||||||
addresses = []
|
addresses = []
|
||||||
for address in self.to_header.split(','):
|
for address in self.to_header.split(","):
|
||||||
if address:
|
if address:
|
||||||
addresses.append(
|
addresses.append(parseaddr(address)[1].lower())
|
||||||
parseaddr(
|
|
||||||
address
|
|
||||||
)[1].lower()
|
|
||||||
)
|
|
||||||
return addresses
|
return addresses
|
||||||
|
|
||||||
def reply(self, message):
|
def reply(self, message):
|
||||||
|
|
@ -593,14 +578,12 @@ class Message(models.Model):
|
||||||
message.from_email = self.mailbox.from_email
|
message.from_email = self.mailbox.from_email
|
||||||
else:
|
else:
|
||||||
message.from_email = django_settings.DEFAULT_FROM_EMAIL
|
message.from_email = django_settings.DEFAULT_FROM_EMAIL
|
||||||
message.extra_headers['Message-ID'] = make_msgid()
|
message.extra_headers["Message-ID"] = make_msgid()
|
||||||
message.extra_headers['Date'] = formatdate()
|
message.extra_headers["Date"] = formatdate()
|
||||||
message.extra_headers['In-Reply-To'] = self.message_id.strip()
|
message.extra_headers["In-Reply-To"] = self.message_id.strip()
|
||||||
message.send()
|
message.send()
|
||||||
return self.mailbox.record_outgoing_message(
|
return self.mailbox.record_outgoing_message(
|
||||||
email.message_from_string(
|
email.message_from_string(message.message().as_string())
|
||||||
message.message().as_string()
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
|
@ -608,18 +591,22 @@ class Message(models.Model):
|
||||||
"""
|
"""
|
||||||
Returns the message body matching content type 'text/plain'.
|
Returns the message body matching content type 'text/plain'.
|
||||||
"""
|
"""
|
||||||
return utils.get_body_from_message(
|
return (
|
||||||
self.get_email_object(), 'text', 'plain'
|
utils.get_body_from_message(self.get_email_object(), "text", "plain")
|
||||||
).replace('=\n', '').strip()
|
.replace("=\n", "")
|
||||||
|
.strip()
|
||||||
|
)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def html(self):
|
def html(self):
|
||||||
"""
|
"""
|
||||||
Returns the message body matching content type 'text/html'.
|
Returns the message body matching content type 'text/html'.
|
||||||
"""
|
"""
|
||||||
return utils.get_body_from_message(
|
return (
|
||||||
self.get_email_object(), 'text', 'html'
|
utils.get_body_from_message(self.get_email_object(), "text", "html")
|
||||||
).replace('\n', '').strip()
|
.replace("\n", "")
|
||||||
|
.strip()
|
||||||
|
)
|
||||||
|
|
||||||
def _rehydrate(self, msg):
|
def _rehydrate(self, msg):
|
||||||
new = EmailMessage()
|
new = EmailMessage()
|
||||||
|
|
@ -629,53 +616,43 @@ class Message(models.Model):
|
||||||
for header, value in msg.items():
|
for header, value in msg.items():
|
||||||
new[header] = value
|
new[header] = value
|
||||||
for part in msg.get_payload():
|
for part in msg.get_payload():
|
||||||
new.attach(
|
new.attach(self._rehydrate(part))
|
||||||
self._rehydrate(part)
|
elif settings["attachment_interpolation_header"] in msg.keys():
|
||||||
)
|
|
||||||
elif settings['attachment_interpolation_header'] in msg.keys():
|
|
||||||
try:
|
try:
|
||||||
attachment = MessageAttachment.objects.get(
|
attachment = MessageAttachment.objects.get(
|
||||||
pk=msg[settings['attachment_interpolation_header']]
|
pk=msg[settings["attachment_interpolation_header"]]
|
||||||
)
|
)
|
||||||
for header, value in attachment.items():
|
for header, value in attachment.items():
|
||||||
new[header] = value
|
new[header] = value
|
||||||
encoding = new['Content-Transfer-Encoding']
|
encoding = new["Content-Transfer-Encoding"]
|
||||||
if encoding and encoding.lower() == 'quoted-printable':
|
if encoding and encoding.lower() == "quoted-printable":
|
||||||
# Cannot use `email.encoders.encode_quopri due to
|
# Cannot use `email.encoders.encode_quopri due to
|
||||||
# bug 14360: http://bugs.python.org/issue14360
|
# bug 14360: http://bugs.python.org/issue14360
|
||||||
output = BytesIO()
|
output = BytesIO()
|
||||||
encode_quopri(
|
encode_quopri(
|
||||||
BytesIO(
|
BytesIO(attachment.document.read()),
|
||||||
attachment.document.read()
|
|
||||||
),
|
|
||||||
output,
|
output,
|
||||||
quotetabs=True,
|
quotetabs=True,
|
||||||
header=False,
|
header=False,
|
||||||
)
|
)
|
||||||
new.set_payload(
|
new.set_payload(output.getvalue().decode().replace(" ", "=20"))
|
||||||
output.getvalue().decode().replace(' ', '=20')
|
del new["Content-Transfer-Encoding"]
|
||||||
)
|
new["Content-Transfer-Encoding"] = "quoted-printable"
|
||||||
del new['Content-Transfer-Encoding']
|
|
||||||
new['Content-Transfer-Encoding'] = 'quoted-printable'
|
|
||||||
else:
|
else:
|
||||||
new.set_payload(
|
new.set_payload(attachment.document.read())
|
||||||
attachment.document.read()
|
del new["Content-Transfer-Encoding"]
|
||||||
)
|
|
||||||
del new['Content-Transfer-Encoding']
|
|
||||||
encode_base64(new)
|
encode_base64(new)
|
||||||
except MessageAttachment.DoesNotExist:
|
except MessageAttachment.DoesNotExist:
|
||||||
new[settings['altered_message_header']] = (
|
new[
|
||||||
'Missing; Attachment %s not found' % (
|
settings["altered_message_header"]
|
||||||
msg[settings['attachment_interpolation_header']]
|
] = "Missing; Attachment %s not found" % (
|
||||||
)
|
msg[settings["attachment_interpolation_header"]]
|
||||||
)
|
)
|
||||||
new.set_payload('')
|
new.set_payload("")
|
||||||
else:
|
else:
|
||||||
for header, value in msg.items():
|
for header, value in msg.items():
|
||||||
new[header] = value
|
new[header] = value
|
||||||
new.set_payload(
|
new.set_payload(msg.get_payload())
|
||||||
msg.get_payload()
|
|
||||||
)
|
|
||||||
return new
|
return new
|
||||||
|
|
||||||
def get_body(self):
|
def get_body(self):
|
||||||
|
|
@ -686,8 +663,8 @@ class Message(models.Model):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
if self.encoded:
|
if self.encoded:
|
||||||
return base64.b64decode(self.body.encode('ascii'))
|
return base64.b64decode(self.body.encode("ascii"))
|
||||||
return self.body.encode('utf-8')
|
return self.body.encode("utf-8")
|
||||||
|
|
||||||
def set_body(self, body):
|
def set_body(self, body):
|
||||||
"""Set the `body` field of this record.
|
"""Set the `body` field of this record.
|
||||||
|
|
@ -698,7 +675,7 @@ class Message(models.Model):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
self.encoded = True
|
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):
|
def get_email_object(self):
|
||||||
"""Returns an `email.message.EmailMessage` instance representing the
|
"""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)
|
(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:
|
||||||
if self.eml.name.endswith('.gz'):
|
if self.eml.name.endswith(".gz"):
|
||||||
body = gzip.GzipFile(fileobj=self.eml).read()
|
body = gzip.GzipFile(fileobj=self.eml).read()
|
||||||
else:
|
else:
|
||||||
self.eml.open()
|
self.eml.open()
|
||||||
|
|
@ -744,28 +721,28 @@ class Message(models.Model):
|
||||||
return self.subject
|
return self.subject
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name = _('E-mail message')
|
verbose_name = _("E-mail message")
|
||||||
verbose_name_plural = _('E-mail messages')
|
verbose_name_plural = _("E-mail messages")
|
||||||
|
|
||||||
|
|
||||||
class MessageAttachment(models.Model):
|
class MessageAttachment(models.Model):
|
||||||
message = models.ForeignKey(
|
message = models.ForeignKey(
|
||||||
Message,
|
Message,
|
||||||
related_name='attachments',
|
related_name="attachments",
|
||||||
null=True,
|
null=True,
|
||||||
blank=True,
|
blank=True,
|
||||||
verbose_name=_('Message'),
|
verbose_name=_("Message"),
|
||||||
on_delete=models.CASCADE
|
on_delete=models.CASCADE,
|
||||||
)
|
)
|
||||||
|
|
||||||
headers = models.TextField(
|
headers = models.TextField(
|
||||||
_('Headers'),
|
_("Headers"),
|
||||||
null=True,
|
null=True,
|
||||||
blank=True,
|
blank=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
document = models.FileField(
|
document = models.FileField(
|
||||||
_('Document'),
|
_("Document"),
|
||||||
upload_to=utils.get_attachment_save_path,
|
upload_to=utils.get_attachment_save_path,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -810,12 +787,12 @@ class MessageAttachment(models.Model):
|
||||||
def __getitem__(self, name):
|
def __getitem__(self, name):
|
||||||
value = self._get_rehydrated_headers()[name]
|
value = self._get_rehydrated_headers()[name]
|
||||||
if value is None:
|
if value is None:
|
||||||
raise KeyError('Header %s does not exist' % name)
|
raise KeyError("Header %s does not exist" % name)
|
||||||
return value
|
return value
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.document.url
|
return self.document.url
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name = _('Message attachment')
|
verbose_name = _("Message attachment")
|
||||||
verbose_name_plural = _('Message attachments')
|
verbose_name_plural = _("Message attachments")
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
from django.dispatch.dispatcher import Signal
|
from django.dispatch.dispatcher import Signal
|
||||||
|
|
||||||
message_received = Signal(providing_args=['message'])
|
message_received = Signal(providing_args=["message"])
|
||||||
|
|
|
||||||
|
|
@ -5,54 +5,92 @@ from django.db import models
|
||||||
|
|
||||||
|
|
||||||
class Migration(SchemaMigration):
|
class Migration(SchemaMigration):
|
||||||
|
|
||||||
def forwards(self, orm):
|
def forwards(self, orm):
|
||||||
# Adding model 'Mailbox'
|
# Adding model 'Mailbox'
|
||||||
db.create_table('django_mailbox_mailbox', (
|
db.create_table(
|
||||||
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
|
"django_mailbox_mailbox",
|
||||||
('name', self.gf('django.db.models.fields.CharField')(max_length=255)),
|
(
|
||||||
('uri', self.gf('django.db.models.fields.CharField')(max_length=255)),
|
("id", self.gf("django.db.models.fields.AutoField")(primary_key=True)),
|
||||||
))
|
("name", self.gf("django.db.models.fields.CharField")(max_length=255)),
|
||||||
db.send_create_signal('django_mailbox', ['Mailbox'])
|
("uri", self.gf("django.db.models.fields.CharField")(max_length=255)),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
db.send_create_signal("django_mailbox", ["Mailbox"])
|
||||||
|
|
||||||
# Adding model 'Message'
|
# Adding model 'Message'
|
||||||
db.create_table('django_mailbox_message', (
|
db.create_table(
|
||||||
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
|
"django_mailbox_message",
|
||||||
('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)),
|
("id", self.gf("django.db.models.fields.AutoField")(primary_key=True)),
|
||||||
('message_id', self.gf('django.db.models.fields.CharField')(max_length=255)),
|
(
|
||||||
('from_address', self.gf('django.db.models.fields.CharField')(max_length=255)),
|
"mailbox",
|
||||||
('body', self.gf('django.db.models.fields.TextField')()),
|
self.gf("django.db.models.fields.related.ForeignKey")(
|
||||||
('received', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True, blank=True)),
|
to=orm["django_mailbox.Mailbox"]
|
||||||
))
|
),
|
||||||
db.send_create_signal('django_mailbox', ['Message'])
|
),
|
||||||
|
(
|
||||||
|
"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):
|
def backwards(self, orm):
|
||||||
# Deleting model 'Mailbox'
|
# Deleting model 'Mailbox'
|
||||||
db.delete_table('django_mailbox_mailbox')
|
db.delete_table("django_mailbox_mailbox")
|
||||||
|
|
||||||
# Deleting model 'Message'
|
# Deleting model 'Message'
|
||||||
db.delete_table('django_mailbox_message')
|
db.delete_table("django_mailbox_message")
|
||||||
|
|
||||||
|
|
||||||
models = {
|
models = {
|
||||||
'django_mailbox.mailbox': {
|
"django_mailbox.mailbox": {
|
||||||
'Meta': {'object_name': 'Mailbox'},
|
"Meta": {"object_name": "Mailbox"},
|
||||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
"id": ("django.db.models.fields.AutoField", [], {"primary_key": "True"}),
|
||||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
"name": ("django.db.models.fields.CharField", [], {"max_length": "255"}),
|
||||||
'uri': ('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"]
|
||||||
|
|
|
||||||
|
|
@ -5,34 +5,64 @@ from django.db import models
|
||||||
|
|
||||||
|
|
||||||
class Migration(SchemaMigration):
|
class Migration(SchemaMigration):
|
||||||
|
|
||||||
def forwards(self, orm):
|
def forwards(self, orm):
|
||||||
|
|
||||||
# Changing field 'Mailbox.uri'
|
# 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):
|
def backwards(self, orm):
|
||||||
|
|
||||||
# User chose to not deal with backwards NULL issues for 'Mailbox.uri'
|
# 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 = {
|
models = {
|
||||||
'django_mailbox.mailbox': {
|
"django_mailbox.mailbox": {
|
||||||
'Meta': {'object_name': 'Mailbox'},
|
"Meta": {"object_name": "Mailbox"},
|
||||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
"id": ("django.db.models.fields.AutoField", [], {"primary_key": "True"}),
|
||||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
"name": ("django.db.models.fields.CharField", [], {"max_length": "255"}),
|
||||||
'uri': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '255', 'null': 'True', 'blank': 'True'})
|
"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"]
|
||||||
|
|
|
||||||
|
|
@ -5,37 +5,62 @@ from django.db import models
|
||||||
|
|
||||||
|
|
||||||
class Migration(SchemaMigration):
|
class Migration(SchemaMigration):
|
||||||
|
|
||||||
def forwards(self, orm):
|
def forwards(self, orm):
|
||||||
# Adding field 'Mailbox.active'
|
# Adding field 'Mailbox.active'
|
||||||
db.add_column('django_mailbox_mailbox', 'active',
|
db.add_column(
|
||||||
self.gf('django.db.models.fields.BooleanField')(default=True),
|
"django_mailbox_mailbox",
|
||||||
keep_default=False)
|
"active",
|
||||||
|
self.gf("django.db.models.fields.BooleanField")(default=True),
|
||||||
|
keep_default=False,
|
||||||
|
)
|
||||||
|
|
||||||
def backwards(self, orm):
|
def backwards(self, orm):
|
||||||
# Deleting field 'Mailbox.active'
|
# Deleting field 'Mailbox.active'
|
||||||
db.delete_column('django_mailbox_mailbox', 'active')
|
db.delete_column("django_mailbox_mailbox", "active")
|
||||||
|
|
||||||
|
|
||||||
models = {
|
models = {
|
||||||
'django_mailbox.mailbox': {
|
"django_mailbox.mailbox": {
|
||||||
'Meta': {'object_name': 'Mailbox'},
|
"Meta": {"object_name": "Mailbox"},
|
||||||
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
"active": ("django.db.models.fields.BooleanField", [], {"default": "True"}),
|
||||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
"id": ("django.db.models.fields.AutoField", [], {"primary_key": "True"}),
|
||||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
"name": ("django.db.models.fields.CharField", [], {"max_length": "255"}),
|
||||||
'uri': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '255', 'null': 'True', 'blank': 'True'})
|
"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"]
|
||||||
|
|
|
||||||
|
|
@ -5,38 +5,67 @@ from django.db import models
|
||||||
|
|
||||||
|
|
||||||
class Migration(SchemaMigration):
|
class Migration(SchemaMigration):
|
||||||
|
|
||||||
def forwards(self, orm):
|
def forwards(self, orm):
|
||||||
# Adding field 'Message.outgoing'
|
# Adding field 'Message.outgoing'
|
||||||
db.add_column('django_mailbox_message', 'outgoing',
|
db.add_column(
|
||||||
self.gf('django.db.models.fields.BooleanField')(default=False),
|
"django_mailbox_message",
|
||||||
keep_default=False)
|
"outgoing",
|
||||||
|
self.gf("django.db.models.fields.BooleanField")(default=False),
|
||||||
|
keep_default=False,
|
||||||
|
)
|
||||||
|
|
||||||
def backwards(self, orm):
|
def backwards(self, orm):
|
||||||
# Deleting field 'Message.outgoing'
|
# Deleting field 'Message.outgoing'
|
||||||
db.delete_column('django_mailbox_message', 'outgoing')
|
db.delete_column("django_mailbox_message", "outgoing")
|
||||||
|
|
||||||
|
|
||||||
models = {
|
models = {
|
||||||
'django_mailbox.mailbox': {
|
"django_mailbox.mailbox": {
|
||||||
'Meta': {'object_name': 'Mailbox'},
|
"Meta": {"object_name": "Mailbox"},
|
||||||
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
"active": ("django.db.models.fields.BooleanField", [], {"default": "True"}),
|
||||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
"id": ("django.db.models.fields.AutoField", [], {"primary_key": "True"}),
|
||||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
"name": ("django.db.models.fields.CharField", [], {"max_length": "255"}),
|
||||||
'uri': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '255', 'null': 'True', 'blank': 'True'})
|
"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"]
|
||||||
|
|
|
||||||
|
|
@ -5,34 +5,58 @@ from django.db import models
|
||||||
|
|
||||||
|
|
||||||
class Migration(SchemaMigration):
|
class Migration(SchemaMigration):
|
||||||
|
|
||||||
def forwards(self, orm):
|
def forwards(self, orm):
|
||||||
db.rename_column('django_mailbox_message', 'from_address', 'address')
|
db.rename_column("django_mailbox_message", "from_address", "address")
|
||||||
db.rename_column('django_mailbox_message', 'received', 'processed')
|
db.rename_column("django_mailbox_message", "received", "processed")
|
||||||
|
|
||||||
def backwards(self, orm):
|
def backwards(self, orm):
|
||||||
db.rename_column('django_mailbox_message', 'address', 'from_address')
|
db.rename_column("django_mailbox_message", "address", "from_address")
|
||||||
db.rename_column('django_mailbox_message', 'processed', 'received')
|
db.rename_column("django_mailbox_message", "processed", "received")
|
||||||
|
|
||||||
models = {
|
models = {
|
||||||
'django_mailbox.mailbox': {
|
"django_mailbox.mailbox": {
|
||||||
'Meta': {'object_name': 'Mailbox'},
|
"Meta": {"object_name": "Mailbox"},
|
||||||
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
"active": ("django.db.models.fields.BooleanField", [], {"default": "True"}),
|
||||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
"id": ("django.db.models.fields.AutoField", [], {"primary_key": "True"}),
|
||||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
"name": ("django.db.models.fields.CharField", [], {"max_length": "255"}),
|
||||||
'uri': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '255', 'null': 'True', 'blank': 'True'})
|
"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"]
|
||||||
|
|
|
||||||
|
|
@ -5,51 +5,116 @@ from django.db import models
|
||||||
|
|
||||||
|
|
||||||
class Migration(SchemaMigration):
|
class Migration(SchemaMigration):
|
||||||
|
|
||||||
def forwards(self, orm):
|
def forwards(self, orm):
|
||||||
# Adding field 'Message.in_reply_to'
|
# Adding field 'Message.in_reply_to'
|
||||||
db.add_column('django_mailbox_message', 'in_reply_to',
|
db.add_column(
|
||||||
self.gf('django.db.models.fields.related.ForeignKey')(blank=True, related_name='replies', null=True, to=orm['django_mailbox.Message']),
|
"django_mailbox_message",
|
||||||
keep_default=False)
|
"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'
|
# Adding M2M table for field references on 'Message'
|
||||||
db.create_table('django_mailbox_message_references', (
|
db.create_table(
|
||||||
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
|
"django_mailbox_message_references",
|
||||||
('from_message', models.ForeignKey(orm['django_mailbox.message'], null=False)),
|
(
|
||||||
('to_message', models.ForeignKey(orm['django_mailbox.message'], null=False))
|
(
|
||||||
))
|
"id",
|
||||||
db.create_unique('django_mailbox_message_references', ['from_message_id', 'to_message_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):
|
def backwards(self, orm):
|
||||||
# Deleting field 'Message.in_reply_to'
|
# 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'
|
# Removing M2M table for field references on 'Message'
|
||||||
db.delete_table('django_mailbox_message_references')
|
db.delete_table("django_mailbox_message_references")
|
||||||
|
|
||||||
|
|
||||||
models = {
|
models = {
|
||||||
'django_mailbox.mailbox': {
|
"django_mailbox.mailbox": {
|
||||||
'Meta': {'object_name': 'Mailbox'},
|
"Meta": {"object_name": "Mailbox"},
|
||||||
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
"active": ("django.db.models.fields.BooleanField", [], {"default": "True"}),
|
||||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
"id": ("django.db.models.fields.AutoField", [], {"primary_key": "True"}),
|
||||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
"name": ("django.db.models.fields.CharField", [], {"max_length": "255"}),
|
||||||
'uri': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '255', 'null': 'True', 'blank': 'True'})
|
"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"]
|
||||||
|
|
|
||||||
|
|
@ -5,55 +5,108 @@ from django.db import models
|
||||||
|
|
||||||
|
|
||||||
class Migration(SchemaMigration):
|
class Migration(SchemaMigration):
|
||||||
|
|
||||||
def forwards(self, orm):
|
def forwards(self, orm):
|
||||||
# Deleting field 'Message.address'
|
# Deleting field 'Message.address'
|
||||||
db.delete_column('django_mailbox_message', 'address')
|
db.delete_column("django_mailbox_message", "address")
|
||||||
|
|
||||||
# Adding field 'Message.from_header'
|
# Adding field 'Message.from_header'
|
||||||
db.add_column('django_mailbox_message', 'from_header',
|
db.add_column(
|
||||||
self.gf('django.db.models.fields.CharField')(default='', max_length=255),
|
"django_mailbox_message",
|
||||||
keep_default=False)
|
"from_header",
|
||||||
|
self.gf("django.db.models.fields.CharField")(default="", max_length=255),
|
||||||
|
keep_default=False,
|
||||||
|
)
|
||||||
|
|
||||||
# Adding field 'Message.to_header'
|
# Adding field 'Message.to_header'
|
||||||
db.add_column('django_mailbox_message', 'to_header',
|
db.add_column(
|
||||||
self.gf('django.db.models.fields.TextField')(default=''),
|
"django_mailbox_message",
|
||||||
keep_default=False)
|
"to_header",
|
||||||
|
self.gf("django.db.models.fields.TextField")(default=""),
|
||||||
|
keep_default=False,
|
||||||
|
)
|
||||||
|
|
||||||
def backwards(self, orm):
|
def backwards(self, orm):
|
||||||
|
|
||||||
# User chose to not deal with backwards NULL issues for 'Message.address'
|
# 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'
|
# 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'
|
# Deleting field 'Message.to_header'
|
||||||
db.delete_column('django_mailbox_message', 'to_header')
|
db.delete_column("django_mailbox_message", "to_header")
|
||||||
|
|
||||||
|
|
||||||
models = {
|
models = {
|
||||||
'django_mailbox.mailbox': {
|
"django_mailbox.mailbox": {
|
||||||
'Meta': {'object_name': 'Mailbox'},
|
"Meta": {"object_name": "Mailbox"},
|
||||||
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
"active": ("django.db.models.fields.BooleanField", [], {"default": "True"}),
|
||||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
"id": ("django.db.models.fields.AutoField", [], {"primary_key": "True"}),
|
||||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
"name": ("django.db.models.fields.CharField", [], {"max_length": "255"}),
|
||||||
'uri': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '255', 'null': 'True', 'blank': 'True'})
|
"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"]
|
||||||
|
|
|
||||||
|
|
@ -4,43 +4,89 @@ from south.db import db
|
||||||
from south.v2 import DataMigration
|
from south.v2 import DataMigration
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
|
||||||
class Migration(DataMigration):
|
|
||||||
|
|
||||||
|
class Migration(DataMigration):
|
||||||
def forwards(self, orm):
|
def forwards(self, orm):
|
||||||
for message in orm['django_mailbox.message'].objects.all():
|
for message in orm["django_mailbox.message"].objects.all():
|
||||||
msg_object = email.message_from_string(
|
msg_object = email.message_from_string(message.body)
|
||||||
message.body
|
message.from_header = msg_object["from"]
|
||||||
)
|
message.to_header = msg_object["to"]
|
||||||
message.from_header = msg_object['from']
|
|
||||||
message.to_header = msg_object['to']
|
|
||||||
message.save()
|
message.save()
|
||||||
|
|
||||||
def backwards(self, orm):
|
def backwards(self, orm):
|
||||||
raise RuntimeError('Cannot reverse this migration.')
|
raise RuntimeError("Cannot reverse this migration.")
|
||||||
|
|
||||||
models = {
|
models = {
|
||||||
'django_mailbox.mailbox': {
|
"django_mailbox.mailbox": {
|
||||||
'Meta': {'object_name': 'Mailbox'},
|
"Meta": {"object_name": "Mailbox"},
|
||||||
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
"active": ("django.db.models.fields.BooleanField", [], {"default": "True"}),
|
||||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
"id": ("django.db.models.fields.AutoField", [], {"primary_key": "True"}),
|
||||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
"name": ("django.db.models.fields.CharField", [], {"max_length": "255"}),
|
||||||
'uri': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '255', 'null': 'True', 'blank': 'True'})
|
"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
|
symmetrical = True
|
||||||
|
|
|
||||||
|
|
@ -5,43 +5,94 @@ from django.db import models
|
||||||
|
|
||||||
|
|
||||||
class Migration(SchemaMigration):
|
class Migration(SchemaMigration):
|
||||||
|
|
||||||
def forwards(self, orm):
|
def forwards(self, orm):
|
||||||
# Removing M2M table for field references on 'Message'
|
# 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):
|
def backwards(self, orm):
|
||||||
# Adding M2M table for field references on 'Message'
|
# Adding M2M table for field references on 'Message'
|
||||||
db.create_table('django_mailbox_message_references', (
|
db.create_table(
|
||||||
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
|
"django_mailbox_message_references",
|
||||||
('from_message', models.ForeignKey(orm['django_mailbox.message'], null=False)),
|
(
|
||||||
('to_message', models.ForeignKey(orm['django_mailbox.message'], null=False))
|
(
|
||||||
))
|
"id",
|
||||||
db.create_unique('django_mailbox_message_references', ['from_message_id', 'to_message_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 = {
|
models = {
|
||||||
'django_mailbox.mailbox': {
|
"django_mailbox.mailbox": {
|
||||||
'Meta': {'object_name': 'Mailbox'},
|
"Meta": {"object_name": "Mailbox"},
|
||||||
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
"active": ("django.db.models.fields.BooleanField", [], {"default": "True"}),
|
||||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
"id": ("django.db.models.fields.AutoField", [], {"primary_key": "True"}),
|
||||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
"name": ("django.db.models.fields.CharField", [], {"max_length": "255"}),
|
||||||
'uri': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '255', 'null': 'True', 'blank': 'True'})
|
"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"]
|
||||||
|
|
|
||||||
|
|
@ -5,41 +5,90 @@ from django.db import models
|
||||||
|
|
||||||
|
|
||||||
class Migration(SchemaMigration):
|
class Migration(SchemaMigration):
|
||||||
|
|
||||||
def forwards(self, orm):
|
def forwards(self, orm):
|
||||||
# Adding field 'Mailbox.from_email'
|
# Adding field 'Mailbox.from_email'
|
||||||
db.add_column('django_mailbox_mailbox', 'from_email',
|
db.add_column(
|
||||||
self.gf('django.db.models.fields.CharField')(default=None, max_length=255, null=True, blank=True),
|
"django_mailbox_mailbox",
|
||||||
keep_default=False)
|
"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):
|
def backwards(self, orm):
|
||||||
# Deleting field 'Mailbox.from_email'
|
# Deleting field 'Mailbox.from_email'
|
||||||
db.delete_column('django_mailbox_mailbox', 'from_email')
|
db.delete_column("django_mailbox_mailbox", "from_email")
|
||||||
|
|
||||||
|
|
||||||
models = {
|
models = {
|
||||||
'django_mailbox.mailbox': {
|
"django_mailbox.mailbox": {
|
||||||
'Meta': {'object_name': 'Mailbox'},
|
"Meta": {"object_name": "Mailbox"},
|
||||||
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
"active": ("django.db.models.fields.BooleanField", [], {"default": "True"}),
|
||||||
'from_email': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '255', 'null': 'True', 'blank': 'True'}),
|
"from_email": (
|
||||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
"django.db.models.fields.CharField",
|
||||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
[],
|
||||||
'uri': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '255', 'null': 'True', 'blank': 'True'})
|
{
|
||||||
|
"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"]
|
||||||
|
|
|
||||||
|
|
@ -5,42 +5,95 @@ from django.db import models
|
||||||
|
|
||||||
|
|
||||||
class Migration(SchemaMigration):
|
class Migration(SchemaMigration):
|
||||||
|
|
||||||
def forwards(self, orm):
|
def forwards(self, orm):
|
||||||
# Adding field 'Message.read'
|
# Adding field 'Message.read'
|
||||||
db.add_column('django_mailbox_message', 'read',
|
db.add_column(
|
||||||
self.gf('django.db.models.fields.DateTimeField')(default=None, null=True, blank=True),
|
"django_mailbox_message",
|
||||||
keep_default=False)
|
"read",
|
||||||
|
self.gf("django.db.models.fields.DateTimeField")(
|
||||||
|
default=None, null=True, blank=True
|
||||||
|
),
|
||||||
|
keep_default=False,
|
||||||
|
)
|
||||||
|
|
||||||
def backwards(self, orm):
|
def backwards(self, orm):
|
||||||
# Deleting field 'Message.read'
|
# Deleting field 'Message.read'
|
||||||
db.delete_column('django_mailbox_message', 'read')
|
db.delete_column("django_mailbox_message", "read")
|
||||||
|
|
||||||
|
|
||||||
models = {
|
models = {
|
||||||
'django_mailbox.mailbox': {
|
"django_mailbox.mailbox": {
|
||||||
'Meta': {'object_name': 'Mailbox'},
|
"Meta": {"object_name": "Mailbox"},
|
||||||
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
"active": ("django.db.models.fields.BooleanField", [], {"default": "True"}),
|
||||||
'from_email': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '255', 'null': 'True', 'blank': 'True'}),
|
"from_email": (
|
||||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
"django.db.models.fields.CharField",
|
||||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
[],
|
||||||
'uri': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '255', 'null': 'True', 'blank': 'True'})
|
{
|
||||||
|
"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"]
|
||||||
|
|
|
||||||
|
|
@ -3,64 +3,148 @@ from south.db import db
|
||||||
from south.v2 import SchemaMigration
|
from south.v2 import SchemaMigration
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
|
||||||
class Migration(SchemaMigration):
|
|
||||||
|
|
||||||
|
class Migration(SchemaMigration):
|
||||||
def forwards(self, orm):
|
def forwards(self, orm):
|
||||||
|
|
||||||
# Adding model 'MessageAttachment'
|
# Adding model 'MessageAttachment'
|
||||||
db.create_table('django_mailbox_messageattachment', (
|
db.create_table(
|
||||||
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
|
"django_mailbox_messageattachment",
|
||||||
('document', self.gf('django.db.models.fields.files.FileField')(max_length=100)),
|
(
|
||||||
))
|
("id", self.gf("django.db.models.fields.AutoField")(primary_key=True)),
|
||||||
db.send_create_signal('django_mailbox', ['MessageAttachment'])
|
(
|
||||||
|
"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'
|
# Adding M2M table for field attachments on 'Message'
|
||||||
db.create_table('django_mailbox_message_attachments', (
|
db.create_table(
|
||||||
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
|
"django_mailbox_message_attachments",
|
||||||
('message', models.ForeignKey(orm['django_mailbox.message'], null=False)),
|
(
|
||||||
('messageattachment', models.ForeignKey(orm['django_mailbox.messageattachment'], null=False))
|
(
|
||||||
))
|
"id",
|
||||||
db.create_unique('django_mailbox_message_attachments', ['message_id', 'messageattachment_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):
|
def backwards(self, orm):
|
||||||
|
|
||||||
# Deleting model 'MessageAttachment'
|
# Deleting model 'MessageAttachment'
|
||||||
db.delete_table('django_mailbox_messageattachment')
|
db.delete_table("django_mailbox_messageattachment")
|
||||||
|
|
||||||
# Removing M2M table for field attachments on 'Message'
|
# Removing M2M table for field attachments on 'Message'
|
||||||
db.delete_table('django_mailbox_message_attachments')
|
db.delete_table("django_mailbox_message_attachments")
|
||||||
|
|
||||||
|
|
||||||
models = {
|
models = {
|
||||||
'django_mailbox.mailbox': {
|
"django_mailbox.mailbox": {
|
||||||
'Meta': {'object_name': 'Mailbox'},
|
"Meta": {"object_name": "Mailbox"},
|
||||||
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
"active": ("django.db.models.fields.BooleanField", [], {"default": "True"}),
|
||||||
'from_email': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '255', 'null': 'True', 'blank': 'True'}),
|
"from_email": (
|
||||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
"django.db.models.fields.CharField",
|
||||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
[],
|
||||||
'uri': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '255', 'null': 'True', 'blank': 'True'})
|
{
|
||||||
|
"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': {
|
"django_mailbox.message": {
|
||||||
'Meta': {'object_name': 'Message'},
|
"Meta": {"object_name": "Message"},
|
||||||
'attachments': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['django_mailbox.MessageAttachment']", 'symmetrical': 'False'}),
|
"attachments": (
|
||||||
'body': ('django.db.models.fields.TextField', [], {}),
|
"django.db.models.fields.related.ManyToManyField",
|
||||||
'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']"}),
|
"to": "orm['django_mailbox.MessageAttachment']",
|
||||||
'mailbox': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'messages'", 'to': "orm['django_mailbox.Mailbox']"}),
|
"symmetrical": "False",
|
||||||
'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'}),
|
"body": ("django.db.models.fields.TextField", [], {}),
|
||||||
'read': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True', 'blank': 'True'}),
|
"from_header": (
|
||||||
'subject': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
"django.db.models.fields.CharField",
|
||||||
'to_header': ('django.db.models.fields.TextField', [], {})
|
[],
|
||||||
|
{"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"]
|
||||||
|
|
|
||||||
|
|
@ -5,49 +5,127 @@ from django.db import models
|
||||||
|
|
||||||
|
|
||||||
class Migration(SchemaMigration):
|
class Migration(SchemaMigration):
|
||||||
|
|
||||||
def forwards(self, orm):
|
def forwards(self, orm):
|
||||||
# Adding field 'MessageAttachment.message'
|
# Adding field 'MessageAttachment.message'
|
||||||
db.add_column('django_mailbox_messageattachment', 'message',
|
db.add_column(
|
||||||
self.gf('django.db.models.fields.related.ForeignKey')(blank=True, related_name='attachments', null=True, to=orm['django_mailbox.Message']),
|
"django_mailbox_messageattachment",
|
||||||
keep_default=False)
|
"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):
|
def backwards(self, orm):
|
||||||
# Deleting field 'MessageAttachment.message'
|
# Deleting field 'MessageAttachment.message'
|
||||||
db.delete_column('django_mailbox_messageattachment', 'message_id')
|
db.delete_column("django_mailbox_messageattachment", "message_id")
|
||||||
|
|
||||||
|
|
||||||
models = {
|
models = {
|
||||||
'django_mailbox.mailbox': {
|
"django_mailbox.mailbox": {
|
||||||
'Meta': {'object_name': 'Mailbox'},
|
"Meta": {"object_name": "Mailbox"},
|
||||||
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
"active": ("django.db.models.fields.BooleanField", [], {"default": "True"}),
|
||||||
'from_email': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '255', 'null': 'True', 'blank': 'True'}),
|
"from_email": (
|
||||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
"django.db.models.fields.CharField",
|
||||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
[],
|
||||||
'uri': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '255', 'null': 'True', 'blank': 'True'})
|
{
|
||||||
|
"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': {
|
"django_mailbox.message": {
|
||||||
'Meta': {'object_name': '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']"}),
|
"attachments": (
|
||||||
'body': ('django.db.models.fields.TextField', [], {}),
|
"django.db.models.fields.related.ManyToManyField",
|
||||||
'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']"}),
|
"symmetrical": "False",
|
||||||
'mailbox': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'messages'", 'to': "orm['django_mailbox.Mailbox']"}),
|
"related_name": "'messages_old'",
|
||||||
'message_id': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
"blank": "True",
|
||||||
'outgoing': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
"to": "orm['django_mailbox.MessageAttachment']",
|
||||||
'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'}),
|
"body": ("django.db.models.fields.TextField", [], {}),
|
||||||
'to_header': ('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"]
|
||||||
|
|
|
||||||
|
|
@ -6,49 +6,121 @@ from django.db import models
|
||||||
|
|
||||||
class Migration(SchemaMigration):
|
class Migration(SchemaMigration):
|
||||||
no_dry_run = True
|
no_dry_run = True
|
||||||
|
|
||||||
def forwards(self, orm):
|
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():
|
for attachment in message.attachments.all():
|
||||||
attachment.message = message
|
attachment.message = message
|
||||||
attachment.save()
|
attachment.save()
|
||||||
|
|
||||||
def backwards(self, orm):
|
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:
|
if attachment.message:
|
||||||
attachment.message.attachments.add(
|
attachment.message.attachments.add(attachment)
|
||||||
attachment
|
|
||||||
)
|
|
||||||
|
|
||||||
models = {
|
models = {
|
||||||
'django_mailbox.mailbox': {
|
"django_mailbox.mailbox": {
|
||||||
'Meta': {'object_name': 'Mailbox'},
|
"Meta": {"object_name": "Mailbox"},
|
||||||
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
"active": ("django.db.models.fields.BooleanField", [], {"default": "True"}),
|
||||||
'from_email': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '255', 'null': 'True', 'blank': 'True'}),
|
"from_email": (
|
||||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
"django.db.models.fields.CharField",
|
||||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
[],
|
||||||
'uri': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '255', 'null': 'True', 'blank': 'True'})
|
{
|
||||||
|
"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': {
|
"django_mailbox.message": {
|
||||||
'Meta': {'object_name': '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']"}),
|
"attachments": (
|
||||||
'body': ('django.db.models.fields.TextField', [], {}),
|
"django.db.models.fields.related.ManyToManyField",
|
||||||
'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']"}),
|
"symmetrical": "False",
|
||||||
'mailbox': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'messages'", 'to': "orm['django_mailbox.Mailbox']"}),
|
"related_name": "'messages_old'",
|
||||||
'message_id': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
"blank": "True",
|
||||||
'outgoing': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
"to": "orm['django_mailbox.MessageAttachment']",
|
||||||
'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'}),
|
"body": ("django.db.models.fields.TextField", [], {}),
|
||||||
'to_header': ('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"]
|
||||||
|
|
|
||||||
|
|
@ -5,60 +5,146 @@ from django.db import models
|
||||||
|
|
||||||
|
|
||||||
class Migration(SchemaMigration):
|
class Migration(SchemaMigration):
|
||||||
|
|
||||||
def forwards(self, orm):
|
def forwards(self, orm):
|
||||||
# Removing M2M table for field attachments on 'Message'
|
# 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'
|
# Adding field 'MessageAttachment.headers'
|
||||||
db.add_column('django_mailbox_messageattachment', 'headers',
|
db.add_column(
|
||||||
self.gf('django.db.models.fields.TextField')(null=True, blank=True),
|
"django_mailbox_messageattachment",
|
||||||
keep_default=False)
|
"headers",
|
||||||
|
self.gf("django.db.models.fields.TextField")(null=True, blank=True),
|
||||||
|
keep_default=False,
|
||||||
|
)
|
||||||
|
|
||||||
def backwards(self, orm):
|
def backwards(self, orm):
|
||||||
# Adding M2M table for field attachments on 'Message'
|
# Adding M2M table for field attachments on 'Message'
|
||||||
db.create_table('django_mailbox_message_attachments', (
|
db.create_table(
|
||||||
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
|
"django_mailbox_message_attachments",
|
||||||
('message', models.ForeignKey(orm['django_mailbox.message'], null=False)),
|
(
|
||||||
('messageattachment', models.ForeignKey(orm['django_mailbox.messageattachment'], null=False))
|
(
|
||||||
))
|
"id",
|
||||||
db.create_unique('django_mailbox_message_attachments', ['message_id', 'messageattachment_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'
|
# Deleting field 'MessageAttachment.headers'
|
||||||
db.delete_column('django_mailbox_messageattachment', 'headers')
|
db.delete_column("django_mailbox_messageattachment", "headers")
|
||||||
|
|
||||||
|
|
||||||
models = {
|
models = {
|
||||||
'django_mailbox.mailbox': {
|
"django_mailbox.mailbox": {
|
||||||
'Meta': {'object_name': 'Mailbox'},
|
"Meta": {"object_name": "Mailbox"},
|
||||||
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
"active": ("django.db.models.fields.BooleanField", [], {"default": "True"}),
|
||||||
'from_email': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '255', 'null': 'True', 'blank': 'True'}),
|
"from_email": (
|
||||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
"django.db.models.fields.CharField",
|
||||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
[],
|
||||||
'uri': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '255', 'null': 'True', 'blank': 'True'})
|
{
|
||||||
|
"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': {
|
"django_mailbox.message": {
|
||||||
'Meta': {'object_name': 'Message'},
|
"Meta": {"object_name": "Message"},
|
||||||
'body': ('django.db.models.fields.TextField', [], {}),
|
"body": ("django.db.models.fields.TextField", [], {}),
|
||||||
'from_header': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
"from_header": (
|
||||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
"django.db.models.fields.CharField",
|
||||||
'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']"}),
|
{"max_length": "255"},
|
||||||
'message_id': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
),
|
||||||
'outgoing': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
"id": ("django.db.models.fields.AutoField", [], {"primary_key": "True"}),
|
||||||
'processed': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
|
"in_reply_to": (
|
||||||
'read': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True', 'blank': 'True'}),
|
"django.db.models.fields.related.ForeignKey",
|
||||||
'subject': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
[],
|
||||||
'to_header': ('django.db.models.fields.TextField', [], {})
|
{
|
||||||
|
"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"]
|
||||||
|
|
|
||||||
|
|
@ -5,50 +5,122 @@ from django.db import models
|
||||||
|
|
||||||
|
|
||||||
class Migration(SchemaMigration):
|
class Migration(SchemaMigration):
|
||||||
|
|
||||||
def forwards(self, orm):
|
def forwards(self, orm):
|
||||||
# Adding field 'Message.encoded'
|
# Adding field 'Message.encoded'
|
||||||
db.add_column('django_mailbox_message', 'encoded',
|
db.add_column(
|
||||||
self.gf('django.db.models.fields.BooleanField')(default=False),
|
"django_mailbox_message",
|
||||||
keep_default=False)
|
"encoded",
|
||||||
|
self.gf("django.db.models.fields.BooleanField")(default=False),
|
||||||
|
keep_default=False,
|
||||||
|
)
|
||||||
|
|
||||||
def backwards(self, orm):
|
def backwards(self, orm):
|
||||||
# Deleting field 'Message.encoded'
|
# Deleting field 'Message.encoded'
|
||||||
db.delete_column('django_mailbox_message', 'encoded')
|
db.delete_column("django_mailbox_message", "encoded")
|
||||||
|
|
||||||
|
|
||||||
models = {
|
models = {
|
||||||
'django_mailbox.mailbox': {
|
"django_mailbox.mailbox": {
|
||||||
'Meta': {'object_name': 'Mailbox'},
|
"Meta": {"object_name": "Mailbox"},
|
||||||
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
"active": ("django.db.models.fields.BooleanField", [], {"default": "True"}),
|
||||||
'from_email': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '255', 'null': 'True', 'blank': 'True'}),
|
"from_email": (
|
||||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
"django.db.models.fields.CharField",
|
||||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
[],
|
||||||
'uri': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '255', 'null': 'True', 'blank': 'True'})
|
{
|
||||||
|
"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': {
|
"django_mailbox.message": {
|
||||||
'Meta': {'object_name': 'Message'},
|
"Meta": {"object_name": "Message"},
|
||||||
'body': ('django.db.models.fields.TextField', [], {}),
|
"body": ("django.db.models.fields.TextField", [], {}),
|
||||||
'encoded': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
"encoded": (
|
||||||
'from_header': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
"django.db.models.fields.BooleanField",
|
||||||
'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']"}),
|
{"default": "False"},
|
||||||
'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'}),
|
"from_header": (
|
||||||
'outgoing': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
"django.db.models.fields.CharField",
|
||||||
'processed': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
|
[],
|
||||||
'read': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True', 'blank': 'True'}),
|
{"max_length": "255"},
|
||||||
'subject': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
),
|
||||||
'to_header': ('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"},
|
||||||
|
),
|
||||||
|
"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"]
|
||||||
|
|
|
||||||
|
|
@ -5,51 +5,129 @@ from django.db import models
|
||||||
|
|
||||||
|
|
||||||
class Migration(SchemaMigration):
|
class Migration(SchemaMigration):
|
||||||
|
|
||||||
def forwards(self, orm):
|
def forwards(self, orm):
|
||||||
# Adding field 'Message.eml'
|
# Adding field 'Message.eml'
|
||||||
db.add_column('django_mailbox_message', 'eml',
|
db.add_column(
|
||||||
self.gf('django.db.models.fields.files.FileField')(max_length=100, null=True),
|
"django_mailbox_message",
|
||||||
keep_default=False)
|
"eml",
|
||||||
|
self.gf("django.db.models.fields.files.FileField")(
|
||||||
|
max_length=100, null=True
|
||||||
|
),
|
||||||
|
keep_default=False,
|
||||||
|
)
|
||||||
|
|
||||||
def backwards(self, orm):
|
def backwards(self, orm):
|
||||||
# Deleting field 'Message.eml'
|
# Deleting field 'Message.eml'
|
||||||
db.delete_column('django_mailbox_message', 'eml')
|
db.delete_column("django_mailbox_message", "eml")
|
||||||
|
|
||||||
|
|
||||||
models = {
|
models = {
|
||||||
'django_mailbox.mailbox': {
|
"django_mailbox.mailbox": {
|
||||||
'Meta': {'object_name': 'Mailbox'},
|
"Meta": {"object_name": "Mailbox"},
|
||||||
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
"active": ("django.db.models.fields.BooleanField", [], {"default": "True"}),
|
||||||
'from_email': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '255', 'null': 'True', 'blank': 'True'}),
|
"from_email": (
|
||||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
"django.db.models.fields.CharField",
|
||||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
[],
|
||||||
'uri': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '255', 'null': 'True', 'blank': 'True'})
|
{
|
||||||
|
"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': {
|
"django_mailbox.message": {
|
||||||
'Meta': {'object_name': 'Message'},
|
"Meta": {"object_name": "Message"},
|
||||||
'body': ('django.db.models.fields.TextField', [], {}),
|
"body": ("django.db.models.fields.TextField", [], {}),
|
||||||
'eml': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'null': 'True'}),
|
"eml": (
|
||||||
'encoded': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
"django.db.models.fields.files.FileField",
|
||||||
'from_header': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
[],
|
||||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
{"max_length": "100", "null": "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']"}),
|
"encoded": (
|
||||||
'message_id': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
"django.db.models.fields.BooleanField",
|
||||||
'outgoing': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
[],
|
||||||
'processed': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
|
{"default": "False"},
|
||||||
'read': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True', 'blank': 'True'}),
|
),
|
||||||
'subject': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
"from_header": (
|
||||||
'to_header': ('django.db.models.fields.TextField', [], {})
|
"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"]
|
||||||
|
|
|
||||||
|
|
@ -17,39 +17,37 @@ def get_email_as_text(name):
|
||||||
with open(
|
with open(
|
||||||
os.path.join(
|
os.path.join(
|
||||||
os.path.dirname(__file__),
|
os.path.dirname(__file__),
|
||||||
'messages',
|
"messages",
|
||||||
name,
|
name,
|
||||||
),
|
),
|
||||||
'rb'
|
"rb",
|
||||||
) as f:
|
) as f:
|
||||||
return f.read()
|
return f.read()
|
||||||
|
|
||||||
|
|
||||||
class EmailMessageTestCase(TestCase):
|
class EmailMessageTestCase(TestCase):
|
||||||
ALLOWED_EXTRA_HEADERS = [
|
ALLOWED_EXTRA_HEADERS = [
|
||||||
'MIME-Version',
|
"MIME-Version",
|
||||||
'Content-Transfer-Encoding',
|
"Content-Transfer-Encoding",
|
||||||
]
|
]
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
dm_settings = utils.get_settings()
|
dm_settings = utils.get_settings()
|
||||||
|
|
||||||
self._ALLOWED_MIMETYPES = dm_settings['allowed_mimetypes']
|
self._ALLOWED_MIMETYPES = dm_settings["allowed_mimetypes"]
|
||||||
self._STRIP_UNALLOWED_MIMETYPES = (
|
self._STRIP_UNALLOWED_MIMETYPES = dm_settings["strip_unallowed_mimetypes"]
|
||||||
dm_settings['strip_unallowed_mimetypes']
|
self._TEXT_STORED_MIMETYPES = dm_settings["text_stored_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_account = os.environ.get("EMAIL_ACCOUNT")
|
||||||
self.test_password = os.environ.get('EMAIL_PASSWORD')
|
self.test_password = os.environ.get("EMAIL_PASSWORD")
|
||||||
self.test_smtp_server = os.environ.get('EMAIL_SMTP_SERVER')
|
self.test_smtp_server = os.environ.get("EMAIL_SMTP_SERVER")
|
||||||
self.test_from_email = 'nobody@nowhere.com'
|
self.test_from_email = "nobody@nowhere.com"
|
||||||
|
|
||||||
self.maximum_wait_seconds = 60 * 5
|
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_HOST = self.test_smtp_server
|
||||||
settings.EMAIL_PORT = 587
|
settings.EMAIL_PORT = 587
|
||||||
settings.EMAIL_HOST_USER = self.test_account
|
settings.EMAIL_HOST_USER = self.test_account
|
||||||
|
|
@ -80,10 +78,10 @@ class EmailMessageTestCase(TestCase):
|
||||||
with open(
|
with open(
|
||||||
os.path.join(
|
os.path.join(
|
||||||
os.path.dirname(__file__),
|
os.path.dirname(__file__),
|
||||||
'messages',
|
"messages",
|
||||||
name,
|
name,
|
||||||
),
|
),
|
||||||
'rb'
|
"rb",
|
||||||
) as f:
|
) as f:
|
||||||
return f.read()
|
return f.read()
|
||||||
|
|
||||||
|
|
@ -92,26 +90,26 @@ class EmailMessageTestCase(TestCase):
|
||||||
return email.message_from_bytes(copy)
|
return email.message_from_bytes(copy)
|
||||||
|
|
||||||
def _headers_identical(self, left, right, header=None):
|
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
|
* This is particularly tricky because Python 2.6, Python 2.7 and
|
||||||
Python 3 each handle header strings slightly differently. This
|
Python 3 each handle header strings slightly differently. This
|
||||||
should mash away all of the differences, though.
|
should mash away all of the differences, though.
|
||||||
* This also has a small loophole in that when re-writing e-mail
|
* 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
|
payload encodings, we re-build the Content-Type header, so if the
|
||||||
header was originally unquoted, it will be quoted when rehydrating
|
header was originally unquoted, it will be quoted when rehydrating
|
||||||
the e-mail message.
|
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
|
# 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
|
# the new content type; we need to make sure that doesn't cause
|
||||||
# this comparison to fail. Also, the case of the encoding could
|
# this comparison to fail. Also, the case of the encoding could
|
||||||
# be changed, etc. etc. etc.
|
# be changed, etc. etc. etc.
|
||||||
left = left.replace('"', '').upper()
|
left = left.replace('"', "").upper()
|
||||||
right = right.replace('"', '').upper()
|
right = right.replace('"', "").upper()
|
||||||
left = left.replace('\n\t', ' ').replace('\n ', ' ')
|
left = left.replace("\n\t", " ").replace("\n ", " ")
|
||||||
right = right.replace('\n\t', ' ').replace('\n ', ' ')
|
right = right.replace("\n\t", " ").replace("\n ", " ")
|
||||||
if right != left:
|
if right != left:
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
@ -152,10 +150,7 @@ class EmailMessageTestCase(TestCase):
|
||||||
if len(left_payloads) != len(right_payloads):
|
if len(left_payloads) != len(right_payloads):
|
||||||
self._raise_mismatched(left, right)
|
self._raise_mismatched(left, right)
|
||||||
for n in range(len(left_payloads)):
|
for n in range(len(left_payloads)):
|
||||||
self.compare_email_objects(
|
self.compare_email_objects(left_payloads[n], right_payloads[n])
|
||||||
left_payloads[n],
|
|
||||||
right_payloads[n]
|
|
||||||
)
|
|
||||||
else:
|
else:
|
||||||
if left.get_payload() is None or right.get_payload() is None:
|
if left.get_payload() is None or right.get_payload() is None:
|
||||||
if left.get_payload() is None:
|
if left.get_payload() is None:
|
||||||
|
|
@ -170,8 +165,7 @@ class EmailMessageTestCase(TestCase):
|
||||||
def _raise_mismatched(self, left, right):
|
def _raise_mismatched(self, left, right):
|
||||||
raise AssertionError(
|
raise AssertionError(
|
||||||
"Message payloads do not match:\n{}\n{}".format(
|
"Message payloads do not match:\n{}\n{}".format(
|
||||||
left.as_string(),
|
left.as_string(), right.as_string()
|
||||||
right.as_string()
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,12 @@
|
||||||
DATABASES = {
|
DATABASES = {
|
||||||
'default': {
|
"default": {
|
||||||
'NAME': 'db.sqlite3',
|
"NAME": "db.sqlite3",
|
||||||
'ENGINE': 'django.db.backends.sqlite3',
|
"ENGINE": "django.db.backends.sqlite3",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
INSTALLED_APPS = [
|
INSTALLED_APPS = [
|
||||||
'django.contrib.auth',
|
"django.contrib.auth",
|
||||||
'django.contrib.contenttypes',
|
"django.contrib.contenttypes",
|
||||||
'django_mailbox',
|
"django_mailbox",
|
||||||
]
|
]
|
||||||
SECRET_KEY = 'beepboop'
|
SECRET_KEY = "beepboop"
|
||||||
|
|
|
||||||
|
|
@ -8,16 +8,14 @@ from django_mailbox.models import Mailbox
|
||||||
from django_mailbox.tests.base import EmailMessageTestCase
|
from django_mailbox.tests.base import EmailMessageTestCase
|
||||||
|
|
||||||
|
|
||||||
__all__ = ['TestImap']
|
__all__ = ["TestImap"]
|
||||||
|
|
||||||
|
|
||||||
class TestImap(EmailMessageTestCase):
|
class TestImap(EmailMessageTestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super().setUp()
|
super().setUp()
|
||||||
|
|
||||||
self.test_imap_server = (
|
self.test_imap_server = os.environ.get("EMAIL_IMAP_SERVER")
|
||||||
os.environ.get('EMAIL_IMAP_SERVER')
|
|
||||||
)
|
|
||||||
|
|
||||||
required_settings = [
|
required_settings = [
|
||||||
self.test_imap_server,
|
self.test_imap_server,
|
||||||
|
|
@ -35,8 +33,7 @@ class TestImap(EmailMessageTestCase):
|
||||||
)
|
)
|
||||||
|
|
||||||
self.mailbox = Mailbox.objects.create(
|
self.mailbox = Mailbox.objects.create(
|
||||||
name='Integration Test Imap',
|
name="Integration Test Imap", uri=self.get_connection_string()
|
||||||
uri=self.get_connection_string()
|
|
||||||
)
|
)
|
||||||
self.arbitrary_identifier = str(uuid.uuid4())
|
self.arbitrary_identifier = str(uuid.uuid4())
|
||||||
|
|
||||||
|
|
@ -48,20 +45,19 @@ class TestImap(EmailMessageTestCase):
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_get_imap_message(self):
|
def test_get_imap_message(self):
|
||||||
text_content = 'This is some content'
|
text_content = "This is some content"
|
||||||
msg = EmailMultiAlternatives(
|
msg = EmailMultiAlternatives(
|
||||||
self.arbitrary_identifier,
|
self.arbitrary_identifier,
|
||||||
text_content,
|
text_content,
|
||||||
self.test_from_email,
|
self.test_from_email,
|
||||||
[
|
[
|
||||||
self.test_account,
|
self.test_account,
|
||||||
]
|
],
|
||||||
)
|
)
|
||||||
msg.send()
|
msg.send()
|
||||||
|
|
||||||
messages = self._get_new_messages(
|
messages = self._get_new_messages(
|
||||||
self.mailbox,
|
self.mailbox, condition=lambda m: m["subject"] == self.arbitrary_identifier
|
||||||
condition=lambda m: m['subject'] == self.arbitrary_identifier
|
|
||||||
)
|
)
|
||||||
message = next(messages)
|
message = next(messages)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,15 +5,15 @@ from django.test import TestCase
|
||||||
from django_mailbox.models import Mailbox
|
from django_mailbox.models import Mailbox
|
||||||
|
|
||||||
|
|
||||||
__all__ = ['TestMailbox']
|
__all__ = ["TestMailbox"]
|
||||||
|
|
||||||
|
|
||||||
class TestMailbox(TestCase):
|
class TestMailbox(TestCase):
|
||||||
def test_protocol_info(self):
|
def test_protocol_info(self):
|
||||||
mailbox = Mailbox()
|
mailbox = Mailbox()
|
||||||
mailbox.uri = 'alpha://test.com'
|
mailbox.uri = "alpha://test.com"
|
||||||
|
|
||||||
expected_protocol = 'alpha'
|
expected_protocol = "alpha"
|
||||||
actual_protocol = mailbox._protocol_info.scheme
|
actual_protocol = mailbox._protocol_info.scheme
|
||||||
|
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
|
|
@ -23,24 +23,30 @@ class TestMailbox(TestCase):
|
||||||
|
|
||||||
def test_last_polling_field_exists(self):
|
def test_last_polling_field_exists(self):
|
||||||
mailbox = Mailbox()
|
mailbox = Mailbox()
|
||||||
self.assertTrue(hasattr(mailbox, 'last_polling'))
|
self.assertTrue(hasattr(mailbox, "last_polling"))
|
||||||
|
|
||||||
def test_get_new_mail_update_last_polling(self):
|
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__),
|
os.path.dirname(__file__),
|
||||||
'messages',
|
"messages",
|
||||||
'generic_message.eml',
|
"generic_message.eml",
|
||||||
))
|
)
|
||||||
|
)
|
||||||
self.assertEqual(mailbox.last_polling, None)
|
self.assertEqual(mailbox.last_polling, None)
|
||||||
list(mailbox.get_new_mail())
|
list(mailbox.get_new_mail())
|
||||||
self.assertNotEqual(mailbox.last_polling, None)
|
self.assertNotEqual(mailbox.last_polling, None)
|
||||||
|
|
||||||
def test_queryset_get_new_mail(self):
|
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__),
|
os.path.dirname(__file__),
|
||||||
'messages',
|
"messages",
|
||||||
'generic_message.eml',
|
"generic_message.eml",
|
||||||
))
|
)
|
||||||
|
)
|
||||||
Mailbox.objects.filter(pk=mailbox.pk).get_new_mail()
|
Mailbox.objects.filter(pk=mailbox.pk).get_new_mail()
|
||||||
mailbox.refresh_from_db()
|
mailbox.refresh_from_db()
|
||||||
self.assertNotEqual(mailbox.last_polling, None)
|
self.assertNotEqual(mailbox.last_polling, None)
|
||||||
|
|
|
||||||
|
|
@ -7,20 +7,20 @@ from django_mailbox.models import Message
|
||||||
from django_mailbox.tests.base import EmailMessageTestCase
|
from django_mailbox.tests.base import EmailMessageTestCase
|
||||||
|
|
||||||
|
|
||||||
__all__ = ['TestMessageFlattening']
|
__all__ = ["TestMessageFlattening"]
|
||||||
|
|
||||||
|
|
||||||
class TestMessageFlattening(EmailMessageTestCase):
|
class TestMessageFlattening(EmailMessageTestCase):
|
||||||
def test_quopri_message_is_properly_rehydrated(self):
|
def test_quopri_message_is_properly_rehydrated(self):
|
||||||
incoming_email_object = self._get_email_object(
|
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
|
# Note: this is identical to the above, but it appears that
|
||||||
# while reading-in an e-mail message, we do alter it slightly
|
# while reading-in an e-mail message, we do alter it slightly
|
||||||
expected_email_object = self._get_email_object(
|
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)
|
msg = self.mailbox.process_incoming_message(incoming_email_object)
|
||||||
|
|
||||||
|
|
@ -33,12 +33,12 @@ class TestMessageFlattening(EmailMessageTestCase):
|
||||||
|
|
||||||
def test_base64_message_is_properly_rehydrated(self):
|
def test_base64_message_is_properly_rehydrated(self):
|
||||||
incoming_email_object = self._get_email_object(
|
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
|
# Note: this is identical to the above, but it appears that
|
||||||
# while reading-in an e-mail message, we do alter it slightly
|
# while reading-in an e-mail message, we do alter it slightly
|
||||||
expected_email_object = self._get_email_object(
|
expected_email_object = self._get_email_object(
|
||||||
'message_with_attachment.eml',
|
"message_with_attachment.eml",
|
||||||
)
|
)
|
||||||
|
|
||||||
msg = self.mailbox.process_incoming_message(incoming_email_object)
|
msg = self.mailbox.process_incoming_message(incoming_email_object)
|
||||||
|
|
@ -52,10 +52,10 @@ class TestMessageFlattening(EmailMessageTestCase):
|
||||||
|
|
||||||
def test_message_handles_rehydration_problems(self):
|
def test_message_handles_rehydration_problems(self):
|
||||||
incoming_email_object = self._get_email_object(
|
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(
|
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
|
# Note: this is identical to the above, but it appears that
|
||||||
# while reading-in an e-mail message, we do alter it slightly
|
# 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):
|
def test_message_content_type_stripping(self):
|
||||||
incoming_email_object = self._get_email_object(
|
incoming_email_object = self._get_email_object(
|
||||||
'message_with_many_multiparts.eml',
|
"message_with_many_multiparts.eml",
|
||||||
)
|
)
|
||||||
expected_email_object = self._get_email_object(
|
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()
|
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 = copy.deepcopy(default_settings)
|
||||||
altered['strip_unallowed_mimetypes'] = True
|
altered["strip_unallowed_mimetypes"] = True
|
||||||
altered['allowed_mimetypes'] = ['text/plain']
|
altered["allowed_mimetypes"] = ["text/plain"]
|
||||||
|
|
||||||
get_settings.return_value = altered
|
get_settings.return_value = altered
|
||||||
|
|
||||||
|
|
@ -100,7 +100,7 @@ class TestMessageFlattening(EmailMessageTestCase):
|
||||||
|
|
||||||
def test_message_processing_unknown_encoding(self):
|
def test_message_processing_unknown_encoding(self):
|
||||||
incoming_email_object = self._get_email_object(
|
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)
|
msg = self.mailbox.process_incoming_message(incoming_email_object)
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import gzip
|
import gzip
|
||||||
import os.path
|
import os.path
|
||||||
import sys
|
|
||||||
|
# import sys
|
||||||
|
|
||||||
import copy
|
import copy
|
||||||
from unittest import mock
|
from unittest import mock
|
||||||
|
|
@ -12,64 +13,56 @@ from django_mailbox.tests.base import EmailMessageTestCase
|
||||||
from django.utils.encoding import force_text
|
from django.utils.encoding import force_text
|
||||||
from django.core.mail import EmailMessage
|
from django.core.mail import EmailMessage
|
||||||
|
|
||||||
__all__ = ['TestProcessEmail']
|
__all__ = ["TestProcessEmail"]
|
||||||
|
|
||||||
|
|
||||||
class TestProcessEmail(EmailMessageTestCase):
|
class TestProcessEmail(EmailMessageTestCase):
|
||||||
def test_message_without_attachments(self):
|
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()
|
mailbox = Mailbox.objects.create()
|
||||||
msg = mailbox.process_incoming_message(message)
|
msg = mailbox.process_incoming_message(message)
|
||||||
|
|
||||||
self.assertEqual(
|
self.assertEqual(msg.mailbox, mailbox)
|
||||||
msg.mailbox,
|
self.assertEqual(msg.subject, "Message Without Attachment")
|
||||||
mailbox
|
|
||||||
)
|
|
||||||
self.assertEqual(msg.subject, 'Message Without Attachment')
|
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
msg.message_id,
|
msg.message_id,
|
||||||
(
|
("<CAMdmm+hGH8Dgn-_0xnXJCd=PhyNAiouOYm5zFP0z" "-foqTO60zA@mail.gmail.com>"),
|
||||||
'<CAMdmm+hGH8Dgn-_0xnXJCd=PhyNAiouOYm5zFP0z'
|
|
||||||
'-foqTO60zA@mail.gmail.com>'
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
msg.from_header,
|
msg.from_header,
|
||||||
'Adam Coddington <test@adamcoddington.net>',
|
"Adam Coddington <test@adamcoddington.net>",
|
||||||
)
|
)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
msg.to_header,
|
msg.to_header,
|
||||||
'Adam Coddington <test@adamcoddington.net>',
|
"Adam Coddington <test@adamcoddington.net>",
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_message_with_encoded_attachment_filenames(self):
|
def test_message_with_encoded_attachment_filenames(self):
|
||||||
message = self._get_email_object(
|
message = self._get_email_object("message_with_koi8r_filename_attachments.eml")
|
||||||
'message_with_koi8r_filename_attachments.eml'
|
|
||||||
)
|
|
||||||
|
|
||||||
mailbox = Mailbox.objects.create()
|
mailbox = Mailbox.objects.create()
|
||||||
msg = mailbox.process_incoming_message(message)
|
msg = mailbox.process_incoming_message(message)
|
||||||
|
|
||||||
attachments = msg.attachments.order_by('pk').all()
|
attachments = msg.attachments.order_by("pk").all()
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
'\u041f\u0430\u043a\u0435\u0442 \u043f\u0440\u0435\u0434\u043b'
|
"\u041f\u0430\u043a\u0435\u0442 \u043f\u0440\u0435\u0434\u043b"
|
||||||
'\u043e\u0436\u0435\u043d\u0438\u0439 HSE Career Fair 8 \u0430'
|
"\u043e\u0436\u0435\u043d\u0438\u0439 HSE Career Fair 8 \u0430"
|
||||||
'\u043f\u0440\u0435\u043b\u044f 2016.pdf',
|
"\u043f\u0440\u0435\u043b\u044f 2016.pdf",
|
||||||
attachments[0].get_filename()
|
attachments[0].get_filename(),
|
||||||
)
|
)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
'\u0412\u0435\u0434\u043e\u043c\u043e\u0441\u0442\u0438.pdf',
|
"\u0412\u0435\u0434\u043e\u043c\u043e\u0441\u0442\u0438.pdf",
|
||||||
attachments[1].get_filename()
|
attachments[1].get_filename(),
|
||||||
)
|
)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
'\u041f\u0430\u043a\u0435\u0442 \u043f\u0440\u0435\u0434\u043b'
|
"\u041f\u0430\u043a\u0435\u0442 \u043f\u0440\u0435\u0434\u043b"
|
||||||
'\u043e\u0436\u0435\u043d\u0438\u0439 2016.pptx',
|
"\u043e\u0436\u0435\u043d\u0438\u0439 2016.pptx",
|
||||||
attachments[2].get_filename()
|
attachments[2].get_filename(),
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_message_with_attachments(self):
|
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()
|
mailbox = Mailbox.objects.create()
|
||||||
msg = mailbox.process_incoming_message(message)
|
msg = mailbox.process_incoming_message(message)
|
||||||
|
|
@ -85,16 +78,16 @@ class TestProcessEmail(EmailMessageTestCase):
|
||||||
attachment = msg.attachments.all()[0]
|
attachment = msg.attachments.all()[0]
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
attachment.get_filename(),
|
attachment.get_filename(),
|
||||||
'heart.png',
|
"heart.png",
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_message_with_utf8_attachment_header(self):
|
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
|
Safe for regress of #104 too
|
||||||
"""
|
"""
|
||||||
email_object = self._get_email_object(
|
email_object = self._get_email_object(
|
||||||
'message_with_utf8_attachment.eml',
|
"message_with_utf8_attachment.eml",
|
||||||
)
|
)
|
||||||
mailbox = Mailbox.objects.create()
|
mailbox = Mailbox.objects.create()
|
||||||
msg = mailbox.process_incoming_message(email_object)
|
msg = mailbox.process_incoming_message(email_object)
|
||||||
|
|
@ -108,24 +101,18 @@ class TestProcessEmail(EmailMessageTestCase):
|
||||||
)
|
)
|
||||||
|
|
||||||
attachment = msg.attachments.all()[0]
|
attachment = msg.attachments.all()[0]
|
||||||
self.assertEqual(
|
self.assertEqual(attachment.get_filename(), "pi\u0142kochwyty.jpg")
|
||||||
attachment.get_filename(),
|
|
||||||
'pi\u0142kochwyty.jpg'
|
|
||||||
)
|
|
||||||
|
|
||||||
attachment = msg.attachments.all()[1]
|
attachment = msg.attachments.all()[1]
|
||||||
self.assertEqual(
|
self.assertEqual(attachment.get_filename(), "odpowied\u017a Burmistrza.jpg")
|
||||||
attachment.get_filename(),
|
|
||||||
'odpowied\u017a Burmistrza.jpg'
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_message_get_text_body(self):
|
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()
|
mailbox = Mailbox.objects.create()
|
||||||
msg = mailbox.process_incoming_message(message)
|
msg = mailbox.process_incoming_message(message)
|
||||||
|
|
||||||
expected_results = 'Hello there!'
|
expected_results = "Hello there!"
|
||||||
actual_results = msg.text.strip()
|
actual_results = msg.text.strip()
|
||||||
|
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
|
|
@ -135,29 +122,21 @@ class TestProcessEmail(EmailMessageTestCase):
|
||||||
|
|
||||||
def test_get_text_body_properly_recomposes_line_continuations(self):
|
def test_get_text_body_properly_recomposes_line_continuations(self):
|
||||||
message = Message()
|
message = Message()
|
||||||
email_object = self._get_email_object(
|
email_object = self._get_email_object("message_with_long_text_lines.eml")
|
||||||
'message_with_long_text_lines.eml'
|
|
||||||
)
|
|
||||||
|
|
||||||
message.get_email_object = lambda: email_object
|
message.get_email_object = lambda: email_object
|
||||||
|
|
||||||
actual_text = message.text
|
actual_text = message.text
|
||||||
expected_text = (
|
expected_text = (
|
||||||
'The one of us with a bike pump is far ahead, '
|
"The one of us with a bike pump is far ahead, "
|
||||||
'but a man stopped to help us and gave us his pump.'
|
"but a man stopped to help us and gave us his pump."
|
||||||
)
|
)
|
||||||
|
|
||||||
self.assertEqual(
|
self.assertEqual(actual_text, expected_text)
|
||||||
actual_text,
|
|
||||||
expected_text
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_get_body_properly_handles_unicode_body(self):
|
def test_get_body_properly_handles_unicode_body(self):
|
||||||
with open(
|
with open(
|
||||||
os.path.join(
|
os.path.join(os.path.dirname(__file__), "messages/generic_message.eml")
|
||||||
os.path.dirname(__file__),
|
|
||||||
'messages/generic_message.eml'
|
|
||||||
)
|
|
||||||
) as f:
|
) as f:
|
||||||
unicode_body = f.read()
|
unicode_body = f.read()
|
||||||
|
|
||||||
|
|
@ -167,23 +146,18 @@ class TestProcessEmail(EmailMessageTestCase):
|
||||||
expected_body = unicode_body
|
expected_body = unicode_body
|
||||||
actual_body = message.get_email_object().as_string()
|
actual_body = message.get_email_object().as_string()
|
||||||
|
|
||||||
self.assertEqual(
|
self.assertEqual(expected_body, actual_body)
|
||||||
expected_body,
|
|
||||||
actual_body
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_message_issue_82(self):
|
def test_message_issue_82(self):
|
||||||
""" Ensure that we properly handle incorrectly encoded messages
|
"""Ensure that we properly handle incorrectly encoded messages"""
|
||||||
|
email_object = self._get_email_object("email_issue_82.eml")
|
||||||
"""
|
it = "works"
|
||||||
email_object = self._get_email_object('email_issue_82.eml')
|
|
||||||
it = 'works'
|
|
||||||
try:
|
try:
|
||||||
# it's ok to call as_string() before passing email_object
|
# it's ok to call as_string() before passing email_object
|
||||||
# to _get_dehydrated_message()
|
# to _get_dehydrated_message()
|
||||||
email_object.as_string()
|
email_object.as_string()
|
||||||
except:
|
except Exception:
|
||||||
it = 'do not works'
|
it = "do not works"
|
||||||
|
|
||||||
success = True
|
success = True
|
||||||
try:
|
try:
|
||||||
|
|
@ -191,15 +165,15 @@ class TestProcessEmail(EmailMessageTestCase):
|
||||||
except ValueError:
|
except ValueError:
|
||||||
success = False
|
success = False
|
||||||
|
|
||||||
self.assertEqual(it, 'works')
|
self.assertEqual(it, "works")
|
||||||
self.assertEqual(True, success)
|
self.assertEqual(True, success)
|
||||||
|
|
||||||
def test_message_issue_82_bis(self):
|
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()
|
calling _get_dehydrated_message()
|
||||||
|
|
||||||
"""
|
"""
|
||||||
message = self._get_email_object('email_issue_82.eml')
|
message = self._get_email_object("email_issue_82.eml")
|
||||||
|
|
||||||
success = True
|
success = True
|
||||||
|
|
||||||
|
|
@ -209,16 +183,16 @@ class TestProcessEmail(EmailMessageTestCase):
|
||||||
# msg.eml.save('%s.eml' % uuid.uuid4(), ContentFile(message),
|
# msg.eml.save('%s.eml' % uuid.uuid4(), ContentFile(message),
|
||||||
# save=False)
|
# save=False)
|
||||||
msg.mailbox = self.mailbox
|
msg.mailbox = self.mailbox
|
||||||
if 'subject' in message:
|
if "subject" in message:
|
||||||
msg.subject = convert_header_to_unicode(message['subject'])[0:255]
|
msg.subject = convert_header_to_unicode(message["subject"])[0:255]
|
||||||
if 'message-id' in message:
|
if "message-id" in message:
|
||||||
msg.message_id = message['message-id'][0:255]
|
msg.message_id = message["message-id"][0:255]
|
||||||
if 'from' in message:
|
if "from" in message:
|
||||||
msg.from_header = convert_header_to_unicode(message['from'])
|
msg.from_header = convert_header_to_unicode(message["from"])
|
||||||
if 'to' in message:
|
if "to" in message:
|
||||||
msg.to_header = convert_header_to_unicode(message['to'])
|
msg.to_header = convert_header_to_unicode(message["to"])
|
||||||
elif 'Delivered-To' in message:
|
elif "Delivered-To" in message:
|
||||||
msg.to_header = convert_header_to_unicode(message['Delivered-To'])
|
msg.to_header = convert_header_to_unicode(message["Delivered-To"])
|
||||||
msg.save()
|
msg.save()
|
||||||
|
|
||||||
# here the message is ok
|
# here the message is ok
|
||||||
|
|
@ -227,14 +201,14 @@ class TestProcessEmail(EmailMessageTestCase):
|
||||||
try:
|
try:
|
||||||
# here as_string raises UnicodeEncodeError
|
# here as_string raises UnicodeEncodeError
|
||||||
str_msg = message.as_string()
|
str_msg = message.as_string()
|
||||||
except:
|
except Exception:
|
||||||
success = False
|
success = False
|
||||||
|
|
||||||
msg.set_body(str_msg)
|
msg.set_body(str_msg)
|
||||||
if message['in-reply-to']:
|
if message["in-reply-to"]:
|
||||||
try:
|
try:
|
||||||
msg.in_reply_to = Message.objects.filter(
|
msg.in_reply_to = Message.objects.filter(
|
||||||
message_id=message['in-reply-to']
|
message_id=message["in-reply-to"]
|
||||||
)[0]
|
)[0]
|
||||||
except IndexError:
|
except IndexError:
|
||||||
pass
|
pass
|
||||||
|
|
@ -243,18 +217,20 @@ class TestProcessEmail(EmailMessageTestCase):
|
||||||
self.assertEqual(True, success)
|
self.assertEqual(True, success)
|
||||||
|
|
||||||
def test_message_with_misplaced_utf8_content(self):
|
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
|
``message_with_utf8_char.eml``'s primary text payload is marked
|
||||||
as being iso-8859-1 data, but actually contains UTF-8 bytes.
|
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)
|
msg = self.mailbox.process_incoming_message(email_object)
|
||||||
|
|
||||||
expected_text = 'This message contains funny UTF16 characters ' + \
|
expected_text = (
|
||||||
'like this one: "\xc2\xa0" and this one "\xe2\x9c\xbf".'
|
"This message contains funny UTF16 characters "
|
||||||
|
+ 'like this one: "\xc2\xa0" and this one "\xe2\x9c\xbf".'
|
||||||
|
)
|
||||||
actual_text = msg.text
|
actual_text = msg.text
|
||||||
|
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
|
|
@ -263,7 +239,7 @@ class TestProcessEmail(EmailMessageTestCase):
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_message_with_invalid_content_for_declared_encoding(self):
|
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:
|
Should a payload body be misencoded, we should:
|
||||||
|
|
||||||
|
|
@ -275,7 +251,7 @@ class TestProcessEmail(EmailMessageTestCase):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
email_object = self._get_email_object(
|
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)
|
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):
|
def test_message_with_valid_content_in_single_byte_encoding(self):
|
||||||
email_object = self._get_email_object(
|
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)
|
msg = self.mailbox.process_incoming_message(email_object)
|
||||||
|
|
||||||
actual_text = msg.text
|
actual_text = msg.text
|
||||||
expected_body = '\u042d\u0442\u043e ' + \
|
expected_body = (
|
||||||
'\u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435 ' + \
|
"\u042d\u0442\u043e "
|
||||||
'\u0438\u043c\u0435\u0435\u0442 ' + \
|
+ "\u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435 "
|
||||||
'\u043d\u0435\u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d' + \
|
+ "\u0438\u043c\u0435\u0435\u0442 "
|
||||||
'\u0443\u044e ' + \
|
+ "\u043d\u0435\u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d"
|
||||||
'\u043a\u043e\u0434\u0438\u0440\u043e\u0432\u043a\u0430.'
|
+ "\u0443\u044e "
|
||||||
|
+ "\u043a\u043e\u0434\u0438\u0440\u043e\u0432\u043a\u0430."
|
||||||
|
)
|
||||||
|
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
actual_text,
|
actual_text,
|
||||||
|
|
@ -304,42 +282,44 @@ class TestProcessEmail(EmailMessageTestCase):
|
||||||
|
|
||||||
def test_message_with_single_byte_subject_encoding(self):
|
def test_message_with_single_byte_subject_encoding(self):
|
||||||
email_object = self._get_email_object(
|
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)
|
msg = self.mailbox.process_incoming_message(email_object)
|
||||||
|
|
||||||
expected_subject = '\u00D3\u00E7\u00ED\u00E0\u00E9 \u00EA\u00E0\u00EA ' + \
|
expected_subject = (
|
||||||
'\u00E7\u00E0\u00F0\u00E0\u00E1\u00E0\u00F2\u00FB\u00E2' + \
|
"\u00D3\u00E7\u00ED\u00E0\u00E9 \u00EA\u00E0\u00EA "
|
||||||
'\u00E0\u00F2\u00FC \u00EE\u00F2 1000$ \u00E2 ' + \
|
+ "\u00E7\u00E0\u00F0\u00E0\u00E1\u00E0\u00F2\u00FB\u00E2"
|
||||||
'\u00ED\u00E5\u00E4\u00E5\u00EB\u00FE!'
|
+ "\u00E0\u00F2\u00FC \u00EE\u00F2 1000$ \u00E2 "
|
||||||
|
+ "\u00ED\u00E5\u00E4\u00E5\u00EB\u00FE!"
|
||||||
|
)
|
||||||
actual_subject = msg.subject
|
actual_subject = msg.subject
|
||||||
self.assertEqual(actual_subject, expected_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
|
actual_from = msg.from_header
|
||||||
self.assertEqual(expected_from, actual_from)
|
self.assertEqual(expected_from, actual_from)
|
||||||
|
|
||||||
def test_message_reply(self):
|
def test_message_reply(self):
|
||||||
email_object = EmailMessage(
|
email_object = EmailMessage(
|
||||||
'Test subject', # subject
|
"Test subject", # subject
|
||||||
'Test body', # body
|
"Test body", # body
|
||||||
'username@example.com', # from
|
"username@example.com", # from
|
||||||
['mr.test32@mail.ru'], # to
|
["mr.test32@mail.ru"], # to
|
||||||
)
|
)
|
||||||
msg = self.mailbox.record_outgoing_message(email_object.message())
|
msg = self.mailbox.record_outgoing_message(email_object.message())
|
||||||
|
|
||||||
self.assertTrue(msg.outgoing)
|
self.assertTrue(msg.outgoing)
|
||||||
|
|
||||||
actual_from = 'username@example.com'
|
actual_from = "username@example.com"
|
||||||
reply_email_object = EmailMessage(
|
reply_email_object = EmailMessage(
|
||||||
'Test subject', # subject
|
"Test subject", # subject
|
||||||
'Test body', # body
|
"Test body", # body
|
||||||
actual_from, # from
|
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)
|
reply_msg = msg.reply(reply_email_object)
|
||||||
|
|
||||||
self.assertEqual(reply_msg.in_reply_to, msg)
|
self.assertEqual(reply_msg.in_reply_to, msg)
|
||||||
|
|
@ -348,62 +328,58 @@ class TestProcessEmail(EmailMessageTestCase):
|
||||||
|
|
||||||
reply_email_object.from_email = None
|
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)
|
second_reply_msg = msg.reply(reply_email_object)
|
||||||
|
|
||||||
self.assertEqual(self.mailbox.from_email, second_reply_msg.from_header)
|
self.assertEqual(self.mailbox.from_email, second_reply_msg.from_header)
|
||||||
|
|
||||||
def test_message_with_text_attachment(self):
|
def test_message_with_text_attachment(self):
|
||||||
email_object = self._get_email_object(
|
email_object = self._get_email_object(
|
||||||
'message_with_text_attachment.eml',
|
"message_with_text_attachment.eml",
|
||||||
)
|
)
|
||||||
|
|
||||||
msg = self.mailbox.process_incoming_message(email_object)
|
msg = self.mailbox.process_incoming_message(email_object)
|
||||||
|
|
||||||
self.assertEqual(msg.attachments.all().count(), 1)
|
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):
|
def test_message_with_long_content(self):
|
||||||
email_object = self._get_email_object(
|
email_object = self._get_email_object(
|
||||||
'message_with_long_content.eml',
|
"message_with_long_content.eml",
|
||||||
)
|
)
|
||||||
size = len(force_text(email_object.as_string()))
|
size = len(force_text(email_object.as_string()))
|
||||||
|
|
||||||
msg = self.mailbox.process_incoming_message(email_object)
|
msg = self.mailbox.process_incoming_message(email_object)
|
||||||
|
|
||||||
self.assertEqual(size,
|
self.assertEqual(size, len(force_text(msg.get_email_object().as_string())))
|
||||||
len(force_text(msg.get_email_object().as_string())))
|
|
||||||
|
|
||||||
def test_message_saved(self):
|
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()
|
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 = copy.deepcopy(default_settings)
|
||||||
altered['store_original_message'] = True
|
altered["store_original_message"] = True
|
||||||
get_settings.return_value = altered
|
get_settings.return_value = altered
|
||||||
|
|
||||||
msg = self.mailbox.process_incoming_message(message)
|
msg = self.mailbox.process_incoming_message(message)
|
||||||
|
|
||||||
self.assertNotEquals(msg.eml, None)
|
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:
|
with open(msg.eml.name, "rb") as f:
|
||||||
self.assertEqual(
|
self.assertEqual(f.read(), self._get_email_as_text("generic_message.eml"))
|
||||||
f.read(),
|
|
||||||
self._get_email_as_text('generic_message.eml')
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_message_saving_ignored(self):
|
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()
|
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 = copy.deepcopy(default_settings)
|
||||||
altered['store_original_message'] = False
|
altered["store_original_message"] = False
|
||||||
get_settings.return_value = altered
|
get_settings.return_value = altered
|
||||||
|
|
||||||
msg = self.mailbox.process_incoming_message(message)
|
msg = self.mailbox.process_incoming_message(message)
|
||||||
|
|
@ -411,22 +387,21 @@ class TestProcessEmail(EmailMessageTestCase):
|
||||||
self.assertEquals(msg.eml, None)
|
self.assertEquals(msg.eml, None)
|
||||||
|
|
||||||
def test_message_compressed(self):
|
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()
|
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 = copy.deepcopy(default_settings)
|
||||||
altered['compress_original_message'] = True
|
altered["compress_original_message"] = True
|
||||||
altered['store_original_message'] = True
|
altered["store_original_message"] = True
|
||||||
get_settings.return_value = altered
|
get_settings.return_value = altered
|
||||||
|
|
||||||
msg = self.mailbox.process_incoming_message(message)
|
msg = self.mailbox.process_incoming_message(message)
|
||||||
|
|
||||||
actual_email_object = msg.get_email_object()
|
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'))
|
|
||||||
|
|
|
||||||
|
|
@ -5,23 +5,26 @@ from django.core.management import call_command, CommandError
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
import django
|
import django
|
||||||
|
|
||||||
|
|
||||||
class CommandsTestCase(TestCase):
|
class CommandsTestCase(TestCase):
|
||||||
def test_processincomingmessage_no_args(self):
|
def test_processincomingmessage_no_args(self):
|
||||||
"""Check that processincomingmessage works with no args"""
|
"""Check that processincomingmessage works with no args"""
|
||||||
|
|
||||||
mailbox_name = None
|
mailbox_name = None
|
||||||
# Mock handle so that the test doesn't hang waiting for input. Note that we are only testing
|
# 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
|
# 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
|
# Don't care about the return value
|
||||||
handle.return_value = None
|
handle.return_value = None
|
||||||
|
|
||||||
call_command('processincomingmessage')
|
call_command("processincomingmessage")
|
||||||
args, kwargs = handle.call_args
|
args, kwargs = handle.call_args
|
||||||
|
|
||||||
# Make sure that we called with the right arguments
|
# Make sure that we called with the right arguments
|
||||||
try:
|
try:
|
||||||
self.assertEqual(kwargs['mailbox_name'], mailbox_name)
|
self.assertEqual(kwargs["mailbox_name"], mailbox_name)
|
||||||
except KeyError:
|
except KeyError:
|
||||||
# Handle Django 1.7
|
# Handle Django 1.7
|
||||||
# It uses optparse instead of argparse, so instead of being
|
# It uses optparse instead of argparse, so instead of being
|
||||||
|
|
@ -29,19 +32,20 @@ class CommandsTestCase(TestCase):
|
||||||
# Thus we expect an empty tuple here
|
# Thus we expect an empty tuple here
|
||||||
self.assertEqual(args, tuple())
|
self.assertEqual(args, tuple())
|
||||||
|
|
||||||
|
|
||||||
def test_processincomingmessage_with_arg(self):
|
def test_processincomingmessage_with_arg(self):
|
||||||
"""Check that processincomingmessage works with mailbox_name given"""
|
"""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
|
handle.return_value = None
|
||||||
|
|
||||||
call_command('processincomingmessage', mailbox_name)
|
call_command("processincomingmessage", mailbox_name)
|
||||||
args, kwargs = handle.call_args
|
args, kwargs = handle.call_args
|
||||||
try:
|
try:
|
||||||
self.assertEqual(kwargs['mailbox_name'], mailbox_name)
|
self.assertEqual(kwargs["mailbox_name"], mailbox_name)
|
||||||
except (AssertionError, KeyError):
|
except (AssertionError, KeyError):
|
||||||
# Handle Django 1.7
|
# Handle Django 1.7
|
||||||
# It uses optparse instead of argparse, so instead of being
|
# 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
|
# error, which means this test is worthless anyway
|
||||||
# For the "compatibility" versions, unexpected arguments aren't handled
|
# For the "compatibility" versions, unexpected arguments aren't handled
|
||||||
# very well, and result in a TypeError
|
# very well, and result in a TypeError
|
||||||
if (LooseVersion(django.get_version()) >= LooseVersion('1.8') and
|
if LooseVersion(django.get_version()) >= LooseVersion("1.8") and LooseVersion(
|
||||||
LooseVersion(django.get_version()) < LooseVersion('1.10')):
|
django.get_version()
|
||||||
|
) < LooseVersion("1.10"):
|
||||||
with self.assertRaises(TypeError):
|
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
|
# 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):
|
with self.assertRaises(CommandError):
|
||||||
call_command('processincomingmessage', 'foo_mailbox', 'invalid_arg')
|
call_command("processincomingmessage", "foo_mailbox", "invalid_arg")
|
||||||
|
|
|
||||||
|
|
@ -1,44 +1,32 @@
|
||||||
from unittest import mock
|
from unittest import mock
|
||||||
|
from os.path import exists
|
||||||
|
from os import remove
|
||||||
from django.test.utils import override_settings
|
from django.test.utils import override_settings
|
||||||
|
|
||||||
from django_mailbox.tests.base import EmailMessageTestCase, get_email_as_text
|
from django_mailbox.tests.base import EmailMessageTestCase, get_email_as_text
|
||||||
from django_mailbox.transports import ImapTransport, Pop3Transport
|
from django_mailbox.transports import ImapTransport, Pop3Transport
|
||||||
|
|
||||||
FAKE_UID_SEARCH_ANSWER = (
|
FAKE_UID_SEARCH_ANSWER = (
|
||||||
'OK',
|
"OK",
|
||||||
[
|
[
|
||||||
b'18 19 20 21 22 23 24 25 26 27 28 29 ' +
|
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"30 31 32 33 34 35 36 37 38 39 40 41 42 43 44"
|
||||||
]
|
],
|
||||||
)
|
)
|
||||||
FAKE_UID_FETCH_SIZES = (
|
FAKE_UID_FETCH_SIZES = (
|
||||||
'OK',
|
"OK",
|
||||||
[
|
[b"1 (UID 18 RFC822.SIZE 58070000000)", b"2 (UID 19 RFC822.SIZE 2593)"],
|
||||||
b'1 (UID 18 RFC822.SIZE 58070000000)',
|
|
||||||
b'2 (UID 19 RFC822.SIZE 2593)'
|
|
||||||
]
|
|
||||||
)
|
)
|
||||||
FAKE_UID_FETCH_MSG = (
|
FAKE_UID_FETCH_MSG = (
|
||||||
'OK',
|
"OK",
|
||||||
[
|
[
|
||||||
(
|
(b"1 (UID 18 RFC822 {5807}", get_email_as_text("generic_message.eml")),
|
||||||
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_UID_COPY_MSG = ("OK", [b"[COPYUID 1 2 2] (Success)"])
|
||||||
FAKE_LIST_ARCHIVE_FOLDERS_ANSWERS = (
|
FAKE_LIST_ARCHIVE_FOLDERS_ANSWERS = (
|
||||||
'OK',
|
"OK",
|
||||||
[
|
[b'(\\HasNoChildren \\All) "/" "[Gmail]/All Mail"'],
|
||||||
b'(\\HasNoChildren \\All) "/" "[Gmail]/All Mail"'
|
|
||||||
]
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -47,14 +35,14 @@ class IMAPTestCase(EmailMessageTestCase):
|
||||||
def imap_server_uid_method(*args):
|
def imap_server_uid_method(*args):
|
||||||
cmd = args[0]
|
cmd = args[0]
|
||||||
arg2 = args[2]
|
arg2 = args[2]
|
||||||
if cmd == 'search':
|
if cmd == "search":
|
||||||
return FAKE_UID_SEARCH_ANSWER
|
return FAKE_UID_SEARCH_ANSWER
|
||||||
if cmd == 'copy':
|
if cmd == "copy":
|
||||||
return FAKE_UID_COPY_MSG
|
return FAKE_UID_COPY_MSG
|
||||||
if cmd == 'fetch':
|
if cmd == "fetch":
|
||||||
if arg2 == '(RFC822.SIZE)':
|
if arg2 == "(RFC822.SIZE)":
|
||||||
return FAKE_UID_FETCH_SIZES
|
return FAKE_UID_FETCH_SIZES
|
||||||
if arg2 == '(RFC822)':
|
if arg2 == "(RFC822)":
|
||||||
return FAKE_UID_FETCH_MSG
|
return FAKE_UID_FETCH_MSG
|
||||||
|
|
||||||
def imap_server_list_method(pattern=None):
|
def imap_server_list_method(pattern=None):
|
||||||
|
|
@ -69,39 +57,35 @@ class IMAPTestCase(EmailMessageTestCase):
|
||||||
class TestImapTransport(IMAPTestCase):
|
class TestImapTransport(IMAPTestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super().setUp()
|
super().setUp()
|
||||||
self.arbitrary_hostname = 'one.two.three'
|
self.arbitrary_hostname = "one.two.three"
|
||||||
self.arbitrary_port = 100
|
self.arbitrary_port = 100
|
||||||
self.ssl = False
|
self.ssl = False
|
||||||
self.transport = ImapTransport(
|
self.transport = ImapTransport(
|
||||||
self.arbitrary_hostname,
|
self.arbitrary_hostname, self.arbitrary_port, self.ssl
|
||||||
self.arbitrary_port,
|
|
||||||
self.ssl
|
|
||||||
)
|
)
|
||||||
self.transport.server = self.imap_server
|
self.transport.server = self.imap_server
|
||||||
|
|
||||||
def test_get_email_message(self):
|
def test_get_email_message(self):
|
||||||
|
if exists("store.json"):
|
||||||
|
remove("store.json")
|
||||||
actual_messages = list(self.transport.get_message())
|
actual_messages = list(self.transport.get_message())
|
||||||
self.assertEqual(len(actual_messages), 27)
|
self.assertEqual(len(actual_messages), 27)
|
||||||
actual_message = actual_messages[0]
|
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)
|
self.assertEqual(expected_message, actual_message)
|
||||||
|
|
||||||
|
|
||||||
class TestImapArchivedTransport(TestImapTransport):
|
class TestImapArchivedTransport(TestImapTransport):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super().setUp()
|
super().setUp()
|
||||||
self.archive = 'Archive'
|
self.archive = "Archive"
|
||||||
self.transport = ImapTransport(
|
self.transport = ImapTransport(
|
||||||
self.arbitrary_hostname,
|
self.arbitrary_hostname, self.arbitrary_port, self.ssl, self.archive
|
||||||
self.arbitrary_port,
|
|
||||||
self.ssl,
|
|
||||||
self.archive
|
|
||||||
)
|
)
|
||||||
self.transport.server = self.imap_server
|
self.transport.server = self.imap_server
|
||||||
|
|
||||||
|
|
||||||
class TestMaxSizeImapTransport(TestImapTransport):
|
class TestMaxSizeImapTransport(TestImapTransport):
|
||||||
|
|
||||||
@override_settings(DJANGO_MAILBOX_MAX_MESSAGE_SIZE=5807)
|
@override_settings(DJANGO_MAILBOX_MAX_MESSAGE_SIZE=5807)
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super().setUp()
|
super().setUp()
|
||||||
|
|
@ -121,38 +105,40 @@ class TestMaxSizeImapTransport(TestImapTransport):
|
||||||
self.assertEqual(len(small_message_ids), 1)
|
self.assertEqual(len(small_message_ids), 1)
|
||||||
|
|
||||||
def test_get_email_message(self):
|
def test_get_email_message(self):
|
||||||
|
if exists("store.json"):
|
||||||
|
remove("store.json")
|
||||||
actual_messages = list(self.transport.get_message())
|
actual_messages = list(self.transport.get_message())
|
||||||
self.assertEqual(len(actual_messages), 1)
|
self.assertEqual(len(actual_messages), 1)
|
||||||
actual_message = actual_messages[0]
|
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)
|
self.assertEqual(expected_message, actual_message)
|
||||||
|
|
||||||
|
|
||||||
class TestPop3Transport(EmailMessageTestCase):
|
class TestPop3Transport(EmailMessageTestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.arbitrary_hostname = 'one.two.three'
|
self.arbitrary_hostname = "one.two.three"
|
||||||
self.arbitrary_port = 100
|
self.arbitrary_port = 100
|
||||||
self.ssl = False
|
self.ssl = False
|
||||||
self.transport = Pop3Transport(
|
self.transport = Pop3Transport(
|
||||||
self.arbitrary_hostname,
|
self.arbitrary_hostname, self.arbitrary_port, self.ssl
|
||||||
self.arbitrary_port,
|
|
||||||
self.ssl
|
|
||||||
)
|
)
|
||||||
self.transport.server = None
|
self.transport.server = None
|
||||||
super().setUp()
|
super().setUp()
|
||||||
|
|
||||||
def test_get_email_message(self):
|
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
|
# Consider this value arbitrary, the second parameter
|
||||||
# should have one entry per message in the inbox
|
# 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 = [
|
server.retr.return_value = [
|
||||||
'+OK message follows',
|
"+OK message follows",
|
||||||
[
|
[
|
||||||
line.encode('ascii')
|
line.encode("ascii")
|
||||||
for line in self._get_email_as_text(
|
for line in self._get_email_as_text("generic_message.eml")
|
||||||
'generic_message.eml'
|
.decode("ascii")
|
||||||
).decode('ascii').split('\n')
|
.split("\n")
|
||||||
],
|
],
|
||||||
10018, # Some arbitrary size, ideally matching the above
|
10018, # Some arbitrary size, ideally matching the above
|
||||||
]
|
]
|
||||||
|
|
@ -162,6 +148,6 @@ class TestPop3Transport(EmailMessageTestCase):
|
||||||
self.assertEqual(len(actual_messages), 1)
|
self.assertEqual(len(actual_messages), 1)
|
||||||
|
|
||||||
actual_message = actual_messages[0]
|
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)
|
self.assertEqual(expected_message, actual_message)
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import sys
|
# import sys
|
||||||
|
|
||||||
from .base import EmailTransport
|
from .base import EmailTransport
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,6 @@ logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class GmailImapTransport(ImapTransport):
|
class GmailImapTransport(ImapTransport):
|
||||||
|
|
||||||
def connect(self, username, password):
|
def connect(self, username, password):
|
||||||
# Try to use oauth2 first. It's much safer
|
# Try to use oauth2 first. It's much safer
|
||||||
try:
|
try:
|
||||||
|
|
@ -28,30 +27,25 @@ class GmailImapTransport(ImapTransport):
|
||||||
AccessTokenNotFound,
|
AccessTokenNotFound,
|
||||||
)
|
)
|
||||||
except ImportError:
|
except ImportError:
|
||||||
raise ValueError(
|
raise ValueError("Install python-social-auth to use oauth2 auth for gmail")
|
||||||
"Install python-social-auth to use oauth2 auth for gmail"
|
|
||||||
)
|
|
||||||
|
|
||||||
access_token = None
|
access_token = None
|
||||||
while access_token is None:
|
while access_token is None:
|
||||||
try:
|
try:
|
||||||
access_token = get_google_access_token(username)
|
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:
|
except TypeError:
|
||||||
# This means that the google process took too long
|
# This means that the google process took too long
|
||||||
# Trying again is the right thing to do
|
# Trying again is the right thing to do
|
||||||
pass
|
pass
|
||||||
except AccessTokenNotFound:
|
except AccessTokenNotFound:
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
"No Token available in python-social-auth for %s" % (
|
"No Token available in python-social-auth for %s" % (username)
|
||||||
username
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
auth_string = 'user={}\1auth=Bearer {}\1\1'.format(
|
auth_string = "user={}\1auth=Bearer {}\1\1".format(
|
||||||
google_email_address,
|
google_email_address, access_token
|
||||||
access_token
|
|
||||||
)
|
)
|
||||||
self.server = self.transport(self.hostname, self.port)
|
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()
|
self.server.select()
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import imaplib
|
import imaplib
|
||||||
import logging
|
import logging
|
||||||
|
from os.path import exists
|
||||||
|
import json
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
|
||||||
from .base import EmailTransport, MessageParseError
|
from .base import EmailTransport, MessageParseError
|
||||||
|
|
@ -18,28 +19,25 @@ logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
class ImapTransport(EmailTransport):
|
class ImapTransport(EmailTransport):
|
||||||
def __init__(
|
def __init__(
|
||||||
self, hostname, port=None, ssl=False, tls=False,
|
self,
|
||||||
archive='', folder=None,
|
hostname,
|
||||||
|
port=None,
|
||||||
|
ssl=False,
|
||||||
|
tls=False,
|
||||||
|
archive="",
|
||||||
|
folder=None,
|
||||||
):
|
):
|
||||||
self.max_message_size = getattr(
|
self.max_message_size = getattr(
|
||||||
settings,
|
settings, "DJANGO_MAILBOX_MAX_MESSAGE_SIZE", False
|
||||||
'DJANGO_MAILBOX_MAX_MESSAGE_SIZE',
|
|
||||||
False
|
|
||||||
)
|
)
|
||||||
self.integration_testing_subject = getattr(
|
self.integration_testing_subject = getattr(
|
||||||
settings,
|
settings, "DJANGO_MAILBOX_INTEGRATION_TESTING_SUBJECT", None
|
||||||
'DJANGO_MAILBOX_INTEGRATION_TESTING_SUBJECT',
|
|
||||||
None
|
|
||||||
)
|
)
|
||||||
self.delete_message_from_server = getattr(
|
self.delete_message_from_server = getattr(
|
||||||
settings,
|
settings, "DJANGO_MAILBOX_DELETE_MESSAGE_FROM_SERVER", True
|
||||||
'DJANGO_MAILBOX_DELETE_MESSAGE_FROM_SERVER',
|
|
||||||
True
|
|
||||||
)
|
)
|
||||||
self.delete_message_store = getattr(
|
self.delete_message_store = getattr(
|
||||||
settings,
|
settings, "DJANGO_MAILBOX_DELETE_MESSAGE_ID_STORE", "/tmp/store.json"
|
||||||
'DJANGO_MAILBOX_DELETE_MESSAGE_ID_STORE',
|
|
||||||
"/tmp/store.json"
|
|
||||||
)
|
)
|
||||||
self.hostname = hostname
|
self.hostname = hostname
|
||||||
self.port = port
|
self.port = port
|
||||||
|
|
@ -68,13 +66,13 @@ class ImapTransport(EmailTransport):
|
||||||
|
|
||||||
def _get_all_message_ids(self):
|
def _get_all_message_ids(self):
|
||||||
# Fetch all the message uids
|
# 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()
|
message_id_string = message_ids[0].strip()
|
||||||
# Usually `message_id_string` will be a list of space-separated
|
# Usually `message_id_string` will be a list of space-separated
|
||||||
# ids; we must make sure that it isn't an empty string before
|
# ids; we must make sure that it isn't an empty string before
|
||||||
# splitting into individual UIDs.
|
# splitting into individual UIDs.
|
||||||
if message_id_string:
|
if message_id_string:
|
||||||
return message_id_string.decode().split(' ')
|
return message_id_string.decode().split(" ")
|
||||||
return []
|
return []
|
||||||
|
|
||||||
def _get_small_message_ids(self, message_ids):
|
def _get_small_message_ids(self, message_ids):
|
||||||
|
|
@ -83,23 +81,17 @@ class ImapTransport(EmailTransport):
|
||||||
# limit
|
# limit
|
||||||
safe_message_ids = []
|
safe_message_ids = []
|
||||||
|
|
||||||
status, data = self.server.uid(
|
status, data = self.server.uid("fetch", ",".join(message_ids), "(RFC822.SIZE)")
|
||||||
'fetch',
|
|
||||||
','.join(message_ids),
|
|
||||||
'(RFC822.SIZE)'
|
|
||||||
)
|
|
||||||
|
|
||||||
for each_msg in data:
|
for each_msg in data:
|
||||||
each_msg = each_msg.decode()
|
each_msg = each_msg.decode()
|
||||||
try:
|
try:
|
||||||
uid = each_msg.split(' ')[2]
|
uid = each_msg.split(" ")[2]
|
||||||
size = each_msg.split(' ')[4].rstrip(')')
|
size = each_msg.split(" ")[4].rstrip(")")
|
||||||
if int(size) <= int(self.max_message_size):
|
if int(size) <= int(self.max_message_size):
|
||||||
safe_message_ids.append(uid)
|
safe_message_ids.append(uid)
|
||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
logger.warning(
|
logger.warning("ValueError: {} working on {}".format(e, each_msg[0]))
|
||||||
"ValueError: {} working on {}".format(e, each_msg[0])
|
|
||||||
)
|
|
||||||
pass
|
pass
|
||||||
return safe_message_ids
|
return safe_message_ids
|
||||||
|
|
||||||
|
|
@ -120,30 +112,29 @@ class ImapTransport(EmailTransport):
|
||||||
self.server.create(self.archive)
|
self.server.create(self.archive)
|
||||||
|
|
||||||
# PoC of alternative method of storing email
|
# PoC of alternative method of storing email
|
||||||
if not self.delete_from_server:
|
if not self.delete_message_from_server:
|
||||||
j = {"uids":[]}
|
j = {"uids": []}
|
||||||
if os.path.exists("store.json"):
|
if exists("store.json"):
|
||||||
with open("store.json","r") as f:
|
with open("store.json", "r") as f:
|
||||||
j = json.load(f)
|
j = json.load(f)
|
||||||
else:
|
else:
|
||||||
with open("store.json","w") as f:
|
with open("store.json", "w") as f:
|
||||||
json.dump(j,f)
|
json.dump(j, f)
|
||||||
# PoC of alternative method of storing email
|
# PoC of alternative method of storing email
|
||||||
|
|
||||||
|
|
||||||
for uid in message_ids:
|
for uid in message_ids:
|
||||||
# PoC of alternative method of storing email
|
# PoC of alternative method of storing email
|
||||||
if not self.delete_from_server:
|
if not self.delete_message_from_server:
|
||||||
if uid in j["uids"]:
|
if uid in j["uids"]:
|
||||||
continue
|
continue
|
||||||
else:
|
else:
|
||||||
j["uids"].append(uid)
|
j["uids"].append(uid)
|
||||||
with open("store.json","w") as f:
|
with open("store.json", "w") as f:
|
||||||
json.dump(j,f)
|
json.dump(j, f)
|
||||||
# PoC of alternative method of storing email
|
# PoC of alternative method of storing email
|
||||||
|
|
||||||
try:
|
try:
|
||||||
typ, msg_contents = self.server.uid('fetch', uid, '(RFC822)')
|
typ, msg_contents = self.server.uid("fetch", uid, "(RFC822)")
|
||||||
if not msg_contents:
|
if not msg_contents:
|
||||||
continue
|
continue
|
||||||
try:
|
try:
|
||||||
|
|
@ -162,8 +153,9 @@ class ImapTransport(EmailTransport):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if self.archive:
|
if self.archive:
|
||||||
self.server.uid('copy', uid, self.archive)
|
self.server.uid("copy", uid, self.archive)
|
||||||
self.delete_message_from_server:
|
|
||||||
self.server.uid('store', uid, "+FLAGS", "(\\Deleted)")
|
if self.delete_message_from_server:
|
||||||
|
self.server.uid("store", uid, "+FLAGS", "(\\Deleted)")
|
||||||
self.server.expunge()
|
self.server.expunge()
|
||||||
return
|
return
|
||||||
|
|
|
||||||
|
|
@ -22,15 +22,13 @@ class Pop3Transport(EmailTransport):
|
||||||
self.server.pass_(password)
|
self.server.pass_(password)
|
||||||
|
|
||||||
def get_message_body(self, message_lines):
|
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):
|
def get_message(self, condition=None):
|
||||||
message_count = len(self.server.list()[1])
|
message_count = len(self.server.list()[1])
|
||||||
for i in range(message_count):
|
for i in range(message_count):
|
||||||
try:
|
try:
|
||||||
msg_contents = self.get_message_body(
|
msg_contents = self.get_message_body(self.server.retr(i + 1)[1])
|
||||||
self.server.retr(i + 1)[1]
|
|
||||||
)
|
|
||||||
message = self.get_email_from_bytes(msg_contents)
|
message = self.get_email_from_bytes(msg_contents)
|
||||||
|
|
||||||
if condition and not condition(message):
|
if condition and not condition(message):
|
||||||
|
|
|
||||||
|
|
@ -11,127 +11,112 @@ logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
def get_settings():
|
def get_settings():
|
||||||
return {
|
return {
|
||||||
'strip_unallowed_mimetypes': getattr(
|
"strip_unallowed_mimetypes": getattr(
|
||||||
settings,
|
settings, "DJANGO_MAILBOX_STRIP_UNALLOWED_MIMETYPES", False
|
||||||
'DJANGO_MAILBOX_STRIP_UNALLOWED_MIMETYPES',
|
|
||||||
False
|
|
||||||
),
|
),
|
||||||
'allowed_mimetypes': getattr(
|
"allowed_mimetypes": getattr(
|
||||||
settings,
|
settings, "DJANGO_MAILBOX_ALLOWED_MIMETYPES", ["text/plain", "text/html"]
|
||||||
'DJANGO_MAILBOX_ALLOWED_MIMETYPES',
|
|
||||||
[
|
|
||||||
'text/plain',
|
|
||||||
'text/html'
|
|
||||||
]
|
|
||||||
),
|
),
|
||||||
'text_stored_mimetypes': getattr(
|
"text_stored_mimetypes": getattr(
|
||||||
settings,
|
settings,
|
||||||
'DJANGO_MAILBOX_TEXT_STORED_MIMETYPES',
|
"DJANGO_MAILBOX_TEXT_STORED_MIMETYPES",
|
||||||
[
|
["text/plain", "text/html"],
|
||||||
'text/plain',
|
|
||||||
'text/html'
|
|
||||||
]
|
|
||||||
),
|
),
|
||||||
'altered_message_header': getattr(
|
"altered_message_header": getattr(
|
||||||
settings,
|
settings,
|
||||||
'DJANGO_MAILBOX_ALTERED_MESSAGE_HEADER',
|
"DJANGO_MAILBOX_ALTERED_MESSAGE_HEADER",
|
||||||
'X-Django-Mailbox-Altered-Message'
|
"X-Django-Mailbox-Altered-Message",
|
||||||
),
|
),
|
||||||
'attachment_interpolation_header': getattr(
|
"attachment_interpolation_header": getattr(
|
||||||
settings,
|
settings,
|
||||||
'DJANGO_MAILBOX_ATTACHMENT_INTERPOLATION_HEADER',
|
"DJANGO_MAILBOX_ATTACHMENT_INTERPOLATION_HEADER",
|
||||||
'X-Django-Mailbox-Interpolate-Attachment'
|
"X-Django-Mailbox-Interpolate-Attachment",
|
||||||
),
|
),
|
||||||
'attachment_upload_to': getattr(
|
"attachment_upload_to": getattr(
|
||||||
settings,
|
settings,
|
||||||
'DJANGO_MAILBOX_ATTACHMENT_UPLOAD_TO',
|
"DJANGO_MAILBOX_ATTACHMENT_UPLOAD_TO",
|
||||||
'mailbox_attachments/%Y/%m/%d/'
|
"mailbox_attachments/%Y/%m/%d/",
|
||||||
),
|
),
|
||||||
'store_original_message': getattr(
|
"store_original_message": getattr(
|
||||||
settings,
|
settings, "DJANGO_MAILBOX_STORE_ORIGINAL_MESSAGE", False
|
||||||
'DJANGO_MAILBOX_STORE_ORIGINAL_MESSAGE',
|
|
||||||
False
|
|
||||||
),
|
),
|
||||||
'compress_original_message': getattr(
|
"compress_original_message": getattr(
|
||||||
settings,
|
settings, "DJANGO_MAILBOX_COMPRESS_ORIGINAL_MESSAGE", False
|
||||||
'DJANGO_MAILBOX_COMPRESS_ORIGINAL_MESSAGE',
|
|
||||||
False
|
|
||||||
),
|
),
|
||||||
'original_message_compression': getattr(
|
"original_message_compression": getattr(
|
||||||
settings,
|
settings, "DJANGO_MAILBOX_ORIGINAL_MESSAGE_COMPRESSION", 6
|
||||||
'DJANGO_MAILBOX_ORIGINAL_MESSAGE_COMPRESSION',
|
|
||||||
6
|
|
||||||
),
|
),
|
||||||
'default_charset': getattr(
|
"default_charset": getattr(
|
||||||
settings,
|
settings,
|
||||||
'DJANGO_MAILBOX_default_charset',
|
"DJANGO_MAILBOX_default_charset",
|
||||||
'iso8859-1',
|
"iso8859-1",
|
||||||
)
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def convert_header_to_unicode(header):
|
def convert_header_to_unicode(header):
|
||||||
default_charset = get_settings()['default_charset']
|
default_charset = get_settings()["default_charset"]
|
||||||
|
|
||||||
def _decode(value, encoding):
|
def _decode(value, encoding):
|
||||||
if isinstance(value, str):
|
if isinstance(value, str):
|
||||||
return value
|
return value
|
||||||
if not encoding or encoding == 'unknown-8bit':
|
if not encoding or encoding == "unknown-8bit":
|
||||||
encoding = default_charset
|
encoding = default_charset
|
||||||
return value.decode(encoding, 'replace')
|
return value.decode(encoding, "replace")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
return ''.join(
|
return "".join(
|
||||||
[
|
[
|
||||||
(
|
(_decode(bytestr, encoding))
|
||||||
_decode(bytestr, encoding)
|
for bytestr, encoding in email.header.decode_header(header)
|
||||||
) for bytestr, encoding in email.header.decode_header(header)
|
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
except UnicodeDecodeError:
|
except UnicodeDecodeError:
|
||||||
logger.exception(
|
logger.exception(
|
||||||
'Errors encountered decoding header %s into encoding %s.',
|
"Errors encountered decoding header %s into encoding %s.",
|
||||||
header,
|
header,
|
||||||
default_charset,
|
default_charset,
|
||||||
)
|
)
|
||||||
return header.decode(default_charset, 'replace')
|
return header.decode(default_charset, "replace")
|
||||||
|
|
||||||
|
|
||||||
def get_body_from_message(message, maintype, subtype):
|
def get_body_from_message(message, maintype, subtype):
|
||||||
"""
|
"""
|
||||||
Fetchs the body message matching main/sub content type.
|
Fetchs the body message matching main/sub content type.
|
||||||
"""
|
"""
|
||||||
body = ''
|
body = ""
|
||||||
for part in message.walk():
|
for part in message.walk():
|
||||||
if part.get('content-disposition', '').startswith('attachment;'):
|
if part.get("content-disposition", "").startswith("attachment;"):
|
||||||
continue
|
continue
|
||||||
if part.get_content_maintype() == maintype and \
|
if (
|
||||||
part.get_content_subtype() == subtype:
|
part.get_content_maintype() == maintype
|
||||||
|
and part.get_content_subtype() == subtype
|
||||||
|
):
|
||||||
charset = part.get_content_charset()
|
charset = part.get_content_charset()
|
||||||
this_part = part.get_payload(decode=True)
|
this_part = part.get_payload(decode=True)
|
||||||
if charset:
|
if charset:
|
||||||
try:
|
try:
|
||||||
this_part = this_part.decode(charset, 'replace')
|
this_part = this_part.decode(charset, "replace")
|
||||||
except LookupError:
|
except LookupError:
|
||||||
this_part = this_part.decode('ascii', 'replace')
|
this_part = this_part.decode("ascii", "replace")
|
||||||
logger.warning(
|
logger.warning(
|
||||||
'Unknown encoding %s encountered while decoding '
|
"Unknown encoding %s encountered while decoding "
|
||||||
'text payload. Interpreting as ASCII with '
|
"text payload. Interpreting as ASCII with "
|
||||||
'replacement, but some data may not be '
|
"replacement, but some data may not be "
|
||||||
'represented as the sender intended.',
|
"represented as the sender intended.",
|
||||||
charset
|
charset,
|
||||||
)
|
)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
this_part = this_part.decode('ascii', 'replace')
|
this_part = this_part.decode("ascii", "replace")
|
||||||
logger.warning(
|
logger.warning(
|
||||||
'Error encountered while decoding text '
|
"Error encountered while decoding text "
|
||||||
'payload from an incorrectly-constructed '
|
"payload from an incorrectly-constructed "
|
||||||
'e-mail; payload was converted to ASCII with '
|
"e-mail; payload was converted to ASCII with "
|
||||||
'replacement, but some data may not be '
|
"replacement, but some data may not be "
|
||||||
'represented as the sender intended.'
|
"represented as the sender intended."
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
this_part = this_part.decode('ascii', 'replace')
|
this_part = this_part.decode("ascii", "replace")
|
||||||
|
|
||||||
body += this_part
|
body += this_part
|
||||||
|
|
||||||
|
|
@ -141,8 +126,8 @@ def get_body_from_message(message, maintype, subtype):
|
||||||
def get_attachment_save_path(instance, filename):
|
def get_attachment_save_path(instance, filename):
|
||||||
settings = get_settings()
|
settings = get_settings()
|
||||||
|
|
||||||
path = settings['attachment_upload_to']
|
path = settings["attachment_upload_to"]
|
||||||
if '%' in path:
|
if "%" in path:
|
||||||
path = datetime.datetime.utcnow().strftime(path)
|
path = datetime.datetime.utcnow().strftime(path)
|
||||||
|
|
||||||
return os.path.join(
|
return os.path.join(
|
||||||
|
|
|
||||||
167
docs/conf.py
167
docs/conf.py
|
|
@ -15,106 +15,106 @@ import sys, os
|
||||||
# If extensions (or modules to document with autodoc) are in another directory,
|
# 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
|
# 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.
|
# 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 -----------------------------------------------------
|
# -- General configuration -----------------------------------------------------
|
||||||
|
|
||||||
# If your documentation needs a minimal Sphinx version, state it here.
|
# 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
|
# Add any Sphinx extension module names here, as strings. They can be extensions
|
||||||
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
|
# 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.
|
# Add any paths that contain templates here, relative to this directory.
|
||||||
templates_path = ['_templates']
|
templates_path = ["_templates"]
|
||||||
|
|
||||||
# The suffix of source filenames.
|
# The suffix of source filenames.
|
||||||
source_suffix = '.rst'
|
source_suffix = ".rst"
|
||||||
|
|
||||||
# The encoding of source files.
|
# The encoding of source files.
|
||||||
#source_encoding = 'utf-8-sig'
|
# source_encoding = 'utf-8-sig'
|
||||||
|
|
||||||
# The master toctree document.
|
# The master toctree document.
|
||||||
master_doc = 'index'
|
master_doc = "index"
|
||||||
|
|
||||||
# General information about the project.
|
# General information about the project.
|
||||||
project = 'django-mailbox'
|
project = "django-mailbox"
|
||||||
copyright = '2020, Adam Coddington'
|
copyright = "2020, Adam Coddington"
|
||||||
|
|
||||||
# The version info for the project you're documenting, acts as replacement for
|
# The version info for the project you're documenting, acts as replacement for
|
||||||
# |version| and |release|, also used in various other places throughout the
|
# |version| and |release|, also used in various other places throughout the
|
||||||
# built documents.
|
# built documents.
|
||||||
#
|
#
|
||||||
# The short X.Y version.
|
# The short X.Y version.
|
||||||
version = '3.3'
|
version = "3.3"
|
||||||
# The full version, including alpha/beta/rc tags.
|
# The full version, including alpha/beta/rc tags.
|
||||||
release = '3.3'
|
release = "3.3"
|
||||||
|
|
||||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||||
# for a list of supported languages.
|
# for a list of supported languages.
|
||||||
#language = None
|
# language = None
|
||||||
|
|
||||||
# There are two options for replacing |today|: either, you set today to some
|
# There are two options for replacing |today|: either, you set today to some
|
||||||
# non-false value, then it is used:
|
# non-false value, then it is used:
|
||||||
#today = ''
|
# today = ''
|
||||||
# Else, today_fmt is used as the format for a strftime call.
|
# 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
|
# List of patterns, relative to source directory, that match files and
|
||||||
# directories to ignore when looking for source files.
|
# 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.
|
# 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.
|
# 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
|
# If true, the current module name will be prepended to all description
|
||||||
# unit titles (such as .. function::).
|
# unit titles (such as .. function::).
|
||||||
#add_module_names = True
|
# add_module_names = True
|
||||||
|
|
||||||
# If true, sectionauthor and moduleauthor directives will be shown in the
|
# If true, sectionauthor and moduleauthor directives will be shown in the
|
||||||
# output. They are ignored by default.
|
# output. They are ignored by default.
|
||||||
#show_authors = False
|
# show_authors = False
|
||||||
|
|
||||||
# The name of the Pygments (syntax highlighting) style to use.
|
# 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.
|
# A list of ignored prefixes for module index sorting.
|
||||||
#modindex_common_prefix = []
|
# modindex_common_prefix = []
|
||||||
|
|
||||||
|
|
||||||
# -- Options for HTML output ---------------------------------------------------
|
# -- Options for HTML output ---------------------------------------------------
|
||||||
|
|
||||||
# The theme to use for HTML and HTML Help pages. See the documentation for
|
# The theme to use for HTML and HTML Help pages. See the documentation for
|
||||||
# a list of builtin themes.
|
# 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
|
# 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
|
# further. For a list of options available for each theme, see the
|
||||||
# documentation.
|
# documentation.
|
||||||
#html_theme_options = {}
|
# html_theme_options = {}
|
||||||
|
|
||||||
# Add any paths that contain custom themes here, relative to this directory.
|
# 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
|
# The name for this set of Sphinx documents. If None, it defaults to
|
||||||
# "<project> v<release> documentation".
|
# "<project> v<release> documentation".
|
||||||
#html_title = None
|
# html_title = None
|
||||||
|
|
||||||
# A shorter title for the navigation bar. Default is the same as html_title.
|
# 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
|
# The name of an image file (relative to this directory) to place at the top
|
||||||
# of the sidebar.
|
# 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
|
# 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
|
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
|
||||||
# pixels large.
|
# pixels large.
|
||||||
#html_favicon = None
|
# html_favicon = None
|
||||||
|
|
||||||
# Add any paths that contain custom static files (such as style sheets) here,
|
# 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,
|
# 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,
|
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
|
||||||
# using the given strftime format.
|
# 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
|
# If true, SmartyPants will be used to convert quotes and dashes to
|
||||||
# typographically correct entities.
|
# typographically correct entities.
|
||||||
#html_use_smartypants = True
|
# html_use_smartypants = True
|
||||||
|
|
||||||
# Custom sidebar templates, maps document names to template names.
|
# 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
|
# Additional templates that should be rendered to pages, maps page names to
|
||||||
# template names.
|
# template names.
|
||||||
#html_additional_pages = {}
|
# html_additional_pages = {}
|
||||||
|
|
||||||
# If false, no module index is generated.
|
# If false, no module index is generated.
|
||||||
#html_domain_indices = True
|
# html_domain_indices = True
|
||||||
|
|
||||||
# If false, no index is generated.
|
# 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.
|
# 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.
|
# 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.
|
# 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.
|
# 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
|
# 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
|
# contain a <link> tag referring to it. The value of this option must be the
|
||||||
# base URL from which the finished HTML is served.
|
# 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").
|
# 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.
|
# Output file base name for HTML help builder.
|
||||||
htmlhelp_basename = 'django-mailboxdoc'
|
htmlhelp_basename = "django-mailboxdoc"
|
||||||
|
|
||||||
|
|
||||||
# -- Options for LaTeX output --------------------------------------------------
|
# -- Options for LaTeX output --------------------------------------------------
|
||||||
|
|
||||||
latex_elements = {
|
latex_elements = {
|
||||||
# The paper size ('letterpaper' or 'a4paper').
|
# The paper size ('letterpaper' or 'a4paper').
|
||||||
#'papersize': 'letterpaper',
|
#'papersize': 'letterpaper',
|
||||||
|
# The font size ('10pt', '11pt' or '12pt').
|
||||||
# The font size ('10pt', '11pt' or '12pt').
|
#'pointsize': '10pt',
|
||||||
#'pointsize': '10pt',
|
# Additional stuff for the LaTeX preamble.
|
||||||
|
#'preamble': '',
|
||||||
# Additional stuff for the LaTeX preamble.
|
|
||||||
#'preamble': '',
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# Grouping the document tree into LaTeX files. List of tuples
|
# Grouping the document tree into LaTeX files. List of tuples
|
||||||
# (source start file, target name, title, author, documentclass [howto/manual]).
|
# (source start file, target name, title, author, documentclass [howto/manual]).
|
||||||
latex_documents = [
|
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 name of an image file (relative to this directory) to place at the top of
|
||||||
# the title page.
|
# the title page.
|
||||||
#latex_logo = None
|
# latex_logo = None
|
||||||
|
|
||||||
# For "manual" documents, if this is true, then toplevel headings are parts,
|
# For "manual" documents, if this is true, then toplevel headings are parts,
|
||||||
# not chapters.
|
# not chapters.
|
||||||
#latex_use_parts = False
|
# latex_use_parts = False
|
||||||
|
|
||||||
# If true, show page references after internal links.
|
# If true, show page references after internal links.
|
||||||
#latex_show_pagerefs = False
|
# latex_show_pagerefs = False
|
||||||
|
|
||||||
# If true, show URL addresses after external links.
|
# 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.
|
# Documents to append as an appendix to all manuals.
|
||||||
#latex_appendices = []
|
# latex_appendices = []
|
||||||
|
|
||||||
# If false, no module index is generated.
|
# If false, no module index is generated.
|
||||||
#latex_domain_indices = True
|
# latex_domain_indices = True
|
||||||
|
|
||||||
|
|
||||||
# -- Options for manual page output --------------------------------------------
|
# -- Options for manual page output --------------------------------------------
|
||||||
|
|
@ -212,12 +215,11 @@ latex_documents = [
|
||||||
# One entry per manual page. List of tuples
|
# One entry per manual page. List of tuples
|
||||||
# (source start file, name, description, authors, manual section).
|
# (source start file, name, description, authors, manual section).
|
||||||
man_pages = [
|
man_pages = [
|
||||||
('index', 'django-mailbox', 'django-mailbox Documentation',
|
("index", "django-mailbox", "django-mailbox Documentation", ["Adam Coddington"], 1)
|
||||||
['Adam Coddington'], 1)
|
|
||||||
]
|
]
|
||||||
|
|
||||||
# If true, show URL addresses after external links.
|
# If true, show URL addresses after external links.
|
||||||
#man_show_urls = False
|
# man_show_urls = False
|
||||||
|
|
||||||
|
|
||||||
# -- Options for Texinfo output ------------------------------------------------
|
# -- Options for Texinfo output ------------------------------------------------
|
||||||
|
|
@ -226,29 +228,41 @@ man_pages = [
|
||||||
# (source start file, target name, title, author,
|
# (source start file, target name, title, author,
|
||||||
# dir menu entry, description, category)
|
# dir menu entry, description, category)
|
||||||
texinfo_documents = [
|
texinfo_documents = [
|
||||||
('index', 'django-mailbox', 'django-mailbox Documentation',
|
(
|
||||||
'Adam Coddington', 'django-mailbox', 'One line description of project.',
|
"index",
|
||||||
'Miscellaneous'),
|
"django-mailbox",
|
||||||
|
"django-mailbox Documentation",
|
||||||
|
"Adam Coddington",
|
||||||
|
"django-mailbox",
|
||||||
|
"One line description of project.",
|
||||||
|
"Miscellaneous",
|
||||||
|
),
|
||||||
]
|
]
|
||||||
|
|
||||||
# Documents to append as an appendix to all manuals.
|
# Documents to append as an appendix to all manuals.
|
||||||
#texinfo_appendices = []
|
# texinfo_appendices = []
|
||||||
|
|
||||||
# If false, no module index is generated.
|
# If false, no module index is generated.
|
||||||
#texinfo_domain_indices = True
|
# texinfo_domain_indices = True
|
||||||
|
|
||||||
# How to display URL addresses: 'footnote', 'no', or 'inline'.
|
# 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 inspect
|
||||||
import django
|
import django
|
||||||
from django.utils.html import strip_tags
|
from django.utils.html import strip_tags
|
||||||
from django.utils.encoding import force_text
|
from django.utils.encoding import force_text
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
settings.configure(INSTALLED_APPS=['django_mailbox', ])
|
|
||||||
|
settings.configure(
|
||||||
|
INSTALLED_APPS=[
|
||||||
|
"django_mailbox",
|
||||||
|
]
|
||||||
|
)
|
||||||
django.setup()
|
django.setup()
|
||||||
|
|
||||||
|
|
||||||
def process_docstring(app, what, name, obj, options, lines):
|
def process_docstring(app, what, name, obj, options, lines):
|
||||||
# Source: https://gist.github.com/abulka/48b54ea4cbc7eb014308
|
# Source: https://gist.github.com/abulka/48b54ea4cbc7eb014308
|
||||||
# This causes import errors if left outside the function
|
# 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:
|
for field in fields:
|
||||||
# Skip ManyToOneRel and ManyToManyRel fields which have no 'verbose_name' or 'help_text'
|
# 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
|
continue
|
||||||
|
|
||||||
# Decode and strip any html out of the field's help text
|
# 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:
|
if help_text:
|
||||||
# Add the model field to the end of the docstring as a param
|
# Add the model field to the end of the docstring as a param
|
||||||
# using the help text as the description
|
# using the help text as the description
|
||||||
lines.append(':param {}: {}'.format(field.attname, help_text))
|
lines.append(":param {}: {}".format(field.attname, help_text))
|
||||||
else:
|
else:
|
||||||
# Add the model field to the end of the docstring as a param
|
# Add the model field to the end of the docstring as a param
|
||||||
# using the verbose name as the description
|
# 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
|
# Add the field's type to the docstring
|
||||||
if isinstance(field, models.ForeignKey):
|
if isinstance(field, models.ForeignKey):
|
||||||
to = field.related_model
|
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:
|
else:
|
||||||
lines.append(':type {}: {}'.format(field.attname, type(field).__name__))
|
lines.append(":type {}: {}".format(field.attname, type(field).__name__))
|
||||||
|
|
||||||
# Return the extended docstring
|
# Return the extended docstring
|
||||||
return lines
|
return lines
|
||||||
|
|
||||||
|
|
||||||
def setup(app):
|
def setup(app):
|
||||||
# Register the docstring processor with sphinx
|
# Register the docstring processor with sphinx
|
||||||
app.connect('autodoc-process-docstring', process_docstring)
|
app.connect("autodoc-process-docstring", process_docstring)
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,8 @@ Django = "^3.1.6"
|
||||||
[tool.poetry.dev-dependencies]
|
[tool.poetry.dev-dependencies]
|
||||||
pytest = "^6.2.2"
|
pytest = "^6.2.2"
|
||||||
pytest-django = "^4.1.0"
|
pytest-django = "^4.1.0"
|
||||||
|
flake8 = "^3.8.4"
|
||||||
|
black = "^20.8b1"
|
||||||
|
|
||||||
[build-system]
|
[build-system]
|
||||||
requires = ["poetry-core>=1.0.0"]
|
requires = ["poetry-core>=1.0.0"]
|
||||||
|
|
|
||||||
|
|
@ -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.
|
Easily ingest messages from POP3, IMAP, or local mailboxes into your Django application.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +0,0 @@
|
||||||
[pytest]
|
|
||||||
norecursedirs=env docs lib .eggs
|
|
||||||
DJANGO_SETTINGS_MODULE=django_mailbox.tests.settings
|
|
||||||
addopts = --tb=short -rxs
|
|
||||||
60
setup.py
60
setup.py
|
|
@ -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'
|
|
||||||
]
|
|
||||||
)
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue