summaryrefslogtreecommitdiff
path: root/src/leap/bitmask/gui
diff options
context:
space:
mode:
Diffstat (limited to 'src/leap/bitmask/gui')
-rw-r--r--src/leap/bitmask/gui/eip_preferenceswindow.py36
-rw-r--r--src/leap/bitmask/gui/eip_status.py4
-rw-r--r--src/leap/bitmask/gui/login.py47
-rw-r--r--src/leap/bitmask/gui/mail_status.py103
-rw-r--r--src/leap/bitmask/gui/mainwindow.py264
-rw-r--r--src/leap/bitmask/gui/preferenceswindow.py50
-rw-r--r--src/leap/bitmask/gui/statemachines.py414
-rw-r--r--src/leap/bitmask/gui/ui/eip_status.ui6
-rw-r--r--src/leap/bitmask/gui/ui/eippreferences.ui99
-rw-r--r--src/leap/bitmask/gui/ui/mainwindow.ui172
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>&amp;Select provider:</string>
+ <string>Select &amp;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>&amp;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 &amp;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>&lt;font color='green'&gt;&lt;b&gt;Automatic EIP start saved!&lt;/b&gt;&lt;/font&gt;</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>&lt;Select provider&gt;</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">