summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore3
-rw-r--r--Makefile4
-rw-r--r--README.rst6
-rw-r--r--data/images/leap-client.icnsbin0 -> 27456 bytes
-rw-r--r--data/leap_client.pro23
-rw-r--r--data/translations/de.ts218
-rw-r--r--data/ts/en_US.ts257
-rw-r--r--docs/api/leap.base.rst98
-rw-r--r--docs/api/leap.base.tests.rst43
-rw-r--r--docs/api/leap.baseapp.rst75
-rw-r--r--docs/api/leap.certs.rst11
-rw-r--r--docs/api/leap.crypto.rst26
-rw-r--r--docs/api/leap.crypto.tests.rst11
-rw-r--r--docs/api/leap.eip.rst74
-rw-r--r--docs/api/leap.eip.tests.rst43
-rw-r--r--docs/api/leap.gui.firstrun.rst91
-rw-r--r--docs/api/leap.gui.rst75
-rw-r--r--docs/api/leap.gui.tests.rst59
-rw-r--r--docs/api/leap.rst41
-rw-r--r--docs/api/leap.testing.rst43
-rw-r--r--docs/api/leap.util.rst66
-rw-r--r--docs/api/leap.util.tests.rst19
-rw-r--r--docs/api/modules.rst7
-rw-r--r--docs/dev/environment.rst2
-rw-r--r--docs/dev/internationalization.rst23
-rw-r--r--docs/index.rst8
-rw-r--r--docs/testers/howto.rst67
-rwxr-xr-xopenvpn/build.zsh2
-rw-r--r--pkg/dev-reqs.pip4
-rw-r--r--pkg/linux/leap-update-resolv-conf90
-rw-r--r--pkg/osx/Info.plist22
-rw-r--r--pkg/osx/Makefile46
-rw-r--r--pkg/osx/README.rst60
-rw-r--r--pkg/osx/install/ProcessNetworkChanges.plist.template16
-rwxr-xr-xpkg/osx/install/client.down.sh146
-rwxr-xr-xpkg/osx/install/client.up.sh596
-rwxr-xr-xpkg/osx/install/install-leapc.sh17
-rw-r--r--pkg/osx/install/leap-installer.platypus90
-rw-r--r--pkg/osx/leap-client.spec36
-rwxr-xr-xpkg/postmkvenv.sh17
-rw-r--r--pkg/requirements.pip12
-rwxr-xr-xpkg/scripts/leap6
-rw-r--r--pkg/scripts/leap_client_bootstrap.sh50
-rw-r--r--pkg/test-requirements.pip1
-rw-r--r--src/leap/app.py13
-rw-r--r--src/leap/base/auth.py17
-rw-r--r--src/leap/base/checks.py129
-rw-r--r--src/leap/base/config.py5
-rw-r--r--src/leap/base/constants.py3
-rw-r--r--src/leap/base/exceptions.py36
-rw-r--r--src/leap/base/network.py17
-rw-r--r--src/leap/base/pluggableconfig.py18
-rw-r--r--src/leap/base/specs.py6
-rw-r--r--src/leap/base/tests/test_checks.py38
-rw-r--r--src/leap/base/tests/test_providers.py10
-rw-r--r--src/leap/baseapp/dialogs.py9
-rw-r--r--src/leap/baseapp/eip.py25
-rw-r--r--src/leap/baseapp/log.py20
-rw-r--r--src/leap/baseapp/mainwindow.py23
-rw-r--r--src/leap/baseapp/network.py24
-rw-r--r--src/leap/baseapp/systray.py60
-rw-r--r--src/leap/crypto/__init__.py26
-rw-r--r--src/leap/eip/checks.py111
-rw-r--r--src/leap/eip/config.py51
-rw-r--r--src/leap/eip/eipconnection.py8
-rw-r--r--src/leap/eip/exceptions.py57
-rw-r--r--src/leap/eip/openvpnconnection.py80
-rw-r--r--src/leap/gui/firstrun/__init__.py5
-rw-r--r--src/leap/gui/firstrun/connect.py (renamed from src/leap/gui/firstrun/regvalidation.py)60
-rw-r--r--src/leap/gui/firstrun/intro.py2
-rw-r--r--src/leap/gui/firstrun/last.py8
-rw-r--r--src/leap/gui/firstrun/login.py13
-rw-r--r--src/leap/gui/firstrun/providerinfo.py9
-rw-r--r--src/leap/gui/firstrun/providerselect.py11
-rw-r--r--src/leap/gui/firstrun/providersetup.py47
-rw-r--r--src/leap/gui/firstrun/register.py26
-rwxr-xr-xsrc/leap/gui/firstrun/wizard.py12
-rw-r--r--src/leap/gui/progress.py7
-rw-r--r--src/leap/gui/tests/test_firstrun_login.py2
-rw-r--r--src/leap/gui/tests/test_firstrun_register.py2
-rw-r--r--src/leap/gui/tests/test_firstrun_wizard.py6
-rw-r--r--src/leap/soledad/__init__.py5
-rw-r--r--src/leap/soledad/backends/leap_backend.py3
-rw-r--r--src/leap/soledad/tests/test_sqlcipher.py2
-rw-r--r--src/leap/soledad/util.py2
-rw-r--r--src/leap/util/__init__.py9
-rw-r--r--src/leap/util/certs.py18
-rw-r--r--src/leap/util/geo.py32
-rw-r--r--src/leap/util/leap_argparse.py2
-rw-r--r--src/leap/util/misc.py20
-rw-r--r--src/leap/util/tests/test_translations.py22
-rw-r--r--src/leap/util/translations.py84
92 files changed, 3381 insertions, 418 deletions
diff --git a/.gitignore b/.gitignore
index 276f782a..bbe004c4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,7 @@
*.swp
*.swo
*.pyc
+*.log
.*
!.coveragerc
!.tx
@@ -21,4 +22,6 @@ src/leap_client.egg-info
src/leap/_branding.py
src/leap/certs/*.pem
src/*.egg-info
+pkg/osx/dist
+pkg/osx/build
MANIFEST
diff --git a/Makefile b/Makefile
index 5bdf9c36..a8859725 100644
--- a/Makefile
+++ b/Makefile
@@ -1,3 +1,4 @@
+SHELL := /bin/zsh
# ################################
# Makefile for compiling resources
# files.
@@ -62,5 +63,8 @@ deb:
@git tag -a debian/$(DEBVER) -m "..."
@debuild -us -uc -i.git
+apidocs:
+ @sphinx-apidoc -o docs/api src/leap
+
clean :
$(RM) $(COMPILED_UI) $(COMPILED_RESOURCES) $(COMPILED_UI:.py=.pyc) $(COMPILED_RESOURCES:.py=.pyc)
diff --git a/README.rst b/README.rst
index 01e1c151..9ef3f99b 100644
--- a/README.rst
+++ b/README.rst
@@ -15,12 +15,12 @@ You can read the documentation online at `http://leap-client.readthedocs.org <ht
Quick Start
==============
-At the current development stage we still do not have any versioned release. Instead, you might want to have a look at the `testers guide<http://leap-client.readthedocs.org/en/latest/testers/howto.html>`_ for a quick howto on fetching and testing latest development code.
+At the current development stage we still do not have any versioned release. Instead, you might want to have a look at the `testers guide <http://leap-client.readthedocs.org/en/latest/testers/howto.html>`_ for a quick howto on fetching and testing latest development code.
Dependencies
------------------
-Leap client depends on these libraries:
+LEAP Client depends on these libraries:
* ``python 2.6`` or ``2.7``
* ``qt4 libraries``
@@ -73,7 +73,7 @@ After a successful installation, there should be a launcher called ``leap-client
Hacking
=======
-See the `hackers guide<http://leap-client.readthedocs.org/en/latest/dev/environment.html>`_
+See the `hackers guide <http://leap-client.readthedocs.org/en/latest/dev/environment.html>`_.
The LEAP client git repository is available at::
diff --git a/data/images/leap-client.icns b/data/images/leap-client.icns
new file mode 100644
index 00000000..d5d52cdc
--- /dev/null
+++ b/data/images/leap-client.icns
Binary files differ
diff --git a/data/leap_client.pro b/data/leap_client.pro
index 4c559029..57764a23 100644
--- a/data/leap_client.pro
+++ b/data/leap_client.pro
@@ -2,15 +2,20 @@
# is not there a f*** way of expanding this? other to template with python I mean...
-SOURCES += ../src/leap/gui/firstrun/intro.py
-SOURCES += ../src/leap/gui/firstrun/last.py
-SOURCES += ../src/leap/gui/firstrun/login.py
-SOURCES += ../src/leap/gui/firstrun/providerinfo.py
-SOURCES += ../src/leap/gui/firstrun/providerselect.py
-SOURCES += ../src/leap/gui/firstrun/providersetup.py
-SOURCES += ../src/leap/gui/firstrun/register.py
-SOURCES += ../src/leap/gui/firstrun/regvalidation.py
-SOURCES += ../src/leap/gui/firstrun/wizard.py
+SOURCES += ../src/leap/base/exceptions.py
+SOURCES += ../src/leap/eip/exceptions.py
+SOURCES += ../src/leap/baseapp/eip.py
+SOURCES += ../src/leap/baseapp/log.py
+SOURCES += ../src/leap/baseapp/systray.py
+SOURCES += ../src/leap/gui/firstrun/intro.py
+SOURCES += ../src/leap/gui/firstrun/last.py
+SOURCES += ../src/leap/gui/firstrun/login.py
+SOURCES += ../src/leap/gui/firstrun/providerinfo.py
+SOURCES += ../src/leap/gui/firstrun/providerselect.py
+SOURCES += ../src/leap/gui/firstrun/providersetup.py
+SOURCES += ../src/leap/gui/firstrun/register.py
+SOURCES += ../src/leap/gui/firstrun/connect.py
+SOURCES += ../src/leap/gui/firstrun/wizard.py
# where to generate ts files -- tx will pick from here
diff --git a/data/translations/de.ts b/data/translations/de.ts
new file mode 100644
index 00000000..f2ab6fa4
--- /dev/null
+++ b/data/translations/de.ts
@@ -0,0 +1,218 @@
+<?xml version="1.0" ?><!DOCTYPE TS><TS language="de" version="2.0">
+<context>
+ <name>IntroPage</name>
+ <message>
+ <location filename="../src/leap/gui/firstrun/intro.py" line="14"/>
+ <source>First run wizard.</source>
+ <translation>Assistent für erstmaligen Start</translation>
+ </message>
+ <message>
+ <location filename="../src/leap/gui/firstrun/intro.py" line="24"/>
+ <source>Now we will guide you through some configuration that is needed before you can connect for the first time.&lt;br&gt;&lt;br&gt;If you ever need to modify these options again, you can find the wizard in the &apos;&lt;i&gt;Settings&lt;/i&gt;&apos; menu from the main window.&lt;br&gt;&lt;br&gt;Do you want to &lt;b&gt;sign up&lt;/b&gt; for a new account, or &lt;b&gt;log in&lt;/b&gt; with an already existing username?&lt;br&gt;</source>
+ <translation>Wir werden dich nun durch einige Konfigurationen führen, die du für den ersten Start benötigst.&lt;br&gt;&lt;br&gt;Wenn du diese Konfigurationen jemals ändern musst, findest du den Assistenten im &apos;&lt;i&gt;Einstellungen&lt;/i&gt;&apos;-Menü des Haupfensters.&lt;br&gt;&lt;br&gt;Möchtest du dich für einen neuen Account &lt;b&gt;anmelden&lt;/b&gt; oder mit einem bestehenden Usernamen &lt;b&gt;einloggen&lt;/b&gt;?</translation>
+ </message>
+ <message>
+ <location filename="../src/leap/gui/firstrun/intro.py" line="37"/>
+ <source>Sign up for a new account.</source>
+ <translation>Für einen neuen Account anmelden.</translation>
+ </message>
+ <message>
+ <location filename="../src/leap/gui/firstrun/intro.py" line="40"/>
+ <source>Log In with my credentials.</source>
+ <translation>Mit bestehenden Daten einloggen.</translation>
+ </message>
+</context>
+<context>
+ <name>LogInPage</name>
+ <message>
+ <location filename="../src/leap/gui/firstrun/login.py" line="96"/>
+ <source>Username must be in the username@provider form.</source>
+ <translation>Der Username muss in der Form username@provider sein.</translation>
+ </message>
+ <message>
+ <location filename="../src/leap/gui/firstrun/login.py" line="140"/>
+ <source>Resolving domain name</source>
+ <translation>Auflösen des Domain-Namens</translation>
+ </message>
+ <message>
+ <location filename="../src/leap/gui/firstrun/login.py" line="163"/>
+ <source>Authentication error: %s</source>
+ <translation>Authentifizierungsfehler: %s</translation>
+ </message>
+ <message>
+ <location filename="../src/leap/gui/firstrun/login.py" line="179"/>
+ <source>Credentials validated.</source>
+ <translation>Anmeldedaten korrekt.</translation>
+ </message>
+</context>
+<context>
+ <name>ProviderInfoPage</name>
+ <message>
+ <location filename="../src/leap/gui/firstrun/providerinfo.py" line="18"/>
+ <source>Provider Info</source>
+ <translation>Provider-info</translation>
+ </message>
+ <message>
+ <location filename="../src/leap/gui/firstrun/providerinfo.py" line="19"/>
+ <source>This is what provider says.</source>
+ <translation>Das ist, was der Provider sagt.</translation>
+ </message>
+</context>
+<context>
+ <name>ProviderSetupValidationPage</name>
+ <message>
+ <location filename="../src/leap/gui/firstrun/providersetup.py" line="26"/>
+ <source>Provider setup</source>
+ <translation>Provider-Setup</translation>
+ </message>
+ <message>
+ <location filename="../src/leap/gui/firstrun/providersetup.py" line="27"/>
+ <source>Doing autoconfig.</source>
+ <translation>Führe autoconfig durch.</translation>
+ </message>
+ <message>
+ <location filename="../src/leap/gui/firstrun/providersetup.py" line="83"/>
+ <source>Fetching CA certificate</source>
+ <translation>Hole CA-Zertifikat</translation>
+ </message>
+ <message>
+ <location filename="../src/leap/gui/firstrun/providersetup.py" line="105"/>
+ <source>Checking CA fingerprint</source>
+ <translation>Überprüfe CA-Fingerprint</translation>
+ </message>
+ <message>
+ <location filename="../src/leap/gui/firstrun/providersetup.py" line="134"/>
+ <source>Validating api certificate</source>
+ <translation>Überprüfe API-Zertifikat</translation>
+ </message>
+</context>
+<context>
+ <name>RegisterUserPage</name>
+ <message>
+ <location filename="../src/leap/gui/firstrun/register.py" line="31"/>
+ <source>Sign Up</source>
+ <translation>Anmelden</translation>
+ </message>
+ <message>
+ <location filename="../src/leap/gui/firstrun/register.py" line="208"/>
+ <source>Registration succeeded!</source>
+ <translation>Registrierung erfolgreich!</translation>
+ </message>
+ <message>
+ <location filename="../src/leap/gui/firstrun/register.py" line="238"/>
+ <source>Password does not match..</source>
+ <translation>Passwort stimmt nicht überien.</translation>
+ </message>
+ <message>
+ <location filename="../src/leap/gui/firstrun/register.py" line="242"/>
+ <source>Password too short.</source>
+ <translation>Passwort zu kurz</translation>
+ </message>
+ <message>
+ <location filename="../src/leap/gui/firstrun/register.py" line="247"/>
+ <source>Password too obvious.</source>
+ <translation>Passwort zu simpel.</translation>
+ </message>
+ <message>
+ <location filename="../src/leap/gui/firstrun/register.py" line="279"/>
+ <source>Error connecting to provider (timeout)</source>
+ <translation>Verbindungsfehler zu Provider (timeout)</translation>
+ </message>
+ <message>
+ <location filename="../src/leap/gui/firstrun/register.py" line="284"/>
+ <source>Error Connecting to provider (connerr).</source>
+ <translation>Verbindungsfehler zu Provider (connerr)</translation>
+ </message>
+ <message>
+ <location filename="../src/leap/gui/firstrun/register.py" line="292"/>
+ <source>Error during registration (%s)</source>
+ <translation>Fehler während der Registrierung (%s)</translation>
+ </message>
+ <message>
+ <location filename="../src/leap/gui/firstrun/register.py" line="302"/>
+ <source>Could not register (bad response)</source>
+ <translation>Konnte nicht registrieren (bad response)</translation>
+ </message>
+ <message>
+ <location filename="../src/leap/gui/firstrun/register.py" line="309"/>
+ <source>Username not available.</source>
+ <translation>Username nicht verfügbar.</translation>
+ </message>
+ <message>
+ <location filename="../src/leap/gui/firstrun/register.py" line="375"/>
+ <source>Register a new user with provider %s.</source>
+ <translation>Registriere einen neuen User bei Provider %s</translation>
+ </message>
+</context>
+<context>
+ <name>RegisterUserValidationPage</name>
+ <message>
+ <location filename="../src/leap/gui/firstrun/regvalidation.py" line="95"/>
+ <source>Fetching provider config...</source>
+ <translation>Hole Provider-Konfiguration...</translation>
+ </message>
+ <message>
+ <location filename="../src/leap/gui/firstrun/regvalidation.py" line="112"/>
+ <source>Authentication error: %s</source>
+ <translation>Authentifizierungsfehler: %s</translation>
+ </message>
+ <message>
+ <location filename="../src/leap/gui/firstrun/regvalidation.py" line="117"/>
+ <source>Fetching eip certificate</source>
+ <translation>Hole EIP-Zertifikat</translation>
+ </message>
+</context>
+<context>
+ <name>SelectProviderPage</name>
+ <message>
+ <location filename="../src/leap/gui/firstrun/providerselect.py" line="32"/>
+ <source>Enter Provider</source>
+ <translation>Gib Provider ein</translation>
+ </message>
+ <message>
+ <location filename="../src/leap/gui/firstrun/providerselect.py" line="33"/>
+ <source>Please enter the domain of the provider you want to use for your connection.</source>
+ <translation>Bitte gib die Domain des Providers an, den du für deine Verbindung nutzen möchtest.</translation>
+ </message>
+ <message>
+ <location filename="../src/leap/gui/firstrun/providerselect.py" line="87"/>
+ <source>chec&amp;k!</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../src/leap/gui/firstrun/providerselect.py" line="97"/>
+ <source>Server certificate could not be verified.</source>
+ <translation>Server-Zertifikat konnte nicht bestätigt werden.</translation>
+ </message>
+ <message>
+ <location filename="../src/leap/gui/firstrun/providerselect.py" line="136"/>
+ <source>Certificate validation</source>
+ <translation>Zertifikatsüberprüfung</translation>
+ </message>
+ <message>
+ <location filename="../src/leap/gui/firstrun/providerselect.py" line="222"/>
+ <source>checking domain name</source>
+ <translation>Prüfe Domain-Name</translation>
+ </message>
+ <message>
+ <location filename="../src/leap/gui/firstrun/providerselect.py" line="276"/>
+ <source>checking https connection</source>
+ <translation>Prüfe HTTPS-Verbindung</translation>
+ </message>
+ <message>
+ <location filename="../src/leap/gui/firstrun/providerselect.py" line="292"/>
+ <source>Could not get info from provider.</source>
+ <translation>Konnte keine Information vom Provider bekommen.</translation>
+ </message>
+ <message>
+ <location filename="../src/leap/gui/firstrun/providerselect.py" line="295"/>
+ <source>Could not download provider info (refused conn.).</source>
+ <translation>Konnte Provider-Info nicht herunterladen (refused conn.).</translation>
+ </message>
+ <message>
+ <location filename="../src/leap/gui/firstrun/providerselect.py" line="305"/>
+ <source>fetching provider info</source>
+ <translation>Hole Provider-Info</translation>
+ </message>
+</context>
+</TS> \ No newline at end of file
diff --git a/data/ts/en_US.ts b/data/ts/en_US.ts
index f6aadc5c..3fdb38da 100644
--- a/data/ts/en_US.ts
+++ b/data/ts/en_US.ts
@@ -1,6 +1,100 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS><TS version="2.0">
<context>
+ <name>EIPConductorAppMixin</name>
+ <message>
+ <location filename="../src/leap/baseapp/eip.py" line="198"/>
+ <source>&amp;Disconnect</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/leap/baseapp/eip.py" line="212"/>
+ <source>&amp;Connect</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>EIPErrors</name>
+ <message>
+ <location filename="../src/leap/eip/exceptions.py" line="66"/>
+ <source>We could not find any authentication agent in your system.&lt;br/&gt;Make sure you have &lt;b&gt;polkit-gnome-authentication-agent-1&lt;/b&gt; running and try again.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/leap/eip/exceptions.py" line="77"/>
+ <source>We could not find &lt;b&gt;pkexec&lt;/b&gt; in your system.&lt;br/&gt; Do you want to try &lt;b&gt;setuid workaround&lt;/b&gt;? (&lt;i&gt;DOES NOTHING YET&lt;/i&gt;)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/leap/eip/exceptions.py" line="88"/>
+ <source>No suitable openvpn command found. &lt;br/&gt;(Might be a permissions problem)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/leap/eip/exceptions.py" line="97"/>
+ <source>there is a problem with provider certificate</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/leap/eip/exceptions.py" line="104"/>
+ <source>an error occurred during configuratio of leap services</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/leap/eip/exceptions.py" line="111"/>
+ <source>Another OpenVPN Process has been detected.Please close it before starting leap-client</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/leap/eip/exceptions.py" line="119"/>
+ <source>Server does not allow secure connections</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/leap/eip/exceptions.py" line="126"/>
+ <source>Server certificate could not be verified</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/leap/eip/exceptions.py" line="137"/>
+ <source>We could not find your eip certs in the expected path</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>Errors</name>
+ <message>
+ <location filename="../src/leap/base/exceptions.py" line="57"/>
+ <source>Interface not found</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/leap/base/exceptions.py" line="64"/>
+ <source>Looks like your computer is not connected to the internet</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/leap/base/exceptions.py" line="72"/>
+ <source>Looks like there are problems with your internet connection</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/leap/base/exceptions.py" line="80"/>
+ <source>It looks like there is no internet connection.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/leap/base/exceptions.py" line="88"/>
+ <source>Domain cannot be found</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/leap/base/exceptions.py" line="95"/>
+ <source>The Encrypted Connection was lost.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
<name>IntroPage</name>
<message>
<location filename="../src/leap/gui/firstrun/intro.py" line="14"/>
@@ -24,6 +118,24 @@
</message>
</context>
<context>
+ <name>LastPage</name>
+ <message>
+ <location filename="../src/leap/gui/firstrun/last.py" line="18"/>
+ <source>Connecting to Encrypted Internet Proxy service...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/leap/gui/firstrun/last.py" line="54"/>
+ <source>You are now using an encrypted connection!</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/leap/gui/firstrun/last.py" line="58"/>
+ <source>Click &apos;&lt;i&gt;%s&lt;/i&gt;&apos; to end the wizard and save your settings.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
<name>LogInPage</name>
<message>
<location filename="../src/leap/gui/firstrun/login.py" line="96"/>
@@ -45,16 +157,54 @@
<source>Credentials validated.</source>
<translation type="unfinished"></translation>
</message>
+ <message>
+ <location filename="../src/leap/gui/firstrun/login.py" line="24"/>
+ <source>Log In</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/leap/gui/firstrun/login.py" line="25"/>
+ <source>Log in with your credentials.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/leap/gui/firstrun/login.py" line="38"/>
+ <source>User &amp;name:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/leap/gui/firstrun/login.py" line="53"/>
+ <source>&amp;Password:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/leap/gui/firstrun/login.py" line="80"/>
+ <source>Log in</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>LogPaneMixin</name>
+ <message>
+ <location filename="../src/leap/baseapp/log.py" line="24"/>
+ <source>&amp;Connect</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/leap/baseapp/log.py" line="37"/>
+ <source>Disconnected</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>ProviderInfoPage</name>
<message>
- <location filename="../src/leap/gui/firstrun/providerinfo.py" line="18"/>
+ <location filename="../src/leap/gui/firstrun/providerinfo.py" line="19"/>
<source>Provider Info</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/gui/firstrun/providerinfo.py" line="19"/>
+ <location filename="../src/leap/gui/firstrun/providerinfo.py" line="20"/>
<source>This is what provider says.</source>
<translation type="unfinished"></translation>
</message>
@@ -144,6 +294,26 @@
<source>Register a new user with provider %s.</source>
<translation type="unfinished"></translation>
</message>
+ <message>
+ <location filename="../src/leap/gui/firstrun/register.py" line="48"/>
+ <source>User &amp;name:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/leap/gui/firstrun/register.py" line="60"/>
+ <source>&amp;Password:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/leap/gui/firstrun/register.py" line="66"/>
+ <source>Password (again):</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/leap/gui/firstrun/register.py" line="72"/>
+ <source>&amp;Remember username and password.</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>RegisterUserValidationPage</name>
@@ -162,6 +332,16 @@
<source>Fetching eip certificate</source>
<translation type="unfinished"></translation>
</message>
+ <message>
+ <location filename="../src/leap/gui/firstrun/regvalidation.py" line="34"/>
+ <source>Connecting...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/leap/gui/firstrun/regvalidation.py" line="36"/>
+ <source>Checking connection with provider.</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>SelectProviderPage</name>
@@ -215,5 +395,78 @@
<source>fetching provider info</source>
<translation type="unfinished"></translation>
</message>
+ <message>
+ <location filename="../src/leap/gui/firstrun/providerselect.py" line="103"/>
+ <source>&amp;Trust this provider certificate.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/leap/gui/firstrun/providerselect.py" line="346"/>
+ <source>Do you want to &lt;b&gt;trust this provider certificate?&lt;/b&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/leap/gui/firstrun/providerselect.py" line="349"/>
+ <source>SHA-256 fingerprint: &lt;i&gt;%s&lt;/i&gt;&lt;br&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>StatusAwareTrayIconMixin</name>
+ <message>
+ <location filename="../src/leap/baseapp/systray.py" line="78"/>
+ <source>EIP Connection Status</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/leap/baseapp/systray.py" line="87"/>
+ <source>&lt;b&gt;disconnected&lt;/b&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/leap/baseapp/systray.py" line="126"/>
+ <source>Encryption ON turn &amp;off</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/leap/baseapp/systray.py" line="131"/>
+ <source>&amp;Details...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/leap/baseapp/systray.py" line="135"/>
+ <source>&amp;About</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/leap/baseapp/systray.py" line="138"/>
+ <source>About Q&amp;t</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/leap/baseapp/systray.py" line="141"/>
+ <source>&amp;Quit</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/leap/baseapp/systray.py" line="152"/>
+ <source>Encryption ON turn o&amp;ff</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/leap/baseapp/systray.py" line="157"/>
+ <source>Encryption OFF turn &amp;on</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/leap/baseapp/systray.py" line="162"/>
+ <source>connecting...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/leap/baseapp/systray.py" line="185"/>
+ <source>About</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
</TS>
diff --git a/docs/api/leap.base.rst b/docs/api/leap.base.rst
new file mode 100644
index 00000000..778836c4
--- /dev/null
+++ b/docs/api/leap.base.rst
@@ -0,0 +1,98 @@
+base Package
+============
+
+:mod:`auth` Module
+------------------
+
+.. automodule:: leap.base.auth
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+:mod:`authentication` Module
+----------------------------
+
+.. automodule:: leap.base.authentication
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+:mod:`checks` Module
+--------------------
+
+.. automodule:: leap.base.checks
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+:mod:`config` Module
+--------------------
+
+.. automodule:: leap.base.config
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+:mod:`connection` Module
+------------------------
+
+.. automodule:: leap.base.connection
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+:mod:`constants` Module
+-----------------------
+
+.. automodule:: leap.base.constants
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+:mod:`exceptions` Module
+------------------------
+
+.. automodule:: leap.base.exceptions
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+:mod:`network` Module
+---------------------
+
+.. automodule:: leap.base.network
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+:mod:`pluggableconfig` Module
+-----------------------------
+
+.. automodule:: leap.base.pluggableconfig
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+:mod:`providers` Module
+-----------------------
+
+.. automodule:: leap.base.providers
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+:mod:`specs` Module
+-------------------
+
+.. automodule:: leap.base.specs
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+Subpackages
+-----------
+
+.. toctree::
+
+ leap.base.tests
+
diff --git a/docs/api/leap.base.tests.rst b/docs/api/leap.base.tests.rst
new file mode 100644
index 00000000..7af035b0
--- /dev/null
+++ b/docs/api/leap.base.tests.rst
@@ -0,0 +1,43 @@
+tests Package
+=============
+
+:mod:`test_auth` Module
+-----------------------
+
+.. automodule:: leap.base.tests.test_auth
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+:mod:`test_checks` Module
+-------------------------
+
+.. automodule:: leap.base.tests.test_checks
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+:mod:`test_config` Module
+-------------------------
+
+.. automodule:: leap.base.tests.test_config
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+:mod:`test_providers` Module
+----------------------------
+
+.. automodule:: leap.base.tests.test_providers
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+:mod:`test_validation` Module
+-----------------------------
+
+.. automodule:: leap.base.tests.test_validation
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
diff --git a/docs/api/leap.baseapp.rst b/docs/api/leap.baseapp.rst
new file mode 100644
index 00000000..f24b4976
--- /dev/null
+++ b/docs/api/leap.baseapp.rst
@@ -0,0 +1,75 @@
+baseapp Package
+===============
+
+:mod:`constants` Module
+-----------------------
+
+.. automodule:: leap.baseapp.constants
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+:mod:`dialogs` Module
+---------------------
+
+.. automodule:: leap.baseapp.dialogs
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+:mod:`eip` Module
+-----------------
+
+.. automodule:: leap.baseapp.eip
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+:mod:`leap_app` Module
+----------------------
+
+.. automodule:: leap.baseapp.leap_app
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+:mod:`log` Module
+-----------------
+
+.. automodule:: leap.baseapp.log
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+:mod:`mainwindow` Module
+------------------------
+
+.. automodule:: leap.baseapp.mainwindow
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+:mod:`network` Module
+---------------------
+
+.. automodule:: leap.baseapp.network
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+:mod:`permcheck` Module
+-----------------------
+
+.. automodule:: leap.baseapp.permcheck
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+:mod:`systray` Module
+---------------------
+
+.. automodule:: leap.baseapp.systray
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
diff --git a/docs/api/leap.certs.rst b/docs/api/leap.certs.rst
new file mode 100644
index 00000000..e9cc2524
--- /dev/null
+++ b/docs/api/leap.certs.rst
@@ -0,0 +1,11 @@
+certs Package
+=============
+
+:mod:`certs` Package
+--------------------
+
+.. automodule:: leap.certs
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
diff --git a/docs/api/leap.crypto.rst b/docs/api/leap.crypto.rst
new file mode 100644
index 00000000..a04e2e1d
--- /dev/null
+++ b/docs/api/leap.crypto.rst
@@ -0,0 +1,26 @@
+crypto Package
+==============
+
+:mod:`certs` Module
+-------------------
+
+.. automodule:: leap.crypto.certs
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+:mod:`leapkeyring` Module
+-------------------------
+
+.. automodule:: leap.crypto.leapkeyring
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+Subpackages
+-----------
+
+.. toctree::
+
+ leap.crypto.tests
+
diff --git a/docs/api/leap.crypto.tests.rst b/docs/api/leap.crypto.tests.rst
new file mode 100644
index 00000000..54ffa62f
--- /dev/null
+++ b/docs/api/leap.crypto.tests.rst
@@ -0,0 +1,11 @@
+tests Package
+=============
+
+:mod:`test_certs` Module
+------------------------
+
+.. automodule:: leap.crypto.tests.test_certs
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
diff --git a/docs/api/leap.eip.rst b/docs/api/leap.eip.rst
new file mode 100644
index 00000000..e418461b
--- /dev/null
+++ b/docs/api/leap.eip.rst
@@ -0,0 +1,74 @@
+eip Package
+===========
+
+:mod:`checks` Module
+--------------------
+
+.. automodule:: leap.eip.checks
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+:mod:`config` Module
+--------------------
+
+.. automodule:: leap.eip.config
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+:mod:`constants` Module
+-----------------------
+
+.. automodule:: leap.eip.constants
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+:mod:`eipconnection` Module
+---------------------------
+
+.. automodule:: leap.eip.eipconnection
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+:mod:`exceptions` Module
+------------------------
+
+.. automodule:: leap.eip.exceptions
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+:mod:`openvpnconnection` Module
+-------------------------------
+
+.. automodule:: leap.eip.openvpnconnection
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+:mod:`specs` Module
+-------------------
+
+.. automodule:: leap.eip.specs
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+:mod:`udstelnet` Module
+-----------------------
+
+.. automodule:: leap.eip.udstelnet
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+Subpackages
+-----------
+
+.. toctree::
+
+ leap.eip.tests
+
diff --git a/docs/api/leap.eip.tests.rst b/docs/api/leap.eip.tests.rst
new file mode 100644
index 00000000..932a074f
--- /dev/null
+++ b/docs/api/leap.eip.tests.rst
@@ -0,0 +1,43 @@
+tests Package
+=============
+
+:mod:`data` Module
+------------------
+
+.. automodule:: leap.eip.tests.data
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+:mod:`test_checks` Module
+-------------------------
+
+.. automodule:: leap.eip.tests.test_checks
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+:mod:`test_config` Module
+-------------------------
+
+.. automodule:: leap.eip.tests.test_config
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+:mod:`test_eipconnection` Module
+--------------------------------
+
+.. automodule:: leap.eip.tests.test_eipconnection
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+:mod:`test_openvpnconnection` Module
+------------------------------------
+
+.. automodule:: leap.eip.tests.test_openvpnconnection
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
diff --git a/docs/api/leap.gui.firstrun.rst b/docs/api/leap.gui.firstrun.rst
new file mode 100644
index 00000000..36470c33
--- /dev/null
+++ b/docs/api/leap.gui.firstrun.rst
@@ -0,0 +1,91 @@
+firstrun Package
+================
+
+:mod:`firstrun` Package
+-----------------------
+
+.. automodule:: leap.gui.firstrun
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+:mod:`intro` Module
+-------------------
+
+.. automodule:: leap.gui.firstrun.intro
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+:mod:`last` Module
+------------------
+
+.. automodule:: leap.gui.firstrun.last
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+:mod:`login` Module
+-------------------
+
+.. automodule:: leap.gui.firstrun.login
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+:mod:`mixins` Module
+--------------------
+
+.. automodule:: leap.gui.firstrun.mixins
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+:mod:`providerinfo` Module
+--------------------------
+
+.. automodule:: leap.gui.firstrun.providerinfo
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+:mod:`providerselect` Module
+----------------------------
+
+.. automodule:: leap.gui.firstrun.providerselect
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+:mod:`providersetup` Module
+---------------------------
+
+.. automodule:: leap.gui.firstrun.providersetup
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+:mod:`register` Module
+----------------------
+
+.. automodule:: leap.gui.firstrun.register
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+:mod:`regvalidation` Module
+---------------------------
+
+.. automodule:: leap.gui.firstrun.regvalidation
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+:mod:`wizard` Module
+--------------------
+
+.. automodule:: leap.gui.firstrun.wizard
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
diff --git a/docs/api/leap.gui.rst b/docs/api/leap.gui.rst
new file mode 100644
index 00000000..a35a7856
--- /dev/null
+++ b/docs/api/leap.gui.rst
@@ -0,0 +1,75 @@
+gui Package
+===========
+
+:mod:`gui` Package
+------------------
+
+.. automodule:: leap.gui
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+:mod:`constants` Module
+-----------------------
+
+.. automodule:: leap.gui.constants
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+:mod:`locale_rc` Module
+-----------------------
+
+.. automodule:: leap.gui.locale_rc
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+:mod:`mainwindow_rc` Module
+---------------------------
+
+.. automodule:: leap.gui.mainwindow_rc
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+:mod:`progress` Module
+----------------------
+
+.. automodule:: leap.gui.progress
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+:mod:`styles` Module
+--------------------
+
+.. automodule:: leap.gui.styles
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+:mod:`threads` Module
+---------------------
+
+.. automodule:: leap.gui.threads
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+:mod:`utils` Module
+-------------------
+
+.. automodule:: leap.gui.utils
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+Subpackages
+-----------
+
+.. toctree::
+
+ leap.gui.firstrun
+ leap.gui.tests
+
diff --git a/docs/api/leap.gui.tests.rst b/docs/api/leap.gui.tests.rst
new file mode 100644
index 00000000..60b0a6ca
--- /dev/null
+++ b/docs/api/leap.gui.tests.rst
@@ -0,0 +1,59 @@
+tests Package
+=============
+
+:mod:`test_firstrun_login` Module
+---------------------------------
+
+.. automodule:: leap.gui.tests.test_firstrun_login
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+:mod:`test_firstrun_providerselect` Module
+------------------------------------------
+
+.. automodule:: leap.gui.tests.test_firstrun_providerselect
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+:mod:`test_firstrun_register` Module
+------------------------------------
+
+.. automodule:: leap.gui.tests.test_firstrun_register
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+:mod:`test_firstrun_wizard` Module
+----------------------------------
+
+.. automodule:: leap.gui.tests.test_firstrun_wizard
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+:mod:`test_mainwindow_rc` Module
+--------------------------------
+
+.. automodule:: leap.gui.tests.test_mainwindow_rc
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+:mod:`test_progress` Module
+---------------------------
+
+.. automodule:: leap.gui.tests.test_progress
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+:mod:`test_threads` Module
+--------------------------
+
+.. automodule:: leap.gui.tests.test_threads
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
diff --git a/docs/api/leap.rst b/docs/api/leap.rst
new file mode 100644
index 00000000..ce715c5d
--- /dev/null
+++ b/docs/api/leap.rst
@@ -0,0 +1,41 @@
+leap Package
+============
+
+:mod:`leap` Package
+-------------------
+
+.. automodule:: leap.__init__
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+:mod:`_version` Module
+----------------------
+
+.. automodule:: leap._version
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+:mod:`app` Module
+-----------------
+
+.. automodule:: leap.app
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+Subpackages
+-----------
+
+.. toctree::
+
+ leap.base
+ leap.baseapp
+ leap.certs
+ leap.crypto
+ leap.eip
+ leap.gui
+ leap.testing
+ leap.util
+
diff --git a/docs/api/leap.testing.rst b/docs/api/leap.testing.rst
new file mode 100644
index 00000000..dfad1b0c
--- /dev/null
+++ b/docs/api/leap.testing.rst
@@ -0,0 +1,43 @@
+testing Package
+===============
+
+:mod:`basetest` Module
+----------------------
+
+.. automodule:: leap.testing.basetest
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+:mod:`https_server` Module
+--------------------------
+
+.. automodule:: leap.testing.https_server
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+:mod:`pyqt` Module
+------------------
+
+.. automodule:: leap.testing.pyqt
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+:mod:`qunittest` Module
+-----------------------
+
+.. automodule:: leap.testing.qunittest
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+:mod:`test_basetest` Module
+---------------------------
+
+.. automodule:: leap.testing.test_basetest
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
diff --git a/docs/api/leap.util.rst b/docs/api/leap.util.rst
new file mode 100644
index 00000000..dbb69ebe
--- /dev/null
+++ b/docs/api/leap.util.rst
@@ -0,0 +1,66 @@
+util Package
+============
+
+:mod:`coroutines` Module
+------------------------
+
+.. automodule:: leap.util.coroutines
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+:mod:`dicts` Module
+-------------------
+
+.. automodule:: leap.util.dicts
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+:mod:`fileutil` Module
+----------------------
+
+.. automodule:: leap.util.fileutil
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+:mod:`leap_argparse` Module
+---------------------------
+
+.. automodule:: leap.util.leap_argparse
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+:mod:`misc` Module
+------------------
+
+.. automodule:: leap.util.misc
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+:mod:`translations` Module
+--------------------------
+
+.. automodule:: leap.util.translations
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+:mod:`web` Module
+-----------------
+
+.. automodule:: leap.util.web
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+Subpackages
+-----------
+
+.. toctree::
+
+ leap.util.tests
+
diff --git a/docs/api/leap.util.tests.rst b/docs/api/leap.util.tests.rst
new file mode 100644
index 00000000..eb6654c4
--- /dev/null
+++ b/docs/api/leap.util.tests.rst
@@ -0,0 +1,19 @@
+tests Package
+=============
+
+:mod:`test_fileutil` Module
+---------------------------
+
+.. automodule:: leap.util.tests.test_fileutil
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+:mod:`test_leap_argparse` Module
+--------------------------------
+
+.. automodule:: leap.util.tests.test_leap_argparse
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
diff --git a/docs/api/modules.rst b/docs/api/modules.rst
new file mode 100644
index 00000000..d49776ae
--- /dev/null
+++ b/docs/api/modules.rst
@@ -0,0 +1,7 @@
+leap
+====
+
+.. toctree::
+ :maxdepth: 4
+
+ leap
diff --git a/docs/dev/environment.rst b/docs/dev/environment.rst
index 55f00d5e..9f70cb04 100644
--- a/docs/dev/environment.rst
+++ b/docs/dev/environment.rst
@@ -96,7 +96,7 @@ Or, if you prefer, you can also `download the official PyQt tarball<http://www.r
Running openvpn without root privileges
---------------------------------------
-In linux, we are using ``policykit`` to be able to run openvpn without run privileges, and a policy file is needed to be installed for that to be possible.
+In linux, we are using ``policykit`` to be able to run openvpn without root privileges, and a policy file is needed to be installed for that to be possible.
The setup script tries to install the policy file when installing the client system-wide, so if you have installed the client in your global site-packages at least once it should have copied this file for you.
If you *only* are running the client from inside a virtualenv, you will need to copy this file by hand::
diff --git a/docs/dev/internationalization.rst b/docs/dev/internationalization.rst
index e6b89dea..1a9af0be 100644
--- a/docs/dev/internationalization.rst
+++ b/docs/dev/internationalization.rst
@@ -38,21 +38,30 @@ tl;dr;::
self.tr('your string')
-for any string that you want to be translated.
+for any string that you want to be translated, as long as the instance derives from ``QObject``.
+
+If you have to translate something that it is not a ``QObject``, use the magic leap ``translate`` method:
+
+
+.. code-block:: python
+
+ from leap.util.translations import translate
+
+ class Foo(object):
+ bar = translate(<Context>, <string>, <comment>)
+
.. Note about this: there seems to be some problems with the .tr method
- on QObjects. Investigate this.
- I still believe we can use a generic _ method which is smart enough to
- fallback to QObject.tr methods or lookup our own tr implementation (for our
- multilungual objects, like in exceptions or provider labels that came from json objects).
+ so the translate method could actually be the preferred thing in all the cases.
+ Still missing what to do for language labels (json-based).
--kali
For i18n maintainers
^^^^^^^^^^^^^^^^^^^^
-You need ``pylupdate4`` for these steps. To get it, in debian::
+You need ``pylupdate4`` and ``lrelease`` for these steps. To get it, in debian::
- $ apt-get install python-qt4-utils
+ $ apt-get install pyqt4-dev-tools qt4-linguist-tools
If you do not already have it, install the ``transifex-client`` from the cheese shop::
diff --git a/docs/index.rst b/docs/index.rst
index 017fa32c..a57e030f 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -78,10 +78,14 @@ API Documentation
If you are looking for a reference to specific classes or functions, you are likely to find it here
+.. I should investigate a bit more how to skip some things, and how to give nice format
+ to the docstrings.
+ Maybe we should not have sphinx-apidocs building everything, but a minimal index of our own.
+
.. note::
- when it's finished, that's it :)
+ when it's a bit more polished, that's it :)
.. toctree::
:maxdepth: 2
- api
+ api/leap
diff --git a/docs/testers/howto.rst b/docs/testers/howto.rst
index e5bf1fa8..31e12419 100644
--- a/docs/testers/howto.rst
+++ b/docs/testers/howto.rst
@@ -26,76 +26,19 @@ First, install all the base dependencies plus git, virtualenv and development fi
Bootstrap script
^^^^^^^^^^^^^^^^
-.. note:: getting latest version of this script.
-
- At some moment we will publish an url from where you can download this script. For now, you can copy and paste this.
-
.. note::
This will fetch the *develop* branch. If you want to test another branch, just change it in the line starting with *pip install...*. Alternatively, bug kali so she add an option branch to a decent script.
.. note::
This script could make use of the after_install hook. Read http://pypi.python.org/pypi/virtualenv/
-Then copy and paste this script somewhere in your path, in the parent folder where you want your testing build to be downloaded. For instance, to `/tmp/leap_client_bootstrap`:
+Download and source the following script in the parent folder where you want your testing build to be downloaded. For instance, to `/tmp/`:
.. code-block:: bash
- :linenos:
-
- #!/bin/bash
-
- # Installs requirements, and
- # clones the latest leap-client
-
- # depends on:
- # openvpn git-core libgnutls-dev python-dev python-qt4 python-setuptools python-virtualenv
-
- # Escape code
- esc=`echo -en "\033"`
-
- # Set colors
- cc_green="${esc}[0;32m"
- cc_yellow="${esc}[0;33m"
- cc_blue="${esc}[0;34m"
- cc_red="${esc}[0;31m"
- cc_normal=`echo -en "${esc}[m\017"`
-
- echo "${cc_yellow}"
- echo "~~~~~~~~~~~~~~~~~~~~~~"
- echo "LEAP "
- echo "client bootstrapping "
- echo "~~~~~~~~~~~~~~~~~~~~~~"
- echo ""
- echo "${cc_green}Creating virtualenv...${cc_normal}"
-
- mkdir leap-client-testbuild
- virtualenv leap-client-testbuild
- source leap-client-testbuild/bin/activate
-
- echo "${cc_green}Installing leap client...${cc_normal}"
-
- # Clone latest git (develop branch)
- # change "develop" for any other branch you want.
-
-
- pip install -e 'git://leap.se/leap_client@develop#egg=leap-client'
-
- cd leap-client-testbuild
-
- # symlink the pyqt libraries to the system libs
- ./src/leap-client/pkg/postmkvenv.sh
-
- echo "${cc_green}leap-client installed! =)"
- echo "${cc_yellow}"
- echo "Launch it with: "
- echo "~~~~~~~~~~~~~~~~~~~~~~"
- echo "bin/leap-client"
- echo "~~~~~~~~~~~~~~~~~~~~~~"
- echo "${cc_normal}"
-
-and then source it::
- $ cd /tmp
- $ source leap_client_bootstrap
+ cd /tmp
+ wget https://raw.github.com/leapcode/leap_client/develop/pkg/scripts/leap_client_bootstrap.sh
+ source leap_client_bootstrap.sh
Tada! If everything went well, you should be able to run the client by typing::
@@ -125,7 +68,7 @@ Config files
If you want to start fresh without config files, just move them. In linux::
- $ mv ~/.config/leap ~/.config/leap.old
+ mv ~/.config/leap ~/.config/leap.old
Pulling latest changes
^^^^^^^^^^^^^^^^^^^^^^
diff --git a/openvpn/build.zsh b/openvpn/build.zsh
index 87c591cc..b36717c1 100755
--- a/openvpn/build.zsh
+++ b/openvpn/build.zsh
@@ -188,4 +188,4 @@ case "$target" in
*)
error "Unknown target: $target"
;;
-esac \ No newline at end of file
+esac
diff --git a/pkg/dev-reqs.pip b/pkg/dev-reqs.pip
new file mode 100644
index 00000000..44799a26
--- /dev/null
+++ b/pkg/dev-reqs.pip
@@ -0,0 +1,4 @@
+ipython
+ipdb
+pdb4qt
+pygeoip
diff --git a/pkg/linux/leap-update-resolv-conf b/pkg/linux/leap-update-resolv-conf
new file mode 100644
index 00000000..a54802e3
--- /dev/null
+++ b/pkg/linux/leap-update-resolv-conf
@@ -0,0 +1,90 @@
+#!/bin/bash
+#
+# Parses options from openvpn to update resolv.conf
+#
+# The only way to enforce that a linux system will not leak DNS
+# queries is to replace /etc/resolv.conf with a file that only
+# has the DNS resolver specified by the VPN.
+#
+# That is what this script does. This is what resolvconf is for,
+# but sadly it does not always work.
+#
+# Example envs set from openvpn:
+# foreign_option_1='dhcp-option DNS 193.43.27.132'
+# foreign_option_2='dhcp-option DNS 193.43.27.133'
+# foreign_option_3='dhcp-option DOMAIN be.bnc.ch'
+#
+
+function up() {
+
+ comment=$(
+cat <<SETVAR
+#
+# This is a temporary resolv.conf set by the LEAP Client in order to
+# strictly enforce that DNS lookups are secured by the VPN.
+#
+# When the LEAP Client quits or the VPN connection it manages is dropped,
+# this file will be replace with the regularly scheduled /etc/resolv.conf
+#
+# If you want custom entries to appear in this file while LEAP is running,
+# put them in /etc/leap/resolv-head or /etc/leap/resolv-tail. These files
+# should only be writable by root.
+#
+
+SETVAR
+)
+
+ if [ -f /etc/leap/resolv-head ] ; then
+ custom_head=$(cat /etc/leap/resolv-head)
+ else
+ custom_head=""
+ fi
+
+ if [ -f /etc/leap/resolv-tail ] ; then
+ custom_tail=$(cat /etc/leap/resolv-tail)
+ else
+ custom_tail=""
+ fi
+
+ for optionname in ${!foreign_option_*} ; do
+ option="${!optionname}"
+ echo $option
+ part1=$(echo "$option" | cut -d " " -f 1)
+ if [ "$part1" == "dhcp-option" ] ; then
+ part2=$(echo "$option" | cut -d " " -f 2)
+ part3=$(echo "$option" | cut -d " " -f 3)
+ if [ "$part2" == "DNS" ] ; then
+ IF_DNS_NAMESERVERS="$IF_DNS_NAMESERVERS $part3"
+ fi
+ if [ "$part2" == "DOMAIN" ] ; then
+ IF_DNS_SEARCH="$IF_DNS_SEARCH $part3"
+ fi
+ fi
+ done
+ R=""
+ for SS in $IF_DNS_SEARCH ; do
+ R="${R}search $SS
+"
+ done
+ for NS in $IF_DNS_NAMESERVERS ; do
+ R="${R}nameserver $NS
+"
+ done
+ mv /etc/resolv.conf /etc/resolv.conf.bak
+ echo "$comment
+$custom_head
+$R
+$custom_tail" > /etc/resolv.conf
+}
+
+function down() {
+ if [ -f /etc/resolv.conf.bak ] ; then
+ unlink /etc/resolv.conf
+ mv /etc/resolv.conf.bak /etc/resolv.conf
+ fi
+}
+
+case $script_type in
+ up) up ;;
+ down) down ;;
+esac
diff --git a/pkg/osx/Info.plist b/pkg/osx/Info.plist
new file mode 100644
index 00000000..e90d920a
--- /dev/null
+++ b/pkg/osx/Info.plist
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleDisplayName</key>
+ <string>leap-client</string>
+ <key>CFBundleExecutable</key>
+ <string>MacOS/app</string>
+ <key>CFBundleIconFile</key>
+ <string>icon-windowed.icns</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundleName</key>
+ <string>leap-client</string>
+ <key>CFBundlePackageType</key>
+ <string>APPL</string>
+ <key>CFBundleShortVersionString</key>
+ <string>1</string>
+ <key>LSBackgroundOnly</key>
+ <false/>
+</dict>
+</plist>
diff --git a/pkg/osx/Makefile b/pkg/osx/Makefile
new file mode 100644
index 00000000..f2520fcf
--- /dev/null
+++ b/pkg/osx/Makefile
@@ -0,0 +1,46 @@
+#WARNING: You need to run this with an activated VIRTUALENV.
+
+OSX = dist/LEAP\ Client.app/Contents/MacOS/
+GITC = `git rev-parse --short HEAD`
+DMG = "dist/leap-client-$(GITC).dmg"
+INST = "dist/LEAP Client installer.app"
+INSTR = "dist/LEAP Client installer.app/Contents/Resources"
+
+pkg : dist trim installer dmg
+
+dist :
+ ~/pyinstaller/pyinstaller.py -w -s leap-client.spec
+ cp -r /opt/local/Library/Frameworks/QtGui.framework/Versions/4/Resources/qt_menu.nib "dist/LEAP Client.app/Contents/Resources"
+ cp Info.plist "dist/LEAP Client.app/Contents/Info.plist"
+ cp ../../data/images/leap-client.icns "dist/LEAP Client.app/Contents/Resources/icon-windowed.icns"
+
+trim:
+ #XXX this should go properly in pyinstaller spec excludes, but going quick'n'dirty
+ rm $(OSX)QtSvg $(OSX)QtXml $(OSX)QtNetwork $(OSX)QtOpenGL $(OSX)Qt3Support $(OSX)QtSql
+
+installer:
+ #XXX need to fix some paths there (binary, etc)
+ platypus -P install/leap-installer.platypus -y $(INST)
+ #XXX should build tuntap extensions ourselves
+ mkdir $(INSTR)/StartupItems
+ mkdir $(INSTR)/Extensions
+ cp -r /opt/local/Library/StartupItems/tun $(INSTR)/StartupItems
+ cp -r /opt/local/Library/StartupItems/tap $(INSTR)/StartupItems
+ cp -r /opt/local/Library/Extensions/tun.kext $(INSTR)/Extensions
+ cp -r /opt/local/Library/Extensions/tap.kext $(INSTR)/Extensions
+ #copy the binary that we have previously built
+ #XXX not building it yet...
+ cp ../../openvpn/build/openvpn.leap $(INSTR)
+ #copy startup scripts
+ cp install/client.up.sh $(INSTR)
+ cp install/client.down.sh $(INSTR)
+ cp install/ProcessNetworkChanges.plist.template $(INSTR)
+ #Finally, copy application bundle...
+ cp -r "dist/LEAP Client.app" $(INSTR)
+
+dmg :
+ rm -f $(DMG)
+ hdiutil create -format UDBZ -srcfolder $(INST) $(DMG)
+
+clean :
+ rm -rf dist/ build/
diff --git a/pkg/osx/README.rst b/pkg/osx/README.rst
new file mode 100644
index 00000000..48d96ffb
--- /dev/null
+++ b/pkg/osx/README.rst
@@ -0,0 +1,60 @@
+environment setup in osx
+========================
+(I rm'd my README by mistake at some point. Re-do).
+
+basically you need this to setup your environment:
+
+# check and consolidate
+
+# install xcode and macports
+# port -v selfupdate
+# port install python26
+# port install python_select # unneeded?
+# port install py26-pyqt4
+# port install py26-twisted
+# port install py26-pip
+# port install py26-virtualenv
+# port install git-core
+# port install gnutls
+# port install platypus
+
+Requirements
+============
+pyinstaller (in ~/pyinstaller)
+platypus (tested with latest macports)
+
+... + install environment as usual,
+ inside virtualenv.
+
+.. note:: there is something missing here, about troubles building gnutls extension,
+ I think I ended by symlinking global install via macports.
+
+Pyinstaller fix for sip api
+---------------------------
+We need a workaround for setting the right sip api.
+Paste this in the top of pyinstaller/support/rthooks/pyi_rth_qt4plugins.py::
+
+ import sip
+ sip.setapi('QString', 2)
+ sip.setapi('QVariant', 2)
+
+See www.pyinstaller.org/wiki/Recipe/PyQtChangeApiVersion.
+
+Building the package
+====================
+
+Building the binary
+-------------------
+We use the scripts in openvpn/build.zsh
+The packaging Makefile is expecting the final binary in the location::
+
+ ../../openvpn/build/openvpn.leap
+
+Running the build
+-----------------
+IMPORTANT: activate the VIRTUALENV FIRST!
+(you will get an import error otherwise)
+
+For running all steps at once::
+
+ make pkg
diff --git a/pkg/osx/install/ProcessNetworkChanges.plist.template b/pkg/osx/install/ProcessNetworkChanges.plist.template
new file mode 100644
index 00000000..faea8dee
--- /dev/null
+++ b/pkg/osx/install/ProcessNetworkChanges.plist.template
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+ <dict>
+ <key>Label</key>
+ <string>net.tunnelblick.openvpn.process-network-changes</string>
+ <key>ProgramArguments</key>
+ <array>
+ <string>${DIR}/process-network-changes</string>
+ </array>
+ <key>WatchPaths</key>
+ <array>
+ <string>/Library/Preferences/SystemConfiguration</string>
+ </array>
+ </dict>
+</plist>
diff --git a/pkg/osx/install/client.down.sh b/pkg/osx/install/client.down.sh
new file mode 100755
index 00000000..47f00ed7
--- /dev/null
+++ b/pkg/osx/install/client.down.sh
@@ -0,0 +1,146 @@
+#!/bin/bash -e
+# Note: must be bash; uses bash-specific tricks
+#
+# ******************************************************************************************************************
+# This Tunnelblick script does everything! It handles TUN and TAP interfaces,
+# pushed configurations and DHCP leases. :)
+#
+# This is the "Down" version of the script, executed after the connection is
+# closed.
+#
+# Created by: Nick Williams (using original code and parts of old Tblk scripts)
+#
+# ******************************************************************************************************************
+
+trap "" TSTP
+trap "" HUP
+trap "" INT
+export PATH="/bin:/sbin:/usr/sbin:/usr/bin"
+
+readonly LOG_MESSAGE_COMMAND=$(basename "${0}")
+
+# Quick check - is the configuration there?
+if ! scutil -w State:/Network/OpenVPN &>/dev/null -t 1 ; then
+ # Configuration isn't there, so we forget it
+ echo "$(date '+%a %b %e %T %Y') *Tunnelblick $LOG_MESSAGE_COMMAND: WARNING: No existing OpenVPN DNS configuration found; not tearing down anything; exiting."
+ exit 0
+fi
+
+# NOTE: This script does not use any arguments passed to it by OpenVPN, so it doesn't shift Tunnelblick options out of the argument list
+
+# Get info saved by the up script
+TUNNELBLICK_CONFIG="$(/usr/sbin/scutil <<-EOF
+ open
+ show State:/Network/OpenVPN
+ quit
+EOF)"
+
+ARG_MONITOR_NETWORK_CONFIGURATION="$(echo "${TUNNELBLICK_CONFIG}" | grep -i '^[[:space:]]*MonitorNetwork :' | sed -e 's/^.*: //g')"
+LEASEWATCHER_PLIST_PATH="$(echo "${TUNNELBLICK_CONFIG}" | grep -i '^[[:space:]]*LeaseWatcherPlistPath :' | sed -e 's/^.*: //g')"
+PSID="$(echo "${TUNNELBLICK_CONFIG}" | grep -i '^[[:space:]]*Service :' | sed -e 's/^.*: //g')"
+SCRIPT_LOG_FILE="$(echo "${TUNNELBLICK_CONFIG}" | grep -i '^[[:space:]]*ScriptLogFile :' | sed -e 's/^.*: //g')"
+# Don't need: ARG_RESTORE_ON_DNS_RESET="$(echo "${TUNNELBLICK_CONFIG}" | grep -i '^[[:space:]]*RestoreOnDNSReset :' | sed -e 's/^.*: //g')"
+# Don't need: ARG_RESTORE_ON_WINS_RESET="$(echo "${TUNNELBLICK_CONFIG}" | grep -i '^[[:space:]]*RestoreOnWINSReset :' | sed -e 's/^.*: //g')"
+# Don't need: PROCESS="$(echo "${TUNNELBLICK_CONFIG}" | grep -i '^[[:space:]]*PID :' | sed -e 's/^.*: //g')"
+# Don't need: ARG_IGNORE_OPTION_FLAGS="$(echo "${TUNNELBLICK_CONFIG}" | grep -i '^[[:space:]]*IgnoreOptionFlags :' | sed -e 's/^.*: //g')"
+ARG_TAP="$(echo "${TUNNELBLICK_CONFIG}" | grep -i '^[[:space:]]*IsTapInterface :' | sed -e 's/^.*: //g')"
+bRouteGatewayIsDhcp="$(echo "${TUNNELBLICK_CONFIG}" | grep -i '^[[:space:]]*RouteGatewayIsDhcp :' | sed -e 's/^.*: //g')"
+
+# @param String message - The message to log
+logMessage()
+{
+ echo "$(date '+%a %b %e %T %Y') *Tunnelblick $LOG_MESSAGE_COMMAND: "${@} >> "${SCRIPT_LOG_FILE}"
+}
+
+trim()
+{
+ echo ${@}
+}
+
+if ${ARG_TAP} ; then
+ if [ "$bRouteGatewayIsDhcp" == "true" ]; then
+ if [ -z "$dev" ]; then
+ logMessage "Cannot configure TAP interface for DHCP without \$dev being defined. Device may not have disconnected properly."
+ else
+ set +e
+ ipconfig set "$dev" NONE 2>/dev/null
+ set -e
+ fi
+ fi
+fi
+
+# Issue warning if the primary service ID has changed
+PSID_CURRENT="$( (scutil | grep Service | sed -e 's/.*Service : //')<<- EOF
+ open
+ show State:/Network/OpenVPN
+ quit
+EOF)"
+if [ "${PSID}" != "${PSID_CURRENT}" ] ; then
+ logMessage "Ignoring change of Network Primary Service from ${PSID} to ${PSID_CURRENT}"
+fi
+
+# Remove leasewatcher
+if ${ARG_MONITOR_NETWORK_CONFIGURATION} ; then
+ launchctl unload "${LEASEWATCHER_PLIST_PATH}"
+ logMessage "Cancelled monitoring of system configuration changes"
+fi
+
+# Restore configurations
+DNS_OLD="$(/usr/sbin/scutil <<-EOF
+ open
+ show State:/Network/OpenVPN/OldDNS
+ quit
+EOF)"
+WINS_OLD="$(/usr/sbin/scutil <<-EOF
+ open
+ show State:/Network/OpenVPN/OldSMB
+ quit
+EOF)"
+TB_NO_SUCH_KEY="<dictionary> {
+ TunnelblickNoSuchKey : true
+}"
+
+if [ "${DNS_OLD}" = "${TB_NO_SUCH_KEY}" ] ; then
+ scutil <<- EOF
+ open
+ remove State:/Network/Service/${PSID}/DNS
+ quit
+EOF
+else
+ scutil <<- EOF
+ open
+ get State:/Network/OpenVPN/OldDNS
+ set State:/Network/Service/${PSID}/DNS
+ quit
+EOF
+fi
+
+if [ "${WINS_OLD}" = "${TB_NO_SUCH_KEY}" ] ; then
+ scutil <<- EOF
+ open
+ remove State:/Network/Service/${PSID}/SMB
+ quit
+EOF
+else
+ scutil <<- EOF
+ open
+ get State:/Network/OpenVPN/OldSMB
+ set State:/Network/Service/${PSID}/SMB
+ quit
+EOF
+fi
+
+logMessage "Restored the DNS and WINS configurations"
+
+# Remove our system configuration data
+scutil <<- EOF
+ open
+ remove State:/Network/OpenVPN/SMB
+ remove State:/Network/OpenVPN/DNS
+ remove State:/Network/OpenVPN/OldSMB
+ remove State:/Network/OpenVPN/OldDNS
+ remove State:/Network/OpenVPN
+ quit
+EOF
+
+exit 0
diff --git a/pkg/osx/install/client.up.sh b/pkg/osx/install/client.up.sh
new file mode 100755
index 00000000..fc7e341a
--- /dev/null
+++ b/pkg/osx/install/client.up.sh
@@ -0,0 +1,596 @@
+#!/bin/bash -e
+# Note: must be bash; uses bash-specific tricks
+#
+# ******************************************************************************************************************
+# This Tunnelblick script does everything! It handles TUN and TAP interfaces,
+# pushed configurations, DHCP with DNS and WINS, and renewed DHCP leases. :)
+#
+# This is the "Up" version of the script, executed after the interface is
+# initialized.
+#
+# Created by: Nick Williams (using original code and parts of old Tblk scripts)
+#
+# ******************************************************************************************************************
+
+trap "" TSTP
+trap "" HUP
+trap "" INT
+export PATH="/bin:/sbin:/usr/sbin:/usr/bin"
+
+# Process optional arguments (if any) for the script
+# Each one begins with a "-"
+# They come from Tunnelblick, and come first, before the OpenVPN arguments
+# So we set ARG_ script variables to their values and shift them out of the argument list
+# When we're done, only the OpenVPN arguments remain for the rest of the script to use
+ARG_MONITOR_NETWORK_CONFIGURATION="false"
+ARG_RESTORE_ON_DNS_RESET="false"
+ARG_RESTORE_ON_WINS_RESET="false"
+ARG_TAP="false"
+ARG_IGNORE_OPTION_FLAGS=""
+
+while [ {$#} ] ; do
+ if [ "$1" = "-m" ] ; then # Handle the arguments we know about
+ ARG_MONITOR_NETWORK_CONFIGURATION="true" # by setting ARG_ script variables to their values
+ shift # Then shift them out
+ elif [ "$1" = "-d" ] ; then
+ ARG_RESTORE_ON_DNS_RESET="true"
+ shift
+ elif [ "$1" = "-w" ] ; then
+ ARG_RESTORE_ON_WINS_RESET="true"
+ shift
+ elif [ "$1" = "-a" ] ; then
+ ARG_TAP="true"
+ shift
+ elif [ "${1:0:2}" = "-i" ] ; then
+ ARG_IGNORE_OPTION_FLAGS="${1}"
+ shift
+ elif [ "${1:0:2}" = "-a" ] ; then
+ ARG_IGNORE_OPTION_FLAGS="${1}"
+ shift
+ else
+ if [ "${1:0:1}" = "-" ] ; then # Shift out Tunnelblick arguments (they start with "-") that we don't understand
+ shift # so the rest of the script sees only the OpenVPN arguments
+ else
+ break
+ fi
+ fi
+done
+
+readonly ARG_MONITOR_NETWORK_CONFIGURATION ARG_RESTORE_ON_DNS_RESET ARG_RESTORE_ON_WINS_RESET ARG_TAP ARG_IGNORE_OPTION_FLAGS
+
+# Note: The script log path name is constructed from the path of the regular config file, not the shadow copy
+# if the config is shadow copy, e.g. /Library/Application Support/Tunnelblick/Users/Jonathan/Folder/Subfolder/config.ovpn
+# then convert to regular config /Users/Jonathan/Library/Application Support/Tunnelblick/Configurations/Folder/Subfolder/config.ovpn
+# to get the script log path
+# Note: "/Users/..." works even if the home directory has a different path; it is used in the name of the log file, and is not used as a path to get to anything.
+readonly TBALTPREFIX="/Library/Application Support/Tunnelblick/Users/"
+readonly TBALTPREFIXLEN="${#TBALTPREFIX}"
+readonly TBCONFIGSTART="${config:0:$TBALTPREFIXLEN}"
+if [ "$TBCONFIGSTART" = "$TBALTPREFIX" ] ; then
+ readonly TBBASE="${config:$TBALTPREFIXLEN}"
+ readonly TBSUFFIX="${TBBASE#*/}"
+ readonly TBUSERNAME="${TBBASE%%/*}"
+ readonly TBCONFIG="/Users/$TBUSERNAME/Library/Application Support/Tunnelblick/Configurations/$TBSUFFIX"
+else
+ readonly TBCONFIG="${config}"
+fi
+
+readonly CONFIG_PATH_DASHES_SLASHES="$(echo "${TBCONFIG}" | sed -e 's/-/--/g' | sed -e 's/\//-S/g')"
+readonly SCRIPT_LOG_FILE="/Library/Application Support/Tunnelblick/Logs/${CONFIG_PATH_DASHES_SLASHES}.script.log"
+
+readonly TB_RESOURCE_PATH=$(dirname "${0}")
+
+LEASEWATCHER_PLIST_PATH="/Library/Application Support/Tunnelblick/LeaseWatch.plist"
+
+readonly OSVER="$(sw_vers | grep 'ProductVersion:' | grep -o '10\.[0-9]*')"
+
+readonly DEFAULT_DOMAIN_NAME="openvpn"
+
+bRouteGatewayIsDhcp="false"
+
+# @param String message - The message to log
+readonly LOG_MESSAGE_COMMAND=$(basename "${0}")
+logMessage()
+{
+ echo "$(date '+%a %b %e %T %Y') *Tunnelblick $LOG_MESSAGE_COMMAND: "${@} >> "${SCRIPT_LOG_FILE}"
+}
+
+# @param String string - Content to trim
+trim()
+{
+ echo ${@}
+}
+
+# @param String[] dnsServers - The name servers to use
+# @param String domainName - The domain name to use
+# @param \optional String[] winsServers - The WINS servers to use
+setDnsServersAndDomainName()
+{
+ declare -a vDNS=("${!1}")
+ domain=$2
+ declare -a vWINS=("${!3}")
+
+ set +e # "grep" will return error status (1) if no matches are found, so don't fail on individual errors
+
+ PSID=$( (scutil | grep PrimaryService | sed -e 's/.*PrimaryService : //')<<- EOF
+ open
+ show State:/Network/Global/IPv4
+ quit
+EOF )
+
+ STATIC_DNS_CONFIG="$( (scutil | sed -e 's/^[[:space:]]*[[:digit:]]* : //g' | tr '\n' ' ')<<- EOF
+ open
+ show Setup:/Network/Service/${PSID}/DNS
+ quit
+EOF )"
+ if echo "${STATIC_DNS_CONFIG}" | grep -q "ServerAddresses" ; then
+ readonly STATIC_DNS="$(trim "$( echo "${STATIC_DNS_CONFIG}" | sed -e 's/^.*ServerAddresses[^{]*{[[:space:]]*\([^}]*\)[[:space:]]*}.*$/\1/g' )")"
+ fi
+ if echo "${STATIC_DNS_CONFIG}" | grep -q "SearchDomains" ; then
+ readonly STATIC_SEARCH="$(trim "$( echo "${STATIC_DNS_CONFIG}" | sed -e 's/^.*SearchDomains[^{]*{[[:space:]]*\([^}]*\)[[:space:]]*}.*$/\1/g' )")"
+ fi
+
+ STATIC_WINS_CONFIG="$( (scutil | sed -e 's/^[[:space:]]*[[:digit:]]* : //g' | tr '\n' ' ')<<- EOF
+ open
+ show Setup:/Network/Service/${PSID}/SMB
+ quit
+EOF )"
+ STATIC_WINS_SERVERS=""
+ STATIC_WORKGROUP=""
+ STATIC_NETBIOSNAME=""
+ if echo "${STATIC_WINS_CONFIG}" | grep -q "WINSAddresses" ; then
+ STATIC_WINS_SERVERS="$(trim "$( echo "${STATIC_WINS_CONFIG}" | sed -e 's/^.*WINSAddresses[^{]*{[[:space:]]*\([^}]*\)[[:space:]]*}.*$/\1/g' )")"
+ fi
+ if echo "${STATIC_WINS_CONFIG}" | grep -q "Workgroup" ; then
+ STATIC_WORKGROUP="$(trim "$( echo "${STATIC_WINS_CONFIG}" | sed -e 's/^.*Workgroup : \([^[:space:]]*\).*$/\1/g' )")"
+ fi
+ if echo "${STATIC_WINS_CONFIG}" | grep -q "NetBIOSName" ; then
+ STATIC_NETBIOSNAME="$(trim "$( echo "${STATIC_WINS_CONFIG}" | sed -e 's/^.*NetBIOSName : \([^[:space:]]*\).*$/\1/g' )")"
+ fi
+ readonly STATIC_WINS_SERVERS STATIC_WORKGROUP STATIC_NETBIOSNAME
+
+ if [ ${#vDNS[*]} -eq 0 ] ; then
+ DYN_DNS="false"
+ ALL_DNS="${STATIC_DNS}"
+ elif [ -n "${STATIC_DNS}" ] ; then
+ case "${OSVER}" in
+ 10.6 | 10.7 )
+ # Do nothing - in 10.6 we don't aggregate our configurations, apparently
+ DYN_DNS="false"
+ ALL_DNS="${STATIC_DNS}"
+ ;;
+ 10.4 | 10.5 )
+ DYN_DNS="true"
+ # We need to remove duplicate DNS entries, so that our reference list matches MacOSX's
+ SDNS="$(echo "${STATIC_DNS}" | tr ' ' '\n')"
+ (( i=0 ))
+ for n in "${vDNS[@]}" ; do
+ if echo "${SDNS}" | grep -q "${n}" ; then
+ unset vDNS[${i}]
+ fi
+ (( i++ ))
+ done
+ if [ ${#vDNS[*]} -gt 0 ] ; then
+ ALL_DNS="$(trim "${STATIC_DNS}" "${vDNS[*]}")"
+ else
+ DYN_DNS="false"
+ ALL_DNS="${STATIC_DNS}"
+ fi
+ ;;
+ esac
+ else
+ DYN_DNS="true"
+ ALL_DNS="$(trim "${vDNS[*]}")"
+ fi
+ readonly DYN_DNS ALL_DNS
+
+ if [ ${#vWINS[*]} -eq 0 ] ; then
+ DYN_WINS="false"
+ ALL_WINS_SERVERS="${STATIC_WINS_SERVERS}"
+ elif [ -n "${STATIC_WINS_SERVERS}" ] ; then
+ case "${OSVER}" in
+ 10.6 | 10.7 )
+ # Do nothing - in 10.6 we don't aggregate our configurations, apparently
+ DYN_WINS="false"
+ ALL_WINS_SERVERS="${STATIC_WINS_SERVERS}"
+ ;;
+ 10.4 | 10.5 )
+ DYN_WINS="true"
+ # We need to remove duplicate WINS entries, so that our reference list matches MacOSX's
+ SWINS="$(echo "${STATIC_WINS_SERVERS}" | tr ' ' '\n')"
+ (( i=0 ))
+ for n in "${vWINS[@]}" ; do
+ if echo "${SWINS}" | grep -q "${n}" ; then
+ unset vWINS[${i}]
+ fi
+ (( i++ ))
+ done
+ if [ ${#vWINS[*]} -gt 0 ] ; then
+ ALL_WINS_SERVERS="$(trim "${STATIC_WINS_SERVERS}" "${vWINS[*]}")"
+ else
+ DYN_WINS="false"
+ ALL_WINS_SERVERS="${STATIC_WINS_SERVERS}"
+ fi
+ ;;
+ esac
+ else
+ DYN_WINS="true"
+ ALL_WINS_SERVERS="$(trim "${vWINS[*]}")"
+ fi
+ readonly DYN_WINS ALL_WINS_SERVERS
+
+ # We double-check that our search domain isn't already on the list
+ SEARCH_DOMAIN="${domain}"
+ case "${OSVER}" in
+ 10.6 | 10.7 )
+ # Do nothing - in 10.6 we don't aggregate our configurations, apparently
+ if [ -n "${STATIC_SEARCH}" ] ; then
+ ALL_SEARCH="${STATIC_SEARCH}"
+ SEARCH_DOMAIN=""
+ else
+ ALL_SEARCH="${SEARCH_DOMAIN}"
+ fi
+ ;;
+ 10.4 | 10.5 )
+ if echo "${STATIC_SEARCH}" | tr ' ' '\n' | grep -q "${SEARCH_DOMAIN}" ; then
+ SEARCH_DOMAIN=""
+ fi
+ if [ -z "${SEARCH_DOMAIN}" ] ; then
+ ALL_SEARCH="${STATIC_SEARCH}"
+ else
+ ALL_SEARCH="$(trim "${STATIC_SEARCH}" "${SEARCH_DOMAIN}")"
+ fi
+ ;;
+ esac
+ readonly SEARCH_DOMAIN ALL_SEARCH
+
+ if ! ${DYN_DNS} ; then
+ NO_DNS="#"
+ fi
+ if ! ${DYN_WINS} ; then
+ NO_WS="#"
+ fi
+ if [ -z "${SEARCH_DOMAIN}" ] ; then
+ NO_SEARCH="#"
+ fi
+ if [ -z "${STATIC_WORKGROUP}" ] ; then
+ NO_WG="#"
+ fi
+ if [ -z "${STATIC_NETBIOSNAME}" ] ; then
+ NO_NB="#"
+ fi
+ if [ -z "${ALL_DNS}" ] ; then
+ AGG_DNS="#"
+ fi
+ if [ -z "${ALL_SEARCH}" ] ; then
+ AGG_SEARCH="#"
+ fi
+ if [ -z "${ALL_WINS_SERVERS}" ] ; then
+ AGG_WINS="#"
+ fi
+
+ # Now, do the aggregation
+ # Save the openvpn process ID and the Network Primary Service ID, leasewather.plist path, logfile path, and optional arguments from Tunnelblick,
+ # then save old and new DNS and WINS settings
+ # PPID is a bash-script variable that contains the process ID of the parent of the process running the script (i.e., OpenVPN's process ID)
+ # config is an environmental variable set to the configuration path by OpenVPN prior to running this up script
+ logMessage "Up to two 'No such key' warnings are normal and may be ignored"
+
+ # If DNS is manually set, it overrides the DHCP setting, which isn't reflected in 'State:/Network/Service/${PSID}/DNS'
+ if echo "${STATIC_DNS_CONFIG}" | grep -q "ServerAddresses" ; then
+ CORRECT_OLD_DNS_KEY="Setup:"
+ else
+ CORRECT_OLD_DNS_KEY="State:"
+ fi
+
+ # If WINS is manually set, it overrides the DHCP setting, which isn't reflected in 'State:/Network/Service/${PSID}/DNS'
+ if echo "${STATIC_WINS_CONFIG}" | grep -q "WINSAddresses" ; then
+ CORRECT_OLD_WINS_KEY="Setup:"
+ else
+ CORRECT_OLD_WINS_KEY="State:"
+ fi
+
+ # If we are not expecting any WINS value, add <TunnelblickNoSuchKey : true> to the expected WINS setup
+ NO_NOSUCH_KEY_WINS="#"
+ if [ "${NO_NB}" = "#" -a "${AGG_WINS}" = "#" -a "${NO_WG}" = "#" ] ; then
+ NO_NOSUCH_KEY_WINS=""
+ fi
+ readonly NO_NOSUCH_KEY_WINS
+
+ set -e # We instruct bash that it CAN again fail on errors
+
+ scutil <<- EOF
+ open
+ d.init
+ d.add PID # ${PPID}
+ d.add Service ${PSID}
+ d.add LeaseWatcherPlistPath "${LEASEWATCHER_PLIST_PATH}"
+ d.add ScriptLogFile "${SCRIPT_LOG_FILE}"
+ d.add MonitorNetwork "${ARG_MONITOR_NETWORK_CONFIGURATION}"
+ d.add RestoreOnDNSReset "${ARG_RESTORE_ON_DNS_RESET}"
+ d.add RestoreOnWINSReset "${ARG_RESTORE_ON_WINS_RESET}"
+ d.add IgnoreOptionFlags "${ARG_IGNORE_OPTION_FLAGS}"
+ d.add IsTapInterface "${ARG_TAP}"
+ d.add RouteGatewayIsDhcp "${bRouteGatewayIsDhcp}"
+ set State:/Network/OpenVPN
+
+ # First, back up the device's current DNS and WINS configurations
+ # Indicate 'no such key' by a dictionary with a single entry: "TunnelblickNoSuchKey : true"
+ d.init
+ d.add TunnelblickNoSuchKey true
+ get ${CORRECT_OLD_DNS_KEY}/Network/Service/${PSID}/DNS
+ set State:/Network/OpenVPN/OldDNS
+
+ d.init
+ d.add TunnelblickNoSuchKey true
+ get ${CORRECT_OLD_WINS_KEY}/Network/Service/${PSID}/SMB
+ set State:/Network/OpenVPN/OldSMB
+
+ # Second, initialize the new DNS map
+ d.init
+ ${NO_DNS}d.add ServerAddresses * ${vDNS[*]}
+ ${NO_SEARCH}d.add SearchDomains * ${SEARCH_DOMAIN}
+ d.add DomainName ${domain}
+ set State:/Network/Service/${PSID}/DNS
+
+ # Third, initialize the WINS map
+ d.init
+ ${NO_NB}d.add NetBIOSName ${STATIC_NETBIOSNAME}
+ ${NO_WS}d.add WINSAddresses * ${vWINS[*]}
+ ${NO_WG}d.add Workgroup ${STATIC_WORKGROUP}
+ set State:/Network/Service/${PSID}/SMB
+
+ # Now, initialize the maps that will be compared against the system-generated map
+ # which means that we will have to aggregate configurations of statically-configured
+ # nameservers, and statically-configured search domains
+ d.init
+ ${AGG_DNS}d.add ServerAddresses * ${ALL_DNS}
+ ${AGG_SEARCH}d.add SearchDomains * ${ALL_SEARCH}
+ d.add DomainName ${domain}
+ set State:/Network/OpenVPN/DNS
+
+ d.init
+ ${NO_NB}d.add NetBIOSName ${STATIC_NETBIOSNAME}
+ ${AGG_WINS}d.add WINSAddresses * ${ALL_WINS_SERVERS}
+ ${NO_WG}d.add Workgroup ${STATIC_WORKGROUP}
+ ${NO_NOSUCH_KEY_WINS}d.add TunnelblickNoSuchKey true
+ set State:/Network/OpenVPN/SMB
+
+ # We are done
+ quit
+EOF
+
+ logMessage "Saved the DNS and WINS configurations for later use"
+
+ if ${ARG_MONITOR_NETWORK_CONFIGURATION} ; then
+ if [ "${ARG_IGNORE_OPTION_FLAGS:0:2}" = "-a" ] ; then
+ # Generate an updated plist with the path for process-network-changes
+ readonly LEASEWATCHER_TEMPLATE_PATH="$(dirname "${0}")/ProcessNetworkChanges.plist.template"
+ sed -e "s|\${DIR}|$(dirname "${0}")|g" "${LEASEWATCHER_TEMPLATE_PATH}" > "${LEASEWATCHER_PLIST_PATH}"
+ launchctl load "${LEASEWATCHER_PLIST_PATH}"
+ logMessage "Set up to monitor system configuration with process-network-changes"
+ else
+ # Generate an updated plist with the path for leasewatch
+ readonly LEASEWATCHER_TEMPLATE_PATH="$(dirname "${0}")/LeaseWatch.plist.template"
+ sed -e "s|\${DIR}|$(dirname "${0}")|g" "${LEASEWATCHER_TEMPLATE_PATH}" > "${LEASEWATCHER_PLIST_PATH}"
+ launchctl load "${LEASEWATCHER_PLIST_PATH}"
+ logMessage "Set up to monitor system configuration with leasewatch"
+ fi
+ fi
+}
+
+configureDhcpDns()
+{
+ # whilst ipconfig will have created the neccessary Network Service keys, the DNS
+ # settings won't actually be used by OS X unless the SupplementalMatchDomains key
+ # is added
+ # ref. <http://lists.apple.com/archives/Macnetworkprog/2005/Jun/msg00011.html>
+ # - is there a way to extract the domains from the SC dictionary and re-insert
+ # as SupplementalMatchDomains? i.e. not requiring the ipconfig domain_name call?
+
+ # - wait until we get a lease before extracting the DNS domain name and merging into SC
+ # - despite it's name, ipconfig waitall doesn't (but maybe one day it will :-)
+ ipconfig waitall
+
+ unset test_domain_name
+ unset test_name_server
+
+ set +e # We instruct bash NOT to exit on individual command errors, because if we need to wait longer these commands will fail
+
+ # usually takes at least a few seconds to get a DHCP lease
+ sleep 3
+ n=0
+ while [ -z "$test_domain_name" -a -z "$test_name_server" -a $n -lt 5 ]
+ do
+ logMessage "Sleeping for $n seconds to wait for DHCP to finish setup."
+ sleep $n
+ n=`expr $n + 1`
+
+ if [ -z "$test_domain_name" ]; then
+ test_domain_name=`ipconfig getoption $dev domain_name 2>/dev/null`
+ fi
+
+ if [ -z "$test_name_server" ]; then
+ test_name_server=`ipconfig getoption $dev domain_name_server 2>/dev/null`
+ fi
+ done
+
+ sGetPacketOutput=`ipconfig getpacket $dev`
+
+ set -e # We instruct bash that it CAN again fail on individual errors
+
+ #echo "`date` test_domain_name = $test_domain_name, test_name_server = $test_name_server, sGetPacketOutput = $sGetPacketOutput"
+
+ unset aNameServers
+ unset aWinsServers
+
+ nNameServerIndex=1
+ nWinsServerIndex=1
+
+ if [ "$sGetPacketOutput" ]; then
+ sGetPacketOutput_FirstLine=`echo "$sGetPacketOutput"|head -n 1`
+ #echo $sGetPacketOutput_FirstLine
+
+ if [ "$sGetPacketOutput_FirstLine" == "op = BOOTREPLY" ]; then
+ set +e # "grep" will return error status (1) if no matches are found, so don't fail on individual errors
+
+ for tNameServer in `echo "$sGetPacketOutput"|grep "domain_name_server"|grep -Eo "\{([0-9\.]+)(, [0-9\.]+)*\}"|grep -Eo "([0-9\.]+)"`; do
+ aNameServers[nNameServerIndex-1]="$(trim "$tNameServer")"
+ let nNameServerIndex++
+ done
+
+ for tWINSServer in `echo "$sGetPacketOutput"|grep "nb_over_tcpip_name_server"|grep -Eo "\{([0-9\.]+)(, [0-9\.]+)*\}"|grep -Eo "([0-9\.]+)"`; do
+ aWinsServers[nWinsServerIndex-1]="$(trim "$tWINSServer")"
+ let nWinsServerIndex++
+ done
+
+ sDomainName=`echo "$sGetPacketOutput"|grep "domain_name "|grep -Eo ": [-A-Za-z0-9\-\.]+"|grep -Eo "[-A-Za-z0-9\-\.]+"`
+ sDomainName="$(trim "$sDomainName")"
+
+ if [ ${#aNameServers[*]} -gt 0 -a "$sDomainName" ]; then
+ logMessage "Retrieved name server(s) [ ${aNameServers[@]} ], domain name [ $sDomainName ], and WINS server(s) [ ${aWinsServers[@]} ]"
+ setDnsServersAndDomainName aNameServers[@] "$sDomainName" aWinsServers[@]
+ return 0
+ elif [ ${#aNameServers[*]} -gt 0 ]; then
+ logMessage "Retrieved name server(s) [ ${aNameServers[@]} ] and WINS server(s) [ ${aWinsServers[@]} ] and using default domain name [ $DEFAULT_DOMAIN_NAME ]"
+ setDnsServersAndDomainName aNameServers[@] "$DEFAULT_DOMAIN_NAME" aWinsServers[@]
+ return 0
+ else
+ # Should we return 1 here and indicate an error, or attempt the old method?
+ logMessage "No useful information extracted from DHCP/BOOTP packet. Attempting legacy configuration."
+ fi
+
+ set -e # We instruct bash that it CAN again fail on errors
+ else
+ # Should we return 1 here and indicate an error, or attempt the old method?
+ logMessage "No DHCP/BOOTP packet found on interface. Attempting legacy configuration."
+ fi
+ fi
+
+ unset sDomainName
+ unset sNameServer
+ unset aNameServers
+
+ sDomainName=`ipconfig getoption $dev domain_name 2>/dev/null`
+ sNameServer=`ipconfig getoption $dev domain_name_server 2>/dev/null`
+
+ sDomainName="$(trim "$sDomainName")"
+ sNameServer="$(trim "$sNameServer")"
+
+ declare -a aWinsServers=( ) # Declare empty WINS array to avoid any useless error messages
+
+ if [ "$sDomainName" -a "$sNameServer" ]; then
+ aNameServers[0]=$sNameServer
+ logMessage "Retrieved name server [ $sNameServer ], domain name [ $sDomainName ], and no WINS servers"
+ setDnsServersAndDomainName aNameServers[@] "$sDomainName" aWinsServers[@]
+ elif [ "$sNameServer" ]; then
+ aNameServers[0]=$sNameServer
+ logMessage "Retrieved name server [ $sNameServer ] and no WINS servers, and using default domain name [ $DEFAULT_DOMAIN_NAME ]"
+ setDnsServersAndDomainName aNameServers[@] "$DEFAULT_DOMAIN_NAME" aWinsServers[@]
+ elif [ "$sDomainName" ]; then
+ logMessage "WARNING: Retrieved domain name [ $sDomainName ] but no name servers from OpenVPN (DHCP), which is not sufficient to make network/DNS configuration changes."
+ if ${ARG_MONITOR_NETWORK_CONFIGURATION} ; then
+ logMessage "Will NOT monitor for other network configuration changes."
+ fi
+ else
+ logMessage "WARNING: No DNS information received from OpenVPN (DHCP), so no network/DNS configuration changes need to be made."
+ if ${ARG_MONITOR_NETWORK_CONFIGURATION} ; then
+ logMessage "Will NOT monitor for other network configuration changes."
+ fi
+ fi
+
+ return 0
+}
+
+configureOpenVpnDns()
+{
+ unset vForOptions
+ unset vOptions
+ unset aNameServers
+ unset aWinsServers
+
+ nOptionIndex=1
+ nNameServerIndex=1
+ nWinsServerIndex=1
+
+ while vForOptions=foreign_option_$nOptionIndex; [ -n "${!vForOptions}" ]; do
+ vOptions[nOptionIndex-1]=${!vForOptions}
+ case ${vOptions[nOptionIndex-1]} in
+ *DOMAIN* )
+ sDomainName="$(trim "${vOptions[nOptionIndex-1]//dhcp-option DOMAIN /}")"
+ ;;
+ *DNS* )
+ aNameServers[nNameServerIndex-1]="$(trim "${vOptions[nOptionIndex-1]//dhcp-option DNS /}")"
+ let nNameServerIndex++
+ ;;
+ *WINS* )
+ aWinsServers[nWinsServerIndex-1]="$(trim "${vOptions[nOptionIndex-1]//dhcp-option WINS /}")"
+ let nWinsServerIndex++
+ ;;
+ * )
+ logMessage "Unknown: 'foreign_option_${nOptionIndex}' = '${vOptions[nOptionIndex-1]}'"
+ ;;
+ esac
+ let nOptionIndex++
+ done
+
+ if [ ${#aNameServers[*]} -gt 0 -a "$sDomainName" ]; then
+ logMessage "Retrieved name server(s) [ ${aNameServers[@]} ], domain name [ $sDomainName ], and WINS server(s) [ ${aWinsServers[@]} ]"
+ setDnsServersAndDomainName aNameServers[@] "$sDomainName" aWinsServers[@]
+ elif [ ${#aNameServers[*]} -gt 0 ]; then
+ logMessage "Retrieved name server(s) [ ${aNameServers[@]} ] and WINS server(s) [ ${aWinsServers[@]} ] and using default domain name [ $DEFAULT_DOMAIN_NAME ]"
+ setDnsServersAndDomainName aNameServers[@] "$DEFAULT_DOMAIN_NAME" aWinsServers[@]
+ else
+ # Should we maybe just return 1 here to indicate an error? Does this mean that something bad has happened?
+ logMessage "No DNS information recieved from OpenVPN, so no network configuration changes need to be made."
+ if ${ARG_MONITOR_NETWORK_CONFIGURATION} ; then
+ logMessage "Will NOT monitor for other network configuration changes."
+ fi
+ fi
+
+ return 0
+}
+
+# We sleep here to allow time for OS X to process network settings
+sleep 2
+
+EXIT_CODE=0
+
+if ${ARG_TAP} ; then
+ # Still need to do: Look for route-gateway dhcp (TAP isn't always DHCP)
+ bRouteGatewayIsDhcp="false"
+ if [ -z "${route_vpn_gateway}" -o "$route_vpn_gateway" == "dhcp" -o "$route_vpn_gateway" == "DHCP" ]; then
+ bRouteGatewayIsDhcp="true"
+ fi
+
+ if [ "$bRouteGatewayIsDhcp" == "true" ]; then
+ if [ -z "$dev" ]; then
+ logMessage "Cannot configure TAP interface for DHCP without \$dev being defined. Exiting."
+ exit 1
+ fi
+
+ ipconfig set "$dev" DHCP
+
+ configureDhcpDns &
+ elif [ "$foreign_option_1" == "" ]; then
+ logMessage "No network configuration changes need to be made."
+ if ${ARG_MONITOR_NETWORK_CONFIGURATION} ; then
+ logMessage "Will NOT monitor for other network configuration changes."
+ fi
+ else
+ configureOpenVpnDns
+ EXIT_CODE=$?
+ fi
+else
+ if [ "$foreign_option_1" == "" ]; then
+ logMessage "No network configuration changes need to be made."
+ if ${ARG_MONITOR_NETWORK_CONFIGURATION} ; then
+ logMessage "Will NOT monitor for other network configuration changes."
+ fi
+ else
+ configureOpenVpnDns
+ EXIT_CODE=$?
+ fi
+fi
+
+exit $EXIT_CODE
diff --git a/pkg/osx/install/install-leapc.sh b/pkg/osx/install/install-leapc.sh
new file mode 100755
index 00000000..2ecfc08e
--- /dev/null
+++ b/pkg/osx/install/install-leapc.sh
@@ -0,0 +1,17 @@
+#!/bin/sh
+echo "Installing LEAP Client in /Applications"
+cp -r "LEAP Client.app" "/Applications"
+
+echo "Copying openvpn binary"
+cp -r openvpn.leap /usr/bin
+
+echo "Installing tun/tap drivers"
+cp -r Extensions/* /Library/Extensions
+cp -r StartupItems/* /Library/StartupItems
+
+echo "Loading tun/tap kernel extension"
+/Library/StartupItems/tun/tun start
+
+echo "Installation Finished!"
+
+ln -s /Applications/LEAP\ Client.app/ /Volumes/LEAP\ Client\ installer/
diff --git a/pkg/osx/install/leap-installer.platypus b/pkg/osx/install/leap-installer.platypus
new file mode 100644
index 00000000..9150961e
--- /dev/null
+++ b/pkg/osx/install/leap-installer.platypus
@@ -0,0 +1,90 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>AcceptsFiles</key>
+ <true/>
+ <key>AcceptsText</key>
+ <false/>
+ <key>Authentication</key>
+ <true/>
+ <key>Author</key>
+ <string>Kali Yuga</string>
+ <key>BundledFiles</key>
+ <array/>
+ <key>Creator</key>
+ <string>Platypus-4.7</string>
+ <key>DeclareService</key>
+ <false/>
+ <key>Destination</key>
+ <string>MyPlatypusApp.app</string>
+ <key>DestinationOverride</key>
+ <false/>
+ <key>DevelopmentVersion</key>
+ <false/>
+ <key>DocIcon</key>
+ <string></string>
+ <key>Droppable</key>
+ <false/>
+ <key>ExecutablePath</key>
+ <string>/opt/local/share/platypus/ScriptExec</string>
+ <key>FileTypes</key>
+ <array>
+ <string>****</string>
+ <string>fold</string>
+ </array>
+ <key>IconPath</key>
+ <string></string>
+ <key>Identifier</key>
+ <string>se.leap.LEAPClientInstaller</string>
+ <key>Interpreter</key>
+ <string>/bin/sh</string>
+ <key>InterpreterArgs</key>
+ <array/>
+ <key>Name</key>
+ <string>LEAPClient Installer</string>
+ <key>NibPath</key>
+ <string>/opt/local/share/platypus/MainMenu.nib</string>
+ <key>OptimizeApplication</key>
+ <true/>
+ <key>Output</key>
+ <string>Progress Bar</string>
+ <key>RemainRunning</key>
+ <true/>
+ <key>Role</key>
+ <string>Viewer</string>
+ <key>ScriptArgs</key>
+ <array/>
+ <key>ScriptPath</key>
+ <string>./install/install-leapc.sh</string>
+ <key>Secure</key>
+ <false/>
+ <key>ShowInDock</key>
+ <false/>
+ <key>StatusItemDisplayType</key>
+ <string>Text</string>
+ <key>StatusItemIcon</key>
+ <data>
+ </data>
+ <key>StatusItemTitle</key>
+ <string>MyPlatypusApp</string>
+ <key>Suffixes</key>
+ <array>
+ <string>*</string>
+ </array>
+ <key>TextBackground</key>
+ <string>#ffffff</string>
+ <key>TextEncoding</key>
+ <integer>4</integer>
+ <key>TextFont</key>
+ <string>Monaco</string>
+ <key>TextForeground</key>
+ <string>#000000</string>
+ <key>TextSize</key>
+ <real>10</real>
+ <key>UseXMLPlistFormat</key>
+ <true/>
+ <key>Version</key>
+ <string>1.0</string>
+</dict>
+</plist>
diff --git a/pkg/osx/leap-client.spec b/pkg/osx/leap-client.spec
new file mode 100644
index 00000000..75bf991b
--- /dev/null
+++ b/pkg/osx/leap-client.spec
@@ -0,0 +1,36 @@
+# -*- mode: python -*-
+a = Analysis(['../../src/leap/app.py'],
+ pathex=[
+ '../../src/leap',
+ '/Users/kaliy/leap/leap-client-testbuild/src/leap-client/pkg/osx'],
+ hiddenimports=['atexit'],
+ hookspath=None)
+pyz = PYZ(a.pure)
+exe = EXE(pyz,
+ a.scripts,
+ exclude_binaries=1,
+ name=os.path.join('build/pyi.darwin/leap-client', 'app'),
+ debug=False,
+ strip=True,
+ upx=True,
+ console=False)
+coll = COLLECT(exe,
+ a.binaries +
+ # this will easitly break if we setup the venv
+ # somewhere else. FIXME
+ [('cacert.pem', '../../../../lib/python2.6/site-packages/requests/cacert.pem', 'DATA'),
+ ],
+ a.zipfiles,
+ a.datas,
+ strip=True,
+ upx=True,
+ name=os.path.join('dist', 'app'))
+app = BUNDLE(coll,
+ name=os.path.join('dist', 'leap-client.app'))
+
+import sys
+if sys.platform.startswith("darwin"):
+ app = BUNDLE(coll,
+ name=os.path.join('dist', 'LEAP Client.app'),
+ appname='LEAP Client',
+ version=1)
diff --git a/pkg/postmkvenv.sh b/pkg/postmkvenv.sh
index efdbc2fb..593b11da 100755
--- a/pkg/postmkvenv.sh
+++ b/pkg/postmkvenv.sh
@@ -8,6 +8,14 @@
# script fails in ubuntu, with path: /usr/lib/pymodules/python2.7/PyQt4
# use import PyQt4; PyQt4.__path__ instead
+platform='unknown'
+unamestr=`uname`
+if [[ "$unamestr" == 'Linux' ]]; then
+ platform='linux'
+elif [[ "$unamestr" == 'Darwin' ]]; then
+ platform='darwin'
+fi
+
LIBS=( PyQt4 sip.so )
PYTHON_VERSION=python$(python -c "import sys; print (str(sys.version_info[0])+'.'+str(sys.version_info[1]))")
@@ -15,7 +23,14 @@ VAR=( $(which -a $PYTHON_VERSION) )
GET_PYTHON_LIB_CMD="from distutils.sysconfig import get_python_lib; print (get_python_lib())"
LIB_VIRTUALENV_PATH=$(python -c "$GET_PYTHON_LIB_CMD")
-LIB_SYSTEM_PATH=$(${VAR[-1]} -c "$GET_PYTHON_LIB_CMD")
+
+if [[ $platform == 'linux' ]]; then
+ LIB_SYSTEM_PATH=$(${VAR[-1]} -c "$GET_PYTHON_LIB_CMD")
+elif [[ $platform == 'darwin' ]]; then
+ LIB_SYSTEM_PATH=$(/opt/local/bin/python2.6 -c "$GET_PYTHON_LIB_CMD")
+else
+ echo "unsupported platform; not doing symlinks"
+fi
for LIB in ${LIBS[@]}
do
diff --git a/pkg/requirements.pip b/pkg/requirements.pip
index d7dc2c91..813a9c62 100644
--- a/pkg/requirements.pip
+++ b/pkg/requirements.pip
@@ -1,14 +1,20 @@
# in order of addition to the project.
-# do not change it, we will freeze the requirements before tagging a release.
+# do not change the ordering.
argparse # only for python 2.6
requests<1.0.0
-ping
+ping # don't use it! needs root.
psutil
netifaces
python-gnutls==1.1.9 # see https://bugs.launchpad.net/ubuntu/+source/python-gnutls/+bug/1027129
jsonschema
-srp # >=1.0.1 MUST HAVE 1.0.1 BUGFIX, but upstream DID NOT UPDATE setup.py so it conflicts
+srp>=1.0.2
pycrypto
keyring
python-dateutil
+python-gnupg
+u1db
+oauth
+couchdb
+sh
+pygeoip
diff --git a/pkg/scripts/leap b/pkg/scripts/leap
deleted file mode 100755
index 6e62b597..00000000
--- a/pkg/scripts/leap
+++ /dev/null
@@ -1,6 +0,0 @@
-#!/usr/bin/env python
-
-from leap.app import main
-
-if __name__ == "__main__":
- main()
diff --git a/pkg/scripts/leap_client_bootstrap.sh b/pkg/scripts/leap_client_bootstrap.sh
new file mode 100644
index 00000000..6c302d3f
--- /dev/null
+++ b/pkg/scripts/leap_client_bootstrap.sh
@@ -0,0 +1,50 @@
+#!/bin/bash
+
+# Installs requirements, and
+# clones the latest leap-client
+
+# depends on:
+# openvpn git-core libgnutls-dev python-dev python-qt4 python-setuptools python-virtualenv
+
+# Escape code
+esc=`echo -en "\033"`
+
+# Set colors
+cc_green="${esc}[0;32m"
+cc_yellow="${esc}[0;33m"
+cc_blue="${esc}[0;34m"
+cc_red="${esc}[0;31m"
+cc_normal=`echo -en "${esc}[m\017"`
+
+echo "${cc_yellow}"
+echo "~~~~~~~~~~~~~~~~~~~~~~"
+echo "LEAP "
+echo "client bootstrapping "
+echo "~~~~~~~~~~~~~~~~~~~~~~"
+echo ""
+echo "${cc_green}Creating virtualenv...${cc_normal}"
+
+mkdir leap-client-testbuild
+virtualenv leap-client-testbuild
+source leap-client-testbuild/bin/activate
+
+echo "${cc_green}Installing leap client...${cc_normal}"
+
+# Clone latest git (develop branch)
+# change "develop" for any other branch you want.
+
+
+pip install -e 'git://leap.se/leap_client@develop#egg=leap-client'
+
+cd leap-client-testbuild
+
+# symlink the pyqt libraries to the system libs
+./src/leap-client/pkg/postmkvenv.sh
+
+echo "${cc_green}leap-client installed! =)"
+echo "${cc_yellow}"
+echo "Launch it with: "
+echo "~~~~~~~~~~~~~~~~~~~~~~"
+echo "bin/leap-client"
+echo "~~~~~~~~~~~~~~~~~~~~~~"
+echo "${cc_normal}"
diff --git a/pkg/test-requirements.pip b/pkg/test-requirements.pip
index 26db61c8..11ccf4eb 100644
--- a/pkg/test-requirements.pip
+++ b/pkg/test-requirements.pip
@@ -1,3 +1,4 @@
+unittest2 # TODO we should include this dep only for python2.6
coverage
mock
nose
diff --git a/src/leap/app.py b/src/leap/app.py
index 334b58c8..912e390d 100644
--- a/src/leap/app.py
+++ b/src/leap/app.py
@@ -49,6 +49,13 @@ def main():
logger.addHandler(console)
#logger.debug(opts)
+ import os
+ ldlib = os.environ.get("LD_LIBRARY_PATH", None)
+ dyldlib = os.environ.get("DYLD_LIBRARY_PATH", None)
+
+ logger.debug("LD_LIBRARY_PATH %s" % ldlib)
+ logger.debug("DYLD_LIBRARY_PATH %s" % dyldlib)
+
logger.info('~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~')
logger.info('LEAP client version %s', VERSION)
logger.info('~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~')
@@ -82,6 +89,10 @@ def main():
app.setApplicationName("leap")
app.setOrganizationDomain("leap.se")
+ # XXX we could check here
+ # if leap-client is already running, and abort
+ # gracefully in that case.
+
if not QSystemTrayIcon.isSystemTrayAvailable():
QMessageBox.critical(None, "Systray",
"I couldn't detect"
@@ -108,6 +119,8 @@ def main():
# if not, it will be set visible
# from the systray menu.
window.show()
+ if sys.platform == "darwin":
+ window.raise_()
# run main loop
sys.exit(app.exec_())
diff --git a/src/leap/base/auth.py b/src/leap/base/auth.py
index ecc24179..f629972f 100644
--- a/src/leap/base/auth.py
+++ b/src/leap/base/auth.py
@@ -43,7 +43,6 @@ class LeapSRPRegister(object):
def __init__(self,
schema="https",
provider=None,
- #port=None,
verify=True,
register_path="1/users.json",
method="POST",
@@ -56,11 +55,6 @@ class LeapSRPRegister(object):
self.schema = schema
- # XXX FIXME
- #self.provider = provider
- #self.port = port
- # XXX splitting server,port
- # deprecate port call.
domain, port = get_https_domain_and_port(provider)
self.provider = domain
self.port = port
@@ -137,6 +131,9 @@ class SRPAuth(requests.auth.AuthBase):
self.server = server
self.verify = verify
+ logger.debug('SRPAuth. verify=%s' % verify)
+ logger.debug('server: %s. username=%s' % (server, username))
+
self.init_data = None
self.session = requests.session()
@@ -168,6 +165,9 @@ class SRPAuth(requests.auth.AuthBase):
except requests.exceptions.ConnectionError:
raise SRPAuthenticationError(
"No connection made (salt).")
+ except:
+ raise SRPAuthenticationError(
+ "Unknown error (salt).")
if init_session.status_code not in (200, ):
raise SRPAuthenticationError(
"No valid response (salt).")
@@ -245,7 +245,6 @@ class SRPAuth(requests.auth.AuthBase):
try:
assert self.srp_usr.authenticated()
logger.debug('user is authenticated!')
- print 'user is authenticated!'
except (AssertionError):
raise SRPAuthenticationError(
"Auth verification failed.")
@@ -268,6 +267,8 @@ def srpauth_protected(user=None, passwd=None, server=None, verify=True):
auth = SRPAuth(user, passwd, server, verify)
kwargs['auth'] = auth
kwargs['verify'] = verify
+ if not args:
+ logger.warning('attempting to get from empty uri!')
return fn(*args, **kwargs)
return wrapper
return srpauth
@@ -275,7 +276,7 @@ def srpauth_protected(user=None, passwd=None, server=None, verify=True):
def get_leap_credentials():
settings = QtCore.QSettings()
- full_username = settings.value('eip_username')
+ full_username = settings.value('username')
username, domain = full_username.split('@')
seed = settings.value('%s_seed' % domain, None)
password = leapkeyring.leap_get_password(full_username, seed=seed)
diff --git a/src/leap/base/checks.py b/src/leap/base/checks.py
index dc2602c2..8abdf774 100644
--- a/src/leap/base/checks.py
+++ b/src/leap/base/checks.py
@@ -1,16 +1,24 @@
# -*- coding: utf-8 -*-
import logging
import platform
+import re
import socket
import netifaces
import ping
import requests
+import sh
from leap.base import constants
from leap.base import exceptions
logger = logging.getLogger(name=__name__)
+_platform = platform.system()
+
+#EVENTS OF NOTE
+EVENT_CONNECT_REFUSED = "[ECONNREFUSED]: Connection refused (code=111)"
+
+ICMP_TARGET = "8.8.8.8"
class LeapNetworkChecker(object):
@@ -34,10 +42,13 @@ class LeapNetworkChecker(object):
if self.provider_gateway:
checker.ping_gateway(self.provider_gateway)
+ checker.parse_log_and_react([], ())
+
def check_internet_connection(self):
try:
# XXX remove this hardcoded random ip
# ping leap.se or eip provider instead...?
+ # XXX could use icmp instead..
requests.get('http://216.172.161.165')
except requests.ConnectionError as e:
error = "Unidentified Connection Error"
@@ -50,6 +61,9 @@ class LeapNetworkChecker(object):
raise exceptions.NoInternetConnection(error)
except (requests.HTTPError, requests.RequestException) as e:
raise exceptions.NoInternetConnection(e.message)
+
+ # XXX should redirect this to netcheck logger.
+ # and don't clutter main log.
logger.debug('Network appears to be up.')
def is_internet_up(self):
@@ -60,56 +74,100 @@ class LeapNetworkChecker(object):
return False
return True
- def check_tunnel_default_interface(self):
- """
- Raises an TunnelNotDefaultRouteError
- (including when no routes are present)
- """
- if not platform.system() == "Linux":
- raise NotImplementedError
+ def _get_route_table_linux(self):
- f = open("/proc/net/route")
- route_table = f.readlines()
- f.close()
+ with open("/proc/net/route") as f:
+ route_table = f.readlines()
#toss out header
route_table.pop(0)
-
if not route_table:
raise exceptions.TunnelNotDefaultRouteError()
+ return route_table
+ def _get_def_iface_osx(self):
+ default_iface = None
+ #gateway = None
+ routes = list(sh.route('-n', 'get', ICMP_TARGET, _iter=True))
+ iface = filter(lambda l: "interface" in l, routes)
+ if not iface:
+ return None, None
+ def_ifacel = re.findall('\w+\d', iface[0])
+ default_iface = def_ifacel[0] if def_ifacel else None
+ if not default_iface:
+ return None, None
+ _gw = filter(lambda l: "gateway" in l, routes)
+ gw = re.findall('\d+\.\d+\.\d+\.\d+', _gw[0])[0]
+ return default_iface, gw
+
+ def _get_tunnel_iface_linux(self):
+ # XXX review.
+ # valid also when local router has a default entry?
+ route_table = self._get_route_table_linux()
line = route_table.pop(0)
iface, destination = line.split('\t')[0:2]
if not destination == '00000000' or not iface == 'tun0':
raise exceptions.TunnelNotDefaultRouteError()
+ return True
- def get_default_interface_gateway(self):
- """only impletemented for linux so far."""
- if not platform.system() == "Linux":
+ def check_tunnel_default_interface(self):
+ """
+ Raises an TunnelNotDefaultRouteError
+ if tun0 is not the chosen default route
+ (including when no routes are present)
+ """
+ #logger.debug('checking tunnel default interface...')
+
+ if _platform == "Linux":
+ valid = self._get_tunnel_iface_linux()
+ return valid
+ elif _platform == "Darwin":
+ default_iface, gw = self._get_def_iface_osx()
+ #logger.debug('iface: %s', default_iface)
+ if default_iface != "tun0":
+ logger.debug('tunnel not default route! gw: %s', default_iface)
+ # XXX should catch this and act accordingly...
+ # but rather, this test should only be launched
+ # when we have successfully completed a connection
+ # ... TRIGGER: Connection stablished (or whatever it is)
+ # in the logs
+ raise exceptions.TunnelNotDefaultRouteError
+ else:
+ #logger.debug('PLATFORM !!! %s', _platform)
raise NotImplementedError
- # XXX use psutil
- f = open("/proc/net/route")
- route_table = f.readlines()
- f.close()
- #toss out header
- route_table.pop(0)
-
+ def _get_def_iface_linux(self):
default_iface = None
gateway = None
+
+ route_table = self._get_route_table_linux()
while route_table:
line = route_table.pop(0)
iface, destination, gateway = line.split('\t')[0:3]
if destination == '00000000':
default_iface = iface
break
+ return default_iface, gateway
+
+ def get_default_interface_gateway(self):
+ """
+ gets the interface we are going thru.
+ (this should be merged with check tunnel default interface,
+ imo...)
+ """
+ if _platform == "Linux":
+ default_iface, gw = self.get_def_iface_linux()
+ elif _platform == "Darwin":
+ default_iface, gw = self.get_def_iface_osx()
+ else:
+ raise NotImplementedError
if not default_iface:
raise exceptions.NoDefaultInterfaceFoundError
if default_iface not in netifaces.interfaces():
raise exceptions.InterfaceNotFoundError
-
- return default_iface, gateway
+ logger.debug('-- default iface', default_iface)
+ return default_iface, gw
def ping_gateway(self, gateway):
# TODO: Discuss how much packet loss (%) is acceptable.
@@ -118,7 +176,14 @@ class LeapNetworkChecker(object):
# -- is it a valid ip? (there's something in util)
# -- is it a domain?
# -- can we resolve? -- raise NoDNSError if not.
+
+ # XXX -- needs review!
+ # We cannout use this ping implementation; it needs root.
+ # We need to look for another, poors-man implementation
+ # or wrap around system traceroute (using sh module, fi)
+ # -- kali
packet_loss = ping.quiet_ping(gateway)[0]
+ logger.debug('packet loss %s' % packet_loss)
if packet_loss > constants.MAX_ICMP_PACKET_LOSS:
raise exceptions.NoConnectionToGateway
@@ -128,3 +193,21 @@ class LeapNetworkChecker(object):
return True
except socket.gaierror:
raise exceptions.CannotResolveDomainError
+
+ def parse_log_and_react(self, log, error_matrix=None):
+ """
+ compares the recent openvpn status log to
+ strings passed in and executes the callbacks passed in.
+ @param log: openvpn log
+ @type log: list of strings
+ @param error_matrix: tuples of strings and tuples of callbacks
+ @type error_matrix: tuples strings and call backs
+ """
+ for line in log:
+ # we could compile a regex here to save some cycles up -- kali
+ for each in error_matrix:
+ error, callbacks = each
+ if error in line:
+ for cb in callbacks:
+ if callable(cb):
+ cb()
diff --git a/src/leap/base/config.py b/src/leap/base/config.py
index 438d1993..e235e5c3 100644
--- a/src/leap/base/config.py
+++ b/src/leap/base/config.py
@@ -155,7 +155,7 @@ class JSONLeapConfig(BaseLeapConfig):
return False
def load(self, fromfile=None, from_uri=None, fetcher=None,
- force_download=False, verify=False):
+ force_download=False, verify=True):
if from_uri is not None:
fetched = self.fetch(
@@ -177,8 +177,7 @@ class JSONLeapConfig(BaseLeapConfig):
if not fetcher:
fetcher = self.fetcher
- logger.debug('verify: %s', verify)
- logger.debug('uri: %s', uri)
+ logger.debug('uri: %s (verify: %s)' % (uri, verify))
rargs = (uri, )
rkwargs = {'verify': verify}
diff --git a/src/leap/base/constants.py b/src/leap/base/constants.py
index b38723be..f5665e5f 100644
--- a/src/leap/base/constants.py
+++ b/src/leap/base/constants.py
@@ -1,6 +1,7 @@
"""constants to be used in base module"""
from leap import __branding
-APP_NAME = __branding.get("short_name", "leap")
+APP_NAME = __branding.get("short_name", "leap-client")
+OPENVPN_BIN = "openvpn"
# default provider placeholder
# using `example.org` we make sure that this
diff --git a/src/leap/base/exceptions.py b/src/leap/base/exceptions.py
index 227da953..2e31b33b 100644
--- a/src/leap/base/exceptions.py
+++ b/src/leap/base/exceptions.py
@@ -14,6 +14,7 @@ Exception attributes and their meaning/uses
* usermessage: the message that will be passed to user in ErrorDialogs
in Qt-land.
"""
+from leap.util.translations import translate
class LeapException(Exception):
@@ -22,6 +23,7 @@ class LeapException(Exception):
sets some parameters that we will check
during error checking routines
"""
+
critical = False
failfirst = False
warning = False
@@ -46,32 +48,50 @@ class ImproperlyConfigured(Exception):
pass
-class NoDefaultInterfaceFoundError(LeapException):
- message = "no default interface found"
- usermessage = "Looks like your computer is not connected to the internet"
+# NOTE: "Errors" (context) has to be a explicit string!
class InterfaceNotFoundError(LeapException):
# XXX should take iface arg on init maybe?
message = "interface not found"
+ usermessage = translate(
+ "Errors",
+ "Interface not found")
+
+
+class NoDefaultInterfaceFoundError(LeapException):
+ message = "no default interface found"
+ usermessage = translate(
+ "Errors",
+ "Looks like your computer "
+ "is not connected to the internet")
class NoConnectionToGateway(CriticalError):
message = "no connection to gateway"
- usermessage = "Looks like there are problems with your internet connection"
+ usermessage = translate(
+ "Errors",
+ "Looks like there are problems "
+ "with your internet connection")
class NoInternetConnection(CriticalError):
message = "No Internet connection found"
- usermessage = "It looks like there is no internet connection."
+ usermessage = translate(
+ "Errors",
+ "It looks like there is no internet connection.")
# and now we try to connect to our web to troubleshoot LOL :P
class CannotResolveDomainError(LeapException):
message = "Cannot resolve domain"
- usermessage = "Domain cannot be found"
+ usermessage = translate(
+ "Errors",
+ "Domain cannot be found")
-class TunnelNotDefaultRouteError(CriticalError):
+class TunnelNotDefaultRouteError(LeapException):
message = "Tunnel connection dissapeared. VPN down?"
- usermessage = "The Encrypted Connection was lost. Shutting down..."
+ usermessage = translate(
+ "Errors",
+ "The Encrypted Connection was lost.")
diff --git a/src/leap/base/network.py b/src/leap/base/network.py
index 765d8ea0..d841e692 100644
--- a/src/leap/base/network.py
+++ b/src/leap/base/network.py
@@ -21,8 +21,8 @@ class NetworkCheckerThread(object):
connection.
"""
def __init__(self, *args, **kwargs):
+
self.status_signals = kwargs.pop('status_signals', None)
- #self.watcher_cb = kwargs.pop('status_signals', None)
self.error_cb = kwargs.pop(
'error_cb',
lambda exc: logger.error("%s", exc.message))
@@ -48,6 +48,7 @@ class NetworkCheckerThread(object):
(self.error_cb,))
def stop(self):
+ self.process_handle.join(timeout=0.1)
self.shutdown.set()
logger.debug("network checked stopped.")
@@ -59,6 +60,7 @@ class NetworkCheckerThread(object):
#here all the observers in fail_callbacks expect one positional argument,
#which is exception so we can try by passing a lambda with logger to
#check it works.
+
def _network_checks_thread(self, fail_callbacks):
#TODO: replace this with waiting for a signal from openvpn
while True:
@@ -69,11 +71,15 @@ class NetworkCheckerThread(object):
# XXX ??? why do we sleep here???
# aa: If the openvpn isn't up and running yet,
# let's give it a moment to breath.
+ #logger.error('NOT DEFAULT ROUTE!----')
+ # Instead of this, we should flag when the
+ # iface IS SUPPOSED to be up imo. -- kali
sleep(1)
fail_observer_dict = dict(((
observer,
process_events(observer)) for observer in fail_callbacks))
+
while not self.shutdown.is_set():
try:
self.checker.check_tunnel_default_interface()
@@ -83,11 +89,18 @@ class NetworkCheckerThread(object):
for obs in fail_observer_dict:
fail_observer_dict[obs].send(exc)
sleep(ROUTE_CHECK_INTERVAL)
+
#reset event
+ # I see a problem with this. You cannot stop it, it
+ # resets itself forever. -- kali
+
+ # XXX use QTimer for the recurrent triggers,
+ # and ditch the sleeps.
+ logger.debug('resetting event')
self.shutdown.clear()
def _launch_recurrent_network_checks(self, fail_callbacks):
- #we need to wrap the fail callback in a tuple
+ # XXX reimplement using QTimer -- kali
watcher = launch_thread(
self._network_checks_thread,
(fail_callbacks,))
diff --git a/src/leap/base/pluggableconfig.py b/src/leap/base/pluggableconfig.py
index 0ca985ea..3517db6b 100644
--- a/src/leap/base/pluggableconfig.py
+++ b/src/leap/base/pluggableconfig.py
@@ -10,6 +10,8 @@ import urlparse
import jsonschema
+from leap.util.translations import LEAPTranslatable
+
logger = logging.getLogger(__name__)
@@ -118,7 +120,6 @@ adaptors['json'] = JSONAdaptor()
# to proper python types.
# TODO:
-# - multilingual object.
# - HTTPS uri
@@ -132,6 +133,20 @@ class DateType(object):
return time.strftime(self.fmt, data)
+class TranslatableType(object):
+ """
+ a type that casts to LEAPTranslatable objects.
+ Used for labels we get from providers and stuff.
+ """
+
+ def to_python(self, data):
+ return LEAPTranslatable(data)
+
+ # needed? we already have an extended dict...
+ #def get_prep_value(self, data):
+ #return dict(data)
+
+
class URIType(object):
def to_python(self, data):
@@ -164,6 +179,7 @@ types = {
'date': DateType(),
'uri': URIType(),
'https-uri': HTTPSURIType(),
+ 'translatable': TranslatableType(),
}
diff --git a/src/leap/base/specs.py b/src/leap/base/specs.py
index 962aa07d..f57d7e9c 100644
--- a/src/leap/base/specs.py
+++ b/src/leap/base/specs.py
@@ -22,12 +22,16 @@ leap_provider_spec = {
#'required': True,
},
'name': {
- 'type': dict, # XXX multilingual object?
+ #'type': LEAPTranslatable,
+ 'type': dict,
+ 'format': 'translatable',
'default': {u'en': u'Test Provider'}
#'required': True
},
'description': {
+ #'type': LEAPTranslatable,
'type': dict,
+ 'format': 'translatable',
'default': {u'en': u'Test provider'}
},
'enrollment_policy': {
diff --git a/src/leap/base/tests/test_checks.py b/src/leap/base/tests/test_checks.py
index 7a694f89..645e615c 100644
--- a/src/leap/base/tests/test_checks.py
+++ b/src/leap/base/tests/test_checks.py
@@ -37,6 +37,8 @@ class LeapNetworkCheckTest(BaseLeapTest):
"missing meth")
self.assertTrue(hasattr(checker, "ping_gateway"),
"missing meth")
+ self.assertTrue(hasattr(checker, "parse_log_and_react"),
+ "missing meth")
def test_checker_should_actually_call_all_tests(self):
checker = checks.LeapNetworkChecker()
@@ -45,6 +47,7 @@ class LeapNetworkCheckTest(BaseLeapTest):
self.assertTrue(mc.check_internet_connection.called, "not called")
self.assertTrue(mc.check_tunnel_default_interface.called, "not called")
self.assertTrue(mc.is_internet_up.called, "not called")
+ self.assertTrue(mc.parse_log_and_react.called, "not called")
# ping gateway only called if we pass provider_gw
checker = checks.LeapNetworkChecker(provider_gw="0.0.0.0")
@@ -54,6 +57,7 @@ class LeapNetworkCheckTest(BaseLeapTest):
self.assertTrue(mc.check_tunnel_default_interface.called, "not called")
self.assertTrue(mc.ping_gateway.called, "not called")
self.assertTrue(mc.is_internet_up.called, "not called")
+ self.assertTrue(mc.parse_log_and_react.called, "not called")
def test_get_default_interface_no_interface(self):
checker = checks.LeapNetworkChecker()
@@ -134,6 +138,40 @@ class LeapNetworkCheckTest(BaseLeapTest):
mock_ping.side_effect = exceptions.NoConnectionToGateway
checker.check_internet_connection()
+ def test_parse_log_and_react(self):
+ checker = checks.LeapNetworkChecker()
+ to_call = Mock()
+ log = [("leap.openvpn - INFO - Mon Nov 19 13:36:24 2012 "
+ "read UDPv4 [ECONNREFUSED]: Connection refused (code=111)"]
+ err_matrix = [(checks.EVENT_CONNECT_REFUSED, (to_call, ))]
+ checker.parse_log_and_react(log, err_matrix)
+ self.assertTrue(to_call.called)
+
+ log = [("2012-11-19 13:36:26,177 - leap.openvpn - INFO - "
+ "Mon Nov 19 13:36:24 2012 ERROR: Linux route delete command "
+ "failed: external program exited"),
+ ("2012-11-19 13:36:26,178 - leap.openvpn - INFO - "
+ "Mon Nov 19 13:36:24 2012 ERROR: Linux route delete command "
+ "failed: external program exited"),
+ ("2012-11-19 13:36:26,180 - leap.openvpn - INFO - "
+ "Mon Nov 19 13:36:24 2012 ERROR: Linux route delete command "
+ "failed: external program exited"),
+ ("2012-11-19 13:36:26,181 - leap.openvpn - INFO - "
+ "Mon Nov 19 13:36:24 2012 /sbin/ifconfig tun0 0.0.0.0"),
+ ("2012-11-19 13:36:26,182 - leap.openvpn - INFO - "
+ "Mon Nov 19 13:36:24 2012 Linux ip addr del failed: external "
+ "program exited with error stat"),
+ ("2012-11-19 13:36:26,183 - leap.openvpn - INFO - "
+ "Mon Nov 19 13:36:26 2012 SIGTERM[hard,] received, process"
+ "exiting"), ]
+ to_call.reset_mock()
+ checker.parse_log_and_react(log, err_matrix)
+ self.assertFalse(to_call.called)
+
+ to_call.reset_mock()
+ checker.parse_log_and_react([], err_matrix)
+ self.assertFalse(to_call.called)
+
@unittest.skipUnless(_uid == 0, "root only")
def test_ping_gateway(self):
checker = checks.LeapNetworkChecker()
diff --git a/src/leap/base/tests/test_providers.py b/src/leap/base/tests/test_providers.py
index 9c11f270..f257f54d 100644
--- a/src/leap/base/tests/test_providers.py
+++ b/src/leap/base/tests/test_providers.py
@@ -15,10 +15,12 @@ from leap.base import providers
EXPECTED_DEFAULT_CONFIG = {
u"api_version": u"0.1.0",
- u"description": {u'en': u"Test provider"},
+ #u"description": "LEAPTranslatable<{u'en': u'Test provider'}>",
+ u"description": {u'en': u'Test provider'},
u"default_language": u"en",
#u"display_name": {u'en': u"Test Provider"},
u"domain": u"testprovider.example.org",
+ #u'name': "LEAPTranslatable<{u'en': u'Test Provider'}>",
u'name': {u'en': u'Test Provider'},
u"enrollment_policy": u"open",
#u"serial": 1,
@@ -66,6 +68,7 @@ class TestLeapProviderDefinition(BaseLeapTest):
self.definition.save(to=self.testfile, force=True)
deserialized = json.load(open(self.testfile, 'rb'))
self.maxDiff = None
+ #import ipdb;ipdb.set_trace()
self.assertEqual(deserialized, EXPECTED_DEFAULT_CONFIG)
def test_provider_dump_to_slug(self):
@@ -84,8 +87,9 @@ class TestLeapProviderDefinition(BaseLeapTest):
with open(self.testfile, 'w') as wf:
wf.write(json.dumps(EXPECTED_DEFAULT_CONFIG))
self.definition.load(fromfile=self.testfile)
- self.assertDictEqual(self.config,
- EXPECTED_DEFAULT_CONFIG)
+ #self.assertDictEqual(self.config,
+ #EXPECTED_DEFAULT_CONFIG)
+ self.assertItemsEqual(self.config, EXPECTED_DEFAULT_CONFIG)
def test_provider_validation(self):
self.definition.validate(self.config)
diff --git a/src/leap/baseapp/dialogs.py b/src/leap/baseapp/dialogs.py
index 3cb539cf..d256fc99 100644
--- a/src/leap/baseapp/dialogs.py
+++ b/src/leap/baseapp/dialogs.py
@@ -23,7 +23,8 @@ class ErrorDialog(QDialog):
def warningMessage(self, msg, label):
msgBox = QMessageBox(QMessageBox.Warning,
- "QMessageBox.warning()", msg,
+ "LEAP Client Error",
+ msg,
QMessageBox.NoButton, self)
msgBox.addButton("&Ok", QMessageBox.AcceptRole)
if msgBox.exec_() == QMessageBox.AcceptRole:
@@ -34,7 +35,8 @@ class ErrorDialog(QDialog):
def criticalMessage(self, msg, label):
msgBox = QMessageBox(QMessageBox.Critical,
- "QMessageBox.critical()", msg,
+ "LEAP Client Error",
+ msg,
QMessageBox.NoButton, self)
msgBox.addButton("&Ok", QMessageBox.AcceptRole)
msgBox.exec_()
@@ -49,7 +51,8 @@ class ErrorDialog(QDialog):
def confirmMessage(self, msg, label, action):
msgBox = QMessageBox(QMessageBox.Critical,
- "QMessageBox.critical()", msg,
+ self.tr("LEAP Client Error"),
+ msg,
QMessageBox.NoButton, self)
msgBox.addButton("&Ok", QMessageBox.AcceptRole)
msgBox.addButton("&Cancel", QMessageBox.RejectRole)
diff --git a/src/leap/baseapp/eip.py b/src/leap/baseapp/eip.py
index 55ecfa79..4c1fb32d 100644
--- a/src/leap/baseapp/eip.py
+++ b/src/leap/baseapp/eip.py
@@ -9,6 +9,8 @@ from leap.baseapp.dialogs import ErrorDialog
from leap.baseapp import constants
from leap.eip import exceptions as eip_exceptions
from leap.eip.eipconnection import EIPConnection
+from leap.base.checks import EVENT_CONNECT_REFUSED
+from leap.util import geo
logger = logging.getLogger(name=__name__)
@@ -21,6 +23,7 @@ class EIPConductorAppMixin(object):
Connects the eip connect/disconnect logic
to the switches in the app (buttons/menu items).
"""
+ ERR_DIALOG = False
def __init__(self, *args, **kwargs):
opts = kwargs.pop('opts')
@@ -93,6 +96,15 @@ class EIPConductorAppMixin(object):
in the future we plan to derive errors to
our log viewer.
"""
+ if self.ERR_DIALOG:
+ logger.warning('another error dialog suppressed')
+ return
+
+ # XXX this is actually a one-shot.
+ # On the dialog there should be
+ # a reset signal binded to the ok button
+ # or something like that.
+ self.ERR_DIALOG = True
if getattr(error, 'usermessage', None):
message = error.usermessage
@@ -104,6 +116,7 @@ class EIPConductorAppMixin(object):
# launching dialog.
# (so Qt tests can assert stuff)
+
if error.critical:
logger.critical(error.message)
#critical error (non recoverable),
@@ -112,6 +125,7 @@ class EIPConductorAppMixin(object):
ErrorDialog(errtype="critical",
msg=message,
label="critical error")
+
elif error.warning:
logger.warning(error.message)
@@ -162,6 +176,8 @@ class EIPConductorAppMixin(object):
self.status_label.setText(con_status)
self.ip_label.setText(ip)
self.remote_label.setText(remote)
+ self.remote_country.setText(
+ geo.get_country_name(remote))
# status i/o
@@ -174,6 +190,11 @@ class EIPConductorAppMixin(object):
self.tun_read_bytes.setText(tun_read)
self.tun_write_bytes.setText(tun_write)
+ # connection information via management interface
+ log = self.conductor.get_log()
+ error_matrix = [(EVENT_CONNECT_REFUSED, (self.start_or_stopVPN, ))]
+ self.network_checker.checker.parse_log_and_react(log, error_matrix)
+
@QtCore.pyqtSlot()
def start_or_stopVPN(self):
"""
@@ -195,7 +216,7 @@ class EIPConductorAppMixin(object):
else:
# no errors, so go on.
if self.debugmode:
- self.startStopButton.setText('&Disconnect')
+ self.startStopButton.setText(self.tr('&Disconnect'))
self.eip_service_started = True
self.toggleEIPAct()
@@ -209,7 +230,7 @@ class EIPConductorAppMixin(object):
self.network_checker.stop()
self.conductor.disconnect()
if self.debugmode:
- self.startStopButton.setText('&Connect')
+ self.startStopButton.setText(self.tr('&Connect'))
self.eip_service_started = False
self.toggleEIPAct()
self.timer.stop()
diff --git a/src/leap/baseapp/log.py b/src/leap/baseapp/log.py
index 8a7f81c3..636e5bae 100644
--- a/src/leap/baseapp/log.py
+++ b/src/leap/baseapp/log.py
@@ -11,6 +11,7 @@ class LogPaneMixin(object):
a simple log pane
that writes new lines as they come
"""
+ EXCLUDES = ('MANAGEMENT',)
def createLogBrowser(self):
"""
@@ -21,7 +22,7 @@ class LogPaneMixin(object):
logging_layout = QtGui.QVBoxLayout()
self.logbrowser = QtGui.QTextBrowser()
- startStopButton = QtGui.QPushButton("&Connect")
+ startStopButton = QtGui.QPushButton(self.tr("&Connect"))
self.startStopButton = startStopButton
logging_layout.addWidget(self.logbrowser)
@@ -34,9 +35,10 @@ class LogPaneMixin(object):
grid = QtGui.QGridLayout()
self.updateTS = QtGui.QLabel('')
- self.status_label = QtGui.QLabel('Disconnected')
+ self.status_label = QtGui.QLabel(self.tr('Disconnected'))
self.ip_label = QtGui.QLabel('')
self.remote_label = QtGui.QLabel('')
+ self.remote_country = QtGui.QLabel('')
tun_read_label = QtGui.QLabel("tun read")
self.tun_read_bytes = QtGui.QLabel("0")
@@ -47,10 +49,11 @@ class LogPaneMixin(object):
grid.addWidget(self.status_label, 0, 1)
grid.addWidget(self.ip_label, 1, 0)
grid.addWidget(self.remote_label, 1, 1)
- grid.addWidget(tun_read_label, 2, 0)
- grid.addWidget(self.tun_read_bytes, 2, 1)
- grid.addWidget(tun_write_label, 3, 0)
- grid.addWidget(self.tun_write_bytes, 3, 1)
+ grid.addWidget(self.remote_country, 2, 1)
+ grid.addWidget(tun_read_label, 3, 0)
+ grid.addWidget(self.tun_read_bytes, 3, 1)
+ grid.addWidget(tun_write_label, 4, 0)
+ grid.addWidget(self.tun_write_bytes, 4, 1)
self.statusBox.setLayout(grid)
@@ -60,6 +63,7 @@ class LogPaneMixin(object):
simple slot: writes new line to logger Pane.
"""
msg = line[:-1]
- if self.debugmode:
+ if self.debugmode and all(map(lambda w: w not in msg,
+ LogPaneMixin.EXCLUDES)):
self.logbrowser.append(msg)
- vpnlogger.info(msg)
+ vpnlogger.info(msg)
diff --git a/src/leap/baseapp/mainwindow.py b/src/leap/baseapp/mainwindow.py
index 02adab65..b1e5bccf 100644
--- a/src/leap/baseapp/mainwindow.py
+++ b/src/leap/baseapp/mainwindow.py
@@ -57,10 +57,10 @@ class LeapWindow(QtGui.QMainWindow,
settings = QtCore.QSettings()
self.provider_domain = settings.value("provider_domain", None)
- self.eip_username = settings.value("eip_username", None)
+ self.username = settings.value("username", None)
logger.debug('provider: %s', self.provider_domain)
- logger.debug('eip_username: %s', self.eip_username)
+ logger.debug('username: %s', self.username)
provider = self.provider_domain
EIPConductorAppMixin.__init__(
@@ -100,11 +100,11 @@ class LeapWindow(QtGui.QMainWindow,
self.startStopButton.clicked.connect(
lambda: self.start_or_stopVPN())
self.start_eipconnection.connect(
- lambda: self.start_or_stopVPN())
+ self.do_start_eipconnection)
self.shutdownSignal.connect(
self.cleanupAndQuit)
self.initNetworkChecker.connect(
- lambda: self.init_network_checker(self.provider_domain))
+ lambda: self.init_network_checker(self.conductor.provider))
# status change.
# TODO unify
@@ -147,9 +147,9 @@ class LeapWindow(QtGui.QMainWindow,
# launch wizard if needed
if need_wizard:
+ logger.debug('running first run wizard')
self.launch_first_run_wizard()
else: # no wizard needed
- logger.debug('running first run wizard')
self.initReady.emit()
def launch_first_run_wizard(self):
@@ -160,7 +160,7 @@ class LeapWindow(QtGui.QMainWindow,
wizard = FirstRunWizard(
self.conductor,
parent=self,
- eip_username=self.eip_username,
+ username=self.username,
start_eipconnection_signal=self.start_eipconnection,
eip_statuschange_signal=self.eipStatusChange,
quitcallback=self.onWizardCancel)
@@ -174,5 +174,16 @@ class LeapWindow(QtGui.QMainWindow,
self.cleanupAndQuit()
def runchecks_and_eipconnect(self):
+ """
+ shows icon and run init checks
+ """
self.show_systray_icon()
self.initchecks.begin()
+
+ def do_start_eipconnection(self):
+ """
+ shows icon and init eip connection
+ called from the end of wizard
+ """
+ self.show_systray_icon()
+ self.start_or_stopVPN()
diff --git a/src/leap/baseapp/network.py b/src/leap/baseapp/network.py
index a33265e5..a67f6340 100644
--- a/src/leap/baseapp/network.py
+++ b/src/leap/baseapp/network.py
@@ -17,6 +17,8 @@ class NetworkCheckerAppMixin(object):
initialize an instance of the Network Checker,
which gathers error and passes them on.
"""
+ ERR_NETERR = False
+
def __init__(self, *args, **kwargs):
provider = kwargs.pop('provider', None)
if provider:
@@ -41,11 +43,19 @@ class NetworkCheckerAppMixin(object):
slot that receives a network exceptions
and raises a user error message
"""
- logger.debug('handling network exception')
- logger.error(exc.message)
- dialog = ErrorDialog(parent=self)
+ # FIXME this should not HANDLE anything after
+ # the network check thread has been stopped.
- if exc.critical:
- dialog.criticalMessage(exc.usermessage, "network error")
- else:
- dialog.warningMessage(exc.usermessage, "network error")
+ logger.debug('handling network exception')
+ if not self.ERR_NETERR:
+ self.ERR_NETERR = True
+
+ logger.error(exc.message)
+ dialog = ErrorDialog(parent=self)
+ if exc.critical:
+ dialog.criticalMessage(exc.usermessage, "network error")
+ else:
+ dialog.warningMessage(exc.usermessage, "network error")
+
+ self.start_or_stopVPN()
+ self.network_checker.stop()
diff --git a/src/leap/baseapp/systray.py b/src/leap/baseapp/systray.py
index 0dd0f195..77eb3fe9 100644
--- a/src/leap/baseapp/systray.py
+++ b/src/leap/baseapp/systray.py
@@ -1,4 +1,6 @@
import logging
+import sys
+
import sip
sip.setapi('QString', 2)
sip.setapi('QVariant', 2)
@@ -73,7 +75,8 @@ class StatusAwareTrayIconMixin(object):
self.iconpath['connected'])),
self.ConnectionWidgets = con_widgets
- self.statusIconBox = QtGui.QGroupBox("EIP Connection Status")
+ self.statusIconBox = QtGui.QGroupBox(
+ self.tr("EIP Connection Status"))
statusIconLayout = QtGui.QHBoxLayout()
statusIconLayout.addWidget(self.ConnectionWidgets['disconnected'])
statusIconLayout.addWidget(self.ConnectionWidgets['connecting'])
@@ -81,7 +84,8 @@ class StatusAwareTrayIconMixin(object):
statusIconLayout.itemAt(1).widget().hide()
statusIconLayout.itemAt(2).widget().hide()
- self.leapConnStatus = QtGui.QLabel("<b>disconnected</b>")
+ self.leapConnStatus = QtGui.QLabel(
+ self.tr("<b>disconnected</b>"))
statusIconLayout.addWidget(self.leapConnStatus)
self.statusIconBox.setLayout(statusIconLayout)
@@ -111,26 +115,32 @@ class StatusAwareTrayIconMixin(object):
#self.trayIconMenu.customContextMenuRequested.connect(
#self.on_context_menu)
- def bad(self):
- logger.error('this should not be called')
+ #def bad(self):
+ #logger.error('this should not be called')
def createActions(self):
"""
creates actions to be binded to tray icon
"""
# XXX change action name on (dis)connect
- self.connAct = QtGui.QAction("Encryption ON turn &off", self,
- triggered=lambda: self.start_or_stopVPN())
-
- self.detailsAct = QtGui.QAction("&Details...",
- self,
- triggered=self.detailsWin)
- self.aboutAct = QtGui.QAction("&About", self,
- triggered=self.about)
- self.aboutQtAct = QtGui.QAction("About Q&t", self,
- triggered=QtGui.qApp.aboutQt)
- self.quitAction = QtGui.QAction("&Quit", self,
- triggered=self.cleanupAndQuit)
+ self.connAct = QtGui.QAction(
+ self.tr("Encryption ON turn &off"),
+ self,
+ triggered=lambda: self.start_or_stopVPN())
+
+ self.detailsAct = QtGui.QAction(
+ self.tr("&Details..."),
+ self,
+ triggered=self.detailsWin)
+ self.aboutAct = QtGui.QAction(
+ self.tr("&About"), self,
+ triggered=self.about)
+ self.aboutQtAct = QtGui.QAction(
+ self.tr("About Q&t"), self,
+ triggered=QtGui.qApp.aboutQt)
+ self.quitAction = QtGui.QAction(
+ self.tr("&Quit"), self,
+ triggered=self.cleanupAndQuit)
def toggleEIPAct(self):
# this is too simple by now.
@@ -139,15 +149,17 @@ class StatusAwareTrayIconMixin(object):
icon_status = self.conductor.get_icon_name()
if icon_status == "connected":
self.connAct.setEnabled(True)
- self.connAct.setText('Encryption ON turn o&ff')
+ self.connAct.setText(
+ self.tr('Encryption ON turn o&ff'))
return
if icon_status == "disconnected":
self.connAct.setEnabled(True)
- self.connAct.setText('Encryption OFF turn &on')
+ self.connAct.setText(
+ self.tr('Encryption OFF turn &on'))
return
if icon_status == "connecting":
self.connAct.setDisabled(True)
- self.connAct.setText('connecting...')
+ self.connAct.setText(self.tr('connecting...'))
return
def detailsWin(self):
@@ -156,18 +168,21 @@ class StatusAwareTrayIconMixin(object):
self.hide()
else:
self.show()
+ if sys.platform == "darwin":
+ self.raise_()
def about(self):
# move to widget
flavor = BRANDING.get('short_name', None)
- content = ("LEAP client<br>"
- "(version <b>%s</b>)<br>" % VERSION)
+ content = self.tr(
+ ("LEAP client<br>"
+ "(version <b>%s</b>)<br>" % VERSION))
if flavor:
content = content + ('<br>Flavor: <i>%s</i><br>' % flavor)
content = content + (
"<br><a href='https://leap.se/'>"
"https://leap.se</a>")
- QtGui.QMessageBox.about(self, "About", content)
+ QtGui.QMessageBox.about(self, self.tr("About"), content)
def setConnWidget(self, icon_name):
oldlayout = self.statusIconBox.layout()
@@ -205,6 +220,7 @@ class StatusAwareTrayIconMixin(object):
# is failing in a way beyond my understanding.
# (not working the first time it's clicked).
# this works however.
+ # XXX in osx it shows some glitches.
context_menu.exec_(self.trayIcon.geometry().center())
@QtCore.pyqtSlot()
diff --git a/src/leap/crypto/__init__.py b/src/leap/crypto/__init__.py
index e69de29b..8df3fc6c 100644
--- a/src/leap/crypto/__init__.py
+++ b/src/leap/crypto/__init__.py
@@ -0,0 +1,26 @@
+"""
+DEBUG! ----------- gnutls lib: libgnutls.26.dylib
+DEBUG! ----------- gnutls lib: /usr/local/lib/libgnutls.26.dylib
+DEBUG! ----------- gnutls lib: /opt/local/lib/libgnutls.26.dylib
+DEBUG! ----------- gnutls lib: libgnutls-extra.26.dylib
+DEBUG! ----------- gnutls lib: /usr/local/lib/libgnutls-extra.26.dylib
+DEBUG! ----------- gnutls lib: /opt/local/lib/libgnutls-extra.26.dylib
+"""
+import sys
+
+# hackaround pyinstaller ctypes dependencies discovery
+# See:
+# http://www.pyinstaller.org/wiki/Features/CtypesDependencySupport#SolutioninPyInstaller
+# gnutls.library.load_library is using a style of dep loading
+# unsupported by pyinstaller. So we force these imports here.
+
+if sys.platform == "darwin":
+ from ctypes import CDLL
+ try:
+ CDLL("libgnutls.26.dylib")
+ except OSError:
+ pass
+ try:
+ CDLL("libgnutls-extra.26.dylib")
+ except OSError:
+ pass
diff --git a/src/leap/eip/checks.py b/src/leap/eip/checks.py
index 9ae6e5f5..0d07ef08 100644
--- a/src/leap/eip/checks.py
+++ b/src/leap/eip/checks.py
@@ -1,8 +1,9 @@
import logging
-import ssl
+#import ssl
#import platform
import time
import os
+import sys
import gnutls.crypto
#import netifaces
@@ -20,7 +21,10 @@ from leap.eip import config as eipconfig
from leap.eip import constants as eipconstants
from leap.eip import exceptions as eipexceptions
from leap.eip import specs as eipspecs
+from leap.util.certs import get_mac_cabundle
from leap.util.fileutil import mkdir_p
+from leap.util.web import get_https_domain_and_port
+from leap.util.misc import null_check
logger = logging.getLogger(name=__name__)
@@ -46,7 +50,7 @@ reachable and testable as a whole.
def get_branding_ca_cert(domain):
- # XXX deprecated
+ # deprecated
ca_file = BRANDING.get('provider_ca_file')
if ca_file:
return leapcerts.where(ca_file)
@@ -63,6 +67,10 @@ class ProviderCertChecker(object):
self.fetcher = fetcher
self.domain = domain
+ #XXX needs some kind of autoinit
+ #right now we set by hand
+ #by loading and reading provider config
+ self.apidomain = None
self.cacert = eipspecs.provider_ca_path(domain)
def run_all(
@@ -159,36 +167,33 @@ class ProviderCertChecker(object):
if autocacert and verify is True and self.cacert is not None:
logger.debug('verify cert: %s', self.cacert)
verify = self.cacert
- logger.debug('is https working?')
+ if sys.platform == "darwin":
+ verify = get_mac_cabundle()
+ logger.debug('checking https connection')
logger.debug('uri: %s (verify:%s)', uri, verify)
+
try:
self.fetcher.get(uri, verify=verify)
except requests.exceptions.SSLError as exc:
- logger.error("SSLError")
- # XXX RAISE! See #638
- #raise eipexceptions.HttpsBadCertError
- logger.warning('BUG #638 CERT VERIFICATION FAILED! '
- '(this should be CRITICAL)')
- logger.warning('SSLError: %s', exc.message)
+ raise eipexceptions.HttpsBadCertError
except requests.exceptions.ConnectionError:
logger.error('ConnectionError')
raise eipexceptions.HttpsNotSupported
else:
- logger.debug('True')
return True
def check_new_cert_needed(self, skip_download=False, verify=True):
- logger.debug('is new cert needed?')
+ # XXX add autocacert
if not self.is_cert_valid(do_raise=False):
- logger.debug('True')
+ logger.debug('cert needed: true')
self.download_new_client_cert(
skip_download=skip_download,
verify=verify)
return True
- logger.debug('False')
+ logger.debug('cert needed: false')
return False
def download_new_client_cert(self, uri=None, verify=True,
@@ -200,20 +205,20 @@ class ProviderCertChecker(object):
if uri is None:
uri = self._get_client_cert_uri()
# XXX raise InsecureURI or something better
- assert uri.startswith('https')
+ #assert uri.startswith('https')
if verify is True and self.cacert is not None:
verify = self.cacert
+ logger.debug('verify = %s', verify)
fgetfn = self.fetcher.get
if credentials:
user, passwd = credentials
-
- logger.debug('domain = %s', self.domain)
+ logger.debug('apidomain = %s', self.apidomain)
@srpauth_protected(user, passwd,
- server="https://%s" % self.domain,
+ server="https://%s" % self.apidomain,
verify=verify)
def getfn(*args, **kwargs):
return fgetfn(*args, **kwargs)
@@ -225,23 +230,23 @@ class ProviderCertChecker(object):
return fgetfn(*args, **kwargs)
try:
- # XXX FIXME!!!!
- # verify=verify
- # Workaround for #638. return to verification
- # when That's done!!!
- #req = self.fetcher.get(uri, verify=False)
- req = getfn(uri, verify=False)
+ req = getfn(uri, verify=verify)
req.raise_for_status()
except requests.exceptions.SSLError:
logger.warning('SSLError while fetching cert. '
'Look below for stack trace.')
# XXX raise better exception
- raise
+ return self.fail("SSLError")
+ except Exception as exc:
+ return self.fail(exc.message)
+
try:
+ logger.debug('validating cert...')
pemfile_content = req.content
valid = self.is_valid_pemfile(pemfile_content)
if not valid:
+ logger.warning('invalid cert')
return False
cert_path = self._get_client_cert_path()
self.write_cert(pemfile_content, to=cert_path)
@@ -276,10 +281,7 @@ class ProviderCertChecker(object):
cert = gnutls.crypto.X509Certificate(cert_s)
from_ = time.gmtime(cert.activation_time)
to_ = time.gmtime(cert.expiration_time)
- # FIXME BUG ON LEAP_CLI, certs are not valid on gmtime
- # See #1153
return from_ < now() < to_
- #return now() < to_
def is_valid_pemfile(self, cert_s=None):
"""
@@ -308,8 +310,7 @@ class ProviderCertChecker(object):
return u"https://%s/" % self.domain
def _get_client_cert_uri(self):
- # XXX get the whole thing from constants
- return "https://%s/1/cert" % self.domain
+ return "https://%s/1/cert" % self.apidomain
def _get_client_cert_path(self):
return eipspecs.client_cert_path(domain=self.domain)
@@ -336,6 +337,9 @@ class ProviderCertChecker(object):
with open(to, 'w') as cert_f:
cert_f.write(pemfile_content)
+ def set_api_domain(self, domain):
+ self.apidomain = domain
+
class EIPConfigChecker(object):
"""
@@ -355,10 +359,15 @@ class EIPConfigChecker(object):
# if not domain, get from config
self.domain = domain
+ self.apidomain = None
+ self.cacert = eipspecs.provider_ca_path(domain)
- self.eipconfig = eipconfig.EIPConfig(domain=domain)
self.defaultprovider = providers.LeapProviderDefinition(domain=domain)
+ self.defaultprovider.load()
+ self.eipconfig = eipconfig.EIPConfig(domain=domain)
+ self.set_api_domain()
self.eipserviceconfig = eipconfig.EIPServiceConfig(domain=domain)
+ self.eipserviceconfig.load()
def run_all(self, checker=None, skip_download=False):
"""
@@ -442,31 +451,41 @@ class EIPConfigChecker(object):
domain = config.get('provider', None)
uri = self._get_provider_definition_uri(domain=domain)
- # FIXME! Pass ca path verify!!!
- # BUG #638
- # FIXME FIXME FIXME
+ if sys.platform == "darwin":
+ verify = get_mac_cabundle()
+ else:
+ verify = True
+
self.defaultprovider.load(
from_uri=uri,
fetcher=self.fetcher,
- verify=False)
+ verify=verify)
self.defaultprovider.save()
def fetch_eip_service_config(self, skip_download=False,
force_download=False,
- config=None, uri=None, domain=None):
+ config=None, uri=None, # domain=None,
+ autocacert=True):
if skip_download:
return True
if config is None:
+ self.eipserviceconfig.load()
config = self.eipserviceconfig.config
if uri is None:
- if not domain:
- domain = self.domain or config.get('provider', None)
- uri = self._get_eip_service_uri(domain=domain)
+ #XXX
+ #if not domain:
+ #domain = self.domain or config.get('provider', None)
+ uri = self._get_eip_service_uri(
+ domain=self.apidomain)
+
+ if autocacert and self.cacert is not None:
+ verify = self.cacert
self.eipserviceconfig.load(
from_uri=uri,
fetcher=self.fetcher,
- force_download=force_download)
+ force_download=force_download,
+ verify=verify)
self.eipserviceconfig.save()
def check_complete_eip_config(self, config=None):
@@ -474,7 +493,6 @@ class EIPConfigChecker(object):
if config is None:
config = self.eipconfig.config
try:
- 'trying assertions'
assert 'provider' in config
assert config['provider'] is not None
# XXX assert there is gateway !!
@@ -513,3 +531,16 @@ class EIPConfigChecker(object):
uri = "https://%s/%s" % (domain, path)
logger.debug('getting eip service file from %s', uri)
return uri
+
+ def set_api_domain(self):
+ """sets api domain from defaultprovider config object"""
+ api = self.defaultprovider.config.get('api_uri', None)
+ # the caller is responsible for having loaded the config
+ # object at this point
+ if api:
+ api_dom = get_https_domain_and_port(api)
+ self.apidomain = "%s:%s" % api_dom
+
+ def get_api_domain(self):
+ """gets api domain"""
+ return self.apidomain
diff --git a/src/leap/eip/config.py b/src/leap/eip/config.py
index 48e6e9a7..a60d7ed5 100644
--- a/src/leap/eip/config.py
+++ b/src/leap/eip/config.py
@@ -18,6 +18,8 @@ from leap.eip import specs as eipspecs
logger = logging.getLogger(name=__name__)
provider_ca_file = BRANDING.get('provider_ca_file', None)
+_platform = platform.system()
+
class EIPConfig(baseconfig.JSONLeapConfig):
spec = eipspecs.eipconfig_spec
@@ -210,8 +212,13 @@ def build_ovpn_options(daemon=False, socket_path=None, **kwargs):
# interface. unix sockets or telnet interface for win.
# XXX take them from the config object.
- ourplatform = platform.system()
- if ourplatform in ("Linux", "Mac"):
+ if _platform == "Windows":
+ opts.append('--management')
+ opts.append('localhost')
+ # XXX which is a good choice?
+ opts.append('7777')
+
+ if _platform in ("Linux", "Darwin"):
opts.append('--management')
if socket_path is None:
@@ -219,16 +226,20 @@ def build_ovpn_options(daemon=False, socket_path=None, **kwargs):
opts.append(socket_path)
opts.append('unix')
- if ourplatform == "Windows":
- opts.append('--management')
- opts.append('localhost')
- # XXX which is a good choice?
- opts.append('7777')
+ opts.append('--script-security')
+ opts.append('2')
+
+ if _platform == "Linux":
+ opts.append("--up")
+ opts.append("/etc/openvpn/update-resolv-conf")
+ opts.append("--down")
+ opts.append("/etc/openvpn/update-resolv-conf")
# certs
client_cert_path = eipspecs.client_cert_path(provider)
ca_cert_path = eipspecs.provider_ca_path(provider)
+ # XXX FIX paths for MAC
opts.append('--cert')
opts.append(client_cert_path)
opts.append('--key')
@@ -242,7 +253,7 @@ def build_ovpn_options(daemon=False, socket_path=None, **kwargs):
#if daemon is True:
#opts.append('--daemon')
- logger.debug('vpn options: %s', opts)
+ logger.debug('vpn options: %s', ' '.join(opts))
return opts
@@ -262,7 +273,7 @@ def build_ovpn_command(debug=False, do_pkexec_check=True, vpnbin=None,
# XXX get use_pkexec from config instead.
- if platform.system() == "Linux" and use_pkexec and do_pkexec_check:
+ if _platform == "Linux" and use_pkexec and do_pkexec_check:
# check for both pkexec
# AND a suitable authentication
@@ -282,8 +293,16 @@ def build_ovpn_command(debug=False, do_pkexec_check=True, vpnbin=None,
raise eip_exceptions.EIPNoPolkitAuthAgentAvailable
command.append('pkexec')
+
if vpnbin is None:
- ovpn = which('openvpn')
+ if _platform == "Darwin":
+ # XXX Should hardcode our installed path
+ # /Applications/LEAPClient.app/Contents/Resources/openvpn.leap
+ openvpn_bin = "openvpn.leap"
+ else:
+ openvpn_bin = "openvpn"
+ #XXX hardcode for darwin
+ ovpn = which(openvpn_bin)
else:
ovpn = vpnbin
if ovpn:
@@ -299,7 +318,17 @@ def build_ovpn_command(debug=False, do_pkexec_check=True, vpnbin=None,
# XXX check len and raise proper error
- return [command[0], command[1:]]
+ if _platform == "Darwin":
+ OSX_ASADMIN = 'do shell script "%s" with administrator privileges'
+ # XXX fix workaround for Nones
+ _command = [x if x else " " for x in command]
+ # XXX debugging!
+ # XXX get openvpn log path from debug flags
+ _command.append('--log')
+ _command.append('/tmp/leap_openvpn.log')
+ return ["osascript", ["-e", OSX_ASADMIN % ' '.join(_command)]]
+ else:
+ return [command[0], command[1:]]
def check_vpn_keys(provider=None):
diff --git a/src/leap/eip/eipconnection.py b/src/leap/eip/eipconnection.py
index 27734f80..20b45e36 100644
--- a/src/leap/eip/eipconnection.py
+++ b/src/leap/eip/eipconnection.py
@@ -27,6 +27,8 @@ class StatusMixIn(object):
# Should separate EIPConnectionStatus (self.status)
# from the OpenVPN state/status command and parsing.
+ ERR_CONNREFUSED = False
+
def connection_state(self):
"""
returns the current connection state
@@ -49,10 +51,12 @@ class StatusMixIn(object):
state = self.get_connection_state()
except eip_exceptions.ConnectionRefusedError:
# connection refused. might be not ready yet.
- logger.warning('connection refused')
+ if not self.ERR_CONNREFUSED:
+ logger.warning('connection refused')
+ self.ERR_CONNREFUSED = True
return
if not state:
- logger.debug('no state')
+ #logger.debug('no state')
return
(ts, status_step,
ok, ip, remote) = state
diff --git a/src/leap/eip/exceptions.py b/src/leap/eip/exceptions.py
index 41eed77a..b7d398c3 100644
--- a/src/leap/eip/exceptions.py
+++ b/src/leap/eip/exceptions.py
@@ -33,6 +33,7 @@ TODO:
"""
from leap.base.exceptions import LeapException
+from leap.util.translations import translate
# This should inherit from LeapException
@@ -62,53 +63,69 @@ class Warning(EIPClientError):
class EIPNoPolkitAuthAgentAvailable(CriticalError):
message = "No polkit authentication agent could be found"
- usermessage = ("We could not find any authentication "
- "agent in your system.<br/>"
- "Make sure you have "
- "<b>polkit-gnome-authentication-agent-1</b> "
- "running and try again.")
+ usermessage = translate(
+ "EIPErrors",
+ "We could not find any authentication "
+ "agent in your system.<br/>"
+ "Make sure you have "
+ "<b>polkit-gnome-authentication-agent-1</b> "
+ "running and try again.")
class EIPNoPkexecAvailable(Warning):
message = "No pkexec binary found"
- usermessage = ("We could not find <b>pkexec</b> in your "
- "system.<br/> Do you want to try "
- "<b>setuid workaround</b>? "
- "(<i>DOES NOTHING YET</i>)")
+ usermessage = translate(
+ "EIPErrors",
+ "We could not find <b>pkexec</b> in your "
+ "system.<br/> Do you want to try "
+ "<b>setuid workaround</b>? "
+ "(<i>DOES NOTHING YET</i>)")
failfirst = True
class EIPNoCommandError(EIPClientError):
message = "no suitable openvpn command found"
- usermessage = ("No suitable openvpn command found. "
- "<br/>(Might be a permissions problem)")
+ usermessage = translate(
+ "EIPErrors",
+ "No suitable openvpn command found. "
+ "<br/>(Might be a permissions problem)")
class EIPBadCertError(Warning):
# XXX this should be critical and fail close
message = "cert verification failed"
- usermessage = "there is a problem with provider certificate"
+ usermessage = translate(
+ "EIPErrors",
+ "there is a problem with provider certificate")
class LeapBadConfigFetchedError(Warning):
message = "provider sent a malformed json file"
- usermessage = "an error occurred during configuratio of leap services"
+ usermessage = translate(
+ "EIPErrors",
+ "an error occurred during configuratio of leap services")
-class OpenVPNAlreadyRunning(EIPClientError):
+class OpenVPNAlreadyRunning(CriticalError):
message = "Another OpenVPN Process is already running."
- usermessage = ("Another OpenVPN Process has been detected."
- "Please close it before starting leap-client")
+ usermessage = translate(
+ "EIPErrors",
+ "Another OpenVPN Process has been detected. "
+ "Please close it before starting leap-client")
class HttpsNotSupported(LeapException):
message = "connection refused while accessing via https"
- usermessage = "Server does not allow secure connections."
+ usermessage = translate(
+ "EIPErrors",
+ "Server does not allow secure connections")
class HttpsBadCertError(LeapException):
message = "verification error on cert"
- usermessage = "Server certificate could not be verified."
+ usermessage = translate(
+ "EIPErrors",
+ "Server certificate could not be verified")
#
# errors still needing some love
@@ -117,7 +134,9 @@ class HttpsBadCertError(LeapException):
class EIPInitNoKeyFileError(CriticalError):
message = "No vpn keys found in the expected path"
- usermessage = "We could not find your eip certs in the expected path"
+ usermessage = translate(
+ "EIPErrors",
+ "We could not find your eip certs in the expected path")
class EIPInitBadKeyFilePermError(Warning):
diff --git a/src/leap/eip/openvpnconnection.py b/src/leap/eip/openvpnconnection.py
index c2dc71a6..05979ff7 100644
--- a/src/leap/eip/openvpnconnection.py
+++ b/src/leap/eip/openvpnconnection.py
@@ -2,17 +2,21 @@
OpenVPN Connection
"""
from __future__ import (print_function)
+from functools import partial
import logging
import os
import psutil
import shutil
+import select
import socket
-from functools import partial
+from time import sleep
logger = logging.getLogger(name=__name__)
from leap.base.connection import Connection
+from leap.base.constants import OPENVPN_BIN
from leap.util.coroutines import spawn_and_watch_process
+from leap.util.misc import get_openvpn_pids
from leap.eip.udstelnet import UDSTelnet
from leap.eip import config as eip_config
@@ -83,7 +87,7 @@ class OpenVPNManagement(object):
try:
self._connect_to_management()
except eip_exceptions.MissingSocketError:
- logger.warning('missing management socket')
+ #logger.warning('missing management socket')
return []
try:
if hasattr(self, 'tn'):
@@ -92,14 +96,19 @@ class OpenVPNManagement(object):
logger.error('socket error')
self._close_management_socket(announce=False)
return []
- buf = self.tn.read_until(b"END", 2)
- self._seek_to_eof()
- blist = buf.split('\r\n')
- if blist[-1].startswith('END'):
- del blist[-1]
- return blist
- else:
- return []
+ try:
+ buf = self.tn.read_until(b"END", 2)
+ self._seek_to_eof()
+ blist = buf.split('\r\n')
+ if blist[-1].startswith('END'):
+ del blist[-1]
+ return blist
+ else:
+ return []
+ except socket.error as exc:
+ logger.debug('socket error: %s' % exc.message)
+ except select.error as exc:
+ logger.debug('select error: %s' % exc.message)
def _send_short_command(self, cmd):
"""
@@ -271,22 +280,20 @@ to be triggered for each one of them.
# checks
+
def _check_if_running_instance(self):
"""
check if openvpn is already running
"""
- try:
- for process in psutil.get_process_list():
- if process.name == "openvpn":
- logger.debug('an openvpn instance is already running.')
- logger.debug('attempting to stop openvpn instance.')
- if not self._stop_openvpn():
- raise eip_exceptions.OpenVPNAlreadyRunning
-
- except psutil.error.NoSuchProcess:
- logger.debug('detected a process which died. passing.')
-
- logger.debug('no openvpn instance found.')
+ openvpn_pids = get_openvpn_pids()
+ if openvpn_pids:
+ logger.debug('an openvpn instance is already running.')
+ logger.debug('attempting to stop openvpn instance.')
+ if not self._stop_openvpn():
+ raise eip_exceptions.OpenVPNAlreadyRunning
+ return
+ else:
+ logger.debug('no openvpn instance found.')
def _set_ovpn_command(self):
try:
@@ -327,12 +334,13 @@ to be triggered for each one of them.
#deprecate watcher_cb,
#use _only_ signal_maps instead
- logger.debug('_launch_openvpn called')
+ #logger.debug('_launch_openvpn called')
if self.watcher_cb is not None:
linewrite_callback = self.watcher_cb
else:
#XXX get logger instead
- linewrite_callback = lambda line: print('watcher: %s' % line)
+ linewrite_callback = lambda line: logger.debug(
+ 'watcher: %s' % line)
# the partial is not
# being applied now because we're not observing the process
@@ -340,7 +348,8 @@ to be triggered for each one of them.
# here since it will be handy for observing patterns in the
# thru-the-manager updates (with regex)
observers = (linewrite_callback,
- partial(lambda con_status, line: None, self.status))
+ partial(lambda con_status,
+ line: linewrite_callback, self.status))
subp, watcher = spawn_and_watch_process(
self.command,
self.args,
@@ -355,23 +364,24 @@ to be triggered for each one of them.
interface
"""
# XXX method a bit too long, split
- logger.debug("terminating openvpn process...")
+ logger.debug("atempting to terminate openvpn process...")
if self.connected():
try:
self._send_command("signal SIGTERM\n")
+ sleep(1)
+ if not self.subp: # XXX ???
+ return True
except socket.error:
logger.warning('management socket died')
return
- if self.subp:
- # ???
- return True
#shutting openvpn failured
#try patching in old openvpn host and trying again
+ # XXX could be more than one!
process = self._get_openvpn_process()
if process:
- logger.debug('process :%s' % process)
+ logger.debug('process: %s' % process.name)
cmdline = process.cmdline
manag_flag = "--management"
@@ -392,9 +402,11 @@ to be triggered for each one of them.
return True
def _get_openvpn_process(self):
- # plist = [p for p in psutil.get_process_list() if p.name == "openvpn"]
- # return plist[0] if plist else None
- for process in psutil.get_process_list():
- if process.name == "openvpn":
+ for process in psutil.process_iter():
+ if OPENVPN_BIN in process.name:
return process
return None
+
+ def get_log(self, lines=1):
+ log = self._send_command("log %s" % lines)
+ return log
diff --git a/src/leap/gui/firstrun/__init__.py b/src/leap/gui/firstrun/__init__.py
index d380b75a..2a523d6a 100644
--- a/src/leap/gui/firstrun/__init__.py
+++ b/src/leap/gui/firstrun/__init__.py
@@ -6,6 +6,7 @@ except ValueError:
pass
import intro
+import connect
import last
import login
import mixins
@@ -13,10 +14,10 @@ import providerinfo
import providerselect
import providersetup
import register
-import regvalidation
__all__ = [
'intro',
+ 'connect',
'last',
'login',
'mixins',
@@ -24,4 +25,4 @@ __all__ = [
'providerselect',
'providersetup',
'register',
- 'regvalidation'] # ,'wizard']
+ ] # ,'wizard']
diff --git a/src/leap/gui/firstrun/regvalidation.py b/src/leap/gui/firstrun/connect.py
index b86583e0..b7688380 100644
--- a/src/leap/gui/firstrun/regvalidation.py
+++ b/src/leap/gui/firstrun/connect.py
@@ -2,15 +2,7 @@
Provider Setup Validation Page,
used in First Run Wizard
"""
-# XXX This page is called regvalidation
-# but it's implementing functionality in the former
-# connect page.
-# We should remame it to connect again, when we integrate
-# the login branch of the wizard.
-
import logging
-#import json
-#import socket
from PyQt4 import QtGui
@@ -25,15 +17,15 @@ from leap.gui.constants import APP_LOGO
logger = logging.getLogger(__name__)
-class RegisterUserValidationPage(ValidationPage):
+class ConnectionPage(ValidationPage):
def __init__(self, parent=None):
- super(RegisterUserValidationPage, self).__init__(parent)
- self.current_page = "signupvalidation"
+ super(ConnectionPage, self).__init__(parent)
+ self.current_page = "connect"
- title = "Connecting..."
+ title = self.tr("Connecting...")
# XXX uh... really?
- subtitle = "Checking connection with provider."
+ subtitle = self.tr("Checking connection with provider.")
self.setTitle(title)
self.setSubTitle(subtitle)
@@ -52,32 +44,31 @@ class RegisterUserValidationPage(ValidationPage):
wizard = self.wizard()
full_domain = self.field('provider_domain')
domain, port = get_https_domain_and_port(full_domain)
- _domain = u"%s:%s" % (domain, port) if port != 443 else unicode(domain)
- # FIXME #BUG 638 FIXME FIXME FIXME
- verify = False # !!!!!!!!!!!!!!!!
- # FIXME #BUG 638 FIXME FIXME FIXME
+ pconfig = wizard.eipconfigchecker(domain=domain)
+ # this should be persisted...
+ pconfig.defaultprovider.load()
+ pconfig.set_api_domain()
+
+ pCertChecker = wizard.providercertchecker(
+ domain=domain)
+ pCertChecker.set_api_domain(pconfig.apidomain)
###########################################
# Set Credentials.
# username and password are in different fields
# if they were stored in log_in or sign_up pages.
- is_signup = self.field("is_signup")
+ from_login = wizard.from_login
unamek_base = 'userName'
passwk_base = 'userPassword'
- unamek = 'login_%s' % unamek_base if not is_signup else unamek_base
- passwk = 'login_%s' % passwk_base if not is_signup else passwk_base
+ unamek = 'login_%s' % unamek_base if from_login else unamek_base
+ passwk = 'login_%s' % passwk_base if from_login else passwk_base
username = self.field(unamek)
password = self.field(passwk)
credentials = username, password
- eipconfigchecker = wizard.eipconfigchecker(domain=_domain)
- #XXX change for _domain (sanitized)
- pCertChecker = wizard.providercertchecker(
- domain=full_domain)
-
yield(("head_sentinel", 0), lambda: None)
##################################################
@@ -85,8 +76,7 @@ class RegisterUserValidationPage(ValidationPage):
##################################################
def fetcheipconf():
try:
- eipconfigchecker.fetch_eip_service_config(
- domain=full_domain)
+ pconfig.fetch_eip_service_config()
# XXX get specific exception
except Exception as exc:
@@ -102,8 +92,7 @@ class RegisterUserValidationPage(ValidationPage):
def fetcheipcert():
try:
downloaded = pCertChecker.download_new_client_cert(
- credentials=credentials,
- verify=verify)
+ credentials=credentials)
if not downloaded:
logger.error('Could not download client cert.')
return False
@@ -111,6 +100,9 @@ class RegisterUserValidationPage(ValidationPage):
except auth.SRPAuthenticationError as exc:
return self.fail(self.tr(
"Authentication error: %s" % exc.message))
+
+ except Exception as exc:
+ return self.fail(exc.message)
else:
return True
@@ -182,8 +174,8 @@ class RegisterUserValidationPage(ValidationPage):
called after _do_checks has finished
(connected to checker thread finished signal)
"""
- is_signup = self.field("is_signup")
- prevpage = "signup" if is_signup else "login"
+ from_login = self.wizard().from_login
+ prevpage = "login" if from_login else "signup"
wizard = self.wizard()
if self.errors:
@@ -200,11 +192,11 @@ class RegisterUserValidationPage(ValidationPage):
def nextId(self):
wizard = self.wizard()
- if not wizard:
- return
+ #if not wizard:
+ #return
return wizard.get_page_index('lastpage')
def initializePage(self):
- super(RegisterUserValidationPage, self).initializePage()
+ super(ConnectionPage, self).initializePage()
self.set_undone()
self.completeChanged.emit()
diff --git a/src/leap/gui/firstrun/intro.py b/src/leap/gui/firstrun/intro.py
index 0a7484e2..0425b764 100644
--- a/src/leap/gui/firstrun/intro.py
+++ b/src/leap/gui/firstrun/intro.py
@@ -50,7 +50,7 @@ class IntroPage(QtGui.QWizardPage):
layout.addWidget(radiobuttonGroup)
self.setLayout(layout)
- self.registerField('is_signup', self.sign_up)
+ #self.registerField('is_signup', self.sign_up)
def validatePage(self):
return True
diff --git a/src/leap/gui/firstrun/last.py b/src/leap/gui/firstrun/last.py
index 1d8caca4..e097b2ae 100644
--- a/src/leap/gui/firstrun/last.py
+++ b/src/leap/gui/firstrun/last.py
@@ -15,7 +15,7 @@ class LastPage(QtGui.QWizardPage):
def __init__(self, parent=None):
super(LastPage, self).__init__(parent)
- self.setTitle("Connecting to Encrypted Internet Proxy service...")
+ self.setTitle(self.tr("Connecting to Encrypted Internet Proxy service..."))
self.setPixmap(
QtGui.QWizard.LogoPixmap,
@@ -51,13 +51,13 @@ class LastPage(QtGui.QWizardPage):
statusline.setText(status)
def set_finished_status(self):
- self.setTitle('You are now using an encrypted connection!')
+ self.setTitle(self.tr('You are now using an encrypted connection!'))
finishText = self.wizard().buttonText(
QtGui.QWizard.FinishButton)
finishText = finishText.replace('&', '')
- self.label.setText(
+ self.label.setText(self.tr(
"Click '<i>%s</i>' to end the wizard and "
- "save your settings." % finishText)
+ "save your settings." % finishText))
# XXX init network checker
# trigger signal
diff --git a/src/leap/gui/firstrun/login.py b/src/leap/gui/firstrun/login.py
index e7afee9f..e39eecc0 100644
--- a/src/leap/gui/firstrun/login.py
+++ b/src/leap/gui/firstrun/login.py
@@ -21,8 +21,8 @@ class LogInPage(InlineValidationPage, UserFormMixIn): # InlineValidationPage
super(LogInPage, self).__init__(parent)
self.current_page = "login"
- self.setTitle("Log In")
- self.setSubTitle("Log in with your credentials.")
+ self.setTitle(self.tr("Log In"))
+ self.setSubTitle(self.tr("Log in with your credentials."))
self.current_page = "login"
self.setPixmap(
@@ -35,7 +35,7 @@ class LogInPage(InlineValidationPage, UserFormMixIn): # InlineValidationPage
self.do_confirm_next = False
def setupUI(self):
- userNameLabel = QtGui.QLabel("User &name:")
+ userNameLabel = QtGui.QLabel(self.tr("User &name:"))
userNameLineEdit = QtGui.QLineEdit()
userNameLineEdit.cursorPositionChanged.connect(
self.reset_validation_status)
@@ -50,7 +50,7 @@ class LogInPage(InlineValidationPage, UserFormMixIn): # InlineValidationPage
#'username@provider.example.org')
self.userNameLineEdit = userNameLineEdit
- userPasswordLabel = QtGui.QLabel("&Password:")
+ userPasswordLabel = QtGui.QLabel(self.tr("&Password:"))
self.userPasswordLineEdit = QtGui.QLineEdit()
self.userPasswordLineEdit.setEchoMode(
QtGui.QLineEdit.Password)
@@ -77,7 +77,7 @@ class LogInPage(InlineValidationPage, UserFormMixIn): # InlineValidationPage
layout.addWidget(self.valFrame, 4, 2, 4, 2)
self.valFrame.hide()
- self.nextText("Log in")
+ self.nextText(self.tr("Log in"))
self.setLayout(layout)
#self.registerField('is_login_wizard')
@@ -108,7 +108,7 @@ class LogInPage(InlineValidationPage, UserFormMixIn): # InlineValidationPage
# page here as a mean to catch
# srp authentication errors while
wizard = self.wizard()
- eipconfigchecker = wizard.eipconfigchecker()
+ eipconfigchecker = wizard.eipconfigchecker(domain=domain)
########################
# 1) try name resolution
@@ -321,6 +321,7 @@ class LogInPage(InlineValidationPage, UserFormMixIn): # InlineValidationPage
self.setField('provider_domain', domain)
self.setField('login_userName', username)
self.setField('login_userPassword', password)
+ self.wizard().from_login = True
return True
diff --git a/src/leap/gui/firstrun/providerinfo.py b/src/leap/gui/firstrun/providerinfo.py
index c5b2984c..357378df 100644
--- a/src/leap/gui/firstrun/providerinfo.py
+++ b/src/leap/gui/firstrun/providerinfo.py
@@ -6,6 +6,7 @@ import logging
from PyQt4 import QtGui
from leap.gui.constants import APP_LOGO
+from leap.util.translations import translate
logger = logging.getLogger(__name__)
@@ -62,6 +63,7 @@ class ProviderInfoPage(QtGui.QWizardPage):
# this should be better handled with signals !!
self.displayName = displayName
self.description = description
+ self.description.setWordWrap(True)
self.enrollment_policy = enrollment_policy
def show_provider_info(self):
@@ -72,7 +74,7 @@ class ProviderInfoPage(QtGui.QWizardPage):
lang = "en"
pconfig = self.wizard().providerconfig
- dn = pconfig.get('display_name')
+ dn = pconfig.get('name')
display_name = dn[lang] if dn else ''
domain_name = self.field('provider_domain')
@@ -80,7 +82,10 @@ class ProviderInfoPage(QtGui.QWizardPage):
"<b>%s</b> https://%s" % (display_name, domain_name))
desc = pconfig.get('description')
- description_text = desc[lang] if desc else ''
+
+ #description_text = desc[lang] if desc else ''
+ description_text = translate(desc) if desc else ''
+
self.description.setText(
"<i>%s</i>" % description_text)
diff --git a/src/leap/gui/firstrun/providerselect.py b/src/leap/gui/firstrun/providerselect.py
index fd48f7f9..ccecd519 100644
--- a/src/leap/gui/firstrun/providerselect.py
+++ b/src/leap/gui/firstrun/providerselect.py
@@ -64,7 +64,7 @@ class SelectProviderPage(InlineValidationPage):
providerNameLabel.setBuddy(providerNameEdit)
# add regex validator
- providerDomainRe = QtCore.QRegExp(r"^[a-z\d_-.]+$")
+ providerDomainRe = QtCore.QRegExp(r"^[a-z1-9_\-\.]+$")
providerNameEdit.setValidator(
QtGui.QRegExpValidator(providerDomainRe, self))
self.providerNameEdit = providerNameEdit
@@ -101,7 +101,7 @@ class SelectProviderPage(InlineValidationPage):
self.certInfo.setWordWrap(True)
self.certWarning = QtGui.QLabel("")
self.trustProviderCertCheckBox = QtGui.QCheckBox(
- "&Trust this provider certificate.")
+ self.tr("&Trust this provider certificate."))
self.trustProviderCertCheckBox.stateChanged.connect(
self.onTrustCheckChanged)
@@ -287,8 +287,6 @@ class SelectProviderPage(InlineValidationPage):
wizard.set_providerconfig(
eipconfigchecker.defaultprovider.config)
except requests.exceptions.SSLError:
- # XXX we should have catched this before.
- # but cert checking is broken.
return self.fail(self.tr(
"Could not get info from provider."))
except requests.exceptions.ConnectionError:
@@ -344,9 +342,10 @@ class SelectProviderPage(InlineValidationPage):
def add_cert_info(self, certinfo): # pragma: no cover XXX
self.certWarning.setText(
- "Do you want to <b>trust this provider certificate?</b>")
+ self.tr("Do you want to <b>trust this provider certificate?</b>"))
+ # XXX Check if this needs to abstracted to remove certinfo
self.certInfo.setText(
- 'SHA-256 fingerprint: <i>%s</i><br>' % certinfo)
+ self.tr('SHA-256 fingerprint: <i>%s</i><br>' % certinfo))
self.certInfo.setWordWrap(True)
self.certinfoGroup.show()
diff --git a/src/leap/gui/firstrun/providersetup.py b/src/leap/gui/firstrun/providersetup.py
index 1a362794..981e3214 100644
--- a/src/leap/gui/firstrun/providersetup.py
+++ b/src/leap/gui/firstrun/providersetup.py
@@ -4,6 +4,8 @@ used if First Run Wizard
"""
import logging
+import requests
+
from PyQt4 import QtGui
from leap.base import exceptions as baseexceptions
@@ -20,8 +22,8 @@ class ProviderSetupValidationPage(ValidationPage):
self.current_page = "providersetupvalidation"
# XXX needed anymore?
- is_signup = self.field("is_signup")
- self.is_signup = is_signup
+ #is_signup = self.field("is_signup")
+ #self.is_signup = is_signup
self.setTitle(self.tr("Provider setup"))
self.setSubTitle(
@@ -110,26 +112,15 @@ class ProviderSetupValidationPage(ValidationPage):
#########################
def validatecacert():
- pass
- #api_uri = pconfig.get('api_uri', None)
- #try:
- #api_cert_verified = pCertChecker.verify_api_https(api_uri)
- #except requests.exceptions.SSLError as exc:
- #logger.error('BUG #638. %s' % exc.message)
- # XXX RAISE! See #638
- # bypassing until the hostname is fixed.
- # We probably should raise yet-another-warning
- # here saying user that the hostname "XX.XX.XX.XX' does not
- # match 'foo.bar.baz'
- #api_cert_verified = True
-
- #if not api_cert_verified:
- # XXX update validationMsg
- # should catch exception
- #return False
-
- #???
- #ca_cert_path = checker.ca_cert_path
+ api_uri = pconfig.get('api_uri', None)
+ try:
+ pCertChecker.verify_api_https(api_uri)
+ except requests.exceptions.SSLError as exc:
+ return self.fail("Validation Error")
+ except Exception as exc:
+ return self.fail(exc.msg)
+ else:
+ return True
yield((self.tr('Validating api certificate'), 90), validatecacert)
@@ -141,8 +132,8 @@ class ProviderSetupValidationPage(ValidationPage):
called after _do_checks has finished
(connected to checker thread finished signal)
"""
- prevpage = "providerselection" if self.is_signup else "login"
wizard = self.wizard()
+ prevpage = "login" if wizard.from_login else "providerselection"
if self.errors:
logger.debug('going back with errors')
@@ -158,14 +149,12 @@ class ProviderSetupValidationPage(ValidationPage):
def nextId(self):
wizard = self.wizard()
- if not wizard:
- return
- is_signup = self.field('is_signup')
- if is_signup is True:
- next_ = 'signup'
- if is_signup is False:
+ from_login = wizard.from_login
+ if from_login:
# XXX bad name. change to connect again.
next_ = 'signupvalidation'
+ else:
+ next_ = 'signup'
return wizard.get_page_index(next_)
def initializePage(self):
diff --git a/src/leap/gui/firstrun/register.py b/src/leap/gui/firstrun/register.py
index 4c811093..741b9267 100644
--- a/src/leap/gui/firstrun/register.py
+++ b/src/leap/gui/firstrun/register.py
@@ -45,7 +45,7 @@ class RegisterUserPage(InlineValidationPage, UserFormMixIn):
self.focused_field = False
def setupUI(self):
- userNameLabel = QtGui.QLabel("User &name:")
+ userNameLabel = QtGui.QLabel(self.tr("User &name:"))
userNameLineEdit = QtGui.QLineEdit()
userNameLineEdit.cursorPositionChanged.connect(
self.reset_validation_status)
@@ -57,20 +57,20 @@ class RegisterUserPage(InlineValidationPage, UserFormMixIn):
QtGui.QRegExpValidator(usernameRe, self))
self.userNameLineEdit = userNameLineEdit
- userPasswordLabel = QtGui.QLabel("&Password:")
+ userPasswordLabel = QtGui.QLabel(self.tr("&Password:"))
self.userPasswordLineEdit = QtGui.QLineEdit()
self.userPasswordLineEdit.setEchoMode(
QtGui.QLineEdit.Password)
userPasswordLabel.setBuddy(self.userPasswordLineEdit)
- userPassword2Label = QtGui.QLabel("Password (again):")
+ userPassword2Label = QtGui.QLabel(self.tr("Password (again):"))
self.userPassword2LineEdit = QtGui.QLineEdit()
self.userPassword2LineEdit.setEchoMode(
QtGui.QLineEdit.Password)
userPassword2Label.setBuddy(self.userPassword2LineEdit)
rememberPasswordCheckBox = QtGui.QCheckBox(
- "&Remember username and password.")
+ self.tr("&Remember username and password."))
rememberPasswordCheckBox.setChecked(True)
self.registerField('userName*', self.userNameLineEdit)
@@ -224,11 +224,17 @@ class RegisterUserPage(InlineValidationPage, UserFormMixIn):
generator that yields actual checks
that are executed in a separate thread
"""
+ wizard = self.wizard()
+
provider = self.field('provider_domain')
username = self.userNameLineEdit.text()
password = self.userPasswordLineEdit.text()
password2 = self.userPassword2LineEdit.text()
+ pconfig = wizard.eipconfigchecker(domain=provider)
+ pconfig.defaultprovider.load()
+ pconfig.set_api_domain()
+
def checkpass():
# we better have here
# some call to a password checker...
@@ -263,14 +269,11 @@ class RegisterUserPage(InlineValidationPage, UserFormMixIn):
self, "showStepsFrame")
def register():
- # XXX FIXME!
- verify = False
signup = auth.LeapSRPRegister(
schema="https",
- provider=provider,
- verify=verify)
- #import ipdb;ipdb.set_trace()
+ provider=pconfig.apidomain,
+ verify=pconfig.cacert)
try:
ok, req = signup.register_user(
username, password)
@@ -381,7 +384,4 @@ class RegisterUserPage(InlineValidationPage, UserFormMixIn):
def nextId(self):
wizard = self.wizard()
- #if not wizard:
- #return
- # XXX this should be called connect
- return wizard.get_page_index('signupvalidation')
+ return wizard.get_page_index('connect')
diff --git a/src/leap/gui/firstrun/wizard.py b/src/leap/gui/firstrun/wizard.py
index 89209401..427f9df8 100755
--- a/src/leap/gui/firstrun/wizard.py
+++ b/src/leap/gui/firstrun/wizard.py
@@ -59,8 +59,8 @@ def get_pages_dict():
('providersetupvalidation',
firstrun.providersetup.ProviderSetupValidationPage),
('signup', firstrun.register.RegisterUserPage),
- ('signupvalidation',
- firstrun.regvalidation.RegisterUserValidationPage),
+ ('connect',
+ firstrun.connect.ConnectionPage),
('lastpage', firstrun.last.LastPage)
))
@@ -72,7 +72,7 @@ class FirstRunWizard(QtGui.QWizard):
conductor_instance,
parent=None,
pages_dict=None,
- eip_username=None,
+ username=None,
providers=None,
success_cb=None, is_provider_setup=False,
trusted_certs=None,
@@ -92,7 +92,7 @@ class FirstRunWizard(QtGui.QWizard):
# in the connection page, before the wizard has ended.
self.conductor = conductor_instance
- self.eip_username = eip_username
+ self.username = username
self.providers = providers
# success callback
@@ -129,7 +129,7 @@ class FirstRunWizard(QtGui.QWizard):
# by setting 1st page??
#self.is_previously_registered = is_previously_registered
# XXX ??? ^v
- self.is_previously_registered = bool(self.eip_username)
+ self.is_previously_registered = bool(self.username)
self.from_login = False
pages_dict = pages_dict or get_pages_dict()
@@ -233,7 +233,7 @@ class FirstRunWizard(QtGui.QWizard):
settings.setValue("remember_user_and_pass", remember_pass)
if remember_pass:
- settings.setValue("eip_username", full_username)
+ settings.setValue("username", full_username)
seed = self.get_random_str(10)
settings.setValue("%s_seed" % provider, seed)
diff --git a/src/leap/gui/progress.py b/src/leap/gui/progress.py
index ffea80de..ca4f6cc3 100644
--- a/src/leap/gui/progress.py
+++ b/src/leap/gui/progress.py
@@ -118,11 +118,12 @@ class StepsTableWidget(QtGui.QTableWidget):
self.setSelectionMode(
QtGui.QAbstractItemView.NoSelection)
width = self.width()
+
# WTF? Here init width is 100...
# but on populating is 456... :(
+ #logger.debug('init table. width=%s' % width)
# XXX do we need this initial?
- logger.debug('init table. width=%s' % width)
self.horizontalHeader().resizeSection(0, width * 0.7)
# this disables the table grid.
@@ -286,7 +287,7 @@ class WithStepsMixIn(object):
pagename = getattr(self, 'prev_page', None)
if pagename is None: # pragma: no cover
return
- logger.debug('cleaning wizard errors for %s' % pagename)
+ #logger.debug('cleaning wizard errors for %s' % pagename)
self.wizard().set_validation_error(pagename, None)
def populateStepsTable(self):
@@ -318,7 +319,7 @@ class WithStepsMixIn(object):
table = self.stepsTableWidget
FIRST_COLUMN_PERCENT = 0.70
width = table.width()
- logger.debug('populate table. width=%s' % width)
+ #logger.debug('populate table. width=%s' % width)
table.horizontalHeader().resizeSection(0, width * FIRST_COLUMN_PERCENT)
def set_item_icon(self, img=ICON_CHECKMARK, current=True):
diff --git a/src/leap/gui/tests/test_firstrun_login.py b/src/leap/gui/tests/test_firstrun_login.py
index fa800c23..6c45b8ef 100644
--- a/src/leap/gui/tests/test_firstrun_login.py
+++ b/src/leap/gui/tests/test_firstrun_login.py
@@ -100,7 +100,7 @@ class RegisterUserPageUITestCase(qunittest.TestCase):
pages = OrderedDict((
(self.pagename, TestPage),
('providersetupvalidation',
- firstrun.regvalidation.RegisterUserValidationPage)))
+ firstrun.connect.ConnectionPage)))
self.wizard = firstrun.wizard.FirstRunWizard(None, pages_dict=pages)
self.page = self.wizard.page(self.wizard.get_page_index(self.pagename))
diff --git a/src/leap/gui/tests/test_firstrun_register.py b/src/leap/gui/tests/test_firstrun_register.py
index 3447fe9d..d3be8897 100644
--- a/src/leap/gui/tests/test_firstrun_register.py
+++ b/src/leap/gui/tests/test_firstrun_register.py
@@ -113,7 +113,7 @@ class RegisterUserPageUITestCase(qunittest.TestCase):
pages = OrderedDict((
(self.pagename, TestPage),
('signupvalidation',
- firstrun.regvalidation.RegisterUserValidationPage)))
+ firstrun.connect.ConnectionPage)))
self.wizard = firstrun.wizard.FirstRunWizard(None, pages_dict=pages)
self.page = self.wizard.page(self.wizard.get_page_index(self.pagename))
diff --git a/src/leap/gui/tests/test_firstrun_wizard.py b/src/leap/gui/tests/test_firstrun_wizard.py
index 091cd932..b6c34000 100644
--- a/src/leap/gui/tests/test_firstrun_wizard.py
+++ b/src/leap/gui/tests/test_firstrun_wizard.py
@@ -29,8 +29,8 @@ PAGES_DICT = dict((
('providersetupvalidation',
firstrun.providersetup.ProviderSetupValidationPage),
('signup', firstrun.register.RegisterUserPage),
- ('signupvalidation',
- firstrun.regvalidation.RegisterUserValidationPage),
+ ('connect',
+ firstrun.connect.ConnectionPage),
('lastpage', firstrun.last.LastPage)
))
@@ -94,7 +94,7 @@ class FirstRunWizardTestCase(qunittest.TestCase):
calls = [call("FirstRunWizardDone", True),
call("provider_domain", "testprovider"),
call("remember_user_and_pass", True),
- call("eip_username", "testuser@testprovider"),
+ call("username", "testuser@testprovider"),
call("testprovider_seed", RANDOMSTR)]
mqs().setValue.assert_has_calls(calls, any_order=True)
diff --git a/src/leap/soledad/__init__.py b/src/leap/soledad/__init__.py
index 26e982ba..1473da38 100644
--- a/src/leap/soledad/__init__.py
+++ b/src/leap/soledad/__init__.py
@@ -45,6 +45,11 @@ class Soledad(object):
# Management of secret for symmetric encryption
#-------------------------------------------------------------------------
+
+ #-------------------------------------------------------------------------
+ # Management of secret for symmetric encryption
+ #-------------------------------------------------------------------------
+
def _has_secret(self):
"""
Verify if secret for symmetric encryption exists on local encrypted file.
diff --git a/src/leap/soledad/backends/leap_backend.py b/src/leap/soledad/backends/leap_backend.py
index 5a7dfa2f..3e859f7c 100644
--- a/src/leap/soledad/backends/leap_backend.py
+++ b/src/leap/soledad/backends/leap_backend.py
@@ -12,12 +12,11 @@ from leap.soledad.util import GPGWrapper
import uuid
-import uuid
-
class NoDefaultKey(Exception):
pass
+
class NoSoledadInstance(Exception):
pass
diff --git a/src/leap/soledad/tests/test_sqlcipher.py b/src/leap/soledad/tests/test_sqlcipher.py
index cfccf605..9e3b4052 100644
--- a/src/leap/soledad/tests/test_sqlcipher.py
+++ b/src/leap/soledad/tests/test_sqlcipher.py
@@ -335,4 +335,4 @@ class SQLCipherEncryptionTest(unittest.TestCase):
SQLCipherDatabase(self.DB_FILE, PASSWORD)
raise DatabaseError("SQLCipher backend should not be able to open non-encrypted dbs.")
except DatabaseIsNotEncrypted:
- pass \ No newline at end of file
+ pass
diff --git a/src/leap/soledad/util.py b/src/leap/soledad/util.py
index a26bff31..040c70ab 100644
--- a/src/leap/soledad/util.py
+++ b/src/leap/soledad/util.py
@@ -52,5 +52,3 @@ class GPGWrapper(gnupg.GPG):
data.close()
return result
-
-
diff --git a/src/leap/util/__init__.py b/src/leap/util/__init__.py
index e69de29b..a70a9a8b 100644
--- a/src/leap/util/__init__.py
+++ b/src/leap/util/__init__.py
@@ -0,0 +1,9 @@
+import logging
+logger = logging.getLogger(__name__)
+
+try:
+ import pygeoip
+ HAS_GEOIP = True
+except ImportError:
+ logger.debug('PyGeoIP not found. Disabled Geo support.')
+ HAS_GEOIP = False
diff --git a/src/leap/util/certs.py b/src/leap/util/certs.py
new file mode 100644
index 00000000..f0f790e9
--- /dev/null
+++ b/src/leap/util/certs.py
@@ -0,0 +1,18 @@
+import os
+import logging
+
+logger = logging.getLogger(__name__)
+
+
+def get_mac_cabundle():
+ # hackaround bundle error
+ # XXX this needs a better fix!
+ f = os.path.split(__file__)[0]
+ sep = os.path.sep
+ f_ = sep.join(f.split(sep)[:-2])
+ verify = os.path.join(f_, 'cacert.pem')
+ #logger.error('VERIFY PATH = %s' % verify)
+ exists = os.path.isfile(verify)
+ #logger.error('do exist? %s', exists)
+ if exists:
+ return verify
diff --git a/src/leap/util/geo.py b/src/leap/util/geo.py
new file mode 100644
index 00000000..54b29596
--- /dev/null
+++ b/src/leap/util/geo.py
@@ -0,0 +1,32 @@
+"""
+experimental geo support.
+not yet a feature.
+in debian, we rely on the (optional) geoip-database
+"""
+import os
+import platform
+
+from leap.util import HAS_GEOIP
+
+GEOIP = None
+
+if HAS_GEOIP:
+ import pygeoip # we know we can :)
+
+ GEOIP_PATH = None
+
+ if platform.system() == "Linux":
+ PATH = "/usr/share/GeoIP/GeoIP.dat"
+ if os.path.isfile(PATH):
+ GEOIP_PATH = PATH
+ GEOIP = pygeoip.GeoIP(GEOIP_PATH, pygeoip.MEMORY_CACHE)
+
+
+def get_country_name(ip):
+ if not GEOIP:
+ return
+ try:
+ country = GEOIP.country_name_by_addr(ip)
+ except pygeoip.GeoIPError:
+ country = None
+ return country if country else "-"
diff --git a/src/leap/util/leap_argparse.py b/src/leap/util/leap_argparse.py
index 2f996a31..5b0775cc 100644
--- a/src/leap/util/leap_argparse.py
+++ b/src/leap/util/leap_argparse.py
@@ -37,5 +37,5 @@ Launches main LEAP Client""", epilog=epilog)
def init_leapc_args():
parser = build_parser()
- opts = parser.parse_args()
+ opts, unknown = parser.parse_known_args()
return parser, opts
diff --git a/src/leap/util/misc.py b/src/leap/util/misc.py
index 3c26892b..aa3ebe25 100644
--- a/src/leap/util/misc.py
+++ b/src/leap/util/misc.py
@@ -1,6 +1,9 @@
"""
misc utils
"""
+import psutil
+
+from leap.base.constants import OPENVPN_BIN
class ImproperlyConfigured(Exception):
@@ -14,3 +17,20 @@ def null_check(value, value_name):
except AssertionError:
raise ImproperlyConfigured(
"%s parameter cannot be None" % value_name)
+
+def get_openvpn_pids():
+ # binary name might change
+
+ openvpn_pids = []
+ for p in psutil.process_iter():
+ try:
+ # XXX Not exact!
+ # Will give false positives.
+ # we should check that cmdline BEGINS
+ # with openvpn or with our wrapper
+ # (pkexec / osascript / whatever)
+ if OPENVPN_BIN in ' '.join(p.cmdline):
+ openvpn_pids.append(p.pid)
+ except psutil.error.AccessDenied:
+ pass
+ return openvpn_pids
diff --git a/src/leap/util/tests/test_translations.py b/src/leap/util/tests/test_translations.py
new file mode 100644
index 00000000..794daeba
--- /dev/null
+++ b/src/leap/util/tests/test_translations.py
@@ -0,0 +1,22 @@
+import unittest
+
+from leap.util import translations
+
+
+class TrasnlationsTestCase(unittest.TestCase):
+ """
+ tests for translation functions and classes
+ """
+
+ def setUp(self):
+ self.trClass = translations.LEAPTranslatable
+
+ def test_trasnlatable(self):
+ tr = self.trClass({"en": "house", "es": "casa"})
+ eq = self.assertEqual
+ eq(tr.tr(to="es"), "casa")
+ eq(tr.tr(to="en"), "house")
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/src/leap/util/translations.py b/src/leap/util/translations.py
new file mode 100644
index 00000000..d782cfe4
--- /dev/null
+++ b/src/leap/util/translations.py
@@ -0,0 +1,84 @@
+import inspect
+import logging
+
+from PyQt4.QtCore import QCoreApplication
+from PyQt4.QtCore import QLocale
+
+logger = logging.getLogger(__name__)
+
+"""
+here I could not do all that I wanted.
+the context is not getting passed to the xml file.
+Looks like pylupdate4 is somehow a hack that does not
+parse too well the python ast.
+I guess we could generate the xml for ourselves as a last recourse.
+"""
+
+# XXX BIG NOTE:
+# RESIST the temptation to get the translate function
+# more compact, or have the Context argument passed as a variable
+# Its name HAS to be explicit due to how the pylupdate parser
+# works.
+
+
+qtTranslate = QCoreApplication.translate
+
+
+def translate(*args, **kwargs):
+ """
+ our magic function.
+ translate(Context, text, comment)
+ """
+ if len(args) == 1:
+ obj = args[0]
+ if isinstance(obj, LEAPTranslatable) and hasattr(obj, 'tr'):
+ return obj.tr()
+
+ klsname = None
+ try:
+ # get class value from instance
+ # using live object inspection
+ prev_frame = inspect.stack()[1][0]
+ locals_ = inspect.getargvalues(prev_frame).locals
+ self = locals_.get('self')
+ if self:
+
+ # Trying to get the class name
+ # but this is useless, the parser
+ # has already got the context.
+ klsname = self.__class__.__name__
+ #print 'KLSNAME -- ', klsname
+ except:
+ logger.error('error getting stack frame')
+
+ if klsname and len(args) == 1:
+ nargs = (klsname,) + args
+ return qtTranslate(*nargs)
+
+ else:
+ #nargs = ('default', ) + args
+ #import pdb4qt; pdb4qt.set_trace()
+ return qtTranslate(*args)
+
+
+class LEAPTranslatable(dict):
+ """
+ An extended dict that implements a .tr method
+ so it can be translated on the fly by our
+ magic translate method
+ """
+
+ try:
+ locale = str(QLocale.system().name()).split('_')[0]
+ except:
+ logger.warning("could not get system locale!")
+ print "could not get system locale!"
+ locale = "en"
+
+ def tr(self, to=None):
+ if not to:
+ to = self.locale
+ _tr = self.get(to, None)
+ if not _tr:
+ _tr = self.get("en", None)
+ return _tr