mirror of
https://github.com/jwinterm/modoboa.git
synced 2026-10-18 05:51:26 +02:00
Fix https://github.com/modoboa/modoboa-sievefilters/issues/4 append imaputils to modoboa main repository
This commit is contained in:
@@ -235,6 +235,26 @@ class GeneralParametersForm(parameters.AdminParametersForm):
|
||||
widget=forms.Select(attrs={"class": "form-control"})
|
||||
)
|
||||
|
||||
sep4 = SeparatorField(label=_("IMAP settings"))
|
||||
|
||||
imap_server = forms.CharField(
|
||||
label=_("Server address"),
|
||||
initial="127.0.0.1",
|
||||
help_text=_("Address of your IMAP server")
|
||||
)
|
||||
|
||||
imap_secured = YesNoField(
|
||||
label=_("Use a secured connection"),
|
||||
initial="no",
|
||||
help_text=_("Use a secured connection to access IMAP server")
|
||||
)
|
||||
|
||||
imap_port = forms.IntegerField(
|
||||
label=_("Server port"),
|
||||
initial=143,
|
||||
help_text=_("Listening port of your IMAP server")
|
||||
)
|
||||
|
||||
# Visibility rules
|
||||
visibility_rules = {
|
||||
"ldap_sep": "authentication_type=ldap",
|
||||
|
||||
@@ -66,3 +66,12 @@ class PermDeniedException(ModoboaException):
|
||||
if self.msg:
|
||||
return _("Permission denied: %s" % self.msg)
|
||||
return _("Permission denied")
|
||||
|
||||
|
||||
class ImapError(ModoboaException):
|
||||
|
||||
def __init__(self, reason):
|
||||
self.reason = reason
|
||||
|
||||
def __str__(self):
|
||||
return str(self.reason)
|
||||
|
||||
@@ -0,0 +1,157 @@
|
||||
# coding: utf-8
|
||||
"""
|
||||
:mod:`fetch_parser` --- Simple parser for FETCH responses
|
||||
---------------------------------------------------------
|
||||
|
||||
The ``imaplib`` module doesn't parse IMAP responses, it returns raw
|
||||
values. This is pretty annoying when one FETCH command is issued to
|
||||
retrieve multiple attributes of a message.
|
||||
|
||||
This simple module tries to fix that *problem*.
|
||||
"""
|
||||
|
||||
|
||||
class ParseError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class Token(object):
|
||||
|
||||
def __init__(self, value):
|
||||
self.value = value
|
||||
|
||||
|
||||
class Literal(Token):
|
||||
|
||||
def __init__(self, value):
|
||||
super(Literal, self).__init__(value)
|
||||
self.next_token_len = int(self.value[1:-1])
|
||||
|
||||
|
||||
def parse_next_token(buf):
|
||||
"""Look for the next *token*
|
||||
|
||||
By *token*, I mean: *literal*, *quoted* or anything else until the
|
||||
next ' ' or ')' character (number, NIL and others should fall into
|
||||
this last category).
|
||||
|
||||
:param buf: the buffer to parse
|
||||
:return: the position of the token's last caracter into ``buf``
|
||||
"""
|
||||
end = -1
|
||||
klass = Token
|
||||
if buf[0] == '{':
|
||||
# Literal
|
||||
end = buf.find('}')
|
||||
klass = Literal
|
||||
elif buf[0] == '"':
|
||||
# quoted
|
||||
end = buf.find('"', 1)
|
||||
else:
|
||||
for pos, c in enumerate(buf):
|
||||
if c in [' ', ')']:
|
||||
end = pos - 1
|
||||
break
|
||||
if end == -1:
|
||||
raise ParseError(
|
||||
"End of buffer reached while looking for a token end")
|
||||
end += 1
|
||||
token = klass(buf[:end])
|
||||
return token, end
|
||||
|
||||
|
||||
def parse_fetch_response(data):
|
||||
"""Parse a FETCH response, previously issued by a UID command
|
||||
|
||||
We extract the message number, the UID and its value and consider
|
||||
what remains as the response data.
|
||||
|
||||
:param data: the data returned by the ``imaplib`` command
|
||||
:return: a dictionnary
|
||||
"""
|
||||
result = {}
|
||||
cpt = 0
|
||||
while cpt < len(data):
|
||||
content = ()
|
||||
while cpt < len(data) and data[cpt] != ')':
|
||||
if isinstance(data[cpt], str):
|
||||
# FIXME : probably an unsolicited response
|
||||
cpt += 1
|
||||
continue
|
||||
content += data[cpt]
|
||||
cpt += 1
|
||||
cpt += 1
|
||||
|
||||
buf = "".join(content)
|
||||
parts = buf.split(' ', 3)
|
||||
msgdef = result[int(parts[2])] = {}
|
||||
response = parts[3]
|
||||
|
||||
while len(response):
|
||||
if response.startswith('BODY') and response[4] == '[':
|
||||
end = response.find(']', 5)
|
||||
if response[end + 1] == '<':
|
||||
end = response.find('>', end + 1)
|
||||
end += 1
|
||||
else:
|
||||
end = response.find(' ')
|
||||
cmdname = response[:end]
|
||||
response = response[end + 1:]
|
||||
|
||||
end = 0
|
||||
if cmdname in ['BODY', 'BODYSTRUCTURE', 'FLAGS']:
|
||||
parendepth = 0
|
||||
instring = False
|
||||
for pos, c in enumerate(response):
|
||||
if not instring and c == '"':
|
||||
instring = True
|
||||
continue
|
||||
if instring and c == '"':
|
||||
if pos and response[pos - 1] != '\\':
|
||||
instring = False
|
||||
continue
|
||||
if not instring and c == '(':
|
||||
parendepth += 1
|
||||
continue
|
||||
if not instring and c == ')':
|
||||
parendepth -= 1
|
||||
if parendepth == 0:
|
||||
end = pos + 1
|
||||
break
|
||||
else:
|
||||
token, end = parse_next_token(response)
|
||||
if isinstance(token, Literal):
|
||||
response = response[end:]
|
||||
end = token.next_token_len
|
||||
|
||||
msgdef[cmdname] = response[:end]
|
||||
response = response[end + 1:]
|
||||
try:
|
||||
func = globals()["parse_%s" % cmdname.lower()]
|
||||
msgdef[cmdname] = func(msgdef[cmdname])[0]
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
return result
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# resp = [('855 (UID 46931 BODYSTRUCTURE ((("text" "plain" ("charset" "iso-8859-1") NIL NIL "quoted-printable" 886 32 NIL NIL NIL NIL)("text" "html" ("charset" "us-ascii") NIL NIL "quoted-printable" 1208 16 NIL NIL NIL NIL) "alternative" ("boundary" "----=_NextPart_001_0003_01CCC564.B2F64FF0") NIL NIL NIL)("application" "octet-stream" ("name" "Carte Verte_2.pdf") NIL NIL "base64" 285610 NIL ("attachment" ("filename" "Carte Verte_2.pdf")) NIL NIL) "mixed" ("boundary" "----=_NextPart_000_0002_01CCC564.B2F64FF0") NIL NIL NIL) BODY[HEADER.FIELDS (DATE FROM TO CC SUBJECT)] {153}', 'From: <Service.client10@maaf.fr>\r\nTo: <TONIO@NGYN.ORG>\r\nCc: \r\nSubject: Notre contact du 28/12/2011 - 192175092\r\nDate: Wed, 28 Dec 2011 13:29:17 +0100\r\n\r\n'), ')']
|
||||
|
||||
# resp = [('856 (UID 46936 BODYSTRUCTURE (("text" "plain" ("charset" "ISO-8859-1") NIL NIL "quoted-printable" 724 22 NIL NIL NIL NIL)("text" "html" ("charset" "ISO-8859-1") NIL NIL "quoted-printable" 2662 48 NIL NIL NIL NIL) "alternative" ("boundary" "----=_Part_1326887_254624357.1325083973970") NIL NIL NIL) BODY[HEADER.FIELDS (DATE FROM TO CC SUBJECT)] {258}', 'Date: Wed, 28 Dec 2011 15:52:53 +0100 (CET)\r\nFrom: =?ISO-8859-1?Q?Malakoff_M=E9d=E9ric?= <communication@communication.malakoffmederic.com>\r\nTo: Antoine Nguyen <tonio@ngyn.org>\r\nSubject: =?ISO-8859-1?Q?Votre_inscription_au_grand_Jeu_Malakoff_M=E9d=E9ric?=\r\n\r\n'), ')']
|
||||
|
||||
resp = [
|
||||
('856 (UID 11111 BODYSTRUCTURE ((("text" "plain" ("charset" "UTF-8") NIL NIL "7bit" 0 0 NIL NIL NIL NIL) "mixed" ("boundary" "----=_Part_407172_3159001.1321948277321") NIL NIL NIL)("application" "octet-stream" ("name" "26274308.pdf") NIL NIL "base64" 14906 NIL ("attachment" ("filename" "26274308.pdf")) NIL NIL) "mixed" ("boundary" "----=_Part_407171_9686991.1321948277321") NIL NIL NIL)',
|
||||
),
|
||||
')']
|
||||
|
||||
# resp = [('19 (UID 19 FLAGS (\\Seen) BODYSTRUCTURE (("text" "plain" ("charset" "ISO-8859-1" "format" "flowed") NIL NIL "7bit" 2 1 NIL NIL NIL NIL)("message" "rfc822" ("name*" "ISO-8859-1\'\'%5B%49%4E%53%43%52%49%50%54%49%4F%4E%5D%20%52%E9%63%E9%70%74%69%6F%6E%20%64%65%20%76%6F%74%72%65%20%64%6F%73%73%69%65%72%20%64%27%69%6E%73%63%72%69%70%74%69%6F%6E%20%46%72%65%65%20%48%61%75%74%20%44%E9%62%69%74") NIL NIL "8bit" 3632 ("Wed, 13 Dec 2006 20:30:02 +0100" {70}',
|
||||
# "[INSCRIPTION] R\xe9c\xe9ption de votre dossier d'inscription Free Haut D\xe9bit"),
|
||||
# (' (("Free Haut Debit" NIL "inscription" "freetelecom.fr")) (("Free Haut Debit" NIL "inscription" "freetelecom.fr")) ((NIL NIL "hautdebit" "freetelecom.fr")) ((NIL NIL "nguyen.antoine" "wanadoo.fr")) NIL NIL NIL "<20061213193125.9DA0919AC@dgroup2-2.proxad.net>") ("text" "plain" ("charset" "iso-8859-1") NIL NIL "8bit" 1428 38 NIL ("inline" NIL) NIL NIL) 76 NIL ("inline" ("filename*" "ISO-8859-1\'\'%5B%49%4E%53%43%52%49%50%54%49%4F%4E%5D%20%52%E9%63%E9%70%74%69%6F%6E%20%64%65%20%76%6F%74%72%65%20%64%6F%73%73%69%65%72%20%64%27%69%6E%73%63%72%69%70%74%69%6F%6E%20%46%72%65%65%20%48%61%75%74%20%44%E9%62%69%74")) NIL NIL) "mixed" ("boundary" "------------040706080908000209030901") NIL NIL NIL) BODY[HEADER.FIELDS (DATE FROM TO CC SUBJECT)] {266}',
|
||||
# 'Date: Tue, 19 Dec 2006 19:50:13 +0100\r\nFrom: Antoine Nguyen <nguyen.antoine@wanadoo.fr>\r\nTo: Antoine Nguyen <tonio@koalabs.org>\r\nSubject: [Fwd: [INSCRIPTION] =?ISO-8859-1?Q?R=E9c=E9ption_de_votre_?=\r\n =?ISO-8859-1?Q?dossier_d=27inscription_Free_Haut_D=E9bit=5D?=\r\n\r\n'), ')']
|
||||
|
||||
# resp = [('123 (UID 3 BODYSTRUCTURE (((("text" "plain" ("charset" "iso-8859-1") NIL NIL "quoted-printable" 1266 30 NIL NIL NIL NIL)("text" "html" ("charset" "iso-8859-1") NIL NIL "quoted-printable" 8830 227 NIL NIL NIL NIL) "alternative" ("boundary" "_000_152AC7ECD1F8AB43A9AD95DBDDCA3118082C09GKIMA24cmcicfr_") NIL NIL NIL)("image" "png" ("name" "image005.png") "<image005.png@01CC6CAA.4FADC490>" "image005.png" "base64" 7464 NIL ("inline" ("filename" "image005.png" "size" "5453" "creation-date" "Tue, 06 Sep 2011 13:33:49 GMT" "modification-date" "Tue, 06 Sep 2011 13:33:49 GMT")) NIL NIL)("image" "jpeg" ("name" "image006.jpg") "<image006.jpg@01CC6CAA.4FADC490>" "image006.jpg" "base64" 2492 NIL ("inline" ("filename" "image006.jpg" "size" "1819" "creation-date" "Tue, 06 Sep 2011 13:33:49 GMT" "modification-date" "Tue, 06 Sep 2011 13:33:49 GMT")) NIL NIL) "related" ("boundary" "_006_152AC7ECD1F8AB43A9AD95DBDDCA3118082C09GKIMA24cmcicfr_" "type" "multipart/alternative") NIL NIL NIL)("application" "pdf" ("name" "bilan assurance CIC.PDF") NIL "bilan assurance CIC.PDF" "base64" 459532 NIL ("attachment" ("filename" "bilan assurance CIC.PDF" "size" "335811" "creation-date" "Fri, 16 Sep 2011 12:45:23 GMT" "modification-date" "Fri, 16 Sep 2011 12:45:23 GMT")) NIL NIL)(("text" "plain" ("charset" "utf-8") NIL NIL "quoted-printable" 1389 29 NIL NIL NIL NIL)("text" "html" ("charset" "utf-8") NIL NIL "quoted-printable" 1457 27 NIL NIL NIL NIL) "alternative" ("boundary" "===============0775904800==") ("inline" NIL) NIL NIL) "mixed" ("boundary" "_007_152AC7ECD1F8AB43A9AD95DBDDCA3118082C09GKIMA24cmcicfr_") NIL ("fr-FR") NIL)',), ')']
|
||||
|
||||
# resp = [('856 (UID 11111 BODYSTRUCTURE ((("text" "plain" ("charset" "UTF-8") NIL NIL "7bit" 0 0 NIL NIL NIL NIL) "mixed" ("boundary" "----=_Part_407172_3159001.1321948277321") NIL NIL NIL)("application" "octet-stream" ("name" "26274308.pdf") NIL NIL "base64" 14906 NIL ("attachment" ("filename" "(26274308.pdf")) NIL NIL) "mixed" ("boundary" "----=_Part_407171_9686991.1321948277321") NIL NIL NIL)',), ')']
|
||||
|
||||
print parse_fetch_response(resp)
|
||||
@@ -0,0 +1,306 @@
|
||||
# coding: utf-8
|
||||
"""
|
||||
:mod:`imaputils` --- Extra IMAPv4 utilities
|
||||
-------------------------------------------
|
||||
"""
|
||||
import imaplib
|
||||
import re
|
||||
import ssl
|
||||
from functools import wraps
|
||||
|
||||
from django.utils.translation import ugettext as _
|
||||
|
||||
from modoboa.lib import parameters, imap_utf7 # NOQA
|
||||
from modoboa.lib.connections import ConnectionsManager
|
||||
from modoboa.lib.exceptions import InternalError
|
||||
|
||||
from .exceptions import ImapError
|
||||
from .fetch_parser import parse_fetch_response
|
||||
|
||||
# imaplib.Debug = 4
|
||||
|
||||
|
||||
class capability(object):
|
||||
|
||||
"""
|
||||
Simple decorator to check if the server presents the required
|
||||
capability. If not, a fallback method is called instead.
|
||||
|
||||
:param name: the capability name (upper case)
|
||||
:param fallback_method: a method's name
|
||||
"""
|
||||
|
||||
def __init__(self, name, fallback_method):
|
||||
self.name = name
|
||||
self.fallback_method = fallback_method
|
||||
|
||||
def __call__(self, method):
|
||||
@wraps(method)
|
||||
def wrapped_func(cls, *args, **kwargs):
|
||||
if self.name in cls.capabilities:
|
||||
return method(cls, *args, **kwargs)
|
||||
return getattr(cls, self.fallback_method)(cls, **kwargs)
|
||||
|
||||
return wrapped_func
|
||||
|
||||
|
||||
class IMAPconnector(object):
|
||||
|
||||
"""The IMAPv4 connector."""
|
||||
|
||||
__metaclass__ = ConnectionsManager
|
||||
|
||||
list_base_pattern = (
|
||||
r'\((?P<flags>.*?)\) "(?P<delimiter>.*)" "?(?P<name>[^"]*)"?'
|
||||
)
|
||||
list_response_pattern_literal = re.compile(
|
||||
r'\((?P<flags>.*?)\) "(?P<delimiter>.*)" \{(?P<namelen>\d+)\}')
|
||||
list_response_pattern = re.compile(list_base_pattern)
|
||||
listextended_response_pattern = \
|
||||
re.compile(list_base_pattern + r'\s*(?P<childinfo>.*)')
|
||||
|
||||
def __init__(self, user=None, password=None):
|
||||
self.__hdelimiter = None
|
||||
self.address = parameters.get_admin("IMAP_SERVER", "core")
|
||||
self.port = int(parameters.get_admin("IMAP_PORT", "core"))
|
||||
self.login(user, password)
|
||||
|
||||
def _cmd(self, name, *args, **kwargs):
|
||||
"""IMAP command wrapper
|
||||
|
||||
To simplify errors handling, this wrapper calls the
|
||||
appropriate method (``uid`` or FIXME) and then check the
|
||||
return code. If an error has occured, an ``ImapError``
|
||||
exception is raised.
|
||||
|
||||
For specific commands commands (FETCH, ...), the result is
|
||||
parsed using the IMAPclient module before being returned.
|
||||
|
||||
:param name: the command's name
|
||||
:return: the command's result
|
||||
"""
|
||||
if name in ['FETCH', 'SORT', 'STORE', 'COPY', 'SEARCH']:
|
||||
try:
|
||||
typ, data = self.m.uid(name, *args)
|
||||
except imaplib.IMAP4.error as e:
|
||||
raise ImapError(e)
|
||||
if typ == "NO":
|
||||
raise ImapError(data)
|
||||
if name == 'FETCH':
|
||||
return parse_fetch_response(data)
|
||||
return data
|
||||
|
||||
try:
|
||||
typ, data = self.m._simple_command(name, *args)
|
||||
except imaplib.IMAP4.error as e:
|
||||
raise ImapError(e)
|
||||
if typ == "NO":
|
||||
raise ImapError(data)
|
||||
if 'responses' not in kwargs:
|
||||
if name not in self.m.untagged_responses:
|
||||
return None
|
||||
return self.m.untagged_responses.pop(name)
|
||||
res = []
|
||||
for r in kwargs['responses']:
|
||||
if r not in self.m.untagged_responses:
|
||||
return None
|
||||
res.append(self.m.untagged_responses.pop(r))
|
||||
return res
|
||||
|
||||
@property
|
||||
def hdelimiter(self):
|
||||
"""Return the default hierachy delimiter.
|
||||
|
||||
This is a simple way to retrieve the default delimiter (see
|
||||
http://www.imapwiki.org/ClientImplementation/MailboxList).
|
||||
|
||||
:return: a string
|
||||
"""
|
||||
if self.__hdelimiter is None:
|
||||
data = self._cmd("LIST", "", "")
|
||||
m = self.list_response_pattern.match(data[0])
|
||||
if m is None:
|
||||
raise InternalError(_("Failed to retrieve hierarchy delimiter"))
|
||||
self.__hdelimiter = m.group('delimiter')
|
||||
return self.__hdelimiter
|
||||
|
||||
def refresh(self, user, password):
|
||||
"""Check if current connection needs a refresh
|
||||
|
||||
Is it really secure?
|
||||
"""
|
||||
if self.m is not None:
|
||||
try:
|
||||
self._cmd("NOOP")
|
||||
except ImapError:
|
||||
if hasattr(self, "current_mailbox"):
|
||||
del self.current_mailbox
|
||||
else:
|
||||
return
|
||||
|
||||
self.login(user, password)
|
||||
|
||||
def login(self, user, passwd):
|
||||
"""Custom login method
|
||||
|
||||
We connect to the server, issue a LOGIN command. If
|
||||
successfull, we try to record a eventuel CAPABILITY untagged
|
||||
response. Otherwise, we issue the command.
|
||||
|
||||
:param user: username
|
||||
:param passwd: password
|
||||
"""
|
||||
import socket
|
||||
|
||||
if isinstance(user, unicode):
|
||||
user = user.encode("utf-8")
|
||||
if isinstance(passwd, unicode):
|
||||
passwd = passwd.encode("utf-8")
|
||||
try:
|
||||
secured = parameters.get_admin("IMAP_SECURED", "core")
|
||||
if secured == "yes":
|
||||
self.m = imaplib.IMAP4_SSL(self.address, self.port)
|
||||
else:
|
||||
self.m = imaplib.IMAP4(self.address, self.port)
|
||||
except (socket.error, imaplib.IMAP4.error, ssl.SSLError) as error:
|
||||
raise ImapError(_("Connection to IMAP server failed: %s" % error))
|
||||
|
||||
data = self._cmd("LOGIN", user, passwd)
|
||||
self.m.state = "AUTH"
|
||||
if "CAPABILITY" in self.m.untagged_responses:
|
||||
self.capabilities = \
|
||||
self.m.untagged_responses.pop('CAPABILITY')[0].split()
|
||||
else:
|
||||
data = self._cmd("CAPABILITY")
|
||||
self.capabilities = data[0].split()
|
||||
|
||||
def logout(self):
|
||||
"""Logout from server."""
|
||||
try:
|
||||
self._cmd("CHECK")
|
||||
except ImapError:
|
||||
pass
|
||||
self._cmd("LOGOUT")
|
||||
del self.m
|
||||
self.m = None
|
||||
if hasattr(self, "current_mailbox"):
|
||||
del self.current_mailbox
|
||||
|
||||
def _listmboxes_simple(self, topmailbox='INBOX', mailboxes=None, **kwargs):
|
||||
# data = self._cmd("LIST", "", "*")
|
||||
if not mailboxes:
|
||||
mailboxes = []
|
||||
(status, data) = self.m.list()
|
||||
newmboxes = []
|
||||
for mb in data:
|
||||
flags, delimiter, name = self.list_response_pattern.match(
|
||||
mb).groups()
|
||||
name = name.strip('"').decode("imap4-utf-7")
|
||||
mdm_found = False
|
||||
for idx, mdm in enumerate(mailboxes):
|
||||
if mdm["name"] == name:
|
||||
mdm_found = True
|
||||
descr = mailboxes[idx]
|
||||
break
|
||||
if not mdm_found:
|
||||
descr = dict(name=name)
|
||||
newmboxes += [descr]
|
||||
|
||||
if re.search("\%s" % delimiter, name):
|
||||
parts = name.split(delimiter)
|
||||
if "path" not in descr:
|
||||
descr["path"] = parts[0]
|
||||
descr["sub"] = []
|
||||
if self._parse_mailbox_name(descr["sub"], parts[0], delimiter,
|
||||
parts[1:]):
|
||||
descr["class"] = "subfolders"
|
||||
continue
|
||||
|
||||
from operator import itemgetter
|
||||
mailboxes += sorted(newmboxes, key=itemgetter("name"))
|
||||
|
||||
@capability('LIST-EXTENDED', '_listmboxes_simple')
|
||||
def _listmboxes(self, topmailbox, mailboxes):
|
||||
"""Retrieve mailboxes list."""
|
||||
pattern = (
|
||||
"{0}{1}%".format(topmailbox.encode("imap4-utf-7"), self.hdelimiter)
|
||||
if topmailbox else "%"
|
||||
)
|
||||
resp = self._cmd("LIST", "", pattern, "RETURN", "(CHILDREN)")
|
||||
newmboxes = []
|
||||
for mb in resp:
|
||||
if not mb:
|
||||
continue
|
||||
if type(mb) in [str, unicode]:
|
||||
flags, delimiter, name, childinfo = \
|
||||
self.listextended_response_pattern.match(mb).groups()
|
||||
else:
|
||||
flags, delimiter, namelen = (
|
||||
self.list_response_pattern_literal.match(mb[0]).groups()
|
||||
)
|
||||
name = mb[1][0:int(namelen)]
|
||||
flags = flags.split(' ')
|
||||
name = name.decode("imap4-utf-7")
|
||||
mdm_found = False
|
||||
for idx, mdm in enumerate(mailboxes):
|
||||
if mdm["name"] == name:
|
||||
mdm_found = True
|
||||
descr = mailboxes[idx]
|
||||
break
|
||||
if not mdm_found:
|
||||
descr = dict(name=name)
|
||||
newmboxes += [descr]
|
||||
|
||||
if r'\Marked' in flags or r'\UnMarked' not in flags:
|
||||
descr["send_status"] = True
|
||||
if r'\HasChildren' in flags:
|
||||
if r'\NonExistent' in flags:
|
||||
descr["removed"] = True
|
||||
descr["path"] = name
|
||||
descr["sub"] = []
|
||||
|
||||
from operator import itemgetter
|
||||
mailboxes += sorted(newmboxes, key=itemgetter("name"))
|
||||
|
||||
def getmboxes(
|
||||
self, user, topmailbox=''):
|
||||
"""Returns a list of mailboxes for a particular user
|
||||
|
||||
By default, only the first level of mailboxes under
|
||||
``topmailbox`` is returned.
|
||||
|
||||
:param user: a ``User`` instance
|
||||
:param topmailbox: the mailbox where to start in the tree
|
||||
:return: a list
|
||||
"""
|
||||
if topmailbox:
|
||||
md_mailboxes = []
|
||||
else:
|
||||
md_mailboxes = [
|
||||
{"name": "INBOX", "class": "fa fa-inbox"},
|
||||
{"name": parameters.get_user(user, "DRAFTS_FOLDER"),
|
||||
"class": "fa fa-file"},
|
||||
{"name": 'Junk', "class": "fa fa-fire"},
|
||||
{"name": parameters.get_user(user, "SENT_FOLDER"),
|
||||
"class": "fa fa-envelope"},
|
||||
{"name": parameters.get_user(user, "TRASH_FOLDER"),
|
||||
"class": "fa fa-trash"}
|
||||
]
|
||||
self._listmboxes(topmailbox, md_mailboxes)
|
||||
|
||||
return md_mailboxes
|
||||
|
||||
|
||||
def get_imapconnector(request):
|
||||
"""Simple shortcut to create a connector
|
||||
|
||||
:param request: a ``Request`` object
|
||||
"""
|
||||
imapc = IMAPconnector(user=request.user.username,
|
||||
password=request.session["password"])
|
||||
return imapc
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import doctest
|
||||
doctest.testmod()
|
||||
Reference in New Issue
Block a user