forked from mirror/django-mailbox
Also changed the default from "messages" to separate-by-date directories "mailbox_messages/%Y/%m/%d/"
165 lines
5 KiB
Python
165 lines
5 KiB
Python
import datetime
|
|
import email.header
|
|
import logging
|
|
import os
|
|
|
|
from django.conf import settings
|
|
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
def get_settings():
|
|
return {
|
|
'strip_unallowed_mimetypes': getattr(
|
|
settings,
|
|
'DJANGO_MAILBOX_STRIP_UNALLOWED_MIMETYPES',
|
|
False
|
|
),
|
|
'allowed_mimetypes': getattr(
|
|
settings,
|
|
'DJANGO_MAILBOX_ALLOWED_MIMETYPES',
|
|
[
|
|
'text/plain',
|
|
'text/html'
|
|
]
|
|
),
|
|
'text_stored_mimetypes': getattr(
|
|
settings,
|
|
'DJANGO_MAILBOX_TEXT_STORED_MIMETYPES',
|
|
[
|
|
'text/plain',
|
|
'text/html'
|
|
]
|
|
),
|
|
'altered_message_header': getattr(
|
|
settings,
|
|
'DJANGO_MAILBOX_ALTERED_MESSAGE_HEADER',
|
|
'X-Django-Mailbox-Altered-Message'
|
|
),
|
|
'attachment_interpolation_header': getattr(
|
|
settings,
|
|
'DJANGO_MAILBOX_ATTACHMENT_INTERPOLATION_HEADER',
|
|
'X-Django-Mailbox-Interpolate-Attachment'
|
|
),
|
|
'attachment_upload_to': getattr(
|
|
settings,
|
|
'DJANGO_MAILBOX_ATTACHMENT_UPLOAD_TO',
|
|
'mailbox_attachments/%Y/%m/%d/'
|
|
),
|
|
'store_original_message': getattr(
|
|
settings,
|
|
'DJANGO_MAILBOX_STORE_ORIGINAL_MESSAGE',
|
|
False
|
|
),
|
|
'compress_original_message': getattr(
|
|
settings,
|
|
'DJANGO_MAILBOX_COMPRESS_ORIGINAL_MESSAGE',
|
|
False
|
|
),
|
|
'original_message_compression': getattr(
|
|
settings,
|
|
'DJANGO_MAILBOX_ORIGINAL_MESSAGE_COMPRESSION',
|
|
6
|
|
),
|
|
'original_message_upload_to': getattr(
|
|
settings,
|
|
'DJANGO_MAILBOX_ORIGINAL_MESSAGE_UPLOAD_TO',
|
|
'mailbox_messages/%Y/%m/%d/'
|
|
),
|
|
'default_charset': getattr(
|
|
settings,
|
|
'DJANGO_MAILBOX_DEFAULT_CHARSET',
|
|
'iso8859-1',
|
|
)
|
|
}
|
|
|
|
|
|
def convert_header_to_unicode(header):
|
|
default_charset = get_settings()['default_charset']
|
|
|
|
def _decode(value, encoding):
|
|
if isinstance(value, str):
|
|
return value
|
|
if not encoding or encoding == 'unknown-8bit':
|
|
encoding = default_charset
|
|
return value.decode(encoding, 'replace')
|
|
|
|
try:
|
|
return ''.join(
|
|
[
|
|
(
|
|
_decode(bytestr, encoding)
|
|
) for bytestr, encoding in email.header.decode_header(header)
|
|
]
|
|
)
|
|
except UnicodeDecodeError:
|
|
logger.exception(
|
|
'Errors encountered decoding header %s into encoding %s.',
|
|
header,
|
|
default_charset,
|
|
)
|
|
return header.decode(default_charset, 'replace')
|
|
|
|
|
|
def get_body_from_message(message, maintype, subtype):
|
|
"""
|
|
Fetchs the body message matching main/sub content type.
|
|
"""
|
|
body = ''
|
|
for part in message.walk():
|
|
if convert_header_to_unicode(part.get('content-disposition', '')).startswith('attachment;'):
|
|
continue
|
|
if part.get_content_maintype() == maintype and \
|
|
part.get_content_subtype() == subtype:
|
|
charset = part.get_content_charset()
|
|
this_part = part.get_payload(decode=True)
|
|
if charset:
|
|
try:
|
|
this_part = this_part.decode(charset, 'replace')
|
|
except LookupError:
|
|
this_part = this_part.decode('ascii', 'replace')
|
|
logger.warning(
|
|
'Unknown encoding %s encountered while decoding '
|
|
'text payload. Interpreting as ASCII with '
|
|
'replacement, but some data may not be '
|
|
'represented as the sender intended.',
|
|
charset
|
|
)
|
|
except ValueError:
|
|
this_part = this_part.decode('ascii', 'replace')
|
|
logger.warning(
|
|
'Error encountered while decoding text '
|
|
'payload from an incorrectly-constructed '
|
|
'e-mail; payload was converted to ASCII with '
|
|
'replacement, but some data may not be '
|
|
'represented as the sender intended.'
|
|
)
|
|
else:
|
|
this_part = this_part.decode('ascii', 'replace')
|
|
|
|
body += this_part
|
|
|
|
return body
|
|
|
|
|
|
def _get_save_path(filename, path):
|
|
if '%' in path:
|
|
path = datetime.datetime.utcnow().strftime(path)
|
|
|
|
return os.path.join(
|
|
path,
|
|
filename,
|
|
)
|
|
|
|
def get_attachment_save_path(instance, filename):
|
|
settings = get_settings()
|
|
|
|
path = settings['attachment_upload_to']
|
|
return _get_save_path(filename, path)
|
|
|
|
def get_original_message_save_path(instance, filename):
|
|
settings = get_settings()
|
|
|
|
path = settings['original_message_upload_to']
|
|
return _get_save_path(filename, path)
|