summaryrefslogtreecommitdiff
path: root/src/leap/bitmask/gui
diff options
context:
space:
mode:
authorKali Kaneko <kali@leap.se>2013-09-12 14:29:32 +0200
committerKali Kaneko <kali@leap.se>2013-09-12 14:29:32 +0200
commitb1a2ec9a672d6f097d56d039d164918508f72ceb (patch)
tree0e259ada6c41b5e660f2e64188238acf4594f140 /src/leap/bitmask/gui
parent4951157e3740b42ecef6775c8927b912a6ee8870 (diff)
parent9568093138c85212e15d50ade5d5fc7dcec9ff6e (diff)
Merge branch 'master' into debian
Conflicts: pkg/requirements.pip setup.py
Diffstat (limited to 'src/leap/bitmask/gui')
-rw-r--r--src/leap/bitmask/gui/loggerwindow.py3
-rw-r--r--src/leap/bitmask/gui/mainwindow.py167
-rw-r--r--src/leap/bitmask/gui/preferenceswindow.py337
-rw-r--r--src/leap/bitmask/gui/statuspanel.py55
-rw-r--r--src/leap/bitmask/gui/ui/mainwindow.ui2
-rw-r--r--src/leap/bitmask/gui/ui/preferences.ui211
-rw-r--r--src/leap/bitmask/gui/ui/wizard.ui93
-rw-r--r--src/leap/bitmask/gui/wizard.py69
8 files changed, 604 insertions, 333 deletions
diff --git a/src/leap/bitmask/gui/loggerwindow.py b/src/leap/bitmask/gui/loggerwindow.py
index 9b4ba55d..ece4cad6 100644
--- a/src/leap/bitmask/gui/loggerwindow.py
+++ b/src/leap/bitmask/gui/loggerwindow.py
@@ -146,7 +146,8 @@ class LoggerWindow(QtGui.QDialog):
Lets the user save the current log to a file
"""
fileName, filtr = QtGui.QFileDialog.getSaveFileName(
- self, self.tr("Save As"))
+ self, self.tr("Save As"),
+ options=QtGui.QFileDialog.DontUseNativeDialog)
if fileName:
try:
diff --git a/src/leap/bitmask/gui/mainwindow.py b/src/leap/bitmask/gui/mainwindow.py
index c832887a..0950462b 100644
--- a/src/leap/bitmask/gui/mainwindow.py
+++ b/src/leap/bitmask/gui/mainwindow.py
@@ -14,9 +14,8 @@
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
-
"""
-Main window for the leap client
+Main window for Bitmask.
"""
import logging
import os
@@ -59,7 +58,7 @@ from leap.bitmask.services.eip.vpnlaunchers import \
EIPNoPolkitAuthAgentAvailable
from leap.bitmask.services.eip.vpnlaunchers import EIPNoTunKextLoaded
-from leap.bitmask.util import __version__ as VERSION
+from leap.bitmask import __version__ as VERSION
from leap.bitmask.util.keyring_helpers import has_keyring
from leap.bitmask.util.leap_log_handler import LeapLogHandler
@@ -102,6 +101,7 @@ class MainWindow(QtGui.QMainWindow):
raise_window = QtCore.Signal([])
soledad_ready = QtCore.Signal([])
mail_client_logged_in = QtCore.Signal([])
+ logout = QtCore.Signal([])
# We use this flag to detect abnormal terminations
user_stopped_eip = False
@@ -254,14 +254,20 @@ class MainWindow(QtGui.QMainWindow):
self._action_eip_provider = QtGui.QAction(
self.tr("No default provider"), self)
self._action_eip_provider.setEnabled(False)
+
self._action_eip_status = QtGui.QAction(
self.tr("Encrypted internet is OFF"),
self)
self._action_eip_status.setEnabled(False)
-
self._status_panel.set_action_eip_status(
self._action_eip_status)
+ self._action_mail_status = QtGui.QAction(
+ self.tr("Encrypted Mail is OFF"), self)
+ self._action_mail_status.setEnabled(False)
+ self._status_panel.set_action_mail_status(
+ self._action_mail_status)
+
self._action_eip_startstop = QtGui.QAction(
self.tr("Turn OFF"), self)
self._action_eip_startstop.triggered.connect(
@@ -270,6 +276,9 @@ class MainWindow(QtGui.QMainWindow):
self._status_panel.set_action_eip_startstop(
self._action_eip_startstop)
+ self._action_preferences = QtGui.QAction(self.tr("Preferences"), self)
+ self._action_preferences.triggered.connect(self._show_preferences)
+
self._action_visible = QtGui.QAction(self.tr("Hide Main Window"), self)
self._action_visible.triggered.connect(self._toggle_visible)
@@ -284,26 +293,26 @@ class MainWindow(QtGui.QMainWindow):
# Services signals/slots connection
self.new_updates.connect(self._react_to_new_updates)
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)
################################# end Qt Signals connection ########
- # Enable the password change when soledad is ready
- self.soledad_ready.connect(
- partial(self.ui.btnPreferences.setEnabled, True))
-
init_platform()
self._wizard = None
self._wizard_firstrun = False
self._logger_window = None
- self._preferences_window = None
self._bypass_checks = bypass_checks
self._soledad = None
+ self._soledad_ready = False
self._keymanager = None
+ self._smtp_service = None
self._imap_service = None
self._login_defer = None
@@ -419,7 +428,26 @@ class MainWindow(QtGui.QMainWindow):
Displays the preferences window.
"""
- PreferencesWindow(self, self._srp_auth, self._soledad).show()
+ preferences_window = PreferencesWindow(
+ self, self._srp_auth, self._settings, self._standalone)
+
+ if self._soledad_ready:
+ preferences_window.set_soledad_ready(self._soledad)
+ else:
+ self.soledad_ready.connect(
+ lambda: preferences_window.set_soledad_ready(self._soledad))
+
+ preferences_window.show()
+
+ def _set_soledad_ready(self):
+ """
+ SLOT
+ TRIGGERS:
+ self.soledad_ready
+
+ It sets the soledad object as ready to use.
+ """
+ self._soledad_ready = True
def _uncheck_logger_button(self):
"""
@@ -491,7 +519,8 @@ class MainWindow(QtGui.QMainWindow):
"""
# XXX: May be this can be divided into two methods?
- self._login_widget.set_providers(self._configured_providers())
+ providers = self._settings.get_configured_providers()
+ self._login_widget.set_providers(providers)
self._show_systray()
self.show()
if IS_MAC:
@@ -589,10 +618,8 @@ class MainWindow(QtGui.QMainWindow):
self._systray.setVisible(True)
return
- # Placeholder actions
- # They are temporary to display the tray as designed
- preferences_action = QtGui.QAction(self.tr("Preferences"), self)
- preferences_action.setEnabled(False)
+ # Placeholder action
+ # It is temporary to display the tray as designed
help_action = QtGui.QAction(self.tr("Help"), self)
help_action.setEnabled(False)
@@ -602,8 +629,9 @@ class MainWindow(QtGui.QMainWindow):
systrayMenu.addAction(self._action_eip_provider)
systrayMenu.addAction(self._action_eip_status)
systrayMenu.addAction(self._action_eip_startstop)
+ systrayMenu.addAction(self._action_mail_status)
systrayMenu.addSeparator()
- systrayMenu.addAction(preferences_action)
+ systrayMenu.addAction(self._action_preferences)
systrayMenu.addAction(help_action)
systrayMenu.addSeparator()
systrayMenu.addAction(self.ui.action_log_out)
@@ -736,34 +764,14 @@ class MainWindow(QtGui.QMainWindow):
QtGui.QMainWindow.closeEvent(self, e)
- def _configured_providers(self):
- """
- Returns the available providers based on the file structure
-
- :rtype: list
- """
-
- # TODO: check which providers have a valid certificate among
- # other things, not just the directories
- providers = []
- try:
- providers = os.listdir(
- os.path.join(self._provider_config.get_path_prefix(),
- "leap",
- "providers"))
- except Exception as e:
- logger.debug("Error listing providers, assume there are none. %r"
- % (e,))
-
- return providers
-
def _first_run(self):
"""
Returns True if there are no configured providers. False otherwise
:rtype: bool
"""
- has_provider_on_disk = len(self._configured_providers()) != 0
+ providers = self._settings.get_configured_providers()
+ has_provider_on_disk = len(providers) != 0
is_proper_provider = self._settings.get_properprovider()
return not (has_provider_on_disk and is_proper_provider)
@@ -891,6 +899,8 @@ class MainWindow(QtGui.QMainWindow):
logger.debug("Cancelling login defer.")
self._login_defer.cancel()
+ self._login_widget.set_status(self.tr("Log in cancelled by the user."))
+
def _provider_config_loaded(self, data):
"""
SLOT
@@ -912,7 +922,7 @@ class MainWindow(QtGui.QMainWindow):
self._srp_auth.logout_finished.connect(
self._done_logging_out)
- # TODO: Add errback!
+ # TODO Add errback!
self._login_defer = self._srp_auth.authenticate(username, password)
else:
self._login_widget.set_status(
@@ -979,7 +989,7 @@ class MainWindow(QtGui.QMainWindow):
"""
passed = data[self._soledad_bootstrapper.PASSED_KEY]
if not passed:
- # TODO: display in the GUI:
+ # TODO display in the GUI:
# should pass signal to a slot in status_panel
# that sets the global status
logger.error("Soledad failed to start: %s" %
@@ -1040,13 +1050,13 @@ class MainWindow(QtGui.QMainWindow):
True)
else:
if self._enabled_services.count(self.MX_SERVICE) > 0:
- pass # TODO: show MX status
+ pass # TODO show MX status
#self._status_panel.set_eip_status(
# self.tr("%s does not support MX") %
# (self._provider_config.get_domain(),),
# error=True)
else:
- pass # TODO: show MX status
+ pass # TODO show MX status
#self._status_panel.set_eip_status(
# self.tr("MX is disabled"))
@@ -1073,25 +1083,43 @@ class MainWindow(QtGui.QMainWindow):
logger.debug("Done bootstrapping SMTP")
hosts = self._smtp_config.get_hosts()
- # TODO: handle more than one host and define how to choose
+ # 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]
- # TODO: pick local smtp port in a better way
- # TODO: Make the encrypted_only configurable
+ # TODO move the start to _start_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
client_cert = self._eip_config.get_client_cert_path(
self._provider_config)
- setup_smtp_relay(port=2013,
- keymanager=self._keymanager,
- smtp_host=host,
- smtp_port=port,
- smtp_cert=client_cert,
- smtp_key=client_cert,
- encrypted_only=False)
+ self._smtp_service = setup_smtp_relay(
+ port=2013,
+ keymanager=self._keymanager,
+ smtp_host=host,
+ smtp_port=port,
+ smtp_cert=client_cert,
+ smtp_key=client_cert,
+ encrypted_only=False)
+
+ 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_service.doStop()
###################################################################
# Service control methods: imap
@@ -1100,7 +1128,7 @@ class MainWindow(QtGui.QMainWindow):
"""
SLOT
TRIGGERS:
- soledad_ready
+ self.soledad_ready
"""
if self._provider_config.provides_mx() and \
self._enabled_services.count(self.MX_SERVICE) > 0:
@@ -1109,17 +1137,6 @@ class MainWindow(QtGui.QMainWindow):
self._imap_service = imap.start_imap_service(
self._soledad,
self._keymanager)
- else:
- if self._enabled_services.count(self.MX_SERVICE) > 0:
- pass # TODO: show MX status
- #self._status_panel.set_eip_status(
- # self.tr("%s does not support MX") %
- # (self._provider_config.get_domain(),),
- # error=True)
- else:
- pass # TODO: show MX status
- #self._status_panel.set_eip_status(
- # self.tr("MX is disabled"))
def _on_mail_client_logged_in(self, req):
"""
@@ -1131,13 +1148,27 @@ class MainWindow(QtGui.QMainWindow):
"""
SLOT
TRIGGERS:
- mail_client_logged_in
+ 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()
+ 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()
+
# end service control methods (imap)
###################################################################
@@ -1149,7 +1180,8 @@ class MainWindow(QtGui.QMainWindow):
:rtype: tuple (str, str) (host, port)
"""
- # TODO: make this properly multiplatform
+ # TODO make this properly multiplatform
+ # TODO get this out of gui/
if platform.system() == "Windows":
host = "localhost"
@@ -1402,6 +1434,7 @@ class MainWindow(QtGui.QMainWindow):
# XXX: If other defers are doing authenticated stuff, this
# might conflict with those. CHECK!
threads.deferToThread(self._srp_auth.logout)
+ self.logout.emit()
def _done_logging_out(self, ok, message):
"""
@@ -1576,7 +1609,7 @@ class MainWindow(QtGui.QMainWindow):
"""
Cleanup and tidely close the main window before quitting.
"""
- # TODO: separate the shutting down of services from the
+ # TODO separate the shutting down of services from the
# UI stuff.
self._cleanup_and_quit()
diff --git a/src/leap/bitmask/gui/preferenceswindow.py b/src/leap/bitmask/gui/preferenceswindow.py
index 67448768..1becfb18 100644
--- a/src/leap/bitmask/gui/preferenceswindow.py
+++ b/src/leap/bitmask/gui/preferenceswindow.py
@@ -18,14 +18,20 @@
"""
Preferences window
"""
+import os
import logging
from functools import partial
-from PySide import QtGui
+from PySide import QtCore, QtGui
from leap.bitmask.gui.ui_preferences import Ui_Preferences
from leap.soledad.client import NoStorageSecret
from leap.bitmask.crypto.srpauth import SRPAuthBadPassword
+from leap.bitmask.util.password import basic_password_checks
+from leap.bitmask.services import get_supported
+from leap.bitmask.config.providerconfig import ProviderConfig
+from leap.bitmask.services.eip.eipconfig import EIPConfig, VPNGatewaySelector
+from leap.bitmask.services import get_service_display_name
logger = logging.getLogger(__name__)
@@ -37,58 +43,62 @@ class PreferencesWindow(QtGui.QDialog):
WEAK_PASSWORDS = ("123456", "qweasd", "qwerty", "password")
- def __init__(self, parent, srp_auth, soledad):
+ def __init__(self, parent, srp_auth, leap_settings, standalone):
"""
: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 soledad: Soledad object configured in the main app.
- :type soledad: Soledad
+ :param standalone: If True, the application is running as standalone
+ and the preferences dialog should display some
+ messages according to this.
+ :type standalone: bool
"""
QtGui.QDialog.__init__(self, parent)
+ self.AUTOMATIC_GATEWAY_LABEL = self.tr("Automatic")
self._srp_auth = srp_auth
- self._soledad = soledad
+ self._settings = leap_settings
+ self._standalone = standalone
+ self._soledad = None
# Load UI
self.ui = Ui_Preferences()
self.ui.setupUi(self)
self.ui.lblPasswordChangeStatus.setVisible(False)
+ self.ui.lblProvidersServicesStatus.setVisible(False)
+ self.ui.lblProvidersGatewayStatus.setVisible(False)
+
+ self._selected_services = set()
# Connections
self.ui.pbChangePassword.clicked.connect(self._change_password)
+ self.ui.cbProvidersServices.currentIndexChanged[unicode].connect(
+ self._populate_services)
+ self.ui.cbProvidersGateway.currentIndexChanged[unicode].connect(
+ self._populate_gateways)
- def _basic_password_checks(self, username, password, password2):
- """
- Performs basic password checks to avoid really easy passwords.
+ self.ui.cbGateways.currentIndexChanged[unicode].connect(
+ lambda x: self.ui.lblProvidersGatewayStatus.setVisible(False))
- :param username: username provided at the registrarion form
- :type username: str
- :param password: password from the registration form
- :type password: str
- :param password2: second password from the registration form
- :type password: str
+ if not self._settings.get_configured_providers():
+ self.ui.gbEnabledServices.setEnabled(False)
+ else:
+ self._add_configured_providers()
- :returns: True and empty message if all the checks pass,
- False and an error message otherwise
- :rtype: tuple(bool, str)
+ def set_soledad_ready(self, soledad):
"""
- message = None
-
- if message is None and password != password2:
- message = self.tr("Passwords don't match")
+ SLOT
+ TRIGGERS:
+ parent.soledad_ready
- if message is None and len(password) < 6:
- message = self.tr("Password too short")
+ It sets the soledad object as ready to use.
- if message is None and password in self.WEAK_PASSWORDS:
- message = self.tr("Password too easy")
-
- if message is None and username == password:
- message = self.tr("Password equal to username")
-
- return message is None, message
+ :param soledad: Soledad object configured in the main app.
+ :type soledad: Soledad
+ """
+ self._soledad = soledad
+ self.ui.gbPasswordChange.setEnabled(True)
def _set_password_change_status(self, status, error=False, success=False):
"""
@@ -116,7 +126,7 @@ class PreferencesWindow(QtGui.QDialog):
:type disable: bool
"""
if disable:
- self._set_password_change_disable(self.tr("Changing password..."))
+ self._set_password_change_status(self.tr("Changing password..."))
self.ui.leCurrentPassword.setEnabled(not disable)
self.ui.leNewPassword.setEnabled(not disable)
@@ -125,6 +135,10 @@ class PreferencesWindow(QtGui.QDialog):
def _change_password(self):
"""
+ SLOT
+ TRIGGERS:
+ self.ui.pbChangePassword.clicked
+
Changes the user's password if the inputboxes are correctly filled.
"""
username = self._srp_auth.get_username()
@@ -132,8 +146,7 @@ class PreferencesWindow(QtGui.QDialog):
new_password = self.ui.leNewPassword.text()
new_password2 = self.ui.leNewPassword2.text()
- ok, msg = self._basic_password_checks(
- username, new_password, new_password2)
+ ok, msg = basic_password_checks(username, new_password, new_password2)
if not ok:
self._set_changing_password(False)
@@ -165,7 +178,7 @@ class PreferencesWindow(QtGui.QDialog):
self._set_password_change_status(
self.tr("Password changed successfully."), success=True)
- self._clear_inputs()
+ self._clear_password_inputs()
self._set_changing_password(False)
def _change_password_problem(self, failure):
@@ -187,10 +200,264 @@ class PreferencesWindow(QtGui.QDialog):
self._set_changing_password(False)
failure.trap(Exception)
- def _clear_inputs(self):
+ def _clear_password_inputs(self):
"""
Clear the contents of the inputs.
"""
self.ui.leCurrentPassword.setText("")
self.ui.leNewPassword.setText("")
self.ui.leNewPassword2.setText("")
+
+ def _set_providers_services_status(self, status, success=False):
+ """
+ Sets the status label for the password change.
+
+ :param status: status message to display, can be HTML
+ :type status: str
+ :param success: is set to True if we should display the
+ message as green
+ :type success: bool
+ """
+ if success:
+ status = "<font color='green'><b>%s</b></font>" % (status,)
+
+ self.ui.lblProvidersServicesStatus.setVisible(True)
+ self.ui.lblProvidersServicesStatus.setText(status)
+
+ def _set_providers_gateway_status(self, status, success=False,
+ error=False):
+ """
+ Sets the status label for the gateway change.
+
+ :param status: status message to display, can be HTML
+ :type status: str
+ :param success: is set to True if we should display the
+ message as green
+ :type success: bool
+ :param error: is set to True if we should display the
+ message as red
+ :type error: bool
+ """
+ if success:
+ status = "<font color='green'><b>%s</b></font>" % (status,)
+ elif error:
+ status = "<font color='red'><b>%s</b></font>" % (status,)
+
+ self.ui.lblProvidersGatewayStatus.setVisible(True)
+ self.ui.lblProvidersGatewayStatus.setText(status)
+
+ def _add_configured_providers(self):
+ """
+ Add the client's configured providers to the providers combo boxes.
+ """
+ self.ui.cbProvidersServices.clear()
+ self.ui.cbProvidersGateway.clear()
+ for provider in self._settings.get_configured_providers():
+ self.ui.cbProvidersServices.addItem(provider)
+ self.ui.cbProvidersGateway.addItem(provider)
+
+ def _service_selection_changed(self, service, state):
+ """
+ SLOT
+ TRIGGER: service_checkbox.stateChanged
+ Adds the service to the state if the state is checked, removes
+ it otherwise
+
+ :param service: service to handle
+ :type service: str
+ :param state: state of the checkbox
+ :type state: int
+ """
+ if state == QtCore.Qt.Checked:
+ self._selected_services = \
+ self._selected_services.union(set([service]))
+ else:
+ self._selected_services = \
+ self._selected_services.difference(set([service]))
+
+ # We hide the maybe-visible status label after a change
+ self.ui.lblProvidersServicesStatus.setVisible(False)
+
+ def _populate_services(self, domain):
+ """
+ SLOT
+ TRIGGERS:
+ self.ui.cbProvidersServices.currentIndexChanged[unicode]
+
+ Loads the services that the provider provides into the UI for
+ the user to enable or disable.
+
+ :param domain: the domain of the provider to load services from.
+ :type domain: str
+ """
+ # We hide the maybe-visible status label after a change
+ self.ui.lblProvidersServicesStatus.setVisible(False)
+
+ if not domain:
+ return
+
+ provider_config = self._get_provider_config(domain)
+ if provider_config is None:
+ return
+
+ # set the proper connection for the 'save' button
+ try:
+ self.ui.pbSaveServices.clicked.disconnect()
+ except RuntimeError:
+ pass # Signal was not connected
+
+ save_services = partial(self._save_enabled_services, domain)
+ self.ui.pbSaveServices.clicked.connect(save_services)
+
+ services = get_supported(provider_config.get_services())
+ services_conf = self._settings.get_enabled_services(domain)
+
+ # discard changes if other provider is selected
+ self._selected_services = set()
+
+ # from: http://stackoverflow.com/a/13103617/687989
+ # remove existing checkboxes
+ layout = self.ui.vlServices
+ for i in reversed(range(layout.count())):
+ layout.itemAt(i).widget().setParent(None)
+
+ # add one checkbox per service and set the current configured value
+ for service in services:
+ try:
+ checkbox = QtGui.QCheckBox(self)
+ service_label = get_service_display_name(
+ service, self._standalone)
+ checkbox.setText(service_label)
+
+ self.ui.vlServices.addWidget(checkbox)
+ checkbox.stateChanged.connect(
+ partial(self._service_selection_changed, service))
+
+ checkbox.setChecked(service in services_conf)
+ except ValueError:
+ logger.error("Something went wrong while trying to "
+ "load service %s" % (service,))
+
+ def _save_enabled_services(self, provider):
+ """
+ SLOT
+ TRIGGERS:
+ self.ui.pbSaveServices.clicked
+
+ Saves the new enabled services settings to the configuration file.
+
+ :param provider: the provider config that we need to save.
+ :type provider: str
+ """
+ services = list(self._selected_services)
+ self._settings.set_enabled_services(provider, services)
+
+ msg = self.tr(
+ "Services settings for provider '{0}' saved.".format(provider))
+ logger.debug(msg)
+ self._set_providers_services_status(msg, success=True)
+
+ def _get_provider_config(self, domain):
+ """
+ Helper to return a valid Provider Config from the domain name.
+
+ :param domain: the domain name of the provider.
+ :type domain: str
+
+ :rtype: ProviderConfig or None if there is a problem loading the config
+ """
+ provider_config = ProviderConfig()
+ provider_config_path = os.path.join(
+ "leap", "providers", domain, "provider.json")
+
+ if not provider_config.load(provider_config_path):
+ provider_config = None
+
+ return provider_config
+
+ def _save_selected_gateway(self, provider):
+ """
+ SLOT
+ TRIGGERS:
+ self.ui.pbSaveGateway.clicked
+
+ Saves the new gateway setting to the configuration file.
+
+ :param provider: the provider config that we need to save.
+ :type provider: str
+ """
+ gateway = self.ui.cbGateways.currentText()
+
+ if gateway == self.AUTOMATIC_GATEWAY_LABEL:
+ gateway = self._settings.GATEWAY_AUTOMATIC
+ else:
+ idx = self.ui.cbGateways.currentIndex()
+ gateway = self.ui.cbGateways.itemData(idx)
+
+ self._settings.set_selected_gateway(provider, gateway)
+
+ msg = self.tr(
+ "Gateway settings for provider '{0}' saved.".format(provider))
+ logger.debug(msg)
+ self._set_providers_gateway_status(msg, success=True)
+
+ def _populate_gateways(self, domain):
+ """
+ SLOT
+ TRIGGERS:
+ self.ui.cbProvidersGateway.currentIndexChanged[unicode]
+
+ Loads the gateways that the provider provides into the UI for
+ the user to select.
+
+ :param domain: the domain of the provider to load gateways from.
+ :type domain: str
+ """
+ # We hide the maybe-visible status label after a change
+ self.ui.lblProvidersGatewayStatus.setVisible(False)
+
+ if not domain:
+ return
+
+ try:
+ # disconnect prevoiusly connected save method
+ self.ui.pbSaveGateway.clicked.disconnect()
+ except RuntimeError:
+ pass # Signal was not connected
+
+ # set the proper connection for the 'save' button
+ save_gateway = partial(self._save_selected_gateway, domain)
+ self.ui.pbSaveGateway.clicked.connect(save_gateway)
+
+ eip_config = EIPConfig()
+ provider_config = self._get_provider_config(domain)
+
+ eip_config_path = os.path.join("leap", "providers",
+ domain, "eip-service.json")
+ api_version = provider_config.get_api_version()
+ eip_config.set_api_version(api_version)
+ eip_loaded = eip_config.load(eip_config_path)
+
+ if not eip_loaded or provider_config is None:
+ self._set_providers_gateway_status(
+ self.tr("There was a problem with configuration files."),
+ error=True)
+ return
+
+ gateways = VPNGatewaySelector(eip_config).get_gateways_list()
+ logger.debug(gateways)
+
+ self.ui.cbGateways.clear()
+ self.ui.cbGateways.addItem(self.AUTOMATIC_GATEWAY_LABEL)
+
+ # Add the available gateways and
+ # select the one stored in configuration file.
+ selected_gateway = self._settings.get_selected_gateway(domain)
+ index = 0
+ for idx, (gw_name, gw_ip) in enumerate(gateways):
+ gateway = "{0} ({1})".format(gw_name, gw_ip)
+ self.ui.cbGateways.addItem(gateway, gw_ip)
+ if gw_ip == selected_gateway:
+ index = idx + 1
+
+ self.ui.cbGateways.setCurrentIndex(index)
diff --git a/src/leap/bitmask/gui/statuspanel.py b/src/leap/bitmask/gui/statuspanel.py
index 9352eb04..3a91f08e 100644
--- a/src/leap/bitmask/gui/statuspanel.py
+++ b/src/leap/bitmask/gui/statuspanel.py
@@ -356,6 +356,16 @@ class StatusPanelWidget(QtGui.QWidget):
leap_assert_type(action_eip_status, QtGui.QAction)
self._action_eip_status = action_eip_status
+ def set_action_mail_status(self, action_mail_status):
+ """
+ Sets the action_mail_status to use.
+
+ :param action_mail_status: action_mail_status to be used
+ :type action_mail_status: QtGui.QAction
+ """
+ leap_assert_type(action_mail_status, QtGui.QAction)
+ self._action_mail_status = action_mail_status
+
def set_global_status(self, status, error=False):
"""
Sets the global status label.
@@ -538,6 +548,27 @@ class StatusPanelWidget(QtGui.QWidget):
def set_provider(self, provider):
self.ui.lblProvider.setText(provider)
+ def _set_mail_status(self, status, ready=False):
+ """
+ Sets the Encrypted Mail status in the label and in the tray icon.
+
+ :param status: the status text to display
+ :type status: unicode
+ :param ready: if mx is ready or not.
+ :type ready: bool
+ """
+ self.ui.lblMailStatus.setText(status)
+
+ tray_status = self.tr('Encrypted Mail is OFF')
+
+ icon = QtGui.QPixmap(self.MAIL_OFF_ICON)
+ if ready:
+ icon = QtGui.QPixmap(self.MAIL_ON_ICON)
+ tray_status = self.tr('Encrypted Mail is ON')
+
+ self.ui.lblMailIcon.setPixmap(icon)
+ self._action_mail_status.setText(tray_status)
+
def _mail_handle_soledad_events(self, req):
"""
Callback for ...
@@ -557,7 +588,7 @@ class StatusPanelWidget(QtGui.QWidget):
:param req: Request type
:type req: leap.common.events.events_pb2.SignalRequest
"""
- self.ui.lblMailStatus.setText(self.tr("Starting..."))
+ self._set_mail_status(self.tr("Starting..."))
ext_status = ""
@@ -591,7 +622,12 @@ class StatusPanelWidget(QtGui.QWidget):
:param req: Request type
:type req: leap.common.events.events_pb2.SignalRequest
"""
- self.ui.lblMailStatus.setText(self.tr("Starting..."))
+ # We want to ignore this kind of events once everything has
+ # started
+ if self._smtp_started and self._imap_started:
+ return
+
+ self._set_mail_status(self.tr("Starting..."))
ext_status = ""
@@ -639,14 +675,11 @@ class StatusPanelWidget(QtGui.QWidget):
ext_status = self.tr("SMTP has started...")
self._smtp_started = True
if self._smtp_started and self._imap_started:
- self.ui.lblMailStatus.setText(self.tr("ON"))
- self.ui.lblMailIcon.setPixmap(QtGui.QPixmap(self.MAIL_ON_ICON))
- self.ui.lblMailIcon.setPixmap(
- QtGui.QPixmap(":/images/mail-locked.png"))
+ self._set_mail_status(self.tr("ON"), ready=True)
ext_status = ""
elif req.event == proto.SMTP_SERVICE_FAILED_TO_START:
ext_status = self.tr("SMTP failed to start, check the logs.")
- self.ui.lblMailStatus.setText(self.tr("Failed"))
+ self._set_mail_status(self.tr("Failed"))
else:
leap_assert(False,
"Don't know how to handle this state: %s"
@@ -679,19 +712,17 @@ class StatusPanelWidget(QtGui.QWidget):
ext_status = self.tr("IMAP has started...")
self._imap_started = True
if self._smtp_started and self._imap_started:
- self.ui.lblMailStatus.setText(self.tr("ON"))
- self.ui.lblMailIcon.setPixmap(QtGui.QPixmap(self.MAIL_ON_ICON))
+ self._set_mail_status(self.tr("ON"), ready=True)
ext_status = ""
elif req.event == proto.IMAP_SERVICE_FAILED_TO_START:
ext_status = self.tr("IMAP failed to start, check the logs.")
- self.ui.lblMailStatus.setText(self.tr("Failed"))
+ self._set_mail_status(self.tr("Failed"))
elif req.event == proto.IMAP_UNREAD_MAIL:
if self._smtp_started and self._imap_started:
self.ui.lblUnread.setText(
self.tr("%s Unread Emails") % (req.content))
self.ui.lblUnread.setVisible(req.content != "0")
- self.ui.lblMailStatus.setText(self.tr("ON"))
- self.ui.lblMailIcon.setPixmap(QtGui.QPixmap(self.MAIL_ON_ICON))
+ self._set_mail_status(self.tr("ON"), ready=True)
else:
leap_assert(False,
"Don't know how to handle this state: %s"
diff --git a/src/leap/bitmask/gui/ui/mainwindow.ui b/src/leap/bitmask/gui/ui/mainwindow.ui
index 834a562e..17837642 100644
--- a/src/leap/bitmask/gui/ui/mainwindow.ui
+++ b/src/leap/bitmask/gui/ui/mainwindow.ui
@@ -217,7 +217,7 @@
<item>
<widget class="QPushButton" name="btnPreferences">
<property name="enabled">
- <bool>false</bool>
+ <bool>true</bool>
</property>
<property name="text">
<string>Preferences</string>
diff --git a/src/leap/bitmask/gui/ui/preferences.ui b/src/leap/bitmask/gui/ui/preferences.ui
index 8c63ccad..e66a2d68 100644
--- a/src/leap/bitmask/gui/ui/preferences.ui
+++ b/src/leap/bitmask/gui/ui/preferences.ui
@@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
- <width>451</width>
- <height>267</height>
+ <width>503</width>
+ <height>529</height>
</rect>
</property>
<property name="windowTitle">
@@ -17,9 +17,149 @@
<iconset resource="../../../../../data/resources/mainwindow.qrc">
<normaloff>:/images/mask-icon.png</normaloff>:/images/mask-icon.png</iconset>
</property>
- <layout class="QVBoxLayout" name="verticalLayout">
- <item>
+ <layout class="QGridLayout" name="gridLayout_3">
+ <item row="5" column="0">
+ <spacer name="verticalSpacer">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item row="1" column="0">
+ <widget class="QGroupBox" name="gbGatewaySelector">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="title">
+ <string>Select gateway for provider</string>
+ </property>
+ <property name="checkable">
+ <bool>false</bool>
+ </property>
+ <layout class="QGridLayout" name="gridLayout">
+ <item row="0" column="1" colspan="2">
+ <widget class="QComboBox" name="cbProvidersGateway">
+ <item>
+ <property name="text">
+ <string>&lt;Select provider&gt;</string>
+ </property>
+ </item>
+ </widget>
+ </item>
+ <item row="0" column="0">
+ <widget class="QLabel" name="lblSelectProvider">
+ <property name="text">
+ <string>&amp;Select provider:</string>
+ </property>
+ <property name="buddy">
+ <cstring>cbProvidersGateway</cstring>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>Select gateway:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1" colspan="2">
+ <widget class="QComboBox" name="cbGateways">
+ <item>
+ <property name="text">
+ <string>Automatic</string>
+ </property>
+ </item>
+ </widget>
+ </item>
+ <item row="5" column="2">
+ <widget class="QPushButton" name="pbSaveGateway">
+ <property name="text">
+ <string>Save this provider settings</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0" colspan="3">
+ <widget class="QLabel" name="lblProvidersGatewayStatus">
+ <property name="text">
+ <string>&lt; Providers Gateway Status &gt;</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item row="4" column="0">
+ <widget class="QGroupBox" name="gbEnabledServices">
+ <property name="title">
+ <string>Enabled services</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout_2">
+ <item row="3" column="1">
+ <widget class="QPushButton" name="pbSaveServices">
+ <property name="text">
+ <string>Save this provider settings</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0" colspan="2">
+ <widget class="QGroupBox" name="gbServices">
+ <property name="title">
+ <string>Services</string>
+ </property>
+ <property name="flat">
+ <bool>false</bool>
+ </property>
+ <layout class="QGridLayout" name="gridLayout_4">
+ <item row="0" column="0">
+ <layout class="QVBoxLayout" name="vlServices"/>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QComboBox" name="cbProvidersServices">
+ <item>
+ <property name="text">
+ <string>&lt;Select provider&gt;</string>
+ </property>
+ </item>
+ </widget>
+ </item>
+ <item row="0" column="0">
+ <widget class="QLabel" name="label_2">
+ <property name="text">
+ <string>Select provider:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0" colspan="2">
+ <widget class="QLabel" name="lblProvidersServicesStatus">
+ <property name="text">
+ <string>&lt; Providers Services Status &gt;</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item row="0" column="0">
<widget class="QGroupBox" name="gbPasswordChange">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
<property name="title">
<string>Password Change</string>
</property>
@@ -98,69 +238,6 @@
</layout>
</widget>
</item>
- <item>
- <widget class="QGroupBox" name="gbGatewaySelector">
- <property name="enabled">
- <bool>false</bool>
- </property>
- <property name="title">
- <string>Select gateway for provider</string>
- </property>
- <property name="checkable">
- <bool>false</bool>
- </property>
- <layout class="QGridLayout" name="gridLayout">
- <item row="0" column="0">
- <widget class="QLabel" name="lblSelectProvider">
- <property name="text">
- <string>&amp;Select provider:</string>
- </property>
- <property name="buddy">
- <cstring>cbProviders</cstring>
- </property>
- </widget>
- </item>
- <item row="0" column="1">
- <widget class="QComboBox" name="cbProviders">
- <item>
- <property name="text">
- <string>&lt;Select provider&gt;</string>
- </property>
- </item>
- </widget>
- </item>
- <item row="1" column="0">
- <widget class="QLabel" name="label">
- <property name="text">
- <string>Select gateway:</string>
- </property>
- </widget>
- </item>
- <item row="1" column="1">
- <widget class="QComboBox" name="comboBox">
- <item>
- <property name="text">
- <string>Automatic</string>
- </property>
- </item>
- </widget>
- </item>
- </layout>
- </widget>
- </item>
- <item>
- <spacer name="verticalSpacer">
- <property name="orientation">
- <enum>Qt::Vertical</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>20</width>
- <height>40</height>
- </size>
- </property>
- </spacer>
- </item>
</layout>
</widget>
<resources>
diff --git a/src/leap/bitmask/gui/ui/wizard.ui b/src/leap/bitmask/gui/ui/wizard.ui
index 3b8f1215..2a412784 100644
--- a/src/leap/bitmask/gui/ui/wizard.ui
+++ b/src/leap/bitmask/gui/ui/wizard.ui
@@ -183,7 +183,7 @@
<item row="2" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
- <string>Can we stablish a secure connection?</string>
+ <string>Can we establish a secure connection?</string>
</property>
</widget>
</item>
@@ -740,97 +740,6 @@
</item>
</layout>
</widget>
- <widget class="QWizardPage" name="finish_page">
- <property name="title">
- <string>Congratulations!</string>
- </property>
- <property name="subTitle">
- <string>You have successfully configured Bitmask.</string>
- </property>
- <attribute name="pageId">
- <string notr="true">6</string>
- </attribute>
- <layout class="QGridLayout" name="gridLayout_10">
- <item row="1" column="0">
- <spacer name="horizontalSpacer_4">
- <property name="orientation">
- <enum>Qt::Horizontal</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>40</width>
- <height>20</height>
- </size>
- </property>
- </spacer>
- </item>
- <item row="0" column="1">
- <spacer name="verticalSpacer_9">
- <property name="orientation">
- <enum>Qt::Vertical</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>20</width>
- <height>40</height>
- </size>
- </property>
- </spacer>
- </item>
- <item row="1" column="1">
- <widget class="QLabel" name="label_23">
- <property name="text">
- <string/>
- </property>
- <property name="pixmap">
- <pixmap resource="../../../../../data/resources/mainwindow.qrc">:/images/mask-icon.png</pixmap>
- </property>
- </widget>
- </item>
- <item row="1" column="2">
- <widget class="QLabel" name="label_25">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="text">
- <string/>
- </property>
- <property name="pixmap">
- <pixmap resource="../../../../../data/resources/mainwindow.qrc">:/images/Globe.png</pixmap>
- </property>
- </widget>
- </item>
- <item row="3" column="1">
- <spacer name="verticalSpacer_10">
- <property name="orientation">
- <enum>Qt::Vertical</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>20</width>
- <height>40</height>
- </size>
- </property>
- </spacer>
- </item>
- <item row="1" column="3">
- <spacer name="horizontalSpacer_5">
- <property name="orientation">
- <enum>Qt::Horizontal</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>40</width>
- <height>20</height>
- </size>
- </property>
- </spacer>
- </item>
- </layout>
- </widget>
</widget>
<customwidgets>
<customwidget>
diff --git a/src/leap/bitmask/gui/wizard.py b/src/leap/bitmask/gui/wizard.py
index ed6c1da0..e004e6cf 100644
--- a/src/leap/bitmask/gui/wizard.py
+++ b/src/leap/bitmask/gui/wizard.py
@@ -32,8 +32,9 @@ from leap.bitmask.crypto.srpregister import SRPRegister
from leap.bitmask.util.privilege_policies import is_missing_policy_permissions
from leap.bitmask.util.request_helpers import get_content
from leap.bitmask.util.keyring_helpers import has_keyring
+from leap.bitmask.util.password import basic_password_checks
from leap.bitmask.services.eip.providerbootstrapper import ProviderBootstrapper
-from leap.bitmask.services import get_supported
+from leap.bitmask.services import get_service_display_name, get_supported
from ui_wizard import Ui_Wizard
@@ -51,7 +52,6 @@ class Wizard(QtGui.QWizard):
SETUP_PROVIDER_PAGE = 3
REGISTER_USER_PAGE = 4
SERVICES_PAGE = 5
- FINISH_PAGE = 6
WEAK_PASSWORDS = ("123456", "qweasd", "qwerty",
"password")
@@ -83,23 +83,6 @@ class Wizard(QtGui.QWizard):
self.ERROR_ICON = QtGui.QPixmap(":/images/Dialog-error.png")
self.OK_ICON = QtGui.QPixmap(":/images/Dialog-accept.png")
- # Correspondence for services and their name to display
- EIP_LABEL = self.tr("Encrypted Internet")
- MX_LABEL = self.tr("Encrypted Mail")
-
- if self._is_need_eip_password_warning():
- EIP_LABEL += " " + self.tr(
- "(will need admin password to start)")
-
- self.SERVICE_DISPLAY = [
- EIP_LABEL,
- MX_LABEL
- ]
- self.SERVICE_CONFIG = [
- "openvpn",
- "mx"
- ]
-
self._selected_services = set()
self._shown_services = set()
@@ -160,7 +143,7 @@ class Wizard(QtGui.QWizard):
self.page(self.REGISTER_USER_PAGE).setButtonText(
QtGui.QWizard.CommitButton, self.tr("&Next >"))
- self.page(self.FINISH_PAGE).setButtonText(
+ self.page(self.SERVICES_PAGE).setButtonText(
QtGui.QWizard.FinishButton, self.tr("Connect"))
# XXX: Temporary removal for enrollment policy
@@ -199,41 +182,6 @@ class Wizard(QtGui.QWizard):
"""
self.ui.lblPassword2.setFocus()
- def _basic_password_checks(self, username, password, password2):
- """
- Performs basic password checks to avoid really easy passwords.
-
- :param username: username provided at the registrarion form
- :type username: str
- :param password: password from the registration form
- :type password: str
- :param password2: second password from the registration form
- :type password: str
-
- :return: returns True if all the checks pass, False otherwise
- :rtype: bool
- """
- message = None
-
- if message is None and password != password2:
- message = self.tr("Passwords don't match")
-
- if message is None and len(password) < 6:
- message = self.tr("Password too short")
-
- if message is None and password in self.WEAK_PASSWORDS:
- message = self.tr("Password too easy")
-
- if message is None and username == password:
- message = self.tr("Password equal to username")
-
- if message is not None:
- self._set_register_status(message, error=True)
- self._focus_password()
- return False
-
- return True
-
def _register(self):
"""
Performs the registration based on the values provided in the form
@@ -244,7 +192,8 @@ class Wizard(QtGui.QWizard):
password = self.ui.lblPassword.text()
password2 = self.ui.lblPassword2.text()
- if self._basic_password_checks(username, password, password2):
+ ok, msg = basic_password_checks(username, password, password2)
+ if ok:
register = SRPRegister(provider_config=self._provider_config)
register.registration_finished.connect(
self._registration_finished)
@@ -258,6 +207,8 @@ class Wizard(QtGui.QWizard):
self._password = password
self._set_register_status(self.tr("Starting registration..."))
else:
+ self._set_register_status(msg, error=True)
+ self._focus_password()
self.ui.btnRegister.setEnabled(True)
def _set_registration_fields_visibility(self, visible):
@@ -538,8 +489,10 @@ class Wizard(QtGui.QWizard):
try:
if service not in self._shown_services:
checkbox = QtGui.QCheckBox(self)
- service_index = self.SERVICE_CONFIG.index(service)
- checkbox.setText(self.SERVICE_DISPLAY[service_index])
+ service_label = get_service_display_name(
+ service, self.standalone)
+ checkbox.setText(service_label)
+
self.ui.serviceListLayout.addWidget(checkbox)
checkbox.stateChanged.connect(
partial(self._service_selection_changed, service))