2016-02-15 19:22:26 -08:00
|
|
|
import imaplib
|
2014-05-29 23:41:11 -06:00
|
|
|
import logging
|
|
|
|
|
|
2014-08-22 19:25:14 -07:00
|
|
|
from django.conf import settings
|
2012-06-30 22:30:13 -07:00
|
|
|
|
2014-04-25 16:37:51 -07:00
|
|
|
from .base import EmailTransport, MessageParseError
|
2013-06-22 14:35:47 -07:00
|
|
|
|
2014-08-22 19:25:14 -07:00
|
|
|
|
|
|
|
|
logger = logging.getLogger(__name__)
|
2014-05-28 20:39:13 -06:00
|
|
|
|
2014-04-25 16:37:51 -07:00
|
|
|
|
|
|
|
|
class ImapTransport(EmailTransport):
|
2015-04-12 11:34:44 -07:00
|
|
|
def __init__(
|
2016-05-15 21:05:11 +02:00
|
|
|
self, hostname, port=None, ssl=False, tls=False,
|
|
|
|
|
archive='', folder=None,
|
2015-04-12 11:34:44 -07:00
|
|
|
):
|
2014-05-30 16:32:57 -07:00
|
|
|
self.max_message_size = getattr(
|
2014-05-29 23:41:11 -06:00
|
|
|
settings,
|
|
|
|
|
'DJANGO_MAILBOX_MAX_MESSAGE_SIZE',
|
|
|
|
|
False
|
|
|
|
|
)
|
2015-04-12 11:34:44 -07:00
|
|
|
self.integration_testing_subject = getattr(
|
|
|
|
|
settings,
|
|
|
|
|
'DJANGO_MAILBOX_INTEGRATION_TESTING_SUBJECT',
|
|
|
|
|
None
|
|
|
|
|
)
|
2012-06-30 22:30:13 -07:00
|
|
|
self.hostname = hostname
|
|
|
|
|
self.port = port
|
2014-05-23 09:17:14 +01:00
|
|
|
self.archive = archive
|
2015-03-16 16:55:12 +03:00
|
|
|
self.folder = folder
|
2016-05-15 21:05:11 +02:00
|
|
|
self.tls = tls
|
2012-06-30 22:30:13 -07:00
|
|
|
if ssl:
|
2016-02-15 19:22:26 -08:00
|
|
|
self.transport = imaplib.IMAP4_SSL
|
2012-06-30 22:30:13 -07:00
|
|
|
if not self.port:
|
|
|
|
|
self.port = 993
|
|
|
|
|
else:
|
2016-02-15 19:22:26 -08:00
|
|
|
self.transport = imaplib.IMAP4
|
2012-06-30 22:30:13 -07:00
|
|
|
if not self.port:
|
|
|
|
|
self.port = 143
|
|
|
|
|
|
|
|
|
|
def connect(self, username, password):
|
|
|
|
|
self.server = self.transport(self.hostname, self.port)
|
2016-05-15 21:05:11 +02:00
|
|
|
if self.tls:
|
|
|
|
|
self.server.starttls()
|
2012-06-30 22:30:13 -07:00
|
|
|
typ, msg = self.server.login(username, password)
|
2015-03-16 16:55:12 +03:00
|
|
|
|
|
|
|
|
if self.folder:
|
|
|
|
|
self.server.select(self.folder)
|
|
|
|
|
else:
|
|
|
|
|
self.server.select()
|
|
|
|
|
|
2024-02-28 11:42:21 -08:00
|
|
|
def close(self):
|
|
|
|
|
self.server.close()
|
|
|
|
|
self.server.logout()
|
|
|
|
|
|
2014-05-28 20:39:13 -06:00
|
|
|
def _get_all_message_ids(self):
|
|
|
|
|
# Fetch all the message uids
|
2014-05-29 23:41:11 -06:00
|
|
|
response, message_ids = self.server.uid('search', None, 'ALL')
|
2014-08-14 21:38:52 -07:00
|
|
|
message_id_string = message_ids[0].strip()
|
|
|
|
|
# Usually `message_id_string` will be a list of space-separated
|
|
|
|
|
# ids; we must make sure that it isn't an empty string before
|
|
|
|
|
# splitting into individual UIDs.
|
|
|
|
|
if message_id_string:
|
2015-04-12 11:34:44 -07:00
|
|
|
return message_id_string.decode().split(' ')
|
2014-08-14 21:38:52 -07:00
|
|
|
return []
|
2014-05-28 20:39:13 -06:00
|
|
|
|
|
|
|
|
def _get_small_message_ids(self, message_ids):
|
|
|
|
|
# Using existing message uids, get the sizes and
|
|
|
|
|
# return only those that are under the size
|
|
|
|
|
# limit
|
|
|
|
|
safe_message_ids = []
|
|
|
|
|
|
|
|
|
|
status, data = self.server.uid(
|
|
|
|
|
'fetch',
|
|
|
|
|
','.join(message_ids),
|
2014-05-29 23:41:11 -06:00
|
|
|
'(RFC822.SIZE)'
|
2014-05-28 20:39:13 -06:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
for each_msg in data:
|
2015-04-12 11:34:44 -07:00
|
|
|
each_msg = each_msg.decode()
|
2014-05-29 23:41:11 -06:00
|
|
|
try:
|
|
|
|
|
uid = each_msg.split(' ')[2]
|
|
|
|
|
size = each_msg.split(' ')[4].rstrip(')')
|
2014-05-30 16:32:57 -07:00
|
|
|
if int(size) <= int(self.max_message_size):
|
2014-05-29 23:41:11 -06:00
|
|
|
safe_message_ids.append(uid)
|
2014-05-29 23:47:48 -06:00
|
|
|
except ValueError as e:
|
2014-05-30 16:32:57 -07:00
|
|
|
logger.warning(
|
2019-10-15 05:31:13 +02:00
|
|
|
"ValueError: {} working on {}".format(e, each_msg[0])
|
2014-05-30 16:32:57 -07:00
|
|
|
)
|
2014-05-29 23:41:11 -06:00
|
|
|
pass
|
2014-05-28 20:39:13 -06:00
|
|
|
return safe_message_ids
|
|
|
|
|
|
2015-07-07 22:22:15 -07:00
|
|
|
def get_message(self, condition=None):
|
2014-05-28 20:39:13 -06:00
|
|
|
message_ids = self._get_all_message_ids()
|
|
|
|
|
|
2015-01-31 15:46:49 -08:00
|
|
|
if not message_ids:
|
|
|
|
|
return
|
|
|
|
|
|
2014-05-28 20:39:13 -06:00
|
|
|
# Limit the uids to the small ones if we care about that
|
2014-05-30 16:32:57 -07:00
|
|
|
if self.max_message_size:
|
2014-05-28 20:39:13 -06:00
|
|
|
message_ids = self._get_small_message_ids(message_ids)
|
|
|
|
|
|
2014-05-23 09:17:14 +01:00
|
|
|
if self.archive:
|
|
|
|
|
typ, folders = self.server.list(pattern=self.archive)
|
2014-05-25 12:57:38 -07:00
|
|
|
if folders[0] is None:
|
|
|
|
|
# If the archive folder does not exist, create it
|
|
|
|
|
self.server.create(self.archive)
|
2014-05-23 09:17:14 +01:00
|
|
|
|
2014-05-28 20:39:13 -06:00
|
|
|
for uid in message_ids:
|
2012-06-30 22:30:13 -07:00
|
|
|
try:
|
2014-05-28 20:39:13 -06:00
|
|
|
typ, msg_contents = self.server.uid('fetch', uid, '(RFC822)')
|
2015-04-12 12:21:48 -07:00
|
|
|
if not msg_contents:
|
|
|
|
|
continue
|
2015-07-07 23:09:41 -07:00
|
|
|
try:
|
|
|
|
|
message = self.get_email_from_bytes(msg_contents[0][1])
|
|
|
|
|
except TypeError:
|
|
|
|
|
# This happens if another thread/process deletes the
|
|
|
|
|
# message between our generating the ID list and our
|
|
|
|
|
# processing it here.
|
|
|
|
|
continue
|
2015-04-12 11:34:44 -07:00
|
|
|
|
2015-07-07 22:22:15 -07:00
|
|
|
if condition and not condition(message):
|
2015-04-12 11:34:44 -07:00
|
|
|
continue
|
|
|
|
|
|
2012-06-30 22:30:13 -07:00
|
|
|
yield message
|
2014-04-25 16:37:51 -07:00
|
|
|
except MessageParseError:
|
2012-06-30 22:30:13 -07:00
|
|
|
continue
|
2014-05-23 09:17:14 +01:00
|
|
|
|
|
|
|
|
if self.archive:
|
2014-05-28 20:39:13 -06:00
|
|
|
self.server.uid('copy', uid, self.archive)
|
2014-05-23 09:17:14 +01:00
|
|
|
|
2014-08-22 19:27:36 -07:00
|
|
|
self.server.uid('store', uid, "+FLAGS", "(\\Deleted)")
|
2012-06-30 22:30:13 -07:00
|
|
|
self.server.expunge()
|
|
|
|
|
return
|