diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/leap/bitmask/_version.py | 4 | ||||
-rw-r--r-- | src/leap/bitmask/gui/eip_preferenceswindow.py | 35 | ||||
-rw-r--r-- | src/leap/bitmask/gui/eip_status.py | 18 | ||||
-rw-r--r-- | src/leap/bitmask/gui/mainwindow.py | 80 | ||||
-rw-r--r-- | src/leap/bitmask/gui/preferenceswindow.py | 9 | ||||
-rw-r--r-- | src/leap/bitmask/provider/providerbootstrapper.py | 32 | ||||
-rw-r--r-- | src/leap/bitmask/services/eip/eipconfig.py | 14 | ||||
-rw-r--r-- | src/leap/bitmask/services/eip/linuxvpnlauncher.py | 17 | ||||
-rw-r--r-- | src/leap/bitmask/services/eip/vpnlauncher.py | 3 | ||||
-rw-r--r-- | src/leap/bitmask/services/soledad/soledadbootstrapper.py | 2 |
10 files changed, 135 insertions, 79 deletions
diff --git a/src/leap/bitmask/_version.py b/src/leap/bitmask/_version.py index 4288481a..b4f33670 100644 --- a/src/leap/bitmask/_version.py +++ b/src/leap/bitmask/_version.py @@ -5,8 +5,8 @@ # unpacked source archive. Distribution tarballs contain a pre-generated copy # of this file. -version_version = '0.3.7' -version_full = '4584fc20e20491508580925454a1d86d26b28374' +version_version = '0.3.8' +version_full = '5a4c47c4a3fb0c394c64000aa87dcece9b5ee990' def get_versions(default={}, verbose=False): diff --git a/src/leap/bitmask/gui/eip_preferenceswindow.py b/src/leap/bitmask/gui/eip_preferenceswindow.py index e0c5d51f..504d1cf1 100644 --- a/src/leap/bitmask/gui/eip_preferenceswindow.py +++ b/src/leap/bitmask/gui/eip_preferenceswindow.py @@ -28,6 +28,7 @@ from leap.bitmask.config.leapsettings import LeapSettings from leap.bitmask.config.providerconfig import ProviderConfig from leap.bitmask.gui.ui_eippreferences import Ui_EIPPreferences from leap.bitmask.services.eip.eipconfig import EIPConfig, VPNGatewaySelector +from leap.bitmask.services.eip.eipconfig import get_eipconfig_path logger = logging.getLogger(__name__) @@ -52,7 +53,7 @@ class EIPPreferencesWindow(QtGui.QDialog): self.ui.lblProvidersGatewayStatus.setVisible(False) # Connections - self.ui.cbProvidersGateway.currentIndexChanged[unicode].connect( + self.ui.cbProvidersGateway.currentIndexChanged[int].connect( self._populate_gateways) self.ui.cbGateways.currentIndexChanged[unicode].connect( @@ -93,7 +94,11 @@ class EIPPreferencesWindow(QtGui.QDialog): return for provider in providers: - self.ui.cbProvidersGateway.addItem(provider) + label = provider + eip_config_path = get_eipconfig_path(provider, relative=False) + if not os.path.isfile(eip_config_path): + label = provider + self.tr(" (uninitialized)") + self.ui.cbProvidersGateway.addItem(label, userData=provider) def _save_selected_gateway(self, provider): """ @@ -120,7 +125,7 @@ class EIPPreferencesWindow(QtGui.QDialog): "Gateway settings for provider '{0}' saved.").format(provider) self._set_providers_gateway_status(msg, success=True) - def _populate_gateways(self, domain): + def _populate_gateways(self, domain_idx): """ SLOT TRIGGERS: @@ -129,15 +134,29 @@ class EIPPreferencesWindow(QtGui.QDialog): Loads the gateways that the provider provides into the UI for the user to select. - :param domain: the domain of the provider to load gateways from. - :type domain: str + :param domain: the domain index of the provider to load gateways from. + :type domain: int """ # We hide the maybe-visible status label after a change self.ui.lblProvidersGatewayStatus.setVisible(False) - if not domain: + if domain_idx == -1: return + domain = self.ui.cbProvidersGateway.itemData(domain_idx) + + if not os.path.isfile(get_eipconfig_path(domain, relative=False)): + self._set_providers_gateway_status( + self.tr("This is an uninitialized provider, " + "please log in first."), + error=True) + self.ui.pbSaveGateway.setEnabled(False) + self.ui.cbGateways.setEnabled(False) + return + else: + self.ui.pbSaveGateway.setEnabled(True) + self.ui.cbGateways.setEnabled(True) + try: # disconnect previously connected save method self.ui.pbSaveGateway.clicked.disconnect() @@ -151,11 +170,9 @@ class EIPPreferencesWindow(QtGui.QDialog): eip_config = EIPConfig() provider_config = ProviderConfig.get_provider_config(domain) - eip_config_path = os.path.join("leap", "providers", - domain, "eip-service.json") api_version = provider_config.get_api_version() eip_config.set_api_version(api_version) - eip_loaded = eip_config.load(eip_config_path) + eip_loaded = eip_config.load(get_eipconfig_path(domain)) if not eip_loaded or provider_config is None: self._set_providers_gateway_status( diff --git a/src/leap/bitmask/gui/eip_status.py b/src/leap/bitmask/gui/eip_status.py index 1899d6a4..4b4d360f 100644 --- a/src/leap/bitmask/gui/eip_status.py +++ b/src/leap/bitmask/gui/eip_status.py @@ -77,6 +77,10 @@ class EIPStatusWidget(QtGui.QWidget): self._provider = "" + # Action for the systray + self._eip_disabled_action = QtGui.QAction( + "{0} is {1}".format(self._service_name, self.tr("disabled")), self) + def _make_status_clickable(self): """ Makes upload and download figures clickable. @@ -252,6 +256,15 @@ class EIPStatusWidget(QtGui.QWidget): self.eip_button.hide() msg = self.tr("You must login to use {0}".format(self._service_name)) self.eip_label.setText(msg) + self._eip_status_menu.setTitle("{0} is {1}".format( + self._service_name, self.tr("disabled"))) + + # Replace EIP tray menu with an action that displays a "disabled" text + menu = self._systray.contextMenu() + menu.insertAction( + self._eip_status_menu.menuAction(), + self._eip_disabled_action) + self._eip_status_menu.menuAction().setVisible(False) @QtCore.Slot() def enable_eip_start(self): @@ -262,6 +275,11 @@ class EIPStatusWidget(QtGui.QWidget): logger.debug('Showing EIP start button') self.eip_button.show() + # Restore the eip action menu + menu = self._systray.contextMenu() + menu.removeAction(self._eip_disabled_action) + self._eip_status_menu.menuAction().setVisible(True) + # XXX disable (later) -------------------------- def set_eip_status(self, status, error=False): """ diff --git a/src/leap/bitmask/gui/mainwindow.py b/src/leap/bitmask/gui/mainwindow.py index b0f25af1..929919ac 100644 --- a/src/leap/bitmask/gui/mainwindow.py +++ b/src/leap/bitmask/gui/mainwindow.py @@ -22,7 +22,7 @@ import os from PySide import QtCore, QtGui from twisted.internet import threads -from zope.proxy import ProxyBase, setProxiedObject, sameProxiedObjects +from zope.proxy import ProxyBase, setProxiedObject from leap.bitmask import __version__ as VERSION from leap.bitmask.config.leapsettings import LeapSettings @@ -44,6 +44,8 @@ from leap.bitmask.platform_init import IS_WIN, IS_MAC from leap.bitmask.platform_init.initializers import init_platform from leap.bitmask.provider.providerbootstrapper import ProviderBootstrapper +from leap.bitmask.services import get_service_display_name, EIP_SERVICE + from leap.bitmask.services.mail import conductor as mail_conductor from leap.bitmask.services import EIP_SERVICE, MX_SERVICE @@ -339,6 +341,8 @@ class MainWindow(QtGui.QMainWindow): self.start_eip_machine() self._mail_conductor.start_mail_machine() + self._eip_name = get_service_display_name(EIP_SERVICE) + if self._first_run(): self._wizard_firstrun = True self._wizard = Wizard(bypass_checks=bypass_checks) @@ -358,15 +362,19 @@ class MainWindow(QtGui.QMainWindow): Called if the wizard has been cancelled or closed before finishing. + This is executed for the first run wizard only. Any other execution of + the wizard won't reach this point. """ - if self._wizard_firstrun: - providers = self._settings.get_configured_providers() - has_provider_on_disk = len(providers) != 0 - if not has_provider_on_disk: - # if we don't have any provider configured (included a pinned - # one) we can't use the application, so quit. - self.quit() + providers = self._settings.get_configured_providers() + has_provider_on_disk = len(providers) != 0 + if not has_provider_on_disk: + # if we don't have any provider configured (included a pinned + # one) we can't use the application, so quit. + self.quit() else: + # This happens if the user finishes the provider + # setup but does not register + self._wizard = None self._finish_init() def _launch_wizard(self): @@ -463,9 +471,9 @@ class MainWindow(QtGui.QMainWindow): Displays the preferences window. """ - preferences_window = PreferencesWindow(self, self._srp_auth, - self._provider_config, - self._soledad) + preferences_window = PreferencesWindow( + self, self._srp_auth, self._provider_config, self._soledad, + self._login_widget.get_selected_provider()) self.soledad_ready.connect(preferences_window.set_soledad_ready) preferences_window.show() @@ -538,8 +546,7 @@ class MainWindow(QtGui.QMainWindow): TRIGGERS: self._wizard.accepted - Also called at the end of the constructor if not first run, - and after _rejected_wizard if not first run. + Also called at the end of the constructor if not first run. Implements the behavior after either constructing the mainwindow object, loading the saved user/password, or after @@ -574,6 +581,9 @@ class MainWindow(QtGui.QMainWindow): if possible_password is not None: self._login_widget.set_password(possible_password) self._login() + else: + self.eip_needs_login.emit() + self._wizard = None else: self._try_autostart_eip() @@ -637,7 +647,8 @@ class MainWindow(QtGui.QMainWindow): systrayMenu.addAction(self._action_visible) systrayMenu.addSeparator() - eip_menu = systrayMenu.addMenu(self.tr("Encrypted Internet: OFF")) + eip_status_label = "{0}: {1}".format(self._eip_name, self.tr("OFF")) + eip_menu = systrayMenu.addMenu(eip_status_label) eip_menu.addAction(self._action_eip_startstop) self._eip_status.set_eip_status_menu(eip_menu) systrayMenu.addSeparator() @@ -1224,10 +1235,9 @@ class MainWindow(QtGui.QMainWindow): provider_config, self._eip_config, provider) if not loaded: - self._eip_status.set_eip_status( - self.tr("Could not load Encrypted Internet " - "Configuration."), - error=True) + eip_status_label = self.tr("Could not load {0} configuration.") + eip_status_label = eip_status_label.format(self._eip_name) + self._eip_status.set_eip_status(eip_status_label, error=True) # signal connection aborted to state machine qtsigs = self._eip_connection.qtsigs qtsigs.connection_aborted_signal.emit() @@ -1264,9 +1274,9 @@ class MainWindow(QtGui.QMainWindow): self._set_eipstatus_off() except EIPNoTunKextLoaded: self._eip_status.set_eip_status( - self.tr("Encrypted Internet cannot be started because " + self.tr("{0} cannot be started because " "the tuntap extension is not installed properly " - "in your system.")) + "in your system.").format(self._eip_name)) self._set_eipstatus_off() except EIPNoPkexecAvailable: self._eip_status.set_eip_status( @@ -1363,8 +1373,7 @@ class MainWindow(QtGui.QMainWindow): Sets eip status to off """ # XXX this should be handled by the state machine. - self._eip_status.set_eip_status(self.tr("EIP has stopped"), - error=error) + self._eip_status.set_eip_status("", error=error) self._eip_status.set_eip_status_icon("error") def _eip_finished(self, exitCode): @@ -1402,17 +1411,18 @@ class MainWindow(QtGui.QMainWindow): # XXX check if these exitCodes are pkexec/cocoasudo specific if exitCode in (126, 127): - self._eip_status.set_eip_status( - self.tr("Encrypted Internet could not be launched " - "because you did not authenticate properly."), - error=True) + eip_status_label = self.tr( + "{0} could not be launched " + "because you did not authenticate properly.") + eip_status_label = eip_status_label.format(self._eip_name) + self._eip_status.set_eip_status(eip_status_label, error=True) self._vpn.killit() signal = qtsigs.connection_aborted_signal elif exitCode != 0 or not self.user_stopped_eip: - self._eip_status.set_eip_status( - self.tr("Encrypted Internet finished in an " - "unexpected manner!"), error=True) + eip_status_label = self.tr("{0} finished in an unexpected manner!") + eip_status_label = eip_status_label.format(self._eip_name) + self._eip_status.set_eip_status(eip_status_label, error=True) signal = qtsigs.connection_died_signal if exitCode == 0 and IS_MAC: @@ -1449,6 +1459,7 @@ class MainWindow(QtGui.QMainWindow): self.tr("Not supported"), error=True) else: + self._eip_status.disable_eip_start() self._eip_status.set_eip_status(self.tr("Disabled")) def _finish_eip_bootstrap(self, data): @@ -1480,10 +1491,9 @@ class MainWindow(QtGui.QMainWindow): # DO START EIP Connection! self._eip_connection.qtsigs.do_connect_signal.emit() else: - self._eip_status.set_eip_status( - self.tr("Could not load Encrypted Internet " - "Configuration."), - error=True) + eip_status_label = self.tr("Could not load {0} configuration.") + eip_status_label = eip_status_label.format(self._eip_name) + self._eip_status.set_eip_status(eip_status_label, error=True) def _eip_intermediate_stage(self, data): # TODO missing param @@ -1645,9 +1655,9 @@ class MainWindow(QtGui.QMainWindow): # XXX this can timeout after loong time: See #3368 self._srp_auth.logout() - if self._soledad: + if self._soledad_bootstrapper.soledad is not None: logger.debug("Closing soledad...") - self._soledad.close() + self._soledad_bootstrapper.soledad.close() else: logger.error("No instance of soledad was found.") diff --git a/src/leap/bitmask/gui/preferenceswindow.py b/src/leap/bitmask/gui/preferenceswindow.py index 8e9ef95a..b4bddef2 100644 --- a/src/leap/bitmask/gui/preferenceswindow.py +++ b/src/leap/bitmask/gui/preferenceswindow.py @@ -42,7 +42,7 @@ class PreferencesWindow(QtGui.QDialog): """ Window that displays the preferences. """ - def __init__(self, parent, srp_auth, provider_config, soledad): + def __init__(self, parent, srp_auth, provider_config, soledad, domain): """ :param parent: parent object of the PreferencesWindow. :parent type: QWidget @@ -52,6 +52,8 @@ class PreferencesWindow(QtGui.QDialog): :type provider_config: ProviderConfig :param soledad: Soledad instance :type soledad: Soledad + :param domain: the selected domain in the login widget + :type domain: unicode """ QtGui.QDialog.__init__(self, parent) self.AUTOMATIC_GATEWAY_LABEL = self.tr("Automatic") @@ -83,9 +85,6 @@ class PreferencesWindow(QtGui.QDialog): # check if the user is logged in if srp_auth is not None and srp_auth.get_token() is not None: # check if provider has 'mx' ... - domain = provider_config.get_domain() - self._select_provider_by_name(domain) - if provider_config.provides_mx(): enabled_services = self._settings.get_enabled_services(domain) mx_name = get_service_display_name(MX_SERVICE) @@ -111,6 +110,8 @@ class PreferencesWindow(QtGui.QDialog): "In order to change your password you need to be logged in.") self._set_password_change_status(msg) + self._select_provider_by_name(domain) + self.ui.gbPasswordChange.setEnabled(pw_enabled) def set_soledad_ready(self): diff --git a/src/leap/bitmask/provider/providerbootstrapper.py b/src/leap/bitmask/provider/providerbootstrapper.py index 2adf8aa8..f5a2003f 100644 --- a/src/leap/bitmask/provider/providerbootstrapper.py +++ b/src/leap/bitmask/provider/providerbootstrapper.py @@ -105,13 +105,13 @@ class ProviderBootstrapper(AbstractBootstrapper): Checks that the name resolution for the provider name works """ leap_assert(self._domain, "Cannot check DNS without a domain") - logger.debug("Checking name resolution for %s" % (self._domain)) + logger.debug("Checking name resolution for %r" % (self._domain)) # We don't skip this check, since it's basic for the whole # system to work # err --- but we can do it after a failure, to diagnose what went # wrong. Right now we're just adding connection overhead. -- kali - socket.gethostbyname(self._domain) + socket.gethostbyname(self._domain.encode('idna')) def _check_https(self, *args): """ @@ -119,7 +119,7 @@ class ProviderBootstrapper(AbstractBootstrapper): checks out """ leap_assert(self._domain, "Cannot check HTTPS without a domain") - logger.debug("Checking https for %s" % (self._domain)) + logger.debug("Checking https for %r" % (self._domain)) # We don't skip this check, since it's basic for the whole # system to work. @@ -131,8 +131,8 @@ class ProviderBootstrapper(AbstractBootstrapper): verify = self.verify.encode(sys.getfilesystemencoding()) try: - res = self._session.get("https://%s" % (self._domain,), - verify=verify, + uri = "https://{0}".format(self._domain.encode('idna')) + res = self._session.get(uri, verify=verify, timeout=REQUEST_TIMEOUT) res.raise_for_status() except requests.exceptions.SSLError as exc: @@ -154,17 +154,17 @@ class ProviderBootstrapper(AbstractBootstrapper): """ leap_assert(self._domain, "Cannot download provider info without a domain") - logger.debug("Downloading provider info for %s" % (self._domain)) + logger.debug("Downloading provider info for %r" % (self._domain)) # -------------------------------------------------------------- # TODO factor out with the download routines in services. # Watch out! We're handling the verify paramenter differently here. headers = {} + domain = self._domain.encode(sys.getfilesystemencoding()) provider_json = os.path.join(util.get_path_prefix(), - "leap", - "providers", - self._domain, "provider.json") + "leap", "providers", domain, + "provider.json") mtime = get_mtime(provider_json) if self._download_if_needed and mtime: @@ -190,7 +190,7 @@ class ProviderBootstrapper(AbstractBootstrapper): logger.debug("Requesting for provider.json... " "uri: {0}, verify: {1}, headers: {2}".format( uri, verify, headers)) - res = self._session.get(uri, verify=verify, + res = self._session.get(uri.encode('idna'), verify=verify, headers=headers, timeout=REQUEST_TIMEOUT) res.raise_for_status() logger.debug("Request status code: {0}".format(res.status_code)) @@ -206,10 +206,8 @@ class ProviderBootstrapper(AbstractBootstrapper): provider_config = ProviderConfig() provider_config.load(data=provider_definition, mtime=mtime) - provider_config.save(["leap", - "providers", - self._domain, - "provider.json"]) + provider_config.save(["leap", "providers", + domain, "provider.json"]) api_version = provider_config.get_api_version() if SupportedAPIs.supports(api_version): @@ -228,7 +226,7 @@ class ProviderBootstrapper(AbstractBootstrapper): Populates the check queue. :param domain: domain to check - :type domain: str + :type domain: unicode :param download_if_needed: if True, makes the checks do not overwrite already downloaded data @@ -271,7 +269,7 @@ class ProviderBootstrapper(AbstractBootstrapper): leap_assert(self._provider_config, "Cannot download the ca cert " "without a provider config!") - logger.debug("Downloading ca cert for %s at %s" % + logger.debug("Downloading ca cert for %r at %r" % (self._domain, self._provider_config.get_ca_cert_uri())) if not self._should_proceed_cert(): @@ -302,7 +300,7 @@ class ProviderBootstrapper(AbstractBootstrapper): leap_assert(self._provider_config, "Cannot check the ca cert " "without a provider config!") - logger.debug("Checking ca fingerprint for %s and cert %s" % + logger.debug("Checking ca fingerprint for %r and cert %r" % (self._domain, self._provider_config.get_ca_cert_path())) diff --git a/src/leap/bitmask/services/eip/eipconfig.py b/src/leap/bitmask/services/eip/eipconfig.py index 16ed4cc0..09a3d257 100644 --- a/src/leap/bitmask/services/eip/eipconfig.py +++ b/src/leap/bitmask/services/eip/eipconfig.py @@ -33,17 +33,25 @@ from leap.common.check import leap_assert, leap_assert_type logger = logging.getLogger(__name__) -def get_eipconfig_path(domain): +def get_eipconfig_path(domain, relative=True): """ - Returns relative path for EIP config. + Returns relative or absolute path for EIP config. :param domain: the domain to which this eipconfig belongs to. :type domain: str + :param relative: defines whether the path should be relative or absolute. + :type relative: bool :returns: the path :rtype: str """ leap_assert(domain is not None, "get_eipconfig_path: We need a domain") - return os.path.join("leap", "providers", domain, "eip-service.json") + + path = os.path.join("leap", "providers", domain, "eip-service.json") + + if not relative: + path = os.path.join(get_path_prefix(), path) + + return path def load_eipconfig_if_needed(provider_config, eip_config, domain): diff --git a/src/leap/bitmask/services/eip/linuxvpnlauncher.py b/src/leap/bitmask/services/eip/linuxvpnlauncher.py index d02f6f96..d24e7ae7 100644 --- a/src/leap/bitmask/services/eip/linuxvpnlauncher.py +++ b/src/leap/bitmask/services/eip/linuxvpnlauncher.py @@ -77,7 +77,7 @@ def _try_to_launch_agent(): Tries to launch a polkit daemon. """ env = None - if flags.STANDALONE is True: + if flags.STANDALONE: env = {"PYTHONPATH": os.path.abspath('../../../../lib/')} try: # We need to quote the command because subprocess call @@ -148,18 +148,21 @@ class LinuxVPNLauncher(VPNLauncher): def missing_other_files(kls): """ 'Extend' the VPNLauncher's missing_other_files to check if the polkit - files is outdated. If the polkit file that is in OTHER_FILES exists but - is not up to date, it is added to the missing list. + files is outdated, in the case of an standalone bundle. + If the polkit file that is in OTHER_FILES exists but is not up to date, + it is added to the missing list. :returns: a list of missing files :rtype: list of str """ # we use `super` in order to send the class to use missing = super(LinuxVPNLauncher, kls).missing_other_files() - polkit_file = LinuxPolicyChecker.get_polkit_path() - if polkit_file not in missing: - if privilege_policies.is_policy_outdated(kls.OPENVPN_BIN_PATH): - missing.append(polkit_file) + + if flags.STANDALONE: + polkit_file = LinuxPolicyChecker.get_polkit_path() + if polkit_file not in missing: + if privilege_policies.is_policy_outdated(kls.OPENVPN_BIN_PATH): + missing.append(polkit_file) return missing diff --git a/src/leap/bitmask/services/eip/vpnlauncher.py b/src/leap/bitmask/services/eip/vpnlauncher.py index 07497814..99cae7f9 100644 --- a/src/leap/bitmask/services/eip/vpnlauncher.py +++ b/src/leap/bitmask/services/eip/vpnlauncher.py @@ -147,7 +147,8 @@ class VPNLauncher(object): args = [] args += [ - '--setenv', "LEAPOPENVPN", "1" + '--setenv', "LEAPOPENVPN", "1", + '--nobind' ] if openvpn_verb is not None: diff --git a/src/leap/bitmask/services/soledad/soledadbootstrapper.py b/src/leap/bitmask/services/soledad/soledadbootstrapper.py index e8c7e9ce..d078ae96 100644 --- a/src/leap/bitmask/services/soledad/soledadbootstrapper.py +++ b/src/leap/bitmask/services/soledad/soledadbootstrapper.py @@ -196,7 +196,7 @@ class SoledadBootstrapper(AbstractBootstrapper): leap_assert(not sameProxiedObjects(self._soledad, None), "Null soledad, error while initializing") - self.deferred = deferToThread(self._do_soledad_sync) + self._do_soledad_sync() def _do_soledad_sync(self): """ |