From 9c4bf6adf42f0f9553ae11f24ffeb6f7cf39f374 Mon Sep 17 00:00:00 2001 From: drebs Date: Tue, 27 Sep 2016 20:44:53 -0300 Subject: [refactor] turn mail services into twisted services --- src/leap/bitmask/core/mail_services.py | 78 +++++++++++++-------------- src/leap/bitmask/core/service.py | 85 ++++++++++++++++++++---------- src/leap/bitmask/mail/imap/service/imap.py | 5 +- src/leap/bitmask/mail/incoming/service.py | 10 ++-- src/leap/bitmask/mail/smtp/__init__.py | 9 ++-- 5 files changed, 110 insertions(+), 77 deletions(-) diff --git a/src/leap/bitmask/core/mail_services.py b/src/leap/bitmask/core/mail_services.py index 04306b4..d2967f8 100644 --- a/src/leap/bitmask/core/mail_services.py +++ b/src/leap/bitmask/core/mail_services.py @@ -457,10 +457,11 @@ class StandardMailService(service.MultiService, HookableService): self.addService(IncomingMailService(self)) def startService(self): - log.msg('Starting Mail Service...') + log.msg('starting mail service') super(StandardMailService, self).startService() def stopService(self): + log.msg('stopping mail service') super(StandardMailService, self).stopService() def startInstance(self, userid, soledad, keymanager): @@ -485,9 +486,6 @@ class StandardMailService(service.MultiService, HookableService): d.addCallback(self._write_tokens_file, userid) return d - def stopInstance(self): - pass - # hooks def hook_on_new_keymanager_instance(self, **kw): @@ -564,20 +562,26 @@ class IMAPService(service.Service): name = 'imap' def __init__(self, soledad_sessions): - port, factory = imap.run_service(soledad_sessions) - - self._port = port - self._factory = factory self._soledad_sessions = soledad_sessions + self._port = None + self._factory = None super(IMAPService, self).__init__() def startService(self): - log.msg('Starting IMAP Service') + log.msg('starting imap service') + port, factory = imap.run_service( + self._soledad_sessions, factory=self._factory) + self._port = port + self._factory = factory super(IMAPService, self).startService() def stopService(self): - self._port.stopListening() - self._factory.doStop() + log.msg("stopping imap service") + if self._port: + self._port.stopListening() + self._port = None + if self._factory: + self._factory.doStop() super(IMAPService, self).stopService() @@ -589,35 +593,47 @@ class SMTPService(service.Service): basedir=DEFAULT_BASEDIR): self._basedir = os.path.expanduser(basedir) - port, factory = smtp.run_service( - soledad_sessions, keymanager_sessions, sendmail_opts) - self._port = port - self._factory = factory self._soledad_sessions = soledad_sessions self._keymanager_sessions = keymanager_sessions self._sendmail_opts = sendmail_opts + self._port = None + self._factory = None super(SMTPService, self).__init__() def startService(self): - log.msg('Starting SMTP Service') + log.msg('starting smtp service') + port, factory = smtp.run_service( + self._soledad_sessions, + self._keymanager_sessions, + self._sendmail_opts, + factory=self._factory) + self._port = port + self._factory = factory super(SMTPService, self).startService() def stopService(self): - # TODO cleanup all instances + log.msg('stopping smtp service') + if self._port: + self._port.stopListening() + self._port = None + if self._factory: + self._factory.doStop() super(SMTPService, self).stopService() -class IncomingMailService(service.Service): +class IncomingMailService(service.MultiService): + """ + Manage child services that check for incoming mail for individual users. + """ name = 'incoming_mail' def __init__(self, mail_service): super(IncomingMailService, self).__init__() self._mail = mail_service - self._instances = {} def startService(self): - log.msg('Starting IncomingMail Service') + log.msg('starting incoming mail service') super(IncomingMailService, self).startService() def stopService(self): @@ -625,25 +641,14 @@ class IncomingMailService(service.Service): # Individual accounts - # TODO IncomingMail *IS* already a service. - # I think we should better model the current Service - # as a startInstance inside a container, and get this - # multi-tenant service inside the leap.mail.incoming.service. - # ... or just simply make it a multiService and set per-user - # instances as Child of this parent. - def startInstance(self, userid): soledad = self._mail.get_soledad_session(userid) keymanager = self._mail.get_keymanager_session(userid) - log.msg('Starting Incoming Mail instance for %s' % userid) + log.msg('setting up incoming mail service for %s' % userid) self._start_incoming_mail_instance( keymanager, soledad, userid) - def stopInstance(self, userid): - # TODO toggle offline! - pass - def _start_incoming_mail_instance(self, keymanager, soledad, userid, start_sync=True): @@ -652,18 +657,13 @@ class IncomingMailService(service.Service): keymanager, soledad, inbox, userid, check_period=INCOMING_CHECK_PERIOD) - return incoming_mail - - def registerInstance(incoming_instance): - self._instances[userid] = incoming_instance - if start_sync: - incoming_instance.startService() + incoming_mail.setName(userid) + self.addService(incoming_mail) acc = Account(soledad, userid) d = acc.callWhenReady( lambda _: acc.get_collection_by_mailbox(INBOX_NAME)) d.addCallback(setUpIncomingMail) - d.addCallback(registerInstance) d.addErrback(log.err) return d diff --git a/src/leap/bitmask/core/service.py b/src/leap/bitmask/core/service.py index e449a8c..de99ea5 100644 --- a/src/leap/bitmask/core/service.py +++ b/src/leap/bitmask/core/service.py @@ -19,10 +19,9 @@ Bitmask-core Service. """ import json import resource -from os.path import join, abspath from twisted.internet import reactor -from twisted.python import log, logfile +from twisted.python import log from leap.bitmask import __version__ from leap.bitmask.core import configurable @@ -61,21 +60,19 @@ class BitmaskBackend(configurable.ConfigurableService): on_start(self.init_bonafide) if enabled('mail'): - on_start(self.init_soledad) - on_start(self.init_keymanager) - on_start(self.init_mail) + on_start(self._init_mail_services) if enabled('eip'): - on_start(self.init_eip) + on_start(self._init_eip) if enabled('zmq'): - on_start(self.init_zmq) + on_start(self._init_zmq) if enabled('web'): - on_start(self.init_web) + on_start(self._init_web) if enabled('websockets'): - on_start(self.init_websockets) + on_start(self._init_websockets) def init_events(self): event_server.ensure_server() @@ -93,47 +90,75 @@ class BitmaskBackend(configurable.ConfigurableService): bf.register_hook('on_bonafide_auth', listener='keymanager') bf.register_hook('on_bonafide_auth', listener='mail') - def init_soledad(self): + def _start_child_service(self, name): + log.msg('starting backend child service: %s' % name) + service = self.getServiceNamed(name) + if service: + service.startService() + + def _stop_child_service(self, name): + log.msg('stopping backend child service: %s' % name) + service = self.getServiceNamed(name) + if service: + service.stopService() + + def _init_mail_services(self): + self._init_soledad() + self._init_keymanager() + self._init_mail() + + def _start_mail_services(self): + self._start_child_service('soledad') + self._start_child_service('keymanager') + self._start_child_service('mail') + + def _stop_mail_services(self): + self._stop_child_service('mail') + self._stop_child_service('keymanager') + self._stop_child_service('soledad') + + def _init_soledad(self): service = mail_services.SoledadService - sol = self._maybe_start_service( + sol = self._maybe_init_service( 'soledad', service, self.basedir) if sol: sol.register_hook( 'on_new_soledad_instance', listener='keymanager') - def init_keymanager(self): + def _init_keymanager(self): service = mail_services.KeymanagerService - km = self._maybe_start_service( + km = self._maybe_init_service( 'keymanager', service, self.basedir) if km: km.register_hook('on_new_keymanager_instance', listener='mail') - def init_mail(self): + def _init_mail(self): service = mail_services.StandardMailService - self._maybe_start_service('mail', service, self.basedir) + self._maybe_init_service('mail', service, self.basedir) - def init_eip(self): + def _init_eip(self): # FIXME -- land EIP into leap.vpn pass - # self._maybe_start_service('eip', EIPService) + # self._maybe_init_service('eip', EIPService) - def init_zmq(self): + def _init_zmq(self): zs = _zmq.ZMQServerService(self) zs.setServiceParent(self) - def init_web(self): + def _init_web(self): service = _web.HTTPDispatcherService - self._maybe_start_service('web', service, self) + self._maybe_init_service('web', service, self) - def init_websockets(self): + def _init_websockets(self): from leap.bitmask.core import websocket ws = websocket.WebSocketsDispatcherService(self) ws.setServiceParent(self) - def _maybe_start_service(self, label, klass, *args, **kw): + def _maybe_init_service(self, label, klass, *args, **kw): try: self.getServiceNamed(label) except KeyError: + log.msg("initializing service: %s" % label) service = klass(*args, **kw) service.setName(label) service.setServiceParent(self) @@ -158,24 +183,26 @@ class BitmaskBackend(configurable.ConfigurableService): self.set_config('services', service, 'True') if service == 'mail': - self.init_soledad() - self.init_keymanager() - self.init_mail() + self._init_mail_services() + self._start_mail_services() elif service == 'eip': - self.init_eip() + self._init_eip() elif service == 'zmq': - self.init_zmq() + self._init_zmq() elif service == 'web': - self.init_web() + self._init_web() return {'enabled': 'ok'} def do_disable_service(self, service): assert service in self.service_names - # TODO -- should stop also? + + if service == 'mail': + self._stop_mail_services() + self.set_config('services', service, 'False') return {'disabled': 'ok'} diff --git a/src/leap/bitmask/mail/imap/service/imap.py b/src/leap/bitmask/mail/imap/service/imap.py index aac49b6..9ccff4a 100644 --- a/src/leap/bitmask/mail/imap/service/imap.py +++ b/src/leap/bitmask/mail/imap/service/imap.py @@ -158,7 +158,7 @@ class LeapIMAPFactory(ServerFactory): return ServerFactory.doStop(self) -def run_service(soledad_sessions, port=IMAP_PORT): +def run_service(soledad_sessions, port=IMAP_PORT, factory=None): """ Main entry point to run the service from the client. @@ -169,7 +169,8 @@ def run_service(soledad_sessions, port=IMAP_PORT): the factory for the protocol. :rtype: tuple """ - factory = LeapIMAPFactory(soledad_sessions) + if not factory: + factory = LeapIMAPFactory(soledad_sessions) try: interface = "localhost" diff --git a/src/leap/bitmask/mail/incoming/service.py b/src/leap/bitmask/mail/incoming/service.py index 05f9bb5..b4cdfcf 100644 --- a/src/leap/bitmask/mail/incoming/service.py +++ b/src/leap/bitmask/mail/incoming/service.py @@ -29,12 +29,15 @@ from StringIO import StringIO from urlparse import urlparse from twisted.application.service import Service +from twisted.application.service import IService from twisted.logger import Logger from twisted.python.failure import Failure from twisted.internet import defer, reactor from twisted.internet.task import LoopingCall from twisted.internet.task import deferLater +from zope.interface import implements + from leap.common.events import emit_async, catalog from leap.common.check import leap_assert, leap_assert_type from leap.common.mail import get_email_charset @@ -47,7 +50,7 @@ from leap.soledad.common.crypto import ENC_SCHEME_KEY, ENC_JSON_KEY from leap.soledad.common.errors import InvalidAuthTokenError -logger = Logger() +logger = Logger(__name__) MULTIPART_ENCRYPTED = "multipart/encrypted" MULTIPART_SIGNED = "multipart/signed" @@ -79,7 +82,8 @@ class IncomingMail(Service): This loop will sync the soledad db with the remote server and process all the documents found tagged as incoming mail. """ - # TODO implements IService? + + implements(IService) name = "IncomingMail" @@ -227,7 +231,7 @@ class IncomingMail(Service): def _signal_invalid_auth(failure): failure.trap(InvalidAuthTokenError) - logger.info('sync failed: %r' % failure) + logger.warn('sync failed because token has expired: %r' % failure) # if the token is invalid, send an event so the GUI can # disable mail and show an error message. emit_async(catalog.SOLEDAD_INVALID_AUTH_TOKEN, self._userid) diff --git a/src/leap/bitmask/mail/smtp/__init__.py b/src/leap/bitmask/mail/smtp/__init__.py index 4e0ac89..f8b8e65 100644 --- a/src/leap/bitmask/mail/smtp/__init__.py +++ b/src/leap/bitmask/mail/smtp/__init__.py @@ -32,7 +32,7 @@ SMTP_PORT = 2013 def run_service(soledad_sessions, keymanager_sessions, sendmail_opts, - port=SMTP_PORT): + port=SMTP_PORT, factory=None): """ Main entry point to run the service from the client. @@ -46,8 +46,9 @@ def run_service(soledad_sessions, keymanager_sessions, sendmail_opts, the factory for the protocol. :rtype: tuple """ - factory = SMTPFactory(soledad_sessions, keymanager_sessions, - sendmail_opts) + if not factory: + factory = SMTPFactory(soledad_sessions, keymanager_sessions, + sendmail_opts) try: interface = "localhost" @@ -60,7 +61,7 @@ def run_service(soledad_sessions, keymanager_sessions, sendmail_opts, tport = reactor.listenTCP(port, factory, interface=interface) emit_async(catalog.SMTP_SERVICE_STARTED, str(port)) - return factory, tport + return tport, factory except CannotListenError: logger.error("SMTP Service failed to start: " "cannot listen in port %s" % port) -- cgit v1.2.3