diff options
Diffstat (limited to 'src')
20 files changed, 1457 insertions, 749 deletions
| diff --git a/src/leap/bitmask/gui/account.py b/src/leap/bitmask/gui/account.py new file mode 100644 index 00000000..c941c3fa --- /dev/null +++ b/src/leap/bitmask/gui/account.py @@ -0,0 +1,49 @@ +# -*- coding: utf-8 -*- +# Copyright (C) 2014 LEAP +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program.  If not, see <http://www.gnu.org/licenses/>. +""" +A frontend GUI object to hold the current username and domain. +""" + +from leap.bitmask.util import make_address +from leap.bitmask.config.leapsettings import LeapSettings +from leap.bitmask.services import EIP_SERVICE, MX_SERVICE + + +class Account(): + +    def __init__(self, username, domain): +        self._settings = LeapSettings() +        self.username = username +        self.domain = domain + +        if self.username is not None: +            self.address = make_address(self.username, self.domain) +        else: +            self.address = self.domain + +    def services(self): +        """ +        returns a list of service name strings + +        TODO: this should depend not just on the domain +        """ +        return self._settings.get_enabled_services(self.domain) + +    def is_email_enabled(self): +        MX_SERVICE in self.services() + +    def is_eip_enabled(self): +        EIP_SERVICE in self.services() diff --git a/src/leap/bitmask/gui/app.py b/src/leap/bitmask/gui/app.py new file mode 100644 index 00000000..eb1a58d5 --- /dev/null +++ b/src/leap/bitmask/gui/app.py @@ -0,0 +1,66 @@ +# -*- coding: utf-8 -*- +# Copyright (C) 2014 LEAP +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program.  If not, see <http://www.gnu.org/licenses/>. +""" +A single App instances holds the signals that are shared among different +frontend UI components. The App also keeps a reference to the backend object +and the signaler get signals from the backend. +""" +import logging + +from functools import partial +from PySide import QtCore, QtGui + +from leap.bitmask.config.leapsettings import LeapSettings +from leap.bitmask.backend.backend_proxy import BackendProxy +from leap.bitmask.backend.leapsignaler import LeapSignaler + +logger = logging.getLogger(__name__) + + +class App(QtGui.QWidget): + +    # the user has changed which services are enabled for a particular account +    # args: account (Account), active services (list of str) +    service_selection_changed = QtCore.Signal(object, list) + +    def __init__(self): +        QtGui.QWidget.__init__(self) + +        self.settings = LeapSettings() +        self.backend = BackendProxy() +        self.signaler = LeapSignaler() +        self.signaler.start() + +        # periodically check if the backend is alive +        self._backend_checker = QtCore.QTimer(self) +        self._backend_checker.timeout.connect(self._check_backend_status) +        self._backend_checker.start(2000) + +    @QtCore.Slot() +    def _check_backend_status(self): +        """ +        TRIGGERS: +            self._backend_checker.timeout + +        Check that the backend is running. Otherwise show an error to the user. +        """ +        if not self.backend.online: +            logger.critical("Backend is not online.") +            QtGui.QMessageBox.critical( +                self, self.tr("Application error"), +                self.tr("There is a problem contacting the backend, please " +                        "restart Bitmask.")) +            self._backend_checker.stop() diff --git a/src/leap/bitmask/gui/flashable.py b/src/leap/bitmask/gui/flashable.py new file mode 100644 index 00000000..a26d1ec6 --- /dev/null +++ b/src/leap/bitmask/gui/flashable.py @@ -0,0 +1,76 @@ +# -*- coding: utf-8 -*- +# Copyright (C) 2014 LEAP +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program.  If not, see <http://www.gnu.org/licenses/>. + + +class Flashable(object): + +    """ +    An abstract super class to give a QWidget handy methods for diplaying +    alert messages inline. The widget inheriting from this class must have +    label named 'flash_label' available at self.ui.flash_label, or pass +    the QLabel object in the constructor. +    """ + +    def __init__(self, widget=None): +        self._setup(widget) + +    def _setup(self, widget=None): +        if not hasattr(self, 'widget'): +            if widget: +                self.widget = widget +            else: +                self.widget = self.ui.flash_label +            self.widget.setVisible(False) + +    def flash_error(self, message): +        """ +        Sets string for the flash message. + +        :param message: the text to be displayed +        :type message: str +        """ +        self._setup() +        message = "<font color='red'><b>%s</b></font>" % (message,) +        self.widget.setVisible(True) +        self.widget.setText(message) + +    def flash_success(self, message): +        """ +        Sets string for the flash message. + +        :param message: the text to be displayed +        :type message: str +        """ +        self._setup() +        message = "<font color='green'><b>%s</b></font>" % (message,) +        self.widget.setVisible(True) +        self.widget.setText(message) + +    def flash_message(self, message): +        """ +        Sets string for the flash message. + +        :param message: the text to be displayed +        :type message: str +        """ +        self._setup() +        message = "<b>%s</b>" % (message,) +        self.widget.setVisible(True) +        self.widget.setText(message) + +    def hide_flash(self): +        self._setup() +        self.widget.setVisible(False) diff --git a/src/leap/bitmask/gui/mainwindow.py b/src/leap/bitmask/gui/mainwindow.py index 916e7c1f..cc4ede09 100644 --- a/src/leap/bitmask/gui/mainwindow.py +++ b/src/leap/bitmask/gui/mainwindow.py @@ -37,7 +37,6 @@ from leap.bitmask.config import flags  from leap.bitmask.config.leapsettings import LeapSettings  from leap.bitmask.gui.advanced_key_management import AdvancedKeyManagement -from leap.bitmask.gui.eip_preferenceswindow import EIPPreferencesWindow  from leap.bitmask.gui.eip_status import EIPStatusWidget  from leap.bitmask.gui.loggerwindow import LoggerWindow  from leap.bitmask.gui.login import LoginWidget @@ -46,6 +45,8 @@ from leap.bitmask.gui.preferenceswindow import PreferencesWindow  from leap.bitmask.gui.systray import SysTray  from leap.bitmask.gui.wizard import Wizard  from leap.bitmask.gui.providers import Providers +from leap.bitmask.gui.account import Account +from leap.bitmask.gui.app import App  from leap.bitmask.platform_init import IS_WIN, IS_MAC, IS_LINUX  from leap.bitmask.platform_init import locks @@ -124,19 +125,10 @@ class MainWindow(QtGui.QMainWindow):          self.ui.setupUi(self)          self.menuBar().setNativeMenuBar(not IS_LINUX) -        self._backend = BackendProxy() - -        # periodically check if the backend is alive -        self._backend_checker = QtCore.QTimer(self) -        self._backend_checker.timeout.connect(self._check_backend_status) -        self._backend_checker.start(2000) - -        self._leap_signaler = LeapSignaler() -        self._leap_signaler.start() - -        self._settings = LeapSettings() -        # gateway = self._settings.get_selected_gateway(provider) -        # self._backend.settings_set_selected_gateway(provider, gateway) +        self.app = App() +        self._backend = self.app.backend +        self._leap_signaler = self.app.signaler +        self._settings = self.app.settings          # Login Widget          self._login_widget = LoginWidget(self._settings, self) @@ -152,6 +144,7 @@ class MainWindow(QtGui.QMainWindow):          # Qt Signal Connections #####################################          # TODO separate logic from ui signals. +        self.app.service_selection_changed.connect(self._update_eip_enabled_status)          self._login_widget.login.connect(self._login)          self._login_widget.cancel_login.connect(self._cancel_login)          self._login_widget.logout.connect(self._logout) @@ -213,8 +206,6 @@ class MainWindow(QtGui.QMainWindow):          self._backend_connect()          self.ui.action_preferences.triggered.connect(self._show_preferences) -        self.ui.action_eip_preferences.triggered.connect( -            self._show_eip_preferences)          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) @@ -245,10 +236,6 @@ class MainWindow(QtGui.QMainWindow):          self._action_visible = QtGui.QAction(self.tr("Show Main Window"), self)          self._action_visible.triggered.connect(self._ensure_visible) -        # disable buttons for now, may come back later. -        # self.ui.btnPreferences.clicked.connect(self._show_preferences) -        # self.ui.btnEIPPreferences.clicked.connect(self._show_eip_preferences) -          self._enabled_services = []          self._ui_mx_visible = True          self._ui_eip_visible = True @@ -343,23 +330,6 @@ class MainWindow(QtGui.QMainWindow):          logger.error("Bad call to the backend:")          logger.error(data) -    @QtCore.Slot() -    def _check_backend_status(self): -        """ -        TRIGGERS: -            self._backend_checker.timeout - -        Check that the backend is running. Otherwise show an error to the user. -        """ -        online = self._backend.online -        if not online: -            logger.critical("Backend is not online.") -            QtGui.QMessageBox.critical( -                self, self.tr("Application error"), -                self.tr("There is a problem contacting the backend, please " -                        "restart Bitmask.")) -            self._backend_checker.stop() -      def _backend_connect(self, only_tracked=False):          """          Connect to backend signals. @@ -599,24 +569,16 @@ class MainWindow(QtGui.QMainWindow):          Display the preferences window.          """ -        user = self._logged_user -        domain = self._providers.get_selected_provider() -        mx_provided = False -        if self._provider_details is not None: -            mx_provided = MX_SERVICE in self._provider_details['services'] -        preferences = PreferencesWindow(self, user, domain, self._backend, -                                        self._soledad_started, mx_provided, -                                        self._leap_signaler) +        account = Account(self._logged_user, +                          self._providers.get_selected_provider()) +        pref_win = PreferencesWindow(self, account, self.app) +        pref_win.show() -        self.soledad_ready.connect(preferences.set_soledad_ready) -        preferences.show() -        preferences.preferences_saved.connect(self._update_eip_enabled_status) - -    @QtCore.Slot() -    def _update_eip_enabled_status(self): +    @QtCore.Slot(object, list) +    def _update_eip_enabled_status(self, account=None, services=None):          """          TRIGGER: -            PreferencesWindow.preferences_saved +            App.service_selection_changed          Enable or disable the EIP start/stop actions and stop EIP if the user          disabled that service. @@ -624,24 +586,35 @@ class MainWindow(QtGui.QMainWindow):          :returns: if the eip actions were enabled or disabled          :rtype: bool          """ -        settings = self._settings -        default_provider = settings.get_defaultprovider() +        if account is not None: +            domain = account.domain +        else: +            # I am not sure why, but asking for the currently selected +            # provider here give you the WRONG provider +            domain = self.app.settings.get_defaultprovider() -        if default_provider is None: +        if domain is None:              logger.warning("Trying to update eip enabled status but there's no"                             " default provider. Disabling EIP for the time"                             " being...")              self._backend_cannot_start_eip()              return -        self._trying_to_start_eip = settings.get_autostart_eip() -        self._backend.eip_can_start(domain=default_provider) +        if not EIP_SERVICE in self.app.settings.get_enabled_services(domain): +            self._eip_conductor.terminate() +            def hide(): +              self.app.backend.eip_can_start(domain=domain) +            QtDelayedCall(100, hide) +            # ^^ VERY VERY Hacky, but with the simple state machine, +            # there is no way to signal 'disconnect and then disable' + +        else: +            self._trying_to_start_eip = self.app.settings.get_autostart_eip() +            if not self._trying_to_start_eip: +                self._backend.eip_setup(provider=domain, skip_network=True) +            # check if EIP can start (will trigger widget update) +            self.app.backend.eip_can_start(domain=domain) -        # If we don't want to start eip, we leave everything -        # initialized to quickly start it -        if not self._trying_to_start_eip: -            self._backend.eip_setup(provider=default_provider, -                                    skip_network=True)      def _backend_can_start_eip(self):          """ @@ -661,7 +634,6 @@ class MainWindow(QtGui.QMainWindow):          enabled_services = []          if default_provider is not None:              enabled_services = settings.get_enabled_services(default_provider) -          eip_enabled = False          if EIP_SERVICE in enabled_services:              eip_enabled = True @@ -718,20 +690,6 @@ class MainWindow(QtGui.QMainWindow):          """          self._eip_status.missing_helpers = True -    @QtCore.Slot() -    def _show_eip_preferences(self): -        """ -        TRIGGERS: -            self.ui.btnEIPPreferences.clicked -            self.ui.action_eip_preferences (disabled for now) - -        Display the EIP preferences window. -        """ -        domain = self._providers.get_selected_provider() -        pref = EIPPreferencesWindow(self, domain, -                                    self._backend, self._leap_signaler) -        pref.show() -      #      # updates      # diff --git a/src/leap/bitmask/gui/passwordwindow.py b/src/leap/bitmask/gui/passwordwindow.py new file mode 100644 index 00000000..f7ef079e --- /dev/null +++ b/src/leap/bitmask/gui/passwordwindow.py @@ -0,0 +1,273 @@ +# -*- coding: utf-8 -*- +# passwordwindow.py +# Copyright (C) 2014 LEAP +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program.  If not, see <http://www.gnu.org/licenses/>. + +""" +Change password dialog window +""" + +from PySide import QtCore, QtGui +from leap.bitmask.util.credentials import password_checks + +from leap.bitmask.gui.ui_password_change import Ui_PasswordChange +from leap.bitmask.gui.flashable import Flashable + +import logging +logger = logging.getLogger(__name__) + + +class PasswordWindow(QtGui.QDialog, Flashable): + +    _current_window = None  # currently visible password window + +    def __init__(self, parent, account, app): +        """ +        :param parent: parent object of the PreferencesWindow. +        :parent type: QWidget + +        :param account: the user set in the login widget +        :type account: Account + +        :param app: App instance +        :type app: App +        """ +        QtGui.QDialog.__init__(self, parent) + +        self.account = account +        self.app = app +        self._backend_connect() + +        self.ui = Ui_PasswordChange() +        self.ui.setupUi(self) + +        self.hide_flash() +        self.ui.ok_button.clicked.connect(self._change_password) +        self.ui.cancel_button.clicked.connect(self.close) +        self.ui.username_lineedit.setText(account.address) + +        if PasswordWindow._current_window is not None: +            PasswordWindow._current_window.close() +        PasswordWindow._current_window = self + +        self._disabled = False  # if set to True, never again enable widgets. + +        if account.username is None: +            # should not ever happen, but just in case +            self._disabled = True +            self._enable_password_widgets(False) +            self.ui.cancel_button.setEnabled(True) +            self.flash_error(self.tr("Please log in to change your password.")) + +        if self.is_soledad_needed() and not self._soledad_ready: +            self._enable_password_widgets(False) +            self.ui.cancel_button.setEnabled(True) +            self.flash_message( +                self.tr("Please wait for data storage to be ready.")) + +    def is_soledad_needed(self): +        """ +        Returns true if the current account needs to change the soledad +        password as well as the SRP password. +        """ +        return self.account.is_email_enabled() + +    # +    # MANAGE WIDGETS +    # + +    def _enable_password_widgets(self, enabled): +        """ +        Enables or disables the widgets in the password change group box. + +        :param enabled: True if the widgets should be enabled. +                        False if widgets should be disabled and +                        display the status label that shows that is +                        changing the password. +        :type enabled: bool +        """ +        if self._disabled: +            return + +        if enabled: +            self.hide_flash() +        else: +            self.flash_message(self.tr("Changing password...")) + +        self.ui.current_password_lineedit.setEnabled(enabled) +        self.ui.new_password_lineedit.setEnabled(enabled) +        self.ui.new_password_confirmation_lineedit.setEnabled(enabled) +        self.ui.ok_button.setEnabled(enabled) +        self.ui.cancel_button.setEnabled(enabled) + +    def _change_password_success(self): +        """ +        Callback used to display a successfully changed password. +        """ +        logger.debug("Password changed successfully.") +        self._clear_password_inputs() +        self._enable_password_widgets(True) +        self.flash_success(self.tr("Password changed successfully.")) + +    def _clear_password_inputs(self): +        """ +        Clear the contents of the inputs. +        """ +        self.ui.current_password_lineedit.setText("") +        self.ui.new_password_lineedit.setText("") +        self.ui.new_password_confirmation_lineedit.setText("") + +    # +    # SLOTS +    # + +    def _backend_connect(self): +        """ +        Helper to connect to backend signals +        """ +        sig = self.app.signaler +        sig.srp_password_change_ok.connect(self._srp_change_password_ok) +        sig.srp_password_change_error.connect(self._srp_password_change_error) +        sig.srp_password_change_badpw.connect(self._srp_password_change_badpw) +        sig.soledad_password_change_ok.connect( +            self._soledad_change_password_ok) +        sig.soledad_password_change_error.connect( +            self._soledad_change_password_problem) + +        self._soledad_ready = False +        sig.soledad_bootstrap_finished.connect(self._on_soledad_ready) + +    @QtCore.Slot() +    def _change_password(self): +        """ +        TRIGGERS: +            self.ui.buttonBox.accepted + +        Changes the user's password if the inputboxes are correctly filled. +        """ +        current_password = self.ui.current_password_lineedit.text() +        new_password = self.ui.new_password_lineedit.text() +        new_password2 = self.ui.new_password_confirmation_lineedit.text() + +        self._enable_password_widgets(True) + +        if len(current_password) == 0: +            self.flash_error(self.tr("Password is empty.")) +            self.ui.current_password_lineedit.setFocus() +            return + +        ok, msg, field = password_checks(self.account.username, new_password, +                                         new_password2) +        if not ok: +            self.flash_error(msg) +            if field == 'new_password': +                self.ui.new_password_lineedit.setFocus() +            elif field == 'new_password_confirmation': +                self.ui.new_password_confirmation_lineedit.setFocus() +            return + +        self._enable_password_widgets(False) +        self.app.backend.user_change_password( +            current_password=current_password, +            new_password=new_password) + +    def closeEvent(self, event=None): +        """ +        TRIGGERS: +            cancel_button (indirectly via self.close()) +            or when window is closed + +        Close this dialog & delete ourselves to clean up signals. +        """ +        PasswordWindow._current_window = None +        self.deleteLater() + +    @QtCore.Slot() +    def _srp_change_password_ok(self): +        """ +        TRIGGERS: +            self._backend.signaler.srp_password_change_ok + +        Callback used to display a successfully changed password. +        """ +        new_password = self.ui.new_password_lineedit.text() +        logger.debug("SRP password changed successfully.") + +        if self.is_soledad_needed(): +            self._backend.soledad_change_password(new_password=new_password) +        else: +            self._change_password_success() + +    @QtCore.Slot() +    def _srp_password_change_error(self): +        """ +        TRIGGERS: +            self._backend.signaler.srp_password_change_error + +        Unknown problem changing password +        """ +        msg = self.tr("There was a problem changing the password.") +        logger.error(msg) +        self._enable_password_widgets(True) +        self.flash_error(msg) + +    @QtCore.Slot() +    def _srp_password_change_badpw(self): +        """ +        TRIGGERS: +            self._backend.signaler.srp_password_change_badpw + +        The password the user entered was wrong. +        """ +        msg = self.tr("You did not enter a correct current password.") +        logger.error(msg) +        self._enable_password_widgets(True) +        self.flash_error(msg) +        self.ui.current_password_lineedit.setFocus() + +    @QtCore.Slot() +    def _soledad_change_password_ok(self): +        """ +        TRIGGERS: +            Signaler.soledad_password_change_ok + +        Soledad password change went OK. +        """ +        logger.debug("Soledad password changed successfully.") +        self._change_password_success() + +    @QtCore.Slot(unicode) +    def _soledad_change_password_problem(self, msg): +        """ +        TRIGGERS: +            Signaler.soledad_password_change_error + +        Callback used to display an error on changing password. + +        :param msg: the message to show to the user. +        :type msg: unicode +        """ +        logger.error("Error changing soledad password: %s" % (msg,)) +        self._enable_password_widgets(True) +        self.flash_error(msg) + +    @QtCore.Slot() +    def _on_soledad_ready(self): +        """ +        TRIGGERS: +            Signaler.soledad_bootstrap_finished +        """ +        self._enable_password_widgets(True) +        self._soledad_ready = True diff --git a/src/leap/bitmask/gui/preferences_account_page.py b/src/leap/bitmask/gui/preferences_account_page.py new file mode 100644 index 00000000..ec6a7716 --- /dev/null +++ b/src/leap/bitmask/gui/preferences_account_page.py @@ -0,0 +1,139 @@ +# -*- coding: utf-8 -*- +# Copyright (C) 2014 LEAP +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program.  If not, see <http://www.gnu.org/licenses/>. +""" +Widget for "account" preferences +""" +import logging + +from functools import partial + +from PySide import QtCore, QtGui +from leap.bitmask.gui.ui_preferences_account_page import Ui_PreferencesAccountPage +from leap.bitmask.gui.passwordwindow import PasswordWindow +from leap.bitmask.services import get_service_display_name + +logger = logging.getLogger(__name__) + + +class PreferencesAccountPage(QtGui.QWidget): + +    def __init__(self, parent, account, app): +        """ +        :param parent: parent object of the PreferencesWindow. +        :parent type: QWidget + +        :param account: user account (user + provider or just provider) +        :type account: Account + +        :param app: the current App object +        :type app: App +        """ +        QtGui.QWidget.__init__(self, parent) +        self.ui = Ui_PreferencesAccountPage() +        self.ui.setupUi(self) + +        self.account = account +        self.app = app + +        self._selected_services = set() +        self.ui.change_password_label.setVisible(False) +        self.ui.provider_services_label.setVisible(False) + +        self.ui.change_password_button.clicked.connect( +            self._show_change_password) +        app.signaler.prov_get_supported_services.connect(self._load_services) +        app.backend.provider_get_supported_services(domain=account.domain) + +        if account.username is None: +            self.ui.change_password_label.setText( +                self.tr('You must be logged in to change your password.')) +            self.ui.change_password_label.setVisible(True) +            self.ui.change_password_button.setEnabled(False) + +    @QtCore.Slot(str, int) +    def _service_selection_changed(self, service, state): +        """ +        TRIGGERS: +            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])) +        services = list(self._selected_services) + +        # We hide the maybe-visible status label after a change +        self.ui.provider_services_label.setVisible(False) + +        # write to config +        self.app.settings.set_enabled_services(self.account.domain, services) + +        # emit signal alerting change +        self.app.service_selection_changed.emit(self.account, services) + +    @QtCore.Slot(str) +    def _load_services(self, services): +        """ +        TRIGGERS: +            prov_get_supported_services + +        Loads the services that the provider provides into the UI for +        the user to enable or disable. + +        :param services: list of supported service names +        :type services: list of str +        """ +        services_conf = self.account.services() + +        self._selected_services = set() + +        # Remove existing checkboxes +        # (the new widget is deleted when its parent is deleted. +        #  We need to loop backwards because removing things from the +        #  beginning shifts items and changes the order of items in the layout. +        #  Using `QObject.deleteLater` doesn't seem to work.) +        layout = self.ui.provider_services_layout +        for i in reversed(range(layout.count())): +            layout.itemAt(i).widget().setParent(None) + +        # add one checkbox per service and set the current value +        # from what is saved in settings. +        for service in services: +            try: +                checkbox = QtGui.QCheckBox( +                    get_service_display_name(service), self) +                self.ui.provider_services_layout.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,)) + +    @QtCore.Slot() +    def _show_change_password(self): +        change_password_window = PasswordWindow(self, self.account, self.app) +        change_password_window.show() diff --git a/src/leap/bitmask/gui/preferences_email_page.py b/src/leap/bitmask/gui/preferences_email_page.py new file mode 100644 index 00000000..80e8d93e --- /dev/null +++ b/src/leap/bitmask/gui/preferences_email_page.py @@ -0,0 +1,35 @@ +# -*- coding: utf-8 -*- +# Copyright (C) 2014 LEAP +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program.  If not, see <http://www.gnu.org/licenses/>. +""" +Widget for "email" preferences +""" +import logging + +from PySide import QtCore, QtGui +from leap.bitmask.gui.ui_preferences_email_page import Ui_PreferencesEmailPage + +logger = logging.getLogger(__name__) + + +class PreferencesEmailPage(QtGui.QWidget): + +    def __init__(self, parent, account, app): +        QtGui.QWidget.__init__(self, parent) +        self.ui = Ui_PreferencesEmailPage() +        self.ui.setupUi(self) + +        self.account = account +        self.app = app diff --git a/src/leap/bitmask/gui/preferences_vpn_page.py b/src/leap/bitmask/gui/preferences_vpn_page.py new file mode 100644 index 00000000..901116b4 --- /dev/null +++ b/src/leap/bitmask/gui/preferences_vpn_page.py @@ -0,0 +1,143 @@ +# -*- coding: utf-8 -*- +# Copyright (C) 2014 LEAP +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program.  If not, see <http://www.gnu.org/licenses/>. +""" +Widget for "vpn" preferences +""" + +from PySide import QtCore, QtGui +from leap.bitmask.gui.ui_preferences_vpn_page import Ui_PreferencesVpnPage + +from leap.bitmask.config.leapsettings import LeapSettings +from leap.bitmask.gui.flashable import Flashable + + +class PreferencesVpnPage(QtGui.QWidget, Flashable): + +    """ +    Page in the preferences window that shows VPN settings +    """ + +    def __init__(self, parent, account, app): +        """ +        :param parent: parent object of the EIPPreferencesWindow. +        :type parent: QWidget + +        :param account: the currently active account +        :type account: Account + +        :param app: shared App instance +        :type app: App +        """ +        QtGui.QWidget.__init__(self, parent) +        self.AUTOMATIC_GATEWAY_LABEL = self.tr("Automatic") + +        self.account = account +        self.app = app + +        # Load UI +        self.ui = Ui_PreferencesVpnPage() +        self.ui.setupUi(self) +        self.ui.flash_label.setVisible(False) +        self.hide_flash() + +        # Connections +        self.ui.gateways_list.clicked.connect(self._save_selected_gateway) +        sig = self.app.signaler +        sig.eip_get_gateways_list.connect(self._update_gateways_list) +        sig.eip_get_gateways_list_error.connect(self._gateways_list_error) +        sig.eip_uninitialized_provider.connect( +            self._gateways_list_uninitialized) + +        # Trigger update +        self.app.backend.eip_get_gateways_list(domain=self.account.domain) + +    @QtCore.Slot(str) +    def _save_selected_gateway(self, index): +        """ +        TRIGGERS: +            self.ui.gateways_list.clicked + +        Saves the new gateway setting to the configuration file. + +        :param index: the current index of the selection. +        :type current_item: QModelIndex +        """ +        item = self.ui.gateways_list.currentItem() + +        if item.text() == self.AUTOMATIC_GATEWAY_LABEL: +            gateway = self.app.settings.GATEWAY_AUTOMATIC +        else: +            gateway = item.data(QtCore.Qt.UserRole) +        self.app.settings.set_selected_gateway(self.account.domain, gateway) +        self.app.backend.settings_set_selected_gateway( +            provider=self.account.domain, +            gateway=gateway) + +    @QtCore.Slot(list) +    def _update_gateways_list(self, gateways): +        """ +        TRIGGERS: +            Signaler.eip_get_gateways_list + +        :param gateways: a list of gateways +        :type gateways: list of unicode + +        Add the available gateways and select the one stored in +        configuration file. +        """ +        self.ui.gateways_list.clear() +        self.ui.gateways_list.addItem(self.AUTOMATIC_GATEWAY_LABEL) + +        selected_gateway = self.app.settings.get_selected_gateway( +            self.account.domain) + +        index = 0 +        for idx, (gw_name, gw_ip, gw_country) in enumerate(gateways): +            gateway_text = "{0} ({1})".format(gw_name, gw_ip) +            item = QtGui.QListWidgetItem(self.ui.gateways_list) +            item.setText(gateway_text) +            item.setIcon(QtGui.QIcon( +                ":/images/countries/%s.png" % (gw_country.lower(),))) +            item.setData(QtCore.Qt.UserRole, gw_ip) +            if gw_ip == selected_gateway: +                index = idx + 1 +        self.ui.gateways_list.setCurrentRow(index) + +    @QtCore.Slot() +    def _gateways_list_error(self): +        """ +        TRIGGERS: +            Signaler.eip_get_gateways_list_error + +        An error has occurred retrieving the gateway list +        so we inform the user. +        """ +        self.flash_error( +            self.tr("Error loading configuration file.")) +        self.ui.gateways_list.setEnabled(False) + +    @QtCore.Slot() +    def _gateways_list_uninitialized(self): +        """ +        TRIGGERS: +            Signaler.eip_uninitialized_provider + +        The requested provider in not initialized yet, so we give the user an +        error msg. +        """ +        self.flash_error( +            self.tr("This is an uninitialized provider, please log in first.")) +        self.ui.gateways_list.setEnabled(False) diff --git a/src/leap/bitmask/gui/preferenceswindow.py b/src/leap/bitmask/gui/preferenceswindow.py index 3c9cd5d0..f1252301 100644 --- a/src/leap/bitmask/gui/preferenceswindow.py +++ b/src/leap/bitmask/gui/preferenceswindow.py @@ -20,428 +20,167 @@ Preferences window  """  import logging -from functools import partial -  from PySide import QtCore, QtGui -from leap.bitmask.config.leapsettings import LeapSettings +from leap.bitmask.services import EIP_SERVICE, MX_SERVICE +  from leap.bitmask.gui.ui_preferences import Ui_Preferences -from leap.bitmask.util.credentials import password_checks -from leap.bitmask.services import get_service_display_name, MX_SERVICE +from leap.bitmask.gui.preferences_account_page import PreferencesAccountPage +from leap.bitmask.gui.preferences_vpn_page import PreferencesVpnPage +from leap.bitmask.gui.preferences_email_page import PreferencesEmailPage  logger = logging.getLogger(__name__)  class PreferencesWindow(QtGui.QDialog): +      """      Window that displays the preferences.      """ -    preferences_saved = QtCore.Signal() -    def __init__(self, parent, username, domain, backend, soledad_started, mx, -                 leap_signaler): +    _current_window = None  # currently visible preferences window + +    def __init__(self, parent, account, app):          """          :param parent: parent object of the PreferencesWindow.          :parent type: QWidget -        :param username: the user set in the login widget -        :type username: unicode -        :param domain: the selected domain in the login widget -        :type domain: unicode -        :param backend: Backend being used -        :type backend: Backend -        :param soledad_started: whether soledad has started or not -        :type soledad_started: bool -        :param mx: whether the current provider provides mx or not. -        :type mx: bool + +        :param account: the user or provider +        :type account: Account + +        :param app: the current App object +        :type app: App          """          QtGui.QDialog.__init__(self, parent) -        self.AUTOMATIC_GATEWAY_LABEL = self.tr("Automatic") - -        self._username = username -        self._domain = domain -        self._leap_signaler = leap_signaler -        self._backend = backend -        self._soledad_started = soledad_started -        self._mx_provided = mx -        self._settings = LeapSettings() -        self._backend_connect() +        self.account = account +        self.app = app -        # Load UI          self.ui = Ui_Preferences()          self.ui.setupUi(self) -        self.ui.lblPasswordChangeStatus.setVisible(False) -        self.ui.lblProvidersServicesStatus.setVisible(False) -        self._selected_services = set() - -        # Connections -        self.ui.pbChangePassword.clicked.connect(self._change_password) -        self.ui.cbProvidersServices.currentIndexChanged[unicode].connect( -            self._populate_services) - -        if not self._settings.get_configured_providers(): -            self.ui.gbEnabledServices.setEnabled(False) -        else: -            self._add_configured_providers() - -        if self._username is None: -            self._not_logged_in() -        else: -            self.ui.gbPasswordChange.setEnabled(True) -            if self._mx_provided: -                self._provides_mx() - -        self._select_provider_by_name(domain) - -    def _not_logged_in(self): -        """ -        Actions to perform if the user is not logged in. -        """ -        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(False) - -    def _provides_mx(self): -        """ -        Actions to perform if the provider provides MX. -        """ -        pw_enabled = True -        enabled_services = self._settings.get_enabled_services(self._domain) -        mx_name = get_service_display_name(MX_SERVICE) - -        if MX_SERVICE 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) -            pw_enabled = False -        else: -            # check if Soledad is bootstrapped -            if not self._soledad_started: -                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) -                pw_enabled = False - -        self.ui.gbPasswordChange.setEnabled(pw_enabled) - -    @QtCore.Slot() -    def set_soledad_ready(self): +        self.ui.close_button.clicked.connect(self.close) +        self.ui.account_label.setText(account.address) + +        self.app.service_selection_changed.connect(self._update_icons) + +        self._add_icons() +        self._add_pages() +        self._update_icons(self.account, self.account.services()) + +        # only allow a single preferences window at a time. +        if PreferencesWindow._current_window is not None: +            PreferencesWindow._current_window.close_window() +        PreferencesWindow._current_window = self + +    def _add_icons(self): +        """ +        Adds all the icons for the different configuration categories. +        Icons are QListWidgetItems added to the nav_widget on the side +        of the preferences window. + +        A note on sizing of QListWidgetItems +          icon_width = list_widget.width - (2 x nav_widget.spacing) - 2 +          icon_height = 56 seems to look ok +        """ +        account_item = QtGui.QListWidgetItem(self.ui.nav_widget) +        account_item.setIcon(QtGui.QIcon(":/images/black/32/user.png")) +        account_item.setText(self.tr("Account")) +        account_item.setTextAlignment( +            QtCore.Qt.AlignHCenter | QtCore.Qt.AlignVCenter) +        account_item.setFlags( +            QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) +        account_item.setSizeHint(QtCore.QSize(98, 56)) +        self._account_item = account_item + +        vpn_item = QtGui.QListWidgetItem(self.ui.nav_widget) +        vpn_item.setHidden(True) +        vpn_item.setIcon(QtGui.QIcon(":/images/black/32/earth.png")) +        vpn_item.setText(self.tr("VPN")) +        vpn_item.setTextAlignment( +            QtCore.Qt.AlignHCenter | QtCore.Qt.AlignVCenter) +        vpn_item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) +        vpn_item.setSizeHint(QtCore.QSize(98, 56)) +        self._vpn_item = vpn_item + +        email_item = QtGui.QListWidgetItem(self.ui.nav_widget) +        email_item.setHidden(True) +        email_item.setIcon(QtGui.QIcon(":/images/black/32/email.png")) +        email_item.setText(self.tr("Email")) +        email_item.setTextAlignment( +            QtCore.Qt.AlignHCenter | QtCore.Qt.AlignVCenter) +        email_item.setFlags( +            QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) +        email_item.setSizeHint(QtCore.QSize(98, 56)) +        self._email_item = email_item + +        self.ui.nav_widget.currentItemChanged.connect(self._change_page) +        self.ui.nav_widget.setCurrentRow(0) + +    def _add_pages(self): +        """ +        Adds the pages for the different configuration categories. +        """ +        self._account_page = PreferencesAccountPage(self, self.account, self.app) +        self._vpn_page = PreferencesVpnPage(self, self.account, self.app) +        self._email_page = PreferencesEmailPage(self, self.account, self.app) + +        self.ui.pages_widget.addWidget(self._account_page) +        self.ui.pages_widget.addWidget(self._vpn_page) +        self.ui.pages_widget.addWidget(self._email_page) + +    # +    # Slots +    # + +    def closeEvent(self, e):          """          TRIGGERS: -            parent.soledad_ready +            self.ui.close_button.clicked +              (since self.close() will trigger closeEvent) +            whenever the window is closed -        It notifies when the soledad object as ready to use. +        Close this dialog and destroy it.          """ -        self.ui.lblPasswordChangeStatus.setVisible(False) -        self.ui.gbPasswordChange.setEnabled(True) +        PreferencesWindow._current_window = None -    def _set_password_change_status(self, status, error=False, success=False): -        """ -        Sets the status label for the password change. - -        :param status: status message to display, can be HTML -        :type status: str -        """ -        if error: -            status = "<font color='red'><b>%s</b></font>" % (status,) -        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) - -    def _set_changing_password(self, disable): -        """ -        Enables or disables the widgets in the password change group box. - -        :param disable: True if the widgets should be disabled and -                        it displays the status label that shows that is -                        changing the password. -                        False if they should be enabled. -        :type disable: bool -        """ -        if disable: -            self._set_password_change_status(self.tr("Changing password...")) - -        self.ui.leCurrentPassword.setEnabled(not disable) -        self.ui.leNewPassword.setEnabled(not disable) -        self.ui.leNewPassword2.setEnabled(not disable) -        self.ui.pbChangePassword.setEnabled(not disable) +        # deleteLater does not seem to cascade to items in stackLayout +        # (even with QtCore.Qt.WA_DeleteOnClose attribute). +        # so, here we call deleteLater() explicitly: +        self._account_page.deleteLater() +        self._vpn_page.deleteLater() +        self._email_page.deleteLater() +        self.deleteLater()      @QtCore.Slot() -    def _change_password(self): +    def _change_page(self, current, previous):          """          TRIGGERS: -            self.ui.pbChangePassword.clicked - -        Changes the user's password if the inputboxes are correctly filled. -        """ -        username = self._username -        current_password = self.ui.leCurrentPassword.text() -        new_password = self.ui.leNewPassword.text() -        new_password2 = self.ui.leNewPassword2.text() - -        ok, msg = password_checks(username, new_password, new_password2) - -        if not ok: -            self._set_changing_password(False) -            self._set_password_change_status(msg, error=True) -            self.ui.leNewPassword.setFocus() -            return - -        self._set_changing_password(True) -        self._backend.user_change_password(current_password=current_password, -                                           new_password=new_password) - -    @QtCore.Slot() -    def _srp_change_password_ok(self): -        """ -        TRIGGERS: -            self._backend.signaler.srp_password_change_ok - -        Callback used to display a successfully changed password. -        """ -        new_password = self.ui.leNewPassword.text() -        logger.debug("SRP password changed successfully.") - -        if self._mx_provided: -            self._backend.soledad_change_password(new_password=new_password) -        else: -            self._change_password_success() +            self.ui.nav_widget.currentItemChanged -    @QtCore.Slot(unicode) -    def _srp_change_password_problem(self, msg): -        """ -        TRIGGERS: -            self._backend.signaler.srp_password_change_error -            self._backend.signaler.srp_password_change_badpw +        Changes what page is displayed. -        Callback used to display an error on changing password. +        :param current: the currently selected item (might be None?) +        :type current: PySide.QtGui.QListWidgetItem -        :param msg: the message to show to the user. -        :type msg: unicode +        :param previous: the previously selected item (might be None) +        :type previous: PySide.QtGui.QListWidgetItem          """ -        logger.error("Error changing password") -        self._set_password_change_status(msg, error=True) -        self._set_changing_password(False) +        if not current: +            current = previous +        self.ui.pages_widget.setCurrentIndex(self.ui.nav_widget.row(current)) -    @QtCore.Slot() -    def _soledad_change_password_ok(self): +    @QtCore.Slot(object, list) +    def _update_icons(self, account, services):          """          TRIGGERS: -            Signaler.soledad_password_change_ok - -        Soledad password change went OK. -        """ -        logger.debug("Soledad password changed successfully.") -        self._change_password_success() +            self.app.service_selection_changed -    def _change_password_success(self): +        Change which icons are visible.          """ -        Callback used to display a successfully changed password. -        """ -        logger.debug("Soledad password changed successfully.") - -        self._set_password_change_status( -            self.tr("Password changed successfully."), success=True) -        self._clear_password_inputs() -        self._set_changing_password(False) - -    @QtCore.Slot(unicode) -    def _soledad_change_password_problem(self, msg): -        """ -        TRIGGERS: -            Signaler.soledad_password_change_error - -        Callback used to display an error on changing password. - -        :param msg: the message to show to the user. -        :type msg: unicode -        """ -        logger.error("Error changing soledad password") -        self._set_password_change_status(msg, error=True) -        self._set_changing_password(False) - -    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 _add_configured_providers(self): -        """ -        Add the client's configured providers to the providers combo boxes. -        """ -        self.ui.cbProvidersServices.clear() -        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) - -    @QtCore.Slot(str, int) -    def _service_selection_changed(self, service, state): -        """ -        TRIGGERS: -            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) - -    @QtCore.Slot(str) -    def _populate_services(self, domain): -        """ -        TRIGGERS: -            self.ui.cbProvidersServices.currentIndexChanged[unicode] - -        Fill the services list with the selected provider's services. - -        :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: +        if account != self.account:              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) - -        self._backend.provider_get_supported_services(domain=domain) - -    @QtCore.Slot(str) -    def _load_services(self, services): -        """ -        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 -        """ -        domain = self.ui.cbProvidersServices.currentText() -        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) -                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,)) - -    @QtCore.Slot(str) -    def _save_enabled_services(self, provider): -        """ -        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) -        self.preferences_saved.emit() - -    def _backend_connect(self): -        """ -        Helper to connect to backend signals -        """ -        sig = self._leap_signaler - -        sig.prov_get_supported_services.connect(self._load_services) - -        sig.srp_password_change_ok.connect(self._srp_change_password_ok) - -        pwd_change_error = lambda: self._srp_change_password_problem( -            self.tr("There was a problem changing the password.")) -        sig.srp_password_change_error.connect(pwd_change_error) - -        pwd_change_badpw = lambda: self._srp_change_password_problem( -            self.tr("You did not enter a correct current password.")) -        sig.srp_password_change_badpw.connect(pwd_change_badpw) - -        sig.soledad_password_change_ok.connect( -            self._soledad_change_password_ok) - -        sig.soledad_password_change_error.connect( -            self._soledad_change_password_problem) +        self._vpn_item.setHidden(not EIP_SERVICE in services) +        # self._email_item.setHidden(not MX_SERVICE in services) +        # ^^ disable email for now, there is nothing there yet. diff --git a/src/leap/bitmask/gui/ui/eippreferences.ui b/src/leap/bitmask/gui/ui/eippreferences.ui deleted file mode 100644 index 1a5fcd24..00000000 --- a/src/leap/bitmask/gui/ui/eippreferences.ui +++ /dev/null @@ -1,102 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<ui version="4.0"> - <class>EIPPreferences</class> - <widget class="QDialog" name="EIPPreferences"> -  <property name="geometry"> -   <rect> -    <x>0</x> -    <y>0</y> -    <width>435</width> -    <height>144</height> -   </rect> -  </property> -  <property name="windowTitle"> -   <string>Encrypted Internet Preferences</string> -  </property> -  <property name="windowIcon"> -   <iconset resource="../../../../../data/resources/icons.qrc"> -    <normaloff>:/images/mask-icon.png</normaloff>:/images/mask-icon.png</iconset> -  </property> -  <layout class="QGridLayout" name="gridLayout_2"> -   <item row="0" 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="0"> -       <widget class="QLabel" name="lblSelectProvider"> -        <property name="text"> -         <string>Select &provider:</string> -        </property> -        <property name="buddy"> -         <cstring>cbProvidersGateway</cstring> -        </property> -       </widget> -      </item> -      <item row="0" column="1" colspan="2"> -       <widget class="QComboBox" name="cbProvidersGateway"> -        <item> -         <property name="text"> -          <string><Select provider></string> -         </property> -        </item> -       </widget> -      </item> -      <item row="7" column="2"> -       <widget class="QPushButton" name="pbSaveGateway"> -        <property name="text"> -         <string>&Save this provider settings</string> -        </property> -       </widget> -      </item> -      <item row="4" column="0" colspan="3"> -       <widget class="QLabel" name="lblProvidersGatewayStatus"> -        <property name="text"> -         <string>< Providers Gateway Status ></string> -        </property> -        <property name="alignment"> -         <set>Qt::AlignCenter</set> -        </property> -       </widget> -      </item> -      <item row="1" column="0"> -       <widget class="QLabel" name="label"> -        <property name="text"> -         <string>Select &gateway:</string> -        </property> -        <property name="buddy"> -         <cstring>cbGateways</cstring> -        </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> -     </layout> -    </widget> -   </item> -  </layout> - </widget> - <tabstops> -  <tabstop>cbProvidersGateway</tabstop> -  <tabstop>cbGateways</tabstop> -  <tabstop>pbSaveGateway</tabstop> - </tabstops> - <resources> -  <include location="../../../../../data/resources/icons.qrc"/> - </resources> - <connections/> -</ui> diff --git a/src/leap/bitmask/gui/ui/mainwindow.ui b/src/leap/bitmask/gui/ui/mainwindow.ui index f7570ee6..b1d68c4a 100644 --- a/src/leap/bitmask/gui/ui/mainwindow.ui +++ b/src/leap/bitmask/gui/ui/mainwindow.ui @@ -317,7 +317,6 @@      <addaction name="action_advanced_key_management"/>      <addaction name="separator"/>      <addaction name="action_preferences"/> -    <addaction name="action_eip_preferences"/>      <addaction name="separator"/>      <addaction name="action_quit"/>     </widget> @@ -338,12 +337,7 @@      <bool>true</bool>     </property>     <property name="text"> -    <string>Account Preferences...</string> -   </property> -  </action> -  <action name="action_eip_preferences"> -   <property name="text"> -    <string>Internet Preferences...</string> +    <string>Pr&eferences...</string>     </property>    </action>    <action name="action_quit"> diff --git a/src/leap/bitmask/gui/ui/password_change.ui b/src/leap/bitmask/gui/ui/password_change.ui new file mode 100644 index 00000000..b7ceac38 --- /dev/null +++ b/src/leap/bitmask/gui/ui/password_change.ui @@ -0,0 +1,182 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>PasswordChange</class> + <widget class="QDialog" name="PasswordChange"> +  <property name="geometry"> +   <rect> +    <x>0</x> +    <y>0</y> +    <width>459</width> +    <height>231</height> +   </rect> +  </property> +  <property name="sizePolicy"> +   <sizepolicy hsizetype="Preferred" vsizetype="MinimumExpanding"> +    <horstretch>0</horstretch> +    <verstretch>0</verstretch> +   </sizepolicy> +  </property> +  <property name="windowTitle"> +   <string>Change Password</string> +  </property> +  <layout class="QVBoxLayout" name="verticalLayout"> +   <item> +    <layout class="QGridLayout" name="grid_layout"> +     <item row="0" column="0"> +      <widget class="QLabel" name="username_label"> +       <property name="text"> +        <string>Username:</string> +       </property> +      </widget> +     </item> +     <item row="3" column="0"> +      <widget class="QLabel" name="new_password_label"> +       <property name="text"> +        <string>New password:</string> +       </property> +       <property name="buddy"> +        <cstring>new_password_lineedit</cstring> +       </property> +      </widget> +     </item> +     <item row="3" column="1"> +      <widget class="QLineEdit" name="new_password_lineedit"> +       <property name="echoMode"> +        <enum>QLineEdit::Password</enum> +       </property> +      </widget> +     </item> +     <item row="4" column="0"> +      <widget class="QLabel" name="new_password_confirmation_label"> +       <property name="text"> +        <string>Re-enter new password:</string> +       </property> +       <property name="buddy"> +        <cstring>new_password_confirmation_lineedit</cstring> +       </property> +      </widget> +     </item> +     <item row="1" column="0"> +      <widget class="QLabel" name="current_password_label"> +       <property name="text"> +        <string>Current password:</string> +       </property> +       <property name="buddy"> +        <cstring>current_password_lineedit</cstring> +       </property> +      </widget> +     </item> +     <item row="4" column="1"> +      <widget class="QLineEdit" name="new_password_confirmation_lineedit"> +       <property name="echoMode"> +        <enum>QLineEdit::Password</enum> +       </property> +      </widget> +     </item> +     <item row="1" column="1"> +      <widget class="QLineEdit" name="current_password_lineedit"> +       <property name="echoMode"> +        <enum>QLineEdit::Password</enum> +       </property> +      </widget> +     </item> +     <item row="2" column="1"> +      <spacer name="spacer"> +       <property name="orientation"> +        <enum>Qt::Vertical</enum> +       </property> +       <property name="sizeHint" stdset="0"> +        <size> +         <width>20</width> +         <height>10</height> +        </size> +       </property> +      </spacer> +     </item> +     <item row="0" column="1"> +      <layout class="QHBoxLayout" name="horizontalLayout"> +       <item> +        <widget class="QLineEdit" name="username_lineedit"> +         <property name="enabled"> +          <bool>false</bool> +         </property> +        </widget> +       </item> +      </layout> +     </item> +    </layout> +   </item> +   <item> +    <widget class="QLabel" name="flash_label"> +     <property name="text"> +      <string><flash_label></string> +     </property> +     <property name="alignment"> +      <set>Qt::AlignCenter</set> +     </property> +    </widget> +   </item> +   <item> +    <spacer name="spacer2"> +     <property name="orientation"> +      <enum>Qt::Vertical</enum> +     </property> +     <property name="sizeHint" stdset="0"> +      <size> +       <width>0</width> +       <height>0</height> +      </size> +     </property> +    </spacer> +   </item> +   <item> +    <layout class="QHBoxLayout" name="button_layout"> +     <item> +      <spacer name="horizontalSpacer"> +       <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> +      <widget class="QPushButton" name="cancel_button"> +       <property name="text"> +        <string>Close</string> +       </property> +       <property name="autoDefault"> +        <bool>false</bool> +       </property> +      </widget> +     </item> +     <item> +      <widget class="QPushButton" name="ok_button"> +       <property name="text"> +        <string>OK</string> +       </property> +       <property name="autoDefault"> +        <bool>false</bool> +       </property> +       <property name="default"> +        <bool>true</bool> +       </property> +      </widget> +     </item> +    </layout> +   </item> +  </layout> + </widget> + <tabstops> +  <tabstop>username_lineedit</tabstop> +  <tabstop>current_password_lineedit</tabstop> +  <tabstop>new_password_lineedit</tabstop> +  <tabstop>new_password_confirmation_lineedit</tabstop> + </tabstops> + <resources/> + <connections/> +</ui> diff --git a/src/leap/bitmask/gui/ui/preferences.ui b/src/leap/bitmask/gui/ui/preferences.ui index cd4d3a77..5e30ea57 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>503</width> -    <height>401</height> +    <width>520</width> +    <height>439</height>     </rect>    </property>    <property name="windowTitle"> @@ -17,159 +17,97 @@     <iconset resource="../../../../../data/resources/icons.qrc">      <normaloff>:/images/mask-icon.png</normaloff>:/images/mask-icon.png</iconset>    </property> -  <layout class="QGridLayout" name="gridLayout_3"> -   <item row="4" column="0"> -    <spacer name="verticalSpacer"> -     <property name="orientation"> -      <enum>Qt::Vertical</enum> +  <layout class="QVBoxLayout" name="verticalLayout" stretch="0,0,1,0"> +   <property name="spacing"> +    <number>6</number> +   </property> +   <item> +    <widget class="QLabel" name="account_label"> +     <property name="font"> +      <font> +       <weight>75</weight> +       <bold>true</bold> +      </font>       </property> -     <property name="sizeHint" stdset="0"> -      <size> -       <width>20</width> -       <height>40</height> -      </size> +     <property name="text"> +      <string>user@example.org</string>       </property> -    </spacer> +    </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> +   <item> +    <widget class="Line" name="line"> +     <property name="orientation"> +      <enum>Qt::Horizontal</enum>       </property> -     <layout class="QFormLayout" name="formLayout"> -      <property name="fieldGrowthPolicy"> -       <enum>QFormLayout::ExpandingFieldsGrow</enum> -      </property> -      <item row="0" column="0"> -       <widget class="QLabel" name="lblCurrentPassword"> -        <property name="text"> -         <string>&Current password:</string> -        </property> -        <property name="buddy"> -         <cstring>leCurrentPassword</cstring> -        </property> -       </widget> -      </item> -      <item row="0" column="1"> -       <widget class="QLineEdit" name="leCurrentPassword"> -        <property name="echoMode"> -         <enum>QLineEdit::Password</enum> -        </property> -       </widget> -      </item> -      <item row="1" column="0"> -       <widget class="QLabel" name="lblNewPassword"> -        <property name="text"> -         <string>&New password:</string> -        </property> -        <property name="buddy"> -         <cstring>leNewPassword</cstring> -        </property> -       </widget> -      </item> -      <item row="1" column="1"> -       <widget class="QLineEdit" name="leNewPassword"> -        <property name="echoMode"> -         <enum>QLineEdit::Password</enum> -        </property> -       </widget> -      </item> -      <item row="2" column="0"> -       <widget class="QLabel" name="lblNewPassword2"> -        <property name="text"> -         <string>&Re-enter new password:</string> -        </property> -        <property name="buddy"> -         <cstring>leNewPassword2</cstring> -        </property> -       </widget> -      </item> -      <item row="2" column="1"> -       <widget class="QLineEdit" name="leNewPassword2"> -        <property name="echoMode"> -         <enum>QLineEdit::Password</enum> -        </property> -       </widget> -      </item> -      <item row="4" column="1"> -       <widget class="QPushButton" name="pbChangePassword"> -        <property name="text"> -         <string>Change</string> -        </property> -       </widget> -      </item> -      <item row="3" column="0" colspan="2"> -       <widget class="QLabel" name="lblPasswordChangeStatus"> -        <property name="text"> -         <string><Password change status></string> -        </property> -        <property name="alignment"> -         <set>Qt::AlignCenter</set> -        </property> -       </widget> -      </item> -     </layout>      </widget>     </item> -   <item row="3" column="0"> -    <widget class="QGroupBox" name="gbEnabledServices"> -     <property name="title"> -      <string>Enabled services</string> +   <item> +    <layout class="QHBoxLayout" name="horizontal_layout" stretch="0,0"> +     <property name="spacing"> +      <number>12</number>       </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><Select provider></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>< Providers Services Status ></string> -        </property> -        <property name="alignment"> -         <set>Qt::AlignCenter</set> -        </property> -       </widget> -      </item> -     </layout> -    </widget> +     <item> +      <widget class="QListWidget" name="nav_widget"> +       <property name="sizePolicy"> +        <sizepolicy hsizetype="Maximum" vsizetype="Expanding"> +         <horstretch>0</horstretch> +         <verstretch>0</verstretch> +        </sizepolicy> +       </property> +       <property name="maximumSize"> +        <size> +         <width>120</width> +         <height>16777215</height> +        </size> +       </property> +       <property name="iconSize"> +        <size> +         <width>32</width> +         <height>32</height> +        </size> +       </property> +       <property name="movement"> +        <enum>QListView::Static</enum> +       </property> +       <property name="spacing"> +        <number>10</number> +       </property> +       <property name="viewMode"> +        <enum>QListView::IconMode</enum> +       </property> +       <property name="uniformItemSizes"> +        <bool>true</bool> +       </property> +      </widget> +     </item> +     <item> +      <widget class="QStackedWidget" name="pages_widget"/> +     </item> +    </layout> +   </item> +   <item> +    <layout class="QHBoxLayout" name="button_layout" stretch="0,0"> +     <item> +      <spacer name="horizontalSpacer"> +       <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> +      <widget class="QPushButton" name="close_button"> +       <property name="text"> +        <string>Close</string> +       </property> +      </widget> +     </item> +    </layout>     </item>    </layout>   </widget> diff --git a/src/leap/bitmask/gui/ui/preferences_account_page.ui b/src/leap/bitmask/gui/ui/preferences_account_page.ui new file mode 100644 index 00000000..9b6d885b --- /dev/null +++ b/src/leap/bitmask/gui/ui/preferences_account_page.ui @@ -0,0 +1,104 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>PreferencesAccountPage</class> + <widget class="QWidget" name="PreferencesAccountPage"> +  <property name="geometry"> +   <rect> +    <x>0</x> +    <y>0</y> +    <width>462</width> +    <height>371</height> +   </rect> +  </property> +  <property name="windowTitle"> +   <string>Form</string> +  </property> +  <layout class="QVBoxLayout" name="verticalLayout"> +   <item> +    <widget class="QGroupBox" name="provider_services_box"> +     <property name="title"> +      <string>Services</string> +     </property> +     <property name="flat"> +      <bool>false</bool> +     </property> +     <layout class="QGridLayout" name="gridLayout_4"> +      <item row="1" column="0"> +       <layout class="QVBoxLayout" name="provider_services_layout"/> +      </item> +      <item row="2" column="0"> +       <widget class="QLabel" name="provider_services_label"> +        <property name="text"> +         <string><provider_services_label></string> +        </property> +        <property name="alignment"> +         <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set> +        </property> +       </widget> +      </item> +     </layout> +    </widget> +   </item> +   <item> +    <spacer name="verticalSpacer"> +     <property name="orientation"> +      <enum>Qt::Vertical</enum> +     </property> +     <property name="sizeType"> +      <enum>QSizePolicy::Preferred</enum> +     </property> +     <property name="sizeHint" stdset="0"> +      <size> +       <width>20</width> +       <height>15</height> +      </size> +     </property> +    </spacer> +   </item> +   <item> +    <widget class="QGroupBox" name="groupBox"> +     <property name="title"> +      <string>Password</string> +     </property> +     <layout class="QVBoxLayout" name="verticalLayout_2"> +      <item> +       <widget class="QPushButton" name="change_password_button"> +        <property name="sizePolicy"> +         <sizepolicy hsizetype="Maximum" vsizetype="Fixed"> +          <horstretch>0</horstretch> +          <verstretch>0</verstretch> +         </sizepolicy> +        </property> +        <property name="text"> +         <string>Change Password</string> +        </property> +       </widget> +      </item> +      <item> +       <widget class="QLabel" name="change_password_label"> +        <property name="text"> +         <string><change_password_label></string> +        </property> +       </widget> +      </item> +     </layout> +    </widget> +   </item> +   <item> +    <spacer name="verticalSpacer_2"> +     <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/> + <connections/> +</ui> diff --git a/src/leap/bitmask/gui/ui/preferences_email_page.ui b/src/leap/bitmask/gui/ui/preferences_email_page.ui new file mode 100644 index 00000000..41b3c28d --- /dev/null +++ b/src/leap/bitmask/gui/ui/preferences_email_page.ui @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>PreferencesEmailPage</class> + <widget class="QWidget" name="PreferencesEmailPage"> +  <property name="geometry"> +   <rect> +    <x>0</x> +    <y>0</y> +    <width>400</width> +    <height>300</height> +   </rect> +  </property> +  <property name="windowTitle"> +   <string>Form</string> +  </property> +  <widget class="QPushButton" name="pushButton"> +   <property name="geometry"> +    <rect> +     <x>250</x> +     <y>210</y> +     <width>98</width> +     <height>27</height> +    </rect> +   </property> +   <property name="text"> +    <string>PushButton</string> +   </property> +  </widget> + </widget> + <resources/> + <connections/> +</ui> diff --git a/src/leap/bitmask/gui/ui/preferences_vpn_page.ui b/src/leap/bitmask/gui/ui/preferences_vpn_page.ui new file mode 100644 index 00000000..1bf3a060 --- /dev/null +++ b/src/leap/bitmask/gui/ui/preferences_vpn_page.ui @@ -0,0 +1,69 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>PreferencesVpnPage</class> + <widget class="QWidget" name="PreferencesVpnPage"> +  <property name="geometry"> +   <rect> +    <x>0</x> +    <y>0</y> +    <width>400</width> +    <height>362</height> +   </rect> +  </property> +  <property name="windowTitle"> +   <string>Form</string> +  </property> +  <layout class="QVBoxLayout" name="verticalLayout"> +   <item> +    <widget class="QLabel" name="flash_label"> +     <property name="text"> +      <string><flash_label></string> +     </property> +     <property name="alignment"> +      <set>Qt::AlignCenter</set> +     </property> +    </widget> +   </item> +   <item> +    <widget class="QLabel" name="heading_label"> +     <property name="text"> +      <string>Default VPN Gateway:</string> +     </property> +     <property name="buddy"> +      <cstring>gateways_list</cstring> +     </property> +    </widget> +   </item> +   <item> +    <widget class="QListWidget" name="gateways_list"> +    </widget> +   </item> +   <item> +    <widget class="QLabel" name="tip_label"> +     <property name="sizePolicy"> +      <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> +       <horstretch>0</horstretch> +       <verstretch>0</verstretch> +      </sizepolicy> +     </property> +     <property name="text"> +      <string>You must reconnect for changes to take effect.</string> +     </property> +     <property name="scaledContents"> +      <bool>false</bool> +     </property> +     <property name="wordWrap"> +      <bool>true</bool> +     </property> +     <property name="buddy"> +      <cstring>gateways_list</cstring> +     </property> +    </widget> +   </item> +  </layout> + </widget> + <resources> +  <include location="../../../../../data/resources/flags.qrc"/> + </resources> + <connections/> +</ui> diff --git a/src/leap/bitmask/gui/wizard.py b/src/leap/bitmask/gui/wizard.py index 8182228d..ff9cae55 100644 --- a/src/leap/bitmask/gui/wizard.py +++ b/src/leap/bitmask/gui/wizard.py @@ -42,6 +42,7 @@ logger = logging.getLogger(__name__)  class Wizard(QtGui.QWizard): +      """      First run wizard to register a user and setup a provider      """ @@ -317,7 +318,8 @@ class Wizard(QtGui.QWizard):          user_ok, msg = username_checks(username)          if user_ok: -            pass_ok, msg = password_checks(username, password, password2) +            pass_ok, msg, field = password_checks( +                username, password, password2)          if user_ok and pass_ok:              self._set_register_status(self.tr("Starting registration...")) diff --git a/src/leap/bitmask/services/eip/conductor.py b/src/leap/bitmask/services/eip/conductor.py index 0ee56628..01dd7449 100644 --- a/src/leap/bitmask/services/eip/conductor.py +++ b/src/leap/bitmask/services/eip/conductor.py @@ -124,6 +124,12 @@ class EIPConductor(object):          """          self._backend.tear_fw_down() +    def terminate(self): +        """ +        Turn off VPN +        """ +        self.qtsigs.do_disconnect_signal.emit() +      @QtCore.Slot()      def _start_eip(self):          """ diff --git a/src/leap/bitmask/services/eip/eipconfig.py b/src/leap/bitmask/services/eip/eipconfig.py index 37c0c8ae..5b51d12e 100644 --- a/src/leap/bitmask/services/eip/eipconfig.py +++ b/src/leap/bitmask/services/eip/eipconfig.py @@ -113,39 +113,38 @@ class VPNGatewaySelector(object):          """          Return the existing gateways, sorted by timezone proximity. -        :rtype: list of tuples (location, ip) -                (str, IPv4Address or IPv6Address object) +        :rtype: list of tuples (label, ip, country_code) +                (str, IPv4Address or IPv6Address object, str)          """          gateways_timezones = []          locations = self._eipconfig.get_locations()          gateways = self._eipconfig.get_gateways()          for idx, gateway in enumerate(gateways): -            gateway_location = gateway.get('location') -            gateway_distance = 99  # if hasn't location -> should go last - -            if gateway_location is not None: -                timezone = locations[gateway['location']]['timezone'] -                gateway_name = locations[gateway['location']].get('name', None) -                if gateway_name is not None: -                    gateway_location = gateway_name - -                gw_offset = int(timezone) -                if gw_offset in self.equivalent_timezones: -                    gw_offset = self.equivalent_timezones[gw_offset] - -                gateway_distance = self._get_timezone_distance(gw_offset) +            distance = 99  # if hasn't location -> should go last +            location = locations.get(gateway.get('location')) +            label = gateway.get('location', 'Unknown') +            country = 'XX' +            if location is not None: +                country = location.get('country_code', 'XX') +                label = location.get('name', label) +                timezone = location.get('timezone') +                if timezone is not None: +                    offset = int(timezone) +                    if offset in self.equivalent_timezones: +                        offset = self.equivalent_timezones[offset] +                    distance = self._get_timezone_distance(offset)              ip = self._eipconfig.get_gateway_ip(idx) -            gateways_timezones.append((ip, gateway_distance, gateway_location)) +            gateways_timezones.append((ip, distance, label, country))          gateways_timezones = sorted(gateways_timezones, key=lambda gw: gw[1]) -        gateways = [] -        for ip, distance, location in gateways_timezones: -            gateways.append((location, ip)) +        result = [] +        for ip, distance, label, country in gateways_timezones: +            result.append((label, ip, country)) -        return gateways +        return result      def get_gateways(self):          """ @@ -153,7 +152,7 @@ class VPNGatewaySelector(object):          :rtype: list of IPv4Address or IPv6Address object.          """ -        gateways = [ip for location, ip in self.get_gateways_list()][:4] +        gateways = [gateway[1] for gateway in self.get_gateways_list()][:4]          return gateways      def get_gateways_country_code(self): diff --git a/src/leap/bitmask/util/credentials.py b/src/leap/bitmask/util/credentials.py index 757ce10c..dfc78a09 100644 --- a/src/leap/bitmask/util/credentials.py +++ b/src/leap/bitmask/util/credentials.py @@ -38,7 +38,7 @@ def username_checks(username):      valid = USERNAME_VALIDATOR.validate(username, 0)      valid_username = valid[0] == QtGui.QValidator.State.Acceptable      if message is None and not valid_username: -        message = _tr("Invalid username") +        message = _tr("That username is not allowed. Try another.")      return message is None, message @@ -54,28 +54,34 @@ def password_checks(username, password, password2):      :param password2: second password from the registration form      :type password: str -    :returns: True and empty message if all the checks pass, -              False and an error message otherwise -    :rtype: tuple(bool, str) +    :returns: (True, None, None) if all the checks pass, +              (False, message, field name) otherwise +    :rtype: tuple(bool, str, str)      """      # translation helper      _tr = QtCore.QObject().tr      message = None +    field = None      if message is None and password != password2:          message = _tr("Passwords don't match") +        field = 'new_password_confirmation'      if message is None and not password: -        message = _tr("You can't use an empty password") +        message = _tr("Password is empty") +        field = 'new_password'      if message is None and len(password) < 8: -        message = _tr("Password too short") +        message = _tr("Password is too short") +        field = 'new_password'      if message is None and password in WEAK_PASSWORDS: -        message = _tr("Password too easy") +        message = _tr("Password is too easy") +        field = 'new_password'      if message is None and username == password: -        message = _tr("Password equal to username") +        message = _tr("Password can't be the same as username") +        field = 'new_password' -    return message is None, message +    return message is None, message, field | 
