diff options
Diffstat (limited to 'src/leap')
-rw-r--r-- | src/leap/app.py | 2 | ||||
-rw-r--r-- | src/leap/config/providerconfig.py | 17 | ||||
-rw-r--r-- | src/leap/gui/login.py | 6 | ||||
-rw-r--r-- | src/leap/gui/mainwindow.py | 72 | ||||
-rw-r--r-- | src/leap/services/eip/eipbootstrapper.py | 4 | ||||
-rw-r--r-- | src/leap/services/eip/eipconfig.py | 13 | ||||
-rw-r--r-- | src/leap/services/eip/eipspec.py | 22 | ||||
-rw-r--r-- | src/leap/services/eip/tests/test_eipconfig.py | 17 | ||||
-rw-r--r-- | src/leap/services/eip/vpnlaunchers.py | 27 | ||||
-rw-r--r-- | src/leap/services/eip/vpnprocess.py | 27 | ||||
-rw-r--r-- | src/leap/services/mail/smtpbootstrapper.py | 8 | ||||
-rw-r--r-- | src/leap/services/mail/smtpconfig.py | 10 | ||||
-rw-r--r-- | src/leap/services/mail/smtpspec.py | 21 |
13 files changed, 184 insertions, 62 deletions
diff --git a/src/leap/app.py b/src/leap/app.py index 774ae41f..e7a8aa42 100644 --- a/src/leap/app.py +++ b/src/leap/app.py @@ -142,6 +142,7 @@ def main(): bypass_checks = getattr(opts, 'danger', False) debug = opts.debug logfile = opts.log_file + openvpn_verb = opts.openvpn_verb logger = add_logger_handlers(debug, logfile) replace_stdout_stderr_with_logging(logger) @@ -202,6 +203,7 @@ def main(): window = MainWindow( lambda: twisted_main.quit(app), standalone=standalone, + openvpn_verb=openvpn_verb, bypass_checks=bypass_checks) sigint_window = partial(sigint_handler, window, logger=logger) diff --git a/src/leap/config/providerconfig.py b/src/leap/config/providerconfig.py index eb097034..f899b17c 100644 --- a/src/leap/config/providerconfig.py +++ b/src/leap/config/providerconfig.py @@ -42,12 +42,25 @@ class ProviderConfig(BaseConfig): def __init__(self): BaseConfig.__init__(self) - def _get_spec(self): + def _get_schema(self): """ - Returns the spec object for the specific configuration + Returns the schema corresponding to the version given. + + :rtype: dict or None if the version is not supported. """ return leap_provider_spec + def _get_spec(self): + """ + Returns the spec object for the specific configuration. + + Override the BaseConfig one because we do not support multiple schemas + for the provider yet. + + :rtype: dict or None if the version is not supported. + """ + return self._get_schema() + def get_api_uri(self): return self._safe_get_value("api_uri") diff --git a/src/leap/gui/login.py b/src/leap/gui/login.py index 3c994597..de0b2d50 100644 --- a/src/leap/gui/login.py +++ b/src/leap/gui/login.py @@ -197,18 +197,18 @@ class LoginWidget(QtGui.QWidget): """ self.ui.lnUser.setEnabled(enabled) self.ui.lnPassword.setEnabled(enabled) - self.ui.btnLogin.setEnabled(enabled) self.ui.chkRemember.setEnabled(enabled) self.ui.cmbProviders.setEnabled(enabled) - def set_cancel(self, enabled=False): + self._set_cancel(not enabled) + + def _set_cancel(self, enabled=False): """ Enables or disables the cancel action in the "log in" process. :param enabled: wether it should be enabled or not :type enabled: bool """ - self.ui.btnLogin.setEnabled(enabled) text = self.tr("Cancel") login_or_cancel = self.cancel_login diff --git a/src/leap/gui/mainwindow.py b/src/leap/gui/mainwindow.py index 73354684..5c7a3928 100644 --- a/src/leap/gui/mainwindow.py +++ b/src/leap/gui/mainwindow.py @@ -101,7 +101,9 @@ class MainWindow(QtGui.QMainWindow): user_stopped_eip = False def __init__(self, quit_callback, - standalone=False, bypass_checks=False): + standalone=False, + openvpn_verb=1, + bypass_checks=False): """ Constructor for the client main window @@ -215,7 +217,7 @@ class MainWindow(QtGui.QMainWindow): self._smtp_bootstrapper.download_config.connect( self._smtp_bootstrapped_stage) - self._vpn = VPN() + self._vpn = VPN(openvpn_verb=openvpn_verb) self._vpn.qtsigs.state_changed.connect( self._status_panel.update_vpn_state) self._vpn.qtsigs.status_changed.connect( @@ -588,13 +590,7 @@ class MainWindow(QtGui.QMainWindow): Displays the context menu from the tray icon """ - get_action = lambda visible: ( - self.tr("Show Main Window"), - self.tr("Hide Main Window"))[int(visible)] - - # set labels - visible = self.isVisible() - self._action_visible.setText(get_action(visible)) + self._update_hideshow_menu() context_menu = self._systray.contextMenu() if not IS_MAC: @@ -604,6 +600,19 @@ class MainWindow(QtGui.QMainWindow): # this works however. context_menu.exec_(self._systray.geometry().center()) + def _update_hideshow_menu(self): + """ + Updates the Hide/Show main window menu text based on the + visibility of the window. + """ + get_action = lambda visible: ( + self.tr("Show Main Window"), + self.tr("Hide Main Window"))[int(visible)] + + # set labels + visible = self.isVisible() + self._action_visible.setText(get_action(visible)) + def _toggle_visible(self): """ SLOT @@ -617,6 +626,8 @@ class MainWindow(QtGui.QMainWindow): else: self.hide() + self._update_hideshow_menu() + def _center_window(self): """ Centers the mainwindow based on the desktop geometry @@ -803,7 +814,6 @@ class MainWindow(QtGui.QMainWindow): self._login_widget.set_status(self.tr("Logging in..."), error=False) self._login_widget.set_enabled(False) - self._login_widget.set_cancel(True) if self._login_widget.get_remember() and has_keyring(): # in the keyring and in the settings @@ -831,7 +841,6 @@ class MainWindow(QtGui.QMainWindow): Stops the login sequence. """ logger.debug("Cancelling log in.") - self._login_widget.set_cancel(False) if self._download_provider_defer: logger.debug("Cancelling download provider defer.") @@ -1091,6 +1100,7 @@ class MainWindow(QtGui.QMainWindow): self._status_panel.eip_started() + # XXX refactor into status_panel method? self._action_eip_startstop.setText(self.tr("Turn OFF")) self._action_eip_startstop.disconnect(self) self._action_eip_startstop.triggered.connect( @@ -1254,29 +1264,34 @@ class MainWindow(QtGui.QMainWindow): loaded """ leap_assert(self._eip_config, "We need an eip config!") + passed = data[self._eip_bootstrapper.PASSED_KEY] + + if not passed: + error_msg = self.tr("There was a problem with the provider") + self._status_panel.set_eip_status(error_msg, error=True) + logger.error(data[self._eip_bootstrapper.ERROR_KEY]) + self._already_started_eip = False + return provider_config = self._get_best_provider_config() domain = provider_config.get_domain() - if data[self._eip_bootstrapper.PASSED_KEY] and \ - (self._eip_config.loaded() or - self._eip_config.load(os.path.join("leap", - "providers", - domain, - "eip-service.json"))): - self._start_eip() + loaded = self._eip_config.loaded() + if not loaded: + eip_config_path = os.path.join("leap", "providers", + domain, "eip-service.json") + api_version = provider_config.get_api_version() + self._eip_config.set_api_version(api_version) + loaded = self._eip_config.load(eip_config_path) + + if loaded: + self._start_eip() else: - if data[self._eip_bootstrapper.PASSED_KEY]: - self._status_panel.set_eip_status( - self.tr("Could not load Encrypted Internet " - "Configuration."), - error=True) - else: - self._status_panel.set_eip_status( - data[self._eip_bootstrapper.ERROR_KEY], - error=True) - self._already_started_eip = False + self._status_panel.set_eip_status( + self.tr("Could not load Encrypted Internet " + "Configuration."), + error=True) def _logout(self): """ @@ -1319,7 +1334,6 @@ class MainWindow(QtGui.QMainWindow): """ passed = data[self._provider_bootstrapper.PASSED_KEY] if not passed: - self._login_widget.set_cancel(False) self._login_widget.set_enabled(True) self._login_widget.set_status( self.tr("Unable to connect: Problem with provider")) diff --git a/src/leap/services/eip/eipbootstrapper.py b/src/leap/services/eip/eipbootstrapper.py index 60270b5b..1d7bc342 100644 --- a/src/leap/services/eip/eipbootstrapper.py +++ b/src/leap/services/eip/eipbootstrapper.py @@ -67,7 +67,9 @@ class EIPBootstrapper(AbstractBootstrapper): logger.debug("Downloading EIP config for %s" % (self._provider_config.get_domain(),)) + api_version = self._provider_config.get_api_version() self._eip_config = EIPConfig() + self._eip_config.set_api_version(api_version) headers = {} mtime = get_mtime(os.path.join(self._eip_config @@ -84,7 +86,7 @@ class EIPBootstrapper(AbstractBootstrapper): # it's in 1/config/eip, config/eip and config/1/eip... config_uri = "%s/%s/config/eip-service.json" % ( self._provider_config.get_api_uri(), - self._provider_config.get_api_version()) + api_version) logger.debug('Downloading eip config from: %s' % config_uri) res = self._session.get(config_uri, diff --git a/src/leap/services/eip/eipconfig.py b/src/leap/services/eip/eipconfig.py index 9e3a9b29..d69e1fd8 100644 --- a/src/leap/services/eip/eipconfig.py +++ b/src/leap/services/eip/eipconfig.py @@ -28,7 +28,7 @@ import ipaddr from leap.common.check import leap_assert, leap_assert_type from leap.common.config.baseconfig import BaseConfig from leap.config.providerconfig import ProviderConfig -from leap.services.eip.eipspec import eipservice_config_spec +from leap.services.eip.eipspec import get_schema logger = logging.getLogger(__name__) @@ -136,12 +136,15 @@ class EIPConfig(BaseConfig): def __init__(self): BaseConfig.__init__(self) + self._api_version = None - def _get_spec(self): + def _get_schema(self): """ - Returns the spec object for the specific configuration + Returns the schema corresponding to the version given. + + :rtype: dict or None if the version is not supported. """ - return eipservice_config_spec + return get_schema(self._api_version) def get_clusters(self): # TODO: create an abstraction for clusters @@ -243,7 +246,7 @@ if __name__ == "__main__": console.setFormatter(formatter) logger.addHandler(console) - eipconfig = EIPConfig() + eipconfig = EIPConfig('1') try: eipconfig.get_clusters() diff --git a/src/leap/services/eip/eipspec.py b/src/leap/services/eip/eipspec.py index 94ba674f..9cc56be3 100644 --- a/src/leap/services/eip/eipspec.py +++ b/src/leap/services/eip/eipspec.py @@ -15,7 +15,15 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. -eipservice_config_spec = { + +# Schemas dict +# To add a schema for a version you should follow the form: +# { '1': schema_v1, '2': schema_v2, ... etc } +# so for instance, to add the '2' version, you should do: +# eipservice_config_spec['2'] = schema_v2 +eipservice_config_spec = {} + +eipservice_config_spec['1'] = { 'description': 'sample eip service config', 'type': 'object', 'properties': { @@ -63,3 +71,15 @@ eipservice_config_spec = { } } } + + +def get_schema(version): + """ + Returns the schema corresponding to the version given. + + :param version: the version of the schema to get. + :type version: str + :rtype: dict or None if the version is not supported. + """ + schema = eipservice_config_spec.get(version, None) + return schema diff --git a/src/leap/services/eip/tests/test_eipconfig.py b/src/leap/services/eip/tests/test_eipconfig.py index 8b746b78..87ce04c2 100644 --- a/src/leap/services/eip/tests/test_eipconfig.py +++ b/src/leap/services/eip/tests/test_eipconfig.py @@ -117,18 +117,21 @@ class EIPConfigTest(BaseLeapTest): conf.write(json.dumps(data)) conf.close() - def _get_eipconfig(self, fromfile=True, data=sample_config): + def _get_eipconfig(self, fromfile=True, data=sample_config, api_ver='1'): """ Helper that returns an EIPConfig object using the data parameter or a sample data. :param fromfile: sets if we should use a file or a string - :fromfile type: bool + :type fromfile: bool :param data: sets the data to be used to load in the EIPConfig object - :data type: dict (valid json) + :type data: dict (valid json) + :param api_ver: the api_version schema to use. + :type api_ver: str :rtype: EIPConfig """ config = EIPConfig() + config.set_api_version(api_ver) loaded = False if fromfile: @@ -308,6 +311,14 @@ class EIPConfigTest(BaseLeapTest): with self.assertRaises(AssertionError): config.get_client_cert_path(provider_config) + def test_fails_without_api_set(self): + config = EIPConfig() + with self.assertRaises(AssertionError): + config.load('non-relevant-path') + + def test_fails_with_api_without_schema(self): + with self.assertRaises(AssertionError): + self._get_eipconfig(api_ver='123') if __name__ == "__main__": unittest.main() diff --git a/src/leap/services/eip/vpnlaunchers.py b/src/leap/services/eip/vpnlaunchers.py index 0151c1c6..dadbf859 100644 --- a/src/leap/services/eip/vpnlaunchers.py +++ b/src/leap/services/eip/vpnlaunchers.py @@ -352,7 +352,7 @@ class LinuxVPNLauncher(VPNLauncher): return None def get_vpn_command(self, eipconfig=None, providerconfig=None, - socket_host=None, socket_port="unix"): + socket_host=None, socket_port="unix", openvpn_verb=1): """ Returns the platform dependant vpn launching command. It will look for openvpn in the regular paths and algo in @@ -375,6 +375,9 @@ class LinuxVPNLauncher(VPNLauncher): socket, or port otherwise :type socket_port: str + :param openvpn_verb: openvpn verbosity wanted + :type openvpn_verb: int + :return: A VPN command ready to be launched :rtype: list """ @@ -404,7 +407,7 @@ class LinuxVPNLauncher(VPNLauncher): args.append(openvpn) openvpn = first(pkexec) - # TODO: handle verbosity + args += ['--verb', '%d' % (openvpn_verb,)] gateway_selector = VPNGatewaySelector(eipconfig) gateways = gateway_selector.get_gateways() @@ -604,7 +607,7 @@ class DarwinVPNLauncher(VPNLauncher): return self.COCOASUDO, args def get_vpn_command(self, eipconfig=None, providerconfig=None, - socket_host=None, socket_port="unix"): + socket_host=None, socket_port="unix", openvpn_verb=1): """ Returns the platform dependant vpn launching command @@ -623,6 +626,9 @@ class DarwinVPNLauncher(VPNLauncher): socket, or port otherwise :type socket_port: str + :param openvpn_verb: openvpn verbosity wanted + :type openvpn_verb: int + :return: A VPN command ready to be launched :rtype: list """ @@ -651,7 +657,7 @@ class DarwinVPNLauncher(VPNLauncher): openvpn = first(openvpn_possibilities) args = [openvpn] - # TODO: handle verbosity + args += ['--verb', '%d' % (openvpn_verb,)] gateway_selector = VPNGatewaySelector(eipconfig) gateways = gateway_selector.get_gateways() @@ -768,9 +774,10 @@ class WindowsVPNLauncher(VPNLauncher): OPENVPN_BIN = 'openvpn_leap.exe' # XXX UPDOWN_FILES ... we do not have updown files defined yet! + # (and maybe we won't) def get_vpn_command(self, eipconfig=None, providerconfig=None, - socket_host=None, socket_port="9876"): + socket_host=None, socket_port="9876", openvpn_verb=1): """ Returns the platform dependant vpn launching command. It will look for openvpn in the regular paths and algo in @@ -780,14 +787,20 @@ class WindowsVPNLauncher(VPNLauncher): :param eipconfig: eip configuration object :type eipconfig: EIPConfig + :param providerconfig: provider specific configuration :type providerconfig: ProviderConfig + :param socket_host: either socket path (unix) or socket IP :type socket_host: str + :param socket_port: either string "unix" if it's a unix socket, or port otherwise :type socket_port: str + :param openvpn_verb: the openvpn verbosity wanted + :type openvpn_verb: int + :return: A VPN command ready to be launched :rtype: list """ @@ -810,8 +823,7 @@ class WindowsVPNLauncher(VPNLauncher): openvpn = first(openvpn_possibilities) args = [] - - # TODO: handle verbosity + args += ['--verb', '%d' % (openvpn_verb,)] gateway_selector = VPNGatewaySelector(eipconfig) gateways = gateway_selector.get_gateways() @@ -903,6 +915,7 @@ if __name__ == "__main__": vpnlauncher = get_platform_launcher() eipconfig = EIPConfig() + eipconfig.set_api_version('1') if eipconfig.load("leap/providers/bitmask.net/eip-service.json"): provider = ProviderConfig() if provider.load("leap/providers/bitmask.net/provider.json"): diff --git a/src/leap/services/eip/vpnprocess.py b/src/leap/services/eip/vpnprocess.py index c4bdb30c..5b07a3cf 100644 --- a/src/leap/services/eip/vpnprocess.py +++ b/src/leap/services/eip/vpnprocess.py @@ -80,7 +80,9 @@ class VPN(object): TERMINATE_MAXTRIES = 10 TERMINATE_WAIT = 1 # secs - def __init__(self): + OPENVPN_VERB = "openvpn_verb" + + def __init__(self, **kwargs): """ Instantiate empty attributes and get a copy of a QObject containing the QSignals that we will pass along @@ -92,6 +94,8 @@ class VPN(object): self._reactor = reactor self._qtsigs = VPNSignals() + self._openvpn_verb = kwargs.get(self.OPENVPN_VERB, None) + @property def qtsigs(self): return self._qtsigs @@ -108,9 +112,12 @@ class VPN(object): """ self._stop_pollers() kwargs['qtsigs'] = self.qtsigs + kwargs['openvpn_verb'] = self._openvpn_verb # start the main vpn subprocess vpnproc = VPNProcess(*args, **kwargs) + #qtsigs=self.qtsigs, + #openvpn_verb=self._openvpn_verb) if vpnproc.get_openvpn_process(): logger.info("Another vpn process is running. Will try to stop it.") @@ -566,7 +573,12 @@ class VPNManager(object): # we should check that cmdline BEGINS # with openvpn or with our wrapper # (pkexec / osascript / whatever) - if "openvpn" in ' '.join(p.cmdline): + + # This needs more work, see #3268, but for the moment + # we need to be able to filter out arguments in the form + # --openvpn-foo, since otherwise we are shooting ourselves + # in the feet. + if any(map(lambda s: s.startswith("openvpn"), p.cmdline)): openvpn_process = p break except psutil.error.AccessDenied: @@ -645,7 +657,7 @@ class VPNProcess(protocol.ProcessProtocol, VPNManager): """ def __init__(self, eipconfig, providerconfig, socket_host, socket_port, - qtsigs): + qtsigs, openvpn_verb): """ :param eipconfig: eip configuration object :type eipconfig: EIPConfig @@ -663,6 +675,10 @@ class VPNProcess(protocol.ProcessProtocol, VPNManager): :param qtsigs: a QObject containing the Qt signals used to notify the UI. :type qtsigs: QObject + + :param openvpn_verb: the desired level of verbosity in the + openvpn invocation + :type openvpn_verb: int """ VPNManager.__init__(self, qtsigs=qtsigs) leap_assert_type(eipconfig, EIPConfig) @@ -682,6 +698,8 @@ class VPNProcess(protocol.ProcessProtocol, VPNManager): self._last_status = None self._alive = False + self._openvpn_verb = openvpn_verb + # processProtocol methods def connectionMade(self): @@ -757,7 +775,8 @@ class VPNProcess(protocol.ProcessProtocol, VPNManager): eipconfig=self._eipconfig, providerconfig=self._providerconfig, socket_host=self._socket_host, - socket_port=self._socket_port) + socket_port=self._socket_port, + openvpn_verb=self._openvpn_verb) return map(str, cmd) # shutdown diff --git a/src/leap/services/mail/smtpbootstrapper.py b/src/leap/services/mail/smtpbootstrapper.py index e8af5349..48040035 100644 --- a/src/leap/services/mail/smtpbootstrapper.py +++ b/src/leap/services/mail/smtpbootstrapper.py @@ -72,10 +72,12 @@ class SMTPBootstrapper(AbstractBootstrapper): if self._download_if_needed and mtime: headers['if-modified-since'] = mtime + api_version = self._provider_config.get_api_version() + # there is some confusion with this uri, config_uri = "%s/%s/config/smtp-service.json" % ( - self._provider_config.get_api_uri(), - self._provider_config.get_api_version()) + self._provider_config.get_api_uri(), api_version) + logger.debug('Downloading SMTP config from: %s' % config_uri) srp_auth = SRPAuth(self._provider_config) @@ -91,6 +93,8 @@ class SMTPBootstrapper(AbstractBootstrapper): cookies=cookies) res.raise_for_status() + self._smtp_config.set_api_version(api_version) + # Not modified if res.status_code == 304: logger.debug("SMTP definition has not been modified") diff --git a/src/leap/services/mail/smtpconfig.py b/src/leap/services/mail/smtpconfig.py index 30371005..ea0f9c37 100644 --- a/src/leap/services/mail/smtpconfig.py +++ b/src/leap/services/mail/smtpconfig.py @@ -21,7 +21,7 @@ SMTP configuration import logging from leap.common.config.baseconfig import BaseConfig -from leap.services.mail.smtpspec import smtp_config_spec +from leap.services.mail.smtpspec import get_schema logger = logging.getLogger(__name__) @@ -34,11 +34,13 @@ class SMTPConfig(BaseConfig): def __init__(self): BaseConfig.__init__(self) - def _get_spec(self): + def _get_schema(self): """ - Returns the spec object for the specific configuration + Returns the schema corresponding to the version given. + + :rtype: dict or None if the version is not supported. """ - return smtp_config_spec + return get_schema(self._api_version) def get_hosts(self): return self._safe_get_value("hosts") diff --git a/src/leap/services/mail/smtpspec.py b/src/leap/services/mail/smtpspec.py index 270dfb76..9fc1984a 100644 --- a/src/leap/services/mail/smtpspec.py +++ b/src/leap/services/mail/smtpspec.py @@ -15,7 +15,14 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. -smtp_config_spec = { +# Schemas dict +# To add a schema for a version you should follow the form: +# { '1': schema_v1, '2': schema_v2, ... etc } +# so for instance, to add the '2' version, you should do: +# eipservice_config_spec['2'] = schema_v2 +smtp_config_spec = {} + +smtp_config_spec['1'] = { 'description': 'sample smtp service config', 'type': 'object', 'properties': { @@ -49,3 +56,15 @@ smtp_config_spec = { } } } + + +def get_schema(version): + """ + Returns the schema corresponding to the version given. + + :param version: the version of the schema to get. + :type version: str + :rtype: dict or None if the version is not supported. + """ + schema = smtp_config_spec.get(version, None) + return schema |