summaryrefslogtreecommitdiff
path: root/src/leap/mail/imap/service/imap.py
diff options
context:
space:
mode:
Diffstat (limited to 'src/leap/mail/imap/service/imap.py')
-rw-r--r--src/leap/mail/imap/service/imap.py189
1 files changed, 25 insertions, 164 deletions
diff --git a/src/leap/mail/imap/service/imap.py b/src/leap/mail/imap/service/imap.py
index ad22da6..5487cfc 100644
--- a/src/leap/mail/imap/service/imap.py
+++ b/src/leap/mail/imap/service/imap.py
@@ -17,17 +17,12 @@
"""
Imap service initialization
"""
-from copy import copy
-
import logging
+import os
from twisted.internet.protocol import ServerFactory
-from twisted.internet.defer import maybeDeferred
from twisted.internet.error import CannotListenError
-from twisted.internet.task import deferLater
from twisted.mail import imap4
-from twisted.python import log
-from twisted import cred
logger = logging.getLogger(__name__)
@@ -36,6 +31,9 @@ from leap.common.check import leap_assert, leap_assert_type, leap_check
from leap.keymanager import KeyManager
from leap.mail.imap.account import SoledadBackedAccount
from leap.mail.imap.fetch import LeapIncomingMail
+from leap.mail.imap.memorystore import MemoryStore
+from leap.mail.imap.server import LeapIMAPServer
+from leap.mail.imap.soledadstore import SoledadStore
from leap.soledad.client import Soledad
# The default port in which imap service will run
@@ -47,7 +45,6 @@ INCOMING_CHECK_PERIOD = 60
from leap.common.events.events_pb2 import IMAP_SERVICE_STARTED
from leap.common.events.events_pb2 import IMAP_SERVICE_FAILED_TO_START
-from leap.common.events.events_pb2 import IMAP_CLIENT_LOGIN
######################################################
# Temporary workaround for RecursionLimit when using
@@ -68,160 +65,9 @@ except Exception:
######################################################
-
-class LeapIMAPServer(imap4.IMAP4Server):
- """
- An IMAP4 Server with mailboxes backed by soledad
- """
- def __init__(self, *args, **kwargs):
- # pop extraneous arguments
- soledad = kwargs.pop('soledad', None)
- uuid = kwargs.pop('uuid', None)
- userid = kwargs.pop('userid', None)
- leap_assert(soledad, "need a soledad instance")
- leap_assert_type(soledad, Soledad)
- leap_assert(uuid, "need a user in the initialization")
-
- self._userid = userid
-
- # initialize imap server!
- imap4.IMAP4Server.__init__(self, *args, **kwargs)
-
- # we should initialize the account here,
- # but we move it to the factory so we can
- # populate the test account properly (and only once
- # per session)
-
- def lineReceived(self, line):
- """
- Attempt to parse a single line from the server.
-
- :param line: the line from the server, without the line delimiter.
- :type line: str
- """
- if self.theAccount.closed is True and self.state != "unauth":
- log.msg("Closing the session. State: unauth")
- self.state = "unauth"
-
- if "login" in line.lower():
- # avoid to log the pass, even though we are using a dummy auth
- # by now.
- msg = line[:7] + " [...]"
- else:
- msg = copy(line)
- log.msg('rcv (%s): %s' % (self.state, msg))
- imap4.IMAP4Server.lineReceived(self, line)
-
- def authenticateLogin(self, username, password):
- """
- Lookup the account with the given parameters, and deny
- the improper combinations.
-
- :param username: the username that is attempting authentication.
- :type username: str
- :param password: the password to authenticate with.
- :type password: str
- """
- # XXX this should use portal:
- # return portal.login(cred.credentials.UsernamePassword(user, pass)
- if username != self._userid:
- # bad username, reject.
- raise cred.error.UnauthorizedLogin()
- # any dummy password is allowed so far. use realm instead!
- leap_events.signal(IMAP_CLIENT_LOGIN, "1")
- return imap4.IAccount, self.theAccount, lambda: None
-
- def do_FETCH(self, tag, messages, query, uid=0):
- """
- Overwritten fetch dispatcher to use the fast fetch_flags
- method
- """
- from twisted.internet import reactor
- if not query:
- self.sendPositiveResponse(tag, 'FETCH complete')
- return # XXX ???
-
- cbFetch = self._IMAP4Server__cbFetch
- ebFetch = self._IMAP4Server__ebFetch
-
- if len(query) == 1 and str(query[0]) == "flags":
- self._oldTimeout = self.setTimeout(None)
- # no need to call iter, we get a generator
- maybeDeferred(
- self.mbox.fetch_flags, messages, uid=uid
- ).addCallback(
- cbFetch, tag, query, uid
- ).addErrback(ebFetch, tag)
- elif len(query) == 1 and str(query[0]) == "rfc822.header":
- self._oldTimeout = self.setTimeout(None)
- # no need to call iter, we get a generator
- maybeDeferred(
- self.mbox.fetch_headers, messages, uid=uid
- ).addCallback(
- cbFetch, tag, query, uid
- ).addErrback(ebFetch, tag)
- else:
- self._oldTimeout = self.setTimeout(None)
- # no need to call iter, we get a generator
- maybeDeferred(
- self.mbox.fetch, messages, uid=uid
- ).addCallback(
- cbFetch, tag, query, uid
- ).addErrback(
- ebFetch, tag)
-
- deferLater(reactor,
- 2, self.mbox.unset_recent_flags, messages)
- deferLater(reactor, 1, self.mbox.signal_unread_to_ui)
-
- select_FETCH = (do_FETCH, imap4.IMAP4Server.arg_seqset,
- imap4.IMAP4Server.arg_fetchatt)
-
- def do_COPY(self, tag, messages, mailbox, uid=0):
- from twisted.internet import reactor
- imap4.IMAP4Server.do_COPY(self, tag, messages, mailbox, uid)
- deferLater(reactor,
- 2, self.mbox.unset_recent_flags, messages)
- deferLater(reactor, 1, self.mbox.signal_unread_to_ui)
-
- select_COPY = (do_COPY, imap4.IMAP4Server.arg_seqset,
- imap4.IMAP4Server.arg_astring)
-
- def notifyNew(self, ignored):
- """
- Notify new messages to listeners.
- """
- self.mbox.notify_new()
-
- def _cbSelectWork(self, mbox, cmdName, tag):
- """
- Callback for selectWork, patched to avoid conformance errors due to
- incomplete UIDVALIDITY line.
- """
- if mbox is None:
- self.sendNegativeResponse(tag, 'No such mailbox')
- return
- if '\\noselect' in [s.lower() for s in mbox.getFlags()]:
- self.sendNegativeResponse(tag, 'Mailbox cannot be selected')
- return
-
- flags = mbox.getFlags()
- self.sendUntaggedResponse(str(mbox.getMessageCount()) + ' EXISTS')
- self.sendUntaggedResponse(str(mbox.getRecentCount()) + ' RECENT')
- self.sendUntaggedResponse('FLAGS (%s)' % ' '.join(flags))
-
- # Patched -------------------------------------------------------
- # imaptest was complaining about the incomplete line, we're adding
- # "UIDs valid" here.
- self.sendPositiveResponse(
- None, '[UIDVALIDITY %d] UIDs valid' % mbox.getUIDValidity())
- # ----------------------------------------------------------------
-
- s = mbox.isWriteable() and 'READ-WRITE' or 'READ-ONLY'
- mbox.addListener(self)
- self.sendPositiveResponse(tag, '[%s] %s successful' % (s, cmdName))
- self.state = 'select'
- self.mbox = mbox
+DO_MANHOLE = os.environ.get("LEAP_MAIL_MANHOLE", None)
+if DO_MANHOLE:
+ from leap.mail.imap.service import manhole
class IMAPAuthRealm(object):
@@ -256,11 +102,16 @@ class LeapIMAPFactory(ServerFactory):
self._uuid = uuid
self._userid = userid
self._soledad = soledad
+ self._memstore = MemoryStore(
+ permanent_store=SoledadStore(soledad))
theAccount = SoledadBackedAccount(
- uuid, soledad=soledad)
+ uuid, soledad=soledad,
+ memstore=self._memstore)
self.theAccount = theAccount
+ # XXX how to pass the store along?
+
def buildProtocol(self, addr):
"Return a protocol suitable for the job."
imapProtocol = LeapIMAPServer(
@@ -281,6 +132,8 @@ def run_service(*args, **kwargs):
the reactor when starts listening, and the factory for
the protocol.
"""
+ from twisted.internet import reactor
+
leap_assert(len(args) == 2)
soledad, keymanager = args
leap_assert_type(soledad, Soledad)
@@ -295,8 +148,6 @@ def run_service(*args, **kwargs):
uuid = soledad._get_uuid()
factory = LeapIMAPFactory(uuid, userid, soledad)
- from twisted.internet import reactor
-
try:
tport = reactor.listenTCP(port, factory,
interface="localhost")
@@ -317,6 +168,16 @@ def run_service(*args, **kwargs):
else:
# all good.
# (the caller has still to call fetcher.start_loop)
+
+ if DO_MANHOLE:
+ # TODO get pass from env var.too.
+ manhole_factory = manhole.getManholeFactory(
+ {'f': factory,
+ 'a': factory.theAccount,
+ 'gm': factory.theAccount.getMailbox},
+ "boss", "leap")
+ reactor.listenTCP(manhole.MANHOLE_PORT, manhole_factory,
+ interface="127.0.0.1")
logger.debug("IMAP4 Server is RUNNING in port %s" % (port,))
leap_events.signal(IMAP_SERVICE_STARTED, str(port))
return fetcher, tport, factory