diff options
author | Tomás Touceda <chiiph@leap.se> | 2013-11-01 10:50:38 -0300 |
---|---|---|
committer | Tomás Touceda <chiiph@leap.se> | 2013-11-01 10:50:38 -0300 |
commit | 23e9034664edf6b2a01a677c4fa4e6efd880e364 (patch) | |
tree | fc90f154cf81cbaf48dd2d4a738a379591c4e7c7 /src/leap/bitmask/gui | |
parent | a06b5719f028e619a4b3800fb346ed3c984e4d25 (diff) | |
parent | 80fde752f0bf7946869361f6a77d1952f9339636 (diff) |
Merge branch 'release-0.3.6'
Diffstat (limited to 'src/leap/bitmask/gui')
-rw-r--r-- | src/leap/bitmask/gui/eip_preferenceswindow.py | 36 | ||||
-rw-r--r-- | src/leap/bitmask/gui/eip_status.py | 4 | ||||
-rw-r--r-- | src/leap/bitmask/gui/login.py | 47 | ||||
-rw-r--r-- | src/leap/bitmask/gui/mail_status.py | 103 | ||||
-rw-r--r-- | src/leap/bitmask/gui/mainwindow.py | 264 | ||||
-rw-r--r-- | src/leap/bitmask/gui/preferenceswindow.py | 50 | ||||
-rw-r--r-- | src/leap/bitmask/gui/statemachines.py | 414 | ||||
-rw-r--r-- | src/leap/bitmask/gui/ui/eip_status.ui | 6 | ||||
-rw-r--r-- | src/leap/bitmask/gui/ui/eippreferences.ui | 99 | ||||
-rw-r--r-- | src/leap/bitmask/gui/ui/mainwindow.ui | 172 |
10 files changed, 756 insertions, 439 deletions
diff --git a/src/leap/bitmask/gui/eip_preferenceswindow.py b/src/leap/bitmask/gui/eip_preferenceswindow.py index 9f8c4ff4..e0c5d51f 100644 --- a/src/leap/bitmask/gui/eip_preferenceswindow.py +++ b/src/leap/bitmask/gui/eip_preferenceswindow.py @@ -50,7 +50,6 @@ class EIPPreferencesWindow(QtGui.QDialog): self.ui = Ui_EIPPreferences() self.ui.setupUi(self) self.ui.lblProvidersGatewayStatus.setVisible(False) - self.ui.lblAutoStartEIPStatus.setVisible(False) # Connections self.ui.cbProvidersGateway.currentIndexChanged[unicode].connect( @@ -59,40 +58,8 @@ class EIPPreferencesWindow(QtGui.QDialog): self.ui.cbGateways.currentIndexChanged[unicode].connect( lambda x: self.ui.lblProvidersGatewayStatus.setVisible(False)) - self.ui.cbProvidersEIP.currentIndexChanged[unicode].connect( - lambda x: self.ui.lblAutoStartEIPStatus.setVisible(False)) - - self.ui.cbAutoStartEIP.toggled.connect( - lambda x: self.ui.lblAutoStartEIPStatus.setVisible(False)) - - self.ui.pbSaveAutoStartEIP.clicked.connect(self._save_auto_start_eip) - self._add_configured_providers() - # Load auto start EIP settings - self.ui.cbAutoStartEIP.setChecked(self._settings.get_autostart_eip()) - default_provider = self._settings.get_defaultprovider() - idx = self.ui.cbProvidersEIP.findText(default_provider) - self.ui.cbProvidersEIP.setCurrentIndex(idx) - - def _save_auto_start_eip(self): - """ - SLOT - TRIGGER: - self.ui.cbAutoStartEIP.toggled - - Saves the automatic start of EIP user preference. - """ - default_provider = self.ui.cbProvidersEIP.currentText() - enabled = self.ui.cbAutoStartEIP.isChecked() - - self._settings.set_autostart_eip(enabled) - self._settings.set_defaultprovider(default_provider) - - self.ui.lblAutoStartEIPStatus.show() - logger.debug('Auto start EIP saved: {0} {1}.'.format( - default_provider, enabled)) - def _set_providers_gateway_status(self, status, success=False, error=False): """ @@ -120,16 +87,13 @@ class EIPPreferencesWindow(QtGui.QDialog): Add the client's configured providers to the providers combo boxes. """ self.ui.cbProvidersGateway.clear() - self.ui.cbProvidersEIP.clear() providers = self._settings.get_configured_providers() if not providers: - self.ui.gbAutomaticEIP.setEnabled(False) self.ui.gbGatewaySelector.setEnabled(False) return for provider in providers: self.ui.cbProvidersGateway.addItem(provider) - self.ui.cbProvidersEIP.addItem(provider) def _save_selected_gateway(self, provider): """ diff --git a/src/leap/bitmask/gui/eip_status.py b/src/leap/bitmask/gui/eip_status.py index 77685cd3..324586c0 100644 --- a/src/leap/bitmask/gui/eip_status.py +++ b/src/leap/bitmask/gui/eip_status.py @@ -269,6 +269,10 @@ class EIPStatusWidget(QtGui.QWidget): :type error: bool """ leap_assert_type(error, bool) + if error: + logger.error(status) + else: + logger.debug(status) self._eip_status = status if error: status = "<font color='red'>%s</font>" % (status,) diff --git a/src/leap/bitmask/gui/login.py b/src/leap/bitmask/gui/login.py index ac34fe23..b21057f0 100644 --- a/src/leap/bitmask/gui/login.py +++ b/src/leap/bitmask/gui/login.py @@ -49,6 +49,9 @@ class LoginWidget(QtGui.QWidget): BARE_USERNAME_REGEX = r"^[A-Za-z\d_]+$" + # Keyring + KEYRING_KEY = "bitmask" + def __init__(self, settings, parent=None): """ Constructs the LoginWidget. @@ -168,7 +171,7 @@ class LoginWidget(QtGui.QWidget): """ Returns the user that appears in the widget. - :rtype: str + :rtype: unicode """ return self.ui.lnUser.text() @@ -177,7 +180,7 @@ class LoginWidget(QtGui.QWidget): Sets the password for the widget :param password: password to set - :type password: str + :type password: unicode """ self.ui.lnPassword.setText(password) @@ -185,7 +188,7 @@ class LoginWidget(QtGui.QWidget): """ Returns the password that appears in the widget - :rtype: str + :rtype: unicode """ return self.ui.lnPassword.text() @@ -366,3 +369,41 @@ class LoginWidget(QtGui.QWidget): self.ui.btnLogout.setText(self.tr("Logout")) self.ui.btnLogout.setEnabled(True) self.ui.clblErrorMsg.hide() + + def load_user_from_keyring(self, saved_user): + """ + Tries to load a user from the keyring, returns True if it was + loaded successfully, False otherwise. + + :param saved_user: String containing the saved username as + user@domain + :type saved_user: unicode + + :rtype: bool + """ + leap_assert_type(saved_user, unicode) + + try: + username, domain = saved_user.split('@') + except ValueError as e: + # if the saved_user does not contain an '@' + logger.error('Username@provider malformed. %r' % (e, )) + return False + + self.set_user(username) + + self.set_remember(True) + + saved_password = None + try: + saved_password = keyring.get_password(self.KEYRING_KEY, + saved_user + .encode("utf8")) + except ValueError as e: + logger.debug("Incorrect Password. %r." % (e,)) + + if saved_password is not None: + self.set_password(saved_password) + return True + + return False diff --git a/src/leap/bitmask/gui/mail_status.py b/src/leap/bitmask/gui/mail_status.py index 83533666..c1e82d4d 100644 --- a/src/leap/bitmask/gui/mail_status.py +++ b/src/leap/bitmask/gui/mail_status.py @@ -50,6 +50,8 @@ class MailStatusWidget(QtGui.QWidget): QtGui.QWidget.__init__(self, parent) self._systray = None + self._disabled = True + self._started = False self.ui = Ui_MailStatusWidget() self.ui.setupUi(self) @@ -98,29 +100,16 @@ class MailStatusWidget(QtGui.QWidget): callback=self._mail_handle_soledad_events, reqcbk=lambda req, resp: None) - register(signal=proto.SMTP_SERVICE_STARTED, - callback=self._mail_handle_smtp_events, - reqcbk=lambda req, resp: None) - - register(signal=proto.SMTP_SERVICE_FAILED_TO_START, - callback=self._mail_handle_smtp_events, - reqcbk=lambda req, resp: None) - - register(signal=proto.IMAP_SERVICE_STARTED, + register(signal=proto.IMAP_UNREAD_MAIL, callback=self._mail_handle_imap_events, reqcbk=lambda req, resp: None) - - register(signal=proto.IMAP_SERVICE_FAILED_TO_START, + register(signal=proto.IMAP_SERVICE_STARTED, callback=self._mail_handle_imap_events, reqcbk=lambda req, resp: None) - - register(signal=proto.IMAP_UNREAD_MAIL, + register(signal=proto.SMTP_SERVICE_STARTED, callback=self._mail_handle_imap_events, reqcbk=lambda req, resp: None) - self._smtp_started = False - self._imap_started = False - self._soledad_event.connect( self._mail_handle_soledad_events_slot) self._imap_event.connect( @@ -176,6 +165,9 @@ class MailStatusWidget(QtGui.QWidget): """ # TODO: Figure out how to handle this with the two status in different # classes + # XXX right now we could connect the state transition signals of the + # two connection machines (EIP/Mail) to a class that keeps track of the + # state -- kali # status = self.tr("Encrypted Internet: {0}").format(self._eip_status) # status += '\n' # status += self.tr("Mail is {0}").format(self._mx_status) @@ -292,11 +284,9 @@ class MailStatusWidget(QtGui.QWidget): """ # We want to ignore this kind of events once everything has # started - if self._smtp_started and self._imap_started: + if self._started: return - self._set_mail_status(self.tr("Starting..."), ready=1) - ext_status = "" if req.event == proto.KEYMANAGER_LOOKING_FOR_KEY: @@ -340,14 +330,9 @@ class MailStatusWidget(QtGui.QWidget): ext_status = "" if req.event == proto.SMTP_SERVICE_STARTED: - ext_status = self.tr("SMTP has started...") self._smtp_started = True - if self._smtp_started and self._imap_started: - self._set_mail_status(self.tr("ON"), ready=2) - ext_status = "" elif req.event == proto.SMTP_SERVICE_FAILED_TO_START: ext_status = self.tr("SMTP failed to start, check the logs.") - self._set_mail_status(self.tr("Failed")) else: leap_assert(False, "Don't know how to handle this state: %s" @@ -355,6 +340,8 @@ class MailStatusWidget(QtGui.QWidget): self._set_mail_status(ext_status, ready=2) + # ----- XXX deprecate (move to mail conductor) + def _mail_handle_imap_events(self, req): """ Callback for the IMAP events @@ -376,27 +363,15 @@ class MailStatusWidget(QtGui.QWidget): """ ext_status = None - if req.event == proto.IMAP_SERVICE_STARTED: - ext_status = self.tr("IMAP has started...") - self._imap_started = True - if self._smtp_started and self._imap_started: - self._set_mail_status(self.tr("ON"), ready=2) - ext_status = "" - elif req.event == proto.IMAP_SERVICE_FAILED_TO_START: - ext_status = self.tr("IMAP failed to start, check the logs.") - self._set_mail_status(self.tr("Failed")) - elif req.event == proto.IMAP_UNREAD_MAIL: - if self._smtp_started and self._imap_started: + if req.event == proto.IMAP_UNREAD_MAIL: + if self._started: if req.content != "0": self._set_mail_status(self.tr("%s Unread Emails") % (req.content,), ready=2) else: self._set_mail_status("", ready=2) - else: - leap_assert(False, # XXX ??? - "Don't know how to handle this state: %s" - % (req.event)) - + elif req.event == proto.IMAP_SERVICE_STARTED: + self._imap_started = True if ext_status is not None: self._set_mail_status(ext_status, ready=1) @@ -414,8 +389,50 @@ class MailStatusWidget(QtGui.QWidget): """ self._set_mail_status(self.tr("Disabled"), -1) - def stopped_mail(self): + # statuses + + # XXX make the signal emit the label and state. + + @QtCore.Slot() + def mail_state_disconnected(self): + """ + Displays the correct UI for the disconnected state. + """ + # XXX this should handle the disabled state better. + self._started = False + if self._disabled: + self.mail_state_disabled() + else: + self._set_mail_status(self.tr("OFF"), -1) + + @QtCore.Slot() + def mail_state_connecting(self): + """ + Displays the correct UI for the connecting state. + """ + self._disabled = False + self._started = True + self._set_mail_status(self.tr("Starting..."), 1) + + @QtCore.Slot() + def mail_state_disconnecting(self): + """ + Displays the correct UI for the connecting state. + """ + self._set_mail_status(self.tr("Disconnecting..."), 1) + + @QtCore.Slot() + def mail_state_connected(self): + """ + Displays the correct UI for the connected state. + """ + self._set_mail_status(self.tr("ON"), 2) + + @QtCore.Slot() + def mail_state_disabled(self): """ - Displayes the correct UI for the stopped state. + Displays the correct UI for the disabled state. """ - self._set_mail_status(self.tr("OFF")) + self._disabled = True + self._set_mail_status( + self.tr("You must be logged in to use encrypted email."), -1) diff --git a/src/leap/bitmask/gui/mainwindow.py b/src/leap/bitmask/gui/mainwindow.py index f5631c69..5eb9e6dc 100644 --- a/src/leap/bitmask/gui/mainwindow.py +++ b/src/leap/bitmask/gui/mainwindow.py @@ -20,10 +20,9 @@ Main window for Bitmask. import logging import os -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 +38,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 +58,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 @@ -90,13 +86,6 @@ class MainWindow(QtGui.QMainWindow): LOGIN_INDEX = 0 EIP_STATUS_INDEX = 1 - # Keyring - KEYRING_KEY = "bitmask" - - # SMTP - PORT_KEY = "port" - IP_KEY = "ip_address" - OPENVPN_SERVICE = "openvpn" MX_SERVICE = "mx" @@ -257,10 +246,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 +292,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 +311,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 +334,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 @@ -458,13 +446,11 @@ class MainWindow(QtGui.QMainWindow): Displays the preferences window. """ - preferences_window = PreferencesWindow(self, self._srp_auth) + preferences_window = PreferencesWindow(self, self._srp_auth, + self._provider_config) - if self._soledad_ready: - preferences_window.set_soledad_ready(self._soledad) - else: - self.soledad_ready.connect( - lambda: preferences_window.set_soledad_ready(self._soledad)) + self.soledad_ready.connect( + lambda: preferences_window.set_soledad_ready(self._soledad)) preferences_window.show() @@ -478,16 +464,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 # @@ -562,6 +538,8 @@ class MainWindow(QtGui.QMainWindow): if IS_MAC: self.raise_() + self._hide_unsupported_services() + if self._wizard: possible_username = self._wizard.get_username() possible_password = self._wizard.get_password() @@ -594,32 +572,34 @@ class MainWindow(QtGui.QMainWindow): saved_user = self._settings.get_user() - try: - username, domain = saved_user.split('@') - except (ValueError, AttributeError) as e: - # if the saved_user does not contain an '@' or its None - logger.error('Username@provider malformed. %r' % (e, )) - saved_user = None - if saved_user is not None and has_keyring(): - # fill the username - self._login_widget.set_user(username) - - self._login_widget.set_remember(True) - - saved_password = None - try: - saved_password = keyring.get_password(self.KEYRING_KEY, - saved_user - .encode("utf8")) - except ValueError, e: - logger.debug("Incorrect Password. %r." % (e,)) - - if saved_password is not None: - self._login_widget.set_password( - saved_password.decode("utf8")) + if self._login_widget.load_user_from_keyring(saved_user): self._login() + def _hide_unsupported_services(self): + """ + Given a set of configured providers, it creates a set of + available services among all of them and displays the service + widgets of only those. + + This means, for example, that with just one provider with EIP + only, the mail widget won't be displayed. + """ + providers = self._settings.get_configured_providers() + + services = set() + + for prov in providers: + provider_config = ProviderConfig() + loaded = provider_config.load( + provider.get_provider_path(prov)) + if loaded: + for service in provider_config.get_services(): + services.add(service) + + self.ui.eipWidget.setVisible(self.OPENVPN_SERVICE in services) + self.ui.mailWidget.setVisible(self.MX_SERVICE in services) + # # systray # @@ -803,6 +783,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 +804,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 @@ -895,8 +877,10 @@ class MainWindow(QtGui.QMainWindow): leap_assert(self._provider_config, "We need a provider config!") if data[self._provider_bootstrapper.PASSED_KEY]: - username = self._login_widget.get_user().encode("utf8") - password = self._login_widget.get_password().encode("utf8") + username = self._login_widget.get_user() + password = self._login_widget.get_password() + + self._hide_unsupported_services() if self._srp_auth is None: self._srp_auth = SRPAuth(self._provider_config) @@ -1023,114 +1007,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 +1067,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 +1075,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 +1542,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 +1619,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 \ diff --git a/src/leap/bitmask/gui/preferenceswindow.py b/src/leap/bitmask/gui/preferenceswindow.py index 58cb05ba..acb39b07 100644 --- a/src/leap/bitmask/gui/preferenceswindow.py +++ b/src/leap/bitmask/gui/preferenceswindow.py @@ -40,12 +40,14 @@ class PreferencesWindow(QtGui.QDialog): """ Window that displays the preferences. """ - def __init__(self, parent, srp_auth): + def __init__(self, parent, srp_auth, provider_config): """ :param parent: parent object of the PreferencesWindow. :parent type: QWidget :param srp_auth: SRPAuth object configured in the main app. :type srp_auth: SRPAuth + :param provider_config: ProviderConfig object. + :type provider_config: ProviderConfig """ QtGui.QDialog.__init__(self, parent) self.AUTOMATIC_GATEWAY_LABEL = self.tr("Automatic") @@ -72,6 +74,36 @@ class PreferencesWindow(QtGui.QDialog): else: self._add_configured_providers() + pw_enabled = False + + # check if the user is logged in + if srp_auth is not None and srp_auth.get_token() is not None: + # check if provider has 'mx' ... + domain = provider_config.get_domain() + self._select_provider_by_name(domain) + if provider_config.provides_mx(): + enabled_services = self._settings.get_enabled_services(domain) + mx_name = get_service_display_name('mx') + + # ... and if the user have it enabled + if 'mx' not in enabled_services: + msg = self.tr("You need to enable {0} in order to change " + "the password.".format(mx_name)) + self._set_password_change_status(msg, error=True) + else: + msg = self.tr( + "You need to wait until {0} is ready in " + "order to change the password.".format(mx_name)) + self._set_password_change_status(msg) + else: + pw_enabled = True + else: + msg = self.tr( + "In order to change your password you need to be logged in.") + self._set_password_change_status(msg) + + self.ui.gbPasswordChange.setEnabled(pw_enabled) + def set_soledad_ready(self, soledad): """ SLOT @@ -84,6 +116,7 @@ class PreferencesWindow(QtGui.QDialog): :type soledad: Soledad """ self._soledad = soledad + self.ui.lblPasswordChangeStatus.setVisible(False) self.ui.gbPasswordChange.setEnabled(True) def _set_password_change_status(self, status, error=False, success=False): @@ -98,6 +131,9 @@ class PreferencesWindow(QtGui.QDialog): elif success: status = "<font color='green'><b>%s</b></font>" % (status,) + if not self.ui.gbPasswordChange.isEnabled(): + status = "<font color='black'>%s</font>" % (status,) + self.ui.lblPasswordChangeStatus.setVisible(True) self.ui.lblPasswordChangeStatus.setText(status) @@ -156,7 +192,7 @@ class PreferencesWindow(QtGui.QDialog): """ logger.debug("SRP password changed successfully.") try: - self._soledad.change_passphrase(str(new_password)) + self._soledad.change_passphrase(new_password) logger.debug("Soledad password changed successfully.") except NoStorageSecret: logger.debug( @@ -218,6 +254,16 @@ class PreferencesWindow(QtGui.QDialog): for provider in self._settings.get_configured_providers(): self.ui.cbProvidersServices.addItem(provider) + def _select_provider_by_name(self, name): + """ + Given a provider name/domain, selects it in the combobox. + + :param name: name or domain for the provider + :type name: str + """ + provider_index = self.ui.cbProvidersServices.findText(name) + self.ui.cbProvidersServices.setCurrentIndex(provider_index) + def _service_selection_changed(self, service, state): """ SLOT diff --git a/src/leap/bitmask/gui/statemachines.py b/src/leap/bitmask/gui/statemachines.py index 94726720..386cb75f 100644 --- a/src/leap/bitmask/gui/statemachines.py +++ b/src/leap/bitmask/gui/statemachines.py @@ -19,7 +19,8 @@ State machines for the Bitmask app. """ import logging -from PySide.QtCore import QStateMachine, QState +from PySide import QtCore +from PySide.QtCore import QStateMachine, QState, Signal from PySide.QtCore import QObject from leap.bitmask.services import connections @@ -36,28 +37,255 @@ _CON = "connecting" _DIS = "disconnecting" -class IntermediateState(QState): +class SignallingState(QState): """ - Intermediate state that emits a custom signal on entry + A state that emits a custom signal on entry. """ - def __init__(self, signal): + def __init__(self, signal, parent=None, name=None): """ Initializer. :param signal: the signal to be emitted on entry on this state. :type signal: QtCore.QSignal """ - super(IntermediateState, self).__init__() + super(SignallingState, self).__init__(parent) self._signal = signal + self._name = name def onEntry(self, *args): """ Emits the signal on entry. """ - logger.debug('IntermediateState entered. Emitting signal ...') + logger.debug('State %s::%s entered. Emitting signal ...' + % (self._name, self.objectName())) if self._signal is not None: self._signal.emit() +class States(object): + """ + States for composite objects + """ + + class Off(SignallingState): + pass + + class Connecting(SignallingState): + pass + + class On(SignallingState): + pass + + class Disconnecting(SignallingState): + pass + + class StepsTrack(QObject): + state_change = Signal() + + def __init__(self, target): + super(States.StepsTrack, self).__init__() + self.received = set([]) + self.target = set(target) + + def is_all_done(self): + return all([ev in self.target for ev in self.received]) + + def is_any_done(self): + return any([ev in self.target for ev in self.received]) + + def seen(self, _type): + if _type in self.target: + self.received.add(_type) + + def reset_seen(self): + self.received = set([]) + + class TransitionOR(QtCore.QSignalTransition): + + def __init__(self, state): + super(States.TransitionOR, self).__init__( + state, QtCore.SIGNAL('state_change()')) + self.state = state + + def eventTest(self, e): + self.state.seen(e.type()) + done = self.state.is_any_done() + if done: + self.state.reset_seen() + return done + + def onTransition(self, e): + pass + + class TransitionAND(QtCore.QSignalTransition): + + def __init__(self, state): + super(States.TransitionAND, self).__init__( + state, QtCore.SIGNAL('state_change()')) + self.state = state + + def eventTest(self, e): + self.state.seen(e.type()) + done = self.state.is_all_done() + if done: + self.state.reset_seen() + return done + + def onTransition(self, e): + pass + + +class CompositeEvent(QtCore.QEvent): + def __init__(self): + super(CompositeEvent, self).__init__( + QtCore.QEvent.Type(self.ID)) + + +class Composite(object): + # TODO we should generate the connectingEvents dinamycally, + # depending on how much composite states do we get. + # This only supports up to 2 composite states. + + class ConnectingEvent1(CompositeEvent): + ID = QtCore.QEvent.User + 1 + + class ConnectingEvent2(CompositeEvent): + ID = QtCore.QEvent.User + 2 + + class ConnectedEvent1(CompositeEvent): + ID = QtCore.QEvent.User + 3 + + class ConnectedEvent2(CompositeEvent): + ID = QtCore.QEvent.User + 4 + + class DisconnectingEvent1(CompositeEvent): + ID = QtCore.QEvent.User + 5 + + class DisconnectingEvent2(CompositeEvent): + ID = QtCore.QEvent.User + 6 + + class DisconnectedEvent1(CompositeEvent): + ID = QtCore.QEvent.User + 7 + + class DisconnectedEvent2(CompositeEvent): + ID = QtCore.QEvent.User + 8 + + +class Events(QtCore.QObject): + """ + A Wrapper object for containing the events that will be + posted to a composite state machine. + """ + def __init__(self, parent=None): + """ + Initializes the QObject with the given parent. + """ + QtCore.QObject.__init__(self, parent) + + +class CompositeMachine(QStateMachine): + + def __init__(self, parent=None): + QStateMachine.__init__(self, parent) + + # events + self.events = Events(parent) + self.create_events() + + def create_events(self): + """ + Creates a bunch of events to be posted to the state machine when + the transitions say so. + """ + # XXX refactor into a dictionary? + self.events.con_ev1 = Composite.ConnectingEvent1() + self.events.con_ev2 = Composite.ConnectingEvent2() + self.events.on_ev1 = Composite.ConnectedEvent1() + self.events.on_ev2 = Composite.ConnectedEvent2() + self.events.dis_ev1 = Composite.DisconnectingEvent1() + self.events.dis_ev2 = Composite.DisconnectingEvent2() + self.events.off_ev1 = Composite.DisconnectedEvent1() + self.events.off_ev2 = Composite.DisconnectedEvent2() + + def beginSelectTransitions(self, e): + """ + Weird. Having this method makes underlying backtraces + to appear magically on the transitions. + :param e: the received event + :type e: QEvent + """ + pass + + def _connect_children(self, child1, child2): + """ + Connects the state transition signals for children machines. + + :param child1: the first child machine + :type child1: QStateMachine + :param child2: the second child machine + :type child2: QStateMachine + """ + # TODO refactor and generalize for composites + # of more than 2 connections. + + c1 = child1.conn + c1.qtsigs.connecting_signal.connect(self.con_ev1_slot) + c1.qtsigs.connected_signal.connect(self.on_ev1_slot) + c1.qtsigs.disconnecting_signal.connect(self.dis_ev1_slot) + c1.qtsigs.disconnected_signal.connect(self.off_ev1_slot) + + c2 = child2.conn + c2.qtsigs.connecting_signal.connect(self.con_ev2_slot) + c2.qtsigs.connected_signal.connect(self.on_ev2_slot) + c2.qtsigs.disconnecting_signal.connect(self.dis_ev2_slot) + c2.qtsigs.disconnected_signal.connect(self.off_ev2_slot) + + # XXX why is this getting deletec in c++? + #Traceback (most recent call last): + #self.postEvent(self.events.on_ev2) + #RuntimeError: Internal C++ object (ConnectedEvent2) already deleted. + # XXX trying the following workaround, since + # I cannot find why in the world this is getting deleted :( + # XXX refactor! + + # slots connection1 + + def con_ev1_slot(self): + # XXX if we just postEvent, we get the Internal C++ object deleted... + # so the workaround is to re-create it each time. + self.events.con_ev1 = Composite.ConnectingEvent1() + self.postEvent(self.events.con_ev1) + + def on_ev1_slot(self): + self.events.on_ev1 = Composite.ConnectedEvent1() + self.postEvent(self.events.on_ev1) + + def dis_ev1_slot(self): + self.events.dis_ev1 = Composite.DisconnectingEvent1() + self.postEvent(self.events.dis_ev1) + + def off_ev1_slot(self): + self.events.off_ev1 = Composite.DisconnectedEvent1() + self.postEvent(self.events.off_ev1) + + # slots connection2 + + def con_ev2_slot(self): + self.events.con_ev2 = Composite.ConnectingEvent2() + self.postEvent(self.events.con_ev2) + + def on_ev2_slot(self): + self.events.on_ev2 = Composite.ConnectedEvent2() + self.postEvent(self.events.on_ev2) + + def dis_ev2_slot(self): + self.events.dis_ev2 = Composite.DisconnectingEvent2() + self.postEvent(self.events.dis_ev2) + + def off_ev2_slot(self): + self.events.off_ev2 = Composite.DisconnectedEvent2() + self.postEvent(self.events.off_ev2) + + class ConnectionMachineBuilder(object): """ Builder class for state machines made from LEAPConnections. @@ -65,16 +293,161 @@ class ConnectionMachineBuilder(object): def __init__(self, connection): """ :param connection: an instance of a concrete LEAPConnection - we will be building a state machine for. + we will be building a state machine for. :type connection: AbstractLEAPConnection """ self._conn = connection leap_assert_type(self._conn, connections.AbstractLEAPConnection) - def make_machine(self, button=None, action=None, label=None): + def make_machine(self, **kwargs): """ Creates a statemachine associated with the passed controls. + It returns the state machine if the connection used for initializing + the ConnectionMachineBuilder inherits exactly from + LEAPAbstractConnection, and a tuple with the Composite Machine and its + individual parts in case that it is a composite machine which + connection definition inherits from more than one class that, on their + time, inherit from LEAPAbstractConnection. + + :params: see parameters for ``_make_simple_machine`` + :returns: a QStateMachine, or a tuple with the form: + (CompositeStateMachine, (StateMachine1, StateMachine2)) + :rtype: QStateMachine or tuple + """ + components = self._conn.components + + if components is None: + # simple case: connection definition inherits directly from + # the abstract connection. + + leap_assert_type(self._conn, connections.AbstractLEAPConnection) + return self._make_simple_machine(self._conn, **kwargs) + + if components: + # composite case: connection definition inherits from several + # classes, each one of which inherit from the abstract connection. + child_machines = tuple( + [ConnectionMachineBuilder(connection()).make_machine() + for connection in components]) + composite_machine = self._make_composite_machine( + self._conn, child_machines, **kwargs) + + composite_machine._connect_children( + *child_machines) + + # XXX should also connect its own states with the signals + # for the composite machine itself + + return (composite_machine, child_machines) + + def _make_composite_machine(self, conn, children, + **kwargs): + """ + Creates a composite machine. + + :param conn: an instance of a connection definition. + :type conn: LEAPAbstractConnection + :param children: children machines + :type children: tuple of state machines + :returns: A composite state machine + :rtype: QStateMachine + """ + # TODO split this method in smaller utility functions. + parent = kwargs.get('parent', None) + + # 1. create machine + machine = CompositeMachine(parent=parent) + + # 2. create states + off = States.Off(conn.qtsigs.disconnected_signal, + parent=machine, + name=conn.name) + off.setObjectName("off") + + on = States.On(conn.qtsigs.connected_signal, + parent=machine, + name=conn.name) + on.setObjectName("on") + + connecting_state = States.Connecting( + conn.qtsigs.connecting_signal, + parent=machine, + name=conn.name) + connecting_state.setObjectName("connecting") + + disconnecting_state = States.Disconnecting( + conn.qtsigs.disconnecting_signal, + parent=machine, + name=conn.name) + disconnecting_state.setObjectName("disconnecting") + + # 3. TODO create as many connectingEvents as needed (dynamically create + # classses for that) + # (we have manually created classes for events under CompositeEvent for + # now, to begin with the simple 2 states case for mail. + + # 4. state tracking objects for each transition stage + + connecting_track0 = States.StepsTrack( + (Composite.ConnectingEvent1.ID, + Composite.ConnectingEvent2.ID)) + connecting_track0.setObjectName("connecting_step_0") + + connecting_track1 = States.StepsTrack( + (Composite.ConnectedEvent1.ID, + Composite.ConnectedEvent2.ID)) + connecting_track1.setObjectName("connecting_step_1") + + disconnecting_track0 = States.StepsTrack( + (Composite.DisconnectingEvent1.ID, + Composite.DisconnectingEvent2.ID)) + disconnecting_track0.setObjectName("disconnecting_step_0") + + disconnecting_track1 = States.StepsTrack( + (Composite.DisconnectedEvent1.ID, + Composite.DisconnectedEvent2.ID)) + disconnecting_track1.setObjectName("disconnecting_step_1") + + # 5. definte the transitions with the matching state-tracking + # objects. + + # off -> connecting + connecting_transition = States.TransitionOR( + connecting_track0) + connecting_transition.setTargetState(connecting_state) + off.addTransition(connecting_transition) + + # connecting -> on + connected_transition = States.TransitionAND( + connecting_track1) + connected_transition.setTargetState(on) + connecting_state.addTransition(connected_transition) + + # on -> disconnecting + disconnecting_transition = States.TransitionOR( + disconnecting_track0) + disconnecting_transition.setTargetState(disconnecting_state) + on.addTransition(disconnecting_transition) + + # disconnecting -> off + disconnected_transition = States.TransitionAND( + disconnecting_track1) + disconnected_transition.setTargetState(off) + disconnecting_state.addTransition(disconnected_transition) + + machine.setInitialState(off) + machine.conn = conn + return machine + + def _make_simple_machine(self, conn, + button=None, action=None, label=None): + """ + Creates a statemachine associated with the passed controls. + + :param conn: the connection instance that defines this machine. + :type conn: AbstractLEAPConnection + :param button: the switch button. :type button: QPushButton @@ -88,9 +461,7 @@ class ConnectionMachineBuilder(object): :rtype: QStateMachine """ machine = QStateMachine() - conn = self._conn - - states = self._make_states(button, action, label) + states = self._make_states(conn, button, action, label) # transitions: @@ -151,11 +522,17 @@ class ConnectionMachineBuilder(object): for state in states.itervalues(): machine.addState(state) machine.setInitialState(states[_OFF]) + + machine.conn = conn return machine - def _make_states(self, button, action, label): + def _make_states(self, conn, button, action, label): """ - Creates the four states for the state machine + Creates the four states for the simple state machine. + Adds the needed properties for the passed controls. + + :param conn: the connection instance that defines this machine. + :type conn: AbstractLEAPConnection :param button: the switch button. :type button: QPushButton @@ -169,7 +546,6 @@ class ConnectionMachineBuilder(object): :returns: a dict of states :rtype: dict """ - conn = self._conn states = {} # TODO add tooltip @@ -190,8 +566,9 @@ class ConnectionMachineBuilder(object): states[_OFF] = off # CONNECTING State ---------------- - connecting = IntermediateState( - conn.qtsigs.connecting_signal) + connecting = SignallingState( + conn.qtsigs.connecting_signal, + name=conn.name) on_label = _tr("Turn {0}").format( conn.Disconnected.short_label) if button: @@ -224,8 +601,9 @@ class ConnectionMachineBuilder(object): states[_ON] = on # DISCONNECTING State ------------- - disconnecting = IntermediateState( - conn.qtsigs.disconnecting_signal) + disconnecting = SignallingState( + conn.qtsigs.disconnecting_signal, + name=conn.name) if button: disconnecting.assignProperty( button, 'enabled', False) diff --git a/src/leap/bitmask/gui/ui/eip_status.ui b/src/leap/bitmask/gui/ui/eip_status.ui index 25831118..d078ca0c 100644 --- a/src/leap/bitmask/gui/ui/eip_status.ui +++ b/src/leap/bitmask/gui/ui/eip_status.ui @@ -185,6 +185,9 @@ <property name="cursor"> <cursorShape>PointingHandCursor</cursorShape> </property> + <property name="styleSheet"> + <string notr="true">text-align: left;</string> + </property> <property name="text"> <string>0.0 KB/s</string> </property> @@ -249,6 +252,9 @@ <property name="cursor"> <cursorShape>PointingHandCursor</cursorShape> </property> + <property name="styleSheet"> + <string notr="true">text-align: left;</string> + </property> <property name="text"> <string>0.0 KB/s</string> </property> diff --git a/src/leap/bitmask/gui/ui/eippreferences.ui b/src/leap/bitmask/gui/ui/eippreferences.ui index cc77c82e..a3050683 100644 --- a/src/leap/bitmask/gui/ui/eippreferences.ui +++ b/src/leap/bitmask/gui/ui/eippreferences.ui @@ -7,7 +7,7 @@ <x>0</x> <y>0</y> <width>435</width> - <height>273</height> + <height>144</height> </rect> </property> <property name="windowTitle"> @@ -18,7 +18,7 @@ <normaloff>:/images/mask-icon.png</normaloff>:/images/mask-icon.png</iconset> </property> <layout class="QGridLayout" name="gridLayout_2"> - <item row="1" column="0"> + <item row="0" column="0"> <widget class="QGroupBox" name="gbGatewaySelector"> <property name="enabled"> <bool>true</bool> @@ -33,7 +33,7 @@ <item row="0" column="0"> <widget class="QLabel" name="lblSelectProvider"> <property name="text"> - <string>&Select provider:</string> + <string>Select &provider:</string> </property> <property name="buddy"> <cstring>cbProvidersGateway</cstring> @@ -52,7 +52,7 @@ <item row="7" column="2"> <widget class="QPushButton" name="pbSaveGateway"> <property name="text"> - <string>Save this provider settings</string> + <string>&Save this provider settings</string> </property> </widget> </item> @@ -69,7 +69,10 @@ <item row="1" column="0"> <widget class="QLabel" name="label"> <property name="text"> - <string>Select gateway:</string> + <string>Select &gateway:</string> + </property> + <property name="buddy"> + <cstring>cbGateways</cstring> </property> </widget> </item> @@ -85,76 +88,9 @@ </layout> </widget> </item> - <item row="0" column="0"> - <widget class="QGroupBox" name="gbAutomaticEIP"> - <property name="title"> - <string>Automatic Encrypted Internet start</string> - </property> - <layout class="QGridLayout" name="gridLayout_3"> - <item row="3" column="0"> - <widget class="QLabel" name="lblAutoStartEIPStatus"> - <property name="layoutDirection"> - <enum>Qt::LeftToRight</enum> - </property> - <property name="styleSheet"> - <string notr="true"/> - </property> - <property name="frameShape"> - <enum>QFrame::NoFrame</enum> - </property> - <property name="frameShadow"> - <enum>QFrame::Plain</enum> - </property> - <property name="text"> - <string><font color='green'><b>Automatic EIP start saved!</b></font></string> - </property> - <property name="alignment"> - <set>Qt::AlignCenter</set> - </property> - </widget> - </item> - <item row="3" column="1"> - <widget class="QPushButton" name="pbSaveAutoStartEIP"> - <property name="text"> - <string>Save auto start setting</string> - </property> - </widget> - </item> - <item row="0" column="0"> - <widget class="QCheckBox" name="cbAutoStartEIP"> - <property name="layoutDirection"> - <enum>Qt::LeftToRight</enum> - </property> - <property name="text"> - <string>Enable Automatic start:</string> - </property> - <property name="checked"> - <bool>true</bool> - </property> - </widget> - </item> - <item row="0" column="1"> - <widget class="QComboBox" name="cbProvidersEIP"> - <item> - <property name="text"> - <string><Select provider></string> - </property> - </item> - </widget> - </item> - </layout> - <zorder>cbAutoStartEIP</zorder> - <zorder>lblAutoStartEIPStatus</zorder> - <zorder>pbSaveAutoStartEIP</zorder> - <zorder>cbProvidersEIP</zorder> - </widget> - </item> </layout> </widget> <tabstops> - <tabstop>cbAutoStartEIP</tabstop> - <tabstop>cbProvidersEIP</tabstop> - <tabstop>pbSaveAutoStartEIP</tabstop> <tabstop>cbProvidersGateway</tabstop> <tabstop>cbGateways</tabstop> <tabstop>pbSaveGateway</tabstop> @@ -162,22 +98,5 @@ <resources> <include location="../../../../../data/resources/mainwindow.qrc"/> </resources> - <connections> - <connection> - <sender>cbAutoStartEIP</sender> - <signal>toggled(bool)</signal> - <receiver>cbProvidersEIP</receiver> - <slot>setEnabled(bool)</slot> - <hints> - <hint type="sourcelabel"> - <x>180</x> - <y>53</y> - </hint> - <hint type="destinationlabel"> - <x>238</x> - <y>53</y> - </hint> - </hints> - </connection> - </connections> + <connections/> </ui> diff --git a/src/leap/bitmask/gui/ui/mainwindow.ui b/src/leap/bitmask/gui/ui/mainwindow.ui index 10c77057..badd291d 100644 --- a/src/leap/bitmask/gui/ui/mainwindow.ui +++ b/src/leap/bitmask/gui/ui/mainwindow.ui @@ -75,7 +75,7 @@ <x>0</x> <y>0</y> <width>524</width> - <height>635</height> + <height>636</height> </rect> </property> <layout class="QVBoxLayout" name="verticalLayout"> @@ -86,89 +86,101 @@ <number>0</number> </property> <item> - <widget class="QFrame" name="frame_2"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="styleSheet"> - <string notr="true">QFrame{background-color: qlineargradient(spread:pad, x1:0, y1:1, x2:0, y2:0, stop:0 rgba(160, 160, 160, 128), stop:1 rgba(255, 255, 255, 0));}</string> - </property> - <layout class="QHBoxLayout" name="horizontalLayout_3"> - <property name="leftMargin"> - <number>24</number> + <widget class="QWidget" name="eipWidget" native="true"> + <layout class="QVBoxLayout" name="verticalLayout_2"> + <property name="spacing"> + <number>0</number> </property> - <property name="rightMargin"> - <number>24</number> + <property name="margin"> + <number>0</number> </property> <item> - <widget class="QLabel" name="label_2"> - <property name="font"> - <font> - <pointsize>16</pointsize> - <weight>75</weight> - <bold>true</bold> - </font> + <widget class="QFrame" name="frame_2"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> </property> <property name="styleSheet"> - <string notr="true">background-color: rgba(255, 255, 255, 0);</string> - </property> - <property name="text"> - <string>Encrypted Internet</string> + <string notr="true">QFrame{background-color: qlineargradient(spread:pad, x1:0, y1:1, x2:0, y2:0, stop:0 rgba(160, 160, 160, 128), stop:1 rgba(255, 255, 255, 0));}</string> </property> + <layout class="QHBoxLayout" name="horizontalLayout_3"> + <property name="leftMargin"> + <number>24</number> + </property> + <property name="rightMargin"> + <number>24</number> + </property> + <item> + <widget class="QLabel" name="label_2"> + <property name="font"> + <font> + <pointsize>16</pointsize> + <weight>75</weight> + <bold>true</bold> + </font> + </property> + <property name="styleSheet"> + <string notr="true">background-color: rgba(255, 255, 255, 0);</string> + </property> + <property name="text"> + <string>Encrypted Internet</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="btnEIPPreferences"> + <property name="maximumSize"> + <size> + <width>48</width> + <height>20</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true"/> + </property> + <property name="text"> + <string/> + </property> + <property name="icon"> + <iconset resource="../../../../../data/resources/mainwindow.qrc"> + <normaloff>:/images/black/32/gear.png</normaloff>:/images/black/32/gear.png</iconset> + </property> + <property name="autoDefault"> + <bool>false</bool> + </property> + <property name="default"> + <bool>false</bool> + </property> + <property name="flat"> + <bool>false</bool> + </property> + </widget> + </item> + </layout> </widget> </item> <item> - <widget class="QPushButton" name="btnEIPPreferences"> - <property name="maximumSize"> - <size> - <width>48</width> - <height>20</height> - </size> + <layout class="QVBoxLayout" name="eipLayout"> + <property name="leftMargin"> + <number>12</number> </property> - <property name="styleSheet"> - <string notr="true"/> + <property name="topMargin"> + <number>0</number> </property> - <property name="text"> - <string/> + <property name="rightMargin"> + <number>12</number> </property> - <property name="icon"> - <iconset resource="../../../../../data/resources/mainwindow.qrc"> - <normaloff>:/images/black/32/gear.png</normaloff>:/images/black/32/gear.png</iconset> - </property> - <property name="autoDefault"> - <bool>false</bool> + <property name="bottomMargin"> + <number>0</number> </property> - <property name="default"> - <bool>false</bool> - </property> - <property name="flat"> - <bool>false</bool> - </property> - </widget> + </layout> </item> </layout> </widget> </item> <item> - <layout class="QVBoxLayout" name="eipLayout"> - <property name="leftMargin"> - <number>12</number> - </property> - <property name="topMargin"> - <number>0</number> - </property> - <property name="rightMargin"> - <number>12</number> - </property> - <property name="bottomMargin"> - <number>0</number> - </property> - </layout> - </item> - <item> <widget class="Line" name="line"> <property name="orientation"> <enum>Qt::Horizontal</enum> @@ -253,14 +265,26 @@ background-color: qlineargradient(spread:pad, x1:0, y1:1, x2:0, y2:0, stop:0 rgb </widget> </item> <item> - <layout class="QVBoxLayout" name="mailLayout" stretch=""> - <property name="spacing"> - <number>-1</number> - </property> - <property name="margin"> - <number>12</number> - </property> - </layout> + <widget class="QWidget" name="mailWidget" native="true"> + <layout class="QVBoxLayout" name="verticalLayout_3"> + <property name="spacing"> + <number>0</number> + </property> + <property name="margin"> + <number>0</number> + </property> + <item> + <layout class="QVBoxLayout" name="mailLayout"> + <property name="spacing"> + <number>-1</number> + </property> + <property name="margin"> + <number>12</number> + </property> + </layout> + </item> + </layout> + </widget> </item> <item> <spacer name="verticalSpacer"> |