summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--changes/feature_add_vpn_state1
-rw-r--r--changes/feature_standalone2
-rw-r--r--src/leap/app.py3
-rw-r--r--src/leap/config/baseconfig.py20
-rw-r--r--src/leap/config/leapsettings.py209
-rw-r--r--src/leap/config/prefixers.py2
-rw-r--r--src/leap/gui/mainwindow.py74
-rw-r--r--src/leap/services/eip/vpn.py7
-rw-r--r--src/leap/services/eip/vpnlaunchers.py46
-rw-r--r--src/leap/util/leap_argparse.py4
10 files changed, 319 insertions, 49 deletions
diff --git a/changes/feature_add_vpn_state b/changes/feature_add_vpn_state
new file mode 100644
index 00000000..8a358e30
--- /dev/null
+++ b/changes/feature_add_vpn_state
@@ -0,0 +1 @@
+ o Add handling for ASSIGN_IP state from OpenVPN in the mainwindow. \ No newline at end of file
diff --git a/changes/feature_standalone b/changes/feature_standalone
new file mode 100644
index 00000000..89ea1167
--- /dev/null
+++ b/changes/feature_standalone
@@ -0,0 +1,2 @@
+ o Support standalone configurations for distribution in thumbdrives
+ and the like. \ No newline at end of file
diff --git a/src/leap/app.py b/src/leap/app.py
index 9f4d4614..14d3c69c 100644
--- a/src/leap/app.py
+++ b/src/leap/app.py
@@ -42,6 +42,7 @@ def main():
_, opts = leap_argparse.init_leapc_args()
debug = opts.debug
+ standalone = opts.standalone
# TODO: get severity from command line args
if debug:
@@ -93,7 +94,7 @@ def main():
# TODO: check if the leap-client is already running and quit
# gracefully in that case.
- window = MainWindow()
+ window = MainWindow(standalone)
window.show()
# This dummy timer ensures that control is given to the outside
diff --git a/src/leap/config/baseconfig.py b/src/leap/config/baseconfig.py
index c497d156..f5c07184 100644
--- a/src/leap/config/baseconfig.py
+++ b/src/leap/config/baseconfig.py
@@ -41,6 +41,16 @@ class BaseConfig:
__metaclass__ = ABCMeta
+ """
+ Standalone is a class wide parameter
+
+ @param standalone: if True it will return the prefix for a
+ standalone application. Otherwise, it will return the system
+ default for configuration storage.
+ @type standalone: bool
+ """
+ standalone = False
+
def __init__(self):
self._data = {}
self._config_checker = None
@@ -62,16 +72,13 @@ class BaseConfig:
leap_assert(self._config_checker, "Load the config first")
return self._config_checker.config[key]
- def get_path_prefix(self, standalone=False):
+ def get_path_prefix(self):
"""
Returns the platform dependant path prefixer
- @param standalone: if True it will return the prefix for a
- standalone application. Otherwise, it will return the system
- default for configuration storage.
- @type standalone: bool
"""
- return get_platform_prefixer().get_path_prefix(standalone=standalone)
+ return get_platform_prefixer().get_path_prefix(
+ standalone=self.standalone)
def loaded(self):
"""
@@ -113,7 +120,6 @@ class BaseConfig:
@return: True if loaded from disk correctly, False otherwise
"""
- # TODO: retrieve standalone option from app-level config
config_path = os.path.join(self.get_path_prefix(),
path)
diff --git a/src/leap/config/leapsettings.py b/src/leap/config/leapsettings.py
new file mode 100644
index 00000000..35c9fef6
--- /dev/null
+++ b/src/leap/config/leapsettings.py
@@ -0,0 +1,209 @@
+# -*- coding: utf-8 -*-
+# leapsettings.py
+# Copyright (C) 2013 LEAP
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+"""
+QSettings abstraction
+"""
+import os
+import logging
+
+from PySide import QtCore
+
+from leap.common.check import leap_assert, leap_assert_type
+from leap.config.prefixers import get_platform_prefixer
+
+logger = logging.getLogger(__name__)
+
+
+def to_bool(val):
+ """
+ Returns the boolean value corresponding to val. Will return False
+ in case val is not a string or something that behaves like one.
+
+ @param val: value to cast
+ @type val: either bool already or str
+
+ @rtype: bool
+ """
+ if isinstance(val, bool):
+ return val
+
+ bool_val = False
+ try:
+ bool_val = val.lower() == "true"
+ except:
+ pass
+
+ return bool_val
+
+
+class LeapSettings(object):
+ """
+ Leap client QSettings wrapper
+ """
+
+ CONFIG_NAME = "leap.conf"
+
+ # keys
+ GEOMETRY_KEY = "Geometry"
+ WINDOWSTATE_KEY = "WindowState"
+ USER_KEY = "User"
+ AUTOLOGIN_KEY = "AutoLogin"
+ PROPERPROVIDER_KEY = "ProperProvider"
+
+ def __init__(self, standalone=False):
+ """
+ Constructor
+
+ @param standalone: parameter used to define the location of
+ the config
+ @type standalone: bool
+ """
+
+ settings_path = os.path.join(get_platform_prefixer()
+ .get_path_prefix(standalone=standalone),
+ "leap",
+ self.CONFIG_NAME)
+ self._settings = QtCore.QSettings(settings_path,
+ QtCore.QSettings.IniFormat)
+
+ def get_geometry(self):
+ """
+ Returns the saved geometry or None if it wasn't saved
+
+ @rtype: bytearray or None
+ """
+ return self._settings.value(self.GEOMETRY_KEY, None)
+
+ def set_geometry(self, geometry):
+ """
+ Saves the geometry to the settings
+
+ @param geometry: bytearray representing the geometry
+ @type geometry: bytearray
+ """
+ leap_assert(geometry, "We need a geometry")
+ self._settings.setValue(self.GEOMETRY_KEY, geometry)
+
+ def get_windowstate(self):
+ """
+ Returns the window state or None if it wasn't saved
+
+ @rtype: bytearray or None
+ """
+ return self._settings.value(self.WINDOWSTATE_KEY, None)
+
+ def set_windowstate(self, windowstate):
+ """
+ Saves the window state to the settings
+
+ @param windowstate: bytearray representing the window state
+ @type windowstate: bytearray
+ """
+ leap_assert(windowstate, "We need a window state")
+ self._settings.setValue(self.WINDOWSTATE_KEY, windowstate)
+
+ def get_enabled_services(self, provider):
+ """
+ Returns a list of enabled services for the given provider
+
+ @param provider: provider domain
+ @type provider: str
+
+ @rtype: list of str
+ """
+
+ leap_assert(len(provider) > 0, "We need a nonempty provider")
+ enabled_services = self._settings.value("%s/Services" % (provider,),
+ [])
+ if isinstance(enabled_services, (str, unicode)):
+ enabled_services = enabled_services.split(",")
+
+ return enabled_services
+
+ def set_enabled_services(self, provider, services):
+ """
+ Saves the list of enabled services for the given provider
+
+ @param provider: provider domain
+ @type provider: str
+ @param services: list of services to save
+ @type services: list of str
+ """
+
+ leap_assert(len(provider) > 0, "We need a nonempty provider")
+ leap_assert_type(services, list)
+
+ self._settings.setValue("%s/Services" % (provider,),
+ services)
+
+ def get_user(self):
+ """
+ Returns the configured user to remember, None if there isn't one
+
+ @rtype: str or None
+ """
+ return self._settings.value(self.USER_KEY, None)
+
+ def set_user(self, user):
+ """
+ Saves the user to remember
+
+ @param user: user name to remember
+ @type user: str
+ """
+ leap_assert(len(user) > 0, "We cannot save an empty user")
+ self._settings.setValue(self.USER_KEY, user)
+
+ def get_autologin(self):
+ """
+ Returns True if the app should automatically login, False otherwise
+
+ @rtype: bool
+ """
+ return to_bool(self._settings.value(self.AUTOLOGIN_KEY, False))
+
+ def set_autologin(self, autologin):
+ """
+ Sets whether the app should automatically login
+
+ @param autologin: True if the app should autologin, False otherwise
+ @type autologin: bool
+ """
+ leap_assert_type(autologin, bool)
+ self._settings.setValue(self.AUTOLOGIN_KEY, autologin)
+
+ # TODO: make this scale with multiple providers, we are assuming
+ # just one for now
+ def get_properprovider(self):
+ """
+ Returns True if there is a properly configured provider
+
+ @rtype: bool
+ """
+ return to_bool(self._settings.value(self.PROPERPROVIDER_KEY, False))
+
+ def set_properprovider(self, properprovider):
+ """
+ Sets wether the app should automatically login
+
+ @param properprovider: True if the provider is properly
+ configured, False otherwise
+ @type properprovider: bool
+ """
+ leap_assert_type(properprovider, bool)
+ self._settings.setValue(self.PROPERPROVIDER_KEY, properprovider)
diff --git a/src/leap/config/prefixers.py b/src/leap/config/prefixers.py
index ebcd49e7..5a9b2112 100644
--- a/src/leap/config/prefixers.py
+++ b/src/leap/config/prefixers.py
@@ -74,7 +74,7 @@ class LinuxPrefixer(Prefixer):
config_dir = BaseDirectory.xdg_config_home
if not standalone:
return config_dir
- return os.getenv("LEAP_CLIENT_PATH", config_dir)
+ return os.path.join(os.getcwd(), "config")
class DarwinPrefixer(Prefixer):
diff --git a/src/leap/gui/mainwindow.py b/src/leap/gui/mainwindow.py
index a93b2e3c..3f29f957 100644
--- a/src/leap/gui/mainwindow.py
+++ b/src/leap/gui/mainwindow.py
@@ -28,6 +28,7 @@ from functools import partial
from ui_mainwindow import Ui_MainWindow
from leap.common.check import leap_assert
+from leap.config.leapsettings import LeapSettings
from leap.config.providerconfig import ProviderConfig
from leap.crypto.srpauth import SRPAuth
from leap.services.eip.vpn import VPN
@@ -54,17 +55,17 @@ class MainWindow(QtGui.QMainWindow):
LOGIN_INDEX = 0
EIP_STATUS_INDEX = 1
- # Settings
- GEOMETRY_KEY = "Geometry"
- WINDOWSTATE_KEY = "WindowState"
- USER_KEY = "User"
- AUTOLOGIN_KEY = "AutoLogin"
- PROPER_PROVIDER = "ProperProvider"
-
# Keyring
KEYRING_KEY = "leap_client"
- def __init__(self):
+ def __init__(self, standalone=False):
+ """
+ Constructor for the client main window
+
+ @param standalone: Set to true if the app should use configs
+ inside its pwd
+ @type standalone: bool
+ """
QtGui.QMainWindow.__init__(self)
self.CONNECTING_ICON = QtGui.QPixmap(":/images/conn_connecting.png")
@@ -91,6 +92,9 @@ class MainWindow(QtGui.QMainWindow):
# This is loaded only once, there's a bug when doing that more
# than once
+ ProviderConfig.standalone = standalone
+ EIPConfig.standalone = standalone
+ self._standalone = standalone
self._provider_config = ProviderConfig()
self._eip_config = EIPConfig()
# This is created once we have a valid provider config
@@ -185,6 +189,7 @@ class MainWindow(QtGui.QMainWindow):
self._action_visible.triggered.connect(self._toggle_visible)
self._enabled_services = []
+ self._settings = LeapSettings(standalone)
self._center_window()
self._wizard = None
@@ -201,8 +206,7 @@ class MainWindow(QtGui.QMainWindow):
def _rejected_wizard(self):
if self._wizard_firstrun:
- settings = QtCore.QSettings()
- settings.setValue(self.PROPER_PROVIDER, False)
+ self._settings.set_properprovider(False)
self.quit()
else:
self._finish_init()
@@ -217,7 +221,6 @@ class MainWindow(QtGui.QMainWindow):
self.ui.chkAutoLogin.setEnabled(state == QtCore.Qt.Checked)
def _finish_init(self):
- settings = QtCore.QSettings()
self.ui.cmbProviders.addItems(self._configured_providers())
self._show_systray()
self.show()
@@ -227,9 +230,9 @@ class MainWindow(QtGui.QMainWindow):
possible_password = self._wizard.get_password()
self.ui.chkRemember.setChecked(self._wizard.get_remember())
self._enabled_services = list(self._wizard.get_services())
- settings.setValue("%s/Services" %
- (self.ui.cmbProviders.currentText(),),
- self._enabled_services)
+ self._settings.set_enabled_services(
+ self.ui.cmbProviders.currentText(),
+ self._enabled_services)
if possible_username is not None:
self.ui.lnUser.setText(possible_username)
self._focus_password()
@@ -238,10 +241,10 @@ class MainWindow(QtGui.QMainWindow):
self.ui.chkRemember.setChecked(True)
self._login()
self._wizard = None
- settings.setValue(self.PROPER_PROVIDER, True)
+ self._settings.set_properprovider(True)
else:
- saved_user = settings.value(self.USER_KEY, None)
- auto_login = settings.value(self.AUTOLOGIN_KEY, "false") != "false"
+ saved_user = self._settings.get_user()
+ auto_login = self._settings.get_autologin()
if saved_user is not None:
self.ui.lnUser.setText(saved_user)
@@ -301,9 +304,9 @@ class MainWindow(QtGui.QMainWindow):
"""
Centers the mainwindow based on the desktop geometry
"""
- settings = QtCore.QSettings()
- geometry = settings.value(self.GEOMETRY_KEY, None)
- state = settings.value(self.WINDOWSTATE_KEY, None)
+ geometry = self._settings.get_geometry()
+ state = self._settings.get_windowstate()
+
if geometry is None:
app = QtGui.QApplication.instance()
width = app.desktop().width()
@@ -361,10 +364,11 @@ class MainWindow(QtGui.QMainWindow):
self._toggle_visible()
e.ignore()
return
- settings = QtCore.QSettings()
- settings.setValue(self.GEOMETRY_KEY, self.saveGeometry())
- settings.setValue(self.WINDOWSTATE_KEY, self.saveState())
- settings.setValue(self.AUTOLOGIN_KEY, self.ui.chkAutoLogin.isChecked())
+
+ self._settings.set_geometry(self.saveGeometry())
+ self._settings.set_windowstate(self.saveState())
+ self._settings.set_autologin(self.ui.chkAutoLogin.isChecked())
+
QtGui.QMainWindow.closeEvent(self, e)
def _configured_providers(self):
@@ -394,10 +398,8 @@ class MainWindow(QtGui.QMainWindow):
@rtype: bool
"""
- settings = QtCore.QSettings()
has_provider_on_disk = len(self._configured_providers()) != 0
- is_proper_provider = settings.value(self.PROPER_PROVIDER,
- "false") != "false"
+ is_proper_provider = self._settings.get_properprovider()
return not (has_provider_on_disk and is_proper_provider)
def _focus_password(self):
@@ -507,13 +509,8 @@ class MainWindow(QtGui.QMainWindow):
password = self.ui.lnPassword.text()
provider = self.ui.cmbProviders.currentText()
- settings = QtCore.QSettings()
- self._enabled_services = settings.value(
- "%s/Services" %
- (self.ui.cmbProviders.currentText(),), "")
-
- if isinstance(self._enabled_services, (str, unicode)):
- self._enabled_services = self._enabled_services.split(",")
+ self._enabled_services = self._settings.get_enabled_services(
+ self.ui.cmbProviders.currentText())
if len(provider) == 0:
self._set_status(self.tr("Please select a valid provider"))
@@ -530,8 +527,6 @@ class MainWindow(QtGui.QMainWindow):
self._set_status(self.tr("Logging in..."), error=False)
self._login_set_enabled(False)
- settings = QtCore.QSettings()
-
if self.ui.chkRemember.isChecked():
try:
keyring.set_password(self.KEYRING_KEY,
@@ -539,7 +534,7 @@ class MainWindow(QtGui.QMainWindow):
password.encode("utf8"))
# Only save the username if it was saved correctly in
# the keyring
- settings.setValue(self.USER_KEY, username)
+ self._settings.set_user(username)
except Exception as e:
logger.error("Problem saving data to keyring. %r"
% (e,))
@@ -699,7 +694,8 @@ class MainWindow(QtGui.QMainWindow):
"""
selected_pixmap = self.ERROR_ICON
tray_message = self.tr("Encryption is OFF")
- if status in ("WAIT", "AUTH", "GET_CONFIG", "RECONNECTING"):
+ if status in ("WAIT", "AUTH", "GET_CONFIG",
+ "RECONNECTING", "ASSIGN_IP"):
selected_pixmap = self.CONNECTING_ICON
elif status in ("CONNECTED"):
tray_message = self.tr("Encryption is ON")
@@ -727,6 +723,8 @@ class MainWindow(QtGui.QMainWindow):
self._set_eip_status(self.tr("VPN: Connected!"))
elif status == "WAIT":
self._set_eip_status(self.tr("VPN: Waiting to start..."))
+ elif status == "ASSIGN_IP":
+ self._set_eip_status(self.tr("VPN: Assigning IP"))
elif status == "ALREADYRUNNING":
# Put the following calls in Qt's event queue, otherwise
# the UI won't update properly
diff --git a/src/leap/services/eip/vpn.py b/src/leap/services/eip/vpn.py
index 55ace23b..9d838609 100644
--- a/src/leap/services/eip/vpn.py
+++ b/src/leap/services/eip/vpn.py
@@ -157,7 +157,14 @@ class VPN(QtCore.QThread):
socket_host=socket_host,
socket_port=socket_port)
try:
+ env = QtCore.QProcessEnvironment.systemEnvironment()
+ for key, val in self._launcher.get_vpn_env(providerconfig).items():
+ env.insert(key, val)
+
self._subp = QtCore.QProcess()
+
+ self._subp.setProcessEnvironment(env)
+
self._subp.finished.connect(self.process_finished)
self._subp.finished.connect(self._dump_exitinfo)
self._subp.start(command[:1][0], command[1:])
diff --git a/src/leap/services/eip/vpnlaunchers.py b/src/leap/services/eip/vpnlaunchers.py
index 773b1f92..78db0176 100644
--- a/src/leap/services/eip/vpnlaunchers.py
+++ b/src/leap/services/eip/vpnlaunchers.py
@@ -79,6 +79,20 @@ class VPNLauncher:
"""
return []
+ @abstractmethod
+ def get_vpn_env(self, providerconfig):
+ """
+ Returns a dictionary with the custom env for the platform.
+ This is mainly used for setting LD_LIBRARY_PATH to the correct
+ path when distributing a standalone client
+
+ @param providerconfig: provider specific configuration
+ @type providerconfig: ProviderConfig
+
+ @rtype: dict
+ """
+ return {}
+
def get_platform_launcher():
launcher = globals()[platform.system() + "VPNLauncher"]
@@ -125,7 +139,9 @@ class LinuxVPNLauncher(VPNLauncher):
def get_vpn_command(self, eipconfig=None, providerconfig=None,
socket_host=None, socket_port="unix"):
"""
- Returns the platform dependant vpn launching command
+ Returns the platform dependant vpn launching command. It will
+ look for openvpn in the regular paths and algo in
+ path_prefix/apps/eip/ (in case standalone is set)
Might raise VPNException.
@@ -149,7 +165,16 @@ class LinuxVPNLauncher(VPNLauncher):
leap_assert(socket_host, "We need a socket host!")
leap_assert(socket_port, "We need a socket port!")
- openvpn_possibilities = which(self.OPENVPN_BIN)
+ kwargs = {}
+ if ProviderConfig.standalone:
+ kwargs['path_extension'] = os.path.join(
+ providerconfig.get_path_prefix(),
+ "..", "apps", "eip")
+
+ openvpn_possibilities = which(
+ self.OPENVPN_BIN,
+ **kwargs)
+
if len(openvpn_possibilities) == 0:
raise OpenVPNNotFoundException()
@@ -229,6 +254,23 @@ class LinuxVPNLauncher(VPNLauncher):
return [openvpn] + args
+ def get_vpn_env(self, providerconfig):
+ """
+ Returns a dictionary with the custom env for the platform.
+ This is mainly used for setting LD_LIBRARY_PATH to the correct
+ path when distributing a standalone client
+
+ @param providerconfig: provider specific configuration
+ @type providerconfig: ProviderConfig
+
+ @rtype: dict
+ """
+ leap_assert(providerconfig, "We need a provider config")
+ leap_assert_type(providerconfig, ProviderConfig)
+
+ return {"LD_LIBRARY_PATH": os.path.join(
+ providerconfig.get_path_prefix(),
+ "..", "lib")}
class DarwinVPNLauncher(VPNLauncher):
"""
diff --git a/src/leap/util/leap_argparse.py b/src/leap/util/leap_argparse.py
index 83272a3d..66268f6f 100644
--- a/src/leap/util/leap_argparse.py
+++ b/src/leap/util/leap_argparse.py
@@ -37,6 +37,10 @@ Launches the LEAP Client""", epilog=epilog)
type=int,
action="store", dest="openvpn_verb",
help='verbosity level for openvpn logs [1-6]')
+ parser.add_argument('-s', '--standalone', action="store_true",
+ help='Makes the client use standalone'
+ 'directories for configuration and binary'
+ 'searching')
# Not in use, we might want to reintroduce them.
#parser.add_argument('-i', '--no-provider-checks',