diff options
| -rw-r--r-- | src/leap/app.py | 3 | ||||
| -rw-r--r-- | src/leap/config/baseconfig.py | 20 | ||||
| -rw-r--r-- | src/leap/config/leapsettings.py | 186 | ||||
| -rw-r--r-- | src/leap/config/prefixers.py | 2 | ||||
| -rw-r--r-- | src/leap/gui/mainwindow.py | 69 | ||||
| -rw-r--r-- | src/leap/services/eip/vpn.py | 7 | ||||
| -rw-r--r-- | src/leap/services/eip/vpnlaunchers.py | 38 | ||||
| -rw-r--r-- | src/leap/util/leap_argparse.py | 4 | 
8 files changed, 281 insertions, 48 deletions
| 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..4f12b4f8 --- /dev/null +++ b/src/leap/config/leapsettings.py @@ -0,0 +1,186 @@ +# -*- 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.config.prefixers import get_platform_prefixer +from leap.common.check import leap_assert, leap_assert_type + +logger = logging.getLogger(__name__) + + +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), +                                     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 self._settings.value(self.AUTOLOGIN_KEY, "false") != "false" + +    def set_autologin(self, autologin): +        """ +        Sets wether 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 self._settings.value(self.PROPERPROVIDER_KEY, +                                    "false") != "false" + +    def set_properprovider(self, properprovider): +        """ +        Sets wether the app should automatically login + +        @param autologin: True if the app should autologin, False otherwise +        @type autologin: 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 c65d8f53..557a77ac 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")  if __name__ == "__main__": diff --git a/src/leap/gui/mainwindow.py b/src/leap/gui/mainwindow.py index eabdfe57..703d1e26 100644 --- a/src/leap/gui/mainwindow.py +++ b/src/leap/gui/mainwindow.py @@ -29,6 +29,7 @@ from functools import partial  from ui_mainwindow import Ui_MainWindow  from leap.common.check import leap_assert  from leap.config.providerconfig import ProviderConfig +from leap.config.leapsettings import LeapSettings  from leap.crypto.srpauth import SRPAuth  from leap.services.eip.vpn import VPN  from leap.services.eip.vpnlaunchers import (VPNLauncherException, @@ -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,)) diff --git a/src/leap/services/eip/vpn.py b/src/leap/services/eip/vpn.py index 66b39dd9..4ac7f8a2 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.start(command[:1][0], command[1:])              logger.debug("Waiting for started...") diff --git a/src/leap/services/eip/vpnlaunchers.py b/src/leap/services/eip/vpnlaunchers.py index f9e8e366..c58649b9 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,11 @@ 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) +        openvpn_possibilities = which( +            self.OPENVPN_BIN, +            path_extension=os.path.join(providerconfig.get_path_prefix(), +                                        "..", "apps", "eip")) +          if len(openvpn_possibilities) == 0:              raise OpenVPNNotFoundException() @@ -227,6 +247,20 @@ 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 + +        @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")}  if __name__ == "__main__":      logger = logging.getLogger(name='leap') diff --git a/src/leap/util/leap_argparse.py b/src/leap/util/leap_argparse.py index 83272a3d..66b1a2a5 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('--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', | 
