From 7fcc4f40eaa8214de8ae20cd71d173337ad64290 Mon Sep 17 00:00:00 2001 From: drebs Date: Mon, 18 Aug 2014 11:36:09 -0300 Subject: Fix soledad imports (#5989). --- src/leap/bitmask/backend/components.py | 2 +- src/leap/bitmask/services/soledad/soledadbootstrapper.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) (limited to 'src/leap/bitmask') diff --git a/src/leap/bitmask/backend/components.py b/src/leap/bitmask/backend/components.py index f721086b..960a922e 100644 --- a/src/leap/bitmask/backend/components.py +++ b/src/leap/bitmask/backend/components.py @@ -60,7 +60,7 @@ from leap.common import certs as leap_certs from leap.keymanager import openpgp from leap.keymanager.errors import KeyAddressMismatch, KeyFingerprintMismatch -from leap.soledad.client import NoStorageSecret, PassphraseTooShort +from leap.soledad.client.secrets import NoStorageSecret, PassphraseTooShort logger = logging.getLogger(__name__) diff --git a/src/leap/bitmask/services/soledad/soledadbootstrapper.py b/src/leap/bitmask/services/soledad/soledadbootstrapper.py index c4e43bfe..745645f3 100644 --- a/src/leap/bitmask/services/soledad/soledadbootstrapper.py +++ b/src/leap/bitmask/services/soledad/soledadbootstrapper.py @@ -45,7 +45,8 @@ from leap.common.files import which from leap.keymanager import KeyManager, openpgp from leap.keymanager.errors import KeyNotFound from leap.soledad.common.errors import InvalidAuthTokenError -from leap.soledad.client import Soledad, BootstrapSequenceError +from leap.soledad.client import Soledad +from leap.soledad.client.secrets import BootstrapSequenceError logger = logging.getLogger(__name__) -- cgit v1.2.3 From 9f8c6bdda8ca6274e2d013d6528ab4ff83b35654 Mon Sep 17 00:00:00 2001 From: Kali Kaneko Date: Mon, 18 Aug 2014 22:02:09 -0500 Subject: make the login not wait for eip it cannot-start-eip --- src/leap/bitmask/backend/components.py | 2 +- src/leap/bitmask/gui/mainwindow.py | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) (limited to 'src/leap/bitmask') diff --git a/src/leap/bitmask/backend/components.py b/src/leap/bitmask/backend/components.py index 960a922e..89f3c5a6 100644 --- a/src/leap/bitmask/backend/components.py +++ b/src/leap/bitmask/backend/components.py @@ -665,7 +665,7 @@ class EIP(object): return False client_cert_path = eip_config.\ - get_client_cert_path(provider_config, about_to_download=False) + get_client_cert_path(provider_config, about_to_download=True) if leap_certs.should_redownload(client_cert_path): logger.error("The client should redownload the certificate," diff --git a/src/leap/bitmask/gui/mainwindow.py b/src/leap/bitmask/gui/mainwindow.py index 8ce7f2fc..70a3f388 100644 --- a/src/leap/bitmask/gui/mainwindow.py +++ b/src/leap/bitmask/gui/mainwindow.py @@ -703,6 +703,10 @@ class MainWindow(QtGui.QMainWindow): self._eip_status.disable_eip_start() self._eip_status.set_eip_status(self.tr("Disabled")) + # this state flag is responsible for deferring the login + # so we must update it, otherwise we're in a deadlock. + self._trying_to_start_eip = False + @QtCore.Slot() def _disable_eip_missing_helpers(self): """ -- cgit v1.2.3 From abd476bffb427bd0733dcf87d12fed00d4cf6aaf Mon Sep 17 00:00:00 2001 From: Kali Kaneko Date: Tue, 19 Aug 2014 11:27:29 -0500 Subject: remove dict comprenhension --- src/leap/bitmask/util/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/leap/bitmask') diff --git a/src/leap/bitmask/util/__init__.py b/src/leap/bitmask/util/__init__.py index caa94ec7..e8eddd64 100644 --- a/src/leap/bitmask/util/__init__.py +++ b/src/leap/bitmask/util/__init__.py @@ -151,6 +151,6 @@ def flags_to_dict(): :rtype: dict. """ items = [i for i in dir(flags) if i[0] != '_'] - values = {i: getattr(flags, i) for i in items} + values = dict((i, getattr(flags, i)) for i in items) return values -- cgit v1.2.3 From c23f2bef3b18e02069720188e1d460c1f2687180 Mon Sep 17 00:00:00 2001 From: Ivan Alejandro Date: Tue, 19 Aug 2014 17:49:34 -0300 Subject: Change settings' provider on provider change. Closes #5995. --- src/leap/bitmask/gui/mainwindow.py | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'src/leap/bitmask') diff --git a/src/leap/bitmask/gui/mainwindow.py b/src/leap/bitmask/gui/mainwindow.py index 70a3f388..60605b48 100644 --- a/src/leap/bitmask/gui/mainwindow.py +++ b/src/leap/bitmask/gui/mainwindow.py @@ -1249,9 +1249,14 @@ class MainWindow(QtGui.QMainWindow): # TODO: we need to add a check for the mail status (smtp/imap/soledad) something_runing = (self._logged_user is not None or self._already_started_eip) + provider = self._providers.get_selected_provider() + if not something_runing: if wizard: self._launch_wizard() + else: + self._settings.set_provider(provider) + self._settings.set_defaultprovider(provider) return title = self.tr("Stop services") @@ -1269,6 +1274,9 @@ class MainWindow(QtGui.QMainWindow): res = msg.exec_() if res == QtGui.QMessageBox.Yes: + self._settings.set_provider(provider) + self._settings.set_defaultprovider(provider) + self._settings.set_autostart_eip(False) self._stop_services() self._eip_conductor.qtsigs.do_disconnect_signal.emit() if wizard: -- cgit v1.2.3 From ced7ebf5407c23c3af585db4e0749c311114272f Mon Sep 17 00:00:00 2001 From: Ivan Alejandro Date: Tue, 19 Aug 2014 18:04:04 -0300 Subject: Update and get ready to start a provider on change. Closes #5996, #5997. --- src/leap/bitmask/gui/mainwindow.py | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src/leap/bitmask') diff --git a/src/leap/bitmask/gui/mainwindow.py b/src/leap/bitmask/gui/mainwindow.py index 60605b48..59896bbb 100644 --- a/src/leap/bitmask/gui/mainwindow.py +++ b/src/leap/bitmask/gui/mainwindow.py @@ -1257,6 +1257,7 @@ class MainWindow(QtGui.QMainWindow): else: self._settings.set_provider(provider) self._settings.set_defaultprovider(provider) + self._update_eip_enabled_status() return title = self.tr("Stop services") @@ -1278,6 +1279,7 @@ class MainWindow(QtGui.QMainWindow): self._settings.set_defaultprovider(provider) self._settings.set_autostart_eip(False) self._stop_services() + self._update_eip_enabled_status() self._eip_conductor.qtsigs.do_disconnect_signal.emit() if wizard: self._launch_wizard() -- cgit v1.2.3 From b6ab5790e8b2ac0d67bf0eb40e474b8bed4c42dd Mon Sep 17 00:00:00 2001 From: Ivan Alejandro Date: Wed, 20 Aug 2014 15:10:02 -0300 Subject: Select current provider on EIP preferences. - Don't disconnect logout signals, so when the wizard is triggered the UI can reflect the logout result. - Restore the selected provider when 'other...' is selected *before* the provider_changed signal is emitted to avoid that option to keep selected in case of some error. - Avoid the currentIndexChanged to be triggered when we load the providers. --- src/leap/bitmask/gui/eip_preferenceswindow.py | 3 +++ src/leap/bitmask/gui/mainwindow.py | 11 +++++------ src/leap/bitmask/gui/providers.py | 2 +- 3 files changed, 9 insertions(+), 7 deletions(-) (limited to 'src/leap/bitmask') diff --git a/src/leap/bitmask/gui/eip_preferenceswindow.py b/src/leap/bitmask/gui/eip_preferenceswindow.py index 0f63972f..b5788f3c 100644 --- a/src/leap/bitmask/gui/eip_preferenceswindow.py +++ b/src/leap/bitmask/gui/eip_preferenceswindow.py @@ -116,11 +116,14 @@ class EIPPreferencesWindow(QtGui.QDialog): self.ui.gbGatewaySelector.setEnabled(False) return + # block signals so the currentIndexChanged slot doesn't get triggered + self.ui.cbProvidersGateway.blockSignals(True) for provider, is_initialized in providers: label = provider if not is_initialized: label += self.tr(" (uninitialized)") self.ui.cbProvidersGateway.addItem(label, userData=provider) + self.ui.cbProvidersGateway.blockSignals(False) # Select provider by name domain = self._selected_domain diff --git a/src/leap/bitmask/gui/mainwindow.py b/src/leap/bitmask/gui/mainwindow.py index 59896bbb..e086d02a 100644 --- a/src/leap/bitmask/gui/mainwindow.py +++ b/src/leap/bitmask/gui/mainwindow.py @@ -413,11 +413,6 @@ class MainWindow(QtGui.QMainWindow): "Invalid username or password.")) conntrack(sig.srp_auth_bad_user_or_password, auth_bad_user_or_password) - # Logout signals - conntrack(sig.srp_logout_ok, self._logout_ok) - conntrack(sig.srp_logout_error, self._logout_error) - conntrack(sig.srp_not_logged_in_error, self._not_logged_in_error) - # EIP bootstrap signals conntrack(sig.eip_config_ready, self._eip_intermediate_stage) conntrack(sig.eip_client_certificate_ready, self._finish_eip_bootstrap) @@ -436,8 +431,12 @@ class MainWindow(QtGui.QMainWindow): sig.prov_unsupported_api.connect(self._incompatible_api) sig.prov_get_all_services.connect(self._provider_get_all_services) - # EIP start signals ============================================== + # Logout signals ================================================= + sig.srp_logout_ok.connect(self._logout_ok) + sig.srp_logout_error.connect(self._logout_error) + sig.srp_not_logged_in_error.connect(self._not_logged_in_error) + # EIP start signals ============================================== self._eip_conductor.connect_backend_signals() sig.eip_can_start.connect(self._backend_can_start_eip) sig.eip_cannot_start.connect(self._backend_cannot_start_eip) diff --git a/src/leap/bitmask/gui/providers.py b/src/leap/bitmask/gui/providers.py index b3eb8620..6954411f 100644 --- a/src/leap/bitmask/gui/providers.py +++ b/src/leap/bitmask/gui/providers.py @@ -109,6 +109,6 @@ class Providers(QtCore.QObject): """ self._providers_indexes.append(idx) is_wizard = idx == (self._combo.count() - 1) - self._provider_changed.emit(is_wizard) if is_wizard: self.restore_previous_provider() + self._provider_changed.emit(is_wizard) -- cgit v1.2.3 From ac83b8900bb8d31e6f1f1fa983435659625ff91f Mon Sep 17 00:00:00 2001 From: Ivan Alejandro Date: Fri, 22 Aug 2014 17:25:52 -0300 Subject: Hide button / improve message on missing helpers. * Emit the `eip_missing_helpers` signal when the user chooses "Don't ask me again". * We emit that signal when some helper file is missing, even if the user doesn't want a warning. * Do the update of the eip enabled status with some delay to give some time the eip machine to start and do what it needs with the buttons/labels and avoid the 'hide turn on button if missing files' being overridden. Closes #5945. --- src/leap/bitmask/gui/eip_status.py | 3 +-- src/leap/bitmask/gui/mainwindow.py | 19 ++++++++++++++++--- src/leap/bitmask/platform_init/initializers.py | 7 +++++++ 3 files changed, 24 insertions(+), 5 deletions(-) (limited to 'src/leap/bitmask') diff --git a/src/leap/bitmask/gui/eip_status.py b/src/leap/bitmask/gui/eip_status.py index abd6e2c9..1c167335 100644 --- a/src/leap/bitmask/gui/eip_status.py +++ b/src/leap/bitmask/gui/eip_status.py @@ -303,7 +303,6 @@ class EIPStatusWidget(QtGui.QWidget): """ # XXX this name is unfortunate. "disable" is also applied to a # pushbutton being grayed out. - logger.debug('Hiding EIP start button') # you might be tempted to change this for a .setEnabled(False). # it won't work. it's under the claws of the state machine. @@ -334,7 +333,7 @@ class EIPStatusWidget(QtGui.QWidget): Triggered after a successful login. Enables the start button. """ - # logger.debug('Showing EIP start button') + logger.debug('Showing EIP start button') self.eip_button.show() self.hide_eip_cancel_button() diff --git a/src/leap/bitmask/gui/mainwindow.py b/src/leap/bitmask/gui/mainwindow.py index e086d02a..6c168a19 100644 --- a/src/leap/bitmask/gui/mainwindow.py +++ b/src/leap/bitmask/gui/mainwindow.py @@ -656,6 +656,10 @@ class MainWindow(QtGui.QMainWindow): to do so, start it. Otherwise it leaves everything in place for the user to click Turn ON. """ + if self._eip_status.missing_helpers: + self._eip_status.disable_eip_start() + return + settings = self._settings default_provider = settings.get_defaultprovider() enabled_services = [] @@ -700,7 +704,9 @@ class MainWindow(QtGui.QMainWindow): self._eip_status.disable_eip_start() else: self._eip_status.disable_eip_start() - self._eip_status.set_eip_status(self.tr("Disabled")) + # NOTE: we shouldn't be setting the message here. + if not self._eip_status.missing_helpers: + self._eip_status.set_eip_status(self.tr("Disabled")) # this state flag is responsible for deferring the login # so we must update it, otherwise we're in a deadlock. @@ -812,6 +818,15 @@ class MainWindow(QtGui.QMainWindow): self._show_hide_unsupported_services() + # XXX - HACK, kind of... + # With the 1ms QTimer.singleShot call we schedule the call right after + # other signals waiting for the qt reactor to take control. + # That way, the method is called right after the EIP machines' signals. + # We need to wait until that happens because the state-machine + # controlled widget shows the 'Turn On' button and we want to do the + # changes to that button right after, not before. + QtDelayedCall(1, self._update_eip_enabled_status) + if self._wizard: possible_username = self._wizard.get_username() possible_password = self._wizard.get_password() @@ -836,8 +851,6 @@ class MainWindow(QtGui.QMainWindow): self._wizard = None self._backend_connect(only_tracked=True) else: - self._update_eip_enabled_status() - domain = self._settings.get_provider() if domain is not None: self._providers.select_provider_by_name(domain) diff --git a/src/leap/bitmask/platform_init/initializers.py b/src/leap/bitmask/platform_init/initializers.py index f56b9330..6c62734c 100644 --- a/src/leap/bitmask/platform_init/initializers.py +++ b/src/leap/bitmask/platform_init/initializers.py @@ -164,6 +164,7 @@ def check_missing(): logger.debug( "Setting alert_missing_scripts to False, we will not " "ask again") + init_signals.eip_missing_helpers.emit() config.set_alert_missing_scripts(False) if complain_missing and missing_some: @@ -171,6 +172,12 @@ def check_missing(): msg = _get_missing_complain_dialog(missing) ret = msg.exec_() + # If there is some missing file and we don't want to complain, we emit the + # 'missing helpers' signal so the eip status can show that some files are + # missing. + if missing_some and not alert_missing and not complain_missing: + init_signals.eip_missing_helpers.emit() + # # windows initializers # -- cgit v1.2.3 From ad85a375eb74609c8a1d7a7a3a0a11b7489a2483 Mon Sep 17 00:00:00 2001 From: Ivan Alejandro Date: Thu, 28 Aug 2014 10:42:53 -0300 Subject: Remove /tmp/bitmask.lock on quit. Closes #5866. Add a platform independent release_lock helper, so all the SO dependent code goes inside the locks file. Also, do some code cleanup. --- src/leap/bitmask/gui/mainwindow.py | 10 ++---- src/leap/bitmask/platform_init/locks.py | 59 ++++++++++++++++++++++----------- 2 files changed, 42 insertions(+), 27 deletions(-) (limited to 'src/leap/bitmask') diff --git a/src/leap/bitmask/gui/mainwindow.py b/src/leap/bitmask/gui/mainwindow.py index 6c168a19..653ebc35 100644 --- a/src/leap/bitmask/gui/mainwindow.py +++ b/src/leap/bitmask/gui/mainwindow.py @@ -48,6 +48,7 @@ from leap.bitmask.gui.wizard import Wizard from leap.bitmask.gui.providers import Providers from leap.bitmask.platform_init import IS_WIN, IS_MAC, IS_LINUX +from leap.bitmask.platform_init import locks from leap.bitmask.platform_init.initializers import init_platform from leap.bitmask.platform_init.initializers import init_signals @@ -63,10 +64,6 @@ from leap.bitmask.util import autostart, make_address from leap.bitmask.util.keyring_helpers import has_keyring from leap.bitmask.logs.leap_log_handler import LeapLogHandler -if IS_WIN: - from leap.bitmask.platform_init.locks import WindowsLock - from leap.bitmask.platform_init.locks import raise_window_ack - from leap.common.events import register from leap.common.events import events_pb2 as proto @@ -1865,7 +1862,7 @@ class MainWindow(QtGui.QMainWindow): Callback for the raise window event """ if IS_WIN: - raise_window_ack() + locks.raise_window_ack() self.raise_window.emit() @QtCore.Slot() @@ -2022,7 +2019,6 @@ class MainWindow(QtGui.QMainWindow): # Remove lockfiles on a clean shutdown. logger.debug('Cleaning pidfiles') - if IS_WIN: - WindowsLock.release_all_locks() + locks.release_lock() self.close() diff --git a/src/leap/bitmask/platform_init/locks.py b/src/leap/bitmask/platform_init/locks.py index 78ebf4cd..ac45a5ce 100644 --- a/src/leap/bitmask/platform_init/locks.py +++ b/src/leap/bitmask/platform_init/locks.py @@ -22,11 +22,11 @@ import errno import os import platform -from leap.bitmask import platform_init +from leap.bitmask.platform_init import IS_WIN, IS_UNIX from leap.common.events import signal as signal_event from leap.common.events import events_pb2 as proto -if platform_init.IS_UNIX: +if IS_UNIX: from fcntl import flock, LOCK_EX, LOCK_NB else: # WINDOWS import datetime @@ -40,7 +40,7 @@ else: # WINDOWS logger = logging.getLogger(__name__) -if platform_init.IS_UNIX: +if IS_UNIX: class UnixLock(object): """ @@ -48,14 +48,13 @@ if platform_init.IS_UNIX: See man 2 flock """ - def __init__(self, path): + _LOCK_FILE = '/tmp/bitmask.lock' + + def __init__(self): """ - iniializes t he UnixLock with the path of the - desired lockfile + Initialize the UnixLock. """ - self._fd = None - self.path = path def get_lock(self): """ @@ -77,7 +76,7 @@ if platform_init.IS_UNIX: :rtype: bool """ - self._fd = os.open(self.path, os.O_CREAT | os.O_RDWR) + self._fd = os.open(self._LOCK_FILE, os.O_CREAT | os.O_RDWR) try: flock(self._fd, LOCK_EX | LOCK_NB) @@ -102,6 +101,21 @@ if platform_init.IS_UNIX: gotit, pid = self._get_lock_and_pid() return pid == os.getpid() + @classmethod + def release_lock(self): + """ + Release the lock. + + :return: True if the lock was released, False otherwise + :rtype: bool + """ + try: + os.remove(self._LOCK_FILE) + return True + except Exception as e: + logger.debug("Problem removing lock, {0!r}".format(e)) + return False + def _get_lock_and_pid(self): """ Tries to get a lock over the file. @@ -109,7 +123,6 @@ if platform_init.IS_UNIX: :rtype: tuple """ - if self._get_lock(): self._write_to_pidfile() return True, None @@ -121,9 +134,7 @@ if platform_init.IS_UNIX: Tries to read pid from the pidfile, returns False if no content found. """ - - pidfile = os.read( - self._fd, 16) + pidfile = os.read(self._fd, 16) if not pidfile: return False @@ -144,7 +155,7 @@ if platform_init.IS_UNIX: os.fsync(fd) -if platform_init.IS_WIN: +if IS_WIN: # Time to wait (in secs) before assuming a raise window signal has not been # ack-ed. @@ -348,17 +359,15 @@ def we_are_the_one_and_only(): :rtype: bool """ - _sys = platform.system() - - if _sys in ("Linux", "Darwin"): - locker = UnixLock('/tmp/bitmask.lock') + if IS_UNIX: + locker = UnixLock() locker.get_lock() we_are_the_one = locker.locked_by_us if not we_are_the_one: signal_event(proto.RAISE_WINDOW) return we_are_the_one - elif _sys == "Windows": + elif IS_WIN: locker = WindowsLock() locker.get_lock() we_are_the_one = locker.locked_by_us @@ -398,6 +407,16 @@ def we_are_the_one_and_only(): else: logger.warning("Multi-instance checker " - "not implemented for %s" % (_sys)) + "not implemented for %s" % (platform.system())) # lies, lies, lies... return True + + +def release_lock(): + """ + Release the acquired lock. + """ + if IS_WIN: + WindowsLock.release_all_locks() + elif IS_UNIX: + UnixLock.release_lock() -- cgit v1.2.3 From 4f4520d938f726b344e1a71949f83aa385d92acd Mon Sep 17 00:00:00 2001 From: Ivan Alejandro Date: Mon, 1 Sep 2014 18:31:36 -0300 Subject: Don't defer action to thread here. We already are running the methods in a thread from the backend. --- src/leap/bitmask/backend/components.py | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) (limited to 'src/leap/bitmask') diff --git a/src/leap/bitmask/backend/components.py b/src/leap/bitmask/backend/components.py index 89f3c5a6..d4f6d176 100644 --- a/src/leap/bitmask/backend/components.py +++ b/src/leap/bitmask/backend/components.py @@ -452,20 +452,14 @@ class EIP(object): else: logger.debug('EIP: no errors') - def _do_stop(self, shutdown=False, restart=False): + def stop(self, shutdown=False, restart=False): """ - Stop the service. This is run in a thread to avoid blocking. + Stop the service. """ self._vpn.terminate(shutdown, restart) if IS_LINUX: self._wait_for_firewall_down() - def stop(self, shutdown=False, restart=False): - """ - Stop the service. - """ - return threads.deferToThread(self._do_stop, shutdown, restart) - def _wait_for_firewall_down(self): """ Wait for the firewall to come down. -- cgit v1.2.3 From 59ae590f4126b3e5635467e05b9985dabc80a2f9 Mon Sep 17 00:00:00 2001 From: Ivan Alejandro Date: Mon, 1 Sep 2014 18:33:10 -0300 Subject: Move reactor import to the top. --- src/leap/bitmask/services/eip/vpnprocess.py | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) (limited to 'src/leap/bitmask') diff --git a/src/leap/bitmask/services/eip/vpnprocess.py b/src/leap/bitmask/services/eip/vpnprocess.py index c7159a93..40aad257 100644 --- a/src/leap/bitmask/services/eip/vpnprocess.py +++ b/src/leap/bitmask/services/eip/vpnprocess.py @@ -50,8 +50,7 @@ from leap.common.check import leap_assert, leap_assert_type logger = logging.getLogger(__name__) vpnlog = logging.getLogger('leap.openvpn') -from twisted.internet import protocol -from twisted.internet import defer +from twisted.internet import defer, protocol, reactor from twisted.internet import error as internet_error from twisted.internet.task import LoopingCall @@ -157,10 +156,8 @@ class VPN(object): of a QObject containing the QSignals that we will pass along to the VPNManager. """ - from twisted.internet import reactor self._vpnproc = None self._pollers = [] - self._reactor = reactor self._signaler = kwargs['signaler'] self._openvpn_verb = flags.OPENVPN_VERBOSITY @@ -224,7 +221,7 @@ class VPN(object): for key, val in vpnproc.vpn_env.items(): env[key] = val - self._reactor.spawnProcess(vpnproc, cmd[0], cmd, env) + reactor.spawnProcess(vpnproc, cmd[0], cmd, env) self._vpnproc = vpnproc # add pollers for status and state @@ -300,7 +297,6 @@ class VPN(object): :param tries: counter of tries, used in recursion :type tries: int """ - from twisted.internet import reactor while tries < self.TERMINATE_MAXTRIES: if self._vpnproc.transport.pid is None: logger.debug("Process has been happily terminated.") @@ -351,7 +347,6 @@ class VPN(object): :param restart: whether this stop is part of a hard restart. :type restart: bool """ - from twisted.internet import reactor self._stop_pollers() # First we try to be polite and send a SIGTERM... @@ -424,8 +419,6 @@ class VPNManager(object): backend :type signaler: backend.Signaler """ - from twisted.internet import reactor - self._reactor = reactor self._tn = None self._signaler = signaler self._aborted = False @@ -602,7 +595,7 @@ class VPNManager(object): logger.debug('trying to connect to management') if not self.aborted and not self.is_connected(): self.connect_to_management(self._socket_host, self._socket_port) - self._reactor.callLater( + reactor.callLater( self.CONNECTION_RETRY_TIME, self.try_to_connect_to_management, retry + 1) -- cgit v1.2.3 From d65ecea2d6a943d6805284390e0361ae0ff00718 Mon Sep 17 00:00:00 2001 From: Ivan Alejandro Date: Mon, 1 Sep 2014 19:03:26 -0300 Subject: Remove unused defers. Also, we no longer return defers for those actions. --- src/leap/bitmask/gui/wizard.py | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) (limited to 'src/leap/bitmask') diff --git a/src/leap/bitmask/gui/wizard.py b/src/leap/bitmask/gui/wizard.py index 0223c053..498e8501 100644 --- a/src/leap/bitmask/gui/wizard.py +++ b/src/leap/bitmask/gui/wizard.py @@ -96,11 +96,6 @@ class Wizard(QtGui.QWizard): # this details are set when the provider download is complete. self._provider_details = None - # We will store a reference to the defers for eventual use - # (eg, to cancel them) but not doing anything with them right now. - self._provider_select_defer = None - self._provider_setup_defer = None - self._connect_and_track(self.currentIdChanged, self._current_id_changed) @@ -479,8 +474,7 @@ class Wizard(QtGui.QWizard): self.button(QtGui.QWizard.BackButton).clearFocus() self.ui.lblNameResolution.setPixmap(self.QUESTION_ICON) - self._provider_select_defer = self._backend.\ - provider_setup(provider=self._domain) + self._backend.provider_setup(provider=self._domain) @QtCore.Slot(bool) def _skip_provider_checks(self, skip): @@ -724,8 +718,7 @@ class Wizard(QtGui.QWizard): if not self._provider_setup_ok: self._reset_provider_setup() self.ui.lblDownloadCaCert.setPixmap(self.QUESTION_ICON) - self._provider_setup_defer = self._backend.\ - provider_bootstrap(provider=self._domain) + self._backend.provider_bootstrap(provider=self._domain) if pageId == self.PRESENT_PROVIDER_PAGE: details = self._provider_details -- cgit v1.2.3 From de2f0e13a5a48e86b75eb54a56fe9f88b720193f Mon Sep 17 00:00:00 2001 From: Ivan Alejandro Date: Wed, 3 Sep 2014 14:50:46 -0300 Subject: Better logging output for missing files and error. --- src/leap/bitmask/platform_init/initializers.py | 13 ++++++++----- src/leap/bitmask/services/eip/vpnprocess.py | 5 +++-- 2 files changed, 11 insertions(+), 7 deletions(-) (limited to 'src/leap/bitmask') diff --git a/src/leap/bitmask/platform_init/initializers.py b/src/leap/bitmask/platform_init/initializers.py index 6c62734c..70d787dd 100644 --- a/src/leap/bitmask/platform_init/initializers.py +++ b/src/leap/bitmask/platform_init/initializers.py @@ -127,12 +127,15 @@ def check_missing(): complain_missing = True launcher = get_vpn_launcher() - missing_scripts = launcher.missing_updown_scripts - missing_other = launcher.missing_other_files + missing_scripts = launcher.missing_updown_scripts() + missing_other = launcher.missing_other_files() - logger.debug("MISSING OTHER: %s" % (str(missing_other()))) + if missing_scripts: + logger.warning("Missing scripts: %s" % (missing_scripts)) + if missing_other: + logger.warning("Missing other files: %s" % (missing_other)) - missing_some = missing_scripts() or missing_other() + missing_some = missing_scripts or missing_other if alert_missing and missing_some: msg = get_missing_helpers_dialog() ret = msg.exec_() @@ -168,7 +171,7 @@ def check_missing(): config.set_alert_missing_scripts(False) if complain_missing and missing_some: - missing = missing_scripts() + missing_other() + missing = missing_scripts + missing_other msg = _get_missing_complain_dialog(missing) ret = msg.exec_() diff --git a/src/leap/bitmask/services/eip/vpnprocess.py b/src/leap/bitmask/services/eip/vpnprocess.py index 40aad257..d41064ec 100644 --- a/src/leap/bitmask/services/eip/vpnprocess.py +++ b/src/leap/bitmask/services/eip/vpnprocess.py @@ -214,9 +214,10 @@ class VPN(object): # and abstract us away from anything else. try: cmd = vpnproc.getCommand() - except Exception: - logger.error("Error while getting vpn command...") + except Exception as e: + logger.error("Error while getting vpn command... {0!r}".format(e)) raise + env = os.environ for key, val in vpnproc.vpn_env.items(): env[key] = val -- cgit v1.2.3 From e6fae0977015919282971dbe8a00938415c1233b Mon Sep 17 00:00:00 2001 From: Ivan Alejandro Date: Mon, 1 Sep 2014 19:03:46 -0300 Subject: Code cleanup and logging improvements. --- src/leap/bitmask/gui/mainwindow.py | 18 ++++++++---------- src/leap/bitmask/gui/wizard.py | 5 +++-- src/leap/bitmask/services/eip/vpnprocess.py | 2 ++ src/leap/bitmask/services/mail/smtpbootstrapper.py | 2 ++ 4 files changed, 15 insertions(+), 12 deletions(-) (limited to 'src/leap/bitmask') diff --git a/src/leap/bitmask/gui/mainwindow.py b/src/leap/bitmask/gui/mainwindow.py index 653ebc35..8127c1f6 100644 --- a/src/leap/bitmask/gui/mainwindow.py +++ b/src/leap/bitmask/gui/mainwindow.py @@ -206,6 +206,9 @@ class MainWindow(QtGui.QMainWindow): self._finally_quitting = False self._system_quit = False + # Used to differentiate between a real quit and a close to tray + self._close_to_tray = True + self._backend_connected_signals = [] self._backend_connect() @@ -229,9 +232,6 @@ class MainWindow(QtGui.QMainWindow): self.raise_window.connect(self._do_raise_mainwindow) - # Used to differentiate between real quits and close to tray - self._really_quit = False - self._systray = None # XXX separate actions into a different module. @@ -1144,7 +1144,7 @@ class MainWindow(QtGui.QMainWindow): return if QtGui.QSystemTrayIcon.isSystemTrayAvailable() and \ - not self._really_quit: + self._close_to_tray: self._ensure_invisible() e.ignore() return @@ -1889,10 +1889,9 @@ class MainWindow(QtGui.QMainWindow): """ Stop services and cancel ongoing actions (if any). """ - logger.debug('About to quit, doing cleanup.') + logger.debug('Stopping services...') self._cancel_ongoing_defers() - self._services_being_stopped = set(('imap', 'eip')) imap_stopped = lambda: self._remove_service('imap') @@ -1921,9 +1920,10 @@ class MainWindow(QtGui.QMainWindow): if self._quitting: return - autostart.set_autostart(False) - self._quitting = True + self._close_to_tray = False + logger.debug('Quitting...') + autostart.set_autostart(False) # first thing to do quitting, hide the mainwindow and show tooltip. self.hide() @@ -1942,8 +1942,6 @@ class MainWindow(QtGui.QMainWindow): # Set this in case that the app is hidden QtGui.QApplication.setQuitOnLastWindowClosed(True) - self._really_quit = True - if not self._backend.online: self.final_quit() return diff --git a/src/leap/bitmask/gui/wizard.py b/src/leap/bitmask/gui/wizard.py index 498e8501..8182228d 100644 --- a/src/leap/bitmask/gui/wizard.py +++ b/src/leap/bitmask/gui/wizard.py @@ -672,8 +672,9 @@ class Wizard(QtGui.QWizard): Loads the services that the provider provides into the UI for the user to enable or disable. """ - self.ui.grpServices.setTitle( - self.tr("Services by {0}").format(self._provider_details['domain'])) + title = self.tr("Services by {0}").format( + self._provider_details['domain']) + self.ui.grpServices.setTitle(title) services = get_supported(self._provider_details['services']) diff --git a/src/leap/bitmask/services/eip/vpnprocess.py b/src/leap/bitmask/services/eip/vpnprocess.py index d41064ec..8dc6021f 100644 --- a/src/leap/bitmask/services/eip/vpnprocess.py +++ b/src/leap/bitmask/services/eip/vpnprocess.py @@ -371,6 +371,8 @@ class VPN(object): logger.debug("Firewall down") else: logger.warning("Could not tear firewall down") + else: + logger.debug("VPN is not running.") def _start_pollers(self): """ diff --git a/src/leap/bitmask/services/mail/smtpbootstrapper.py b/src/leap/bitmask/services/mail/smtpbootstrapper.py index 3ef755e8..9dd61488 100644 --- a/src/leap/bitmask/services/mail/smtpbootstrapper.py +++ b/src/leap/bitmask/services/mail/smtpbootstrapper.py @@ -173,3 +173,5 @@ class SMTPBootstrapper(AbstractBootstrapper): logger.debug('Stopping SMTP service.') self._smtp_port.stopListening() self._smtp_service.doStop() + else: + logger.debug('SMTP service not running.') -- cgit v1.2.3 From dff4c53be63e5412fe4a8e69d26bf45bd949ec71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Touceda?= Date: Fri, 5 Sep 2014 15:37:07 -0300 Subject: Send the token when querying the EIP cert --- src/leap/bitmask/crypto/certs.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) (limited to 'src/leap/bitmask') diff --git a/src/leap/bitmask/crypto/certs.py b/src/leap/bitmask/crypto/certs.py index 244decfd..c3ca4efb 100644 --- a/src/leap/bitmask/crypto/certs.py +++ b/src/leap/bitmask/crypto/certs.py @@ -46,19 +46,27 @@ def download_client_cert(provider_config, path, session): # again. srp_auth = SRPAuth(provider_config) session_id = srp_auth.get_session_id() + token = srp_auth.get_token() cookies = None - if session_id: + if session_id is not None: cookies = {"_session_id": session_id} cert_uri = "%s/%s/cert" % ( provider_config.get_api_uri(), provider_config.get_api_version()) logger.debug('getting cert from uri: %s' % cert_uri) + headers = {} + + # API v2 will only support token auth, but in v1 we can send both + if token is not None: + headers["Authorization"] = 'Token token="{0}"'.format(token) + res = session.get(cert_uri, verify=provider_config .get_ca_cert_path(), cookies=cookies, - timeout=REQUEST_TIMEOUT) + timeout=REQUEST_TIMEOUT, + headers=headers) res.raise_for_status() client_cert = res.content -- cgit v1.2.3 From 3d0708ad3e20aa8dddf6894b7536be3cd59cfbca Mon Sep 17 00:00:00 2001 From: Ruben Pollan Date: Mon, 8 Sep 2014 14:22:29 -0500 Subject: Add email firewall --- src/leap/bitmask/gui/mainwindow.py | 40 ++------ src/leap/bitmask/services/eip/linuxvpnlauncher.py | 99 ++----------------- src/leap/bitmask/services/mail/conductor.py | 19 ++++ src/leap/bitmask/services/mail/emailfirewall.py | 115 ++++++++++++++++++++++ src/leap/bitmask/util/privilege_policies.py | 98 ++++++++++++++++++ 5 files changed, 247 insertions(+), 124 deletions(-) create mode 100644 src/leap/bitmask/services/mail/emailfirewall.py (limited to 'src/leap/bitmask') diff --git a/src/leap/bitmask/gui/mainwindow.py b/src/leap/bitmask/gui/mainwindow.py index 8127c1f6..9c5045ec 100644 --- a/src/leap/bitmask/gui/mainwindow.py +++ b/src/leap/bitmask/gui/mainwindow.py @@ -271,9 +271,7 @@ class MainWindow(QtGui.QMainWindow): # Services signals/slots connection self.new_updates.connect(self._react_to_new_updates) - # XXX should connect to mail_conductor.start_mail_service instead - self.soledad_ready.connect(self._start_smtp_bootstrapping) - self.soledad_ready.connect(self._start_imap_service) + self.soledad_ready.connect(self._start_mail_service) # ################################ end Qt Signals connection ######## init_platform() @@ -1563,37 +1561,12 @@ class MainWindow(QtGui.QMainWindow): self.soledad_ready.emit() ################################################################### - # Service control methods: smtp - - @QtCore.Slot() - def _start_smtp_bootstrapping(self): - """ - TRIGGERS: - self.soledad_ready - """ - if flags.OFFLINE is True: - logger.debug("not starting smtp in offline mode") - return - - if self._provides_mx_and_enabled(): - self._mail_conductor.start_smtp_service(download_if_needed=True) - - ################################################################### - # Service control methods: imap - + # Service control methods: mail @QtCore.Slot() - def _start_imap_service(self): - """ - TRIGGERS: - self.soledad_ready - """ - # TODO in the OFFLINE mode we should also modify the rules - # in the mail state machine so it shows that imap is active - # (but not smtp since it's not yet ready for offline use) + def _start_mail_service(self): if self._provides_mx_and_enabled() or flags.OFFLINE: - self._mail_conductor.start_imap_service() - - # end service control methods (imap) + self._mail_conductor.start_mail_service(download_if_needed=True, + offline=flags.OFFLINE) ################################################################### # Service control methods: eip @@ -1902,8 +1875,7 @@ class MainWindow(QtGui.QMainWindow): self._leap_signaler.eip_stopped.connect(eip_stopped) logger.debug('Stopping mail services') - self._backend.imap_stop_service() - self._backend.smtp_stop_service() + self._mail_conductor.stop_mail_services() if self._logged_user is not None: logger.debug("Doing logout") diff --git a/src/leap/bitmask/services/eip/linuxvpnlauncher.py b/src/leap/bitmask/services/eip/linuxvpnlauncher.py index b6e47f25..a3ab408b 100644 --- a/src/leap/bitmask/services/eip/linuxvpnlauncher.py +++ b/src/leap/bitmask/services/eip/linuxvpnlauncher.py @@ -20,17 +20,15 @@ Linux VPN launcher implementation. import commands import logging import os -import subprocess import sys -import time from leap.bitmask.config import flags from leap.bitmask.util.privilege_policies import LinuxPolicyChecker -from leap.common.files import which +from leap.bitmask.util.privilege_policies import NoPolkitAuthAgentAvailable +from leap.bitmask.util.privilege_policies import NoPkexecAvailable from leap.bitmask.services.eip.vpnlauncher import VPNLauncher from leap.bitmask.services.eip.vpnlauncher import VPNLauncherException from leap.bitmask.util import get_path_prefix, force_eval -from leap.common.check import leap_assert from leap.bitmask.util import first logger = logging.getLogger(__name__) @@ -46,66 +44,11 @@ class EIPNoPkexecAvailable(VPNLauncherException): pass -def _is_pkexec_in_system(): - """ - Checks the existence of the pkexec binary in system. - """ - pkexec_path = which('pkexec') - if len(pkexec_path) == 0: - return False - return True - - -def _is_auth_agent_running(): - """ - Checks if a polkit daemon is running. - - :return: True if it's running, False if it's not. - :rtype: boolean - """ - # Note that gnome-shell does not uses a separate process for the - # polkit-agent, it uses a polkit-agent within its own process so we can't - # ps-grep a polkit process, we can ps-grep gnome-shell itself. - - # the [x] thing is to avoid grep match itself - polkit_options = [ - 'ps aux | grep "polkit-[g]nome-authentication-agent-1"', - 'ps aux | grep "polkit-[k]de-authentication-agent-1"', - 'ps aux | grep "polkit-[m]ate-authentication-agent-1"', - 'ps aux | grep "[l]xpolkit"', - 'ps aux | grep "[g]nome-shell"', - 'ps aux | grep "[f]ingerprint-polkit-agent"', - ] - is_running = [commands.getoutput(cmd) for cmd in polkit_options] - - return any(is_running) - - -def _try_to_launch_agent(): - """ - Tries to launch a polkit daemon. - """ - env = None - if flags.STANDALONE: - env = {"PYTHONPATH": os.path.abspath('../../../../lib/')} - try: - # We need to quote the command because subprocess call - # will do "sh -c 'foo'", so if we do not quoute it we'll end - # up with a invocation to the python interpreter. And that - # is bad. - logger.debug("Trying to launch polkit agent") - subprocess.call(["python -m leap.bitmask.util.polkit_agent"], - shell=True, env=env) - except Exception as exc: - logger.exception(exc) - - SYSTEM_CONFIG = "/etc/leap" leapfile = lambda f: "%s/%s" % (SYSTEM_CONFIG, f) class LinuxVPNLauncher(VPNLauncher): - PKEXEC_BIN = 'pkexec' # The following classes depend on force_eval to be called against # the classes, to get the evaluation of the standalone flag on runtine. @@ -129,36 +72,6 @@ class LinuxVPNLauncher(VPNLauncher): OTHER_FILES = (POLKIT_PATH, BITMASK_ROOT, OPENVPN_BIN_PATH) - @classmethod - def maybe_pkexec(kls): - """ - Checks whether pkexec is available in the system, and - returns the path if found. - - Might raise: - EIPNoPkexecAvailable, - EIPNoPolkitAuthAgentAvailable. - - :returns: a list of the paths where pkexec is to be found - :rtype: list - """ - if _is_pkexec_in_system(): - if not _is_auth_agent_running(): - _try_to_launch_agent() - time.sleep(2) - if _is_auth_agent_running(): - pkexec_possibilities = which(kls.PKEXEC_BIN) - leap_assert(len(pkexec_possibilities) > 0, - "We couldn't find pkexec") - return pkexec_possibilities - else: - logger.warning("No polkit auth agent found. pkexec " + - "will use its own auth agent.") - raise EIPNoPolkitAuthAgentAvailable() - else: - logger.warning("System has no pkexec") - raise EIPNoPkexecAvailable() - @classmethod def get_vpn_command(kls, eipconfig, providerconfig, socket_host, socket_port="unix", openvpn_verb=1): @@ -194,7 +107,13 @@ class LinuxVPNLauncher(VPNLauncher): command.insert(1, "openvpn") command.insert(2, "start") - pkexec = kls.maybe_pkexec() + policyChecker = LinuxPolicyChecker() + try: + pkexec = policyChecker.maybe_pkexec() + except NoPolkitAuthAgentAvailable: + raise EIPNoPolkitAuthAgentAvailable() + except NoPkexecAvailable: + raise EIPNoPkexecAvailable() if pkexec: command.insert(0, first(pkexec)) diff --git a/src/leap/bitmask/services/mail/conductor.py b/src/leap/bitmask/services/mail/conductor.py index 5e85368f..416aff34 100644 --- a/src/leap/bitmask/services/mail/conductor.py +++ b/src/leap/bitmask/services/mail/conductor.py @@ -22,6 +22,7 @@ import logging from leap.bitmask.config import flags from leap.bitmask.gui import statemachines from leap.bitmask.services.mail import connection as mail_connection +from leap.bitmask.services.mail.emailfirewall import get_email_firewall from leap.common.events import events_pb2 as leap_events from leap.common.events import register as leap_register @@ -211,6 +212,11 @@ class MailConductor(IMAPControl, SMTPControl): self._mail_connection = mail_connection.MailConnection() self._userid = None + try: + self._firewall = get_email_firewall() + except NotImplementedError: + self._firewall = None + logger.info("Email firewall is not implemented in this platform") @property def userid(self): @@ -247,12 +253,25 @@ class MailConductor(IMAPControl, SMTPControl): self._smtp_machine = smtp self._smtp_machine.start() + def start_mail_service(self, download_if_needed=False, offline=False): + """ + Start the IMAP and SMTP servcies. + """ + if self._firewall is not None: + self._firewall.start() + if not offline: + logger.debug("not starting smtp in offline mode") + self.start_smtp_service(download_if_needed=download_if_needed) + self.start_imap_service() + def stop_mail_services(self): """ Stop the IMAP and SMTP services. """ self.stop_imap_service() self.stop_smtp_service() + if self._firewall is not None: + self._firewall.stop() def connect_mail_signals(self, widget): """ diff --git a/src/leap/bitmask/services/mail/emailfirewall.py b/src/leap/bitmask/services/mail/emailfirewall.py new file mode 100644 index 00000000..2cd2ec31 --- /dev/null +++ b/src/leap/bitmask/services/mail/emailfirewall.py @@ -0,0 +1,115 @@ +# -*- coding: utf-8 -*- +# emailfirewall.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 . +""" +Email firewall implementation. +""" + +import os +import subprocess + +from abc import ABCMeta, abstractmethod + +from leap.bitmask.config import flags +from leap.bitmask.platform_init import IS_LINUX +from leap.bitmask.util import first, force_eval +from leap.bitmask.util.privilege_policies import LinuxPolicyChecker +from leap.common.check import leap_assert + + +def get_email_firewall(): + """ + Return the email firewall handler for the current platform. + """ + if not (IS_LINUX): + error_msg = "Email firewall not implemented for this platform." + raise NotImplementedError(error_msg) + + firewall = None + if IS_LINUX: + firewall = LinuxEmailFirewall + + leap_assert(firewall is not None) + + return firewall() + + +class EmailFirewall(object): + """ + Abstract email firwall class + """ + __metaclass__ = ABCMeta + + @abstractmethod + def start(self): + """ + Start email firewall + """ + return False + + @abstractmethod + def stop(self): + """ + Stop email firewall + """ + return False + + +class EmailFirewallException(Exception): + pass + + +class LinuxEmailFirewall(EmailFirewall): + + class BITMASK_ROOT(object): + def __call__(self): + return ("/usr/local/sbin/bitmask-root" if flags.STANDALONE else + "/usr/sbin/bitmask-root") + + def start(self): + uid = str(os.getuid()) + return True if self._run(["start", uid]) is 0 else False + + def stop(self): + return True if self._run(["stop"]) is 0 else False + + def _run(self, cmd): + """ + Run an email firewall command with bitmask-root + + Might raise: + NoPkexecAvailable, + NoPolkitAuthAgentAvailable, + + :param cmd: command to send to bitmask-root fw-email + :type cmd: [str] + :returns: exit code of bitmask-root + :rtype: int + """ + command = [] + + policyChecker = LinuxPolicyChecker() + pkexec = policyChecker.maybe_pkexec() + if pkexec: + command.append(first(pkexec)) + + command.append(force_eval(self.BITMASK_ROOT)) + command.append("fw-email") + command += cmd + + # XXX: will be nice to use twisted ProcessProtocol instead of + # subprocess to avoid blocking until it finish + return subprocess.call(command) diff --git a/src/leap/bitmask/util/privilege_policies.py b/src/leap/bitmask/util/privilege_policies.py index f894d73b..2016e67b 100644 --- a/src/leap/bitmask/util/privilege_policies.py +++ b/src/leap/bitmask/util/privilege_policies.py @@ -18,17 +18,30 @@ Helpers to determine if the needed policies for privilege escalation are operative under this client run. """ +import commands import logging import os +import subprocess import platform +import time from abc import ABCMeta, abstractmethod from leap.bitmask.config import flags +from leap.common.check import leap_assert +from leap.common.files import which logger = logging.getLogger(__name__) +class NoPolkitAuthAgentAvailable(Exception): + pass + + +class NoPkexecAvailable(Exception): + pass + + def is_missing_policy_permissions(): """ Returns True if we do not have implemented a policy checker for this @@ -75,6 +88,7 @@ class LinuxPolicyChecker(PolicyChecker): "se.leap.bitmask.policy") LINUX_POLKIT_FILE_BUNDLE = ("/usr/share/polkit-1/actions/" "se.leap.bitmask.bundle.policy") + PKEXEC_BIN = 'pkexec' @classmethod def get_polkit_path(self): @@ -97,3 +111,87 @@ class LinuxPolicyChecker(PolicyChecker): """ path = self.get_polkit_path() return not os.path.isfile(path) + + @classmethod + def maybe_pkexec(self): + """ + Checks whether pkexec is available in the system, and + returns the path if found. + + Might raise: + NoPkexecAvailable, + NoPolkitAuthAgentAvailable. + + :returns: a list of the paths where pkexec is to be found + :rtype: list + """ + if self._is_pkexec_in_system(): + if not self.is_up(): + self.launch() + time.sleep(2) + if self.is_up(): + pkexec_possibilities = which(self.PKEXEC_BIN) + leap_assert(len(pkexec_possibilities) > 0, + "We couldn't find pkexec") + return pkexec_possibilities + else: + logger.warning("No polkit auth agent found. pkexec " + + "will use its own auth agent.") + raise NoPolkitAuthAgentAvailable() + else: + logger.warning("System has no pkexec") + raise NoPkexecAvailable() + + @classmethod + def launch(self): + """ + Tries to launch policykit + """ + env = None + if flags.STANDALONE: + env = {"PYTHONPATH": os.path.abspath('../../../../lib/')} + try: + # We need to quote the command because subprocess call + # will do "sh -c 'foo'", so if we do not quoute it we'll end + # up with a invocation to the python interpreter. And that + # is bad. + logger.debug("Trying to launch polkit agent") + subprocess.call(["python -m leap.bitmask.util.polkit_agent"], + shell=True, env=env) + except Exception as exc: + logger.exception(exc) + + @classmethod + def is_up(self): + """ + Checks if a polkit daemon is running. + + :return: True if it's running, False if it's not. + :rtype: boolean + """ + # Note that gnome-shell does not uses a separate process for the + # polkit-agent, it uses a polkit-agent within its own process so we + # can't ps-grep a polkit process, we can ps-grep gnome-shell itself. + + # the [x] thing is to avoid grep match itself + polkit_options = [ + 'ps aux | grep "polkit-[g]nome-authentication-agent-1"', + 'ps aux | grep "polkit-[k]de-authentication-agent-1"', + 'ps aux | grep "polkit-[m]ate-authentication-agent-1"', + 'ps aux | grep "[l]xpolkit"', + 'ps aux | grep "[g]nome-shell"', + 'ps aux | grep "[f]ingerprint-polkit-agent"', + ] + is_running = [commands.getoutput(cmd) for cmd in polkit_options] + + return any(is_running) + + @classmethod + def _is_pkexec_in_system(self): + """ + Checks the existence of the pkexec binary in system. + """ + pkexec_path = which('pkexec') + if len(pkexec_path) == 0: + return False + return True -- cgit v1.2.3 From f9ed736e41aa63b60925802fad021fc387dbc303 Mon Sep 17 00:00:00 2001 From: Ruben Pollan Date: Fri, 12 Sep 2014 13:30:42 -0500 Subject: Initializers needs to use PolicyChecker to get pkexec in linux --- src/leap/bitmask/platform_init/initializers.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/leap/bitmask') diff --git a/src/leap/bitmask/platform_init/initializers.py b/src/leap/bitmask/platform_init/initializers.py index 70d787dd..1d6bb1d0 100644 --- a/src/leap/bitmask/platform_init/initializers.py +++ b/src/leap/bitmask/platform_init/initializers.py @@ -29,9 +29,9 @@ from PySide import QtGui, QtCore from leap.bitmask.config import flags from leap.bitmask.config.leapsettings import LeapSettings from leap.bitmask.services.eip import get_vpn_launcher -from leap.bitmask.services.eip.linuxvpnlauncher import LinuxVPNLauncher from leap.bitmask.services.eip.darwinvpnlauncher import DarwinVPNLauncher from leap.bitmask.util import first +from leap.bitmask.util.privilege_policies import LinuxPolicyChecker logger = logging.getLogger(__name__) @@ -445,7 +445,6 @@ def _linux_install_missing_scripts(badexec, notfound): success = False installer_path = os.path.abspath( os.path.join(os.getcwd(), "apps", "eip", "files")) - launcher = LinuxVPNLauncher install_helper = "leap-install-helper.sh" install_helper_path = os.path.join(installer_path, install_helper) @@ -456,7 +455,8 @@ def _linux_install_missing_scripts(badexec, notfound): if os.path.isdir(installer_path): try: - pkexec = first(launcher.maybe_pkexec()) + policyChecker = LinuxPolicyChecker() + pkexec = first(policyChecker.maybe_pkexec()) cmdline = ["%s %s %s" % ( pkexec, install_helper_path, install_opts)] -- cgit v1.2.3 From 719c472f23490dfe039327743f4553f600c799a9 Mon Sep 17 00:00:00 2001 From: Ivan Alejandro Date: Mon, 15 Sep 2014 17:52:57 -0300 Subject: Improve support for RTL languages. * Update transifex resource name. * Use RightToLeft layout for Arabic language. * Use better names and comments on i18n section. * Use unicode to initialize widgets with text that otherwise (str) will fail when Arabic (and most likely any other language with non-ascii characters) is used. Closes #5289, #6033. --- src/leap/bitmask/frontend_app.py | 27 ++++++++++++++++++--------- src/leap/bitmask/gui/eip_status.py | 2 +- src/leap/bitmask/gui/mainwindow.py | 2 +- 3 files changed, 20 insertions(+), 11 deletions(-) (limited to 'src/leap/bitmask') diff --git a/src/leap/bitmask/frontend_app.py b/src/leap/bitmask/frontend_app.py index 5ea89fc9..909005f0 100644 --- a/src/leap/bitmask/frontend_app.py +++ b/src/leap/bitmask/frontend_app.py @@ -76,15 +76,24 @@ def run_frontend(options, flags_dict, backend_pid): qApp = QtGui.QApplication(sys.argv) - # To test: - # $ LANG=es ./app.py - locale = QtCore.QLocale.system().name() - qtTranslator = QtCore.QTranslator() - if qtTranslator.load("qt_%s" % locale, ":/translations"): - qApp.installTranslator(qtTranslator) - appTranslator = QtCore.QTranslator() - if appTranslator.load("%s.qm" % locale[:2], ":/translations"): - qApp.installTranslator(appTranslator) + # To test the app in other language you can do: + # shell> LANG=es bitmask + # or in some rare case if the code above didn't work: + # shell> LC_ALL=es LANG=es bitmask + locale = QtCore.QLocale.system().name() # en_US, es_AR, ar_SA, etc + locale_short = locale[:2] # en, es, ar, etc + rtl_languages = ('ar', ) # right now tested on 'arabic' only. + + systemQtTranslator = QtCore.QTranslator() + if systemQtTranslator.load("qt_%s" % locale, ":/translations"): + qApp.installTranslator(systemQtTranslator) + + bitmaskQtTranslator = QtCore.QTranslator() + if bitmaskQtTranslator.load("%s.qm" % locale_short, ":/translations"): + qApp.installTranslator(bitmaskQtTranslator) + + if locale_short in rtl_languages: + qApp.setLayoutDirection(QtCore.Qt.LayoutDirection.RightToLeft) # Needed for initializing qsettings it will write # .config/leap/leap.conf top level app settings in a platform diff --git a/src/leap/bitmask/gui/eip_status.py b/src/leap/bitmask/gui/eip_status.py index 1c167335..a5cd03d3 100644 --- a/src/leap/bitmask/gui/eip_status.py +++ b/src/leap/bitmask/gui/eip_status.py @@ -97,7 +97,7 @@ class EIPStatusWidget(QtGui.QWidget): # Action for the systray self._eip_disabled_action = QtGui.QAction( - "{0} is {1}".format(self._service_name, self.tr("disabled")), self) + u"{0} is {1}".format(self._service_name, self.tr("disabled")), self) def connect_backend_signals(self): """ diff --git a/src/leap/bitmask/gui/mainwindow.py b/src/leap/bitmask/gui/mainwindow.py index 9c5045ec..4e11c979 100644 --- a/src/leap/bitmask/gui/mainwindow.py +++ b/src/leap/bitmask/gui/mainwindow.py @@ -934,7 +934,7 @@ class MainWindow(QtGui.QMainWindow): systrayMenu.addAction(self._action_visible) systrayMenu.addSeparator() - eip_status_label = "{0}: {1}".format( + eip_status_label = u"{0}: {1}".format( self._eip_conductor.eip_name, self.tr("OFF")) self._eip_menu = eip_menu = systrayMenu.addMenu(eip_status_label) eip_menu.addAction(self._action_eip_startstop) -- cgit v1.2.3 From 0feb84c61b1f8efe9196af915e7500e97fa6b314 Mon Sep 17 00:00:00 2001 From: Ivan Alejandro Date: Tue, 16 Sep 2014 18:17:59 -0300 Subject: Hide AKM menu and disable the qt connection. Hide the Advaneced Key Management menu from the ui file and comment out the connection between the triggered action and the method that shows the AKM window. Closes #6087. --- src/leap/bitmask/gui/mainwindow.py | 5 +++-- src/leap/bitmask/gui/ui/mainwindow.ui | 9 ++++++--- 2 files changed, 9 insertions(+), 5 deletions(-) (limited to 'src/leap/bitmask') diff --git a/src/leap/bitmask/gui/mainwindow.py b/src/leap/bitmask/gui/mainwindow.py index 4e11c979..916e7c1f 100644 --- a/src/leap/bitmask/gui/mainwindow.py +++ b/src/leap/bitmask/gui/mainwindow.py @@ -224,8 +224,9 @@ class MainWindow(QtGui.QMainWindow): self.ui.action_create_new_account.triggered.connect( self._on_provider_changed) - self.ui.action_advanced_key_management.triggered.connect( - self._show_AKM) + # Action item hidden since we don't provide stable mail yet. + # self.ui.action_advanced_key_management.triggered.connect( + # self._show_AKM) if IS_MAC: self.ui.menuFile.menuAction().setText(self.tr("File")) diff --git a/src/leap/bitmask/gui/ui/mainwindow.ui b/src/leap/bitmask/gui/ui/mainwindow.ui index 92c13d15..f7570ee6 100644 --- a/src/leap/bitmask/gui/ui/mainwindow.ui +++ b/src/leap/bitmask/gui/ui/mainwindow.ui @@ -75,7 +75,7 @@ 0 0 524 - 540 + 549 @@ -306,7 +306,7 @@ 0 0 524 - 25 + 21 @@ -378,11 +378,14 @@ - true + false Advanced Key Management + + false + -- cgit v1.2.3 From c7304e54e40cd9151e6d00a8441aaf48b68c9bcc Mon Sep 17 00:00:00 2001 From: elijah Date: Wed, 3 Sep 2014 15:50:08 -0700 Subject: single pref win: move eip preferences to new combined preference window. --- src/leap/bitmask/gui/mainwindow.py | 38 +- src/leap/bitmask/gui/preferences_account_page.py | 165 ++++++++ src/leap/bitmask/gui/preferences_email_page.py | 38 ++ src/leap/bitmask/gui/preferences_vpn_page.py | 150 ++++++++ src/leap/bitmask/gui/preferenceswindow.py | 419 +++------------------ src/leap/bitmask/gui/ui/eippreferences.ui | 102 ----- src/leap/bitmask/gui/ui/mainwindow.ui | 8 +- src/leap/bitmask/gui/ui/preferences.ui | 234 +++++------- .../bitmask/gui/ui/preferences_account_page.ui | 95 +++++ src/leap/bitmask/gui/ui/preferences_email_page.ui | 32 ++ src/leap/bitmask/gui/ui/preferences_vpn_page.ui | 89 +++++ src/leap/bitmask/services/eip/eipconfig.py | 43 ++- 12 files changed, 746 insertions(+), 667 deletions(-) create mode 100644 src/leap/bitmask/gui/preferences_account_page.py create mode 100644 src/leap/bitmask/gui/preferences_email_page.py create mode 100644 src/leap/bitmask/gui/preferences_vpn_page.py delete mode 100644 src/leap/bitmask/gui/ui/eippreferences.ui create mode 100644 src/leap/bitmask/gui/ui/preferences_account_page.ui create mode 100644 src/leap/bitmask/gui/ui/preferences_email_page.ui create mode 100644 src/leap/bitmask/gui/ui/preferences_vpn_page.ui (limited to 'src/leap/bitmask') diff --git a/src/leap/bitmask/gui/mainwindow.py b/src/leap/bitmask/gui/mainwindow.py index 916e7c1f..243fe117 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 @@ -97,6 +96,9 @@ class MainWindow(QtGui.QMainWindow): # We give the services some time to a halt before forcing quit. SERVICES_STOP_TIMEOUT = 3000 # in milliseconds + # Preferences window + preferences = None + def __init__(self, start_hidden=False, backend_pid=None): """ Constructor for the client main window @@ -213,8 +215,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) @@ -247,7 +247,6 @@ class MainWindow(QtGui.QMainWindow): # 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 @@ -601,16 +600,13 @@ class MainWindow(QtGui.QMainWindow): """ 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) - - self.soledad_ready.connect(preferences.set_soledad_ready) - preferences.show() - preferences.preferences_saved.connect(self._update_eip_enabled_status) + if self.preferences is not None: + self.preferences.close() + self.preferences = PreferencesWindow(self, user, domain, + self._backend, + self._soledad_started, + self._leap_signaler) + self.preferences.show() @QtCore.Slot() def _update_eip_enabled_status(self): @@ -718,20 +714,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/preferences_account_page.py b/src/leap/bitmask/gui/preferences_account_page.py new file mode 100644 index 00000000..9cc94482 --- /dev/null +++ b/src/leap/bitmask/gui/preferences_account_page.py @@ -0,0 +1,165 @@ +# -*- 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 . +""" +Widget for "account" preferences +""" +import logging + +from functools import partial + +from PySide import QtCore, QtGui +from ui_preferences_account_page import Ui_PreferencesAccountPage + +logger = logging.getLogger(__name__) + +class PreferencesAccountPage(QtGui.QWidget): + """ + + """ + + def __init__(self, parent): + """ + """ + QtGui.QWidget.__init__(self, parent) + self.ui = Ui_PreferencesAccountPage() + self.ui.setupUi(self) + self.show() + + self._selected_services = set() + #self._leap_signaler.prov_get_supported_services.connect(self._load_services) + + @QtCore.Slot() + def set_soledad_ready(self): + """ + TRIGGERS: + parent.soledad_ready + + It notifies when the soledad object as ready to use. + """ + #self.ui.lblPasswordChangeStatus.setVisible(False) + #self.ui.gbPasswordChange.setEnabled(True) + + @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: + 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() 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..08ff5463 --- /dev/null +++ b/src/leap/bitmask/gui/preferences_email_page.py @@ -0,0 +1,38 @@ +# -*- 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 . +""" +Widget for "email" preferences +""" +import logging + +from PySide import QtCore, QtGui +from ui_preferences_email_page import Ui_PreferencesEmailPage + +logger = logging.getLogger(__name__) + +class PreferencesEmailPage(QtGui.QWidget): + """ + + """ + + def __init__(self, parent): + """ + """ + QtGui.QWidget.__init__(self, parent) + self.ui = Ui_PreferencesEmailPage() + self.ui.setupUi(self) + self.show() + 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..ba1366e4 --- /dev/null +++ b/src/leap/bitmask/gui/preferences_vpn_page.py @@ -0,0 +1,150 @@ +# -*- 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 . +""" +Widget for "vpn" preferences +""" + +from PySide import QtCore, QtGui +from ui_preferences_vpn_page import Ui_PreferencesVpnPage + +from leap.bitmask.config.leapsettings import LeapSettings + + +class PreferencesVpnPage(QtGui.QWidget): + """ + Page in the preferences window that shows VPN settings + """ + + def __init__(self, parent, domain, backend, leap_signaler): + """ + :param parent: parent object of the EIPPreferencesWindow. + :type parent: QWidget + + :param domain: the selected by default domain. + :type domain: unicode + + :param backend: Backend being used + :type backend: Backend + """ + QtGui.QWidget.__init__(self, parent) + self.AUTOMATIC_GATEWAY_LABEL = self.tr("Automatic") + + self._settings = LeapSettings() + self._leap_signaler = leap_signaler + self._backend = backend + + # Load UI + self.ui = Ui_PreferencesVpnPage() + self.ui.setupUi(self) + self.ui.flash_label.setVisible(False) + + # Connections + self.ui.gateways_list.clicked.connect(self._save_selected_gateway) + + self._domain = domain + self._backend_connect() + self._backend.eip_get_gateways_list(domain=domain) + + def _flash_error(self, message): + """ + Sets string for the flash message. + + :param message: the text to be displayed + :type message: str + """ + message = "%s" % (message,) + self.ui.flash_label.setVisible(True) + self.ui.flash_label.setText(message) + + # def _flash_success(self, message): + # """ + # Sets string for the flash message. + # + # :param message: the text to be displayed + # :type message: str + # """ + # message = "%s" % (message,) + # self.ui.flash_label.setVisible(True) + # self.ui.flash_label.setText(message) + + @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._settings.GATEWAY_AUTOMATIC + else: + gateway = item.data(QtCore.Qt.UserRole) + self._settings.set_selected_gateway(self._domain, gateway) + self._backend.settings_set_selected_gateway(provider=self._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._settings.get_selected_gateway( + self._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) + + def _backend_connect(self): + sig = self._leap_signaler + sig.eip_get_gateways_list.connect(self._update_gateways_list) + sig.eip_get_gateways_list_error.connect(self._gateways_list_error) diff --git a/src/leap/bitmask/gui/preferenceswindow.py b/src/leap/bitmask/gui/preferenceswindow.py index 3c9cd5d0..a9c301c4 100644 --- a/src/leap/bitmask/gui/preferenceswindow.py +++ b/src/leap/bitmask/gui/preferenceswindow.py @@ -20,13 +20,16 @@ Preferences window """ import logging -from functools import partial - from PySide import QtCore, QtGui from leap.bitmask.config.leapsettings import LeapSettings + from leap.bitmask.gui.ui_preferences import Ui_Preferences -from leap.bitmask.util.credentials import password_checks + +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 + from leap.bitmask.services import get_service_display_name, MX_SERVICE logger = logging.getLogger(__name__) @@ -38,8 +41,7 @@ class PreferencesWindow(QtGui.QDialog): """ preferences_saved = QtCore.Signal() - def __init__(self, parent, username, domain, backend, soledad_started, mx, - leap_signaler): + def __init__(self, parent, username, domain, backend, soledad_started, leap_signaler): """ :param parent: parent object of the PreferencesWindow. :parent type: QWidget @@ -51,397 +53,94 @@ class PreferencesWindow(QtGui.QDialog): :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 leap_signaler: signal server + :type leap_signaler: LeapSignaler """ QtGui.QDialog.__init__(self, parent) - self.AUTOMATIC_GATEWAY_LABEL = self.tr("Automatic") + self._parent = parent 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() # 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.ui.close_button.clicked.connect(self.close) - self._select_provider_by_name(domain) + self._add_icons() + self._add_pages() - def _not_logged_in(self): + def _add_icons(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) + Adds all the icons for the different configuration categories. + Icons are QListWidgetItems added to the nav_widget on the side + of the preferences window. - def _provides_mx(self): - """ - Actions to perform if the provider provides MX. + A note on sizing of QListWidgetItems + icon_width = list_widget.width - (2 x nav_widget.spacing) - 2 + icon_height = 56 seems to look ok """ - pw_enabled = True - enabled_services = self._settings.get_enabled_services(self._domain) - mx_name = get_service_display_name(MX_SERVICE) + account_button = QtGui.QListWidgetItem(self.ui.nav_widget) + account_button.setIcon(QtGui.QIcon(":/images/black/32/user.png")) + account_button.setText(self.tr("Account")) + account_button.setTextAlignment(QtCore.Qt.AlignHCenter | QtCore.Qt.AlignVCenter) + account_button.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) + account_button.setSizeHint(QtCore.QSize(98,56)) - 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 + vpn_button = QtGui.QListWidgetItem(self.ui.nav_widget) + vpn_button.setIcon(QtGui.QIcon(":/images/black/32/earth.png")) + vpn_button.setText(self.tr("VPN")) + vpn_button.setTextAlignment(QtCore.Qt.AlignHCenter | QtCore.Qt.AlignVCenter) + vpn_button.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) + vpn_button.setSizeHint(QtCore.QSize(98,56)) - self.ui.gbPasswordChange.setEnabled(pw_enabled) + email_button = QtGui.QListWidgetItem(self.ui.nav_widget) + email_button.setIcon(QtGui.QIcon(":/images/black/32/email.png")) + email_button.setText(self.tr("Email")) + email_button.setTextAlignment(QtCore.Qt.AlignHCenter | QtCore.Qt.AlignVCenter) + email_button.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) + email_button.setSizeHint(QtCore.QSize(98,56)) - @QtCore.Slot() - def set_soledad_ready(self): - """ - TRIGGERS: - parent.soledad_ready + self.ui.nav_widget.currentItemChanged.connect(self._change_page) + self.ui.nav_widget.setCurrentRow(0) - It notifies when the soledad object as ready to use. + def _add_pages(self): """ - self.ui.lblPasswordChangeStatus.setVisible(False) - self.ui.gbPasswordChange.setEnabled(True) - - def _set_password_change_status(self, status, error=False, success=False): + Adds the pages for the different configuration categories. """ - Sets the status label for the password change. + self.ui.pages_widget.addWidget(PreferencesAccountPage(self)) + self.ui.pages_widget.addWidget(PreferencesVpnPage(self, self._domain, self._backend, self._leap_signaler)) + self.ui.pages_widget.addWidget(PreferencesEmailPage(self)) - :param status: status message to display, can be HTML - :type status: str - """ - if error: - status = "%s" % (status,) - elif success: - status = "%s" % (status,) - - if not self.ui.gbPasswordChange.isEnabled(): - status = "%s" % (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) + # + # Slots + # @QtCore.Slot() - def _change_password(self): + def close(self): """ TRIGGERS: - self.ui.pbChangePassword.clicked + self.ui.close_button.clicked - Changes the user's password if the inputboxes are correctly filled. + Close this dialog """ - 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) + self._parent.preferences = None + self.hide() @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() - - @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 - - 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 password") - self._set_password_change_status(msg, error=True) - self._set_changing_password(False) - - @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() - - def _change_password_success(self): - """ - 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 = "%s" % (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): + def _change_page(self, current, previous): """ TRIGGERS: - service_checkbox.stateChanged + self.ui.nav_widget.currentItemChanged - 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): + Changes what page is displayed. """ - 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: - 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) + if not current: + current = previous + self.ui.pages_widget.setCurrentIndex(self.ui.nav_widget.row(current)) 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 @@ - - - EIPPreferences - - - - 0 - 0 - 435 - 144 - - - - Encrypted Internet Preferences - - - - :/images/mask-icon.png:/images/mask-icon.png - - - - - - true - - - Select gateway for provider - - - false - - - - - - Select &provider: - - - cbProvidersGateway - - - - - - - - <Select provider> - - - - - - - - &Save this provider settings - - - - - - - < Providers Gateway Status > - - - Qt::AlignCenter - - - - - - - Select &gateway: - - - cbGateways - - - - - - - - Automatic - - - - - - - - - - - cbProvidersGateway - cbGateways - pbSaveGateway - - - - - - diff --git a/src/leap/bitmask/gui/ui/mainwindow.ui b/src/leap/bitmask/gui/ui/mainwindow.ui index f7570ee6..2e8aea8c 100644 --- a/src/leap/bitmask/gui/ui/mainwindow.ui +++ b/src/leap/bitmask/gui/ui/mainwindow.ui @@ -317,7 +317,6 @@ - @@ -338,12 +337,7 @@ true - Account Preferences... - - - - - Internet Preferences... + Preferences... 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 @@ 0 0 - 503 - 401 + 520 + 439 @@ -17,159 +17,97 @@ :/images/mask-icon.png:/images/mask-icon.png - - - - - Qt::Vertical + + + 6 + + + + + + 75 + true + - - - 20 - 40 - + + user@example.org - + - - - - false - - - Password Change + + + + Qt::Horizontal - - - QFormLayout::ExpandingFieldsGrow - - - - - &Current password: - - - leCurrentPassword - - - - - - - QLineEdit::Password - - - - - - - &New password: - - - leNewPassword - - - - - - - QLineEdit::Password - - - - - - - &Re-enter new password: - - - leNewPassword2 - - - - - - - QLineEdit::Password - - - - - - - Change - - - - - - - <Password change status> - - - Qt::AlignCenter - - - - - - - - Enabled services + + + + 12 - - - - - Save this provider settings - - - - - - - Services - - - false - - - - - - - - - - - - - <Select provider> - - - - - - - - Select provider: - - - - - - - < Providers Services Status > - - - Qt::AlignCenter - - - - - + + + + + 0 + 0 + + + + + 120 + 16777215 + + + + + 32 + 32 + + + + QListView::Static + + + 10 + + + QListView::IconMode + + + true + + + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Close + + + + 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..4e58b2ab --- /dev/null +++ b/src/leap/bitmask/gui/ui/preferences_account_page.ui @@ -0,0 +1,95 @@ + + + PreferencesAccountPage + + + + 0 + 0 + 462 + 371 + + + + Form + + + + + + + 0 + 0 + + + + Change Password + + + + + + + <change password status> + + + + + + + Qt::Vertical + + + QSizePolicy::Preferred + + + + 20 + 15 + + + + + + + + Services + + + false + + + + + + + + + < Providers Services Status > + + + Qt::AlignCenter + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + 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 @@ + + + PreferencesEmailPage + + + + 0 + 0 + 400 + 300 + + + + Form + + + + + 250 + 210 + 98 + 27 + + + + PushButton + + + + + + 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..85a0dc60 --- /dev/null +++ b/src/leap/bitmask/gui/ui/preferences_vpn_page.ui @@ -0,0 +1,89 @@ + + + PreferencesVpnPage + + + + 0 + 0 + 400 + 362 + + + + Form + + + + + + <flash_label> + + + Qt::AlignCenter + + + + + + + Default VPN Gateway: + + + gateways_list + + + + + + + + New Item + + + + :/images/countries/us.png + + + + + + New Item + + + + :/images/countries/br.png + + + + + + + + + + 0 + 0 + + + + You must reconnect for changes to take effect. + + + false + + + true + + + gateways_list + + + + + + + + + + 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): -- cgit v1.2.3 From 4e7c4b48b4255ceac06900fa9e65824c52e15ba7 Mon Sep 17 00:00:00 2001 From: elijah Date: Thu, 4 Sep 2014 17:09:29 -0700 Subject: single pref win: enabled/disable services via preferences window, account page. --- src/leap/bitmask/gui/account.py | 43 +++++++++ src/leap/bitmask/gui/app.py | 68 ++++++++++++++ src/leap/bitmask/gui/mainwindow.py | 87 +++++++---------- src/leap/bitmask/gui/preferences_account_page.py | 103 ++++++--------------- src/leap/bitmask/gui/preferences_email_page.py | 7 +- src/leap/bitmask/gui/preferences_vpn_page.py | 54 ++++++----- src/leap/bitmask/gui/preferenceswindow.py | 90 ++++++++++-------- .../bitmask/gui/ui/preferences_account_page.ui | 67 ++++++++------ src/leap/bitmask/services/eip/conductor.py | 6 ++ 9 files changed, 304 insertions(+), 221 deletions(-) create mode 100644 src/leap/bitmask/gui/account.py create mode 100644 src/leap/bitmask/gui/app.py (limited to 'src/leap/bitmask') diff --git a/src/leap/bitmask/gui/account.py b/src/leap/bitmask/gui/account.py new file mode 100644 index 00000000..b08053a9 --- /dev/null +++ b/src/leap/bitmask/gui/account.py @@ -0,0 +1,43 @@ +# -*- 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 . +""" +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 + +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) + + diff --git a/src/leap/bitmask/gui/app.py b/src/leap/bitmask/gui/app.py new file mode 100644 index 00000000..7fcf69af --- /dev/null +++ b/src/leap/bitmask/gui/app.py @@ -0,0 +1,68 @@ +# -*- 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 . +""" +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): + + #preferences_saved = QtCore.Signal() + + # 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/mainwindow.py b/src/leap/bitmask/gui/mainwindow.py index 243fe117..b106364d 100644 --- a/src/leap/bitmask/gui/mainwindow.py +++ b/src/leap/bitmask/gui/mainwindow.py @@ -45,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 @@ -126,19 +128,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) @@ -154,6 +147,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) @@ -245,9 +239,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._enabled_services = [] self._ui_mx_visible = True self._ui_eip_visible = True @@ -342,23 +333,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. @@ -598,21 +572,18 @@ class MainWindow(QtGui.QMainWindow): Display the preferences window. """ - user = self._logged_user - domain = self._providers.get_selected_provider() + account = Account(self._logged_user, + self._providers.get_selected_provider()) if self.preferences is not None: self.preferences.close() - self.preferences = PreferencesWindow(self, user, domain, - self._backend, - self._soledad_started, - self._leap_signaler) + self.preferences = PreferencesWindow(self, account, self.app) self.preferences.show() - @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. @@ -620,24 +591,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): """ @@ -657,7 +639,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 diff --git a/src/leap/bitmask/gui/preferences_account_page.py b/src/leap/bitmask/gui/preferences_account_page.py index 9cc94482..bb90aab5 100644 --- a/src/leap/bitmask/gui/preferences_account_page.py +++ b/src/leap/bitmask/gui/preferences_account_page.py @@ -22,35 +22,29 @@ from functools import partial from PySide import QtCore, QtGui from ui_preferences_account_page import Ui_PreferencesAccountPage +from leap.bitmask.services import get_service_display_name +from leap.bitmask.config.leapsettings import LeapSettings logger = logging.getLogger(__name__) class PreferencesAccountPage(QtGui.QWidget): - """ - """ - - def __init__(self, parent): + def __init__(self, parent, account, app): """ """ QtGui.QWidget.__init__(self, parent) self.ui = Ui_PreferencesAccountPage() self.ui.setupUi(self) - self.show() - self._selected_services = set() - #self._leap_signaler.prov_get_supported_services.connect(self._load_services) + self.account = account + self.app = app - @QtCore.Slot() - def set_soledad_ready(self): - """ - TRIGGERS: - parent.soledad_ready + self._selected_services = set() + self.ui.change_password_label.setVisible(False) + self.ui.provider_services_label.setVisible(False) - It notifies when the soledad object as ready to use. - """ - #self.ui.lblPasswordChangeStatus.setVisible(False) - #self.ui.gbPasswordChange.setEnabled(True) + app.signaler.prov_get_supported_services.connect(self._load_services) + app.backend.provider_get_supported_services(domain=account.domain) @QtCore.Slot(str, int) def _service_selection_changed(self, service, state): @@ -72,94 +66,49 @@ class PreferencesAccountPage(QtGui.QWidget): 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.lblProvidersServicesStatus.setVisible(False) + self.ui.provider_services_label.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) + # write to config + self.app.settings.set_enabled_services(self.account.domain, services) - if not domain: - return + # emit signal alerting change + self.app.service_selection_changed.emit(self.account, services) - # 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] + prov_get_supported_services 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 + :param services: list of supported service names + :type services: list of str """ - domain = self.ui.cbProvidersServices.currentText() - services_conf = self._settings.get_enabled_services(domain) + services_conf = self.account.services() - # 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 + 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 configured value + # add one checkbox per service and set the current value + # from what is saved in settings. 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 = 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(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() diff --git a/src/leap/bitmask/gui/preferences_email_page.py b/src/leap/bitmask/gui/preferences_email_page.py index 08ff5463..da902802 100644 --- a/src/leap/bitmask/gui/preferences_email_page.py +++ b/src/leap/bitmask/gui/preferences_email_page.py @@ -28,11 +28,14 @@ class PreferencesEmailPage(QtGui.QWidget): """ - def __init__(self, parent): + def __init__(self, parent, account, app): """ """ QtGui.QWidget.__init__(self, parent) self.ui = Ui_PreferencesEmailPage() self.ui.setupUi(self) - self.show() + + self.parent = parent + 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 index ba1366e4..a8f074d2 100644 --- a/src/leap/bitmask/gui/preferences_vpn_page.py +++ b/src/leap/bitmask/gui/preferences_vpn_page.py @@ -28,23 +28,22 @@ class PreferencesVpnPage(QtGui.QWidget): Page in the preferences window that shows VPN settings """ - def __init__(self, parent, domain, backend, leap_signaler): + def __init__(self, parent, account, app): """ :param parent: parent object of the EIPPreferencesWindow. :type parent: QWidget - :param domain: the selected by default domain. - :type domain: unicode + :param account: the currently active account + :type account: Account - :param backend: Backend being used - :type backend: Backend + :param app: shared App instance + :type app: App """ QtGui.QWidget.__init__(self, parent) self.AUTOMATIC_GATEWAY_LABEL = self.tr("Automatic") - self._settings = LeapSettings() - self._leap_signaler = leap_signaler - self._backend = backend + self.account = account + self.app = app # Load UI self.ui = Ui_PreferencesVpnPage() @@ -53,10 +52,14 @@ class PreferencesVpnPage(QtGui.QWidget): # 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) - self._domain = domain - self._backend_connect() - self._backend.eip_get_gateways_list(domain=domain) + # Trigger update + self.app.backend.eip_get_gateways_list(domain=self.account.domain) def _flash_error(self, message): """ @@ -94,12 +97,13 @@ class PreferencesVpnPage(QtGui.QWidget): item = self.ui.gateways_list.currentItem() if item.text() == self.AUTOMATIC_GATEWAY_LABEL: - gateway = self._settings.GATEWAY_AUTOMATIC + gateway = self.app.settings.GATEWAY_AUTOMATIC else: gateway = item.data(QtCore.Qt.UserRole) - self._settings.set_selected_gateway(self._domain, gateway) - self._backend.settings_set_selected_gateway(provider=self._domain, - gateway=gateway) + 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): @@ -116,8 +120,8 @@ class PreferencesVpnPage(QtGui.QWidget): self.ui.gateways_list.clear() self.ui.gateways_list.addItem(self.AUTOMATIC_GATEWAY_LABEL) - selected_gateway = self._settings.get_selected_gateway( - self._domain) + selected_gateway = self.app.settings.get_selected_gateway( + self.account.domain) index = 0 for idx, (gw_name, gw_ip, gw_country) in enumerate(gateways): @@ -144,7 +148,15 @@ class PreferencesVpnPage(QtGui.QWidget): self.tr("Error loading configuration file.")) self.ui.gateways_list.setEnabled(False) - def _backend_connect(self): - sig = self._leap_signaler - sig.eip_get_gateways_list.connect(self._update_gateways_list) - sig.eip_get_gateways_list_error.connect(self._gateways_list_error) + @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 a9c301c4..35a875fa 100644 --- a/src/leap/bitmask/gui/preferenceswindow.py +++ b/src/leap/bitmask/gui/preferenceswindow.py @@ -22,16 +22,13 @@ import logging 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.gui.preferences_account_page import PreferencesAccountPage from leap.bitmask.gui.preferences_vpn_page import PreferencesVpnPage from leap.bitmask.gui.preferences_email_page import PreferencesEmailPage -from leap.bitmask.services import get_service_display_name, MX_SERVICE - logger = logging.getLogger(__name__) @@ -39,9 +36,8 @@ class PreferencesWindow(QtGui.QDialog): """ Window that displays the preferences. """ - preferences_saved = QtCore.Signal() - def __init__(self, parent, username, domain, backend, soledad_started, leap_signaler): + def __init__(self, parent, account, app): """ :param parent: parent object of the PreferencesWindow. :parent type: QWidget @@ -51,30 +47,26 @@ class PreferencesWindow(QtGui.QDialog): :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 leap_signaler: signal server :type leap_signaler: LeapSignaler """ QtGui.QDialog.__init__(self, parent) self._parent = parent - self._username = username - self._domain = domain - self._leap_signaler = leap_signaler - self._backend = backend - self._soledad_started = soledad_started - - self._settings = LeapSettings() + self.account = account + self.app = app - # Load UI 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._add_icons() self._add_pages() + self._update_icons(self.account, self.account.services()) def _add_icons(self): """ @@ -86,26 +78,31 @@ class PreferencesWindow(QtGui.QDialog): icon_width = list_widget.width - (2 x nav_widget.spacing) - 2 icon_height = 56 seems to look ok """ - account_button = QtGui.QListWidgetItem(self.ui.nav_widget) - account_button.setIcon(QtGui.QIcon(":/images/black/32/user.png")) - account_button.setText(self.tr("Account")) - account_button.setTextAlignment(QtCore.Qt.AlignHCenter | QtCore.Qt.AlignVCenter) - account_button.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) - account_button.setSizeHint(QtCore.QSize(98,56)) - - vpn_button = QtGui.QListWidgetItem(self.ui.nav_widget) - vpn_button.setIcon(QtGui.QIcon(":/images/black/32/earth.png")) - vpn_button.setText(self.tr("VPN")) - vpn_button.setTextAlignment(QtCore.Qt.AlignHCenter | QtCore.Qt.AlignVCenter) - vpn_button.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) - vpn_button.setSizeHint(QtCore.QSize(98,56)) - - email_button = QtGui.QListWidgetItem(self.ui.nav_widget) - email_button.setIcon(QtGui.QIcon(":/images/black/32/email.png")) - email_button.setText(self.tr("Email")) - email_button.setTextAlignment(QtCore.Qt.AlignHCenter | QtCore.Qt.AlignVCenter) - email_button.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) - email_button.setSizeHint(QtCore.QSize(98,56)) + 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) @@ -114,9 +111,9 @@ class PreferencesWindow(QtGui.QDialog): """ Adds the pages for the different configuration categories. """ - self.ui.pages_widget.addWidget(PreferencesAccountPage(self)) - self.ui.pages_widget.addWidget(PreferencesVpnPage(self, self._domain, self._backend, self._leap_signaler)) - self.ui.pages_widget.addWidget(PreferencesEmailPage(self)) + self.ui.pages_widget.addWidget(PreferencesAccountPage(self, self.account, self.app)) + self.ui.pages_widget.addWidget(PreferencesVpnPage(self, self.account, self.app)) + self.ui.pages_widget.addWidget(PreferencesEmailPage(self, self.account, self.app)) # # Slots @@ -144,3 +141,18 @@ class PreferencesWindow(QtGui.QDialog): if not current: current = previous self.ui.pages_widget.setCurrentIndex(self.ui.nav_widget.row(current)) + + @QtCore.Slot(object, list) + def _update_icons(self, account, services): + """ + TRIGGERS: + self.app.service_selection_changed + + Change which icons are visible. + """ + if account != self.account: + return + + 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/preferences_account_page.ui b/src/leap/bitmask/gui/ui/preferences_account_page.ui index 4e58b2ab..9b6d885b 100644 --- a/src/leap/bitmask/gui/ui/preferences_account_page.ui +++ b/src/leap/bitmask/gui/ui/preferences_account_page.ui @@ -15,23 +15,28 @@ - - - - 0 - 0 - - - - Change Password + + + Services - - - - - - <change password status> + + false + + + + + + + + <provider_services_label> + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + + @@ -51,24 +56,28 @@ - + - Services + Password - - false - - - - - - - + + + + + + 0 + 0 + + - < Providers Services Status > + Change Password - - Qt::AlignCenter + + + + + + <change_password_label> 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): """ -- cgit v1.2.3 From 6166ffedcae0763f3c00076c79e74847f5c80823 Mon Sep 17 00:00:00 2001 From: elijah Date: Mon, 8 Sep 2014 02:01:14 -0700 Subject: single pref win: moved password change UI to a separate window, opened from account page in preferences. --- src/leap/bitmask/gui/account.py | 16 +- src/leap/bitmask/gui/flashable.py | 75 +++++++ src/leap/bitmask/gui/passwordwindow.py | 269 +++++++++++++++++++++++ src/leap/bitmask/gui/preferences_account_page.py | 15 ++ src/leap/bitmask/gui/ui/password_change.ui | 182 +++++++++++++++ src/leap/bitmask/gui/wizard.py | 2 +- src/leap/bitmask/util/credentials.py | 24 +- 7 files changed, 568 insertions(+), 15 deletions(-) create mode 100644 src/leap/bitmask/gui/flashable.py create mode 100644 src/leap/bitmask/gui/passwordwindow.py create mode 100644 src/leap/bitmask/gui/ui/password_change.ui (limited to 'src/leap/bitmask') diff --git a/src/leap/bitmask/gui/account.py b/src/leap/bitmask/gui/account.py index b08053a9..ae8127c0 100644 --- a/src/leap/bitmask/gui/account.py +++ b/src/leap/bitmask/gui/account.py @@ -19,6 +19,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 class Account(): @@ -33,11 +34,16 @@ class Account(): self.address = self.domain def services(self): - """ - returns a list of service name strings + """ + returns a list of service name strings - TODO: this should depend not just on the domain - """ - return self._settings.get_enabled_services(self.domain) + 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/flashable.py b/src/leap/bitmask/gui/flashable.py new file mode 100644 index 00000000..94e3ab60 --- /dev/null +++ b/src/leap/bitmask/gui/flashable.py @@ -0,0 +1,75 @@ +# -*- 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 . + +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 = "%s" % (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 = "%s" % (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 = "%s" % (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/passwordwindow.py b/src/leap/bitmask/gui/passwordwindow.py new file mode 100644 index 00000000..9946febe --- /dev/null +++ b/src/leap/bitmask/gui/passwordwindow.py @@ -0,0 +1,269 @@ +# -*- 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 . + +""" +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): + + 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) + + 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) + + pwd_change_error = lambda: self._srp_change_password_problem( + self.tr("There was a problem changing the password."), + None) + 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."), + '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._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) + + @QtCore.Slot() + def _close(self): + """ + TRIGGERS: + self.ui.buttonBox.rejected + + Close this dialog + """ + self.hide() + + @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(unicode) + def _srp_change_password_problem(self, msg, field): + """ + TRIGGERS: + self._backend.signaler.srp_password_change_error + self._backend.signaler.srp_password_change_badpw + + 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 password: %s" % (msg,)) + self._enable_password_widgets(True) + self.flash_error(msg) + if field == 'current_password': + 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 index bb90aab5..895d84b5 100644 --- a/src/leap/bitmask/gui/preferences_account_page.py +++ b/src/leap/bitmask/gui/preferences_account_page.py @@ -22,6 +22,7 @@ from functools import partial from PySide import QtCore, QtGui from ui_preferences_account_page import Ui_PreferencesAccountPage +from passwordwindow import PasswordWindow from leap.bitmask.services import get_service_display_name from leap.bitmask.config.leapsettings import LeapSettings @@ -43,9 +44,17 @@ class PreferencesAccountPage(QtGui.QWidget): 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): """ @@ -112,3 +121,9 @@ class PreferencesAccountPage(QtGui.QWidget): 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/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 @@ + + + PasswordChange + + + + 0 + 0 + 459 + 231 + + + + + 0 + 0 + + + + Change Password + + + + + + + + Username: + + + + + + + New password: + + + new_password_lineedit + + + + + + + QLineEdit::Password + + + + + + + Re-enter new password: + + + new_password_confirmation_lineedit + + + + + + + Current password: + + + current_password_lineedit + + + + + + + QLineEdit::Password + + + + + + + QLineEdit::Password + + + + + + + Qt::Vertical + + + + 20 + 10 + + + + + + + + + + false + + + + + + + + + + + <flash_label> + + + Qt::AlignCenter + + + + + + + Qt::Vertical + + + + 0 + 0 + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Close + + + false + + + + + + + OK + + + false + + + true + + + + + + + + + username_lineedit + current_password_lineedit + new_password_lineedit + new_password_confirmation_lineedit + + + + diff --git a/src/leap/bitmask/gui/wizard.py b/src/leap/bitmask/gui/wizard.py index 8182228d..4d55a39e 100644 --- a/src/leap/bitmask/gui/wizard.py +++ b/src/leap/bitmask/gui/wizard.py @@ -317,7 +317,7 @@ 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/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 -- cgit v1.2.3 From c29b1dd9345e01e761b9891728ecd0b8d964a02d Mon Sep 17 00:00:00 2001 From: elijah Date: Tue, 16 Sep 2014 16:40:52 -0700 Subject: single pref win: gets the autopep8 beauty scrub. --- src/leap/bitmask/gui/account.py | 6 ++--- src/leap/bitmask/gui/app.py | 4 +-- src/leap/bitmask/gui/flashable.py | 3 ++- src/leap/bitmask/gui/passwordwindow.py | 7 +++-- src/leap/bitmask/gui/preferences_account_page.py | 3 +-- src/leap/bitmask/gui/preferences_email_page.py | 3 ++- src/leap/bitmask/gui/preferences_vpn_page.py | 1 + src/leap/bitmask/gui/preferenceswindow.py | 33 +++++++++++++++--------- src/leap/bitmask/gui/wizard.py | 4 ++- 9 files changed, 37 insertions(+), 27 deletions(-) (limited to 'src/leap/bitmask') diff --git a/src/leap/bitmask/gui/account.py b/src/leap/bitmask/gui/account.py index ae8127c0..c941c3fa 100644 --- a/src/leap/bitmask/gui/account.py +++ b/src/leap/bitmask/gui/account.py @@ -21,6 +21,7 @@ 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): @@ -29,9 +30,9 @@ class Account(): self.domain = domain if self.username is not None: - self.address = make_address(self.username, self.domain) + self.address = make_address(self.username, self.domain) else: - self.address = self.domain + self.address = self.domain def services(self): """ @@ -46,4 +47,3 @@ class Account(): 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 index 7fcf69af..eb1a58d5 100644 --- a/src/leap/bitmask/gui/app.py +++ b/src/leap/bitmask/gui/app.py @@ -29,9 +29,8 @@ from leap.bitmask.backend.leapsignaler import LeapSignaler logger = logging.getLogger(__name__) -class App(QtGui.QWidget): - #preferences_saved = QtCore.Signal() +class App(QtGui.QWidget): # the user has changed which services are enabled for a particular account # args: account (Account), active services (list of str) @@ -50,7 +49,6 @@ class App(QtGui.QWidget): self._backend_checker.timeout.connect(self._check_backend_status) self._backend_checker.start(2000) - @QtCore.Slot() def _check_backend_status(self): """ diff --git a/src/leap/bitmask/gui/flashable.py b/src/leap/bitmask/gui/flashable.py index 94e3ab60..a26d1ec6 100644 --- a/src/leap/bitmask/gui/flashable.py +++ b/src/leap/bitmask/gui/flashable.py @@ -14,7 +14,9 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . + 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 @@ -72,4 +74,3 @@ class Flashable(object): def hide_flash(self): self._setup() self.widget.setVisible(False) - diff --git a/src/leap/bitmask/gui/passwordwindow.py b/src/leap/bitmask/gui/passwordwindow.py index 9946febe..5354ab86 100644 --- a/src/leap/bitmask/gui/passwordwindow.py +++ b/src/leap/bitmask/gui/passwordwindow.py @@ -28,6 +28,7 @@ from leap.bitmask.gui.flashable import Flashable import logging logger = logging.getLogger(__name__) + class PasswordWindow(QtGui.QDialog, Flashable): def __init__(self, parent, account, app): @@ -55,7 +56,7 @@ class PasswordWindow(QtGui.QDialog, Flashable): self.ui.cancel_button.clicked.connect(self._close) self.ui.username_lineedit.setText(account.address) - self._disabled = False # if set to True, never again enable widgets. + self._disabled = False # if set to True, never again enable widgets. if account.username is None: # should not ever happen, but just in case @@ -153,7 +154,6 @@ class PasswordWindow(QtGui.QDialog, Flashable): self._soledad_ready = False sig.soledad_bootstrap_finished.connect(self._on_soledad_ready) - @QtCore.Slot() def _change_password(self): """ @@ -174,7 +174,7 @@ class PasswordWindow(QtGui.QDialog, Flashable): return ok, msg, field = password_checks(self.account.username, new_password, - new_password2) + new_password2) if not ok: self.flash_error(msg) if field == 'new_password': @@ -258,7 +258,6 @@ class PasswordWindow(QtGui.QDialog, Flashable): self._enable_password_widgets(True) self.flash_error(msg) - @QtCore.Slot() def _on_soledad_ready(self): """ diff --git a/src/leap/bitmask/gui/preferences_account_page.py b/src/leap/bitmask/gui/preferences_account_page.py index 895d84b5..00dbe626 100644 --- a/src/leap/bitmask/gui/preferences_account_page.py +++ b/src/leap/bitmask/gui/preferences_account_page.py @@ -28,6 +28,7 @@ from leap.bitmask.config.leapsettings import LeapSettings logger = logging.getLogger(__name__) + class PreferencesAccountPage(QtGui.QWidget): def __init__(self, parent, account, app): @@ -86,7 +87,6 @@ class PreferencesAccountPage(QtGui.QWidget): # emit signal alerting change self.app.service_selection_changed.emit(self.account, services) - @QtCore.Slot(str) def _load_services(self, services): """ @@ -126,4 +126,3 @@ class PreferencesAccountPage(QtGui.QWidget): 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 index da902802..0535762a 100644 --- a/src/leap/bitmask/gui/preferences_email_page.py +++ b/src/leap/bitmask/gui/preferences_email_page.py @@ -23,7 +23,9 @@ from ui_preferences_email_page import Ui_PreferencesEmailPage logger = logging.getLogger(__name__) + class PreferencesEmailPage(QtGui.QWidget): + """ """ @@ -38,4 +40,3 @@ class PreferencesEmailPage(QtGui.QWidget): self.parent = parent 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 index a8f074d2..e3417f89 100644 --- a/src/leap/bitmask/gui/preferences_vpn_page.py +++ b/src/leap/bitmask/gui/preferences_vpn_page.py @@ -24,6 +24,7 @@ from leap.bitmask.config.leapsettings import LeapSettings class PreferencesVpnPage(QtGui.QWidget): + """ Page in the preferences window that shows VPN settings """ diff --git a/src/leap/bitmask/gui/preferenceswindow.py b/src/leap/bitmask/gui/preferenceswindow.py index 35a875fa..ccddb764 100644 --- a/src/leap/bitmask/gui/preferenceswindow.py +++ b/src/leap/bitmask/gui/preferenceswindow.py @@ -33,6 +33,7 @@ logger = logging.getLogger(__name__) class PreferencesWindow(QtGui.QDialog): + """ Window that displays the preferences. """ @@ -81,27 +82,32 @@ class PreferencesWindow(QtGui.QDialog): 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)) + 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.setTextAlignment( + QtCore.Qt.AlignHCenter | QtCore.Qt.AlignVCenter) vpn_item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) - vpn_item.setSizeHint(QtCore.QSize(98,56)) + 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)) + 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) @@ -111,9 +117,12 @@ class PreferencesWindow(QtGui.QDialog): """ Adds the pages for the different configuration categories. """ - self.ui.pages_widget.addWidget(PreferencesAccountPage(self, self.account, self.app)) - self.ui.pages_widget.addWidget(PreferencesVpnPage(self, self.account, self.app)) - self.ui.pages_widget.addWidget(PreferencesEmailPage(self, self.account, self.app)) + self.ui.pages_widget.addWidget( + PreferencesAccountPage(self, self.account, self.app)) + self.ui.pages_widget.addWidget( + PreferencesVpnPage(self, self.account, self.app)) + self.ui.pages_widget.addWidget( + PreferencesEmailPage(self, self.account, self.app)) # # Slots @@ -154,5 +163,5 @@ class PreferencesWindow(QtGui.QDialog): return self._vpn_item.setHidden(not EIP_SERVICE in services) - #self._email_item.setHidden(not MX_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/wizard.py b/src/leap/bitmask/gui/wizard.py index 4d55a39e..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, field = 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...")) -- cgit v1.2.3 From 1d331478a431047bf59fc6249a93e127450bff24 Mon Sep 17 00:00:00 2001 From: elijah Date: Tue, 16 Sep 2014 17:07:12 -0700 Subject: single pref win: clean up vpn pref error displaying & don't show dummy gateways in case of error. --- src/leap/bitmask/gui/preferences_vpn_page.py | 31 ++++--------------------- src/leap/bitmask/gui/ui/preferences_vpn_page.ui | 20 ---------------- 2 files changed, 5 insertions(+), 46 deletions(-) (limited to 'src/leap/bitmask') diff --git a/src/leap/bitmask/gui/preferences_vpn_page.py b/src/leap/bitmask/gui/preferences_vpn_page.py index e3417f89..f3fa1ecc 100644 --- a/src/leap/bitmask/gui/preferences_vpn_page.py +++ b/src/leap/bitmask/gui/preferences_vpn_page.py @@ -21,9 +21,9 @@ from PySide import QtCore, QtGui from 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): +class PreferencesVpnPage(QtGui.QWidget, Flashable): """ Page in the preferences window that shows VPN settings @@ -50,6 +50,7 @@ class PreferencesVpnPage(QtGui.QWidget): 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) @@ -62,28 +63,6 @@ class PreferencesVpnPage(QtGui.QWidget): # Trigger update self.app.backend.eip_get_gateways_list(domain=self.account.domain) - def _flash_error(self, message): - """ - Sets string for the flash message. - - :param message: the text to be displayed - :type message: str - """ - message = "%s" % (message,) - self.ui.flash_label.setVisible(True) - self.ui.flash_label.setText(message) - - # def _flash_success(self, message): - # """ - # Sets string for the flash message. - # - # :param message: the text to be displayed - # :type message: str - # """ - # message = "%s" % (message,) - # self.ui.flash_label.setVisible(True) - # self.ui.flash_label.setText(message) - @QtCore.Slot(str) def _save_selected_gateway(self, index): """ @@ -145,7 +124,7 @@ class PreferencesVpnPage(QtGui.QWidget): An error has occurred retrieving the gateway list so we inform the user. """ - self._flash_error( + self.flash_error( self.tr("Error loading configuration file.")) self.ui.gateways_list.setEnabled(False) @@ -158,6 +137,6 @@ class PreferencesVpnPage(QtGui.QWidget): The requested provider in not initialized yet, so we give the user an error msg. """ - self._flash_error( + 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/ui/preferences_vpn_page.ui b/src/leap/bitmask/gui/ui/preferences_vpn_page.ui index 85a0dc60..1bf3a060 100644 --- a/src/leap/bitmask/gui/ui/preferences_vpn_page.ui +++ b/src/leap/bitmask/gui/ui/preferences_vpn_page.ui @@ -36,26 +36,6 @@ - - - New Item - - - - :/images/countries/us.png - - - - - - New Item - - - - :/images/countries/br.png - - - -- cgit v1.2.3 From 5f56629884da77c3f1427ef5ceb8a830654eb424 Mon Sep 17 00:00:00 2001 From: elijah Date: Wed, 17 Sep 2014 16:15:56 -0700 Subject: single pref win: move preference window tracking to PreferencesWindow --- src/leap/bitmask/gui/mainwindow.py | 9 ++------- src/leap/bitmask/gui/preferenceswindow.py | 24 ++++++++++++++---------- 2 files changed, 16 insertions(+), 17 deletions(-) (limited to 'src/leap/bitmask') diff --git a/src/leap/bitmask/gui/mainwindow.py b/src/leap/bitmask/gui/mainwindow.py index b106364d..cc4ede09 100644 --- a/src/leap/bitmask/gui/mainwindow.py +++ b/src/leap/bitmask/gui/mainwindow.py @@ -98,9 +98,6 @@ class MainWindow(QtGui.QMainWindow): # We give the services some time to a halt before forcing quit. SERVICES_STOP_TIMEOUT = 3000 # in milliseconds - # Preferences window - preferences = None - def __init__(self, start_hidden=False, backend_pid=None): """ Constructor for the client main window @@ -574,10 +571,8 @@ class MainWindow(QtGui.QMainWindow): """ account = Account(self._logged_user, self._providers.get_selected_provider()) - if self.preferences is not None: - self.preferences.close() - self.preferences = PreferencesWindow(self, account, self.app) - self.preferences.show() + pref_win = PreferencesWindow(self, account, self.app) + pref_win.show() @QtCore.Slot(object, list) def _update_eip_enabled_status(self, account=None, services=None): diff --git a/src/leap/bitmask/gui/preferenceswindow.py b/src/leap/bitmask/gui/preferenceswindow.py index ccddb764..32651d5c 100644 --- a/src/leap/bitmask/gui/preferenceswindow.py +++ b/src/leap/bitmask/gui/preferenceswindow.py @@ -38,22 +38,21 @@ class PreferencesWindow(QtGui.QDialog): Window that displays the preferences. """ + _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 leap_signaler: signal server - :type leap_signaler: LeapSignaler + + :param account: the user or provider + :type account: Account + + :param app: the current App object + :type app: App """ QtGui.QDialog.__init__(self, parent) - self._parent = parent self.account = account self.app = app @@ -69,6 +68,11 @@ class PreferencesWindow(QtGui.QDialog): self._add_pages() self._update_icons(self.account, self.account.services()) + # only allow a single preferrences window at a time. + if PreferencesWindow._current_window is not None: + PreferencesWindow._current_window.close() + PreferencesWindow._current_window = self + def _add_icons(self): """ Adds all the icons for the different configuration categories. @@ -136,7 +140,7 @@ class PreferencesWindow(QtGui.QDialog): Close this dialog """ - self._parent.preferences = None + PreferencesWindow._current_window = None self.hide() @QtCore.Slot() -- cgit v1.2.3 From d8105d53e3aa66448094df3f34eda54c3dcab865 Mon Sep 17 00:00:00 2001 From: elijah Date: Wed, 17 Sep 2014 16:42:51 -0700 Subject: single pref win: minor linting & add changes file --- src/leap/bitmask/gui/preferences_account_page.py | 19 +++++++++++++++---- src/leap/bitmask/gui/preferences_email_page.py | 9 +-------- src/leap/bitmask/gui/preferences_vpn_page.py | 3 ++- src/leap/bitmask/gui/preferenceswindow.py | 6 ++++++ 4 files changed, 24 insertions(+), 13 deletions(-) (limited to 'src/leap/bitmask') diff --git a/src/leap/bitmask/gui/preferences_account_page.py b/src/leap/bitmask/gui/preferences_account_page.py index 00dbe626..ec6a7716 100644 --- a/src/leap/bitmask/gui/preferences_account_page.py +++ b/src/leap/bitmask/gui/preferences_account_page.py @@ -21,10 +21,9 @@ import logging from functools import partial from PySide import QtCore, QtGui -from ui_preferences_account_page import Ui_PreferencesAccountPage -from passwordwindow import PasswordWindow +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 -from leap.bitmask.config.leapsettings import LeapSettings logger = logging.getLogger(__name__) @@ -33,6 +32,14 @@ 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() @@ -103,7 +110,11 @@ class PreferencesAccountPage(QtGui.QWidget): self._selected_services = set() - # remove existing checkboxes + # 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) diff --git a/src/leap/bitmask/gui/preferences_email_page.py b/src/leap/bitmask/gui/preferences_email_page.py index 0535762a..80e8d93e 100644 --- a/src/leap/bitmask/gui/preferences_email_page.py +++ b/src/leap/bitmask/gui/preferences_email_page.py @@ -19,24 +19,17 @@ Widget for "email" preferences import logging from PySide import QtCore, QtGui -from ui_preferences_email_page import Ui_PreferencesEmailPage +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.parent = parent 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 index f3fa1ecc..901116b4 100644 --- a/src/leap/bitmask/gui/preferences_vpn_page.py +++ b/src/leap/bitmask/gui/preferences_vpn_page.py @@ -18,11 +18,12 @@ Widget for "vpn" preferences """ from PySide import QtCore, QtGui -from ui_preferences_vpn_page import Ui_PreferencesVpnPage +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): """ diff --git a/src/leap/bitmask/gui/preferenceswindow.py b/src/leap/bitmask/gui/preferenceswindow.py index 32651d5c..f9b7ddf6 100644 --- a/src/leap/bitmask/gui/preferenceswindow.py +++ b/src/leap/bitmask/gui/preferenceswindow.py @@ -150,6 +150,12 @@ class PreferencesWindow(QtGui.QDialog): self.ui.nav_widget.currentItemChanged Changes what page is displayed. + + :param current: the currently selected item (might be None?) + :type current: PySide.QtGui.QListWidgetItem + + :param previous: the previously selected item (might be None) + :type previous: PySide.QtGui.QListWidgetItem """ if not current: current = previous -- cgit v1.2.3 From 92fc4c1d9d60a213a4be21db459efc3bfc3e205f Mon Sep 17 00:00:00 2001 From: elijah Date: Thu, 18 Sep 2014 15:17:46 -0700 Subject: single pref win: added shortcut to preferences --- src/leap/bitmask/gui/ui/mainwindow.ui | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/leap/bitmask') diff --git a/src/leap/bitmask/gui/ui/mainwindow.ui b/src/leap/bitmask/gui/ui/mainwindow.ui index 2e8aea8c..b1d68c4a 100644 --- a/src/leap/bitmask/gui/ui/mainwindow.ui +++ b/src/leap/bitmask/gui/ui/mainwindow.ui @@ -337,7 +337,7 @@ true - Preferences... + Pr&eferences... -- cgit v1.2.3 From 0ad8c8ea3f8d5130f44aa90b55da59622d0048c7 Mon Sep 17 00:00:00 2001 From: elijah Date: Thu, 18 Sep 2014 15:18:46 -0700 Subject: single pref win: ensure proper deletion of preference window pages. --- src/leap/bitmask/gui/preferenceswindow.py | 41 +++++++++++++++++++++---------- 1 file changed, 28 insertions(+), 13 deletions(-) (limited to 'src/leap/bitmask') diff --git a/src/leap/bitmask/gui/preferenceswindow.py b/src/leap/bitmask/gui/preferenceswindow.py index f9b7ddf6..e18be976 100644 --- a/src/leap/bitmask/gui/preferenceswindow.py +++ b/src/leap/bitmask/gui/preferenceswindow.py @@ -38,7 +38,7 @@ class PreferencesWindow(QtGui.QDialog): Window that displays the preferences. """ - _current_window = None # currently visible preferences window + _current_window = None # currently visible preferences window def __init__(self, parent, account, app): """ @@ -59,7 +59,7 @@ class PreferencesWindow(QtGui.QDialog): self.ui = Ui_Preferences() self.ui.setupUi(self) - self.ui.close_button.clicked.connect(self.close) + self.ui.close_button.clicked.connect(self.close_window) self.ui.account_label.setText(account.address) self.app.service_selection_changed.connect(self._update_icons) @@ -68,9 +68,9 @@ class PreferencesWindow(QtGui.QDialog): self._add_pages() self._update_icons(self.account, self.account.services()) - # only allow a single preferrences window at a time. + # only allow a single preferences window at a time. if PreferencesWindow._current_window is not None: - PreferencesWindow._current_window.close() + PreferencesWindow._current_window.close_window() PreferencesWindow._current_window = self def _add_icons(self): @@ -121,27 +121,42 @@ class PreferencesWindow(QtGui.QDialog): """ Adds the pages for the different configuration categories. """ - self.ui.pages_widget.addWidget( - PreferencesAccountPage(self, self.account, self.app)) - self.ui.pages_widget.addWidget( - PreferencesVpnPage(self, self.account, self.app)) - self.ui.pages_widget.addWidget( - PreferencesEmailPage(self, self.account, self.app)) + 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) + + def closeEvent(self, e): + """ + Override closeEvent to capture when user closes the window. + """ + self.close_window() # # Slots # @QtCore.Slot() - def close(self): + def close_window(self): """ TRIGGERS: self.ui.close_button.clicked - Close this dialog + Close this dialog and destroy it. """ PreferencesWindow._current_window = None - self.hide() + self.close() + + # 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_page(self, current, previous): -- cgit v1.2.3 From d47adca6cb7494e55c4a9fbc88896c62c06affa5 Mon Sep 17 00:00:00 2001 From: elijah Date: Fri, 19 Sep 2014 14:22:42 -0700 Subject: single pref win: fix problems with cleaning up closed windows (lambdas were keeping the python object from getting garbage collected, and this keeps the old signal connections active) --- src/leap/bitmask/gui/passwordwindow.py | 61 +++++++++++++++++-------------- src/leap/bitmask/gui/preferenceswindow.py | 14 ++----- 2 files changed, 37 insertions(+), 38 deletions(-) (limited to 'src/leap/bitmask') diff --git a/src/leap/bitmask/gui/passwordwindow.py b/src/leap/bitmask/gui/passwordwindow.py index 5354ab86..f7ef079e 100644 --- a/src/leap/bitmask/gui/passwordwindow.py +++ b/src/leap/bitmask/gui/passwordwindow.py @@ -31,6 +31,8 @@ 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. @@ -53,9 +55,13 @@ class PasswordWindow(QtGui.QDialog, Flashable): self.hide_flash() self.ui.ok_button.clicked.connect(self._change_password) - self.ui.cancel_button.clicked.connect(self._close) + 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: @@ -132,22 +138,11 @@ class PasswordWindow(QtGui.QDialog, Flashable): Helper to connect to backend signals """ sig = self.app.signaler - 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."), - None) - 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."), - 'current_password') - sig.srp_password_change_badpw.connect(pwd_change_badpw) - + 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) @@ -188,15 +183,16 @@ class PasswordWindow(QtGui.QDialog, Flashable): current_password=current_password, new_password=new_password) - @QtCore.Slot() - def _close(self): + def closeEvent(self, event=None): """ TRIGGERS: - self.ui.buttonBox.rejected + cancel_button (indirectly via self.close()) + or when window is closed - Close this dialog + Close this dialog & delete ourselves to clean up signals. """ - self.hide() + PasswordWindow._current_window = None + self.deleteLater() @QtCore.Slot() def _srp_change_password_ok(self): @@ -214,23 +210,32 @@ class PasswordWindow(QtGui.QDialog, Flashable): else: self._change_password_success() - @QtCore.Slot(unicode) - def _srp_change_password_problem(self, msg, field): + @QtCore.Slot() + def _srp_password_change_error(self): """ TRIGGERS: self._backend.signaler.srp_password_change_error - self._backend.signaler.srp_password_change_badpw - Callback used to display an error on changing password. + 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) - :param msg: the message to show to the user. - :type msg: unicode + @QtCore.Slot() + def _srp_password_change_badpw(self): """ - logger.error("Error changing password: %s" % (msg,)) + 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) - if field == 'current_password': - self.ui.current_password_lineedit.setFocus() + self.ui.current_password_lineedit.setFocus() @QtCore.Slot() def _soledad_change_password_ok(self): diff --git a/src/leap/bitmask/gui/preferenceswindow.py b/src/leap/bitmask/gui/preferenceswindow.py index e18be976..f1252301 100644 --- a/src/leap/bitmask/gui/preferenceswindow.py +++ b/src/leap/bitmask/gui/preferenceswindow.py @@ -59,7 +59,7 @@ class PreferencesWindow(QtGui.QDialog): self.ui = Ui_Preferences() self.ui.setupUi(self) - self.ui.close_button.clicked.connect(self.close_window) + self.ui.close_button.clicked.connect(self.close) self.ui.account_label.setText(account.address) self.app.service_selection_changed.connect(self._update_icons) @@ -129,26 +129,20 @@ class PreferencesWindow(QtGui.QDialog): self.ui.pages_widget.addWidget(self._vpn_page) self.ui.pages_widget.addWidget(self._email_page) - def closeEvent(self, e): - """ - Override closeEvent to capture when user closes the window. - """ - self.close_window() - # # Slots # - @QtCore.Slot() - def close_window(self): + def closeEvent(self, e): """ TRIGGERS: self.ui.close_button.clicked + (since self.close() will trigger closeEvent) + whenever the window is closed Close this dialog and destroy it. """ PreferencesWindow._current_window = None - self.close() # deleteLater does not seem to cascade to items in stackLayout # (even with QtCore.Qt.WA_DeleteOnClose attribute). -- cgit v1.2.3 From 0db3d5a4aecc0a3c271b92c6187922c50d46df0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Touceda?= Date: Fri, 26 Sep 2014 10:13:44 -0300 Subject: Revert "Fix soledad imports (#5989)." This reverts commit 7fcc4f40eaa8214de8ae20cd71d173337ad64290. --- src/leap/bitmask/backend/components.py | 2 +- src/leap/bitmask/services/soledad/soledadbootstrapper.py | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) (limited to 'src/leap/bitmask') diff --git a/src/leap/bitmask/backend/components.py b/src/leap/bitmask/backend/components.py index d4f6d176..5ef6befd 100644 --- a/src/leap/bitmask/backend/components.py +++ b/src/leap/bitmask/backend/components.py @@ -60,7 +60,7 @@ from leap.common import certs as leap_certs from leap.keymanager import openpgp from leap.keymanager.errors import KeyAddressMismatch, KeyFingerprintMismatch -from leap.soledad.client.secrets import NoStorageSecret, PassphraseTooShort +from leap.soledad.client import NoStorageSecret, PassphraseTooShort logger = logging.getLogger(__name__) diff --git a/src/leap/bitmask/services/soledad/soledadbootstrapper.py b/src/leap/bitmask/services/soledad/soledadbootstrapper.py index 745645f3..c4e43bfe 100644 --- a/src/leap/bitmask/services/soledad/soledadbootstrapper.py +++ b/src/leap/bitmask/services/soledad/soledadbootstrapper.py @@ -45,8 +45,7 @@ from leap.common.files import which from leap.keymanager import KeyManager, openpgp from leap.keymanager.errors import KeyNotFound from leap.soledad.common.errors import InvalidAuthTokenError -from leap.soledad.client import Soledad -from leap.soledad.client.secrets import BootstrapSequenceError +from leap.soledad.client import Soledad, BootstrapSequenceError logger = logging.getLogger(__name__) -- cgit v1.2.3