# -*- coding: utf-8 -*- # imap.py # Copyright (C) 2013-2015 LEAP # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . """ IMAP service initialization """ import logging import os from twisted.internet import reactor from twisted.internet.error import CannotListenError from twisted.internet.protocol import ServerFactory from twisted.mail import imap4 from twisted.python import log logger = logging.getLogger(__name__) from leap.common import events as leap_events from leap.common.check import leap_assert, leap_assert_type, leap_check from leap.mail.imap.account import IMAPAccount from leap.mail.imap.server import LEAPIMAPServer from leap.mail.incoming import IncomingMail from leap.soledad.client import Soledad from leap.common.events.events_pb2 import IMAP_SERVICE_STARTED from leap.common.events.events_pb2 import IMAP_SERVICE_FAILED_TO_START DO_MANHOLE = os.environ.get("LEAP_MAIL_MANHOLE", None) if DO_MANHOLE: from leap.mail.imap.service import manhole DO_PROFILE = os.environ.get("LEAP_PROFILE", None) if DO_PROFILE: import cProfile log.msg("Starting PROFILING...") PROFILE_DAT = "/tmp/leap_mail_profile.pstats" pr = cProfile.Profile() pr.enable() # The default port in which imap service will run IMAP_PORT = 1984 class IMAPAuthRealm(object): """ Dummy authentication realm. Do not use in production! """ theAccount = None def requestAvatar(self, avatarId, mind, *interfaces): return imap4.IAccount, self.theAccount, lambda: None class LeapIMAPFactory(ServerFactory): """ Factory for a IMAP4 server with soledad remote sync and gpg-decryption capabilities. """ def __init__(self, uuid, userid, soledad): """ Initializes the server factory. :param uuid: user uuid :type uuid: str :param userid: user id (user@provider.org) :type userid: str :param soledad: soledad instance :type soledad: Soledad """ self._uuid = uuid self._userid = userid self._soledad = soledad theAccount = IMAPAccount(uuid, soledad) self.theAccount = theAccount # XXX how to pass the store along? def buildProtocol(self, addr): """ Return a protocol suitable for the job. :param addr: remote ip address :type addr: str """ # XXX addr not used??! imapProtocol = LEAPIMAPServer( uuid=self._uuid, userid=self._userid, soledad=self._soledad) imapProtocol.theAccount = self.theAccount imapProtocol.factory = self return imapProtocol def doStop(self): """ Stops imap service (fetcher, factory and port). """ # TODO should wait for all the pending deferreds, # the twisted way! if DO_PROFILE: log.msg("Stopping PROFILING") pr.disable() pr.dump_stats(PROFILE_DAT) return ServerFactory.doStop(self) def run_service(*args, **kwargs): """ Main entry point to run the service from the client. :returns: the port as returned by the reactor when starts listening, and the factory for the protocol. """ leap_assert(len(args) == 2) soledad = args leap_assert_type(soledad, Soledad) port = kwargs.get('port', IMAP_PORT) userid = kwargs.get('userid', None) leap_check(userid is not None, "need an user id") uuid = soledad.uuid factory = LeapIMAPFactory(uuid, userid, soledad) try: tport = reactor.listenTCP(port, factory, interface="localhost") except CannotListenError: logger.error("IMAP Service failed to start: " "cannot listen in port %s" % (port,)) except Exception as exc: logger.error("Error launching IMAP service: %r" % (exc,)) 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)) # FIXME -- change service signature return tport, factory # not ok, signal error. leap_events.signal(IMAP_SERVICE_FAILED_TO_START, str(port))