1
0
Fork 1
mirror of https://github.com/coddingtonbear/django-mailbox.git synced 2026-07-10 06:48:19 +02:00

changes main directory to mailbox2

This commit is contained in:
Joe 2021-02-07 22:54:52 -05:00
parent 010611dc55
commit 5177ea5c65
83 changed files with 4 additions and 53025 deletions

View file

@ -0,0 +1,10 @@
# all imports below are only used by external modules
# flake8: noqa
from django_mailbox.transports.imap import ImapTransport
from django_mailbox.transports.pop3 import Pop3Transport
from django_mailbox.transports.maildir import MaildirTransport
from django_mailbox.transports.mbox import MboxTransport
from django_mailbox.transports.babyl import BabylTransport
from django_mailbox.transports.mh import MHTransport
from django_mailbox.transports.mmdf import MMDFTransport
from django_mailbox.transports.gmail import GmailImapTransport

View file

@ -0,0 +1,6 @@
from mailbox import Babyl
from django_mailbox.transports.generic import GenericFileMailbox
class BabylTransport(GenericFileMailbox):
_variant = Babyl

View file

@ -0,0 +1,11 @@
import email
# Do *not* remove this, we need to use this in subclasses of EmailTransport
from email.errors import MessageParseError # noqa: F401
class EmailTransport:
def get_email_from_bytes(self, contents):
message = email.message_from_bytes(contents)
return message

View file

@ -0,0 +1,26 @@
# import sys
from .base import EmailTransport
class GenericFileMailbox(EmailTransport):
_variant = None
_path = None
def __init__(self, path):
super().__init__()
self._path = path
def get_instance(self):
return self._variant(self._path)
def get_message(self, condition=None):
repository = self.get_instance()
repository.lock()
for key, message in repository.items():
if condition and not condition(message):
continue
repository.remove(key)
yield message
repository.flush()
repository.unlock()

View file

@ -0,0 +1,51 @@
import logging
from django_mailbox.transports.imap import ImapTransport
logger = logging.getLogger(__name__)
class GmailImapTransport(ImapTransport):
def connect(self, username, password):
# Try to use oauth2 first. It's much safer
try:
self._connect_oauth(username)
except (TypeError, ValueError) as e:
logger.warning("Couldn't do oauth2 because %s" % e)
self.server = self.transport(self.hostname, self.port)
typ, msg = self.server.login(username, password)
self.server.select()
def _connect_oauth(self, username):
# username should be an email address that has already been authorized
# for gmail access
try:
from django_mailbox.google_utils import (
get_google_access_token,
fetch_user_info,
AccessTokenNotFound,
)
except ImportError:
raise ValueError("Install python-social-auth to use oauth2 auth for gmail")
access_token = None
while access_token is None:
try:
access_token = get_google_access_token(username)
google_email_address = fetch_user_info(username)["email"]
except TypeError:
# This means that the google process took too long
# Trying again is the right thing to do
pass
except AccessTokenNotFound:
raise ValueError(
"No Token available in python-social-auth for %s" % (username)
)
auth_string = "user={}\1auth=Bearer {}\1\1".format(
google_email_address, access_token
)
self.server = self.transport(self.hostname, self.port)
self.server.authenticate("XOAUTH2", lambda x: auth_string)
self.server.select()

View file

@ -0,0 +1,135 @@
import imaplib
import logging
from django.conf import settings
from .base import EmailTransport, MessageParseError
# By default, imaplib will raise an exception if it encounters more
# than 10k bytes; sometimes users attempt to consume mailboxes that
# have a more, and modern computers are skookum-enough to handle just
# a *few* more messages without causing any sort of problem.
imaplib._MAXLINE = 1000000
logger = logging.getLogger(__name__)
class ImapTransport(EmailTransport):
def __init__(
self,
hostname,
port=None,
ssl=False,
tls=False,
archive="",
folder=None,
):
self.max_message_size = getattr(
settings, "DJANGO_MAILBOX_MAX_MESSAGE_SIZE", False
)
self.integration_testing_subject = getattr(
settings, "DJANGO_MAILBOX_INTEGRATION_TESTING_SUBJECT", None
)
self.delete_message_from_server = getattr(
settings, "DJANGO_MAILBOX_DELETE_MESSAGE_FROM_SERVER", True
)
self.hostname = hostname
self.port = port
self.archive = archive
self.folder = folder
self.tls = tls
if ssl:
self.transport = imaplib.IMAP4_SSL
if not self.port:
self.port = 993
else:
self.transport = imaplib.IMAP4
if not self.port:
self.port = 143
def connect(self, username, password):
self.server = self.transport(self.hostname, self.port)
if self.tls:
self.server.starttls()
typ, msg = self.server.login(username, password)
if self.folder:
self.server.select(self.folder)
else:
self.server.select()
def _get_all_message_ids(self):
# Fetch all the message uids
response, message_ids = self.server.uid("search", None, "ALL")
message_id_string = message_ids[0].strip()
# Usually `message_id_string` will be a list of space-separated
# ids; we must make sure that it isn't an empty string before
# splitting into individual UIDs.
if message_id_string:
return message_id_string.decode().split(" ")
return []
def _get_small_message_ids(self, message_ids):
# Using existing message uids, get the sizes and
# return only those that are under the size
# limit
safe_message_ids = []
status, data = self.server.uid("fetch", ",".join(message_ids), "(RFC822.SIZE)")
for each_msg in data:
each_msg = each_msg.decode()
try:
uid = each_msg.split(" ")[2]
size = each_msg.split(" ")[4].rstrip(")")
if int(size) <= int(self.max_message_size):
safe_message_ids.append(uid)
except ValueError as e:
logger.warning("ValueError: {} working on {}".format(e, each_msg[0]))
pass
return safe_message_ids
def get_message(self, condition=None):
message_ids = self._get_all_message_ids()
if not message_ids:
return
# Limit the uids to the small ones if we care about that
if self.max_message_size:
message_ids = self._get_small_message_ids(message_ids)
if self.archive:
typ, folders = self.server.list(pattern=self.archive)
if folders[0] is None:
# If the archive folder does not exist, create it
self.server.create(self.archive)
for uid in message_ids:
try:
typ, msg_contents = self.server.uid("fetch", uid, "(RFC822)")
if not msg_contents:
continue
try:
message = self.get_email_from_bytes(msg_contents[0][1])
except TypeError:
# This happens if another thread/process deletes the
# message between our generating the ID list and our
# processing it here.
continue
if condition and not condition(message):
continue
yield message
except MessageParseError:
continue
if self.archive:
self.server.uid("copy", uid, self.archive)
if self.delete_message_from_server:
self.server.uid("store", uid, "+FLAGS", "(\\Deleted)")
self.server.expunge()
return

View file

@ -0,0 +1,9 @@
from mailbox import Maildir
from django_mailbox.transports.generic import GenericFileMailbox
class MaildirTransport(GenericFileMailbox):
_variant = Maildir
def get_instance(self):
return self._variant(self._path, None)

View file

@ -0,0 +1,6 @@
from mailbox import mbox
from django_mailbox.transports.generic import GenericFileMailbox
class MboxTransport(GenericFileMailbox):
_variant = mbox

View file

@ -0,0 +1,6 @@
from mailbox import MH
from django_mailbox.transports.generic import GenericFileMailbox
class MHTransport(GenericFileMailbox):
_variant = MH

View file

@ -0,0 +1,6 @@
from mailbox import MMDF
from django_mailbox.transports.generic import GenericFileMailbox
class MMDFTransport(GenericFileMailbox):
_variant = MMDF

View file

@ -0,0 +1,42 @@
from poplib import POP3, POP3_SSL
from .base import EmailTransport, MessageParseError
class Pop3Transport(EmailTransport):
def __init__(self, hostname, port=None, ssl=False):
self.hostname = hostname
self.port = port
if ssl:
self.transport = POP3_SSL
if not self.port:
self.port = 995
else:
self.transport = POP3
if not self.port:
self.port = 110
def connect(self, username, password):
self.server = self.transport(self.hostname, self.port)
self.server.user(username)
self.server.pass_(password)
def get_message_body(self, message_lines):
return bytes("\r\n", "ascii").join(message_lines)
def get_message(self, condition=None):
message_count = len(self.server.list()[1])
for i in range(message_count):
try:
msg_contents = self.get_message_body(self.server.retr(i + 1)[1])
message = self.get_email_from_bytes(msg_contents)
if condition and not condition(message):
continue
yield message
except MessageParseError:
continue
self.server.dele(i + 1)
self.server.quit()
return