From d4398c52acc54fb27a4b8bba2735a41f55b8f402 Mon Sep 17 00:00:00 2001 From: Kali Kaneko Date: Thu, 17 Oct 2013 12:04:12 -0300 Subject: Mail State Machine refactor. Closes: #4059 --- src/leap/bitmask/gui/mainwindow.py | 195 +++++++++++-------------------------- 1 file changed, 57 insertions(+), 138 deletions(-) (limited to 'src/leap/bitmask/gui/mainwindow.py') diff --git a/src/leap/bitmask/gui/mainwindow.py b/src/leap/bitmask/gui/mainwindow.py index f5631c69..dddd53da 100644 --- a/src/leap/bitmask/gui/mainwindow.py +++ b/src/leap/bitmask/gui/mainwindow.py @@ -24,6 +24,7 @@ import keyring from PySide import QtCore, QtGui from twisted.internet import threads +from zope.proxy import ProxyBase, setProxiedObject, sameProxiedObjects from leap.bitmask import __version__ as VERSION from leap.bitmask.config.leapsettings import LeapSettings @@ -39,18 +40,15 @@ from leap.bitmask.gui.mail_status import MailStatusWidget from leap.bitmask.gui.wizard import Wizard from leap.bitmask import provider -from leap.bitmask.provider.providerbootstrapper import ProviderBootstrapper -from leap.bitmask.services.eip.eipbootstrapper import EIPBootstrapper -from leap.bitmask.services.eip import eipconfig -# XXX: Soledad might not work out of the box in Windows, issue #2932 -from leap.bitmask.services.soledad.soledadbootstrapper import \ - SoledadBootstrapper -from leap.bitmask.services.mail.smtpbootstrapper import SMTPBootstrapper -from leap.bitmask.services.mail import imap from leap.bitmask.platform_init import IS_WIN, IS_MAC from leap.bitmask.platform_init.initializers import init_platform +from leap.bitmask.provider.providerbootstrapper import ProviderBootstrapper +from leap.bitmask.services.mail import conductor as mail_conductor + +from leap.bitmask.services.eip import eipconfig from leap.bitmask.services.eip import get_openvpn_management +from leap.bitmask.services.eip.eipbootstrapper import EIPBootstrapper from leap.bitmask.services.eip.connection import EIPConnection from leap.bitmask.services.eip.vpnprocess import VPN from leap.bitmask.services.eip.vpnprocess import OpenVPNAlreadyRunning @@ -62,12 +60,12 @@ from leap.bitmask.services.eip.linuxvpnlauncher import EIPNoPkexecAvailable from leap.bitmask.services.eip.linuxvpnlauncher import \ EIPNoPolkitAuthAgentAvailable from leap.bitmask.services.eip.darwinvpnlauncher import EIPNoTunKextLoaded +from leap.bitmask.services.soledad.soledadbootstrapper import \ + SoledadBootstrapper from leap.bitmask.util.keyring_helpers import has_keyring from leap.bitmask.util.leap_log_handler import LeapLogHandler -from leap.bitmask.services.mail.smtpconfig import SMTPConfig - if IS_WIN: from leap.bitmask.platform_init.locks import WindowsLock from leap.bitmask.platform_init.locks import raise_window_ack @@ -93,10 +91,6 @@ class MainWindow(QtGui.QMainWindow): # Keyring KEYRING_KEY = "bitmask" - # SMTP - PORT_KEY = "port" - IP_KEY = "ip_address" - OPENVPN_SERVICE = "openvpn" MX_SERVICE = "mx" @@ -257,10 +251,6 @@ class MainWindow(QtGui.QMainWindow): self._soledad_bootstrapper.soledad_failed.connect( self._mail_status.set_soledad_failed) - self._smtp_bootstrapper = SMTPBootstrapper() - self._smtp_bootstrapper.download_config.connect( - self._smtp_bootstrapped_stage) - self.ui.action_about_leap.triggered.connect(self._about) self.ui.action_quit.triggered.connect(self.quit) self.ui.action_wizard.triggered.connect(self._launch_wizard) @@ -307,12 +297,13 @@ class MainWindow(QtGui.QMainWindow): # Services signals/slots connection self.new_updates.connect(self._react_to_new_updates) + + # 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.soledad_ready.connect(self._set_soledad_ready) self.mail_client_logged_in.connect(self._fetch_incoming_mail) self.logout.connect(self._stop_imap_service) self.logout.connect(self._stop_smtp_service) - self.logout.connect(self._mail_status.stopped_mail) ################################# end Qt Signals connection ######## @@ -325,17 +316,18 @@ class MainWindow(QtGui.QMainWindow): self._bypass_checks = bypass_checks - self._soledad = None - self._soledad_ready = False - self._keymanager = None - self._smtp_service = None - self._smtp_port = None - self._imap_service = None + # We initialize Soledad and Keymanager instances as + # transparent proxies, so we can pass the reference freely + # around. + self._soledad = ProxyBase(None) + self._keymanager = ProxyBase(None) self._login_defer = None self._download_provider_defer = None - self._smtp_config = SMTPConfig() + self._mail_conductor = mail_conductor.MailConductor( + self._soledad, self._keymanager) + self._mail_conductor.connect_mail_signals(self._mail_status) # Eip machine is a public attribute where the state machine for # the eip connection will be available to the different components. @@ -347,6 +339,7 @@ class MainWindow(QtGui.QMainWindow): self.eip_machine = None # start event machines self.start_eip_machine() + self._mail_conductor.start_mail_machine(parent=self) if self._first_run(): self._wizard_firstrun = True @@ -460,7 +453,7 @@ class MainWindow(QtGui.QMainWindow): """ preferences_window = PreferencesWindow(self, self._srp_auth) - if self._soledad_ready: + if sameProxiedObjects(self._soledad, None): preferences_window.set_soledad_ready(self._soledad) else: self.soledad_ready.connect( @@ -478,16 +471,6 @@ class MainWindow(QtGui.QMainWindow): """ EIPPreferencesWindow(self).show() - def _set_soledad_ready(self): - """ - SLOT - TRIGGERS: - self.soledad_ready - - It sets the soledad object as ready to use. - """ - self._soledad_ready = True - # # updates # @@ -803,6 +786,7 @@ class MainWindow(QtGui.QMainWindow): provider configuration if it's not present, otherwise will emit the corresponding signals inmediately """ + # XXX should rename this provider, name clash. provider = self._login_widget.get_selected_provider() pb = self._provider_bootstrapper @@ -823,6 +807,7 @@ class MainWindow(QtGui.QMainWindow): :type data: dict """ if data[self._provider_bootstrapper.PASSED_KEY]: + # XXX should rename this provider, name clash. provider = self._login_widget.get_selected_provider() # If there's no loaded provider or @@ -1023,114 +1008,58 @@ class MainWindow(QtGui.QMainWindow): logger.debug("ERROR on soledad bootstrapping:") logger.error("%r" % data[self._soledad_bootstrapper.ERROR_KEY]) return - else: - logger.debug("Done bootstrapping Soledad") - self._soledad = self._soledad_bootstrapper.soledad - self._keymanager = self._soledad_bootstrapper.keymanager + logger.debug("Done bootstrapping Soledad") + + # Update the proxy objects to point to + # the initialized instances. + setProxiedObject(self._soledad, + self._soledad_bootstrapper.soledad) + setProxiedObject(self._keymanager, + self._soledad_bootstrapper.keymanager) # Ok, now soledad is ready, so we can allow other things that # depend on soledad to start. # this will trigger start_imap_service + # and start_smtp_boostrapping self.soledad_ready.emit() - # TODO connect all these activations to the soledad_ready - # signal so the logic is clearer to follow. - - if self._provider_config.provides_mx() and \ - self._enabled_services.count(self.MX_SERVICE) > 0: - self._smtp_bootstrapper.run_smtp_setup_checks( - self._provider_config, - self._smtp_config, - True) - ################################################################### # Service control methods: smtp - def _smtp_bootstrapped_stage(self, data): + @QtCore.Slot() + def _start_smtp_bootstrapping(self): """ SLOT TRIGGERS: - self._smtp_bootstrapper.download_config - - If there was a problem, displays it, otherwise it does nothing. - This is used for intermediate bootstrapping stages, in case - they fail. - - :param data: result from the bootstrapping stage for Soledad - :type data: dict - """ - passed = data[self._smtp_bootstrapper.PASSED_KEY] - if not passed: - logger.error(data[self._smtp_bootstrapper.ERROR_KEY]) - return - logger.debug("Done bootstrapping SMTP") - self._check_smtp_config() - - def _check_smtp_config(self): - """ - Checks smtp config and tries to download smtp client cert if needed. + self.soledad_ready """ - hosts = self._smtp_config.get_hosts() - # TODO handle more than one host and define how to choose - if len(hosts) > 0: - hostname = hosts.keys()[0] - logger.debug("Using hostname %s for SMTP" % (hostname,)) - host = hosts[hostname][self.IP_KEY].encode("utf-8") - port = hosts[hostname][self.PORT_KEY] - - client_cert = self._smtp_config.get_client_cert_path( + # TODO for simmetry, this should be called start_smtp_service + # (and delegate all the checks to the conductor) + if self._provider_config.provides_mx() and \ + self._enabled_services.count(self.MX_SERVICE) > 0: + self._mail_conductor.smtp_bootstrapper.run_smtp_setup_checks( self._provider_config, - about_to_download=True) - - if not os.path.isfile(client_cert): - self._smtp_bootstrapper._download_client_certificates() - if os.path.isfile(client_cert): - self._start_smtp_service(host, port, client_cert) - else: - logger.warning("Tried to download email client " - "certificate, but could not find any") - - else: - logger.warning("No smtp hosts configured") - - def _start_smtp_service(self, host, port, cert): - """ - Starts the smtp service. - """ - # TODO Make the encrypted_only configurable - # TODO pick local smtp port in a better way - # TODO remove hard-coded port and let leap.mail set - # the specific default. - - from leap.mail.smtp import setup_smtp_relay - self._smtp_service, self._smtp_port = setup_smtp_relay( - port=2013, - keymanager=self._keymanager, - smtp_host=host, - smtp_port=port, - smtp_cert=cert, - smtp_key=cert, - encrypted_only=False) + self._mail_conductor.smtp_config, + download_if_needed=True) + # XXX --- should remove from here, and connecte directly to the state + # machine. + @QtCore.Slot() def _stop_smtp_service(self): """ SLOT TRIGGERS: self.logout """ - # There is a subtle difference here: - # we are stopping the factory for the smtp service here, - # but in the imap case we are just stopping the fetcher. - if self._smtp_service is not None: - logger.debug('Stopping smtp service.') - self._smtp_port.stopListening() - self._smtp_service.doStop() + # TODO call stop_mail_service + self._mail_conductor.stop_smtp_service() ################################################################### # Service control methods: imap + @QtCore.Slot() def _start_imap_service(self): """ SLOT @@ -1139,11 +1068,7 @@ class MainWindow(QtGui.QMainWindow): """ if self._provider_config.provides_mx() and \ self._enabled_services.count(self.MX_SERVICE) > 0: - logger.debug('Starting imap service') - - self._imap_service = imap.start_imap_service( - self._soledad, - self._keymanager) + self._mail_conductor.start_imap_service() def _on_mail_client_logged_in(self, req): """ @@ -1151,30 +1076,25 @@ class MainWindow(QtGui.QMainWindow): """ self.mail_client_logged_in.emit() + @QtCore.Slot() def _fetch_incoming_mail(self): """ SLOT TRIGGERS: self.mail_client_logged_in """ - # TODO have a mutex over fetch operation. - if self._imap_service: - logger.debug('Client connected, fetching mail...') - self._imap_service.fetch() + # TODO connect signal directly!!! + self._mail_conductor.fetch_incoming_mail() + @QtCore.Slot() def _stop_imap_service(self): """ SLOT TRIGGERS: self.logout """ - # There is a subtle difference here: - # we are just stopping the fetcher here, - # but in the smtp case we are stopping the factory. - # We should homogenize both services. - if self._imap_service is not None: - logger.debug('Stopping imap service.') - self._imap_service.stop() + # TODO call stop_mail_service + self._mail_conductor.stop_imap_service() # end service control methods (imap) @@ -1623,8 +1543,8 @@ class MainWindow(QtGui.QMainWindow): if ok: self._logged_user = None - self._login_widget.logged_out() + self._mail_status.mail_state_disabled() else: self._login_widget.set_login_status( @@ -1700,8 +1620,7 @@ class MainWindow(QtGui.QMainWindow): """ logger.debug('About to quit, doing cleanup...') - if self._imap_service is not None: - self._imap_service.stop() + self._mail_conductor.stop_imap_service() if self._srp_auth is not None: if self._srp_auth.get_session_id() is not None or \ -- cgit v1.2.3