From 2f57c31cda8208340322efd5c02f8b9120ce29aa Mon Sep 17 00:00:00 2001 From: Ivan Alejandro Date: Tue, 6 May 2014 16:16:40 -0300 Subject: Separate imap/smtp logic from conductor. --- src/leap/bitmask/backend.py | 33 +++++++ src/leap/bitmask/gui/mainwindow.py | 26 +----- src/leap/bitmask/services/mail/conductor.py | 104 +++++++++-------------- src/leap/bitmask/services/mail/imapcontroller.py | 98 +++++++++++++++++++++ 4 files changed, 172 insertions(+), 89 deletions(-) create mode 100644 src/leap/bitmask/services/mail/imapcontroller.py diff --git a/src/leap/bitmask/backend.py b/src/leap/bitmask/backend.py index 327131b3..fd09929a 100644 --- a/src/leap/bitmask/backend.py +++ b/src/leap/bitmask/backend.py @@ -46,6 +46,8 @@ from leap.bitmask.services.eip.eipbootstrapper import EIPBootstrapper from leap.bitmask.services.eip import vpnlauncher, vpnprocess from leap.bitmask.services.eip import linuxvpnlauncher, darwinvpnlauncher +from leap.bitmask.services.mail import imap + from leap.bitmask.services.soledad.soledadbootstrapper import \ SoledadBootstrapper @@ -632,6 +634,37 @@ class Soledad(object): soledad.close() +class Mail(object): + """ + Interfaces with setup and launch of Mail. + """ + + zope.interface.implements(ILEAPComponent) + + def __init__(self, signaler=None): + """ + Constructor for the Mail component. + + :param signaler: Object in charge of handling communication + back to the frontend + :type signaler: Signaler + """ + self.key = "mail" + self._signaler = signaler + + def start_smtp_service(self): + pass + + def start_imap_service(self, offline=False): + pass + + def stop_smtp_service(self): + pass + + def stop_imap_service(self): + pass + + class Authenticate(object): """ Interfaces with setup and bootstrapping operations for a provider diff --git a/src/leap/bitmask/gui/mainwindow.py b/src/leap/bitmask/gui/mainwindow.py index cceb1efe..bf76f574 100644 --- a/src/leap/bitmask/gui/mainwindow.py +++ b/src/leap/bitmask/gui/mainwindow.py @@ -87,7 +87,6 @@ class MainWindow(QtGui.QMainWindow): new_updates = QtCore.Signal(object) raise_window = QtCore.Signal([]) soledad_ready = QtCore.Signal([]) - mail_client_logged_in = QtCore.Signal([]) logout = QtCore.Signal([]) # We use this flag to detect abnormal terminations @@ -123,9 +122,6 @@ class MainWindow(QtGui.QMainWindow): register(signal=proto.RAISE_WINDOW, callback=self._on_raise_window_event, reqcbk=lambda req, resp: None) # make rpc call async - register(signal=proto.IMAP_CLIENT_LOGIN, - callback=self._on_mail_client_logged_in, - reqcbk=lambda req, resp: None) # make rpc call async # end register leap events #################################### self._quit_callback = quit_callback @@ -265,7 +261,6 @@ class MainWindow(QtGui.QMainWindow): # XXX should connect to mail_conductor.start_mail_service instead self.soledad_ready.connect(self._start_smtp_bootstrapping) self.soledad_ready.connect(self._start_imap_service) - self.mail_client_logged_in.connect(self._fetch_incoming_mail) self.logout.connect(self._stop_imap_service) self.logout.connect(self._stop_smtp_service) @@ -1373,7 +1368,7 @@ class MainWindow(QtGui.QMainWindow): password = unicode(self._login_widget.get_password()) provider_domain = self._login_widget.get_selected_provider() - if flags.OFFLINE is True: + if flags.OFFLINE: self._provisional_provider_config.load( provider.get_provider_path(provider_domain)) @@ -1455,34 +1450,19 @@ class MainWindow(QtGui.QMainWindow): # in the mail state machine so it shows that imap is active # (but not smtp since it's not yet ready for offline use) start_fun = self._mail_conductor.start_imap_service - if flags.OFFLINE is True: + if flags.OFFLINE: provider_domain = self._login_widget.get_selected_provider() self._provider_config.load( provider.get_provider_path(provider_domain)) provides_mx = self._provider_config.provides_mx() - if flags.OFFLINE is True and provides_mx: + if flags.OFFLINE and provides_mx: start_fun() return if self._provides_mx_and_enabled(): start_fun() - def _on_mail_client_logged_in(self, req): - """ - Triggers qt signal when client login event is received. - """ - self.mail_client_logged_in.emit() - - @QtCore.Slot() - def _fetch_incoming_mail(self): - """ - TRIGGERS: - self.mail_client_logged_in - """ - # TODO connect signal directly!!! - self._mail_conductor.fetch_incoming_mail() - @QtCore.Slot() def _stop_imap_service(self): """ diff --git a/src/leap/bitmask/services/mail/conductor.py b/src/leap/bitmask/services/mail/conductor.py index b4e97ac1..aa22afe4 100644 --- a/src/leap/bitmask/services/mail/conductor.py +++ b/src/leap/bitmask/services/mail/conductor.py @@ -19,15 +19,15 @@ Mail Services Conductor """ import logging -from zope.proxy import sameProxiedObjects +from twisted.internet import threads +from leap.bitmask.config import flags from leap.bitmask.gui import statemachines from leap.bitmask.services.mail import connection as mail_connection -from leap.bitmask.services.mail import imap from leap.bitmask.services.mail.smtpbootstrapper import SMTPBootstrapper from leap.bitmask.services.mail.smtpconfig import SMTPConfig +from leap.bitmask.services.mail.imapcontroller import IMAPController -from leap.common.check import leap_assert from leap.common.events import events_pb2 as leap_events from leap.common.events import register as leap_register @@ -39,15 +39,20 @@ class IMAPControl(object): """ Methods related to IMAP control. """ - def __init__(self): + def __init__(self, soledad, keymanager): """ Initializes smtp variables. + + :param soledad: a transparent proxy that eventually will point to a + Soledad Instance. + :type soledad: zope.proxy.ProxyBase + :param keymanager: a transparent proxy that eventually will point to a + Keymanager Instance. + :type keymanager: zope.proxy.ProxyBase """ self.imap_machine = None - self.imap_service = None - self.imap_port = None - self.imap_factory = None self.imap_connection = None + self._imap_controller = IMAPController(soledad, keymanager) leap_register(signal=leap_events.IMAP_SERVICE_STARTED, callback=self._handle_imap_events, @@ -55,10 +60,13 @@ class IMAPControl(object): leap_register(signal=leap_events.IMAP_SERVICE_FAILED_TO_START, callback=self._handle_imap_events, reqcbk=lambda req, resp: None) + leap_register(signal=leap_events.IMAP_CLIENT_LOGIN, + callback=self._handle_imap_events, + reqcbk=lambda req, resp: None) def set_imap_connection(self, imap_connection): """ - Sets the imap connection to an initialized connection. + Set the imap connection to an initialized connection. :param imap_connection: an initialized imap connection :type imap_connection: IMAPConnection instance. @@ -67,65 +75,23 @@ class IMAPControl(object): def start_imap_service(self): """ - Starts imap service. + Start imap service. """ - from leap.bitmask.config import flags - - logger.debug('Starting imap service') - leap_assert(not sameProxiedObjects(self._soledad, None), - "We need a non-null soledad for initializing imap service") - leap_assert(not sameProxiedObjects(self._keymanager, None), - "We need a non-null keymanager for initializing imap " - "service") - - offline = flags.OFFLINE - self.imap_service, self.imap_port, \ - self.imap_factory = imap.start_imap_service( - self._soledad, - self._keymanager, - userid=self.userid, - offline=offline) - - if offline is False: - logger.debug("Starting loop") - self.imap_service.start_loop() + threads.deferToThread(self._imap_controller.start_imap_service, + self.userid, flags.OFFLINE) def stop_imap_service(self, cv): """ - Stops imap service (fetcher, factory and port). + Stop imap service (fetcher, factory and port). :param cv: A condition variable to which we can signal when imap indeed stops. :type cv: threading.Condition """ self.imap_connection.qtsigs.disconnecting_signal.emit() - # TODO We should homogenize both services. - if self.imap_service is not None: - logger.debug('Stopping imap service.') - # Stop the loop call in the fetcher - self.imap_service.stop() - self.imap_service = None - # Stop listening on the IMAP port - self.imap_port.stopListening() - # Stop the protocol - self.imap_factory.theAccount.closed = True - self.imap_factory.doStop(cv) - else: - # main window does not have to wait because there's no service to - # be stopped, so we release the condition variable - cv.acquire() - cv.notify() - cv.release() - - def fetch_incoming_mail(self): - """ - Fetches incoming mail. - """ - if self.imap_service: - logger.debug('Client connected, fetching mail...') - self.imap_service.fetch() - - # handle events + logger.debug('Stopping imap service.') + + self._imap_controller.stop_imap_service(cv) def _handle_imap_events(self, req): """ @@ -135,25 +101,31 @@ class IMAPControl(object): :type req: leap.common.events.events_pb2.SignalRequest """ if req.event == leap_events.IMAP_SERVICE_STARTED: - self.on_imap_connected() + self._on_imap_connected() elif req.event == leap_events.IMAP_SERVICE_FAILED_TO_START: - self.on_imap_failed() + self._on_imap_failed() + elif req.event == leap_events.IMAP_CLIENT_LOGIN: + self._on_mail_client_logged_in() - # emit connection signals + def _on_mail_client_logged_in(self): + """ + On mail client logged in, fetch incoming mail. + """ + self._controller.imap_service_fetch() - def on_imap_connecting(self): + def _on_imap_connecting(self): """ Callback for IMAP connecting state. """ self.imap_connection.qtsigs.connecting_signal.emit() - def on_imap_connected(self): + def _on_imap_connected(self): """ Callback for IMAP connected state. """ self.imap_connection.qtsigs.connected_signal.emit() - def on_imap_failed(self): + def _on_imap_failed(self): """ Callback for IMAP failed state. """ @@ -197,7 +169,8 @@ class SMTPControl(object): :type download_if_needed: bool """ self.smtp_connection.qtsigs.connecting_signal.emit() - self.smtp_bootstrapper.start_smtp_service( + threads.deferToThread( + self.smtp_bootstrapper.start_smtp_service, provider_config, self.smtp_config, self._keymanager, self.userid, download_if_needed) @@ -258,12 +231,11 @@ class MailConductor(IMAPControl, SMTPControl): :param soledad: a transparent proxy that eventually will point to a Soledad Instance. :type soledad: zope.proxy.ProxyBase - :param keymanager: a transparent proxy that eventually will point to a Keymanager Instance. :type keymanager: zope.proxy.ProxyBase """ - IMAPControl.__init__(self) + IMAPControl.__init__(self, soledad, keymanager) SMTPControl.__init__(self) self._soledad = soledad self._keymanager = keymanager diff --git a/src/leap/bitmask/services/mail/imapcontroller.py b/src/leap/bitmask/services/mail/imapcontroller.py new file mode 100644 index 00000000..efca5867 --- /dev/null +++ b/src/leap/bitmask/services/mail/imapcontroller.py @@ -0,0 +1,98 @@ +# -*- coding: utf-8 -*- +# imapcontroller.py +# Copyright (C) 2013 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 controller. +""" +import logging + +from leap.bitmask.services.mail import imap + + +logger = logging.getLogger(__name__) + + +class IMAPController(object): + """ + IMAP Controller. + """ + def __init__(self, soledad, keymanager): + """ + Initialize IMAP variables. + + :param soledad: a transparent proxy that eventually will point to a + Soledad Instance. + :type soledad: zope.proxy.ProxyBase + :param keymanager: a transparent proxy that eventually will point to a + Keymanager Instance. + :type keymanager: zope.proxy.ProxyBase + """ + self._soledad = soledad + self._keymanager = keymanager + + self.imap_service = None + self.imap_port = None + self.imap_factory = None + + def start_imap_service(self, userid, offline=False): + """ + Start IMAP service. + """ + logger.debug('Starting imap service') + + self.imap_service, self.imap_port, \ + self.imap_factory = imap.start_imap_service( + self._soledad, + self._keymanager, + userid=userid, + offline=offline) + + if offline is False: + logger.debug("Starting loop") + self.imap_service.start_loop() + + def stop_imap_service(self, cv): + """ + Stop IMAP service (fetcher, factory and port). + + :param cv: A condition variable to which we can signal when imap + indeed stops. + :type cv: threading.Condition + """ + if self.imap_service is not None: + # Stop the loop call in the fetcher + self.imap_service.stop() + self.imap_service = None + + # Stop listening on the IMAP port + self.imap_port.stopListening() + + # Stop the protocol + self.imap_factory.theAccount.closed = True + self.imap_factory.doStop(cv) + else: + # Release the condition variable so the caller doesn't have to wait + cv.acquire() + cv.notify() + cv.release() + + def fetch_incoming_mail(self): + """ + Fetch incoming mail. + """ + if self.imap_service: + logger.debug('Client connected, fetching mail...') + self.imap_service.fetch() -- cgit v1.2.3