diff options
| -rw-r--r-- | changes/feature-email-preferences-panel | 1 | ||||
| -rw-r--r-- | src/leap/bitmask/backend/components.py | 20 | ||||
| -rw-r--r-- | src/leap/bitmask/gui/account.py | 10 | ||||
| -rw-r--r-- | src/leap/bitmask/gui/app.py | 34 | ||||
| -rw-r--r-- | src/leap/bitmask/gui/mainwindow.py | 32 | ||||
| -rw-r--r-- | src/leap/bitmask/gui/passwordwindow.py | 2 | ||||
| -rw-r--r-- | src/leap/bitmask/gui/preferences_account_page.py | 31 | ||||
| -rw-r--r-- | src/leap/bitmask/gui/preferences_email_page.py | 184 | ||||
| -rw-r--r-- | src/leap/bitmask/gui/preferences_page.py | 50 | ||||
| -rw-r--r-- | src/leap/bitmask/gui/preferences_vpn_page.py | 31 | ||||
| -rw-r--r-- | src/leap/bitmask/gui/preferenceswindow.py | 131 | ||||
| -rw-r--r-- | src/leap/bitmask/gui/ui/preferences.ui | 22 | ||||
| -rw-r--r-- | src/leap/bitmask/gui/ui/preferences_email_page.ui | 563 | 
13 files changed, 1007 insertions, 104 deletions
diff --git a/changes/feature-email-preferences-panel b/changes/feature-email-preferences-panel new file mode 100644 index 00000000..e9bbfb6a --- /dev/null +++ b/changes/feature-email-preferences-panel @@ -0,0 +1 @@ +- Add email panel to preferences window.
\ No newline at end of file diff --git a/src/leap/bitmask/backend/components.py b/src/leap/bitmask/backend/components.py index acb562c7..3192e1c4 100644 --- a/src/leap/bitmask/backend/components.py +++ b/src/leap/bitmask/backend/components.py @@ -576,8 +576,10 @@ class EIP(object):                      self._signaler.eip_uninitialized_provider)              return -        eip_config = eipconfig.EIPConfig()          provider_config = ProviderConfig.get_provider_config(domain) +        if EIP_SERVICE not in provider_config.get_services(): +            return +        eip_config = eipconfig.EIPConfig()          api_version = provider_config.get_api_version()          eip_config.set_api_version(api_version) @@ -1003,13 +1005,11 @@ class Keymanager(object):      def get_key_details(self, username):          """ -        List all the keys stored in the local DB. +        Get information on our primary key pair          """          def signal_details(public_key): -            # XXX: We should avoid the key-id -            details = (public_key.fingerprint[-16:], public_key.fingerprint)              self._signaler.signal(self._signaler.keymanager_key_details, -                                  details) +                                  public_key.get_dict())          d = self._keymanager_proxy.get_key(username,                                             openpgp.OpenPGPKey) @@ -1215,15 +1215,13 @@ class Authenticate(object):      def get_logged_in_status(self):          """ -        Signal if the user is currently logged in or not. +        Signal if the user is currently logged in or not. If logged in, +        authenticated username is passed as argument to the signal.          """          if self._signaler is None:              return -        signal = None          if self._is_logged_in(): -            signal = self._signaler.srp_status_logged_in +            self._signaler.signal(self._signaler.srp_status_logged_in)          else: -            signal = self._signaler.srp_status_not_logged_in - -        self._signaler.signal(signal) +            self._signaler.signal(self._signaler.srp_status_not_logged_in) diff --git a/src/leap/bitmask/gui/account.py b/src/leap/bitmask/gui/account.py index 81f96389..5e43c8fe 100644 --- a/src/leap/bitmask/gui/account.py +++ b/src/leap/bitmask/gui/account.py @@ -20,7 +20,7 @@ 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 - +from leap.bitmask._components import HAS_EIP, HAS_MAIL  class Account(): @@ -42,8 +42,8 @@ class Account():          """          return self._settings.get_enabled_services(self.domain) -    def is_email_enabled(self): -        return MX_SERVICE in self.services() +    def has_email(self): +        return HAS_MAIL and MX_SERVICE in self.services() -    def is_eip_enabled(self): -        return EIP_SERVICE in self.services() +    def has_eip(self): +        return HAS_EIP and EIP_SERVICE in self.services() diff --git a/src/leap/bitmask/gui/app.py b/src/leap/bitmask/gui/app.py index 97fd0549..e3a4d7fe 100644 --- a/src/leap/bitmask/gui/app.py +++ b/src/leap/bitmask/gui/app.py @@ -20,6 +20,7 @@ and the signaler get signals from the backend.  """  from PySide import QtCore, QtGui +from leap.bitmask.gui.account import Account  from leap.bitmask.config.leapsettings import LeapSettings  from leap.bitmask.backend.backend_proxy import BackendProxy  from leap.bitmask.backend.leapsignaler import LeapSignaler @@ -44,12 +45,37 @@ class App(QtGui.QWidget):          self.signaler.start()          self.soledad_started = False +        self.service_tokens = {} +        self.login_state = None +        self.providers_widget = None          # 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) +        # store the service tokens for later use, once they are known. +        self.signaler.soledad_got_service_token.connect( +            self._set_service_tokens) + +    def current_account(self): +        """ +        Alas, the only definitive account information is buried in the memory of +        QT widgets. + +        :returns: an object representing the current user account. +        :rtype: Account +        """ +        if self.login_state is None or self.providers_widget is None: +            return None + +        if self.login_state.full_logged_username is not None: +            username, domain = self.login_state.full_logged_username.split('@') +            return Account(username, domain) +        else: +            domain = self.providers_widget.get_selected_provider() +            return Account(None, domain) +      def _check_backend_status(self):          """          TRIGGERS: @@ -64,3 +90,11 @@ class App(QtGui.QWidget):                  self.tr("There is a problem contacting the backend, please "                          "restart Bitmask."))              self._backend_checker.stop() + +    def _set_service_tokens(self, data): +        """ +        Triggered by signal soledad_got_service_token. +        Saves the service tokens. +        """ +        service, token = data +        self.service_tokens[service] = token diff --git a/src/leap/bitmask/gui/mainwindow.py b/src/leap/bitmask/gui/mainwindow.py index cde44f7b..ca14e631 100644 --- a/src/leap/bitmask/gui/mainwindow.py +++ b/src/leap/bitmask/gui/mainwindow.py @@ -46,7 +46,6 @@ from leap.bitmask.gui.signaltracker import SignalTracker  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 @@ -154,6 +153,14 @@ class MainWindow(QtGui.QMainWindow, SignalTracker):          # Provider List          self._providers = Providers(self.ui.cmbProviders) +        ## +        ## tmphack: important state information about the application is stored +        ## in widgets. Rather than rewrite the UI, for now we simulate this +        ## info being stored in an application object: +        ## +        self.app.login_state      = self._login_widget._state +        self.app.providers_widget = self._providers +          # Qt Signal Connections #####################################          # TODO separate logic from ui signals. @@ -416,10 +423,6 @@ class MainWindow(QtGui.QMainWindow, SignalTracker):          sig.soledad_invalid_auth_token.connect(              self._mail_status.set_soledad_invalid_auth_token) -        self._service_tokens = {} -        sig.soledad_got_service_token.connect( -            self._set_service_tokens) -          # TODO: connect this with something          # sig.soledad_cancelled_bootstrap.connect() @@ -569,15 +572,7 @@ class MainWindow(QtGui.QMainWindow, SignalTracker):          Display the preferences window.          """ -        logged_user = self._login_widget.get_logged_user() -        if logged_user is not None: -            user, domain = logged_user.split('@') -        else: -            user = None -            domain = self._providers.get_selected_provider() - -        account = Account(user, domain) -        pref_win = PreferencesWindow(self, account, self.app) +        pref_win = PreferencesWindow(self, self.app)          pref_win.show()      def _show_pixelated_browser(self): @@ -1054,13 +1049,6 @@ class MainWindow(QtGui.QMainWindow, SignalTracker):          msg = msg.format(ver=VERSION, ver_hash=VERSION_HASH[:10], greet=greet)          QtGui.QMessageBox.about(self, title, msg) -    def _set_service_tokens(self, data): -        """ -        Set the received service token. -        """ -        service, token = data -        self._service_tokens[service] = token -      def _help(self):          """          TRIGGERS: @@ -1095,7 +1083,7 @@ class MainWindow(QtGui.QMainWindow, SignalTracker):          # FIXME on i3, this doens't allow to mouse-select.          # Switch to a dialog in which we can set the QLabel          mail_auth_token = ( -            self._service_tokens.get('mail_auth', None) or +            self.app.service_tokens.get('mail_auth', None) or              "??? (log in to unlock)")          mail_password = self.tr("IMAP/SMTP Password:") + " %s" % (              mail_auth_token,) diff --git a/src/leap/bitmask/gui/passwordwindow.py b/src/leap/bitmask/gui/passwordwindow.py index dedfcb10..fe70b250 100644 --- a/src/leap/bitmask/gui/passwordwindow.py +++ b/src/leap/bitmask/gui/passwordwindow.py @@ -83,7 +83,7 @@ class PasswordWindow(QtGui.QDialog, Flashable):          Returns true if the current account needs to change the soledad          password as well as the SRP password.          """ -        return self.account.is_email_enabled() +        return self.account.has_email()      #      # MANAGE WIDGETS diff --git a/src/leap/bitmask/gui/preferences_account_page.py b/src/leap/bitmask/gui/preferences_account_page.py index da9da14d..c175c42b 100644 --- a/src/leap/bitmask/gui/preferences_account_page.py +++ b/src/leap/bitmask/gui/preferences_account_page.py @@ -22,6 +22,7 @@ from PySide import QtCore, QtGui  from leap.bitmask.logs.utils import get_logger  from leap.bitmask.gui import ui_preferences_account_page as ui_pref +from leap.bitmask.gui.preferences_page import PreferencesPage  from leap.bitmask.gui.passwordwindow import PasswordWindow  from leap.bitmask.services import get_service_display_name  from leap.bitmask._components import HAS_EIP @@ -29,7 +30,7 @@ from leap.bitmask._components import HAS_EIP  logger = get_logger() -class PreferencesAccountPage(QtGui.QWidget): +class PreferencesAccountPage(PreferencesPage):      def __init__(self, parent, account, app):          """ @@ -42,20 +43,15 @@ class PreferencesAccountPage(QtGui.QWidget):          :param app: the current App object          :type app: App          """ -        QtGui.QWidget.__init__(self, parent) +        PreferencesPage.__init__(self, parent, account, app)          self.ui = ui_pref.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) +        self.setup_connections()          app.backend.provider_get_supported_services(domain=account.domain)          if account.username is None: @@ -64,6 +60,25 @@ class PreferencesAccountPage(QtGui.QWidget):              self.ui.change_password_label.setVisible(True)              self.ui.change_password_button.setEnabled(False) +    def setup_connections(self): +        """ +        connect signals +        """ +        self.ui.change_password_button.clicked.connect( +            self._show_change_password) +        self.app.signaler.prov_get_supported_services.connect( +            self._load_services) + + +    def teardown_connections(self): +        """ +        disconnect signals +        """ +        self.ui.change_password_button.clicked.disconnect( +            self._show_change_password) +        self.app.signaler.prov_get_supported_services.disconnect( +            self._load_services) +      def _service_selection_changed(self, service, state):          """          TRIGGERS: diff --git a/src/leap/bitmask/gui/preferences_email_page.py b/src/leap/bitmask/gui/preferences_email_page.py index 3087f343..50f244fb 100644 --- a/src/leap/bitmask/gui/preferences_email_page.py +++ b/src/leap/bitmask/gui/preferences_email_page.py @@ -16,20 +16,192 @@  """  Widget for "email" preferences  """ -from PySide import QtGui +from PySide import QtCore, QtGui + +from datetime import datetime  from leap.bitmask.logs.utils import get_logger  from leap.bitmask.gui.ui_preferences_email_page import Ui_PreferencesEmailPage +from leap.bitmask.gui.preferences_page import PreferencesPage +from leap.mail.imap.service.imap import IMAP_PORT  logger = get_logger() - -class PreferencesEmailPage(QtGui.QWidget): +class PreferencesEmailPage(PreferencesPage):      def __init__(self, parent, account, app): -        QtGui.QWidget.__init__(self, parent) +        """ +        :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 +        """ +        PreferencesPage.__init__(self, parent, account, app)          self.ui = Ui_PreferencesEmailPage()          self.ui.setupUi(self) -        self.account = account -        self.app = app +        # the only way to set the tab titles is to re-add them: +        self.ui.email_tabs.addTab(self.ui.config_tab, +          self.tr("Mail Client")) +        self.ui.email_tabs.addTab(self.ui.my_key_tab, +          self.tr("My Key")) +        self.ui.email_tabs.addTab(self.ui.other_keys_tab, +          self.tr("Other Keys")) + +        # set mail client configuration help text +        lang = QtCore.QLocale.system().name().replace('_', '-') +        thunderbird_extension_url = \ +            "https://addons.mozilla.org/{0}/" \ +            "thunderbird/addon/bitmask/".format(lang) +        self.ui.thunderbird_label.setText(self.tr( +            "For Thunderbird, you can use the Bitmask extension. " +            "Search for \"Bitmask\" in the add-on manager or " +            "download it from <a href='{0}'>addons.mozilla.org</a>.".format( +            thunderbird_extension_url))) +        self.ui.mail_client_label.setText(self.tr( +            "Alternatively, you can manually configure your mail client to " +            "use Bitmask Email with these options:")) + +        self.ui.keys_table.horizontalHeader().setResizeMode( +            0, QtGui.QHeaderView.Stretch) + +        self.setup_connections() + + +    def setup_connections(self): +        """ +        connect signals +        """ +        self.app.signaler.keymanager_key_details.connect(self._key_details) +        self.app.signaler.keymanager_export_ok.connect( +            self._keymanager_export_ok) +        self.app.signaler.keymanager_export_error.connect( +            self._keymanager_export_error) +        self.ui.import_button.clicked.connect(self._import_keys) +        self.ui.export_button.clicked.connect(self._export_keys) + +    def teardown_connections(self): +        """ +        disconnect signals +        """ +        self.app.signaler.keymanager_key_details.disconnect(self._key_details) +        self.app.signaler.keymanager_export_ok.disconnect( +            self._keymanager_export_ok) +        self.app.signaler.keymanager_export_error.disconnect( +            self._keymanager_export_error) + +    def showEvent(self, event): +        """ +        called whenever this widget is shown +        """ +        self.ui.keys_table.clearContents() + +        if self.account.username is None: +            self.ui.email_tabs.setVisible(False) +            self.ui.message_label.setVisible(True) +            self.ui.message_label.setText( +                self.tr('You must be logged in to edit email settings.')) +        else: +            self.ui.import_button.setVisible(False) # hide this until working +            self.ui.message_label.setVisible(False) +            self.ui.email_tabs.setVisible(True) +            smtp_port = 2013 +            self.ui.imap_port_edit.setText(str(IMAP_PORT)) +            self.ui.imap_host_edit.setText("127.0.0.1") +            self.ui.smtp_port_edit.setText(str(smtp_port)) +            self.ui.smtp_host_edit.setText("127.0.0.1") +            self.ui.username_edit.setText(self.account.address) +            self.ui.password_edit.setText( +                self.app.service_tokens.get('mail_auth', '')) + +            self.app.backend.keymanager_list_keys() +            self.app.backend.keymanager_get_key_details( +                username=self.account.address) + +    def _key_details(self, details): +        """ +        Trigger by signal: keymanager_key_details +        Set the current user's key details into the gui. +        """ +        self.ui.fingerprint_edit.setPlainText( +          self._format_fingerprint(details["fingerprint"])) +        self.ui.expiration_edit.setText(details["expiry_date"]) +        self.ui.uid_edit.setText(" ".join(details["uids"])) +        self.ui.public_key_edit.setPlainText(details["key_data"]) + +    def _format_fingerprint(self, fingerprint): +        """ +        formats an openpgp fingerprint in a manner similar to what gpg +        produces, wrapped to two lines. +        """ +        fp = fingerprint.upper() +        fp_list = [fp[i:i+4] for i in range(0, len(fp), 4)] +        fp_wrapped =  " ".join(fp_list[0:5]) + "\n" + " ".join(fp_list[5:10]) +        return fp_wrapped + +    def _export_keys(self): +        """ +        Exports the user's key pair. +        """ +        file_name, filtr = QtGui.QFileDialog.getSaveFileName( +            self, self.tr("Save private key file"), +            filter="*.pem", +            options=QtGui.QFileDialog.DontUseNativeDialog) + +        if file_name: +            if not file_name.endswith('.pem'): +                file_name += '.pem' +            self.app.backend.keymanager_export_keys( +              username=self.account.address, +              filename=file_name) +        else: +            logger.debug('Export canceled by the user.') + +    def _keymanager_export_ok(self): +        """ +        TRIGGERS: +            Signaler.keymanager_export_ok + +        Notify the user that the key export went OK. +        """ +        QtGui.QMessageBox.information( +            self, self.tr("Export Successful"), +            self.tr("The key pair was exported successfully.\n" +                    "Please, store your private key in a safe place.")) + +    def _keymanager_export_error(self): +        """ +        TRIGGERS: +            Signaler.keymanager_export_error + +        Notify the user that the key export didn't go well. +        """ +        QtGui.QMessageBox.critical( +            self, self.tr("Input/Output error"), +            self.tr("There was an error accessing the file.\n" +                    "Export canceled.")) + +    def _import_keys(self): +      """ +      not yet supported +      """ + +    def _keymanager_keys_list(self, keys): +        """ +        TRIGGERS: +            Signaler.keymanager_keys_list + +        Load the keys given as parameter in the table. + +        :param keys: the list of keys to load. +        :type keys: list +        """ +        for key in keys: +            row = self.ui.keys_table.rowCount() +            self.ui.keys_table.insertRow(row) +            self.ui.keys_table.setItem(row, 0, QtGui.QTableWidgetItem(key.address)) +            self.ui.keys_table.setItem(row, 1, QtGui.QTableWidgetItem(key.fingerprint)) diff --git a/src/leap/bitmask/gui/preferences_page.py b/src/leap/bitmask/gui/preferences_page.py new file mode 100644 index 00000000..c75b4991 --- /dev/null +++ b/src/leap/bitmask/gui/preferences_page.py @@ -0,0 +1,50 @@ +# -*- 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/>. +""" +base class for preference pages +""" + +from PySide import QtCore, QtGui + +class PreferencesPage(QtGui.QWidget): + +    def __init__(self, parent, account=None, app=None): +        """ +        :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.app = app +        self.account = account + +    def setup_connections(self): +        """ +        connect signals +        must be overridden by subclass +        """ + +    def teardown_connections(self): +        """ +        disconnect signals +        must be overridden by subclass +        """ + diff --git a/src/leap/bitmask/gui/preferences_vpn_page.py b/src/leap/bitmask/gui/preferences_vpn_page.py index 5b5c9604..fc15340f 100644 --- a/src/leap/bitmask/gui/preferences_vpn_page.py +++ b/src/leap/bitmask/gui/preferences_vpn_page.py @@ -22,9 +22,9 @@ 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 +from leap.bitmask.gui.preferences_page import PreferencesPage - -class PreferencesVpnPage(QtGui.QWidget, Flashable): +class PreferencesVpnPage(PreferencesPage, Flashable):      """      Page in the preferences window that shows VPN settings @@ -41,19 +41,24 @@ class PreferencesVpnPage(QtGui.QWidget, Flashable):          :param app: shared App instance          :type app: App          """ -        QtGui.QWidget.__init__(self, parent) +        PreferencesPage.__init__(self, parent, account, app)          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.setup_connections() + +        # Trigger update +        self.app.backend.eip_get_gateways_list(domain=self.account.domain) + +    def setup_connections(self): +        """ +        connect signals +        """          self.ui.gateways_list.clicked.connect(self._save_selected_gateway)          sig = self.app.signaler          sig.eip_get_gateways_list.connect(self._update_gateways_list) @@ -61,8 +66,16 @@ class PreferencesVpnPage(QtGui.QWidget, Flashable):          sig.eip_uninitialized_provider.connect(              self._gateways_list_uninitialized) -        # Trigger update -        self.app.backend.eip_get_gateways_list(domain=self.account.domain) +    def teardown_connections(self): +        """ +        disconnect signals +        """ +        self.ui.gateways_list.clicked.disconnect(self._save_selected_gateway) +        sig = self.app.signaler +        sig.eip_get_gateways_list.disconnect(self._update_gateways_list) +        sig.eip_get_gateways_list_error.disconnect(self._gateways_list_error) +        sig.eip_uninitialized_provider.disconnect( +            self._gateways_list_uninitialized)      def _save_selected_gateway(self, index):          """ diff --git a/src/leap/bitmask/gui/preferenceswindow.py b/src/leap/bitmask/gui/preferenceswindow.py index 44c4641c..1facba69 100644 --- a/src/leap/bitmask/gui/preferenceswindow.py +++ b/src/leap/bitmask/gui/preferenceswindow.py @@ -20,18 +20,15 @@ Preferences window  """  from PySide import QtCore, QtGui -from leap.bitmask.services import EIP_SERVICE -from leap.bitmask._components import HAS_EIP -  from leap.bitmask.logs.utils import get_logger  from leap.bitmask.gui.ui_preferences import Ui_Preferences +from leap.bitmask.gui.preferences_page import PreferencesPage  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 = get_logger() -  class PreferencesWindow(QtGui.QDialog):      """ @@ -40,39 +37,46 @@ class PreferencesWindow(QtGui.QDialog):      _current_window = None  # currently visible preferences window -    def __init__(self, parent, account, app): +    def __init__(self, parent, app):          """          :param parent: parent object of the PreferencesWindow.          :parent type: QWidget -        :param account: the user or provider -        :type account: Account -          :param app: the current App object          :type app: App          """          QtGui.QDialog.__init__(self, parent) -        self.account = account          self.app = app          self.ui = Ui_Preferences()          self.ui.setupUi(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._account_page = None +        self._vpn_page = None +        self._email_page = None          self._add_icons() -        self._add_pages() -        self._update_icons(self.account, self.account.services()) +        self._set_account(app.current_account()) +        self._setup_connections()          # only allow a single preferences window at a time.          if PreferencesWindow._current_window is not None:              PreferencesWindow._current_window.close()          PreferencesWindow._current_window = self +    def _set_account(self, account): +        """ +        Initially sets, or resets, the currently viewed account. +        The account might not represent an authenticated user, but +        just a domain. +        """ +        self.ui.account_label.setText(account.address) +        self._add_pages(account) +        self._update_icons(account) +        self.ui.pages_widget.setCurrentIndex(0) +        self.ui.nav_widget.setCurrentRow(0) +      def _add_icons(self):          """          Adds all the icons for the different configuration categories. @@ -114,22 +118,71 @@ class PreferencesWindow(QtGui.QDialog):          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): +    def _add_pages(self, account):          """          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._remove_pages() # in case different account was loaded. + +        # load placeholder widgets if the page should not be loaded. +        # the order of the pages is important, and must match the order +        # of the nav_widget icons. +        self._account_page = PreferencesAccountPage(self, account, self.app) +        if account.has_eip(): +            self._vpn_page = PreferencesVpnPage(self, account, self.app) +        else: +            self._vpn_page = PreferencesPage(self) +        if account.has_email(): +            self._email_page = PreferencesEmailPage(self, account, self.app) +        else: +            self._email_page = PreferencesPage(self)          self.ui.pages_widget.addWidget(self._account_page)          self.ui.pages_widget.addWidget(self._vpn_page)          self.ui.pages_widget.addWidget(self._email_page) +    def _remove_pages(self): +        # deleteLater does not seem to cascade to items in stackLayout +        # (even with QtCore.Qt.WA_DeleteOnClose attribute). +        # so, here we call deleteLater() explicitly. +        if self._account_page is not None: +            self.ui.pages_widget.removeWidget(self._account_page) +            self._account_page.teardown_connections() +            self._account_page.deleteLater() +        if self._vpn_page is not None: +            self.ui.pages_widget.removeWidget(self._vpn_page) +            self._vpn_page.teardown_connections() +            self._vpn_page.deleteLater() +        if self._email_page is not None: +            self.ui.pages_widget.removeWidget(self._email_page) +            self._email_page.teardown_connections() +            self._email_page.deleteLater() + +    def _setup_connections(self): +        """ +        setup signal connections +        """ +        self.ui.nav_widget.currentItemChanged.connect(self._change_page) +        self.ui.close_button.clicked.connect(self.close) +        self.app.service_selection_changed.connect(self._update_icons) +        sig = self.app.signaler +        sig.srp_auth_ok.connect(self._login_status_changed) +        sig.srp_logout_ok.connect(self._login_status_changed) +        sig.srp_status_logged_in.connect(self._update_account) +        sig.srp_status_not_logged_in.connect(self._update_account) + +    def _teardown_connections(self): +        """ +        clean up signal connections +        """ +        self.ui.nav_widget.currentItemChanged.disconnect(self._change_page) +        self.ui.close_button.clicked.disconnect(self.close) +        self.app.service_selection_changed.disconnect(self._update_icons) +        sig = self.app.signaler +        sig.srp_auth_ok.disconnect(self._login_status_changed) +        sig.srp_logout_ok.disconnect(self._login_status_changed) +        sig.srp_status_logged_in.disconnect(self._update_account) +        sig.srp_status_not_logged_in.disconnect(self._update_account) +      #      # Slots      # @@ -144,13 +197,8 @@ class PreferencesWindow(QtGui.QDialog):          Close this dialog and destroy it.          """          PreferencesWindow._current_window = None - -        # 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._teardown_connections(); +        self._remove_pages();          self.deleteLater()      def _change_page(self, current, previous): @@ -170,17 +218,24 @@ class PreferencesWindow(QtGui.QDialog):              current = previous          self.ui.pages_widget.setCurrentIndex(self.ui.nav_widget.row(current)) -    def _update_icons(self, account, services): +    def _update_icons(self, account):          """          TRIGGERS:              self.app.service_selection_changed          Change which icons are visible.          """ -        if account != self.account: -            return +        self._vpn_item.setHidden(not account.has_eip()) +        self._email_item.setHidden(not account.has_email()) + +    def _login_status_changed(self): +        """ +        Triggered by signal srp_auth_ok, srp_logout_ok +        """ +        self.app.backend.user_get_logged_in_status() -        if HAS_EIP: -            self._vpn_item.setHidden(EIP_SERVICE not in services) -        # self._email_item.setHidden(not MX_SERVICE in services) -        # ^^ disable email for now, there is nothing there yet. +    def _update_account(self): +        """ +        Triggered by get srp_status_logged_in, srp_status_not_logged_in +        """ +        self._set_account(self.app.current_account()) diff --git a/src/leap/bitmask/gui/ui/preferences.ui b/src/leap/bitmask/gui/ui/preferences.ui index 5e30ea57..51cad0a1 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>520</width> -    <height>439</height> +    <width>630</width> +    <height>500</height>     </rect>    </property>    <property name="windowTitle"> @@ -60,6 +60,24 @@           <height>16777215</height>          </size>         </property> +       <property name="styleSheet"> +        <string notr="true">background: palette(base); border: 1px solid palette(dark); border-radius: 2px;</string> +       </property> +       <property name="frameShape"> +        <enum>QFrame::StyledPanel</enum> +       </property> +       <property name="frameShadow"> +        <enum>QFrame::Plain</enum> +       </property> +       <property name="lineWidth"> +        <number>1</number> +       </property> +       <property name="midLineWidth"> +        <number>0</number> +       </property> +       <property name="horizontalScrollBarPolicy"> +        <enum>Qt::ScrollBarAlwaysOff</enum> +       </property>         <property name="iconSize">          <size>           <width>32</width> diff --git a/src/leap/bitmask/gui/ui/preferences_email_page.ui b/src/leap/bitmask/gui/ui/preferences_email_page.ui index 7cc5bb3c..5f83426b 100644 --- a/src/leap/bitmask/gui/ui/preferences_email_page.ui +++ b/src/leap/bitmask/gui/ui/preferences_email_page.ui @@ -6,13 +6,572 @@     <rect>      <x>0</x>      <y>0</y> -    <width>400</width> -    <height>300</height> +    <width>526</width> +    <height>605</height>     </rect>    </property>    <property name="windowTitle">     <string>Form</string>    </property> +  <layout class="QVBoxLayout" name="verticalLayout"> +   <property name="spacing"> +    <number>0</number> +   </property> +   <property name="margin"> +    <number>0</number> +   </property> +   <item> +    <widget class="QTabWidget" name="email_tabs"> +     <property name="currentIndex"> +      <number>2</number> +     </property> +     <widget class="QWidget" name="config_tab"> +      <property name="accessibleName"> +       <string/> +      </property> +      <property name="accessibleDescription"> +       <string/> +      </property> +      <attribute name="title"> +       <string>Tab 1</string> +      </attribute> +      <layout class="QVBoxLayout" name="verticalLayout_4"> +       <item> +        <widget class="QGroupBox" name="thunderbird_box"> +         <property name="title"> +          <string>Thunderbird Configuration</string> +         </property> +         <layout class="QVBoxLayout" name="verticalLayout_2"> +          <item> +           <widget class="QLabel" name="thunderbird_label"> +            <property name="text"> +             <string>thunderbird information</string> +            </property> +            <property name="wordWrap"> +             <bool>true</bool> +            </property> +           </widget> +          </item> +         </layout> +        </widget> +       </item> +       <item> +        <widget class="QGroupBox" name="imap_box"> +         <property name="sizePolicy"> +          <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> +           <horstretch>0</horstretch> +           <verstretch>0</verstretch> +          </sizepolicy> +         </property> +         <property name="title"> +          <string>Mail Client Configuration</string> +         </property> +         <layout class="QVBoxLayout" name="verticalLayout_3"> +          <item> +           <widget class="QLabel" name="mail_client_label"> +            <property name="text"> +             <string>mail client information</string> +            </property> +            <property name="wordWrap"> +             <bool>true</bool> +            </property> +           </widget> +          </item> +          <item> +           <layout class="QGridLayout" name="gridLayout_2"> +            <item row="1" column="1"> +             <layout class="QGridLayout" name="gridLayout_5"> +              <item row="0" column="0"> +               <widget class="QLabel" name="label_8"> +                <property name="sizePolicy"> +                 <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> +                  <horstretch>0</horstretch> +                  <verstretch>0</verstretch> +                 </sizepolicy> +                </property> +                <property name="text"> +                 <string>Host</string> +                </property> +                <property name="alignment"> +                 <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> +                </property> +               </widget> +              </item> +              <item row="0" column="2"> +               <widget class="QLabel" name="label_9"> +                <property name="sizePolicy"> +                 <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> +                  <horstretch>0</horstretch> +                  <verstretch>0</verstretch> +                 </sizepolicy> +                </property> +                <property name="text"> +                 <string>Port</string> +                </property> +                <property name="alignment"> +                 <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> +                </property> +               </widget> +              </item> +              <item row="0" column="1"> +               <widget class="QLineEdit" name="smtp_host_edit"> +                <property name="sizePolicy"> +                 <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> +                  <horstretch>0</horstretch> +                  <verstretch>0</verstretch> +                 </sizepolicy> +                </property> +                <property name="maximumSize"> +                 <size> +                  <width>100</width> +                  <height>16777215</height> +                 </size> +                </property> +                <property name="text"> +                 <string/> +                </property> +                <property name="readOnly"> +                 <bool>true</bool> +                </property> +               </widget> +              </item> +              <item row="0" column="4"> +               <widget class="QLabel" name="label_10"> +                <property name="sizePolicy"> +                 <sizepolicy hsizetype="Fixed" vsizetype="Preferred"> +                  <horstretch>0</horstretch> +                  <verstretch>0</verstretch> +                 </sizepolicy> +                </property> +                <property name="text"> +                 <string>TLS: off</string> +                </property> +               </widget> +              </item> +              <item row="0" column="3"> +               <widget class="QLineEdit" name="smtp_port_edit"> +                <property name="sizePolicy"> +                 <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> +                  <horstretch>0</horstretch> +                  <verstretch>0</verstretch> +                 </sizepolicy> +                </property> +                <property name="maximumSize"> +                 <size> +                  <width>50</width> +                  <height>16777215</height> +                 </size> +                </property> +                <property name="readOnly"> +                 <bool>true</bool> +                </property> +               </widget> +              </item> +              <item row="0" column="5"> +               <spacer name="horizontalSpacer_3"> +                <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> +            </item> +            <item row="0" column="0"> +             <widget class="QLabel" name="label"> +              <property name="text"> +               <string>IMAP</string> +              </property> +             </widget> +            </item> +            <item row="2" column="0"> +             <widget class="QLabel" name="label_4"> +              <property name="text"> +               <string>Username</string> +              </property> +             </widget> +            </item> +            <item row="1" column="0"> +             <widget class="QLabel" name="label_2"> +              <property name="text"> +               <string>SMTP</string> +              </property> +             </widget> +            </item> +            <item row="0" column="1"> +             <layout class="QGridLayout" name="gridLayout_4"> +              <item row="1" column="0"> +               <widget class="QLabel" name="label_5"> +                <property name="sizePolicy"> +                 <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> +                  <horstretch>0</horstretch> +                  <verstretch>0</verstretch> +                 </sizepolicy> +                </property> +                <property name="text"> +                 <string>Host</string> +                </property> +                <property name="alignment"> +                 <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> +                </property> +               </widget> +              </item> +              <item row="1" column="1"> +               <widget class="QLineEdit" name="imap_host_edit"> +                <property name="sizePolicy"> +                 <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> +                  <horstretch>0</horstretch> +                  <verstretch>0</verstretch> +                 </sizepolicy> +                </property> +                <property name="maximumSize"> +                 <size> +                  <width>100</width> +                  <height>16777215</height> +                 </size> +                </property> +                <property name="text"> +                 <string/> +                </property> +                <property name="readOnly"> +                 <bool>true</bool> +                </property> +               </widget> +              </item> +              <item row="1" column="2"> +               <widget class="QLabel" name="label_6"> +                <property name="sizePolicy"> +                 <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> +                  <horstretch>0</horstretch> +                  <verstretch>0</verstretch> +                 </sizepolicy> +                </property> +                <property name="text"> +                 <string>Port</string> +                </property> +                <property name="alignment"> +                 <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> +                </property> +               </widget> +              </item> +              <item row="1" column="6"> +               <spacer name="horizontalSpacer_2"> +                <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="1" column="5"> +               <widget class="QLabel" name="label_7"> +                <property name="sizePolicy"> +                 <sizepolicy hsizetype="Fixed" vsizetype="Preferred"> +                  <horstretch>0</horstretch> +                  <verstretch>0</verstretch> +                 </sizepolicy> +                </property> +                <property name="text"> +                 <string>TLS: off</string> +                </property> +               </widget> +              </item> +              <item row="1" column="3"> +               <widget class="QLineEdit" name="imap_port_edit"> +                <property name="sizePolicy"> +                 <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> +                  <horstretch>0</horstretch> +                  <verstretch>0</verstretch> +                 </sizepolicy> +                </property> +                <property name="maximumSize"> +                 <size> +                  <width>50</width> +                  <height>16777215</height> +                 </size> +                </property> +                <property name="readOnly"> +                 <bool>true</bool> +                </property> +               </widget> +              </item> +             </layout> +            </item> +            <item row="2" column="1"> +             <widget class="QLineEdit" name="username_edit"> +              <property name="readOnly"> +               <bool>true</bool> +              </property> +             </widget> +            </item> +            <item row="3" column="0"> +             <widget class="QLabel" name="label_3"> +              <property name="text"> +               <string>Password</string> +              </property> +             </widget> +            </item> +            <item row="3" column="1"> +             <widget class="QLineEdit" name="password_edit"> +              <property name="readOnly"> +               <bool>true</bool> +              </property> +             </widget> +            </item> +           </layout> +          </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> +     <widget class="QWidget" name="my_key_tab"> +      <attribute name="title"> +       <string>Tab 2</string> +      </attribute> +      <layout class="QVBoxLayout" name="verticalLayout_5"> +       <item> +        <widget class="QWidget" name="mykey_box" native="true"> +         <layout class="QGridLayout" name="gridLayout_3"> +          <item row="4" column="0"> +           <widget class="QLabel" name="pub_label"> +            <property name="text"> +             <string>Public Key</string> +            </property> +           </widget> +          </item> +          <item row="4" column="1"> +           <widget class="QPlainTextEdit" name="public_key_edit"> +            <property name="sizePolicy"> +             <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> +              <horstretch>0</horstretch> +              <verstretch>0</verstretch> +             </sizepolicy> +            </property> +            <property name="font"> +             <font> +              <family>Courier</family> +             </font> +            </property> +            <property name="lineWrapMode"> +             <enum>QPlainTextEdit::NoWrap</enum> +            </property> +            <property name="readOnly"> +             <bool>true</bool> +            </property> +           </widget> +          </item> +          <item row="3" column="1"> +           <widget class="QPlainTextEdit" name="fingerprint_edit"> +            <property name="sizePolicy"> +             <sizepolicy hsizetype="Expanding" vsizetype="Fixed"> +              <horstretch>0</horstretch> +              <verstretch>42</verstretch> +             </sizepolicy> +            </property> +            <property name="maximumSize"> +             <size> +              <width>16777215</width> +              <height>48</height> +             </size> +            </property> +            <property name="font"> +             <font> +              <family>Courier</family> +              <weight>50</weight> +              <bold>false</bold> +             </font> +            </property> +            <property name="acceptDrops"> +             <bool>false</bool> +            </property> +            <property name="styleSheet"> +             <string notr="true"/> +            </property> +            <property name="lineWrapMode"> +             <enum>QPlainTextEdit::NoWrap</enum> +            </property> +            <property name="readOnly"> +             <bool>true</bool> +            </property> +            <property name="plainText"> +             <string notr="true"/> +            </property> +           </widget> +          </item> +          <item row="1" column="0"> +           <widget class="QLabel" name="expiration_label"> +            <property name="text"> +             <string>Expiration</string> +            </property> +           </widget> +          </item> +          <item row="3" column="0"> +           <widget class="QLabel" name="fp_label"> +            <property name="text"> +             <string>Fingerprint</string> +            </property> +           </widget> +          </item> +          <item row="0" column="1"> +           <widget class="QLineEdit" name="uid_edit"> +            <property name="enabled"> +             <bool>true</bool> +            </property> +            <property name="text"> +             <string/> +            </property> +            <property name="alignment"> +             <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set> +            </property> +            <property name="readOnly"> +             <bool>true</bool> +            </property> +           </widget> +          </item> +          <item row="0" column="0"> +           <widget class="QLabel" name="uid_label"> +            <property name="text"> +             <string>Address</string> +            </property> +           </widget> +          </item> +          <item row="1" column="1"> +           <widget class="QLineEdit" name="expiration_edit"> +            <property name="readOnly"> +             <bool>true</bool> +            </property> +           </widget> +          </item> +          <item row="5" column="1"> +           <layout class="QGridLayout" name="gridLayout"> +            <item row="2" column="1"> +             <widget class="QPushButton" name="export_button"> +              <property name="text"> +               <string>Export Private Key</string> +              </property> +             </widget> +            </item> +            <item row="2" column="2"> +             <widget class="QPushButton" name="import_button"> +              <property name="text"> +               <string>Import Private Key</string> +              </property> +             </widget> +            </item> +            <item row="2" column="3"> +             <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> +           </layout> +          </item> +         </layout> +         <zorder>uid_edit</zorder> +         <zorder>fp_label</zorder> +         <zorder>uid_label</zorder> +         <zorder>expiration_edit</zorder> +         <zorder>expiration_label</zorder> +         <zorder>fingerprint_edit</zorder> +         <zorder>public_key_edit</zorder> +         <zorder>pub_label</zorder> +         <zorder></zorder> +        </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> +     <widget class="QWidget" name="other_keys_tab"> +      <attribute name="title"> +       <string>Page</string> +      </attribute> +      <layout class="QVBoxLayout" name="verticalLayout_6"> +       <item> +        <widget class="QTableWidget" name="keys_table"> +         <property name="editTriggers"> +          <set>QAbstractItemView::NoEditTriggers</set> +         </property> +         <property name="alternatingRowColors"> +          <bool>true</bool> +         </property> +         <property name="selectionMode"> +          <enum>QAbstractItemView::SingleSelection</enum> +         </property> +         <property name="selectionBehavior"> +          <enum>QAbstractItemView::SelectRows</enum> +         </property> +         <property name="textElideMode"> +          <enum>Qt::ElideRight</enum> +         </property> +         <attribute name="horizontalHeaderStretchLastSection"> +          <bool>true</bool> +         </attribute> +         <column> +          <property name="text"> +           <string>Email</string> +          </property> +         </column> +         <column> +          <property name="text"> +           <string>Key ID</string> +          </property> +         </column> +        </widget> +       </item> +      </layout> +     </widget> +    </widget> +   </item> +   <item> +    <widget class="QLabel" name="message_label"> +     <property name="text"> +      <string>this message should be hidden</string> +     </property> +    </widget> +   </item> +  </layout>   </widget>   <resources/>   <connections/>  | 
